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

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

diff --combined fs/overlayfs/copy_up.c
@@@ -22,9 -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)
        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);
@@@ -195,7 -206,8 +206,7 @@@ int ovl_set_attr(struct dentry *upperde
  
  static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
                              struct dentry *dentry, struct path *lowerpath,
 -                            struct kstat *stat, struct iattr *attr,
 -                            const char *link)
 +                            struct kstat *stat, const char *link)
  {
        struct inode *wdir = workdir->d_inode;
        struct inode *udir = upperdir->d_inode;
  
        mutex_lock(&newdentry->d_inode->i_mutex);
        err = ovl_set_attr(newdentry, stat);
 -      if (!err && attr)
 -              err = notify_change(newdentry, attr, NULL);
        mutex_unlock(&newdentry->d_inode->i_mutex);
        if (err)
                goto out_cleanup;
@@@ -283,7 -297,8 +294,7 @@@ out_cleanup
   * that point the file will have already been copied up anyway.
   */
  int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
 -                  struct path *lowerpath, struct kstat *stat,
 -                  struct iattr *attr)
 +                  struct path *lowerpath, struct kstat *stat)
  {
        struct dentry *workdir = ovl_workdir(dentry);
        int err;
        }
        upperdentry = ovl_dentry_upper(dentry);
        if (upperdentry) {
 -              unlock_rename(workdir, upperdir);
 +              /* Raced with another copy-up?  Nothing to do, then... */
                err = 0;
 -              /* Raced with another copy-up?  Do the setattr here */
 -              if (attr) {
 -                      mutex_lock(&upperdentry->d_inode->i_mutex);
 -                      err = notify_change(upperdentry, attr, NULL);
 -                      mutex_unlock(&upperdentry->d_inode->i_mutex);
 -              }
 -              goto out_put_cred;
 +              goto out_unlock;
        }
  
        err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath,
 -                               stat, attr, link);
 +                               stat, link);
        if (!err) {
                /* Restore timestamps on parent (best effort) */
                ovl_set_timestamps(upperdir, &pstat);
        }
  out_unlock:
        unlock_rename(workdir, upperdir);
 -out_put_cred:
        revert_creds(old_cred);
        put_cred(override_cred);
  
@@@ -395,7 -417,7 +406,7 @@@ int ovl_copy_up(struct dentry *dentry
                ovl_path_lower(next, &lowerpath);
                err = vfs_getattr(&lowerpath, &stat);
                if (!err)
 -                      err = ovl_copy_up_one(parent, next, &lowerpath, &stat, NULL);
 +                      err = ovl_copy_up_one(parent, next, &lowerpath, &stat);
  
                dput(parent);
                dput(next);
diff --combined fs/overlayfs/inode.c
@@@ -12,7 -12,8 +12,7 @@@
  #include <linux/xattr.h>
  #include "overlayfs.h"
  
 -static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr,
 -                          bool no_data)
 +static int ovl_copy_up_truncate(struct dentry *dentry)
  {
        int err;
        struct dentry *parent;
        if (err)
                goto out_dput_parent;
  
 -      if (no_data)
 -              stat.size = 0;
 -
 -      err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat, attr);
 +      stat.size = 0;
 +      err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
  
  out_dput_parent:
        dput(parent);
