11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/hfs/extent.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1995-1997 Paul H. Hargrove 51da177e4SLinus Torvalds * (C) 2003 Ardis Technologies <roman@ardistech.com> 61da177e4SLinus Torvalds * This file may be distributed under the terms of the GNU General Public License. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This file contains the functions related to the extents B-tree. 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <linux/pagemap.h> 121da177e4SLinus Torvalds 131da177e4SLinus Torvalds #include "hfs_fs.h" 141da177e4SLinus Torvalds #include "btree.h" 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds /*================ File-local functions ================*/ 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds /* 191da177e4SLinus Torvalds * build_key 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds static void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type) 221da177e4SLinus Torvalds { 231da177e4SLinus Torvalds key->key_len = 7; 241da177e4SLinus Torvalds key->ext.FkType = type; 251da177e4SLinus Torvalds key->ext.FNum = cpu_to_be32(cnid); 261da177e4SLinus Torvalds key->ext.FABN = cpu_to_be16(block); 271da177e4SLinus Torvalds } 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds /* 301da177e4SLinus Torvalds * hfs_ext_compare() 311da177e4SLinus Torvalds * 321da177e4SLinus Torvalds * Description: 331da177e4SLinus Torvalds * This is the comparison function used for the extents B-tree. In 341da177e4SLinus Torvalds * comparing extent B-tree entries, the file id is the most 351da177e4SLinus Torvalds * significant field (compared as unsigned ints); the fork type is 361da177e4SLinus Torvalds * the second most significant field (compared as unsigned chars); 371da177e4SLinus Torvalds * and the allocation block number field is the least significant 381da177e4SLinus Torvalds * (compared as unsigned ints). 391da177e4SLinus Torvalds * Input Variable(s): 401da177e4SLinus Torvalds * struct hfs_ext_key *key1: pointer to the first key to compare 411da177e4SLinus Torvalds * struct hfs_ext_key *key2: pointer to the second key to compare 421da177e4SLinus Torvalds * Output Variable(s): 431da177e4SLinus Torvalds * NONE 441da177e4SLinus Torvalds * Returns: 451da177e4SLinus Torvalds * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 461da177e4SLinus Torvalds * Preconditions: 471da177e4SLinus Torvalds * key1 and key2 point to "valid" (struct hfs_ext_key)s. 481da177e4SLinus Torvalds * Postconditions: 491da177e4SLinus Torvalds * This function has no side-effects */ 501da177e4SLinus Torvalds int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2) 511da177e4SLinus Torvalds { 521da177e4SLinus Torvalds __be32 fnum1, fnum2; 531da177e4SLinus Torvalds __be16 block1, block2; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds fnum1 = key1->ext.FNum; 561da177e4SLinus Torvalds fnum2 = key2->ext.FNum; 571da177e4SLinus Torvalds if (fnum1 != fnum2) 581da177e4SLinus Torvalds return be32_to_cpu(fnum1) < be32_to_cpu(fnum2) ? -1 : 1; 591da177e4SLinus Torvalds if (key1->ext.FkType != key2->ext.FkType) 601da177e4SLinus Torvalds return key1->ext.FkType < key2->ext.FkType ? -1 : 1; 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds block1 = key1->ext.FABN; 631da177e4SLinus Torvalds block2 = key2->ext.FABN; 641da177e4SLinus Torvalds if (block1 == block2) 651da177e4SLinus Torvalds return 0; 661da177e4SLinus Torvalds return be16_to_cpu(block1) < be16_to_cpu(block2) ? -1 : 1; 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds /* 701da177e4SLinus Torvalds * hfs_ext_find_block 711da177e4SLinus Torvalds * 721da177e4SLinus Torvalds * Find a block within an extent record 731da177e4SLinus Torvalds */ 741da177e4SLinus Torvalds static u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds int i; 771da177e4SLinus Torvalds u16 count; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds for (i = 0; i < 3; ext++, i++) { 801da177e4SLinus Torvalds count = be16_to_cpu(ext->count); 811da177e4SLinus Torvalds if (off < count) 821da177e4SLinus Torvalds return be16_to_cpu(ext->block) + off; 831da177e4SLinus Torvalds off -= count; 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds /* panic? */ 861da177e4SLinus Torvalds return 0; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds static int hfs_ext_block_count(struct hfs_extent *ext) 901da177e4SLinus Torvalds { 911da177e4SLinus Torvalds int i; 921da177e4SLinus Torvalds u16 count = 0; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds for (i = 0; i < 3; ext++, i++) 951da177e4SLinus Torvalds count += be16_to_cpu(ext->count); 961da177e4SLinus Torvalds return count; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds static u16 hfs_ext_lastblock(struct hfs_extent *ext) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds int i; 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds ext += 2; 1041da177e4SLinus Torvalds for (i = 0; i < 2; ext--, i++) 1051da177e4SLinus Torvalds if (ext->count) 1061da177e4SLinus Torvalds break; 1071da177e4SLinus Torvalds return be16_to_cpu(ext->block) + be16_to_cpu(ext->count); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds static void __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) 1111da177e4SLinus Torvalds { 1121da177e4SLinus Torvalds int res; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start, 1151da177e4SLinus Torvalds HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); 1161da177e4SLinus Torvalds res = hfs_brec_find(fd); 1171da177e4SLinus Torvalds if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { 1181da177e4SLinus Torvalds if (res != -ENOENT) 1191da177e4SLinus Torvalds return; 1201da177e4SLinus Torvalds hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); 1211da177e4SLinus Torvalds HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); 1221da177e4SLinus Torvalds } else { 1231da177e4SLinus Torvalds if (res) 1241da177e4SLinus Torvalds return; 1251da177e4SLinus Torvalds hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength); 1261da177e4SLinus Torvalds HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds void hfs_ext_write_extent(struct inode *inode) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds struct hfs_find_data fd; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) { 1351da177e4SLinus Torvalds hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); 1361da177e4SLinus Torvalds __hfs_ext_write_extent(inode, &fd); 1371da177e4SLinus Torvalds hfs_find_exit(&fd); 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent, 1421da177e4SLinus Torvalds u32 cnid, u32 block, u8 type) 1431da177e4SLinus Torvalds { 1441da177e4SLinus Torvalds int res; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds hfs_ext_build_key(fd->search_key, cnid, block, type); 1471da177e4SLinus Torvalds fd->key->ext.FNum = 0; 1481da177e4SLinus Torvalds res = hfs_brec_find(fd); 1491da177e4SLinus Torvalds if (res && res != -ENOENT) 1501da177e4SLinus Torvalds return res; 1511da177e4SLinus Torvalds if (fd->key->ext.FNum != fd->search_key->ext.FNum || 1521da177e4SLinus Torvalds fd->key->ext.FkType != fd->search_key->ext.FkType) 1531da177e4SLinus Torvalds return -ENOENT; 1541da177e4SLinus Torvalds if (fd->entrylength != sizeof(hfs_extent_rec)) 1551da177e4SLinus Torvalds return -EIO; 1561da177e4SLinus Torvalds hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec)); 1571da177e4SLinus Torvalds return 0; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) 1611da177e4SLinus Torvalds { 1621da177e4SLinus Torvalds int res; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) 1651da177e4SLinus Torvalds __hfs_ext_write_extent(inode, fd); 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino, 1681da177e4SLinus Torvalds block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); 1691da177e4SLinus Torvalds if (!res) { 1701da177e4SLinus Torvalds HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN); 1711da177e4SLinus Torvalds HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents); 1721da177e4SLinus Torvalds } else { 1731da177e4SLinus Torvalds HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; 1741da177e4SLinus Torvalds HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds return res; 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds static int hfs_ext_read_extent(struct inode *inode, u16 block) 1801da177e4SLinus Torvalds { 1811da177e4SLinus Torvalds struct hfs_find_data fd; 1821da177e4SLinus Torvalds int res; 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds if (block >= HFS_I(inode)->cached_start && 1851da177e4SLinus Torvalds block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks) 1861da177e4SLinus Torvalds return 0; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); 1891da177e4SLinus Torvalds res = __hfs_ext_cache_extent(&fd, inode, block); 1901da177e4SLinus Torvalds hfs_find_exit(&fd); 1911da177e4SLinus Torvalds return res; 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds static void hfs_dump_extent(struct hfs_extent *extent) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds int i; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds dprint(DBG_EXTENT, " "); 1991da177e4SLinus Torvalds for (i = 0; i < 3; i++) 2001da177e4SLinus Torvalds dprint(DBG_EXTENT, " %u:%u", be16_to_cpu(extent[i].block), 2011da177e4SLinus Torvalds be16_to_cpu(extent[i].count)); 2021da177e4SLinus Torvalds dprint(DBG_EXTENT, "\n"); 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds static int hfs_add_extent(struct hfs_extent *extent, u16 offset, 2061da177e4SLinus Torvalds u16 alloc_block, u16 block_count) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds u16 count, start; 2091da177e4SLinus Torvalds int i; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds hfs_dump_extent(extent); 2121da177e4SLinus Torvalds for (i = 0; i < 3; extent++, i++) { 2131da177e4SLinus Torvalds count = be16_to_cpu(extent->count); 2141da177e4SLinus Torvalds if (offset == count) { 2151da177e4SLinus Torvalds start = be16_to_cpu(extent->block); 2161da177e4SLinus Torvalds if (alloc_block != start + count) { 2171da177e4SLinus Torvalds if (++i >= 3) 2181da177e4SLinus Torvalds return -ENOSPC; 2191da177e4SLinus Torvalds extent++; 2201da177e4SLinus Torvalds extent->block = cpu_to_be16(alloc_block); 2211da177e4SLinus Torvalds } else 2221da177e4SLinus Torvalds block_count += count; 2231da177e4SLinus Torvalds extent->count = cpu_to_be16(block_count); 2241da177e4SLinus Torvalds return 0; 2251da177e4SLinus Torvalds } else if (offset < count) 2261da177e4SLinus Torvalds break; 2271da177e4SLinus Torvalds offset -= count; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds /* panic? */ 2301da177e4SLinus Torvalds return -EIO; 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds static int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent, 2341da177e4SLinus Torvalds u16 offset, u16 block_nr) 2351da177e4SLinus Torvalds { 2361da177e4SLinus Torvalds u16 count, start; 2371da177e4SLinus Torvalds int i; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds hfs_dump_extent(extent); 2401da177e4SLinus Torvalds for (i = 0; i < 3; extent++, i++) { 2411da177e4SLinus Torvalds count = be16_to_cpu(extent->count); 2421da177e4SLinus Torvalds if (offset == count) 2431da177e4SLinus Torvalds goto found; 2441da177e4SLinus Torvalds else if (offset < count) 2451da177e4SLinus Torvalds break; 2461da177e4SLinus Torvalds offset -= count; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds /* panic? */ 2491da177e4SLinus Torvalds return -EIO; 2501da177e4SLinus Torvalds found: 2511da177e4SLinus Torvalds for (;;) { 2521da177e4SLinus Torvalds start = be16_to_cpu(extent->block); 2531da177e4SLinus Torvalds if (count <= block_nr) { 2541da177e4SLinus Torvalds hfs_clear_vbm_bits(sb, start, count); 2551da177e4SLinus Torvalds extent->block = 0; 2561da177e4SLinus Torvalds extent->count = 0; 2571da177e4SLinus Torvalds block_nr -= count; 2581da177e4SLinus Torvalds } else { 2591da177e4SLinus Torvalds count -= block_nr; 2601da177e4SLinus Torvalds hfs_clear_vbm_bits(sb, start + count, block_nr); 2611da177e4SLinus Torvalds extent->count = cpu_to_be16(count); 2621da177e4SLinus Torvalds block_nr = 0; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds if (!block_nr || !i) 2651da177e4SLinus Torvalds return 0; 2661da177e4SLinus Torvalds i--; 2671da177e4SLinus Torvalds extent--; 2681da177e4SLinus Torvalds count = be16_to_cpu(extent->count); 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds struct hfs_find_data fd; 2751da177e4SLinus Torvalds u32 total_blocks, blocks, start; 2761da177e4SLinus Torvalds u32 cnid = be32_to_cpu(file->FlNum); 2771da177e4SLinus Torvalds struct hfs_extent *extent; 2781da177e4SLinus Torvalds int res, i; 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds if (type == HFS_FK_DATA) { 2811da177e4SLinus Torvalds total_blocks = be32_to_cpu(file->PyLen); 2821da177e4SLinus Torvalds extent = file->ExtRec; 2831da177e4SLinus Torvalds } else { 2841da177e4SLinus Torvalds total_blocks = be32_to_cpu(file->RPyLen); 2851da177e4SLinus Torvalds extent = file->RExtRec; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds total_blocks /= HFS_SB(sb)->alloc_blksz; 2881da177e4SLinus Torvalds if (!total_blocks) 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds blocks = 0; 2921da177e4SLinus Torvalds for (i = 0; i < 3; extent++, i++) 2931da177e4SLinus Torvalds blocks += be16_to_cpu(extent[i].count); 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds res = hfs_free_extents(sb, extent, blocks, blocks); 2961da177e4SLinus Torvalds if (res) 2971da177e4SLinus Torvalds return res; 2981da177e4SLinus Torvalds if (total_blocks == blocks) 2991da177e4SLinus Torvalds return 0; 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds hfs_find_init(HFS_SB(sb)->ext_tree, &fd); 3021da177e4SLinus Torvalds do { 3031da177e4SLinus Torvalds res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type); 3041da177e4SLinus Torvalds if (res) 3051da177e4SLinus Torvalds break; 3061da177e4SLinus Torvalds start = be16_to_cpu(fd.key->ext.FABN); 3071da177e4SLinus Torvalds hfs_free_extents(sb, extent, total_blocks - start, total_blocks); 3081da177e4SLinus Torvalds hfs_brec_remove(&fd); 3091da177e4SLinus Torvalds total_blocks = start; 3101da177e4SLinus Torvalds } while (total_blocks > blocks); 3111da177e4SLinus Torvalds hfs_find_exit(&fd); 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds return res; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds /* 3171da177e4SLinus Torvalds * hfs_get_block 3181da177e4SLinus Torvalds */ 3191da177e4SLinus Torvalds int hfs_get_block(struct inode *inode, sector_t block, 3201da177e4SLinus Torvalds struct buffer_head *bh_result, int create) 3211da177e4SLinus Torvalds { 3221da177e4SLinus Torvalds struct super_block *sb; 3231da177e4SLinus Torvalds u16 dblock, ablock; 3241da177e4SLinus Torvalds int res; 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds sb = inode->i_sb; 3271da177e4SLinus Torvalds /* Convert inode block to disk allocation block */ 3281da177e4SLinus Torvalds ablock = (u32)block / HFS_SB(sb)->fs_div; 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds if (block >= HFS_I(inode)->fs_blocks) { 3311da177e4SLinus Torvalds if (block > HFS_I(inode)->fs_blocks || !create) 3321da177e4SLinus Torvalds return -EIO; 3331da177e4SLinus Torvalds if (ablock >= HFS_I(inode)->alloc_blocks) { 3341da177e4SLinus Torvalds res = hfs_extend_file(inode); 3351da177e4SLinus Torvalds if (res) 3361da177e4SLinus Torvalds return res; 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds } else 3391da177e4SLinus Torvalds create = 0; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds if (ablock < HFS_I(inode)->first_blocks) { 3421da177e4SLinus Torvalds dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock); 3431da177e4SLinus Torvalds goto done; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 34639f8d472SMatthias Kaehlcke mutex_lock(&HFS_I(inode)->extents_lock); 3471da177e4SLinus Torvalds res = hfs_ext_read_extent(inode, ablock); 3481da177e4SLinus Torvalds if (!res) 3491da177e4SLinus Torvalds dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents, 3501da177e4SLinus Torvalds ablock - HFS_I(inode)->cached_start); 3511da177e4SLinus Torvalds else { 35239f8d472SMatthias Kaehlcke mutex_unlock(&HFS_I(inode)->extents_lock); 3531da177e4SLinus Torvalds return -EIO; 3541da177e4SLinus Torvalds } 35539f8d472SMatthias Kaehlcke mutex_unlock(&HFS_I(inode)->extents_lock); 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds done: 3581da177e4SLinus Torvalds map_bh(bh_result, sb, HFS_SB(sb)->fs_start + 3591da177e4SLinus Torvalds dblock * HFS_SB(sb)->fs_div + 3601da177e4SLinus Torvalds (u32)block % HFS_SB(sb)->fs_div); 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds if (create) { 3631da177e4SLinus Torvalds set_buffer_new(bh_result); 3641da177e4SLinus Torvalds HFS_I(inode)->phys_size += sb->s_blocksize; 3651da177e4SLinus Torvalds HFS_I(inode)->fs_blocks++; 3661da177e4SLinus Torvalds inode_add_bytes(inode, sb->s_blocksize); 3671da177e4SLinus Torvalds mark_inode_dirty(inode); 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds return 0; 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds int hfs_extend_file(struct inode *inode) 3731da177e4SLinus Torvalds { 3741da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 3751da177e4SLinus Torvalds u32 start, len, goal; 3761da177e4SLinus Torvalds int res; 3771da177e4SLinus Torvalds 37839f8d472SMatthias Kaehlcke mutex_lock(&HFS_I(inode)->extents_lock); 3791da177e4SLinus Torvalds if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) 3801da177e4SLinus Torvalds goal = hfs_ext_lastblock(HFS_I(inode)->first_extents); 3811da177e4SLinus Torvalds else { 3821da177e4SLinus Torvalds res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks); 3831da177e4SLinus Torvalds if (res) 3841da177e4SLinus Torvalds goto out; 3851da177e4SLinus Torvalds goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents); 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds len = HFS_I(inode)->clump_blocks; 3891da177e4SLinus Torvalds start = hfs_vbm_search_free(sb, goal, &len); 3901da177e4SLinus Torvalds if (!len) { 3911da177e4SLinus Torvalds res = -ENOSPC; 3921da177e4SLinus Torvalds goto out; 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); 3961da177e4SLinus Torvalds if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) { 3971da177e4SLinus Torvalds if (!HFS_I(inode)->first_blocks) { 3981da177e4SLinus Torvalds dprint(DBG_EXTENT, "first extents\n"); 3991da177e4SLinus Torvalds /* no extents yet */ 4001da177e4SLinus Torvalds HFS_I(inode)->first_extents[0].block = cpu_to_be16(start); 4011da177e4SLinus Torvalds HFS_I(inode)->first_extents[0].count = cpu_to_be16(len); 4021da177e4SLinus Torvalds res = 0; 4031da177e4SLinus Torvalds } else { 4041da177e4SLinus Torvalds /* try to append to extents in inode */ 4051da177e4SLinus Torvalds res = hfs_add_extent(HFS_I(inode)->first_extents, 4061da177e4SLinus Torvalds HFS_I(inode)->alloc_blocks, 4071da177e4SLinus Torvalds start, len); 4081da177e4SLinus Torvalds if (res == -ENOSPC) 4091da177e4SLinus Torvalds goto insert_extent; 4101da177e4SLinus Torvalds } 4111da177e4SLinus Torvalds if (!res) { 4121da177e4SLinus Torvalds hfs_dump_extent(HFS_I(inode)->first_extents); 4131da177e4SLinus Torvalds HFS_I(inode)->first_blocks += len; 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds } else { 4161da177e4SLinus Torvalds res = hfs_add_extent(HFS_I(inode)->cached_extents, 4171da177e4SLinus Torvalds HFS_I(inode)->alloc_blocks - 4181da177e4SLinus Torvalds HFS_I(inode)->cached_start, 4191da177e4SLinus Torvalds start, len); 4201da177e4SLinus Torvalds if (!res) { 4211da177e4SLinus Torvalds hfs_dump_extent(HFS_I(inode)->cached_extents); 4221da177e4SLinus Torvalds HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; 4231da177e4SLinus Torvalds HFS_I(inode)->cached_blocks += len; 4241da177e4SLinus Torvalds } else if (res == -ENOSPC) 4251da177e4SLinus Torvalds goto insert_extent; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds out: 42839f8d472SMatthias Kaehlcke mutex_unlock(&HFS_I(inode)->extents_lock); 4291da177e4SLinus Torvalds if (!res) { 4301da177e4SLinus Torvalds HFS_I(inode)->alloc_blocks += len; 4311da177e4SLinus Torvalds mark_inode_dirty(inode); 4321da177e4SLinus Torvalds if (inode->i_ino < HFS_FIRSTUSER_CNID) 4331da177e4SLinus Torvalds set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags); 4341da177e4SLinus Torvalds set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); 4351da177e4SLinus Torvalds sb->s_dirt = 1; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds return res; 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds insert_extent: 4401da177e4SLinus Torvalds dprint(DBG_EXTENT, "insert new extent\n"); 4411da177e4SLinus Torvalds hfs_ext_write_extent(inode); 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec)); 4441da177e4SLinus Torvalds HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start); 4451da177e4SLinus Torvalds HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len); 4461da177e4SLinus Torvalds hfs_dump_extent(HFS_I(inode)->cached_extents); 4471da177e4SLinus Torvalds HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW; 4481da177e4SLinus Torvalds HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks; 4491da177e4SLinus Torvalds HFS_I(inode)->cached_blocks = len; 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds res = 0; 4521da177e4SLinus Torvalds goto out; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds void hfs_file_truncate(struct inode *inode) 4561da177e4SLinus Torvalds { 4571da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 4581da177e4SLinus Torvalds struct hfs_find_data fd; 4591da177e4SLinus Torvalds u16 blk_cnt, alloc_cnt, start; 4601da177e4SLinus Torvalds u32 size; 4611da177e4SLinus Torvalds int res; 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, 4641da177e4SLinus Torvalds (long long)HFS_I(inode)->phys_size, inode->i_size); 4651da177e4SLinus Torvalds if (inode->i_size > HFS_I(inode)->phys_size) { 4661da177e4SLinus Torvalds struct address_space *mapping = inode->i_mapping; 4677903d9eeSNick Piggin void *fsdata; 4681da177e4SLinus Torvalds struct page *page; 4691da177e4SLinus Torvalds int res; 4701da177e4SLinus Torvalds 4717903d9eeSNick Piggin /* XXX: Can use generic_cont_expand? */ 4721da177e4SLinus Torvalds size = inode->i_size - 1; 4737903d9eeSNick Piggin res = pagecache_write_begin(NULL, mapping, size+1, 0, 4747903d9eeSNick Piggin AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); 4757903d9eeSNick Piggin if (!res) { 4767903d9eeSNick Piggin res = pagecache_write_end(NULL, mapping, size+1, 0, 0, 4777903d9eeSNick Piggin page, fsdata); 4787903d9eeSNick Piggin } 4791da177e4SLinus Torvalds if (res) 4801da177e4SLinus Torvalds inode->i_size = HFS_I(inode)->phys_size; 4811da177e4SLinus Torvalds return; 482f76d28d2SRoman Zippel } else if (inode->i_size == HFS_I(inode)->phys_size) 483f76d28d2SRoman Zippel return; 4841da177e4SLinus Torvalds size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1; 4851da177e4SLinus Torvalds blk_cnt = size / HFS_SB(sb)->alloc_blksz; 4861da177e4SLinus Torvalds alloc_cnt = HFS_I(inode)->alloc_blocks; 4871da177e4SLinus Torvalds if (blk_cnt == alloc_cnt) 4881da177e4SLinus Torvalds goto out; 4891da177e4SLinus Torvalds 49039f8d472SMatthias Kaehlcke mutex_lock(&HFS_I(inode)->extents_lock); 4911da177e4SLinus Torvalds hfs_find_init(HFS_SB(sb)->ext_tree, &fd); 4921da177e4SLinus Torvalds while (1) { 4931da177e4SLinus Torvalds if (alloc_cnt == HFS_I(inode)->first_blocks) { 4941da177e4SLinus Torvalds hfs_free_extents(sb, HFS_I(inode)->first_extents, 4951da177e4SLinus Torvalds alloc_cnt, alloc_cnt - blk_cnt); 4961da177e4SLinus Torvalds hfs_dump_extent(HFS_I(inode)->first_extents); 4971da177e4SLinus Torvalds HFS_I(inode)->first_blocks = blk_cnt; 4981da177e4SLinus Torvalds break; 4991da177e4SLinus Torvalds } 5001da177e4SLinus Torvalds res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt); 5011da177e4SLinus Torvalds if (res) 5021da177e4SLinus Torvalds break; 5031da177e4SLinus Torvalds start = HFS_I(inode)->cached_start; 5041da177e4SLinus Torvalds hfs_free_extents(sb, HFS_I(inode)->cached_extents, 5051da177e4SLinus Torvalds alloc_cnt - start, alloc_cnt - blk_cnt); 5061da177e4SLinus Torvalds hfs_dump_extent(HFS_I(inode)->cached_extents); 5071da177e4SLinus Torvalds if (blk_cnt > start) { 5081da177e4SLinus Torvalds HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; 5091da177e4SLinus Torvalds break; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds alloc_cnt = start; 5121da177e4SLinus Torvalds HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; 5131da177e4SLinus Torvalds HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); 5141da177e4SLinus Torvalds hfs_brec_remove(&fd); 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds hfs_find_exit(&fd); 51739f8d472SMatthias Kaehlcke mutex_unlock(&HFS_I(inode)->extents_lock); 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds HFS_I(inode)->alloc_blocks = blk_cnt; 5201da177e4SLinus Torvalds out: 5211da177e4SLinus Torvalds HFS_I(inode)->phys_size = inode->i_size; 5221da177e4SLinus Torvalds HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; 5231da177e4SLinus Torvalds inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits); 5241da177e4SLinus Torvalds mark_inode_dirty(inode); 5251da177e4SLinus Torvalds } 526