]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 May 2016 23:44:39 +0000 (16:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 May 2016 23:44:39 +0000 (16:44 -0700)
Pull overlayfs update from Miklos Szeredi:
 "The meat of this is a change to use the mounter's credentials for
  operations that require elevated privileges (such as whiteout
  creation).  This fixes behavior under user namespaces as well as being
  a nice cleanup"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: Do d_type check only if work dir creation was successful
  ovl: update documentation
  ovl: override creds with the ones from the superblock mounter

Documentation/filesystems/overlayfs.txt
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/overlayfs.h
fs/overlayfs/readdir.c
fs/overlayfs/super.c

index 28091457b71ae89357530b351322f6a59c35bc9e..d6259c7863165939074088908323500e2fcd0fe8 100644 (file)
@@ -194,15 +194,6 @@ If a file with multiple hard links is copied up, then this will
 "break" the link.  Changes will not be propagated to other names
 referring to the same inode.
 
-Symlinks in /proc/PID/ and /proc/PID/fd which point to a non-directory
-object in overlayfs will not contain valid absolute paths, only
-relative paths leading up to the filesystem's root.  This will be
-fixed in the future.
-
-Some operations are not atomic, for example a crash during copy_up or
-rename will leave the filesystem in an inconsistent state.  This will
-be addressed in the future.
-
 Changes to underlying filesystems
 ---------------------------------
 
index cc514da6f3e7bc973a5a8c6b2a739c57e8b51ebb..80aa6f1eb336996e15c964d72a3d5d05cdc19b46 100644 (file)
@@ -336,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
        struct dentry *upperdir;
        struct dentry *upperdentry;
        const struct cred *old_cred;
-       struct cred *override_cred;
        char *link = NULL;
 
        if (WARN_ON(!workdir))
@@ -357,28 +356,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
                        return PTR_ERR(link);
        }
 
-       err = -ENOMEM;
-       override_cred = prepare_creds();
-       if (!override_cred)
-               goto out_free_link;
-
-       override_cred->fsuid = stat->uid;
-       override_cred->fsgid = stat->gid;
-       /*
-        * CAP_SYS_ADMIN for copying up extended attributes
-        * CAP_DAC_OVERRIDE for create
-        * CAP_FOWNER for chmod, timestamp update
-        * CAP_FSETID for chmod
-        * CAP_CHOWN for chown
-        * CAP_MKNOD for mknod
-        */
-       cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
-       cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
-       cap_raise(override_cred->cap_effective, CAP_FOWNER);
-       cap_raise(override_cred->cap_effective, CAP_FSETID);
-       cap_raise(override_cred->cap_effective, CAP_CHOWN);
-       cap_raise(override_cred->cap_effective, CAP_MKNOD);
-       old_cred = override_creds(override_cred);
+       old_cred = ovl_override_creds(dentry->d_sb);
 
        err = -EIO;
        if (lock_rename(workdir, upperdir) != NULL) {
@@ -401,9 +379,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
 out_unlock:
        unlock_rename(workdir, upperdir);
        revert_creds(old_cred);
-       put_cred(override_cred);
 
-out_free_link:
        if (link)
                free_page((unsigned long) link);
 
index b3fc0a35bf6242b4bc3cfc9144bcc2e3ae638713..22f0253a3567745ed02520573c8fc99c56189208 100644 (file)
@@ -405,28 +405,13 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
                err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
        } else {
                const struct cred *old_cred;
-               struct cred *override_cred;
 
-               err = -ENOMEM;
-               override_cred = prepare_creds();
-               if (!override_cred)
-                       goto out_iput;
-
-               /*
-                * CAP_SYS_ADMIN for setting opaque xattr
-                * CAP_DAC_OVERRIDE for create in workdir, rename
-                * CAP_FOWNER for removing whiteout from sticky dir
-                */
-               cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
-               cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
-               cap_raise(override_cred->cap_effective, CAP_FOWNER);
-               old_cred = override_creds(override_cred);
+               old_cred = ovl_override_creds(dentry->d_sb);
 
                err = ovl_create_over_whiteout(dentry, inode, &stat, link,
                                               hardlink);
 
                revert_creds(old_cred);
-               put_cred(override_cred);
        }
 
        if (!err)
