ovl: check IS_APPEND() on real upper inode
authorAmir Goldstein <amir73il@gmail.com>
Sat, 8 Apr 2017 11:49:07 +0000 (14:49 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 20 Apr 2017 14:37:26 +0000 (16:37 +0200)
For overlay file open, check IS_APPEND() on the real upper inode
inside d_real(), because the overlay inode does not have the
S_APPEND flag and IS_APPEND() can only be checked at open time.

Note that because overlayfs does not copy up the chattr inode flags
(i.e. S_APPEND, S_IMMUTABLE), the IS_APPEND() check is only relevant
for upper inodes that were set with chattr +a and not to lower
inodes that had chattr +a before copy up.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/super.c

index 07c8793..6faefa5 100644 (file)
@@ -49,11 +49,28 @@ static void ovl_dentry_release(struct dentry *dentry)
        }
 }
 
+static int ovl_check_append_only(struct inode *inode, int flag)
+{
+       /*
+        * This test was moot in vfs may_open() because overlay inode does
+        * not have the S_APPEND flag, so re-check on real upper inode
+        */
+       if (IS_APPEND(inode)) {
+               if  ((flag & O_ACCMODE) != O_RDONLY && !(flag & O_APPEND))
+                       return -EPERM;
+               if (flag & O_TRUNC)
+                       return -EPERM;
+       }
+
+       return 0;
+}
+
 static struct dentry *ovl_d_real(struct dentry *dentry,
                                 const struct inode *inode,
                                 unsigned int open_flags)
 {
        struct dentry *real;
+       int err;
 
        if (!d_is_reg(dentry)) {
                if (!inode || inode == d_inode(dentry))
@@ -65,15 +82,20 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
                return dentry;
 
        if (open_flags) {
-               int err = ovl_open_maybe_copy_up(dentry, open_flags);
-
+               err = ovl_open_maybe_copy_up(dentry, open_flags);
                if (err)
                        return ERR_PTR(err);
        }
 
        real = ovl_dentry_upper(dentry);
-       if (real && (!inode || inode == d_inode(real)))
+       if (real && (!inode || inode == d_inode(real))) {
+               if (!inode) {
+                       err = ovl_check_append_only(d_inode(real), open_flags);
+                       if (err)
+                               return ERR_PTR(err);
+               }
                return real;
+       }
 
        real = ovl_dentry_lower(dentry);
        if (!real)