162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/fs/hfs/extent.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 1995-1997  Paul H. Hargrove
562306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
662306a36Sopenharmony_ci * This file may be distributed under the terms of the GNU General Public License.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file contains the functions related to the extents B-tree.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/pagemap.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "hfs_fs.h"
1462306a36Sopenharmony_ci#include "btree.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*================ File-local functions ================*/
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * build_key
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_cistatic void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	key->key_len = 7;
2462306a36Sopenharmony_ci	key->ext.FkType = type;
2562306a36Sopenharmony_ci	key->ext.FNum = cpu_to_be32(cnid);
2662306a36Sopenharmony_ci	key->ext.FABN = cpu_to_be16(block);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * hfs_ext_compare()
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Description:
3362306a36Sopenharmony_ci *   This is the comparison function used for the extents B-tree.  In
3462306a36Sopenharmony_ci *   comparing extent B-tree entries, the file id is the most
3562306a36Sopenharmony_ci *   significant field (compared as unsigned ints); the fork type is
3662306a36Sopenharmony_ci *   the second most significant field (compared as unsigned chars);
3762306a36Sopenharmony_ci *   and the allocation block number field is the least significant
3862306a36Sopenharmony_ci *   (compared as unsigned ints).
3962306a36Sopenharmony_ci * Input Variable(s):
4062306a36Sopenharmony_ci *   struct hfs_ext_key *key1: pointer to the first key to compare
4162306a36Sopenharmony_ci *   struct hfs_ext_key *key2: pointer to the second key to compare
4262306a36Sopenharmony_ci * Output Variable(s):
4362306a36Sopenharmony_ci *   NONE
4462306a36Sopenharmony_ci * Returns:
4562306a36Sopenharmony_ci *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
4662306a36Sopenharmony_ci * Preconditions:
4762306a36Sopenharmony_ci *   key1 and key2 point to "valid" (struct hfs_ext_key)s.
4862306a36Sopenharmony_ci * Postconditions:
4962306a36Sopenharmony_ci *   This function has no side-effects */
5062306a36Sopenharmony_ciint hfs_ext_keycmp(const btree_key *key1, const btree_key *key2)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	__be32 fnum1, fnum2;
5362306a36Sopenharmony_ci	__be16 block1, block2;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	fnum1 = key1->ext.FNum;
5662306a36Sopenharmony_ci	fnum2 = key2->ext.FNum;
5762306a36Sopenharmony_ci	if (fnum1 != fnum2)
5862306a36Sopenharmony_ci		return be32_to_cpu(fnum1) < be32_to_cpu(fnum2) ? -1 : 1;
5962306a36Sopenharmony_ci	if (key1->ext.FkType != key2->ext.FkType)
6062306a36Sopenharmony_ci		return key1->ext.FkType < key2->ext.FkType ? -1 : 1;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	block1 = key1->ext.FABN;
6362306a36Sopenharmony_ci	block2 = key2->ext.FABN;
6462306a36Sopenharmony_ci	if (block1 == block2)
6562306a36Sopenharmony_ci		return 0;
6662306a36Sopenharmony_ci	return be16_to_cpu(block1) < be16_to_cpu(block2) ? -1 : 1;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * hfs_ext_find_block
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Find a block within an extent record
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci	u16 count;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	for (i = 0; i < 3; ext++, i++) {
8062306a36Sopenharmony_ci		count = be16_to_cpu(ext->count);
8162306a36Sopenharmony_ci		if (off < count)
8262306a36Sopenharmony_ci			return be16_to_cpu(ext->block) + off;
8362306a36Sopenharmony_ci		off -= count;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	/* panic? */
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int hfs_ext_block_count(struct hfs_extent *ext)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int i;
9262306a36Sopenharmony_ci	u16 count = 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	for (i = 0; i < 3; ext++, i++)
9562306a36Sopenharmony_ci		count += be16_to_cpu(ext->count);
9662306a36Sopenharmony_ci	return count;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic u16 hfs_ext_lastblock(struct hfs_extent *ext)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int i;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ext += 2;
10462306a36Sopenharmony_ci	for (i = 0; i < 2; ext--, i++)
10562306a36Sopenharmony_ci		if (ext->count)
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci	return be16_to_cpu(ext->block) + be16_to_cpu(ext->count);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	int res;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start,
11562306a36Sopenharmony_ci			  HFS_IS_RSRC(inode) ?  HFS_FK_RSRC : HFS_FK_DATA);
11662306a36Sopenharmony_ci	res = hfs_brec_find(fd);
11762306a36Sopenharmony_ci	if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
11862306a36Sopenharmony_ci		if (res != -ENOENT)
11962306a36Sopenharmony_ci			return res;
12062306a36Sopenharmony_ci		/* Fail early and avoid ENOSPC during the btree operation */
12162306a36Sopenharmony_ci		res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
12262306a36Sopenharmony_ci		if (res)
12362306a36Sopenharmony_ci			return res;
12462306a36Sopenharmony_ci		hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
12562306a36Sopenharmony_ci		HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
12662306a36Sopenharmony_ci	} else {
12762306a36Sopenharmony_ci		if (res)
12862306a36Sopenharmony_ci			return res;
12962306a36Sopenharmony_ci		hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength);
13062306a36Sopenharmony_ci		HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciint hfs_ext_write_extent(struct inode *inode)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct hfs_find_data fd;
13862306a36Sopenharmony_ci	int res = 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
14162306a36Sopenharmony_ci		res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
14262306a36Sopenharmony_ci		if (res)
14362306a36Sopenharmony_ci			return res;
14462306a36Sopenharmony_ci		res = __hfs_ext_write_extent(inode, &fd);
14562306a36Sopenharmony_ci		hfs_find_exit(&fd);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	return res;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent,
15162306a36Sopenharmony_ci					u32 cnid, u32 block, u8 type)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	int res;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	hfs_ext_build_key(fd->search_key, cnid, block, type);
15662306a36Sopenharmony_ci	fd->key->ext.FNum = 0;
15762306a36Sopenharmony_ci	res = hfs_brec_find(fd);
15862306a36Sopenharmony_ci	if (res && res != -ENOENT)
15962306a36Sopenharmony_ci		return res;
16062306a36Sopenharmony_ci	if (fd->key->ext.FNum != fd->search_key->ext.FNum ||
16162306a36Sopenharmony_ci	    fd->key->ext.FkType != fd->search_key->ext.FkType)
16262306a36Sopenharmony_ci		return -ENOENT;
16362306a36Sopenharmony_ci	if (fd->entrylength != sizeof(hfs_extent_rec))
16462306a36Sopenharmony_ci		return -EIO;
16562306a36Sopenharmony_ci	hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec));
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int res;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
17462306a36Sopenharmony_ci		res = __hfs_ext_write_extent(inode, fd);
17562306a36Sopenharmony_ci		if (res)
17662306a36Sopenharmony_ci			return res;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino,
18062306a36Sopenharmony_ci				    block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA);
18162306a36Sopenharmony_ci	if (!res) {
18262306a36Sopenharmony_ci		HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN);
18362306a36Sopenharmony_ci		HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents);
18462306a36Sopenharmony_ci	} else {
18562306a36Sopenharmony_ci		HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
18662306a36Sopenharmony_ci		HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	return res;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int hfs_ext_read_extent(struct inode *inode, u16 block)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct hfs_find_data fd;
19462306a36Sopenharmony_ci	int res;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (block >= HFS_I(inode)->cached_start &&
19762306a36Sopenharmony_ci	    block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks)
19862306a36Sopenharmony_ci		return 0;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
20162306a36Sopenharmony_ci	if (!res) {
20262306a36Sopenharmony_ci		res = __hfs_ext_cache_extent(&fd, inode, block);
20362306a36Sopenharmony_ci		hfs_find_exit(&fd);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci	return res;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void hfs_dump_extent(struct hfs_extent *extent)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int i;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	hfs_dbg(EXTENT, "   ");
21362306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
21462306a36Sopenharmony_ci		hfs_dbg_cont(EXTENT, " %u:%u",
21562306a36Sopenharmony_ci			     be16_to_cpu(extent[i].block),
21662306a36Sopenharmony_ci			     be16_to_cpu(extent[i].count));
21762306a36Sopenharmony_ci	hfs_dbg_cont(EXTENT, "\n");
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int hfs_add_extent(struct hfs_extent *extent, u16 offset,
22162306a36Sopenharmony_ci			  u16 alloc_block, u16 block_count)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	u16 count, start;
22462306a36Sopenharmony_ci	int i;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	hfs_dump_extent(extent);
22762306a36Sopenharmony_ci	for (i = 0; i < 3; extent++, i++) {
22862306a36Sopenharmony_ci		count = be16_to_cpu(extent->count);
22962306a36Sopenharmony_ci		if (offset == count) {
23062306a36Sopenharmony_ci			start = be16_to_cpu(extent->block);
23162306a36Sopenharmony_ci			if (alloc_block != start + count) {
23262306a36Sopenharmony_ci				if (++i >= 3)
23362306a36Sopenharmony_ci					return -ENOSPC;
23462306a36Sopenharmony_ci				extent++;
23562306a36Sopenharmony_ci				extent->block = cpu_to_be16(alloc_block);
23662306a36Sopenharmony_ci			} else
23762306a36Sopenharmony_ci				block_count += count;
23862306a36Sopenharmony_ci			extent->count = cpu_to_be16(block_count);
23962306a36Sopenharmony_ci			return 0;
24062306a36Sopenharmony_ci		} else if (offset < count)
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		offset -= count;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	/* panic? */
24562306a36Sopenharmony_ci	return -EIO;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent,
24962306a36Sopenharmony_ci			    u16 offset, u16 block_nr)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	u16 count, start;
25262306a36Sopenharmony_ci	int i;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	hfs_dump_extent(extent);
25562306a36Sopenharmony_ci	for (i = 0; i < 3; extent++, i++) {
25662306a36Sopenharmony_ci		count = be16_to_cpu(extent->count);
25762306a36Sopenharmony_ci		if (offset == count)
25862306a36Sopenharmony_ci			goto found;
25962306a36Sopenharmony_ci		else if (offset < count)
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci		offset -= count;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	/* panic? */
26462306a36Sopenharmony_ci	return -EIO;
26562306a36Sopenharmony_cifound:
26662306a36Sopenharmony_ci	for (;;) {
26762306a36Sopenharmony_ci		start = be16_to_cpu(extent->block);
26862306a36Sopenharmony_ci		if (count <= block_nr) {
26962306a36Sopenharmony_ci			hfs_clear_vbm_bits(sb, start, count);
27062306a36Sopenharmony_ci			extent->block = 0;
27162306a36Sopenharmony_ci			extent->count = 0;
27262306a36Sopenharmony_ci			block_nr -= count;
27362306a36Sopenharmony_ci		} else {
27462306a36Sopenharmony_ci			count -= block_nr;
27562306a36Sopenharmony_ci			hfs_clear_vbm_bits(sb, start + count, block_nr);
27662306a36Sopenharmony_ci			extent->count = cpu_to_be16(count);
27762306a36Sopenharmony_ci			block_nr = 0;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci		if (!block_nr || !i)
28062306a36Sopenharmony_ci			return 0;
28162306a36Sopenharmony_ci		i--;
28262306a36Sopenharmony_ci		extent--;
28362306a36Sopenharmony_ci		count = be16_to_cpu(extent->count);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciint hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct hfs_find_data fd;
29062306a36Sopenharmony_ci	u32 total_blocks, blocks, start;
29162306a36Sopenharmony_ci	u32 cnid = be32_to_cpu(file->FlNum);
29262306a36Sopenharmony_ci	struct hfs_extent *extent;
29362306a36Sopenharmony_ci	int res, i;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (type == HFS_FK_DATA) {
29662306a36Sopenharmony_ci		total_blocks = be32_to_cpu(file->PyLen);
29762306a36Sopenharmony_ci		extent = file->ExtRec;
29862306a36Sopenharmony_ci	} else {
29962306a36Sopenharmony_ci		total_blocks = be32_to_cpu(file->RPyLen);
30062306a36Sopenharmony_ci		extent = file->RExtRec;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	total_blocks /= HFS_SB(sb)->alloc_blksz;
30362306a36Sopenharmony_ci	if (!total_blocks)
30462306a36Sopenharmony_ci		return 0;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	blocks = 0;
30762306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
30862306a36Sopenharmony_ci		blocks += be16_to_cpu(extent[i].count);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	res = hfs_free_extents(sb, extent, blocks, blocks);
31162306a36Sopenharmony_ci	if (res)
31262306a36Sopenharmony_ci		return res;
31362306a36Sopenharmony_ci	if (total_blocks == blocks)
31462306a36Sopenharmony_ci		return 0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
31762306a36Sopenharmony_ci	if (res)
31862306a36Sopenharmony_ci		return res;
31962306a36Sopenharmony_ci	do {
32062306a36Sopenharmony_ci		res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type);
32162306a36Sopenharmony_ci		if (res)
32262306a36Sopenharmony_ci			break;
32362306a36Sopenharmony_ci		start = be16_to_cpu(fd.key->ext.FABN);
32462306a36Sopenharmony_ci		hfs_free_extents(sb, extent, total_blocks - start, total_blocks);
32562306a36Sopenharmony_ci		hfs_brec_remove(&fd);
32662306a36Sopenharmony_ci		total_blocks = start;
32762306a36Sopenharmony_ci	} while (total_blocks > blocks);
32862306a36Sopenharmony_ci	hfs_find_exit(&fd);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return res;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/*
33462306a36Sopenharmony_ci * hfs_get_block
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_ciint hfs_get_block(struct inode *inode, sector_t block,
33762306a36Sopenharmony_ci		  struct buffer_head *bh_result, int create)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct super_block *sb;
34062306a36Sopenharmony_ci	u16 dblock, ablock;
34162306a36Sopenharmony_ci	int res;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	sb = inode->i_sb;
34462306a36Sopenharmony_ci	/* Convert inode block to disk allocation block */
34562306a36Sopenharmony_ci	ablock = (u32)block / HFS_SB(sb)->fs_div;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (block >= HFS_I(inode)->fs_blocks) {
34862306a36Sopenharmony_ci		if (!create)
34962306a36Sopenharmony_ci			return 0;
35062306a36Sopenharmony_ci		if (block > HFS_I(inode)->fs_blocks)
35162306a36Sopenharmony_ci			return -EIO;
35262306a36Sopenharmony_ci		if (ablock >= HFS_I(inode)->alloc_blocks) {
35362306a36Sopenharmony_ci			res = hfs_extend_file(inode);
35462306a36Sopenharmony_ci			if (res)
35562306a36Sopenharmony_ci				return res;
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	} else
35862306a36Sopenharmony_ci		create = 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (ablock < HFS_I(inode)->first_blocks) {
36162306a36Sopenharmony_ci		dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock);
36262306a36Sopenharmony_ci		goto done;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mutex_lock(&HFS_I(inode)->extents_lock);
36662306a36Sopenharmony_ci	res = hfs_ext_read_extent(inode, ablock);
36762306a36Sopenharmony_ci	if (!res)
36862306a36Sopenharmony_ci		dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents,
36962306a36Sopenharmony_ci					    ablock - HFS_I(inode)->cached_start);
37062306a36Sopenharmony_ci	else {
37162306a36Sopenharmony_ci		mutex_unlock(&HFS_I(inode)->extents_lock);
37262306a36Sopenharmony_ci		return -EIO;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	mutex_unlock(&HFS_I(inode)->extents_lock);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cidone:
37762306a36Sopenharmony_ci	map_bh(bh_result, sb, HFS_SB(sb)->fs_start +
37862306a36Sopenharmony_ci	       dblock * HFS_SB(sb)->fs_div +
37962306a36Sopenharmony_ci	       (u32)block % HFS_SB(sb)->fs_div);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (create) {
38262306a36Sopenharmony_ci		set_buffer_new(bh_result);
38362306a36Sopenharmony_ci		HFS_I(inode)->phys_size += sb->s_blocksize;
38462306a36Sopenharmony_ci		HFS_I(inode)->fs_blocks++;
38562306a36Sopenharmony_ci		inode_add_bytes(inode, sb->s_blocksize);
38662306a36Sopenharmony_ci		mark_inode_dirty(inode);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ciint hfs_extend_file(struct inode *inode)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
39462306a36Sopenharmony_ci	u32 start, len, goal;
39562306a36Sopenharmony_ci	int res;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	mutex_lock(&HFS_I(inode)->extents_lock);
39862306a36Sopenharmony_ci	if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks)
39962306a36Sopenharmony_ci		goal = hfs_ext_lastblock(HFS_I(inode)->first_extents);
40062306a36Sopenharmony_ci	else {
40162306a36Sopenharmony_ci		res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks);
40262306a36Sopenharmony_ci		if (res)
40362306a36Sopenharmony_ci			goto out;
40462306a36Sopenharmony_ci		goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents);
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	len = HFS_I(inode)->clump_blocks;
40862306a36Sopenharmony_ci	start = hfs_vbm_search_free(sb, goal, &len);
40962306a36Sopenharmony_ci	if (!len) {
41062306a36Sopenharmony_ci		res = -ENOSPC;
41162306a36Sopenharmony_ci		goto out;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
41562306a36Sopenharmony_ci	if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) {
41662306a36Sopenharmony_ci		if (!HFS_I(inode)->first_blocks) {
41762306a36Sopenharmony_ci			hfs_dbg(EXTENT, "first extents\n");
41862306a36Sopenharmony_ci			/* no extents yet */
41962306a36Sopenharmony_ci			HFS_I(inode)->first_extents[0].block = cpu_to_be16(start);
42062306a36Sopenharmony_ci			HFS_I(inode)->first_extents[0].count = cpu_to_be16(len);
42162306a36Sopenharmony_ci			res = 0;
42262306a36Sopenharmony_ci		} else {
42362306a36Sopenharmony_ci			/* try to append to extents in inode */
42462306a36Sopenharmony_ci			res = hfs_add_extent(HFS_I(inode)->first_extents,
42562306a36Sopenharmony_ci					     HFS_I(inode)->alloc_blocks,
42662306a36Sopenharmony_ci					     start, len);
42762306a36Sopenharmony_ci			if (res == -ENOSPC)
42862306a36Sopenharmony_ci				goto insert_extent;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci		if (!res) {
43162306a36Sopenharmony_ci			hfs_dump_extent(HFS_I(inode)->first_extents);
43262306a36Sopenharmony_ci			HFS_I(inode)->first_blocks += len;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci	} else {
43562306a36Sopenharmony_ci		res = hfs_add_extent(HFS_I(inode)->cached_extents,
43662306a36Sopenharmony_ci				     HFS_I(inode)->alloc_blocks -
43762306a36Sopenharmony_ci				     HFS_I(inode)->cached_start,
43862306a36Sopenharmony_ci				     start, len);
43962306a36Sopenharmony_ci		if (!res) {
44062306a36Sopenharmony_ci			hfs_dump_extent(HFS_I(inode)->cached_extents);
44162306a36Sopenharmony_ci			HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
44262306a36Sopenharmony_ci			HFS_I(inode)->cached_blocks += len;
44362306a36Sopenharmony_ci		} else if (res == -ENOSPC)
44462306a36Sopenharmony_ci			goto insert_extent;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ciout:
44762306a36Sopenharmony_ci	mutex_unlock(&HFS_I(inode)->extents_lock);
44862306a36Sopenharmony_ci	if (!res) {
44962306a36Sopenharmony_ci		HFS_I(inode)->alloc_blocks += len;
45062306a36Sopenharmony_ci		mark_inode_dirty(inode);
45162306a36Sopenharmony_ci		if (inode->i_ino < HFS_FIRSTUSER_CNID)
45262306a36Sopenharmony_ci			set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags);
45362306a36Sopenharmony_ci		set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
45462306a36Sopenharmony_ci		hfs_mark_mdb_dirty(sb);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci	return res;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ciinsert_extent:
45962306a36Sopenharmony_ci	hfs_dbg(EXTENT, "insert new extent\n");
46062306a36Sopenharmony_ci	res = hfs_ext_write_extent(inode);
46162306a36Sopenharmony_ci	if (res)
46262306a36Sopenharmony_ci		goto out;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
46562306a36Sopenharmony_ci	HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start);
46662306a36Sopenharmony_ci	HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len);
46762306a36Sopenharmony_ci	hfs_dump_extent(HFS_I(inode)->cached_extents);
46862306a36Sopenharmony_ci	HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW;
46962306a36Sopenharmony_ci	HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks;
47062306a36Sopenharmony_ci	HFS_I(inode)->cached_blocks = len;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	res = 0;
47362306a36Sopenharmony_ci	goto out;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_civoid hfs_file_truncate(struct inode *inode)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
47962306a36Sopenharmony_ci	struct hfs_find_data fd;
48062306a36Sopenharmony_ci	u16 blk_cnt, alloc_cnt, start;
48162306a36Sopenharmony_ci	u32 size;
48262306a36Sopenharmony_ci	int res;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	hfs_dbg(INODE, "truncate: %lu, %Lu -> %Lu\n",
48562306a36Sopenharmony_ci		inode->i_ino, (long long)HFS_I(inode)->phys_size,
48662306a36Sopenharmony_ci		inode->i_size);
48762306a36Sopenharmony_ci	if (inode->i_size > HFS_I(inode)->phys_size) {
48862306a36Sopenharmony_ci		struct address_space *mapping = inode->i_mapping;
48962306a36Sopenharmony_ci		void *fsdata = NULL;
49062306a36Sopenharmony_ci		struct page *page;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		/* XXX: Can use generic_cont_expand? */
49362306a36Sopenharmony_ci		size = inode->i_size - 1;
49462306a36Sopenharmony_ci		res = hfs_write_begin(NULL, mapping, size + 1, 0, &page,
49562306a36Sopenharmony_ci				&fsdata);
49662306a36Sopenharmony_ci		if (!res) {
49762306a36Sopenharmony_ci			res = generic_write_end(NULL, mapping, size + 1, 0, 0,
49862306a36Sopenharmony_ci					page, fsdata);
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci		if (res)
50162306a36Sopenharmony_ci			inode->i_size = HFS_I(inode)->phys_size;
50262306a36Sopenharmony_ci		return;
50362306a36Sopenharmony_ci	} else if (inode->i_size == HFS_I(inode)->phys_size)
50462306a36Sopenharmony_ci		return;
50562306a36Sopenharmony_ci	size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1;
50662306a36Sopenharmony_ci	blk_cnt = size / HFS_SB(sb)->alloc_blksz;
50762306a36Sopenharmony_ci	alloc_cnt = HFS_I(inode)->alloc_blocks;
50862306a36Sopenharmony_ci	if (blk_cnt == alloc_cnt)
50962306a36Sopenharmony_ci		goto out;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	mutex_lock(&HFS_I(inode)->extents_lock);
51262306a36Sopenharmony_ci	res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
51362306a36Sopenharmony_ci	if (res) {
51462306a36Sopenharmony_ci		mutex_unlock(&HFS_I(inode)->extents_lock);
51562306a36Sopenharmony_ci		/* XXX: We lack error handling of hfs_file_truncate() */
51662306a36Sopenharmony_ci		return;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci	while (1) {
51962306a36Sopenharmony_ci		if (alloc_cnt == HFS_I(inode)->first_blocks) {
52062306a36Sopenharmony_ci			hfs_free_extents(sb, HFS_I(inode)->first_extents,
52162306a36Sopenharmony_ci					 alloc_cnt, alloc_cnt - blk_cnt);
52262306a36Sopenharmony_ci			hfs_dump_extent(HFS_I(inode)->first_extents);
52362306a36Sopenharmony_ci			HFS_I(inode)->first_blocks = blk_cnt;
52462306a36Sopenharmony_ci			break;
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci		res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt);
52762306a36Sopenharmony_ci		if (res)
52862306a36Sopenharmony_ci			break;
52962306a36Sopenharmony_ci		start = HFS_I(inode)->cached_start;
53062306a36Sopenharmony_ci		hfs_free_extents(sb, HFS_I(inode)->cached_extents,
53162306a36Sopenharmony_ci				 alloc_cnt - start, alloc_cnt - blk_cnt);
53262306a36Sopenharmony_ci		hfs_dump_extent(HFS_I(inode)->cached_extents);
53362306a36Sopenharmony_ci		if (blk_cnt > start) {
53462306a36Sopenharmony_ci			HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
53562306a36Sopenharmony_ci			break;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci		alloc_cnt = start;
53862306a36Sopenharmony_ci		HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
53962306a36Sopenharmony_ci		HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
54062306a36Sopenharmony_ci		hfs_brec_remove(&fd);
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci	hfs_find_exit(&fd);
54362306a36Sopenharmony_ci	mutex_unlock(&HFS_I(inode)->extents_lock);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	HFS_I(inode)->alloc_blocks = blk_cnt;
54662306a36Sopenharmony_ciout:
54762306a36Sopenharmony_ci	HFS_I(inode)->phys_size = inode->i_size;
54862306a36Sopenharmony_ci	HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
54962306a36Sopenharmony_ci	inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits);
55062306a36Sopenharmony_ci	mark_inode_dirty(inode);
55162306a36Sopenharmony_ci}
552