11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/hfsplus/extents.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2001 51da177e4SLinus Torvalds * Brad Boyer (flar@allandria.com) 61da177e4SLinus Torvalds * (C) 2003 Ardis Technologies <roman@ardistech.com> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Handling of Extents both in catalog and extents overflow trees 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <linux/errno.h> 121da177e4SLinus Torvalds #include <linux/fs.h> 131da177e4SLinus Torvalds #include <linux/pagemap.h> 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #include "hfsplus_fs.h" 161da177e4SLinus Torvalds #include "hfsplus_raw.h" 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds /* Compare two extents keys, returns 0 on same, pos/neg for difference */ 192179d372SDavid Elliott int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1, 202179d372SDavid Elliott const hfsplus_btree_key *k2) 211da177e4SLinus Torvalds { 221da177e4SLinus Torvalds __be32 k1id, k2id; 231da177e4SLinus Torvalds __be32 k1s, k2s; 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds k1id = k1->ext.cnid; 261da177e4SLinus Torvalds k2id = k2->ext.cnid; 271da177e4SLinus Torvalds if (k1id != k2id) 281da177e4SLinus Torvalds return be32_to_cpu(k1id) < be32_to_cpu(k2id) ? -1 : 1; 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds if (k1->ext.fork_type != k2->ext.fork_type) 311da177e4SLinus Torvalds return k1->ext.fork_type < k2->ext.fork_type ? -1 : 1; 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds k1s = k1->ext.start_block; 341da177e4SLinus Torvalds k2s = k2->ext.start_block; 351da177e4SLinus Torvalds if (k1s == k2s) 361da177e4SLinus Torvalds return 0; 371da177e4SLinus Torvalds return be32_to_cpu(k1s) < be32_to_cpu(k2s) ? -1 : 1; 381da177e4SLinus Torvalds } 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static void hfsplus_ext_build_key(hfsplus_btree_key *key, u32 cnid, 411da177e4SLinus Torvalds u32 block, u8 type) 421da177e4SLinus Torvalds { 431da177e4SLinus Torvalds key->key_len = cpu_to_be16(HFSPLUS_EXT_KEYLEN - 2); 441da177e4SLinus Torvalds key->ext.cnid = cpu_to_be32(cnid); 451da177e4SLinus Torvalds key->ext.start_block = cpu_to_be32(block); 461da177e4SLinus Torvalds key->ext.fork_type = type; 471da177e4SLinus Torvalds key->ext.pad = 0; 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds static u32 hfsplus_ext_find_block(struct hfsplus_extent *ext, u32 off) 511da177e4SLinus Torvalds { 521da177e4SLinus Torvalds int i; 531da177e4SLinus Torvalds u32 count; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds for (i = 0; i < 8; ext++, i++) { 561da177e4SLinus Torvalds count = be32_to_cpu(ext->block_count); 571da177e4SLinus Torvalds if (off < count) 581da177e4SLinus Torvalds return be32_to_cpu(ext->start_block) + off; 591da177e4SLinus Torvalds off -= count; 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds /* panic? */ 621da177e4SLinus Torvalds return 0; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds static int hfsplus_ext_block_count(struct hfsplus_extent *ext) 661da177e4SLinus Torvalds { 671da177e4SLinus Torvalds int i; 681da177e4SLinus Torvalds u32 count = 0; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds for (i = 0; i < 8; ext++, i++) 711da177e4SLinus Torvalds count += be32_to_cpu(ext->block_count); 721da177e4SLinus Torvalds return count; 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds static u32 hfsplus_ext_lastblock(struct hfsplus_extent *ext) 761da177e4SLinus Torvalds { 771da177e4SLinus Torvalds int i; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds ext += 7; 801da177e4SLinus Torvalds for (i = 0; i < 7; ext--, i++) 811da177e4SLinus Torvalds if (ext->block_count) 821da177e4SLinus Torvalds break; 831da177e4SLinus Torvalds return be32_to_cpu(ext->start_block) + be32_to_cpu(ext->block_count); 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 862753cc28SAnton Salikhmetov static void __hfsplus_ext_write_extent(struct inode *inode, 872753cc28SAnton Salikhmetov struct hfs_find_data *fd) 881da177e4SLinus Torvalds { 896af502deSChristoph Hellwig struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 901da177e4SLinus Torvalds int res; 911da177e4SLinus Torvalds 927fcc99f4SChristoph Hellwig WARN_ON(!mutex_is_locked(&hip->extents_lock)); 937fcc99f4SChristoph Hellwig 946af502deSChristoph Hellwig hfsplus_ext_build_key(fd->search_key, inode->i_ino, hip->cached_start, 956af502deSChristoph Hellwig HFSPLUS_IS_RSRC(inode) ? 966af502deSChristoph Hellwig HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); 976af502deSChristoph Hellwig 981da177e4SLinus Torvalds res = hfs_brec_find(fd); 99b33b7921SChristoph Hellwig if (hip->extent_state & HFSPLUS_EXT_NEW) { 1001da177e4SLinus Torvalds if (res != -ENOENT) 1011da177e4SLinus Torvalds return; 1026af502deSChristoph Hellwig hfs_brec_insert(fd, hip->cached_extents, 1036af502deSChristoph Hellwig sizeof(hfsplus_extent_rec)); 104b33b7921SChristoph Hellwig hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); 1051da177e4SLinus Torvalds } else { 1061da177e4SLinus Torvalds if (res) 1071da177e4SLinus Torvalds return; 1086af502deSChristoph Hellwig hfs_bnode_write(fd->bnode, hip->cached_extents, 1096af502deSChristoph Hellwig fd->entryoffset, fd->entrylength); 110b33b7921SChristoph Hellwig hip->extent_state &= ~HFSPLUS_EXT_DIRTY; 1111da177e4SLinus Torvalds } 112e3494705SChristoph Hellwig 113e3494705SChristoph Hellwig /* 114e3494705SChristoph Hellwig * We can't just use hfsplus_mark_inode_dirty here, because we 115e3494705SChristoph Hellwig * also get called from hfsplus_write_inode, which should not 116e3494705SChristoph Hellwig * redirty the inode. Instead the callers have to be careful 117e3494705SChristoph Hellwig * to explicily mark the inode dirty, too. 118e3494705SChristoph Hellwig */ 119e3494705SChristoph Hellwig set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1227fcc99f4SChristoph Hellwig static void hfsplus_ext_write_extent_locked(struct inode *inode) 1231da177e4SLinus Torvalds { 124b33b7921SChristoph Hellwig if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) { 1251da177e4SLinus Torvalds struct hfs_find_data fd; 1261da177e4SLinus Torvalds 127dd73a01aSChristoph Hellwig hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); 1281da177e4SLinus Torvalds __hfsplus_ext_write_extent(inode, &fd); 1291da177e4SLinus Torvalds hfs_find_exit(&fd); 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 1337fcc99f4SChristoph Hellwig void hfsplus_ext_write_extent(struct inode *inode) 1347fcc99f4SChristoph Hellwig { 1357fcc99f4SChristoph Hellwig mutex_lock(&HFSPLUS_I(inode)->extents_lock); 1367fcc99f4SChristoph Hellwig hfsplus_ext_write_extent_locked(inode); 1377fcc99f4SChristoph Hellwig mutex_unlock(&HFSPLUS_I(inode)->extents_lock); 1387fcc99f4SChristoph Hellwig } 1397fcc99f4SChristoph Hellwig 1401da177e4SLinus Torvalds static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd, 1411da177e4SLinus Torvalds struct hfsplus_extent *extent, 1421da177e4SLinus Torvalds u32 cnid, u32 block, u8 type) 1431da177e4SLinus Torvalds { 1441da177e4SLinus Torvalds int res; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds hfsplus_ext_build_key(fd->search_key, cnid, block, type); 1471da177e4SLinus Torvalds fd->key->ext.cnid = 0; 1481da177e4SLinus Torvalds res = hfs_brec_find(fd); 1491da177e4SLinus Torvalds if (res && res != -ENOENT) 1501da177e4SLinus Torvalds return res; 1511da177e4SLinus Torvalds if (fd->key->ext.cnid != fd->search_key->ext.cnid || 1521da177e4SLinus Torvalds fd->key->ext.fork_type != fd->search_key->ext.fork_type) 1531da177e4SLinus Torvalds return -ENOENT; 1541da177e4SLinus Torvalds if (fd->entrylength != sizeof(hfsplus_extent_rec)) 1551da177e4SLinus Torvalds return -EIO; 1562753cc28SAnton Salikhmetov hfs_bnode_read(fd->bnode, extent, fd->entryoffset, 1572753cc28SAnton Salikhmetov sizeof(hfsplus_extent_rec)); 1581da177e4SLinus Torvalds return 0; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1612753cc28SAnton Salikhmetov static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, 1622753cc28SAnton Salikhmetov struct inode *inode, u32 block) 1631da177e4SLinus Torvalds { 1646af502deSChristoph Hellwig struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 1651da177e4SLinus Torvalds int res; 1661da177e4SLinus Torvalds 1677fcc99f4SChristoph Hellwig WARN_ON(!mutex_is_locked(&hip->extents_lock)); 1687fcc99f4SChristoph Hellwig 169b33b7921SChristoph Hellwig if (hip->extent_state & HFSPLUS_EXT_DIRTY) 1701da177e4SLinus Torvalds __hfsplus_ext_write_extent(inode, fd); 1711da177e4SLinus Torvalds 1726af502deSChristoph Hellwig res = __hfsplus_ext_read_extent(fd, hip->cached_extents, inode->i_ino, 1736af502deSChristoph Hellwig block, HFSPLUS_IS_RSRC(inode) ? 1746af502deSChristoph Hellwig HFSPLUS_TYPE_RSRC : 1756af502deSChristoph Hellwig HFSPLUS_TYPE_DATA); 1761da177e4SLinus Torvalds if (!res) { 1776af502deSChristoph Hellwig hip->cached_start = be32_to_cpu(fd->key->ext.start_block); 1782753cc28SAnton Salikhmetov hip->cached_blocks = 1792753cc28SAnton Salikhmetov hfsplus_ext_block_count(hip->cached_extents); 1801da177e4SLinus Torvalds } else { 1816af502deSChristoph Hellwig hip->cached_start = hip->cached_blocks = 0; 182b33b7921SChristoph Hellwig hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds return res; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static int hfsplus_ext_read_extent(struct inode *inode, u32 block) 1881da177e4SLinus Torvalds { 1896af502deSChristoph Hellwig struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 1901da177e4SLinus Torvalds struct hfs_find_data fd; 1911da177e4SLinus Torvalds int res; 1921da177e4SLinus Torvalds 1936af502deSChristoph Hellwig if (block >= hip->cached_start && 1946af502deSChristoph Hellwig block < hip->cached_start + hip->cached_blocks) 1951da177e4SLinus Torvalds return 0; 1961da177e4SLinus Torvalds 197dd73a01aSChristoph Hellwig hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); 1981da177e4SLinus Torvalds res = __hfsplus_ext_cache_extent(&fd, inode, block); 1991da177e4SLinus Torvalds hfs_find_exit(&fd); 2001da177e4SLinus Torvalds return res; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds /* Get a block at iblock for inode, possibly allocating if create */ 2041da177e4SLinus Torvalds int hfsplus_get_block(struct inode *inode, sector_t iblock, 2051da177e4SLinus Torvalds struct buffer_head *bh_result, int create) 2061da177e4SLinus Torvalds { 207dd73a01aSChristoph Hellwig struct super_block *sb = inode->i_sb; 208dd73a01aSChristoph Hellwig struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 2096af502deSChristoph Hellwig struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 2101da177e4SLinus Torvalds int res = -EIO; 2111da177e4SLinus Torvalds u32 ablock, dblock, mask; 212bf1a1b31SChristoph Hellwig sector_t sector; 213e3494705SChristoph Hellwig int was_dirty = 0; 2141da177e4SLinus Torvalds int shift; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds /* Convert inode block to disk allocation block */ 217dd73a01aSChristoph Hellwig shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; 218dd73a01aSChristoph Hellwig ablock = iblock >> sbi->fs_shift; 2191da177e4SLinus Torvalds 2206af502deSChristoph Hellwig if (iblock >= hip->fs_blocks) { 2216af502deSChristoph Hellwig if (iblock > hip->fs_blocks || !create) 2221da177e4SLinus Torvalds return -EIO; 2236af502deSChristoph Hellwig if (ablock >= hip->alloc_blocks) { 2241da177e4SLinus Torvalds res = hfsplus_file_extend(inode); 2251da177e4SLinus Torvalds if (res) 2261da177e4SLinus Torvalds return res; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds } else 2291da177e4SLinus Torvalds create = 0; 2301da177e4SLinus Torvalds 2316af502deSChristoph Hellwig if (ablock < hip->first_blocks) { 2326af502deSChristoph Hellwig dblock = hfsplus_ext_find_block(hip->first_extents, ablock); 2331da177e4SLinus Torvalds goto done; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 236248736c2SEric Sesterhenn if (inode->i_ino == HFSPLUS_EXT_CNID) 237248736c2SEric Sesterhenn return -EIO; 238248736c2SEric Sesterhenn 2396af502deSChristoph Hellwig mutex_lock(&hip->extents_lock); 240e3494705SChristoph Hellwig 241e3494705SChristoph Hellwig /* 242e3494705SChristoph Hellwig * hfsplus_ext_read_extent will write out a cached extent into 243e3494705SChristoph Hellwig * the extents btree. In that case we may have to mark the inode 244e3494705SChristoph Hellwig * dirty even for a pure read of an extent here. 245e3494705SChristoph Hellwig */ 246e3494705SChristoph Hellwig was_dirty = (hip->extent_state & HFSPLUS_EXT_DIRTY); 2471da177e4SLinus Torvalds res = hfsplus_ext_read_extent(inode, ablock); 248e3494705SChristoph Hellwig if (res) { 2496af502deSChristoph Hellwig mutex_unlock(&hip->extents_lock); 2501da177e4SLinus Torvalds return -EIO; 2511da177e4SLinus Torvalds } 252e3494705SChristoph Hellwig dblock = hfsplus_ext_find_block(hip->cached_extents, 253e3494705SChristoph Hellwig ablock - hip->cached_start); 2546af502deSChristoph Hellwig mutex_unlock(&hip->extents_lock); 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds done: 2572753cc28SAnton Salikhmetov dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", 2582753cc28SAnton Salikhmetov inode->i_ino, (long long)iblock, dblock); 259bf1a1b31SChristoph Hellwig 260dd73a01aSChristoph Hellwig mask = (1 << sbi->fs_shift) - 1; 261bf1a1b31SChristoph Hellwig sector = ((sector_t)dblock << sbi->fs_shift) + 262bf1a1b31SChristoph Hellwig sbi->blockoffset + (iblock & mask); 263bf1a1b31SChristoph Hellwig map_bh(bh_result, sb, sector); 264bf1a1b31SChristoph Hellwig 2651da177e4SLinus Torvalds if (create) { 2661da177e4SLinus Torvalds set_buffer_new(bh_result); 2676af502deSChristoph Hellwig hip->phys_size += sb->s_blocksize; 2686af502deSChristoph Hellwig hip->fs_blocks++; 2691da177e4SLinus Torvalds inode_add_bytes(inode, sb->s_blocksize); 2701da177e4SLinus Torvalds } 271e3494705SChristoph Hellwig if (create || was_dirty) 272e3494705SChristoph Hellwig mark_inode_dirty(inode); 2731da177e4SLinus Torvalds return 0; 2741da177e4SLinus Torvalds } 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds static void hfsplus_dump_extent(struct hfsplus_extent *extent) 2771da177e4SLinus Torvalds { 2781da177e4SLinus Torvalds int i; 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds dprint(DBG_EXTENT, " "); 2811da177e4SLinus Torvalds for (i = 0; i < 8; i++) 2821da177e4SLinus Torvalds dprint(DBG_EXTENT, " %u:%u", be32_to_cpu(extent[i].start_block), 2831da177e4SLinus Torvalds be32_to_cpu(extent[i].block_count)); 2841da177e4SLinus Torvalds dprint(DBG_EXTENT, "\n"); 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds static int hfsplus_add_extent(struct hfsplus_extent *extent, u32 offset, 2881da177e4SLinus Torvalds u32 alloc_block, u32 block_count) 2891da177e4SLinus Torvalds { 2901da177e4SLinus Torvalds u32 count, start; 2911da177e4SLinus Torvalds int i; 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds hfsplus_dump_extent(extent); 2941da177e4SLinus Torvalds for (i = 0; i < 8; extent++, i++) { 2951da177e4SLinus Torvalds count = be32_to_cpu(extent->block_count); 2961da177e4SLinus Torvalds if (offset == count) { 2971da177e4SLinus Torvalds start = be32_to_cpu(extent->start_block); 2981da177e4SLinus Torvalds if (alloc_block != start + count) { 2991da177e4SLinus Torvalds if (++i >= 8) 3001da177e4SLinus Torvalds return -ENOSPC; 3011da177e4SLinus Torvalds extent++; 3021da177e4SLinus Torvalds extent->start_block = cpu_to_be32(alloc_block); 3031da177e4SLinus Torvalds } else 3041da177e4SLinus Torvalds block_count += count; 3051da177e4SLinus Torvalds extent->block_count = cpu_to_be32(block_count); 3061da177e4SLinus Torvalds return 0; 3071da177e4SLinus Torvalds } else if (offset < count) 3081da177e4SLinus Torvalds break; 3091da177e4SLinus Torvalds offset -= count; 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds /* panic? */ 3121da177e4SLinus Torvalds return -EIO; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds static int hfsplus_free_extents(struct super_block *sb, 3161da177e4SLinus Torvalds struct hfsplus_extent *extent, 3171da177e4SLinus Torvalds u32 offset, u32 block_nr) 3181da177e4SLinus Torvalds { 3191da177e4SLinus Torvalds u32 count, start; 3201da177e4SLinus Torvalds int i; 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds hfsplus_dump_extent(extent); 3231da177e4SLinus Torvalds for (i = 0; i < 8; extent++, i++) { 3241da177e4SLinus Torvalds count = be32_to_cpu(extent->block_count); 3251da177e4SLinus Torvalds if (offset == count) 3261da177e4SLinus Torvalds goto found; 3271da177e4SLinus Torvalds else if (offset < count) 3281da177e4SLinus Torvalds break; 3291da177e4SLinus Torvalds offset -= count; 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds /* panic? */ 3321da177e4SLinus Torvalds return -EIO; 3331da177e4SLinus Torvalds found: 3341da177e4SLinus Torvalds for (;;) { 3351da177e4SLinus Torvalds start = be32_to_cpu(extent->start_block); 3361da177e4SLinus Torvalds if (count <= block_nr) { 3371da177e4SLinus Torvalds hfsplus_block_free(sb, start, count); 3381da177e4SLinus Torvalds extent->block_count = 0; 3391da177e4SLinus Torvalds extent->start_block = 0; 3401da177e4SLinus Torvalds block_nr -= count; 3411da177e4SLinus Torvalds } else { 3421da177e4SLinus Torvalds count -= block_nr; 3431da177e4SLinus Torvalds hfsplus_block_free(sb, start + count, block_nr); 3441da177e4SLinus Torvalds extent->block_count = cpu_to_be32(count); 3451da177e4SLinus Torvalds block_nr = 0; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds if (!block_nr || !i) 3481da177e4SLinus Torvalds return 0; 3491da177e4SLinus Torvalds i--; 3501da177e4SLinus Torvalds extent--; 3511da177e4SLinus Torvalds count = be32_to_cpu(extent->block_count); 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds } 3541da177e4SLinus Torvalds 3552753cc28SAnton Salikhmetov int hfsplus_free_fork(struct super_block *sb, u32 cnid, 3562753cc28SAnton Salikhmetov struct hfsplus_fork_raw *fork, int type) 3571da177e4SLinus Torvalds { 3581da177e4SLinus Torvalds struct hfs_find_data fd; 3591da177e4SLinus Torvalds hfsplus_extent_rec ext_entry; 3601da177e4SLinus Torvalds u32 total_blocks, blocks, start; 3611da177e4SLinus Torvalds int res, i; 3621da177e4SLinus Torvalds 3631da177e4SLinus Torvalds total_blocks = be32_to_cpu(fork->total_blocks); 3641da177e4SLinus Torvalds if (!total_blocks) 3651da177e4SLinus Torvalds return 0; 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds blocks = 0; 3681da177e4SLinus Torvalds for (i = 0; i < 8; i++) 3691da177e4SLinus Torvalds blocks += be32_to_cpu(fork->extents[i].block_count); 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds res = hfsplus_free_extents(sb, fork->extents, blocks, blocks); 3721da177e4SLinus Torvalds if (res) 3731da177e4SLinus Torvalds return res; 3741da177e4SLinus Torvalds if (total_blocks == blocks) 3751da177e4SLinus Torvalds return 0; 3761da177e4SLinus Torvalds 377dd73a01aSChristoph Hellwig hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); 3781da177e4SLinus Torvalds do { 3791da177e4SLinus Torvalds res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid, 3801da177e4SLinus Torvalds total_blocks, type); 3811da177e4SLinus Torvalds if (res) 3821da177e4SLinus Torvalds break; 3831da177e4SLinus Torvalds start = be32_to_cpu(fd.key->ext.start_block); 3841da177e4SLinus Torvalds hfsplus_free_extents(sb, ext_entry, 3851da177e4SLinus Torvalds total_blocks - start, 3861da177e4SLinus Torvalds total_blocks); 3871da177e4SLinus Torvalds hfs_brec_remove(&fd); 3881da177e4SLinus Torvalds total_blocks = start; 3891da177e4SLinus Torvalds } while (total_blocks > blocks); 3901da177e4SLinus Torvalds hfs_find_exit(&fd); 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds return res; 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds int hfsplus_file_extend(struct inode *inode) 3961da177e4SLinus Torvalds { 3971da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 398dd73a01aSChristoph Hellwig struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 3996af502deSChristoph Hellwig struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 4001da177e4SLinus Torvalds u32 start, len, goal; 4011da177e4SLinus Torvalds int res; 4021da177e4SLinus Torvalds 4031065348dSChristoph Hellwig if (sbi->alloc_file->i_size * 8 < 4041065348dSChristoph Hellwig sbi->total_blocks - sbi->free_blocks + 8) { 40521f2296aSAnton Salikhmetov /* extend alloc file */ 406b2837fcfSAnton Salikhmetov printk(KERN_ERR "hfs: extend alloc file! " 407b2837fcfSAnton Salikhmetov "(%llu,%u,%u)\n", 408dd73a01aSChristoph Hellwig sbi->alloc_file->i_size * 8, 409dd73a01aSChristoph Hellwig sbi->total_blocks, sbi->free_blocks); 4101da177e4SLinus Torvalds return -ENOSPC; 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds 4136af502deSChristoph Hellwig mutex_lock(&hip->extents_lock); 4146af502deSChristoph Hellwig if (hip->alloc_blocks == hip->first_blocks) 4156af502deSChristoph Hellwig goal = hfsplus_ext_lastblock(hip->first_extents); 4161da177e4SLinus Torvalds else { 4176af502deSChristoph Hellwig res = hfsplus_ext_read_extent(inode, hip->alloc_blocks); 4181da177e4SLinus Torvalds if (res) 4191da177e4SLinus Torvalds goto out; 4206af502deSChristoph Hellwig goal = hfsplus_ext_lastblock(hip->cached_extents); 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4236af502deSChristoph Hellwig len = hip->clump_blocks; 424dd73a01aSChristoph Hellwig start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len); 425dd73a01aSChristoph Hellwig if (start >= sbi->total_blocks) { 4261da177e4SLinus Torvalds start = hfsplus_block_allocate(sb, goal, 0, &len); 4271da177e4SLinus Torvalds if (start >= goal) { 4281da177e4SLinus Torvalds res = -ENOSPC; 4291da177e4SLinus Torvalds goto out; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); 4346af502deSChristoph Hellwig 4356af502deSChristoph Hellwig if (hip->alloc_blocks <= hip->first_blocks) { 4366af502deSChristoph Hellwig if (!hip->first_blocks) { 4371da177e4SLinus Torvalds dprint(DBG_EXTENT, "first extents\n"); 4381da177e4SLinus Torvalds /* no extents yet */ 4396af502deSChristoph Hellwig hip->first_extents[0].start_block = cpu_to_be32(start); 4406af502deSChristoph Hellwig hip->first_extents[0].block_count = cpu_to_be32(len); 4411da177e4SLinus Torvalds res = 0; 4421da177e4SLinus Torvalds } else { 4431da177e4SLinus Torvalds /* try to append to extents in inode */ 4446af502deSChristoph Hellwig res = hfsplus_add_extent(hip->first_extents, 4456af502deSChristoph Hellwig hip->alloc_blocks, 4461da177e4SLinus Torvalds start, len); 4471da177e4SLinus Torvalds if (res == -ENOSPC) 4481da177e4SLinus Torvalds goto insert_extent; 4491da177e4SLinus Torvalds } 4501da177e4SLinus Torvalds if (!res) { 4516af502deSChristoph Hellwig hfsplus_dump_extent(hip->first_extents); 4526af502deSChristoph Hellwig hip->first_blocks += len; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds } else { 4556af502deSChristoph Hellwig res = hfsplus_add_extent(hip->cached_extents, 4566af502deSChristoph Hellwig hip->alloc_blocks - hip->cached_start, 4571da177e4SLinus Torvalds start, len); 4581da177e4SLinus Torvalds if (!res) { 4596af502deSChristoph Hellwig hfsplus_dump_extent(hip->cached_extents); 460b33b7921SChristoph Hellwig hip->extent_state |= HFSPLUS_EXT_DIRTY; 4616af502deSChristoph Hellwig hip->cached_blocks += len; 4621da177e4SLinus Torvalds } else if (res == -ENOSPC) 4631da177e4SLinus Torvalds goto insert_extent; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds out: 4666af502deSChristoph Hellwig mutex_unlock(&hip->extents_lock); 4671da177e4SLinus Torvalds if (!res) { 4686af502deSChristoph Hellwig hip->alloc_blocks += len; 469e3494705SChristoph Hellwig hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); 4701da177e4SLinus Torvalds } 4711da177e4SLinus Torvalds return res; 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds insert_extent: 4741da177e4SLinus Torvalds dprint(DBG_EXTENT, "insert new extent\n"); 4757fcc99f4SChristoph Hellwig hfsplus_ext_write_extent_locked(inode); 4761da177e4SLinus Torvalds 4776af502deSChristoph Hellwig memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); 4786af502deSChristoph Hellwig hip->cached_extents[0].start_block = cpu_to_be32(start); 4796af502deSChristoph Hellwig hip->cached_extents[0].block_count = cpu_to_be32(len); 4806af502deSChristoph Hellwig hfsplus_dump_extent(hip->cached_extents); 481b33b7921SChristoph Hellwig hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW; 4826af502deSChristoph Hellwig hip->cached_start = hip->alloc_blocks; 4836af502deSChristoph Hellwig hip->cached_blocks = len; 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds res = 0; 4861da177e4SLinus Torvalds goto out; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds void hfsplus_file_truncate(struct inode *inode) 4901da177e4SLinus Torvalds { 4911da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 4926af502deSChristoph Hellwig struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 4931da177e4SLinus Torvalds struct hfs_find_data fd; 4941da177e4SLinus Torvalds u32 alloc_cnt, blk_cnt, start; 4951da177e4SLinus Torvalds int res; 4961da177e4SLinus Torvalds 497b2837fcfSAnton Salikhmetov dprint(DBG_INODE, "truncate: %lu, %llu -> %llu\n", 498b2837fcfSAnton Salikhmetov inode->i_ino, (long long)hip->phys_size, 499b2837fcfSAnton Salikhmetov inode->i_size); 5006af502deSChristoph Hellwig 5016af502deSChristoph Hellwig if (inode->i_size > hip->phys_size) { 5021da177e4SLinus Torvalds struct address_space *mapping = inode->i_mapping; 5031da177e4SLinus Torvalds struct page *page; 5047c0efc62SNick Piggin void *fsdata; 5057c0efc62SNick Piggin u32 size = inode->i_size; 5061da177e4SLinus Torvalds int res; 5071da177e4SLinus Torvalds 5087c0efc62SNick Piggin res = pagecache_write_begin(NULL, mapping, size, 0, 5097c0efc62SNick Piggin AOP_FLAG_UNINTERRUPTIBLE, 5107c0efc62SNick Piggin &page, &fsdata); 5111da177e4SLinus Torvalds if (res) 5127c0efc62SNick Piggin return; 5132753cc28SAnton Salikhmetov res = pagecache_write_end(NULL, mapping, size, 5142753cc28SAnton Salikhmetov 0, 0, page, fsdata); 5157c0efc62SNick Piggin if (res < 0) 5167c0efc62SNick Piggin return; 5171da177e4SLinus Torvalds mark_inode_dirty(inode); 5181da177e4SLinus Torvalds return; 5196af502deSChristoph Hellwig } else if (inode->i_size == hip->phys_size) 520f76d28d2SRoman Zippel return; 521f76d28d2SRoman Zippel 522dd73a01aSChristoph Hellwig blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >> 523dd73a01aSChristoph Hellwig HFSPLUS_SB(sb)->alloc_blksz_shift; 5246af502deSChristoph Hellwig alloc_cnt = hip->alloc_blocks; 5251da177e4SLinus Torvalds if (blk_cnt == alloc_cnt) 5261da177e4SLinus Torvalds goto out; 5271da177e4SLinus Torvalds 5286af502deSChristoph Hellwig mutex_lock(&hip->extents_lock); 529dd73a01aSChristoph Hellwig hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); 5301da177e4SLinus Torvalds while (1) { 5316af502deSChristoph Hellwig if (alloc_cnt == hip->first_blocks) { 5326af502deSChristoph Hellwig hfsplus_free_extents(sb, hip->first_extents, 5331da177e4SLinus Torvalds alloc_cnt, alloc_cnt - blk_cnt); 5346af502deSChristoph Hellwig hfsplus_dump_extent(hip->first_extents); 5356af502deSChristoph Hellwig hip->first_blocks = blk_cnt; 5361da177e4SLinus Torvalds break; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); 5391da177e4SLinus Torvalds if (res) 5401da177e4SLinus Torvalds break; 5416af502deSChristoph Hellwig start = hip->cached_start; 5426af502deSChristoph Hellwig hfsplus_free_extents(sb, hip->cached_extents, 5431da177e4SLinus Torvalds alloc_cnt - start, alloc_cnt - blk_cnt); 5446af502deSChristoph Hellwig hfsplus_dump_extent(hip->cached_extents); 5451da177e4SLinus Torvalds if (blk_cnt > start) { 546b33b7921SChristoph Hellwig hip->extent_state |= HFSPLUS_EXT_DIRTY; 5471da177e4SLinus Torvalds break; 5481da177e4SLinus Torvalds } 5491da177e4SLinus Torvalds alloc_cnt = start; 5506af502deSChristoph Hellwig hip->cached_start = hip->cached_blocks = 0; 551b33b7921SChristoph Hellwig hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); 5521da177e4SLinus Torvalds hfs_brec_remove(&fd); 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds hfs_find_exit(&fd); 5556af502deSChristoph Hellwig mutex_unlock(&hip->extents_lock); 5561da177e4SLinus Torvalds 5576af502deSChristoph Hellwig hip->alloc_blocks = blk_cnt; 5581da177e4SLinus Torvalds out: 5596af502deSChristoph Hellwig hip->phys_size = inode->i_size; 5602753cc28SAnton Salikhmetov hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> 5612753cc28SAnton Salikhmetov sb->s_blocksize_bits; 5626af502deSChristoph Hellwig inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); 563e3494705SChristoph Hellwig hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); 5641da177e4SLinus Torvalds } 565