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