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