static int hfsplus_add_extent(struct hfsplus_extent *extent, u32 offset, u32 alloc_block, u32 block_count) { u32 count, start; int i; hfsplus_dump_extent(extent); for (i = 0; i < 8; extent++, i++) { count = be32_to_cpu(extent->block_count); if (offset == count) { start = be32_to_cpu(extent->start_block); if (alloc_block != start + count) { if (++i >= 8) return -ENOSPC; extent++; extent->start_block = cpu_to_be32(alloc_block); } else block_count += count; extent->block_count = cpu_to_be32(block_count); return 0; } else if (offset < count) break; offset -= count; } /* panic? */ return -EIO; }
static int hfsplus_free_extents(struct super_block *sb, struct hfsplus_extent *extent, u32 offset, u32 block_nr) { u32 count, start; int i; int err = 0; hfsplus_dump_extent(extent); for (i = 0; i < 8; extent++, i++) { count = be32_to_cpu(extent->block_count); if (offset == count) goto found; else if (offset < count) break; offset -= count; } /* panic? */ return -EIO; found: for (;;) { start = be32_to_cpu(extent->start_block); if (count <= block_nr) { err = hfsplus_block_free(sb, start, count); if (err) { pr_err("can't free extent\n"); hfs_dbg(EXTENT, " start: %u count: %u\n", start, count); } extent->block_count = 0; extent->start_block = 0; block_nr -= count; } else { count -= block_nr; err = hfsplus_block_free(sb, start + count, block_nr); if (err) { pr_err("can't free extent\n"); hfs_dbg(EXTENT, " start: %u count: %u\n", start, count); } extent->block_count = cpu_to_be32(count); block_nr = 0; } if (!block_nr || !i) { /* * Try to free all extents and * return only last error */ return err; } i--; extent--; count = be32_to_cpu(extent->block_count); } }
static int hfsplus_free_extents(hfsplus_handle_t *hfsplus_handle, struct super_block *sb, struct hfsplus_extent *extent, u32 offset, u32 block_nr) { u32 count, start; int i; hfsplus_dump_extent(extent); for (i = 0; i < 8; extent++, i++) { count = be32_to_cpu(extent->block_count); if (offset == count) goto found; else if (offset < count) break; offset -= count; } /* panic? */ printk("#### Return EIO from %s()\n", __FUNCTION__); return -EIO; found: for (;;) { start = be32_to_cpu(extent->start_block); if (count <= block_nr) { hfsplus_block_free(hfsplus_handle, sb, start, count); extent->block_count = 0; extent->start_block = 0; block_nr -= count; } else { count -= block_nr; hfsplus_block_free(hfsplus_handle, sb, start + count, block_nr); extent->block_count = cpu_to_be32(count); block_nr = 0; } if (!block_nr || !i) return 0; i--; extent--; count = be32_to_cpu(extent->block_count); } }
void hfsplus_file_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; struct hfs_find_data fd; u32 alloc_cnt, blk_cnt, start; int res; dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, (long long)HFSPLUS_I(inode).phys_size, inode->i_size); if (inode->i_size > HFSPLUS_I(inode).phys_size) { struct address_space *mapping = inode->i_mapping; struct page *page; u32 size = inode->i_size - 1; int res; page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); if (!page) return; size &= PAGE_CACHE_SIZE - 1; size++; res = mapping->a_ops->prepare_write(NULL, page, size, size); if (!res) res = mapping->a_ops->commit_write(NULL, page, size, size); if (res) inode->i_size = HFSPLUS_I(inode).phys_size; unlock_page(page); page_cache_release(page); mark_inode_dirty(inode); return; } blk_cnt = (inode->i_size + HFSPLUS_SB(sb).alloc_blksz - 1) >> HFSPLUS_SB(sb).alloc_blksz_shift; alloc_cnt = HFSPLUS_I(inode).alloc_blocks; if (blk_cnt == alloc_cnt) goto out; down(&HFSPLUS_I(inode).extents_lock); hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd); while (1) { if (alloc_cnt == HFSPLUS_I(inode).first_blocks) { hfsplus_free_extents(sb, HFSPLUS_I(inode).first_extents, alloc_cnt, alloc_cnt - blk_cnt); hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); HFSPLUS_I(inode).first_blocks = blk_cnt; break; } res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); if (res) break; start = HFSPLUS_I(inode).cached_start; hfsplus_free_extents(sb, HFSPLUS_I(inode).cached_extents, alloc_cnt - start, alloc_cnt - blk_cnt); hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); if (blk_cnt > start) { HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; break; } alloc_cnt = start; HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); hfs_brec_remove(&fd); } hfs_find_exit(&fd); up(&HFSPLUS_I(inode).extents_lock); HFSPLUS_I(inode).alloc_blocks = blk_cnt; out: HFSPLUS_I(inode).phys_size = inode->i_size; mark_inode_dirty(inode); inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; }
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; }
void hfsplus_file_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfs_find_data fd; u32 alloc_cnt, blk_cnt, start; int res; hfs_dbg(INODE, "truncate: %lu, %llu -> %llu\n", inode->i_ino, (long long)hip->phys_size, inode->i_size); if (inode->i_size > hip->phys_size) { struct address_space *mapping = inode->i_mapping; struct page *page; void *fsdata; loff_t size = inode->i_size; res = pagecache_write_begin(NULL, mapping, size, 0, AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); if (res) return; res = pagecache_write_end(NULL, mapping, size, 0, 0, page, fsdata); if (res < 0) return; mark_inode_dirty(inode); return; } else if (inode->i_size == hip->phys_size) return; blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >> HFSPLUS_SB(sb)->alloc_blksz_shift; mutex_lock(&hip->extents_lock); alloc_cnt = hip->alloc_blocks; if (blk_cnt == alloc_cnt) goto out_unlock; res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); if (res) { mutex_unlock(&hip->extents_lock); /* XXX: We lack error handling of hfsplus_file_truncate() */ return; } while (1) { if (alloc_cnt == hip->first_blocks) { hfsplus_free_extents(sb, hip->first_extents, alloc_cnt, alloc_cnt - blk_cnt); hfsplus_dump_extent(hip->first_extents); hip->first_blocks = blk_cnt; break; } res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); if (res) break; start = hip->cached_start; hfsplus_free_extents(sb, hip->cached_extents, alloc_cnt - start, alloc_cnt - blk_cnt); hfsplus_dump_extent(hip->cached_extents); if (blk_cnt > start) { hip->extent_state |= HFSPLUS_EXT_DIRTY; break; } alloc_cnt = start; hip->cached_start = hip->cached_blocks = 0; hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); hfs_brec_remove(&fd); } hfs_find_exit(&fd); hip->alloc_blocks = blk_cnt; out_unlock: mutex_unlock(&hip->extents_lock); hip->phys_size = inode->i_size; hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); }
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; }