18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/hfsplus/extents.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2001
68c2ecf20Sopenharmony_ci * Brad Boyer (flar@allandria.com)
78c2ecf20Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Handling of Extents both in catalog and extents overflow trees
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "hfsplus_fs.h"
178c2ecf20Sopenharmony_ci#include "hfsplus_raw.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Compare two extents keys, returns 0 on same, pos/neg for difference */
208c2ecf20Sopenharmony_ciint hfsplus_ext_cmp_key(const hfsplus_btree_key *k1,
218c2ecf20Sopenharmony_ci			const hfsplus_btree_key *k2)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	__be32 k1id, k2id;
248c2ecf20Sopenharmony_ci	__be32 k1s, k2s;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	k1id = k1->ext.cnid;
278c2ecf20Sopenharmony_ci	k2id = k2->ext.cnid;
288c2ecf20Sopenharmony_ci	if (k1id != k2id)
298c2ecf20Sopenharmony_ci		return be32_to_cpu(k1id) < be32_to_cpu(k2id) ? -1 : 1;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (k1->ext.fork_type != k2->ext.fork_type)
328c2ecf20Sopenharmony_ci		return k1->ext.fork_type < k2->ext.fork_type ? -1 : 1;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	k1s = k1->ext.start_block;
358c2ecf20Sopenharmony_ci	k2s = k2->ext.start_block;
368c2ecf20Sopenharmony_ci	if (k1s == k2s)
378c2ecf20Sopenharmony_ci		return 0;
388c2ecf20Sopenharmony_ci	return be32_to_cpu(k1s) < be32_to_cpu(k2s) ? -1 : 1;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void hfsplus_ext_build_key(hfsplus_btree_key *key, u32 cnid,
428c2ecf20Sopenharmony_ci				  u32 block, u8 type)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	key->key_len = cpu_to_be16(HFSPLUS_EXT_KEYLEN - 2);
458c2ecf20Sopenharmony_ci	key->ext.cnid = cpu_to_be32(cnid);
468c2ecf20Sopenharmony_ci	key->ext.start_block = cpu_to_be32(block);
478c2ecf20Sopenharmony_ci	key->ext.fork_type = type;
488c2ecf20Sopenharmony_ci	key->ext.pad = 0;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic u32 hfsplus_ext_find_block(struct hfsplus_extent *ext, u32 off)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int i;
548c2ecf20Sopenharmony_ci	u32 count;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	for (i = 0; i < 8; ext++, i++) {
578c2ecf20Sopenharmony_ci		count = be32_to_cpu(ext->block_count);
588c2ecf20Sopenharmony_ci		if (off < count)
598c2ecf20Sopenharmony_ci			return be32_to_cpu(ext->start_block) + off;
608c2ecf20Sopenharmony_ci		off -= count;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	/* panic? */
638c2ecf20Sopenharmony_ci	return 0;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int hfsplus_ext_block_count(struct hfsplus_extent *ext)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int i;
698c2ecf20Sopenharmony_ci	u32 count = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	for (i = 0; i < 8; ext++, i++)
728c2ecf20Sopenharmony_ci		count += be32_to_cpu(ext->block_count);
738c2ecf20Sopenharmony_ci	return count;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic u32 hfsplus_ext_lastblock(struct hfsplus_extent *ext)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int i;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	ext += 7;
818c2ecf20Sopenharmony_ci	for (i = 0; i < 7; ext--, i++)
828c2ecf20Sopenharmony_ci		if (ext->block_count)
838c2ecf20Sopenharmony_ci			break;
848c2ecf20Sopenharmony_ci	return be32_to_cpu(ext->start_block) + be32_to_cpu(ext->block_count);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int __hfsplus_ext_write_extent(struct inode *inode,
888c2ecf20Sopenharmony_ci		struct hfs_find_data *fd)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
918c2ecf20Sopenharmony_ci	int res;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	WARN_ON(!mutex_is_locked(&hip->extents_lock));
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	hfsplus_ext_build_key(fd->search_key, inode->i_ino, hip->cached_start,
968c2ecf20Sopenharmony_ci			      HFSPLUS_IS_RSRC(inode) ?
978c2ecf20Sopenharmony_ci				HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	res = hfs_brec_find(fd, hfs_find_rec_by_key);
1008c2ecf20Sopenharmony_ci	if (hip->extent_state & HFSPLUS_EXT_NEW) {
1018c2ecf20Sopenharmony_ci		if (res != -ENOENT)
1028c2ecf20Sopenharmony_ci			return res;
1038c2ecf20Sopenharmony_ci		/* Fail early and avoid ENOSPC during the btree operation */
1048c2ecf20Sopenharmony_ci		res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
1058c2ecf20Sopenharmony_ci		if (res)
1068c2ecf20Sopenharmony_ci			return res;
1078c2ecf20Sopenharmony_ci		hfs_brec_insert(fd, hip->cached_extents,
1088c2ecf20Sopenharmony_ci				sizeof(hfsplus_extent_rec));
1098c2ecf20Sopenharmony_ci		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1108c2ecf20Sopenharmony_ci	} else {
1118c2ecf20Sopenharmony_ci		if (res)
1128c2ecf20Sopenharmony_ci			return res;
1138c2ecf20Sopenharmony_ci		hfs_bnode_write(fd->bnode, hip->cached_extents,
1148c2ecf20Sopenharmony_ci				fd->entryoffset, fd->entrylength);
1158c2ecf20Sopenharmony_ci		hip->extent_state &= ~HFSPLUS_EXT_DIRTY;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/*
1198c2ecf20Sopenharmony_ci	 * We can't just use hfsplus_mark_inode_dirty here, because we
1208c2ecf20Sopenharmony_ci	 * also get called from hfsplus_write_inode, which should not
1218c2ecf20Sopenharmony_ci	 * redirty the inode.  Instead the callers have to be careful
1228c2ecf20Sopenharmony_ci	 * to explicily mark the inode dirty, too.
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int hfsplus_ext_write_extent_locked(struct inode *inode)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	int res = 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) {
1348c2ecf20Sopenharmony_ci		struct hfs_find_data fd;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
1378c2ecf20Sopenharmony_ci		if (res)
1388c2ecf20Sopenharmony_ci			return res;
1398c2ecf20Sopenharmony_ci		res = __hfsplus_ext_write_extent(inode, &fd);
1408c2ecf20Sopenharmony_ci		hfs_find_exit(&fd);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	return res;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciint hfsplus_ext_write_extent(struct inode *inode)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int res;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	mutex_lock(&HFSPLUS_I(inode)->extents_lock);
1508c2ecf20Sopenharmony_ci	res = hfsplus_ext_write_extent_locked(inode);
1518c2ecf20Sopenharmony_ci	mutex_unlock(&HFSPLUS_I(inode)->extents_lock);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return res;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd,
1578c2ecf20Sopenharmony_ci					    struct hfsplus_extent *extent,
1588c2ecf20Sopenharmony_ci					    u32 cnid, u32 block, u8 type)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int res;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	hfsplus_ext_build_key(fd->search_key, cnid, block, type);
1638c2ecf20Sopenharmony_ci	fd->key->ext.cnid = 0;
1648c2ecf20Sopenharmony_ci	res = hfs_brec_find(fd, hfs_find_rec_by_key);
1658c2ecf20Sopenharmony_ci	if (res && res != -ENOENT)
1668c2ecf20Sopenharmony_ci		return res;
1678c2ecf20Sopenharmony_ci	if (fd->key->ext.cnid != fd->search_key->ext.cnid ||
1688c2ecf20Sopenharmony_ci	    fd->key->ext.fork_type != fd->search_key->ext.fork_type)
1698c2ecf20Sopenharmony_ci		return -ENOENT;
1708c2ecf20Sopenharmony_ci	if (fd->entrylength != sizeof(hfsplus_extent_rec))
1718c2ecf20Sopenharmony_ci		return -EIO;
1728c2ecf20Sopenharmony_ci	hfs_bnode_read(fd->bnode, extent, fd->entryoffset,
1738c2ecf20Sopenharmony_ci		sizeof(hfsplus_extent_rec));
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd,
1788c2ecf20Sopenharmony_ci		struct inode *inode, u32 block)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1818c2ecf20Sopenharmony_ci	int res;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	WARN_ON(!mutex_is_locked(&hip->extents_lock));
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (hip->extent_state & HFSPLUS_EXT_DIRTY) {
1868c2ecf20Sopenharmony_ci		res = __hfsplus_ext_write_extent(inode, fd);
1878c2ecf20Sopenharmony_ci		if (res)
1888c2ecf20Sopenharmony_ci			return res;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	res = __hfsplus_ext_read_extent(fd, hip->cached_extents, inode->i_ino,
1928c2ecf20Sopenharmony_ci					block, HFSPLUS_IS_RSRC(inode) ?
1938c2ecf20Sopenharmony_ci						HFSPLUS_TYPE_RSRC :
1948c2ecf20Sopenharmony_ci						HFSPLUS_TYPE_DATA);
1958c2ecf20Sopenharmony_ci	if (!res) {
1968c2ecf20Sopenharmony_ci		hip->cached_start = be32_to_cpu(fd->key->ext.start_block);
1978c2ecf20Sopenharmony_ci		hip->cached_blocks =
1988c2ecf20Sopenharmony_ci			hfsplus_ext_block_count(hip->cached_extents);
1998c2ecf20Sopenharmony_ci	} else {
2008c2ecf20Sopenharmony_ci		hip->cached_start = hip->cached_blocks = 0;
2018c2ecf20Sopenharmony_ci		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	return res;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int hfsplus_ext_read_extent(struct inode *inode, u32 block)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
2098c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
2108c2ecf20Sopenharmony_ci	int res;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (block >= hip->cached_start &&
2138c2ecf20Sopenharmony_ci	    block < hip->cached_start + hip->cached_blocks)
2148c2ecf20Sopenharmony_ci		return 0;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
2178c2ecf20Sopenharmony_ci	if (!res) {
2188c2ecf20Sopenharmony_ci		res = __hfsplus_ext_cache_extent(&fd, inode, block);
2198c2ecf20Sopenharmony_ci		hfs_find_exit(&fd);
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci	return res;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* Get a block at iblock for inode, possibly allocating if create */
2258c2ecf20Sopenharmony_ciint hfsplus_get_block(struct inode *inode, sector_t iblock,
2268c2ecf20Sopenharmony_ci		      struct buffer_head *bh_result, int create)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
2298c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
2308c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
2318c2ecf20Sopenharmony_ci	int res = -EIO;
2328c2ecf20Sopenharmony_ci	u32 ablock, dblock, mask;
2338c2ecf20Sopenharmony_ci	sector_t sector;
2348c2ecf20Sopenharmony_ci	int was_dirty = 0;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Convert inode block to disk allocation block */
2378c2ecf20Sopenharmony_ci	ablock = iblock >> sbi->fs_shift;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (iblock >= hip->fs_blocks) {
2408c2ecf20Sopenharmony_ci		if (!create)
2418c2ecf20Sopenharmony_ci			return 0;
2428c2ecf20Sopenharmony_ci		if (iblock > hip->fs_blocks)
2438c2ecf20Sopenharmony_ci			return -EIO;
2448c2ecf20Sopenharmony_ci		if (ablock >= hip->alloc_blocks) {
2458c2ecf20Sopenharmony_ci			res = hfsplus_file_extend(inode, false);
2468c2ecf20Sopenharmony_ci			if (res)
2478c2ecf20Sopenharmony_ci				return res;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci	} else
2508c2ecf20Sopenharmony_ci		create = 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (ablock < hip->first_blocks) {
2538c2ecf20Sopenharmony_ci		dblock = hfsplus_ext_find_block(hip->first_extents, ablock);
2548c2ecf20Sopenharmony_ci		goto done;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (inode->i_ino == HFSPLUS_EXT_CNID)
2588c2ecf20Sopenharmony_ci		return -EIO;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	mutex_lock(&hip->extents_lock);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * hfsplus_ext_read_extent will write out a cached extent into
2648c2ecf20Sopenharmony_ci	 * the extents btree.  In that case we may have to mark the inode
2658c2ecf20Sopenharmony_ci	 * dirty even for a pure read of an extent here.
2668c2ecf20Sopenharmony_ci	 */
2678c2ecf20Sopenharmony_ci	was_dirty = (hip->extent_state & HFSPLUS_EXT_DIRTY);
2688c2ecf20Sopenharmony_ci	res = hfsplus_ext_read_extent(inode, ablock);
2698c2ecf20Sopenharmony_ci	if (res) {
2708c2ecf20Sopenharmony_ci		mutex_unlock(&hip->extents_lock);
2718c2ecf20Sopenharmony_ci		return -EIO;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci	dblock = hfsplus_ext_find_block(hip->cached_extents,
2748c2ecf20Sopenharmony_ci					ablock - hip->cached_start);
2758c2ecf20Sopenharmony_ci	mutex_unlock(&hip->extents_lock);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cidone:
2788c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "get_block(%lu): %llu - %u\n",
2798c2ecf20Sopenharmony_ci		inode->i_ino, (long long)iblock, dblock);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	mask = (1 << sbi->fs_shift) - 1;
2828c2ecf20Sopenharmony_ci	sector = ((sector_t)dblock << sbi->fs_shift) +
2838c2ecf20Sopenharmony_ci		  sbi->blockoffset + (iblock & mask);
2848c2ecf20Sopenharmony_ci	map_bh(bh_result, sb, sector);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (create) {
2878c2ecf20Sopenharmony_ci		set_buffer_new(bh_result);
2888c2ecf20Sopenharmony_ci		hip->phys_size += sb->s_blocksize;
2898c2ecf20Sopenharmony_ci		hip->fs_blocks++;
2908c2ecf20Sopenharmony_ci		inode_add_bytes(inode, sb->s_blocksize);
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	if (create || was_dirty)
2938c2ecf20Sopenharmony_ci		mark_inode_dirty(inode);
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic void hfsplus_dump_extent(struct hfsplus_extent *extent)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	int i;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "   ");
3028c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
3038c2ecf20Sopenharmony_ci		hfs_dbg_cont(EXTENT, " %u:%u",
3048c2ecf20Sopenharmony_ci			     be32_to_cpu(extent[i].start_block),
3058c2ecf20Sopenharmony_ci			     be32_to_cpu(extent[i].block_count));
3068c2ecf20Sopenharmony_ci	hfs_dbg_cont(EXTENT, "\n");
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int hfsplus_add_extent(struct hfsplus_extent *extent, u32 offset,
3108c2ecf20Sopenharmony_ci			      u32 alloc_block, u32 block_count)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	u32 count, start;
3138c2ecf20Sopenharmony_ci	int i;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	hfsplus_dump_extent(extent);
3168c2ecf20Sopenharmony_ci	for (i = 0; i < 8; extent++, i++) {
3178c2ecf20Sopenharmony_ci		count = be32_to_cpu(extent->block_count);
3188c2ecf20Sopenharmony_ci		if (offset == count) {
3198c2ecf20Sopenharmony_ci			start = be32_to_cpu(extent->start_block);
3208c2ecf20Sopenharmony_ci			if (alloc_block != start + count) {
3218c2ecf20Sopenharmony_ci				if (++i >= 8)
3228c2ecf20Sopenharmony_ci					return -ENOSPC;
3238c2ecf20Sopenharmony_ci				extent++;
3248c2ecf20Sopenharmony_ci				extent->start_block = cpu_to_be32(alloc_block);
3258c2ecf20Sopenharmony_ci			} else
3268c2ecf20Sopenharmony_ci				block_count += count;
3278c2ecf20Sopenharmony_ci			extent->block_count = cpu_to_be32(block_count);
3288c2ecf20Sopenharmony_ci			return 0;
3298c2ecf20Sopenharmony_ci		} else if (offset < count)
3308c2ecf20Sopenharmony_ci			break;
3318c2ecf20Sopenharmony_ci		offset -= count;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	/* panic? */
3348c2ecf20Sopenharmony_ci	return -EIO;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int hfsplus_free_extents(struct super_block *sb,
3388c2ecf20Sopenharmony_ci				struct hfsplus_extent *extent,
3398c2ecf20Sopenharmony_ci				u32 offset, u32 block_nr)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	u32 count, start;
3428c2ecf20Sopenharmony_ci	int i;
3438c2ecf20Sopenharmony_ci	int err = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* Mapping the allocation file may lock the extent tree */
3468c2ecf20Sopenharmony_ci	WARN_ON(mutex_is_locked(&HFSPLUS_SB(sb)->ext_tree->tree_lock));
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	hfsplus_dump_extent(extent);
3498c2ecf20Sopenharmony_ci	for (i = 0; i < 8; extent++, i++) {
3508c2ecf20Sopenharmony_ci		count = be32_to_cpu(extent->block_count);
3518c2ecf20Sopenharmony_ci		if (offset == count)
3528c2ecf20Sopenharmony_ci			goto found;
3538c2ecf20Sopenharmony_ci		else if (offset < count)
3548c2ecf20Sopenharmony_ci			break;
3558c2ecf20Sopenharmony_ci		offset -= count;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	/* panic? */
3588c2ecf20Sopenharmony_ci	return -EIO;
3598c2ecf20Sopenharmony_cifound:
3608c2ecf20Sopenharmony_ci	for (;;) {
3618c2ecf20Sopenharmony_ci		start = be32_to_cpu(extent->start_block);
3628c2ecf20Sopenharmony_ci		if (count <= block_nr) {
3638c2ecf20Sopenharmony_ci			err = hfsplus_block_free(sb, start, count);
3648c2ecf20Sopenharmony_ci			if (err) {
3658c2ecf20Sopenharmony_ci				pr_err("can't free extent\n");
3668c2ecf20Sopenharmony_ci				hfs_dbg(EXTENT, " start: %u count: %u\n",
3678c2ecf20Sopenharmony_ci					start, count);
3688c2ecf20Sopenharmony_ci			}
3698c2ecf20Sopenharmony_ci			extent->block_count = 0;
3708c2ecf20Sopenharmony_ci			extent->start_block = 0;
3718c2ecf20Sopenharmony_ci			block_nr -= count;
3728c2ecf20Sopenharmony_ci		} else {
3738c2ecf20Sopenharmony_ci			count -= block_nr;
3748c2ecf20Sopenharmony_ci			err = hfsplus_block_free(sb, start + count, block_nr);
3758c2ecf20Sopenharmony_ci			if (err) {
3768c2ecf20Sopenharmony_ci				pr_err("can't free extent\n");
3778c2ecf20Sopenharmony_ci				hfs_dbg(EXTENT, " start: %u count: %u\n",
3788c2ecf20Sopenharmony_ci					start, count);
3798c2ecf20Sopenharmony_ci			}
3808c2ecf20Sopenharmony_ci			extent->block_count = cpu_to_be32(count);
3818c2ecf20Sopenharmony_ci			block_nr = 0;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci		if (!block_nr || !i) {
3848c2ecf20Sopenharmony_ci			/*
3858c2ecf20Sopenharmony_ci			 * Try to free all extents and
3868c2ecf20Sopenharmony_ci			 * return only last error
3878c2ecf20Sopenharmony_ci			 */
3888c2ecf20Sopenharmony_ci			return err;
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci		i--;
3918c2ecf20Sopenharmony_ci		extent--;
3928c2ecf20Sopenharmony_ci		count = be32_to_cpu(extent->block_count);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ciint hfsplus_free_fork(struct super_block *sb, u32 cnid,
3978c2ecf20Sopenharmony_ci		struct hfsplus_fork_raw *fork, int type)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
4008c2ecf20Sopenharmony_ci	hfsplus_extent_rec ext_entry;
4018c2ecf20Sopenharmony_ci	u32 total_blocks, blocks, start;
4028c2ecf20Sopenharmony_ci	int res, i;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	total_blocks = be32_to_cpu(fork->total_blocks);
4058c2ecf20Sopenharmony_ci	if (!total_blocks)
4068c2ecf20Sopenharmony_ci		return 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	blocks = 0;
4098c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
4108c2ecf20Sopenharmony_ci		blocks += be32_to_cpu(fork->extents[i].block_count);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	res = hfsplus_free_extents(sb, fork->extents, blocks, blocks);
4138c2ecf20Sopenharmony_ci	if (res)
4148c2ecf20Sopenharmony_ci		return res;
4158c2ecf20Sopenharmony_ci	if (total_blocks == blocks)
4168c2ecf20Sopenharmony_ci		return 0;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
4198c2ecf20Sopenharmony_ci	if (res)
4208c2ecf20Sopenharmony_ci		return res;
4218c2ecf20Sopenharmony_ci	do {
4228c2ecf20Sopenharmony_ci		res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid,
4238c2ecf20Sopenharmony_ci						total_blocks, type);
4248c2ecf20Sopenharmony_ci		if (res)
4258c2ecf20Sopenharmony_ci			break;
4268c2ecf20Sopenharmony_ci		start = be32_to_cpu(fd.key->ext.start_block);
4278c2ecf20Sopenharmony_ci		hfs_brec_remove(&fd);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		mutex_unlock(&fd.tree->tree_lock);
4308c2ecf20Sopenharmony_ci		hfsplus_free_extents(sb, ext_entry, total_blocks - start,
4318c2ecf20Sopenharmony_ci				     total_blocks);
4328c2ecf20Sopenharmony_ci		total_blocks = start;
4338c2ecf20Sopenharmony_ci		mutex_lock(&fd.tree->tree_lock);
4348c2ecf20Sopenharmony_ci	} while (total_blocks > blocks);
4358c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return res;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ciint hfsplus_file_extend(struct inode *inode, bool zeroout)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
4438c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
4448c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
4458c2ecf20Sopenharmony_ci	u32 start, len, goal;
4468c2ecf20Sopenharmony_ci	int res;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (sbi->alloc_file->i_size * 8 <
4498c2ecf20Sopenharmony_ci	    sbi->total_blocks - sbi->free_blocks + 8) {
4508c2ecf20Sopenharmony_ci		/* extend alloc file */
4518c2ecf20Sopenharmony_ci		pr_err("extend alloc file! (%llu,%u,%u)\n",
4528c2ecf20Sopenharmony_ci		       sbi->alloc_file->i_size * 8,
4538c2ecf20Sopenharmony_ci		       sbi->total_blocks, sbi->free_blocks);
4548c2ecf20Sopenharmony_ci		return -ENOSPC;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	mutex_lock(&hip->extents_lock);
4588c2ecf20Sopenharmony_ci	if (hip->alloc_blocks == hip->first_blocks)
4598c2ecf20Sopenharmony_ci		goal = hfsplus_ext_lastblock(hip->first_extents);
4608c2ecf20Sopenharmony_ci	else {
4618c2ecf20Sopenharmony_ci		res = hfsplus_ext_read_extent(inode, hip->alloc_blocks);
4628c2ecf20Sopenharmony_ci		if (res)
4638c2ecf20Sopenharmony_ci			goto out;
4648c2ecf20Sopenharmony_ci		goal = hfsplus_ext_lastblock(hip->cached_extents);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	len = hip->clump_blocks;
4688c2ecf20Sopenharmony_ci	start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len);
4698c2ecf20Sopenharmony_ci	if (start >= sbi->total_blocks) {
4708c2ecf20Sopenharmony_ci		start = hfsplus_block_allocate(sb, goal, 0, &len);
4718c2ecf20Sopenharmony_ci		if (start >= goal) {
4728c2ecf20Sopenharmony_ci			res = -ENOSPC;
4738c2ecf20Sopenharmony_ci			goto out;
4748c2ecf20Sopenharmony_ci		}
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (zeroout) {
4788c2ecf20Sopenharmony_ci		res = sb_issue_zeroout(sb, start, len, GFP_NOFS);
4798c2ecf20Sopenharmony_ci		if (res)
4808c2ecf20Sopenharmony_ci			goto out;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (hip->alloc_blocks <= hip->first_blocks) {
4868c2ecf20Sopenharmony_ci		if (!hip->first_blocks) {
4878c2ecf20Sopenharmony_ci			hfs_dbg(EXTENT, "first extents\n");
4888c2ecf20Sopenharmony_ci			/* no extents yet */
4898c2ecf20Sopenharmony_ci			hip->first_extents[0].start_block = cpu_to_be32(start);
4908c2ecf20Sopenharmony_ci			hip->first_extents[0].block_count = cpu_to_be32(len);
4918c2ecf20Sopenharmony_ci			res = 0;
4928c2ecf20Sopenharmony_ci		} else {
4938c2ecf20Sopenharmony_ci			/* try to append to extents in inode */
4948c2ecf20Sopenharmony_ci			res = hfsplus_add_extent(hip->first_extents,
4958c2ecf20Sopenharmony_ci						 hip->alloc_blocks,
4968c2ecf20Sopenharmony_ci						 start, len);
4978c2ecf20Sopenharmony_ci			if (res == -ENOSPC)
4988c2ecf20Sopenharmony_ci				goto insert_extent;
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci		if (!res) {
5018c2ecf20Sopenharmony_ci			hfsplus_dump_extent(hip->first_extents);
5028c2ecf20Sopenharmony_ci			hip->first_blocks += len;
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci	} else {
5058c2ecf20Sopenharmony_ci		res = hfsplus_add_extent(hip->cached_extents,
5068c2ecf20Sopenharmony_ci					 hip->alloc_blocks - hip->cached_start,
5078c2ecf20Sopenharmony_ci					 start, len);
5088c2ecf20Sopenharmony_ci		if (!res) {
5098c2ecf20Sopenharmony_ci			hfsplus_dump_extent(hip->cached_extents);
5108c2ecf20Sopenharmony_ci			hip->extent_state |= HFSPLUS_EXT_DIRTY;
5118c2ecf20Sopenharmony_ci			hip->cached_blocks += len;
5128c2ecf20Sopenharmony_ci		} else if (res == -ENOSPC)
5138c2ecf20Sopenharmony_ci			goto insert_extent;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ciout:
5168c2ecf20Sopenharmony_ci	if (!res) {
5178c2ecf20Sopenharmony_ci		hip->alloc_blocks += len;
5188c2ecf20Sopenharmony_ci		mutex_unlock(&hip->extents_lock);
5198c2ecf20Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
5208c2ecf20Sopenharmony_ci		return 0;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	mutex_unlock(&hip->extents_lock);
5238c2ecf20Sopenharmony_ci	return res;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ciinsert_extent:
5268c2ecf20Sopenharmony_ci	hfs_dbg(EXTENT, "insert new extent\n");
5278c2ecf20Sopenharmony_ci	res = hfsplus_ext_write_extent_locked(inode);
5288c2ecf20Sopenharmony_ci	if (res)
5298c2ecf20Sopenharmony_ci		goto out;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
5328c2ecf20Sopenharmony_ci	hip->cached_extents[0].start_block = cpu_to_be32(start);
5338c2ecf20Sopenharmony_ci	hip->cached_extents[0].block_count = cpu_to_be32(len);
5348c2ecf20Sopenharmony_ci	hfsplus_dump_extent(hip->cached_extents);
5358c2ecf20Sopenharmony_ci	hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW;
5368c2ecf20Sopenharmony_ci	hip->cached_start = hip->alloc_blocks;
5378c2ecf20Sopenharmony_ci	hip->cached_blocks = len;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	res = 0;
5408c2ecf20Sopenharmony_ci	goto out;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_civoid hfsplus_file_truncate(struct inode *inode)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
5468c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
5478c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
5488c2ecf20Sopenharmony_ci	u32 alloc_cnt, blk_cnt, start;
5498c2ecf20Sopenharmony_ci	int res;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	hfs_dbg(INODE, "truncate: %lu, %llu -> %llu\n",
5528c2ecf20Sopenharmony_ci		inode->i_ino, (long long)hip->phys_size, inode->i_size);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (inode->i_size > hip->phys_size) {
5558c2ecf20Sopenharmony_ci		struct address_space *mapping = inode->i_mapping;
5568c2ecf20Sopenharmony_ci		struct page *page;
5578c2ecf20Sopenharmony_ci		void *fsdata;
5588c2ecf20Sopenharmony_ci		loff_t size = inode->i_size;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		res = pagecache_write_begin(NULL, mapping, size, 0, 0,
5618c2ecf20Sopenharmony_ci					    &page, &fsdata);
5628c2ecf20Sopenharmony_ci		if (res)
5638c2ecf20Sopenharmony_ci			return;
5648c2ecf20Sopenharmony_ci		res = pagecache_write_end(NULL, mapping, size,
5658c2ecf20Sopenharmony_ci			0, 0, page, fsdata);
5668c2ecf20Sopenharmony_ci		if (res < 0)
5678c2ecf20Sopenharmony_ci			return;
5688c2ecf20Sopenharmony_ci		mark_inode_dirty(inode);
5698c2ecf20Sopenharmony_ci		return;
5708c2ecf20Sopenharmony_ci	} else if (inode->i_size == hip->phys_size)
5718c2ecf20Sopenharmony_ci		return;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >>
5748c2ecf20Sopenharmony_ci			HFSPLUS_SB(sb)->alloc_blksz_shift;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	mutex_lock(&hip->extents_lock);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	alloc_cnt = hip->alloc_blocks;
5798c2ecf20Sopenharmony_ci	if (blk_cnt == alloc_cnt)
5808c2ecf20Sopenharmony_ci		goto out_unlock;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
5838c2ecf20Sopenharmony_ci	if (res) {
5848c2ecf20Sopenharmony_ci		mutex_unlock(&hip->extents_lock);
5858c2ecf20Sopenharmony_ci		/* XXX: We lack error handling of hfsplus_file_truncate() */
5868c2ecf20Sopenharmony_ci		return;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	while (1) {
5898c2ecf20Sopenharmony_ci		if (alloc_cnt == hip->first_blocks) {
5908c2ecf20Sopenharmony_ci			mutex_unlock(&fd.tree->tree_lock);
5918c2ecf20Sopenharmony_ci			hfsplus_free_extents(sb, hip->first_extents,
5928c2ecf20Sopenharmony_ci					     alloc_cnt, alloc_cnt - blk_cnt);
5938c2ecf20Sopenharmony_ci			hfsplus_dump_extent(hip->first_extents);
5948c2ecf20Sopenharmony_ci			hip->first_blocks = blk_cnt;
5958c2ecf20Sopenharmony_ci			mutex_lock(&fd.tree->tree_lock);
5968c2ecf20Sopenharmony_ci			break;
5978c2ecf20Sopenharmony_ci		}
5988c2ecf20Sopenharmony_ci		res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
5998c2ecf20Sopenharmony_ci		if (res)
6008c2ecf20Sopenharmony_ci			break;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		start = hip->cached_start;
6038c2ecf20Sopenharmony_ci		if (blk_cnt <= start)
6048c2ecf20Sopenharmony_ci			hfs_brec_remove(&fd);
6058c2ecf20Sopenharmony_ci		mutex_unlock(&fd.tree->tree_lock);
6068c2ecf20Sopenharmony_ci		hfsplus_free_extents(sb, hip->cached_extents,
6078c2ecf20Sopenharmony_ci				     alloc_cnt - start, alloc_cnt - blk_cnt);
6088c2ecf20Sopenharmony_ci		hfsplus_dump_extent(hip->cached_extents);
6098c2ecf20Sopenharmony_ci		mutex_lock(&fd.tree->tree_lock);
6108c2ecf20Sopenharmony_ci		if (blk_cnt > start) {
6118c2ecf20Sopenharmony_ci			hip->extent_state |= HFSPLUS_EXT_DIRTY;
6128c2ecf20Sopenharmony_ci			break;
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci		alloc_cnt = start;
6158c2ecf20Sopenharmony_ci		hip->cached_start = hip->cached_blocks = 0;
6168c2ecf20Sopenharmony_ci		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	hip->alloc_blocks = blk_cnt;
6218c2ecf20Sopenharmony_ciout_unlock:
6228c2ecf20Sopenharmony_ci	mutex_unlock(&hip->extents_lock);
6238c2ecf20Sopenharmony_ci	hip->phys_size = inode->i_size;
6248c2ecf20Sopenharmony_ci	hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >>
6258c2ecf20Sopenharmony_ci		sb->s_blocksize_bits;
6268c2ecf20Sopenharmony_ci	inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
6278c2ecf20Sopenharmony_ci	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
6288c2ecf20Sopenharmony_ci}
629