int hfsplus_journaled_get_block(struct page *page) { struct inode * const inode = page->mapping->host; struct super_block * const sb = inode->i_sb; struct hfsplus_inode_info *hip = HFSPLUS_I(inode); u32 ablock, res; sector_t iblock; s32 block_num = -1; iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); ablock = iblock >> HFSPLUS_SB(sb)->fs_shift; if (ablock < hip->first_blocks) { block_num = hfsplus_ext_find_block(hip->first_extents, ablock); } else { mutex_lock(&hip->extents_lock); res = hfsplus_ext_read_extent(inode, ablock); if (!res) { mutex_lock(&hip->extents_lock); block_num = hfsplus_ext_find_block(hip->cached_extents, ablock - hip->cached_start); mutex_unlock(&hip->extents_lock); } else { mutex_unlock(&hip->extents_lock); } mutex_unlock(&hip->extents_lock); } return block_num; }
/* Get a block at iblock for inode, possibly allocating if create */ int hfsplus_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct super_block *sb; int res = -EIO; u32 ablock, dblock, mask; int shift; sb = inode->i_sb; /* Convert inode block to disk allocation block */ shift = HFSPLUS_SB(sb).alloc_blksz_shift - sb->s_blocksize_bits; ablock = iblock >> HFSPLUS_SB(sb).fs_shift; if (iblock >= inode->i_blocks) { if (iblock > inode->i_blocks || !create) return -EIO; if (ablock >= HFSPLUS_I(inode).alloc_blocks) { res = hfsplus_file_extend(inode); if (res) return res; } } else create = 0; if (ablock < HFSPLUS_I(inode).first_blocks) { dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock); goto done; } down(&HFSPLUS_I(inode).extents_lock); res = hfsplus_ext_read_extent(inode, ablock); if (!res) { dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock - HFSPLUS_I(inode).cached_start); } else { up(&HFSPLUS_I(inode).extents_lock); return -EIO; } up(&HFSPLUS_I(inode).extents_lock); done: dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); mask = (1 << HFSPLUS_SB(sb).fs_shift) - 1; map_bh(bh_result, sb, (dblock << HFSPLUS_SB(sb).fs_shift) + HFSPLUS_SB(sb).blockoffset + (iblock & mask)); if (create) { set_buffer_new(bh_result); HFSPLUS_I(inode).phys_size += sb->s_blocksize; inode->i_blocks++; mark_inode_dirty(inode); } return 0; }
int hfsplus_file_extend(struct inode *inode) { struct super_block *sb = inode->i_sb; u32 start, len, goal; int res; if (HFSPLUS_SB(sb).alloc_file->i_size * 8 < HFSPLUS_SB(sb).total_blocks - HFSPLUS_SB(sb).free_blocks + 8) { // extend alloc file printk("extend alloc file! (%Lu,%u,%u)\n", HFSPLUS_SB(sb).alloc_file->i_size * 8, HFSPLUS_SB(sb).total_blocks, HFSPLUS_SB(sb).free_blocks); return -ENOSPC; //BUG(); } down(&HFSPLUS_I(inode).extents_lock); if (HFSPLUS_I(inode).alloc_blocks == HFSPLUS_I(inode).first_blocks) goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).first_extents); else { res = hfsplus_ext_read_extent(inode, HFSPLUS_I(inode).alloc_blocks); if (res) goto out; goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).cached_extents); } len = HFSPLUS_I(inode).clump_blocks; start = hfsplus_block_allocate(sb, HFSPLUS_SB(sb).total_blocks, goal, &len); if (start >= HFSPLUS_SB(sb).total_blocks) { start = hfsplus_block_allocate(sb, goal, 0, &len); if (start >= goal) { res = -ENOSPC; goto out; } } dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).first_blocks) { if (!HFSPLUS_I(inode).first_blocks) { dprint(DBG_EXTENT, "first extents\n"); /* no extents yet */ HFSPLUS_I(inode).first_extents[0].start_block = cpu_to_be32(start); HFSPLUS_I(inode).first_extents[0].block_count = cpu_to_be32(len); res = 0; } else { /* try to append to extents in inode */ res = hfsplus_add_extent(HFSPLUS_I(inode).first_extents, HFSPLUS_I(inode).alloc_blocks, start, len); if (res == -ENOSPC) goto insert_extent; } if (!res) { hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); HFSPLUS_I(inode).first_blocks += len; } } else { res = hfsplus_add_extent(HFSPLUS_I(inode).cached_extents, HFSPLUS_I(inode).alloc_blocks - HFSPLUS_I(inode).cached_start, start, len); if (!res) { hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; HFSPLUS_I(inode).cached_blocks += len; } else if (res == -ENOSPC) goto insert_extent; } out: up(&HFSPLUS_I(inode).extents_lock); if (!res) { HFSPLUS_I(inode).alloc_blocks += len; mark_inode_dirty(inode); } return res; insert_extent: dprint(DBG_EXTENT, "insert new extent\n"); hfsplus_ext_write_extent(inode); memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); HFSPLUS_I(inode).cached_extents[0].start_block = cpu_to_be32(start); HFSPLUS_I(inode).cached_extents[0].block_count = cpu_to_be32(len); hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW; HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).alloc_blocks; HFSPLUS_I(inode).cached_blocks = len; res = 0; goto out; }
int hfsplus_file_extend(struct inode *inode, bool zeroout) { struct super_block *sb = inode->i_sb; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct hfsplus_inode_info *hip = HFSPLUS_I(inode); u32 start, len, goal; int res; if (sbi->alloc_file->i_size * 8 < sbi->total_blocks - sbi->free_blocks + 8) { /* extend alloc file */ pr_err("extend alloc file! (%llu,%u,%u)\n", sbi->alloc_file->i_size * 8, sbi->total_blocks, sbi->free_blocks); return -ENOSPC; } mutex_lock(&hip->extents_lock); if (hip->alloc_blocks == hip->first_blocks) goal = hfsplus_ext_lastblock(hip->first_extents); else { res = hfsplus_ext_read_extent(inode, hip->alloc_blocks); if (res) goto out; goal = hfsplus_ext_lastblock(hip->cached_extents); } len = hip->clump_blocks; start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len); if (start >= sbi->total_blocks) { start = hfsplus_block_allocate(sb, goal, 0, &len); if (start >= goal) { res = -ENOSPC; goto out; } } if (zeroout) { res = sb_issue_zeroout(sb, start, len, GFP_NOFS); if (res) goto out; } hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); if (hip->alloc_blocks <= hip->first_blocks) { if (!hip->first_blocks) { hfs_dbg(EXTENT, "first extents\n"); /* no extents yet */ hip->first_extents[0].start_block = cpu_to_be32(start); hip->first_extents[0].block_count = cpu_to_be32(len); res = 0; } else { /* try to append to extents in inode */ res = hfsplus_add_extent(hip->first_extents, hip->alloc_blocks, start, len); if (res == -ENOSPC) goto insert_extent; } if (!res) { hfsplus_dump_extent(hip->first_extents); hip->first_blocks += len; } } else { res = hfsplus_add_extent(hip->cached_extents, hip->alloc_blocks - hip->cached_start, start, len); if (!res) { hfsplus_dump_extent(hip->cached_extents); hip->extent_state |= HFSPLUS_EXT_DIRTY; hip->cached_blocks += len; } else if (res == -ENOSPC) goto insert_extent; } out: if (!res) { hip->alloc_blocks += len; mutex_unlock(&hip->extents_lock); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); return 0; } mutex_unlock(&hip->extents_lock); return res; insert_extent: hfs_dbg(EXTENT, "insert new extent\n"); res = hfsplus_ext_write_extent_locked(inode); if (res) goto out; memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); hip->cached_extents[0].start_block = cpu_to_be32(start); hip->cached_extents[0].block_count = cpu_to_be32(len); hfsplus_dump_extent(hip->cached_extents); hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW; hip->cached_start = hip->alloc_blocks; hip->cached_blocks = len; res = 0; goto out; }
/* Get a block at iblock for inode, possibly allocating if create */ int hfsplus_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res = -EIO; u32 ablock, dblock, mask; sector_t sector; int was_dirty = 0; /* Convert inode block to disk allocation block */ ablock = iblock >> sbi->fs_shift; if (iblock >= hip->fs_blocks) { if (iblock > hip->fs_blocks || !create) return -EIO; if (ablock >= hip->alloc_blocks) { res = hfsplus_file_extend(inode, false); if (res) return res; } } else create = 0; if (ablock < hip->first_blocks) { dblock = hfsplus_ext_find_block(hip->first_extents, ablock); goto done; } if (inode->i_ino == HFSPLUS_EXT_CNID) return -EIO; mutex_lock(&hip->extents_lock); /* * hfsplus_ext_read_extent will write out a cached extent into * the extents btree. In that case we may have to mark the inode * dirty even for a pure read of an extent here. */ was_dirty = (hip->extent_state & HFSPLUS_EXT_DIRTY); res = hfsplus_ext_read_extent(inode, ablock); if (res) { mutex_unlock(&hip->extents_lock); return -EIO; } dblock = hfsplus_ext_find_block(hip->cached_extents, ablock - hip->cached_start); mutex_unlock(&hip->extents_lock); done: hfs_dbg(EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); mask = (1 << sbi->fs_shift) - 1; sector = ((sector_t)dblock << sbi->fs_shift) + sbi->blockoffset + (iblock & mask); map_bh(bh_result, sb, sector); if (create) { set_buffer_new(bh_result); hip->phys_size += sb->s_blocksize; hip->fs_blocks++; inode_add_bytes(inode, sb->s_blocksize); } if (create || was_dirty) mark_inode_dirty(inode); return 0; }
/* Get a block at iblock for inode, possibly allocating if create */ int hfsplus_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct super_block *sb; int res = -EIO; u32 ablock, dblock, mask; int shift; hfsplus_handle_t *hfsplus_handle, tmp_hfsplus_handle; tmp_hfsplus_handle.journaled = !HFSPLUS_JOURNAL_PRESENT; tmp_hfsplus_handle.handle = NULL; sb = inode->i_sb; /* Journal device */ if (HFSPLUS_SB(sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT) { /* Write Metadata */ if (((inode->i_mapping->a_ops == &hfsplus_journalled_btree_aops) || (inode->i_mapping->a_ops == &hfsplus_journalled_aops)) && create) { hfsplus_handle = hfsplus_jbd_current_handle(); if (hfsplus_handle == NULL) { printk("hfsplus_handle is NULL\n"); hfsplus_handle = &tmp_hfsplus_handle; } } else { hfsplus_handle = &tmp_hfsplus_handle; } } /* Non-journal device */ else { hfsplus_handle = &tmp_hfsplus_handle; } /* Convert inode block to disk allocation block */ shift = HFSPLUS_SB(sb).alloc_blksz_shift - sb->s_blocksize_bits; ablock = iblock >> HFSPLUS_SB(sb).fs_shift; if (iblock >= HFSPLUS_I(inode).fs_blocks) { if (iblock > HFSPLUS_I(inode).fs_blocks || !create) { return -EIO; } if (ablock >= HFSPLUS_I(inode).alloc_blocks) { res = hfsplus_file_extend(hfsplus_handle, inode); if (res) { return res; } } } else create = 0; if (ablock < HFSPLUS_I(inode).first_blocks) { dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock); goto done; } mutex_lock(&HFSPLUS_I(inode).extents_lock); res = hfsplus_ext_read_extent(hfsplus_handle, inode, ablock); if (!res) { dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock - HFSPLUS_I(inode).cached_start); } else { mutex_unlock(&HFSPLUS_I(inode).extents_lock); return -EIO; } mutex_unlock(&HFSPLUS_I(inode).extents_lock); done: dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); mask = (1 << HFSPLUS_SB(sb).fs_shift) - 1; map_bh(bh_result, sb, (dblock << HFSPLUS_SB(sb).fs_shift) + HFSPLUS_SB(sb).blockoffset + (iblock & mask)); if (create) { set_buffer_new(bh_result); HFSPLUS_I(inode).phys_size += sb->s_blocksize; HFSPLUS_I(inode).fs_blocks++; inode_add_bytes(inode, sb->s_blocksize); if (hfsplus_journalled_mark_inode_dirty(__FUNCTION__, hfsplus_handle, inode)) { printk("HFS+-fs: Error in %s()\n", __FUNCTION__); return -1; } } return 0; }