@@ -662,32 +647,11 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
        if (OVL_TYPE_PURE_UPPER(type)) {
                err = ovl_remove_upper(dentry, is_dir);
        } else {
-               const struct cred *old_cred;
-               struct cred *override_cred;
-
-               err = -ENOMEM;
-               override_cred = prepare_creds();
-               if (!override_cred)
-                       goto out_drop_write;
-
-               /*
-                * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
-                * CAP_DAC_OVERRIDE for create in workdir, rename
-                * CAP_FOWNER for removing whiteout from sticky dir
-                * CAP_FSETID for chmod of opaque dir
-                * CAP_CHOWN for chown of opaque dir
-                */
-               cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
-               cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
-               cap_raise(override_cred->cap_effective, CAP_FOWNER);
-               cap_raise(override_cred->cap_effective, CAP_FSETID);
-               cap_raise(override_cred->cap_effective, CAP_CHOWN);
-               old_cred = override_creds(override_cred);
+               const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
 
                err = ovl_remove_and_whiteout(dentry, is_dir);
 
                revert_creds(old_cred);
-               put_cred(override_cred);
        }
 out_drop_write:
        ovl_drop_write(dentry);
@@ -725,7 +689,6 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
        bool new_is_dir = false;
        struct dentry *opaquedir = NULL;
        const struct cred *old_cred = NULL;
-       struct cred *override_cred = NULL;
 
        err = -EINVAL;
        if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
@@ -794,26 +757,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
        old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
        new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
 
-       if (old_opaque || new_opaque) {
-               err = -ENOMEM;
-               override_cred = prepare_creds();
-               if (!override_cred)
-                       goto out_drop_write;
-
-               /*
-                * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
-                * CAP_DAC_OVERRIDE for create in workdir
-                * CAP_FOWNER for removing whiteout from sticky dir
-                * CAP_FSETID for chmod of opaque dir
-                * CAP_CHOWN for chown of opaque dir
-                */
-               cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
-               cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
-               cap_raise(override_cred->cap_effective, CAP_FOWNER);
-               cap_raise(override_cred->cap_effective, CAP_FSETID);
-               cap_raise(override_cred->cap_effective, CAP_CHOWN);
-               old_cred = override_creds(override_cred);
-       }
+       if (old_opaque || new_opaque)
+               old_cred = ovl_override_creds(old->d_sb);
 
        if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
                opaquedir = ovl_check_empty_and_clear(new);
@@ -943,10 +888,8 @@ out_dput_old:
 out_unlock:
        unlock_rename(new_upperdir, old_upperdir);
 out_revert_creds:
-       if (old_opaque || new_opaque) {
+       if (old_opaque || new_opaque)
                revert_creds(old_cred);
-               put_cred(override_cred);
-       }
 out_drop_write:
        ovl_drop_write(old);
 out:
index 99ec4b0352371f47202a793c350b1e09d846c798..724f5fcb4e249314ab2359b221c3c4fd56cd3d74 100644 (file)
@@ -153,6 +153,7 @@ void ovl_drop_write(struct dentry *dentry);
 bool ovl_dentry_is_opaque(struct dentry *dentry);
 void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
 bool ovl_is_whiteout(struct dentry *dentry);
+const struct cred *ovl_override_creds(struct super_block *sb);
 void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
 struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags);
