Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Jan 2016 20:20:46 +0000 (12:20 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Jan 2016 20:20:46 +0000 (12:20 -0800)
Pull overlayfs updates from Miklos Szeredi:
 "This contains several bug fixes and a new mount option
  'default_permissions' that allows read-only exported NFS
  filesystems to be used as lower layer"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: check dentry positiveness in ovl_cleanup_whiteouts()
  ovl: setattr: check permissions before copy-up
  ovl: root: copy attr
  ovl: move super block magic number to magic.h
  ovl: use a minimal buffer in ovl_copy_xattr
  ovl: allow zero size xattr
  ovl: default permissions

fs/overlayfs/copy_up.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/readdir.c
fs/overlayfs/super.c
include/uapi/linux/magic.h

index 0a89834..eff6319 100644 (file)
@@ -22,9 +22,9 @@
 
 int ovl_copy_xattr(struct dentry *old, struct dentry *new)
 {
-       ssize_t list_size, size;
-       char *buf, *name, *value;
-       int error;
+       ssize_t list_size, size, value_size = 0;
+       char *buf, *name, *value = NULL;
+       int uninitialized_var(error);
 
        if (!old->d_inode->i_op->getxattr ||
            !new->d_inode->i_op->getxattr)
@@ -41,29 +41,40 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
        if (!buf)
                return -ENOMEM;
 
-       error = -ENOMEM;
-       value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL);
-       if (!value)
-               goto out;
-
        list_size = vfs_listxattr(old, buf, list_size);
        if (list_size <= 0) {
                error = list_size;
-               goto out_free_value;
+               goto out;
        }
 
        for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
-               size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX);
-               if (size <= 0) {
+retry:
+               size = vfs_getxattr(old, name, value, value_size);
+               if (size == -ERANGE)
+                       size = vfs_getxattr(old, name, NULL, 0);
+
+               if (size < 0) {
                        error = size;
-                       goto out_free_value;
+                       break;
+               }
+
+               if (size > value_size) {
+                       void *new;
+
+                       new = krealloc(value, size, GFP_KERNEL);
+                       if (!new) {
+                               error = -ENOMEM;
+                               break;
+                       }
+                       value = new;
+                       value_size = size;
+                       goto retry;
                }
+
                error = vfs_setxattr(new, name, value, size, 0);
                if (error)
-                       goto out_free_value;
+                       break;
        }
-
-out_free_value:
        kfree(value);
 out:
        kfree(buf);
index 964a60f..bf996e5 100644 (file)
@@ -42,6 +42,19 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
        int err;
        struct dentry *upperdentry;
 
+       /*
+        * Check for permissions before trying to copy-up.  This is redundant
+        * since it will be rechecked later by ->setattr() on upper dentry.  But
+        * without this, copy-up can be triggered by just about anybody.
+        *
+        * We don't initialize inode->size, which just means that
+        * inode_newsize_ok() will always check against MAX_LFS_FILESIZE and not
+        * check for a swapfile (which this won't be anyway).
+        */
+       err = inode_change_ok(dentry->d_inode, attr);
+       if (err)
+               return err;
+
        err = ovl_want_write(dentry);
        if (err)
                goto out;
@@ -95,6 +108,29 @@ int ovl_permission(struct inode *inode, int mask)
 
        realdentry = ovl_entry_real(oe, &is_upper);
 
+       if (ovl_is_default_permissions(inode)) {
+               struct kstat stat;
+               struct path realpath = { .dentry = realdentry };
+
+               if (mask & MAY_NOT_BLOCK)
+                       return -ECHILD;
+
+               realpath.mnt = ovl_entry_mnt_real(oe, inode, is_upper);
+
+               err = vfs_getattr(&realpath, &stat);
+               if (err)
+                       return err;
+
+               if ((stat.mode ^ inode->i_mode) & S_IFMT)
+                       return -ESTALE;
+
+               inode->i_mode = stat.mode;
+               inode->i_uid = stat.uid;
+               inode->i_gid = stat.gid;
+
+               return generic_permission(inode, mask);
+       }
+
        /* Careful in RCU walk mode */
        realinode = ACCESS_ONCE(realdentry->d_inode);
        if (!realinode) {
index e17154a..99b4168 100644 (file)
@@ -142,7 +142,10 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry);
 struct dentry *ovl_dentry_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_real(struct dentry *dentry);
 struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper);
+struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
+                                   bool is_upper);
 struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
