ovl: update atime on upper
authorMiklos Szeredi <mszeredi@redhat.com>
Fri, 29 Jul 2016 10:05:23 +0000 (12:05 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 29 Jul 2016 10:05:23 +0000 (12:05 +0200)
Fix atime update logic in overlayfs.

This patch adds an i_op->update_time() handler to overlayfs inodes.  This
forwards atime updates to the upper layer only.  No atime updates are done
on lower layers.

Remove implicit atime updates to underlying files and directories with
O_NOATIME.  Remove explicit atime update in ovl_readlink().

Clear atime related mnt flags from cloned upper mount.  This means atime
updates are controlled purely by overlayfs mount options.

Reported-by: Konstantin Khlebnikov <koct9i@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c

index 8beeed34dad660a97b9ee630252675a06f617b03..b4eac8173f936954047f92ff47d93d36ffe2e2e5 100644 (file)
@@ -943,4 +943,5 @@ const struct inode_operations ovl_dir_inode_operations = {
        .listxattr      = ovl_listxattr,
        .removexattr    = ovl_removexattr,
        .get_acl        = ovl_get_acl,
+       .update_time    = ovl_update_time,
 };
index 66f42f5cf70508c2eca6687e49031f6ddc377f0a..041db9c6621ca5f462d36f1278db1802e78337a7 100644 (file)
@@ -185,8 +185,6 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
        if (!realinode->i_op->readlink)
                return -EINVAL;
 
-       touch_atime(&realpath);
-
        old_cred = ovl_override_creds(dentry->d_sb);
        err = realinode->i_op->readlink(realpath.dentry, buf, bufsiz);
        revert_creds(old_cred);
@@ -367,6 +365,29 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
        return err;
 }
 
+int ovl_update_time(struct inode *inode, struct timespec *ts, int flags)
+{
+       struct dentry *alias;
+       struct path upperpath;
+
+       if (!(flags & S_ATIME))
+               return 0;
+
+       alias = d_find_any_alias(inode);
+       if (!alias)
+               return 0;
+
+       ovl_path_upper(alias, &upperpath);
+       if (upperpath.dentry) {
+               touch_atime(&upperpath);
+               inode->i_atime = d_inode(upperpath.dentry)->i_atime;
+       }
+
+       dput(alias);
+
+       return 0;
+}
+
 static const struct inode_operations ovl_file_inode_operations = {
        .setattr        = ovl_setattr,
        .permission     = ovl_permission,
@@ -376,6 +397,7 @@ static const struct inode_operations ovl_file_inode_operations = {
        .listxattr      = ovl_listxattr,
        .removexattr    = ovl_removexattr,
        .get_acl        = ovl_get_acl,
+       .update_time    = ovl_update_time,
 };
 
 static const struct inode_operations ovl_symlink_inode_operations = {
@@ -387,6 +409,7 @@ static const struct inode_operations ovl_symlink_inode_operations = {
        .getxattr       = ovl_getxattr,
        .listxattr      = ovl_listxattr,
        .removexattr    = ovl_removexattr,
+       .update_time    = ovl_update_time,
 };
 
 struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
@@ -400,7 +423,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
 
        inode->i_ino = get_next_ino();
        inode->i_mode = mode;
-       inode->i_flags |= S_NOATIME | S_NOCMTIME;
+       inode->i_flags |= S_NOCMTIME;
        inode->i_private = oe;
 
        mode &= S_IFMT;
index e8d50da384d5baa09c784be7aafce5e851ca9408..fb73c09a84e74140d0b8a223e4a0fd002c5d8b91 100644 (file)
@@ -181,6 +181,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
 int ovl_removexattr(struct dentry *dentry, const char *name);
 struct posix_acl *ovl_get_acl(struct inode *inode, int type);
 int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
+int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
 
 struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
                            struct ovl_entry *oe);
@@ -189,6 +190,9 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
        to->i_uid = from->i_uid;
        to->i_gid = from->i_gid;
        to->i_mode = from->i_mode;
+       to->i_atime = from->i_atime;
+       to->i_mtime = from->i_mtime;
+       to->i_ctime = from->i_ctime;
 }
 
 /* dir.c */
index 80598912a5d98009139ede12f4a5bd49b3d35647..058103c60f54392e315a722011c0798683f65a63 100644 (file)
@@ -608,7 +608,7 @@ out:
 
 struct file *ovl_path_open(struct path *path, int flags)
 {
-       return dentry_open(path, flags, current_cred());
+       return dentry_open(path, flags | O_NOATIME, current_cred());
 }
 
 static void ovl_put_super(struct super_block *sb)
@@ -1075,6 +1075,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        pr_err("overlayfs: failed to clone upperpath\n");
                        goto out_put_lowerpath;
                }
+               /* Don't inherit atime flags */
+               ufs->upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
+
+               sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran;
 
                ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
                err = PTR_ERR(ufs->workdir);
@@ -1122,7 +1126,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                 * Make lower_mnt R/O.  That way fchmod/fchown on lower file
                 * will fail instead of modifying lower fs.
                 */
-               mnt->mnt_flags |= MNT_READONLY;
+               mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
 
                ufs->lower_mnt[ufs->numlower] = mnt;
                ufs->numlower++;