@@@ -42,17 -45,30 +42,30 @@@ int ovl_setattr(struct dentry *dentry, 
        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;
  
 -      upperdentry = ovl_dentry_upper(dentry);
 -      if (upperdentry) {
 +      err = ovl_copy_up(dentry);
 +      if (!err) {
 +              upperdentry = ovl_dentry_upper(dentry);
 +
                mutex_lock(&upperdentry->d_inode->i_mutex);
                err = notify_change(upperdentry, attr, NULL);
                mutex_unlock(&upperdentry->d_inode->i_mutex);
 -      } else {
 -              err = ovl_copy_up_last(dentry, attr, false);
        }
        ovl_drop_write(dentry);
  out:
@@@ -95,6 -111,29 +108,29 @@@ int ovl_permission(struct inode *inode
  
        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) {
@@@ -131,23 -170,57 +167,23 @@@ out_dput
        return err;
  }
  
 -
 -struct ovl_link_data {
 -      struct dentry *realdentry;
 -      void *cookie;
 -};
 -
 -static const char *ovl_follow_link(struct dentry *dentry, void **cookie)
 +static const char *ovl_get_link(struct dentry *dentry,
 +                              struct inode *inode,
 +                              struct delayed_call *done)
  {
        struct dentry *realdentry;
        struct inode *realinode;
 -      struct ovl_link_data *data = NULL;
 -      const char *ret;
 +
 +      if (!dentry)
 +              return ERR_PTR(-ECHILD);
  
        realdentry = ovl_dentry_real(dentry);
        realinode = realdentry->d_inode;
  
 -      if (WARN_ON(!realinode->i_op->follow_link))
 +      if (WARN_ON(!realinode->i_op->get_link))
                return ERR_PTR(-EPERM);
  
 -      if (realinode->i_op->put_link) {
 -              data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL);
 -              if (!data)
 -                      return ERR_PTR(-ENOMEM);
 -              data->realdentry = realdentry;
 -      }
 -
 -      ret = realinode->i_op->follow_link(realdentry, cookie);
 -      if (IS_ERR_OR_NULL(ret)) {
 -              kfree(data);
 -              return ret;
 -      }
 -
 -      if (data)
 -              data->cookie = *cookie;
 -
 -      *cookie = data;
 -
 -      return ret;
 -}
 -
 -static void ovl_put_link(struct inode *unused, void *c)
 -{
 -      struct inode *realinode;
 -      struct ovl_link_data *data = c;
 -
 -      if (!data)
 -              return;
 -
 -      realinode = data->realdentry->d_inode;
 -      realinode->i_op->put_link(realinode, data->cookie);
 -      kfree(data);
 +      return realinode->i_op->get_link(realdentry, realinode, done);
  }
  
  static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
@@@ -316,7 -389,7 +352,7 @@@ struct inode *ovl_d_select_inode(struc
                        return ERR_PTR(err);
  
                if (file_flags & O_TRUNC)
 -                      err = ovl_copy_up_last(dentry, NULL, true);
 +                      err = ovl_copy_up_truncate(dentry);
                else
                        err = ovl_copy_up(dentry);
                ovl_drop_write(dentry);
@@@ -344,7 -417,8 +380,7 @@@ static const struct inode_operations ov
  
  static const struct inode_operations ovl_symlink_inode_operations = {
        .setattr        = ovl_setattr,
 -      .follow_link    = ovl_follow_link,
 -      .put_link       = ovl_put_link,
 +      .get_link       = ovl_get_link,
        .readlink       = ovl_readlink,
        .getattr        = ovl_getattr,
        .setxattr       = ovl_setxattr,
diff --combined fs/overlayfs/overlayfs.h
@@@ -142,7 -142,10 +142,10 @@@ struct dentry *ovl_dentry_upper(struct 
  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);
@@@ -194,6 -197,7 +197,6 @@@ void ovl_cleanup(struct inode *dir, str
  /* copy_up.c */
  int ovl_copy_up(struct dentry *dentry);
  int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
 -                  struct path *lowerpath, struct kstat *stat,
 -                  struct iattr *attr);
 +                  struct path *lowerpath, struct kstat *stat);
  int ovl_copy_xattr(struct dentry *old, struct dentry *new);
  int ovl_set_attr(struct dentry *upper, struct kstat *stat);
diff --combined fs/overlayfs/super.c
@@@ -9,6 -9,7 +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 +25,11 @@@ MODULE_AUTHOR("Miklos Szeredi <miklos@s
  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 +154,18 @@@ struct dentry *ovl_entry_real(struct ov
        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;
        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;
@@@ -589,11 -608,13 +608,13 @@@ static int ovl_show_options(struct seq_
        struct super_block *sb = dentry->d_sb;
        struct ovl_fs *ufs = sb->s_fs_info;
  
 -      seq_printf(m, ",lowerdir=%s", ufs->config.lowerdir);
 +      seq_show_option(m, "lowerdir", ufs->config.lowerdir);
        if (ufs->config.upperdir) {
 -              seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
 -              seq_printf(m, ",workdir=%s", ufs->config.workdir);
 +              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 +639,7 @@@ enum 
        OPT_LOWERDIR,
        OPT_UPPERDIR,
        OPT_WORKDIR,
+       OPT_DEFAULT_PERMISSIONS,
        OPT_ERR,
  };
  
@@@ -625,6 -647,7 +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 +708,10 @@@ static int ovl_parse_opt(char *opt, str
                                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 +937,7 @@@ static int ovl_fill_super(struct super_
        }
  
        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");
  
        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;
@@@ -31,6 -31,7 +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 */
@@@ -54,7 -55,6 +55,7 @@@
  
  #define SMB_SUPER_MAGIC               0x517B
  #define CGROUP_SUPER_MAGIC    0x27e0eb
 +#define CGROUP2_SUPER_MAGIC   0x63677270
  
  
  #define STACK_END_MAGIC               0x57AC6E9D
@@@ -76,6 -76,5 +77,6 @@@
  #define ANON_INODE_FS_MAGIC   0x09041934
  #define BTRFS_TEST_MAGIC      0x73727279
  #define NSFS_MAGIC            0x6e736673
 +#define BPF_FS_MAGIC          0xcafe4a11
  
  #endif /* __LINUX_MAGIC_H__ */