+bool ovl_is_default_permissions(struct inode *inode);
 void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
 struct dentry *ovl_workdir(struct dentry *dentry);
 int ovl_want_write(struct dentry *dentry);
index 70e9af5..adcb139 100644 (file)
@@ -571,7 +571,8 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
                               (int) PTR_ERR(dentry));
                        continue;
                }
-               ovl_cleanup(upper->d_inode, dentry);
+               if (dentry->d_inode)
+                       ovl_cleanup(upper->d_inode, dentry);
                dput(dentry);
        }
        mutex_unlock(&upper->d_inode->i_mutex);
index e38ee0f..a4cbdf9 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/fs.h>
 #include <linux/namei.h>
+#include <linux/pagemap.h>
 #include <linux/xattr.h>
 #include <linux/security.h>
 #include <linux/mount.h>
@@ -24,12 +25,11 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
 MODULE_DESCRIPTION("Overlay filesystem");
 MODULE_LICENSE("GPL");
 
-#define OVERLAYFS_SUPER_MAGIC 0x794c7630
-
 struct ovl_config {
        char *lowerdir;
        char *upperdir;
        char *workdir;
+       bool default_permissions;
 };
 
 /* private information held for overlayfs's superblock */
@@ -154,6 +154,18 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
        return realdentry;
 }
 
+struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
+                                   bool is_upper)
+{
+       if (is_upper) {
+               struct ovl_fs *ofs = inode->i_sb->s_fs_info;
+
+               return ofs->upper_mnt;
+       } else {
+               return oe->numlower ? oe->lowerstack[0].mnt : NULL;
+       }
+}
+
 struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
 {
        struct ovl_entry *oe = dentry->d_fsdata;
@@ -161,6 +173,13 @@ struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
        return oe->cache;
 }
 
+bool ovl_is_default_permissions(struct inode *inode)
+{
+       struct ovl_fs *ofs = inode->i_sb->s_fs_info;
+
+       return ofs->config.default_permissions;
+}
+
 void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
 {
        struct ovl_entry *oe = dentry->d_fsdata;
@@ -594,6 +613,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
                seq_show_option(m, "upperdir", ufs->config.upperdir);
                seq_show_option(m, "workdir", ufs->config.workdir);
        }
+       if (ufs->config.default_permissions)
+               seq_puts(m, ",default_permissions");
        return 0;
 }
 
@@ -618,6 +639,7 @@ enum {
        OPT_LOWERDIR,
        OPT_UPPERDIR,
        OPT_WORKDIR,
+       OPT_DEFAULT_PERMISSIONS,
        OPT_ERR,
 };
 
@@ -625,6 +647,7 @@ static const match_table_t ovl_tokens = {
        {OPT_LOWERDIR,                  "lowerdir=%s"},
        {OPT_UPPERDIR,                  "upperdir=%s"},
        {OPT_WORKDIR,                   "workdir=%s"},
+       {OPT_DEFAULT_PERMISSIONS,       "default_permissions"},
        {OPT_ERR,                       NULL}
 };
 
@@ -685,6 +708,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
                                return -ENOMEM;
                        break;
 
+               case OPT_DEFAULT_PERMISSIONS:
+                       config->default_permissions = true;
+                       break;
+
                default:
                        pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
                        return -EINVAL;
@@ -910,6 +937,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        }
 
        sb->s_stack_depth = 0;
+       sb->s_maxbytes = MAX_LFS_FILESIZE;
        if (ufs->config.upperdir) {
                if (!ufs->config.workdir) {
                        pr_err("overlayfs: missing 'workdir'\n");
@@ -1053,6 +1081,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
        root_dentry->d_fsdata = oe;
 
+       ovl_copyattr(ovl_dentry_real(root_dentry)->d_inode,
+                    root_dentry->d_inode);
+
        sb->s_magic = OVERLAYFS_SUPER_MAGIC;
        sb->s_op = &ovl_super_operations;
        sb->s_root = root_dentry;
index b283d56..0de181a 100644 (file)
@@ -31,6 +31,7 @@
 #define PSTOREFS_MAGIC         0x6165676C
 #define EFIVARFS_MAGIC         0xde5e81e4
 #define HOSTFS_SUPER_MAGIC     0x00c0ffee
+#define OVERLAYFS_SUPER_MAGIC  0x794c7630
 
 #define MINIX_SUPER_MAGIC      0x137F          /* minix v1 fs, 14 char names */
 #define MINIX_SUPER_MAGIC2     0x138F          /* minix v1 fs, 30 char names */