index da186ee4f846820703fa1a2fffb290c876991f1e..d11ae826bcbc94215abc441a4c1d53434691e5c4 100644 (file)
@@ -36,6 +36,7 @@ struct ovl_dir_cache {
 
 struct ovl_readdir_data {
        struct dir_context ctx;
+       struct dentry *dentry;
        bool is_lowest;
        struct rb_root root;
        struct list_head *list;
@@ -206,17 +207,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
        struct ovl_cache_entry *p;
        struct dentry *dentry;
        const struct cred *old_cred;
-       struct cred *override_cred;
-
-       override_cred = prepare_creds();
-       if (!override_cred)
-               return -ENOMEM;
 
-       /*
-        * CAP_DAC_OVERRIDE for lookup
-        */
-       cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
-       old_cred = override_creds(override_cred);
+       old_cred = ovl_override_creds(rdd->dentry->d_sb);
 
        inode_lock(dir->d_inode);
        err = 0;
@@ -234,7 +226,6 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
                inode_unlock(dir->d_inode);
        }
        revert_creds(old_cred);
-       put_cred(override_cred);
 
        return err;
 }
@@ -290,6 +281,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
        struct path realpath;
        struct ovl_readdir_data rdd = {
                .ctx.actor = ovl_fill_merge,
+               .dentry = dentry,
                .list = list,
                .root = RB_ROOT,
                .is_lowest = false,
index ed53ae0fe86881ea3578320e880cc561b113b459..ce02f46029da7c3aa7c69be9291d62c81baeb642 100644 (file)
@@ -42,6 +42,8 @@ struct ovl_fs {
        long lower_namelen;
        /* pathnames of lower and upper dirs, for show_options */
        struct ovl_config config;
+       /* creds of process who forced instantiation of super block */
+       const struct cred *creator_cred;
 };
 
 struct ovl_dir_cache;
@@ -265,6 +267,13 @@ bool ovl_is_whiteout(struct dentry *dentry)
        return inode && IS_WHITEOUT(inode);
 }
 
+const struct cred *ovl_override_creds(struct super_block *sb)
+{
+       struct ovl_fs *ofs = sb->s_fs_info;
+
+       return override_creds(ofs->creator_cred);
+}
+
 static bool ovl_is_opaquedir(struct dentry *dentry)
 {
        int res;
@@ -603,6 +612,7 @@ static void ovl_put_super(struct super_block *sb)
        kfree(ufs->config.lowerdir);
        kfree(ufs->config.upperdir);
        kfree(ufs->config.workdir);
+       put_cred(ufs->creator_cred);
        kfree(ufs);
 }
 
@@ -1064,16 +1074,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                /*
                 * Upper should support d_type, else whiteouts are visible.
                 * Given workdir and upper are on same fs, we can do
-                * iterate_dir() on workdir.
+                * iterate_dir() on workdir. This check requires successful
+                * creation of workdir in previous step.
                 */
-               err = ovl_check_d_type_supported(&workpath);
-               if (err < 0)
-                       goto out_put_workdir;
+               if (ufs->workdir) {
+                       err = ovl_check_d_type_supported(&workpath);
+                       if (err < 0)
+                               goto out_put_workdir;
 
-               if (!err) {
-                       pr_err("overlayfs: upper fs needs to support d_type.\n");
-                       err = -EINVAL;
-                       goto out_put_workdir;
+                       if (!err) {
+                               pr_err("overlayfs: upper fs needs to support d_type.\n");
+                               err = -EINVAL;
+                               goto out_put_workdir;
+                       }
                }
        }
 
@@ -1108,10 +1121,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        else
                sb->s_d_op = &ovl_dentry_operations;
 
+       ufs->creator_cred = prepare_creds();
+       if (!ufs->creator_cred)
+               goto out_put_lower_mnt;
+
        err = -ENOMEM;
        oe = ovl_alloc_entry(numlower);
        if (!oe)
-               goto out_put_lower_mnt;
+               goto out_put_cred;
 
        root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
        if (!root_dentry)
@@ -1144,6 +1161,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
 out_free_oe:
        kfree(oe);
+out_put_cred:
+       put_cred(ufs->creator_cred);
 out_put_lower_mnt:
        for (i = 0; i < ufs->numlower; i++)
                mntput(ufs->lower_mnt[i]);