From: Nick Piggin <npiggin@suse.de>
To: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@ZenIV.linux.org.uk>,
linux-fsdevel@vger.kernel.org, Miklos Szeredi <miklos@szeredi.hu>
Subject: [patch v2] fix truncate inode time modification breakage
Date: Thu, 3 Jun 2010 05:55:38 +1000 [thread overview]
Message-ID: <20100602195538.GG6152@laptop> (raw)
In-Reply-To: <20100601135655.GU9453@laptop>
On Tue, Jun 01, 2010 at 11:56:55PM +1000, Nick Piggin wrote:
> On Tue, Jun 01, 2010 at 03:48:01PM +0200, Christoph Hellwig wrote:
> > On Tue, Jun 01, 2010 at 11:39:23PM +1000, Nick Piggin wrote:
> > > It appears that I've broken inode time modifications on tmpfs/ext2.
> > > While ftruncate always updates these attributes, truncate must not
> > > unless size is changed. I hadn't actually understood that until
> > > Christoph told me.
> > >
> > > Confusion is increased because other filesystems get this wrong.
> > > Those without ->setattr or ->truncate get it wrong by default.
> > > Others appear to have problems too.
> > >
> > > I haven't gone through many yet, but is there any reason not to
> > > just do it in the vfs?
> >
> > Doing it in the VFS is fine with me, we still have the the file
> > pointer in struct iatta to indicate a ftruncate / open O_TRUNC
> > if any filesystem really cares. But I think you need to audit
> > all instances if they care about this. And while you're at it
> > also remove the code handling this and the comments about it in
> > XFS.
>
> Yes I was starting to look at that. Just want to see if I'm on the
> right track.
>
>
> > > - if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
> > > - loff_t newsize = attr->ia_size;
> > > + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE) &&
> > > + newsize != inode->i_size) {
> >
> > Btw, the S_ISREG is superflous - we only ever set ATTR_SIZE for
> > regular files from the upper layer code.
>
> OK I'll get rid of it in the same patch.
Well I looked through filesystems and I cannot seem to find anywhere
they can use ATTR_SIZE|ATTR_[MC]TIME to do anything strange that would
let them distinguish truncate from ftruncate and O_TRUNC.
I couldn't quite understand what FUSE was trying to do, or whether I
improved it.
---
Index: linux-2.6/fs/ext2/inode.c
===================================================================
--- linux-2.6.orig/fs/ext2/inode.c
+++ linux-2.6/fs/ext2/inode.c
@@ -1203,7 +1203,6 @@ int ext2_setsize(struct inode *inode, lo
__ext2_truncate_blocks(inode, newsize);
- inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
if (inode_needs_sync(inode)) {
sync_mapping_buffers(inode->i_mapping);
ext2_sync_inode (inode);
Index: linux-2.6/fs/open.c
===================================================================
--- linux-2.6.orig/fs/open.c
+++ linux-2.6/fs/open.c
@@ -55,6 +55,9 @@ int do_truncate(struct dentry *dentry, l
newattrs.ia_valid |= ret | ATTR_FORCE;
mutex_lock(&dentry->d_inode->i_mutex);
+ /* Unlike ftruncate, truncate only updates times when size changes */
+ if (length != dentry->d_inode->i_size)
+ newattrs.ia_valid |= ATTR_MTIME|ATTR_CTIME;
ret = notify_change(dentry, &newattrs);
mutex_unlock(&dentry->d_inode->i_mutex);
return ret;
Index: linux-2.6/mm/shmem.c
===================================================================
--- linux-2.6.orig/mm/shmem.c
+++ linux-2.6/mm/shmem.c
@@ -764,10 +764,10 @@ done2:
static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
+ loff_t newsize = attr->ia_size;
int error;
- if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
- loff_t newsize = attr->ia_size;
+ if ((attr->ia_valid & ATTR_SIZE) && newsize != inode->i_size) {
struct page *page = NULL;
if (newsize < inode->i_size) {
Index: linux-2.6/fs/ramfs/file-nommu.c
===================================================================
--- linux-2.6.orig/fs/ramfs/file-nommu.c
+++ linux-2.6/fs/ramfs/file-nommu.c
@@ -175,11 +175,6 @@ static int ramfs_nommu_setattr(struct de
ret = ramfs_nommu_resize(inode, ia->ia_size, size);
if (ret < 0 || ia->ia_valid == ATTR_SIZE)
goto out;
- } else {
- /* we skipped the truncate but must still update
- * timestamps
- */
- ia->ia_valid |= ATTR_MTIME|ATTR_CTIME;
}
}
Index: linux-2.6/fs/xfs/xfs_vnodeops.c
===================================================================
--- linux-2.6.orig/fs/xfs/xfs_vnodeops.c
+++ linux-2.6/fs/xfs/xfs_vnodeops.c
@@ -286,25 +286,6 @@ xfs_setattr(
xfs_trans_ijoin(tp, ip, lock_flags);
xfs_trans_ihold(tp, ip);
- /*
- * Only change the c/mtime if we are changing the size
- * or we are explicitly asked to change it. This handles
- * the semantic difference between truncate() and ftruncate()
- * as implemented in the VFS.
- *
- * The regular truncate() case without ATTR_CTIME and ATTR_MTIME
- * is a special case where we need to update the times despite
- * not having these flags set. For all other operations the
- * VFS set these flags explicitly if it wants a timestamp
- * update.
- */
- if (iattr->ia_size != ip->i_size &&
- (!(mask & (ATTR_CTIME | ATTR_MTIME)))) {
- iattr->ia_ctime = iattr->ia_mtime =
- current_fs_time(inode->i_sb);
- mask |= ATTR_CTIME | ATTR_MTIME;
- }
-
if (iattr->ia_size > ip->i_size) {
ip->i_d.di_size = iattr->ia_size;
ip->i_size = iattr->ia_size;
Index: linux-2.6/fs/fuse/dir.c
===================================================================
--- linux-2.6.orig/fs/fuse/dir.c
+++ linux-2.6/fs/fuse/dir.c
@@ -1161,20 +1161,6 @@ static int fuse_dir_fsync(struct file *f
return fuse_fsync_common(file, datasync, 1);
}
-static bool update_mtime(unsigned ivalid)
-{
- /* Always update if mtime is explicitly set */
- if (ivalid & ATTR_MTIME_SET)
- return true;
-
- /* If it's an open(O_TRUNC) or an ftruncate(), don't update */
- if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
- return false;
-
- /* In all other cases update */
- return true;
-}
-
static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
{
unsigned ivalid = iattr->ia_valid;
@@ -1194,7 +1180,7 @@ static void iattr_to_fattr(struct iattr
if (!(ivalid & ATTR_ATIME_SET))
arg->valid |= FATTR_ATIME_NOW;
}
- if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+ if (ivalid & ATTR_MTIME) {
arg->valid |= FATTR_MTIME;
arg->mtime = iattr->ia_mtime.tv_sec;
arg->mtimensec = iattr->ia_mtime.tv_nsec;
Index: linux-2.6/fs/ntfs/inode.c
===================================================================
--- linux-2.6.orig/fs/ntfs/inode.c
+++ linux-2.6/fs/ntfs/inode.c
@@ -2917,12 +2917,6 @@ int ntfs_setattr(struct dentry *dentry,
err = vmtruncate(vi, attr->ia_size);
if (err || ia_valid == ATTR_SIZE)
goto out;
- } else {
- /*
- * We skipped the truncate but must still update
- * timestamps.
- */
- ia_valid |= ATTR_MTIME | ATTR_CTIME;
}
}
if (ia_valid & ATTR_ATIME)
Index: linux-2.6/fs/reiserfs/inode.c
===================================================================
--- linux-2.6.orig/fs/reiserfs/inode.c
+++ linux-2.6/fs/reiserfs/inode.c
@@ -3105,11 +3105,6 @@ int reiserfs_setattr(struct dentry *dent
}
if (error)
goto out;
- /*
- * file size is changed, ctime and mtime are
- * to be updated
- */
- attr->ia_valid |= (ATTR_MTIME | ATTR_CTIME);
}
}
Index: linux-2.6/fs/ubifs/file.c
===================================================================
--- linux-2.6.orig/fs/ubifs/file.c
+++ linux-2.6/fs/ubifs/file.c
@@ -1175,8 +1175,6 @@ static int do_truncation(struct ubifs_in
mutex_lock(&ui->ui_mutex);
ui->ui_size = inode->i_size;
- /* Truncation changes inode [mc]time */
- inode->i_mtime = inode->i_ctime = ubifs_current_time(inode);
/* Other attributes may be changed at the same time as well */
do_attr_changes(inode, attr);
err = ubifs_jnl_truncate(c, inode, old_size, new_size);
@@ -1224,8 +1222,6 @@ static int do_setattr(struct ubifs_info
mutex_lock(&ui->ui_mutex);
if (attr->ia_valid & ATTR_SIZE) {
- /* Truncation changes inode [mc]time */
- inode->i_mtime = inode->i_ctime = ubifs_current_time(inode);
/* 'simple_setsize()' changed @i_size, update @ui_size */
ui->ui_size = inode->i_size;
}
next prev parent reply other threads:[~2010-06-02 19:55 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-06-01 13:39 [patch] fix truncate inode time modification breakage Nick Piggin
2010-06-01 13:48 ` Christoph Hellwig
2010-06-01 13:56 ` Nick Piggin
2010-06-02 19:55 ` Nick Piggin [this message]
2010-06-02 20:08 ` Filesystem setattr/truncate notes and problems Nick Piggin
2010-06-03 7:28 ` Christoph Hellwig
2010-06-03 7:32 ` Christoph Hellwig
2010-06-03 9:50 ` Nick Piggin
2010-06-03 9:18 ` Nick Piggin
2010-06-03 9:26 ` Nick Piggin
2010-06-03 8:18 ` [patch v2] fix truncate inode time modification breakage Miklos Szeredi
2010-06-03 8:40 ` Boaz Harrosh
2010-06-03 9:05 ` Miklos Szeredi
2010-06-03 12:13 ` Boaz Harrosh
2010-06-03 9:14 ` Nick Piggin
2010-06-03 9:28 ` Miklos Szeredi
2010-06-03 10:07 ` Nick Piggin
2010-06-03 10:58 ` Miklos Szeredi
2010-06-03 11:09 ` Christoph Hellwig
2010-06-03 12:01 ` [patch v3] " Nick Piggin
2010-06-03 11:49 ` [patch v2] " Nick Piggin
2010-06-03 12:03 ` Miklos Szeredi
2010-06-01 14:10 ` [patch] " Boaz Harrosh
2010-06-01 14:32 ` Nick Piggin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100602195538.GG6152@laptop \
--to=npiggin@suse.de \
--cc=hch@lst.de \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).