18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  linux/fs/hfs/extent.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 1995-1997  Paul H. Hargrove
58c2ecf20Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
68c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the GNU General Public License.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file contains the functions related to the extents B-tree.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "hfs_fs.h"
148c2ecf20Sopenharmony_ci#include "btree.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*================ File-local functions ================*/
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * build_key
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	key->key_len = 7;
248c2ecf20Sopenharmony_ci	key->ext.FkType = type;
258c2ecf20Sopenharmony_ci	key->ext.FNum = cpu_to_be32(cnid);
268c2ecf20Sopenharmony_ci	key->ext.FABN = cpu_to_be16(block);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * hfs_ext_compare()
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Description:
338c2ecf20Sopenharmony_ci *   This is the comparison function used for the extents B-tree.  In
348c2ecf20Sopenharmony_ci *   comparing extent B-tree entries, the file id is the most
358c2ecf20Sopenharmony_ci *   significant field (compared as unsigned ints); the fork type is
368c2ecf20Sopenharmony_ci *   the second most significant field (compared as unsigned chars);
378c2ecf20Sopenharmony_ci *   and the allocation block number field is the least significant
388c2ecf20Sopenharmony_ci *   (compared as unsigned ints).
398c2ecf20Sopenharmony_ci * Input Variable(s):
408c2ecf20Sopenharmony_ci *   struct hfs_ext_key *key1: pointer to the first key to compare
418c2ecf20Sopenharmony_ci *   struct hfs_ext_key *key2: pointer to the second key to compare
428c2ecf20Sopenharmony_ci * Output Variable(s):
438c2ecf20Sopenharmony_ci *   NONE
448c2ecf20Sopenharmony_ci * Returns:
458c2ecf20Sopenharmony_ci *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
468c2ecf20Sopenharmony_ci * Preconditions:
478c2ecf20Sopenharmony_ci *   key1 and key2 point to "valid" (struct hfs_ext_key)s.
488c2ecf20Sopenharmony_ci * Postconditions:
498c2ecf20Sopenharmony_ci *   This function has no side-effects */
508c2ecf20Sopenharmony_ciint hfs_ext_keycmp(const btree_key *key1, const btree_key *key2)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	__be32 fnum1, fnum2;
538c2ecf20Sopenharmony_ci	__be16 block1, block2;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	fnum1 = key1->ext.FNum;
568c2ecf20Sopenharmony_ci	fnum2 = key2->ext.FNum;
578c2ecf20Sopenharmony_ci	if (fnum1 != fnum2)
588c2ecf20Sopenharmony_ci		return be32_to_cpu(fnum1) < be32_to_cpu(fnum2) ? -1 : 1;
598c2ecf20Sopenharmony_ci	if (key1->ext.FkType != key2->ext.FkType)
608c2ecf20Sopenharmony_ci		return key1->ext.FkType < key2->ext.FkType ? -1 : 1;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	block1 = key1->ext.FABN;
638c2ecf20Sopenharmony_ci	block2 = key2->ext.FABN;
648c2ecf20Sopenharmony_ci	if (block1 == block2)
658c2ecf20Sopenharmony_ci		return 0;
668c2ecf20Sopenharmony_ci	return be16_to_cpu(block1) < be16_to_cpu(block2) ? -1 : 1;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * hfs_ext_find_block
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Find a block within an extent record
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	int i;
778c2ecf20Sopenharmony_ci	u16 count;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	for (i = 0; i < 3; ext++, i++) {
808c2ecf20Sopenharmony_ci		count = be16_to_cpu(ext->count);
818c2ecf20Sopenharmony_ci		if (off < count)
828c2ecf20Sopenharmony_ci			return be16_to_cpu(ext->block) + off;
838c2ecf20Sopenharmony_ci		off -= count;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci	/* panic? */
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int hfs_ext_block_count(struct hfs_extent *ext)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int i;
928c2ecf20Sopenharmony_ci	u16 count = 0;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	for (i = 0; i < 3; ext++, i++)
958c2ecf20Sopenharmony_ci		count += be16_to_cpu(ext->count);
968c2ecf20Sopenharmony_ci	return count;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic u16 hfs_ext_lastblock(struct hfs_extent *ext)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	int i;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	ext += 2;
1048c2ecf20Sopenharmony_ci	for (i = 0; i < 2; ext--, i++)
1058c2ecf20Sopenharmony_ci		if (ext->count)
1068c2ecf20Sopenharmony_ci			break;
1078c2ecf20Sopenharmony_ci	return be16_to_cpu(ext->block) + be16_to_cpu(ext->count);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int res;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start,
1158c2ecf20Sopenharmony_ci			  HFS_IS_RSRC(inode) ?  HFS_FK_RSRC : HFS_FK_DATA);
1168c2ecf20Sopenharmony_ci	res = hfs_brec_find(fd);
1178c2ecf20Sopenharmony_ci	if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
1188c2ecf20Sopenharmony_ci		if (res != -ENOENT)
1198c2ecf20Sopenharmony_ci			return res;
1208c2ecf20Sopenharmony_ci		/* Fail early and avoid ENOSPC during the btree operation */
1218c2ecf20Sopenharmony_ci		res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
1228c2ecf20Sopenharmony_ci		if (res)
1238c2ecf20Sopenharmony_ci			return res;
1248c2ecf20Sopenharmony_ci		hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
1258c2ecf20Sopenharmony_ci		HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
1268c2ecf20Sopenharmony_ci	} else {
1278c2ecf20Sopenharmony_ci		if (res)
1288c2ecf20Sopenharmony_ci			return res;
1298c2ecf20Sopenharmony_ci		hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength);
1308c2ecf20Sopenharmony_ci		HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciint hfs_ext_write_extent(struct inode *inode)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
1388c2ecf20Sopenharmony_ci	int res = 0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
1418c2ecf20Sopenharmony_ci		res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
1428c2ecf20Sopenharmony_ci		if (res)
1438c2ecf20Sopenharmony_ci			return res;
1448c2ecf20Sopenharmony_ci		res = __hfs_ext_write_extent(inode, &fd);
1458c2ecf20Sopenharmony_ci		hfs_find_exit(&fd);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci	return res;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent,
1518c2ecf20Sopenharmony_ci					u32 cnid, u32 block, u8 type)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	int res;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	hfs_ext_build_key(fd->search_key, cnid, block, type);
1568c2ecf20Sopenharmony_ci	fd->key->ext.FNum = 0;
1578c2ecf20Sopenharmony_ci	res = hfs_brec_find(fd);
1588c2ecf20Sopenharmony_ci	if (res && res != -ENOENT)
1598c2ecf20Sopenharmony_ci		return res;
1608c2ecf20Sopenharmony_ci	if (fd->key->ext.FNum != fd->search_key->ext.FNum ||
1618c2ecf20Sopenharmony_ci	    fd->key->ext.FkType != fd->search_key->ext.FkType)
1628c2ecf20Sopenharmony_ci		return -ENOENT;
1638c2ecf20Sopenharmony_ci	if (fd->entrylength != sizeof(hfs_extent_rec))
1648c2ecf20Sopenharmony_ci		return -EIO;
1658c2ecf20Sopenharmony_ci	hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec));
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	int res;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
1748c2ecf20Sopenharmony_ci		res = __hfs_ext_write_extent(inode, fd);
1758c2ecf20Sopenharmony_ci		if (res)
1768c2ecf20Sopenharmony_ci			return res;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino,
1808c2ecf20Sopenharmony_ci				    block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA);
1818c2ecf20Sopenharmony_ci	if (!res) {
1828c2ecf20Sopenharmony_ci		HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN);
1838c2ecf20Sopenharmony_ci		HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents);
1848c2ecf20Sopenharmony_ci	} else {
1858c2ecf20Sopenharmony_ci		HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
1868c2ecf20Sopenharmony_ci		HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	return res;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int hfs_ext_read_extent(struct inode *inode, u16 block)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
1948c2ecf20Sopenharmony_ci	int res;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (block >= HFS_I(inode)->cached_start &&
1978c2ecf20Sopenharmony_ci	    block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks)
1988c2ecf20Sopenharmony_ci		return 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
2018c2ecf20Sopenharmony_ci	if (!res) {
2028c2ecf20Sopenharmony_ci		res = __hfs_ext_cache_extent(&fd, inode, block);
2038c2ecf20Sopenharmony_ci		hfs_find_exit(&fd);
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci	return res;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void hfs_dump_extent(struct hfs_extent *extent)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int i;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "   ");
2138c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
2148c2ecf20Sopenharmony_ci		hfs_dbg_cont(EXTENT, " %u:%u",
2158c2ecf20Sopenharmony_ci			     be16_to_cpu(extent[i].block),
2168c2ecf20Sopenharmony_ci			     be16_to_cpu(extent[i].count));
2178c2ecf20Sopenharmony_ci	hfs_dbg_cont(EXTENT, "\n");
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int hfs_add_extent(struct hfs_extent *extent, u16 offset,
2218c2ecf20Sopenharmony_ci			  u16 alloc_block, u16 block_count)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	u16 count, start;
2248c2ecf20Sopenharmony_ci	int i;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	hfs_dump_extent(extent);
2278c2ecf20Sopenharmony_ci	for (i = 0; i < 3; extent++, i++) {
2288c2ecf20Sopenharmony_ci		count = be16_to_cpu(extent->count);
2298c2ecf20Sopenharmony_ci		if (offset == count) {
2308c2ecf20Sopenharmony_ci			start = be16_to_cpu(extent->block);
2318c2ecf20Sopenharmony_ci			if (alloc_block != start + count) {
2328c2ecf20Sopenharmony_ci				if (++i >= 3)
2338c2ecf20Sopenharmony_ci					return -ENOSPC;
2348c2ecf20Sopenharmony_ci				extent++;
2358c2ecf20Sopenharmony_ci				extent->block = cpu_to_be16(alloc_block);
2368c2ecf20Sopenharmony_ci			} else
2378c2ecf20Sopenharmony_ci				block_count += count;
2388c2ecf20Sopenharmony_ci			extent->count = cpu_to_be16(block_count);
2398c2ecf20Sopenharmony_ci			return 0;
2408c2ecf20Sopenharmony_ci		} else if (offset < count)
2418c2ecf20Sopenharmony_ci			break;
2428c2ecf20Sopenharmony_ci		offset -= count;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci	/* panic? */
2458c2ecf20Sopenharmony_ci	return -EIO;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent,
2498c2ecf20Sopenharmony_ci			    u16 offset, u16 block_nr)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	u16 count, start;
2528c2ecf20Sopenharmony_ci	int i;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	hfs_dump_extent(extent);
2558c2ecf20Sopenharmony_ci	for (i = 0; i < 3; extent++, i++) {
2568c2ecf20Sopenharmony_ci		count = be16_to_cpu(extent->count);
2578c2ecf20Sopenharmony_ci		if (offset == count)
2588c2ecf20Sopenharmony_ci			goto found;
2598c2ecf20Sopenharmony_ci		else if (offset < count)
2608c2ecf20Sopenharmony_ci			break;
2618c2ecf20Sopenharmony_ci		offset -= count;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	/* panic? */
2648c2ecf20Sopenharmony_ci	return -EIO;
2658c2ecf20Sopenharmony_cifound:
2668c2ecf20Sopenharmony_ci	for (;;) {
2678c2ecf20Sopenharmony_ci		start = be16_to_cpu(extent->block);
2688c2ecf20Sopenharmony_ci		if (count <= block_nr) {
2698c2ecf20Sopenharmony_ci			hfs_clear_vbm_bits(sb, start, count);
2708c2ecf20Sopenharmony_ci			extent->block = 0;
2718c2ecf20Sopenharmony_ci			extent->count = 0;
2728c2ecf20Sopenharmony_ci			block_nr -= count;
2738c2ecf20Sopenharmony_ci		} else {
2748c2ecf20Sopenharmony_ci			count -= block_nr;
2758c2ecf20Sopenharmony_ci			hfs_clear_vbm_bits(sb, start + count, block_nr);
2768c2ecf20Sopenharmony_ci			extent->count = cpu_to_be16(count);
2778c2ecf20Sopenharmony_ci			block_nr = 0;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci		if (!block_nr || !i)
2808c2ecf20Sopenharmony_ci			return 0;
2818c2ecf20Sopenharmony_ci		i--;
2828c2ecf20Sopenharmony_ci		extent--;
2838c2ecf20Sopenharmony_ci		count = be16_to_cpu(extent->count);
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ciint hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
2908c2ecf20Sopenharmony_ci	u32 total_blocks, blocks, start;
2918c2ecf20Sopenharmony_ci	u32 cnid = be32_to_cpu(file->FlNum);
2928c2ecf20Sopenharmony_ci	struct hfs_extent *extent;
2938c2ecf20Sopenharmony_ci	int res, i;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (type == HFS_FK_DATA) {
2968c2ecf20Sopenharmony_ci		total_blocks = be32_to_cpu(file->PyLen);
2978c2ecf20Sopenharmony_ci		extent = file->ExtRec;
2988c2ecf20Sopenharmony_ci	} else {
2998c2ecf20Sopenharmony_ci		total_blocks = be32_to_cpu(file->RPyLen);
3008c2ecf20Sopenharmony_ci		extent = file->RExtRec;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	total_blocks /= HFS_SB(sb)->alloc_blksz;
3038c2ecf20Sopenharmony_ci	if (!total_blocks)
3048c2ecf20Sopenharmony_ci		return 0;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	blocks = 0;
3078c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
3088c2ecf20Sopenharmony_ci		blocks += be16_to_cpu(extent[i].count);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	res = hfs_free_extents(sb, extent, blocks, blocks);
3118c2ecf20Sopenharmony_ci	if (res)
3128c2ecf20Sopenharmony_ci		return res;
3138c2ecf20Sopenharmony_ci	if (total_blocks == blocks)
3148c2ecf20Sopenharmony_ci		return 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
3178c2ecf20Sopenharmony_ci	if (res)
3188c2ecf20Sopenharmony_ci		return res;
3198c2ecf20Sopenharmony_ci	do {
3208c2ecf20Sopenharmony_ci		res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type);
3218c2ecf20Sopenharmony_ci		if (res)
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci		start = be16_to_cpu(fd.key->ext.FABN);
3248c2ecf20Sopenharmony_ci		hfs_free_extents(sb, extent, total_blocks - start, total_blocks);
3258c2ecf20Sopenharmony_ci		hfs_brec_remove(&fd);
3268c2ecf20Sopenharmony_ci		total_blocks = start;
3278c2ecf20Sopenharmony_ci	} while (total_blocks > blocks);
3288c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return res;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci/*
3348c2ecf20Sopenharmony_ci * hfs_get_block
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_ciint hfs_get_block(struct inode *inode, sector_t block,
3378c2ecf20Sopenharmony_ci		  struct buffer_head *bh_result, int create)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct super_block *sb;
3408c2ecf20Sopenharmony_ci	u16 dblock, ablock;
3418c2ecf20Sopenharmony_ci	int res;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	sb = inode->i_sb;
3448c2ecf20Sopenharmony_ci	/* Convert inode block to disk allocation block */
3458c2ecf20Sopenharmony_ci	ablock = (u32)block / HFS_SB(sb)->fs_div;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (block >= HFS_I(inode)->fs_blocks) {
3488c2ecf20Sopenharmony_ci		if (!create)
3498c2ecf20Sopenharmony_ci			return 0;
3508c2ecf20Sopenharmony_ci		if (block > HFS_I(inode)->fs_blocks)
3518c2ecf20Sopenharmony_ci			return -EIO;
3528c2ecf20Sopenharmony_ci		if (ablock >= HFS_I(inode)->alloc_blocks) {
3538c2ecf20Sopenharmony_ci			res = hfs_extend_file(inode);
3548c2ecf20Sopenharmony_ci			if (res)
3558c2ecf20Sopenharmony_ci				return res;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci	} else
3588c2ecf20Sopenharmony_ci		create = 0;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (ablock < HFS_I(inode)->first_blocks) {
3618c2ecf20Sopenharmony_ci		dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock);
3628c2ecf20Sopenharmony_ci		goto done;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	mutex_lock(&HFS_I(inode)->extents_lock);
3668c2ecf20Sopenharmony_ci	res = hfs_ext_read_extent(inode, ablock);
3678c2ecf20Sopenharmony_ci	if (!res)
3688c2ecf20Sopenharmony_ci		dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents,
3698c2ecf20Sopenharmony_ci					    ablock - HFS_I(inode)->cached_start);
3708c2ecf20Sopenharmony_ci	else {
3718c2ecf20Sopenharmony_ci		mutex_unlock(&HFS_I(inode)->extents_lock);
3728c2ecf20Sopenharmony_ci		return -EIO;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	mutex_unlock(&HFS_I(inode)->extents_lock);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cidone:
3778c2ecf20Sopenharmony_ci	map_bh(bh_result, sb, HFS_SB(sb)->fs_start +
3788c2ecf20Sopenharmony_ci	       dblock * HFS_SB(sb)->fs_div +
3798c2ecf20Sopenharmony_ci	       (u32)block % HFS_SB(sb)->fs_div);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (create) {
3828c2ecf20Sopenharmony_ci		set_buffer_new(bh_result);
3838c2ecf20Sopenharmony_ci		HFS_I(inode)->phys_size += sb->s_blocksize;
3848c2ecf20Sopenharmony_ci		HFS_I(inode)->fs_blocks++;
3858c2ecf20Sopenharmony_ci		inode_add_bytes(inode, sb->s_blocksize);
3868c2ecf20Sopenharmony_ci		mark_inode_dirty(inode);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ciint hfs_extend_file(struct inode *inode)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
3948c2ecf20Sopenharmony_ci	u32 start, len, goal;
3958c2ecf20Sopenharmony_ci	int res;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	mutex_lock(&HFS_I(inode)->extents_lock);
3988c2ecf20Sopenharmony_ci	if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks)
3998c2ecf20Sopenharmony_ci		goal = hfs_ext_lastblock(HFS_I(inode)->first_extents);
4008c2ecf20Sopenharmony_ci	else {
4018c2ecf20Sopenharmony_ci		res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks);
4028c2ecf20Sopenharmony_ci		if (res)
4038c2ecf20Sopenharmony_ci			goto out;
4048c2ecf20Sopenharmony_ci		goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents);
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	len = HFS_I(inode)->clump_blocks;
4088c2ecf20Sopenharmony_ci	start = hfs_vbm_search_free(sb, goal, &len);
4098c2ecf20Sopenharmony_ci	if (!len) {
4108c2ecf20Sopenharmony_ci		res = -ENOSPC;
4118c2ecf20Sopenharmony_ci		goto out;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
4158c2ecf20Sopenharmony_ci	if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) {
4168c2ecf20Sopenharmony_ci		if (!HFS_I(inode)->first_blocks) {
4178c2ecf20Sopenharmony_ci			hfs_dbg(EXTENT, "first extents\n");
4188c2ecf20Sopenharmony_ci			/* no extents yet */
4198c2ecf20Sopenharmony_ci			HFS_I(inode)->first_extents[0].block = cpu_to_be16(start);
4208c2ecf20Sopenharmony_ci			HFS_I(inode)->first_extents[0].count = cpu_to_be16(len);
4218c2ecf20Sopenharmony_ci			res = 0;
4228c2ecf20Sopenharmony_ci		} else {
4238c2ecf20Sopenharmony_ci			/* try to append to extents in inode */
4248c2ecf20Sopenharmony_ci			res = hfs_add_extent(HFS_I(inode)->first_extents,
4258c2ecf20Sopenharmony_ci					     HFS_I(inode)->alloc_blocks,
4268c2ecf20Sopenharmony_ci					     start, len);
4278c2ecf20Sopenharmony_ci			if (res == -ENOSPC)
4288c2ecf20Sopenharmony_ci				goto insert_extent;
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci		if (!res) {
4318c2ecf20Sopenharmony_ci			hfs_dump_extent(HFS_I(inode)->first_extents);
4328c2ecf20Sopenharmony_ci			HFS_I(inode)->first_blocks += len;
4338c2ecf20Sopenharmony_ci		}
4348c2ecf20Sopenharmony_ci	} else {
4358c2ecf20Sopenharmony_ci		res = hfs_add_extent(HFS_I(inode)->cached_extents,
4368c2ecf20Sopenharmony_ci				     HFS_I(inode)->alloc_blocks -
4378c2ecf20Sopenharmony_ci				     HFS_I(inode)->cached_start,
4388c2ecf20Sopenharmony_ci				     start, len);
4398c2ecf20Sopenharmony_ci		if (!res) {
4408c2ecf20Sopenharmony_ci			hfs_dump_extent(HFS_I(inode)->cached_extents);
4418c2ecf20Sopenharmony_ci			HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
4428c2ecf20Sopenharmony_ci			HFS_I(inode)->cached_blocks += len;
4438c2ecf20Sopenharmony_ci		} else if (res == -ENOSPC)
4448c2ecf20Sopenharmony_ci			goto insert_extent;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ciout:
4478c2ecf20Sopenharmony_ci	mutex_unlock(&HFS_I(inode)->extents_lock);
4488c2ecf20Sopenharmony_ci	if (!res) {
4498c2ecf20Sopenharmony_ci		HFS_I(inode)->alloc_blocks += len;
4508c2ecf20Sopenharmony_ci		mark_inode_dirty(inode);
4518c2ecf20Sopenharmony_ci		if (inode->i_ino < HFS_FIRSTUSER_CNID)
4528c2ecf20Sopenharmony_ci			set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags);
4538c2ecf20Sopenharmony_ci		set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
4548c2ecf20Sopenharmony_ci		hfs_mark_mdb_dirty(sb);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci	return res;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ciinsert_extent:
4598c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "insert new extent\n");
4608c2ecf20Sopenharmony_ci	res = hfs_ext_write_extent(inode);
4618c2ecf20Sopenharmony_ci	if (res)
4628c2ecf20Sopenharmony_ci		goto out;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
4658c2ecf20Sopenharmony_ci	HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start);
4668c2ecf20Sopenharmony_ci	HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len);
4678c2ecf20Sopenharmony_ci	hfs_dump_extent(HFS_I(inode)->cached_extents);
4688c2ecf20Sopenharmony_ci	HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW;
4698c2ecf20Sopenharmony_ci	HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks;
4708c2ecf20Sopenharmony_ci	HFS_I(inode)->cached_blocks = len;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	res = 0;
4738c2ecf20Sopenharmony_ci	goto out;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_civoid hfs_file_truncate(struct inode *inode)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
4798c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
4808c2ecf20Sopenharmony_ci	u16 blk_cnt, alloc_cnt, start;
4818c2ecf20Sopenharmony_ci	u32 size;
4828c2ecf20Sopenharmony_ci	int res;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	hfs_dbg(INODE, "truncate: %lu, %Lu -> %Lu\n",
4858c2ecf20Sopenharmony_ci		inode->i_ino, (long long)HFS_I(inode)->phys_size,
4868c2ecf20Sopenharmony_ci		inode->i_size);
4878c2ecf20Sopenharmony_ci	if (inode->i_size > HFS_I(inode)->phys_size) {
4888c2ecf20Sopenharmony_ci		struct address_space *mapping = inode->i_mapping;
4898c2ecf20Sopenharmony_ci		void *fsdata;
4908c2ecf20Sopenharmony_ci		struct page *page;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		/* XXX: Can use generic_cont_expand? */
4938c2ecf20Sopenharmony_ci		size = inode->i_size - 1;
4948c2ecf20Sopenharmony_ci		res = pagecache_write_begin(NULL, mapping, size+1, 0, 0,
4958c2ecf20Sopenharmony_ci					    &page, &fsdata);
4968c2ecf20Sopenharmony_ci		if (!res) {
4978c2ecf20Sopenharmony_ci			res = pagecache_write_end(NULL, mapping, size+1, 0, 0,
4988c2ecf20Sopenharmony_ci					page, fsdata);
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci		if (res)
5018c2ecf20Sopenharmony_ci			inode->i_size = HFS_I(inode)->phys_size;
5028c2ecf20Sopenharmony_ci		return;
5038c2ecf20Sopenharmony_ci	} else if (inode->i_size == HFS_I(inode)->phys_size)
5048c2ecf20Sopenharmony_ci		return;
5058c2ecf20Sopenharmony_ci	size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1;
5068c2ecf20Sopenharmony_ci	blk_cnt = size / HFS_SB(sb)->alloc_blksz;
5078c2ecf20Sopenharmony_ci	alloc_cnt = HFS_I(inode)->alloc_blocks;
5088c2ecf20Sopenharmony_ci	if (blk_cnt == alloc_cnt)
5098c2ecf20Sopenharmony_ci		goto out;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	mutex_lock(&HFS_I(inode)->extents_lock);
5128c2ecf20Sopenharmony_ci	res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
5138c2ecf20Sopenharmony_ci	if (res) {
5148c2ecf20Sopenharmony_ci		mutex_unlock(&HFS_I(inode)->extents_lock);
5158c2ecf20Sopenharmony_ci		/* XXX: We lack error handling of hfs_file_truncate() */
5168c2ecf20Sopenharmony_ci		return;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci	while (1) {
5198c2ecf20Sopenharmony_ci		if (alloc_cnt == HFS_I(inode)->first_blocks) {
5208c2ecf20Sopenharmony_ci			hfs_free_extents(sb, HFS_I(inode)->first_extents,
5218c2ecf20Sopenharmony_ci					 alloc_cnt, alloc_cnt - blk_cnt);
5228c2ecf20Sopenharmony_ci			hfs_dump_extent(HFS_I(inode)->first_extents);
5238c2ecf20Sopenharmony_ci			HFS_I(inode)->first_blocks = blk_cnt;
5248c2ecf20Sopenharmony_ci			break;
5258c2ecf20Sopenharmony_ci		}
5268c2ecf20Sopenharmony_ci		res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt);
5278c2ecf20Sopenharmony_ci		if (res)
5288c2ecf20Sopenharmony_ci			break;
5298c2ecf20Sopenharmony_ci		start = HFS_I(inode)->cached_start;
5308c2ecf20Sopenharmony_ci		hfs_free_extents(sb, HFS_I(inode)->cached_extents,
5318c2ecf20Sopenharmony_ci				 alloc_cnt - start, alloc_cnt - blk_cnt);
5328c2ecf20Sopenharmony_ci		hfs_dump_extent(HFS_I(inode)->cached_extents);
5338c2ecf20Sopenharmony_ci		if (blk_cnt > start) {
5348c2ecf20Sopenharmony_ci			HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
5358c2ecf20Sopenharmony_ci			break;
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci		alloc_cnt = start;
5388c2ecf20Sopenharmony_ci		HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
5398c2ecf20Sopenharmony_ci		HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
5408c2ecf20Sopenharmony_ci		hfs_brec_remove(&fd);
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
5438c2ecf20Sopenharmony_ci	mutex_unlock(&HFS_I(inode)->extents_lock);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	HFS_I(inode)->alloc_blocks = blk_cnt;
5468c2ecf20Sopenharmony_ciout:
5478c2ecf20Sopenharmony_ci	HFS_I(inode)->phys_size = inode->i_size;
5488c2ecf20Sopenharmony_ci	HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
5498c2ecf20Sopenharmony_ci	inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits);
5508c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
5518c2ecf20Sopenharmony_ci}
552