Oops, sorry for the subject. It should have been just [PATCH] instead of [PATCH 5/7]... On Tue 22-09-09 19:42:05, Jan Kara wrote: > CC: linux-ext4@xxxxxxxxxxxxxxx > CC: tytso@xxxxxxx > Signed-off-by: Jan Kara <jack@xxxxxxx> > --- > fs/ext4/file.c | 2 +- > fs/ext4/inode.c | 166 ++++++++++++++++++++++++++++++++---------------------- > 2 files changed, 99 insertions(+), 69 deletions(-) > > diff --git a/fs/ext4/file.c b/fs/ext4/file.c > index 3f1873f..22f49d7 100644 > --- a/fs/ext4/file.c > +++ b/fs/ext4/file.c > @@ -198,7 +198,7 @@ const struct file_operations ext4_file_operations = { > }; > > const struct inode_operations ext4_file_inode_operations = { > - .truncate = ext4_truncate, > + .new_truncate = 1, > .setattr = ext4_setattr, > .getattr = ext4_getattr, > #ifdef CONFIG_EXT4_FS_XATTR > diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c > index 58492ab..be25874 100644 > --- a/fs/ext4/inode.c > +++ b/fs/ext4/inode.c > @@ -4682,28 +4686,97 @@ int ext4_write_inode(struct inode *inode, int wait) > } > > /* > - * ext4_setattr() > + * ext4_setsize() > + * > + * This is a helper for ext4_setattr(). It sets i_size, truncates page cache > + * and truncates inode blocks if they are over i_size. > * > - * Called from notify_change. > + * We take care of updating i_disksize and adding inode to the orphan list. > + * That makes sure that we can guarantee that any commit will leave the blocks > + * being truncated in an unused state on disk. (On recovery, the inode will > + * get truncated and the blocks will be freed, so we have a strong guarantee > + * that no future commit will leave these blocks visible to the user.) > * > - * We want to trap VFS attempts to truncate the file as soon as > - * possible. In particular, we want to make sure that when the VFS > - * shrinks i_size, we put the inode on the orphan list and modify > - * i_disksize immediately, so that during the subsequent flushing of > - * dirty pages and freeing of disk blocks, we can guarantee that any > - * commit will leave the blocks being flushed in an unused state on > - * disk. (On recovery, the inode will get truncated and the blocks will > - * be freed, so we have a strong guarantee that no future commit will > - * leave these blocks visible to the user.) > + * Another thing we have to assure is that if we are in ordered mode and inode > + * is still attached to the committing transaction, we must we start writeout > + * of all the dirty pages which are being truncated. This way we are sure that > + * all the data written in the previous transaction are already on disk > + * (truncate waits for pages under writeback). > + */ > +static int ext4_setsize(struct inode *inode, loff_t newsize) > +{ > + int error = 0, rc; > + loff_t oldsize = inode->i_size; > + handle_t *handle; > + > + error = inode_newsize_ok(inode, newsize); > + if (error) > + goto out; > + /* VFS should have checked these and return error... */ > + WARN_ON(!S_ISREG(inode->i_mode) || IS_APPEND(inode) || > + IS_IMMUTABLE(inode)); > + > + if (newsize < oldsize) { > + handle = ext4_journal_start(inode, 3); > + if (IS_ERR(handle)) { > + error = PTR_ERR(handle); > + goto err_out; > + } > + > + error = ext4_orphan_add(handle, inode); > + EXT4_I(inode)->i_disksize = newsize; > + rc = ext4_mark_inode_dirty(handle, inode); > + if (!error) > + error = rc; > + ext4_journal_stop(handle); > + > + if (ext4_should_order_data(inode)) { > + error = ext4_begin_ordered_truncate(inode, newsize); > + if (error) { > + /* Do as much error cleanup as possible */ > + handle = ext4_journal_start(inode, 3); > + if (IS_ERR(handle)) { > + ext4_orphan_del(NULL, inode); > + goto err_out; > + } > + ext4_orphan_del(handle, inode); > + ext4_journal_stop(handle); > + goto err_out; > + } > + } > + } else if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) { > + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); > + > + if (newsize > sbi->s_bitmap_maxbytes) { > + error = -EFBIG; > + goto out; > + } > + } > + > + i_size_write(inode, newsize); > + truncate_pagecache(inode, oldsize, newsize); > + ext4_truncate(inode); > + > + /* > + * If we failed to get a transaction handle at all, we need to clean up > + * the in-core orphan list manually. > + */ > + if (inode->i_nlink) > + ext4_orphan_del(NULL, inode); > +err_out: > + ext4_std_error(inode->i_sb, error); > +out: > + return error; > +} > + > + > +/* > + * ext4_setattr() > * > - * Another thing we have to assure is that if we are in ordered mode > - * and inode is still attached to the committing transaction, we must > - * we start writeout of all the dirty pages which are being truncated. > - * This way we are sure that all the data written in the previous > - * transaction are already on disk (truncate waits for pages under > - * writeback). > + * Handle special things ext4 needs for changing owner of the file, changing > + * ACLs, or truncating file. > * > - * Called with inode->i_mutex down. > + * Called from notify_change with inode->i_mutex down. > */ > int ext4_setattr(struct dentry *dentry, struct iattr *attr) > { > @@ -4743,61 +4816,18 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) > } > > if (attr->ia_valid & ATTR_SIZE) { > - if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) { > - struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); > - > - if (attr->ia_size > sbi->s_bitmap_maxbytes) { > - error = -EFBIG; > - goto err_out; > - } > - } > - } > - > - if (S_ISREG(inode->i_mode) && > - attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { > - handle_t *handle; > - > - handle = ext4_journal_start(inode, 3); > - if (IS_ERR(handle)) { > - error = PTR_ERR(handle); > - goto err_out; > - } > - > - error = ext4_orphan_add(handle, inode); > - EXT4_I(inode)->i_disksize = attr->ia_size; > - rc = ext4_mark_inode_dirty(handle, inode); > - if (!error) > - error = rc; > - ext4_journal_stop(handle); > - > - if (ext4_should_order_data(inode)) { > - error = ext4_begin_ordered_truncate(inode, > - attr->ia_size); > - if (error) { > - /* Do as much error cleanup as possible */ > - handle = ext4_journal_start(inode, 3); > - if (IS_ERR(handle)) { > - ext4_orphan_del(NULL, inode); > - goto err_out; > - } > - ext4_orphan_del(handle, inode); > - ext4_journal_stop(handle); > - goto err_out; > - } > - } > + error = ext4_setsize(inode, attr->ia_size); > + if (error) > + return error; > } > > - rc = inode_setattr(inode, attr); > - > - /* If inode_setattr's call to ext4_truncate failed to get a > - * transaction handle at all, we need to clean up the in-core > - * orphan list manually. */ > - if (inode->i_nlink) > - ext4_orphan_del(NULL, inode); > + generic_setattr(inode, attr); > > - if (!rc && (ia_valid & ATTR_MODE)) > + if (ia_valid & ATTR_MODE) > rc = ext4_acl_chmod(inode); > > + /* Mark inode dirty due to changes done by generic_setattr() */ > + mark_inode_dirty(inode); > err_out: > ext4_std_error(inode->i_sb, error); > if (!error) > -- > 1.6.0.2 > -- Jan Kara <jack@xxxxxxx> SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html