static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; struct qstr str; char name[32]; u32 cnid; int res; hfsplus_handle_t hfsplus_handle; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && atomic_read(&HFSPLUS_I(inode).opencnt)) { str.name = name; str.len = sprintf(name, "temp%lu", inode->i_ino); res = hfsplus_rename_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, HFSPLUS_SB(sb).hidden_dir, &str); if (!res) inode->i_flags |= S_DEAD; hfsplus_journal_stop(&hfsplus_handle); return res; } res = hfsplus_delete_cat(&hfsplus_handle, cnid, dir, &dentry->d_name); if (res) { hfsplus_journal_stop(&hfsplus_handle); return res; } if (inode->i_nlink > 0) drop_nlink(inode); if (inode->i_ino == cnid) clear_nlink(inode); if (!inode->i_nlink) { if (inode->i_ino != cnid) { HFSPLUS_SB(sb).file_count--; if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { res = hfsplus_delete_cat(&hfsplus_handle, inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); if (!res) hfsplus_delete_inode(&hfsplus_handle, inode); } else inode->i_flags |= S_DEAD; } else hfsplus_delete_inode(&hfsplus_handle,inode); } else HFSPLUS_SB(sb).file_count--; inode->i_ctime = CURRENT_TIME_SEC; res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return res; }
static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode = dentry->d_inode; struct qstr str; char name[32]; u32 cnid; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; mutex_lock(&sbi->vh_mutex); cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && atomic_read(&HFSPLUS_I(inode)->opencnt)) { str.name = name; str.len = sprintf(name, "temp%lu", inode->i_ino); res = hfsplus_rename_cat(inode->i_ino, dir, &dentry->d_name, sbi->hidden_dir, &str); if (!res) { inode->i_flags |= S_DEAD; drop_nlink(inode); } goto out; } res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); if (res) goto out; if (inode->i_nlink > 0) drop_nlink(inode); if (inode->i_ino == cnid) clear_nlink(inode); if (!inode->i_nlink) { if (inode->i_ino != cnid) { sbi->file_count--; if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) { res = hfsplus_delete_cat(inode->i_ino, sbi->hidden_dir, NULL); if (!res) hfsplus_delete_inode(inode); } else inode->i_flags |= S_DEAD; } else hfsplus_delete_inode(inode); } else sbi->file_count--; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); out: mutex_unlock(&sbi->vh_mutex); return res; }
int hfsplus_ext_write_extent(struct inode *inode) { int res; mutex_lock(&HFSPLUS_I(inode)->extents_lock); res = hfsplus_ext_write_extent_locked(inode); mutex_unlock(&HFSPLUS_I(inode)->extents_lock); return res; }
static void hfsplus_evict_inode(struct inode *inode) { dprint(DBG_INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino); truncate_inode_pages(&inode->i_data, 0); end_writeback(inode); if (HFSPLUS_IS_RSRC(inode)) { HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL; iput(HFSPLUS_I(inode)->rsrc_inode); } }
static int hfsplus_dir_release(struct inode *inode, struct file *file) { struct hfsplus_readdir_data *rd = file->private_data; if (rd) { spin_lock(&HFSPLUS_I(inode)->open_dir_lock); list_del(&rd->list); spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); kfree(rd); } return 0; }
static int hfsplus_ext_read_extent(struct inode *inode, u32 block) { struct hfs_find_data fd; int res; if (block >= HFSPLUS_I(inode).cached_start && block < HFSPLUS_I(inode).cached_start + HFSPLUS_I(inode).cached_blocks) return 0; hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd); res = __hfsplus_ext_cache_extent(&fd, inode, block); hfs_find_exit(&fd); return res; }
/* 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; }
static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) { struct inode *inode = file_inode(file); struct hfsplus_inode_info *hip = HFSPLUS_I(inode); unsigned int flags, new_fl = 0; int err = 0; err = mnt_want_write_file(file); if (err) goto out; if (!inode_owner_or_capable(inode)) { err = -EACCES; goto out_drop_write; } if (get_user(flags, user_flags)) { err = -EFAULT; goto out_drop_write; } mutex_lock(&inode->i_mutex); if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || inode->i_flags & (S_IMMUTABLE|S_APPEND)) { if (!capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto out_unlock_inode; } } /* don't silently ignore unsupported ext2 flags */ if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { err = -EOPNOTSUPP; goto out_unlock_inode; } if (flags & FS_IMMUTABLE_FL) new_fl |= S_IMMUTABLE; if (flags & FS_APPEND_FL) new_fl |= S_APPEND; inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND); if (flags & FS_NODUMP_FL) hip->userflags |= HFSPLUS_FLG_NODUMP; else hip->userflags &= ~HFSPLUS_FLG_NODUMP; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); out_unlock_inode: mutex_unlock(&inode->i_mutex); out_drop_write: mnt_drop_write_file(file); out: return err; }
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; }
static void hfsplus_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); INIT_LIST_HEAD(&inode->i_dentry); kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode)); }
static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) { struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res; WARN_ON(!mutex_is_locked(&hip->extents_lock)); if (hip->extent_state & HFSPLUS_EXT_DIRTY) { res = __hfsplus_ext_write_extent(inode, fd); if (res) return res; } res = __hfsplus_ext_read_extent(fd, hip->cached_extents, inode->i_ino, block, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); if (!res) { hip->cached_start = be32_to_cpu(fd->key->ext.start_block); hip->cached_blocks = hfsplus_ext_block_count(hip->cached_extents); } else { hip->cached_start = hip->cached_blocks = 0; hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); } return res; }
static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) { struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res; WARN_ON(!mutex_is_locked(&hip->extents_lock)); hfsplus_ext_build_key(fd->search_key, inode->i_ino, hip->cached_start, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); res = hfs_brec_find(fd); if (hip->extent_state & HFSPLUS_EXT_NEW) { if (res != -ENOENT) return; hfs_brec_insert(fd, hip->cached_extents, sizeof(hfsplus_extent_rec)); hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); } else { if (res) return; hfs_bnode_write(fd->bnode, hip->cached_extents, fd->entryoffset, fd->entrylength); hip->extent_state &= ~HFSPLUS_EXT_DIRTY; } /* * We can't just use hfsplus_mark_inode_dirty here, because we * also get called from hfsplus_write_inode, which should not * redirty the inode. Instead the callers have to be careful * to explicily mark the inode dirty, too. */ set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags); }
static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; struct qstr str; char name[32]; u32 cnid; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && atomic_read(&HFSPLUS_I(inode).opencnt)) { str.name = name; str.len = sprintf(name, "temp%lu", inode->i_ino); res = hfsplus_rename_cat(inode->i_ino, dir, &dentry->d_name, HFSPLUS_SB(sb).hidden_dir, &str); if (!res) inode->i_flags |= S_DEAD; return res; } res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); if (res) return res; if (inode->i_nlink > 0) drop_nlink(inode); hfsplus_delete_inode(inode); if (inode->i_ino != cnid && !inode->i_nlink) { if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); if (!res) hfsplus_delete_inode(inode); } else inode->i_flags |= S_DEAD; } else clear_nlink(inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); return res; }
static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb); struct inode *inode = src_dentry->d_inode; struct inode *src_dir = src_dentry->d_parent->d_inode; struct qstr str; char name[32]; u32 cnid, id; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if (!S_ISREG(inode->i_mode)) return -EPERM; mutex_lock(&sbi->vh_mutex); if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { get_random_bytes(&id, sizeof(cnid)); id &= 0x3fffffff; str.name = name; str.len = sprintf(name, "iNode%d", id); res = hfsplus_rename_cat(inode->i_ino, src_dir, &src_dentry->d_name, sbi->hidden_dir, &str); if (!res) break; if (res != -EEXIST) goto out; } HFSPLUS_I(inode)->linkid = id; cnid = sbi->next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); if (res) /* panic? */ goto out; sbi->file_count++; } cnid = sbi->next_cnid++; res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); if (res) goto out; inc_nlink(inode); hfsplus_instantiate(dst_dentry, inode, cnid); ihold(inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); sbi->file_count++; hfsplus_mark_mdb_dirty(dst_dir->i_sb); out: mutex_unlock(&sbi->vh_mutex); return res; }
static int hfsplus_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { *pagep = NULL; return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfsplus_get_block, &HFSPLUS_I(mapping->host).phys_size); }
void hfsplus_ext_write_extent(struct inode *inode) { if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) { struct hfs_find_data fd; hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd); __hfsplus_ext_write_extent(inode, &fd); hfs_find_exit(&fd); } }
static void hfsplus_ext_write_extent_locked(struct inode *inode) { if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) { struct hfs_find_data fd; hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); __hfsplus_ext_write_extent(inode, &fd); hfs_find_exit(&fd); } }
static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { struct super_block *sb = dst_dir->i_sb; struct inode *inode = src_dentry->d_inode; struct inode *src_dir = src_dentry->d_parent->d_inode; struct qstr str; char name[32]; u32 cnid, id; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { get_random_bytes(&id, sizeof(cnid)); id &= 0x3fffffff; str.name = name; str.len = sprintf(name, "iNode%d", id); res = hfsplus_rename_cat(inode->i_ino, src_dir, &src_dentry->d_name, HFSPLUS_SB(sb).hidden_dir, &str); if (!res) break; if (res != -EEXIST) return res; } HFSPLUS_I(inode).dev = id; cnid = HFSPLUS_SB(sb).next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); if (res) /* panic? */ return res; HFSPLUS_SB(sb).file_count++; } cnid = HFSPLUS_SB(sb).next_cnid++; res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); if (res) return res; inc_nlink(inode); hfsplus_instantiate(dst_dentry, inode, cnid); atomic_inc(&inode->i_count); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); HFSPLUS_SB(sb).file_count++; sb->s_dirt = 1; return 0; }
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; hfsplus_handle_t hfsplus_handle; 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; void *fsdata; 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->write_begin(NULL, mapping, size, 0, AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); if (!res) res = mapping->a_ops->write_end(NULL, mapping, size, 0, 0, page, fsdata); if (res) inode->i_size = HFSPLUS_I(inode).phys_size; unlock_page(page); page_cache_release(page); if (hfsplus_journal_start(__FUNCTION__, sb, &hfsplus_handle)) return; hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return; } else if (inode->i_size == HFSPLUS_I(inode).phys_size)
static int hfsplus_ext_write_extent_locked(struct inode *inode) { int res = 0; if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) { struct hfs_find_data fd; res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); if (res) return res; res = __hfsplus_ext_write_extent(inode, &fd); hfs_find_exit(&fd); } return res; }
static int hfsplus_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { int ret; *pagep = NULL; ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfsplus_get_block, &HFSPLUS_I(mapping->host)->phys_size); if (unlikely(ret)) hfsplus_write_failed(mapping, pos + len); return ret; }
static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) { struct inode *inode = file->f_path.dentry->d_inode; struct hfsplus_inode_info *hip = HFSPLUS_I(inode); unsigned int flags = 0; if (inode->i_flags & S_IMMUTABLE) flags |= FS_IMMUTABLE_FL; if (inode->i_flags & S_APPEND) flags |= FS_APPEND_FL; if (hip->userflags & HFSPLUS_FLG_NODUMP) flags |= FS_NODUMP_FL; return put_user(flags, user_flags); }
static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) { int res; if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) __hfsplus_ext_write_extent(inode, fd); res = __hfsplus_ext_read_extent(fd, HFSPLUS_I(inode).cached_extents, inode->i_ino, block, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); if (!res) { HFSPLUS_I(inode).cached_start = be32_to_cpu(fd->key->ext.start_block); HFSPLUS_I(inode).cached_blocks = hfsplus_ext_block_count(HFSPLUS_I(inode).cached_extents); } else { HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); } return res; }
static int hfsplus_ext_read_extent(struct inode *inode, u32 block) { struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfs_find_data fd; int res; if (block >= hip->cached_start && block < hip->cached_start + hip->cached_blocks) return 0; res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); if (!res) { res = __hfsplus_ext_cache_extent(&fd, inode, block); hfs_find_exit(&fd); } return res; }
static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) { int res; hfsplus_ext_build_key(fd->search_key, inode->i_ino, HFSPLUS_I(inode).cached_start, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); res = hfs_brec_find(fd); if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_NEW) { if (res != -ENOENT) return; hfs_brec_insert(fd, HFSPLUS_I(inode).cached_extents, sizeof(hfsplus_extent_rec)); HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); } else { if (res) return; hfs_bnode_write(fd->bnode, HFSPLUS_I(inode).cached_extents, fd->entryoffset, fd->entrylength); HFSPLUS_I(inode).flags &= ~HFSPLUS_FLG_EXT_DIRTY; } }
struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) { struct hfs_find_data fd; struct inode *inode; int err; inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); mutex_init(&HFSPLUS_I(inode)->extents_lock); HFSPLUS_I(inode)->flags = 0; HFSPLUS_I(inode)->extent_state = 0; HFSPLUS_I(inode)->rsrc_inode = NULL; atomic_set(&HFSPLUS_I(inode)->opencnt, 0); if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || inode->i_ino == HFSPLUS_ROOT_CNID) { hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); if (!err) err = hfsplus_cat_read_inode(inode, &fd); hfs_find_exit(&fd); } else { err = hfsplus_system_read_inode(inode); } if (err) { iget_failed(inode); return ERR_PTR(err); } unlock_new_inode(inode); return inode; }
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; }
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; }
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); }