Merge tag 'ovl-update-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 30 May 2022 18:19:16 +0000 (11:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 30 May 2022 18:19:16 +0000 (11:19 -0700)
Pull overlayfs updates from Miklos Szeredi:

 - Support idmapped layers in overlayfs (Christian Brauner)

 - Add a fix to exportfs that is relevant to open_by_handle_at(2) as
   well

 - Introduce new lookup helpers that allow passing mnt_userns into
   inode_permission()

* tag 'ovl-update-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: support idmapped layers
  ovl: handle idmappings in ovl_xattr_{g,s}et()
  ovl: handle idmappings in layer open helpers
  ovl: handle idmappings in ovl_permission()
  ovl: use ovl_copy_{real,upper}attr() wrappers
  ovl: store lower path in ovl_inode
  ovl: handle idmappings for layer lookup
  ovl: handle idmappings for layer fileattrs
  ovl: use ovl_path_getxattr() wrapper
  ovl: use ovl_lookup_upper() wrapper
  ovl: use ovl_do_notify_change() wrapper
  ovl: pass layer mnt to ovl_open_realfile()
  ovl: pass ofs to setattr operations
  ovl: handle idmappings in creation operations
  ovl: add ovl_upper_mnt_userns() wrapper
  ovl: pass ofs to creation operations
  ovl: use wrappers to all vfs_*xattr() calls
  exportfs: support idmapped mounts
  fs: add two trivial lookup helpers

14 files changed:
fs/exportfs/expfs.c
fs/namei.c
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/export.c
fs/overlayfs/file.c
fs/overlayfs/inode.c
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/ovl_entry.h
fs/overlayfs/readdir.c
fs/overlayfs/super.c
fs/overlayfs/util.c
include/linux/namei.h

index 0106eba46d5afcf6738dd0db21186754b7b99128..3ef80d000e13d5a46e886492583322e8344f182e 100644 (file)
@@ -145,7 +145,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
        if (err)
                goto out_err;
        dprintk("%s: found name: %s\n", __func__, nbuf);
-       tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf));
+       tmp = lookup_one_unlocked(mnt_user_ns(mnt), nbuf, parent, strlen(nbuf));
        if (IS_ERR(tmp)) {
                dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
                err = PTR_ERR(tmp);
@@ -525,7 +525,8 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
                }
 
                inode_lock(target_dir->d_inode);
-               nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
+               nresult = lookup_one(mnt_user_ns(mnt), nbuf,
+                                    target_dir, strlen(nbuf));
                if (!IS_ERR(nresult)) {
                        if (unlikely(nresult->d_inode != result->d_inode)) {
                                dput(nresult);
index 3dc0db2df561835e78e6e3ab6b301d26d3f637fc..776ecf6799650c28702780749cf5102ebe7a0f4c 100644 (file)
@@ -2769,7 +2769,8 @@ struct dentry *lookup_one(struct user_namespace *mnt_userns, const char *name,
 EXPORT_SYMBOL(lookup_one);
 
 /**
- * lookup_one_len_unlocked - filesystem helper to lookup single pathname component
+ * lookup_one_unlocked - filesystem helper to lookup single pathname component
+ * @mnt_userns:        idmapping of the mount the lookup is performed from
  * @name:      pathname component to lookup
  * @base:      base directory to lookup from
  * @len:       maximum length @len should be interpreted to
@@ -2780,14 +2781,15 @@ EXPORT_SYMBOL(lookup_one);
  * Unlike lookup_one_len, it should be called without the parent
  * i_mutex held, and will take the i_mutex itself if necessary.
  */
-struct dentry *lookup_one_len_unlocked(const char *name,
-                                      struct dentry *base, int len)
+struct dentry *lookup_one_unlocked(struct user_namespace *mnt_userns,
+                                  const char *name, struct dentry *base,
+                                  int len)
 {
        struct qstr this;
        int err;
        struct dentry *ret;
 
-       err = lookup_one_common(&init_user_ns, name, base, len, &this);
+       err = lookup_one_common(mnt_userns, name, base, len, &this);
        if (err)
                return ERR_PTR(err);
 
@@ -2796,6 +2798,59 @@ struct dentry *lookup_one_len_unlocked(const char *name,
                ret = lookup_slow(&this, base, 0);
        return ret;
 }
+EXPORT_SYMBOL(lookup_one_unlocked);
+
+/**
+ * lookup_one_positive_unlocked - filesystem helper to lookup single
+ *                               pathname component
+ * @mnt_userns:        idmapping of the mount the lookup is performed from
+ * @name:      pathname component to lookup
+ * @base:      base directory to lookup from
+ * @len:       maximum length @len should be interpreted to
+ *
+ * This helper will yield ERR_PTR(-ENOENT) on negatives. The helper returns
+ * known positive or ERR_PTR(). This is what most of the users want.
+ *
+ * Note that pinned negative with unlocked parent _can_ become positive at any
+ * time, so callers of lookup_one_unlocked() need to be very careful; pinned
+ * positives have >d_inode stable, so this one avoids such problems.
+ *
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.
+ *
+ * The helper should be called without i_mutex held.
+ */
+struct dentry *lookup_one_positive_unlocked(struct user_namespace *mnt_userns,
+                                           const char *name,
+                                           struct dentry *base, int len)
+{
+       struct dentry *ret = lookup_one_unlocked(mnt_userns, name, base, len);
+
+       if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
+               dput(ret);
+               ret = ERR_PTR(-ENOENT);
+       }
+       return ret;
+}
+EXPORT_SYMBOL(lookup_one_positive_unlocked);
+
+/**
+ * lookup_one_len_unlocked - filesystem helper to lookup single pathname component
+ * @name:      pathname component to lookup
+ * @base:      base directory to lookup from
+ * @len:       maximum length @len should be interpreted to
+ *
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.
+ *
+ * Unlike lookup_one_len, it should be called without the parent
+ * i_mutex held, and will take the i_mutex itself if necessary.
+ */
+struct dentry *lookup_one_len_unlocked(const char *name,
+                                      struct dentry *base, int len)
+{
+       return lookup_one_unlocked(&init_user_ns, name, base, len);
+}
 EXPORT_SYMBOL(lookup_one_len_unlocked);
 
 /*
@@ -2809,12 +2864,7 @@ EXPORT_SYMBOL(lookup_one_len_unlocked);
 struct dentry *lookup_positive_unlocked(const char *name,
                                       struct dentry *base, int len)
 {
-       struct dentry *ret = lookup_one_len_unlocked(name, base, len);
-       if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
-               dput(ret);
-               ret = ERR_PTR(-ENOENT);
-       }
-       return ret;
+       return lookup_one_positive_unlocked(&init_user_ns, name, base, len);
 }
 EXPORT_SYMBOL(lookup_positive_unlocked);
 
index e040970408d4ff2b57c3e64518961eacbf66113a..714ec569d25b31129831418d5f58a1d525580e0f 100644 (file)
@@ -44,9 +44,9 @@ static bool ovl_must_copy_xattr(const char *name)
               !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
 }
 
-int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
-                  struct dentry *new)
+int ovl_copy_xattr(struct super_block *sb, struct path *oldpath, struct dentry *new)
 {
+       struct dentry *old = oldpath->dentry;
        ssize_t list_size, size, value_size = 0;
        char *buf, *name, *value = NULL;
        int error = 0;
@@ -94,9 +94,9 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
                        continue; /* Discard */
                }
 retry:
-               size = vfs_getxattr(&init_user_ns, old, name, value, value_size);
+               size = ovl_do_getxattr(oldpath, name, value, value_size);
                if (size == -ERANGE)
-                       size = vfs_getxattr(&init_user_ns, old, name, NULL, 0);
+                       size = ovl_do_getxattr(oldpath, name, NULL, 0);
 
                if (size < 0) {
                        error = size;
@@ -117,7 +117,7 @@ retry:
                        goto retry;
                }
 
-               error = vfs_setxattr(&init_user_ns, new, name, value, size, 0);
+               error = ovl_do_setxattr(OVL_FS(sb), new, name, value, size, 0);
                if (error) {
                        if (error != -EOPNOTSUPP || ovl_must_copy_xattr(name))
                                break;
@@ -292,17 +292,19 @@ out_fput:
        return error;
 }
 
-static int ovl_set_size(struct dentry *upperdentry, struct kstat *stat)
+static int ovl_set_size(struct ovl_fs *ofs,
+                       struct dentry *upperdentry, struct kstat *stat)
 {
        struct iattr attr = {
                .ia_valid = ATTR_SIZE,
                .ia_size = stat->size,
        };
 
-       return notify_change(&init_user_ns, upperdentry, &attr, NULL);
+       return ovl_do_notify_change(ofs, upperdentry, &attr);
 }
 
-static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
+static int ovl_set_timestamps(struct ovl_fs *ofs, struct dentry *upperdentry,
+                             struct kstat *stat)
 {
        struct iattr attr = {
                .ia_valid =
@@ -311,10 +313,11 @@ static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
                .ia_mtime = stat->mtime,
        };
 
-       return notify_change(&init_user_ns, upperdentry, &attr, NULL);
+       return ovl_do_notify_change(ofs, upperdentry, &attr);
 }
 
-int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
+int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upperdentry,
+                struct kstat *stat)
 {
        int err = 0;
 
@@ -323,7 +326,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
                        .ia_valid = ATTR_MODE,
                        .ia_mode = stat->mode,
                };
-               err = notify_change(&init_user_ns, upperdentry, &attr, NULL);
+               err = ovl_do_notify_change(ofs, upperdentry, &attr);
        }
        if (!err) {
                struct iattr attr = {
@@ -331,10 +334,10 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
                        .ia_uid = stat->uid,
                        .ia_gid = stat->gid,
                };
-               err = notify_change(&init_user_ns, upperdentry, &attr, NULL);
+               err = ovl_do_notify_change(ofs, upperdentry, &attr);
        }
        if (!err)
-               ovl_set_timestamps(upperdentry, stat);
+               ovl_set_timestamps(ofs, upperdentry, stat);
 
        return err;
 }
@@ -433,7 +436,7 @@ static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper,
        if (IS_ERR(fh))
                return PTR_ERR(fh);
 
-       err = ovl_do_setxattr(ofs, index, OVL_XATTR_UPPER, fh->buf, fh->fb.len);
+       err = ovl_setxattr(ofs, index, OVL_XATTR_UPPER, fh->buf, fh->fb.len);
 
        kfree(fh);
        return err;
@@ -474,7 +477,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
        if (err)
                return err;
 
-       temp = ovl_create_temp(indexdir, OVL_CATTR(S_IFDIR | 0));
+       temp = ovl_create_temp(ofs, indexdir, OVL_CATTR(S_IFDIR | 0));
        err = PTR_ERR(temp);
        if (IS_ERR(temp))
                goto free_name;
@@ -483,16 +486,16 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
        if (err)
                goto out;
 
-       index = lookup_one_len(name.name, indexdir, name.len);
+       index = ovl_lookup_upper(ofs, name.name, indexdir, name.len);
        if (IS_ERR(index)) {
                err = PTR_ERR(index);
        } else {
-               err = ovl_do_rename(dir, temp, dir, index, 0);
+               err = ovl_do_rename(ofs, dir, temp, dir, index, 0);
                dput(index);
        }
 out:
        if (err)
-               ovl_cleanup(dir, temp);
+               ovl_cleanup(ofs, dir, temp);
        dput(temp);
 free_name:
        kfree(name.name);
@@ -519,6 +522,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
        int err;
        struct dentry *upper;
        struct dentry *upperdir = ovl_dentry_upper(c->parent);
+       struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
        struct inode *udir = d_inode(upperdir);
 
        /* Mark parent "impure" because it may now contain non-pure upper */
@@ -531,16 +535,16 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
                return err;
 
        inode_lock_nested(udir, I_MUTEX_PARENT);
-       upper = lookup_one_len(c->dentry->d_name.name, upperdir,
-                              c->dentry->d_name.len);
+       upper = ovl_lookup_upper(ofs, c->dentry->d_name.name, upperdir,
+                                c->dentry->d_name.len);
        err = PTR_ERR(upper);
        if (!IS_ERR(upper)) {
-               err = ovl_do_link(ovl_dentry_upper(c->dentry), udir, upper);
+               err = ovl_do_link(ofs, ovl_dentry_upper(c->dentry), udir, upper);
                dput(upper);
 
                if (!err) {
                        /* Restore timestamps on parent (best effort) */
-                       ovl_set_timestamps(upperdir, &c->pstat);
+                       ovl_set_timestamps(ofs, upperdir, &c->pstat);
                        ovl_dentry_set_upper_alias(c->dentry);
                }
        }
@@ -578,7 +582,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
                        return err;
        }
 
-       err = ovl_copy_xattr(c->dentry->d_sb, c->lowerpath.dentry, temp);
+       err = ovl_copy_xattr(c->dentry->d_sb, &c->lowerpath, temp);
        if (err)
                return err;
 
@@ -614,9 +618,9 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
 
        inode_lock(temp->d_inode);
        if (S_ISREG(c->stat.mode))
-               err = ovl_set_size(temp, &c->stat);
+               err = ovl_set_size(ofs, temp, &c->stat);
        if (!err)
-               err = ovl_set_attr(temp, &c->stat);
+               err = ovl_set_attr(ofs, temp, &c->stat);
        inode_unlock(temp->d_inode);
 
        return err;
@@ -656,6 +660,7 @@ static void ovl_revert_cu_creds(struct ovl_cu_creds *cc)
  */
 static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
 {
+       struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
        struct inode *inode;
        struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
        struct dentry *temp, *upper;
@@ -677,7 +682,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
        if (err)
                goto unlock;
 
-       temp = ovl_create_temp(c->workdir, &cattr);
+       temp = ovl_create_temp(ofs, c->workdir, &cattr);
        ovl_revert_cu_creds(&cc);
 
        err = PTR_ERR(temp);
@@ -694,12 +699,13 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
                        goto cleanup;
        }
 
-       upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
+       upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
+                                c->destname.len);
        err = PTR_ERR(upper);
        if (IS_ERR(upper))
                goto cleanup;
 
-       err = ovl_do_rename(wdir, temp, udir, upper, 0);
+       err = ovl_do_rename(ofs, wdir, temp, udir, upper, 0);
        dput(upper);
        if (err)
                goto cleanup;
@@ -716,7 +722,7 @@ unlock:
        return err;
 
 cleanup:
-       ovl_cleanup(wdir, temp);
+       ovl_cleanup(ofs, wdir, temp);
        dput(temp);
        goto unlock;
 }
@@ -724,6 +730,7 @@ cleanup:
 /* Copyup using O_TMPFILE which does not require cross dir locking */
 static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
 {
+       struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
        struct inode *udir = d_inode(c->destdir);
        struct dentry *temp, *upper;
        struct ovl_cu_creds cc;
@@ -733,7 +740,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
        if (err)
                return err;
 
-       temp = ovl_do_tmpfile(c->workdir, c->stat.mode);
+       temp = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
        ovl_revert_cu_creds(&cc);
 
        if (IS_ERR(temp))
@@ -745,10 +752,11 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
 
        inode_lock_nested(udir, I_MUTEX_PARENT);
 
-       upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
+       upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
+                                c->destname.len);
        err = PTR_ERR(upper);
        if (!IS_ERR(upper)) {
-               err = ovl_do_link(temp, udir, upper);
+               err = ovl_do_link(ofs, temp, udir, upper);
                dput(upper);
        }
        inode_unlock(udir);
@@ -836,7 +844,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
 
                /* Restore timestamps on parent (best effort) */
                inode_lock(udir);
-               ovl_set_timestamps(c->destdir, &c->pstat);
+               ovl_set_timestamps(ofs, c->destdir, &c->pstat);
                inode_unlock(udir);
 
                ovl_dentry_set_upper_alias(c->dentry);
@@ -865,12 +873,12 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
        return true;
 }
 
-static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
+static ssize_t ovl_getxattr_value(struct path *path, char *name, char **value)
 {
        ssize_t res;
        char *buf;
 
-       res = vfs_getxattr(&init_user_ns, dentry, name, NULL, 0);
+       res = ovl_do_getxattr(path, name, NULL, 0);
        if (res == -ENODATA || res == -EOPNOTSUPP)
                res = 0;
 
@@ -879,7 +887,7 @@ static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
                if (!buf)
                        return -ENOMEM;
 
-               res = vfs_getxattr(&init_user_ns, dentry, name, buf, res);
+               res = ovl_do_getxattr(path, name, buf, res);
                if (res < 0)
                        kfree(buf);
                else
@@ -906,8 +914,8 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
                return -EIO;
 
        if (c->stat.size) {
-               err = cap_size = ovl_getxattr(upperpath.dentry, XATTR_NAME_CAPS,
-                                             &capability);
+               err = cap_size = ovl_getxattr_value(&upperpath, XATTR_NAME_CAPS,
+                                                   &capability);
                if (cap_size < 0)
                        goto out;
        }
@@ -921,14 +929,14 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
         * don't want that to happen for normal copy-up operation.
         */
        if (capability) {
-               err = vfs_setxattr(&init_user_ns, upperpath.dentry,
-                                  XATTR_NAME_CAPS, capability, cap_size, 0);
+               err = ovl_do_setxattr(ofs, upperpath.dentry, XATTR_NAME_CAPS,
+                                     capability, cap_size, 0);
                if (err)
                        goto out_free;
        }
 
 
-       err = ovl_do_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY);
+       err = ovl_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY);
        if (err)
                goto out_free;
 
index f18490813170af41375c24360ba6e136a841b30a..6b03457f72bb10b3ca0cb4532e6e12eb4ff7cb64 100644 (file)
@@ -23,15 +23,15 @@ MODULE_PARM_DESC(redirect_max,
 
 static int ovl_set_redirect(struct dentry *dentry, bool samedir);
 
-int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
+int ovl_cleanup(struct ovl_fs *ofs, struct inode *wdir, struct dentry *wdentry)
 {
        int err;
 
        dget(wdentry);
        if (d_is_dir(wdentry))
-               err = ovl_do_rmdir(wdir, wdentry);
+               err = ovl_do_rmdir(ofs, wdir, wdentry);
        else
-               err = ovl_do_unlink(wdir, wdentry);
+               err = ovl_do_unlink(ofs, wdir, wdentry);
        dput(wdentry);
 
        if (err) {
@@ -42,7 +42,7 @@ int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
        return err;
 }
 
-struct dentry *ovl_lookup_temp(struct dentry *workdir)
+struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir)
 {
        struct dentry *temp;
        char name[20];
@@ -51,7 +51,7 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir)
        /* counter is allowed to wrap, since temp dentries are ephemeral */
        snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
 
-       temp = lookup_one_len(name, workdir, strlen(name));
+       temp = ovl_lookup_upper(ofs, name, workdir, strlen(name));
        if (!IS_ERR(temp) && temp->d_inode) {
                pr_err("workdir/%s already exists\n", name);
                dput(temp);
@@ -70,11 +70,11 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
        struct inode *wdir = workdir->d_inode;
 
        if (!ofs->whiteout) {
-               whiteout = ovl_lookup_temp(workdir);
+               whiteout = ovl_lookup_temp(ofs, workdir);
                if (IS_ERR(whiteout))
                        goto out;
 
-               err = ovl_do_whiteout(wdir, whiteout);
+               err = ovl_do_whiteout(ofs, wdir, whiteout);
                if (err) {
                        dput(whiteout);
                        whiteout = ERR_PTR(err);
@@ -84,11 +84,11 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
        }
 
        if (ofs->share_whiteout) {
-               whiteout = ovl_lookup_temp(workdir);
+               whiteout = ovl_lookup_temp(ofs, workdir);
                if (IS_ERR(whiteout))
                        goto out;
 
-               err = ovl_do_link(ofs->whiteout, wdir, whiteout);
+               err = ovl_do_link(ofs, ofs->whiteout, wdir, whiteout);
                if (!err)
                        goto out;
 
@@ -122,27 +122,28 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
        if (d_is_dir(dentry))
                flags = RENAME_EXCHANGE;
 
-       err = ovl_do_rename(wdir, whiteout, dir, dentry, flags);
+       err = ovl_do_rename(ofs, wdir, whiteout, dir, dentry, flags);
        if (err)
                goto kill_whiteout;
        if (flags)
-               ovl_cleanup(wdir, dentry);
+               ovl_cleanup(ofs, wdir, dentry);
 
 out:
        dput(whiteout);
        return err;
 
 kill_whiteout:
-       ovl_cleanup(wdir, whiteout);
+       ovl_cleanup(ofs, wdir, whiteout);
        goto out;
 }
 
-int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode)
+int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
+                  struct dentry **newdentry, umode_t mode)
 {
        int err;
        struct dentry *d, *dentry = *newdentry;
 
-       err = ovl_do_mkdir(dir, dentry, mode);
+       err = ovl_do_mkdir(ofs, dir, dentry, mode);
        if (err)
                return err;
 
@@ -154,8 +155,8 @@ int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode)
         * to it unhashed and negative. If that happens, try to
         * lookup a new hashed and positive dentry.
         */
-       d = lookup_one_len(dentry->d_name.name, dentry->d_parent,
-                          dentry->d_name.len);
+       d = ovl_lookup_upper(ofs, dentry->d_name.name, dentry->d_parent,
+                            dentry->d_name.len);
        if (IS_ERR(d)) {
                pr_warn("failed lookup after mkdir (%pd2, err=%i).\n",
                        dentry, err);
@@ -167,8 +168,8 @@ int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode)
        return 0;
 }
 
-struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
-                              struct ovl_cattr *attr)
+struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
+                              struct dentry *newdentry, struct ovl_cattr *attr)
 {
        int err;
 
@@ -180,28 +181,28 @@ struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
                goto out;
 
        if (attr->hardlink) {
-               err = ovl_do_link(attr->hardlink, dir, newdentry);
+               err = ovl_do_link(ofs, attr->hardlink, dir, newdentry);
        } else {
                switch (attr->mode & S_IFMT) {
                case S_IFREG:
-                       err = ovl_do_create(dir, newdentry, attr->mode);
+                       err = ovl_do_create(ofs, dir, newdentry, attr->mode);
                        break;
 
                case S_IFDIR:
                        /* mkdir is special... */
-                       err =  ovl_mkdir_real(dir, &newdentry, attr->mode);
+                       err =  ovl_mkdir_real(ofs, dir, &newdentry, attr->mode);
                        break;
 
                case S_IFCHR:
                case S_IFBLK:
                case S_IFIFO:
                case S_IFSOCK:
-                       err = ovl_do_mknod(dir, newdentry, attr->mode,
+                       err = ovl_do_mknod(ofs, dir, newdentry, attr->mode,
                                           attr->rdev);
                        break;
 
                case S_IFLNK:
-                       err = ovl_do_symlink(dir, newdentry, attr->link);
+                       err = ovl_do_symlink(ofs, dir, newdentry, attr->link);
                        break;
 
                default:
@@ -223,10 +224,11 @@ out:
        return newdentry;
 }
 
-struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr)
+struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
+                              struct ovl_cattr *attr)
 {
-       return ovl_create_real(d_inode(workdir), ovl_lookup_temp(workdir),
-                              attr);
+       return ovl_create_real(ofs, d_inode(workdir),
+                              ovl_lookup_temp(ofs, workdir), attr);
 }
 
 static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
@@ -330,10 +332,9 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
                attr->mode &= ~current_umask();
 
        inode_lock_nested(udir, I_MUTEX_PARENT);
-       newdentry = ovl_create_real(udir,
-                                   lookup_one_len(dentry->d_name.name,
-                                                  upperdir,
-                                                  dentry->d_name.len),
+       newdentry = ovl_create_real(ofs, udir,
+                                   ovl_lookup_upper(ofs, dentry->d_name.name,
+                                                    upperdir, dentry->d_name.len),
                                    attr);
        err = PTR_ERR(newdentry);
        if (IS_ERR(newdentry))
@@ -353,7 +354,7 @@ out_unlock:
        return err;
 
 out_cleanup:
-       ovl_cleanup(udir, newdentry);
+       ovl_cleanup(ofs, udir, newdentry);
        dput(newdentry);
        goto out_unlock;
 }
@@ -361,6 +362,7 @@ out_cleanup:
 static struct dentry *ovl_clear_empty(struct dentry *dentry,
                                      struct list_head *list)
 {
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        struct dentry *workdir = ovl_workdir(dentry);
        struct inode *wdir = workdir->d_inode;
        struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
@@ -391,12 +393,12 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
        if (upper->d_parent->d_inode != udir)
                goto out_unlock;
 
-       opaquedir = ovl_create_temp(workdir, OVL_CATTR(stat.mode));
+       opaquedir = ovl_create_temp(ofs, workdir, OVL_CATTR(stat.mode));
        err = PTR_ERR(opaquedir);
        if (IS_ERR(opaquedir))
                goto out_unlock;
 
-       err = ovl_copy_xattr(dentry->d_sb, upper, opaquedir);
+       err = ovl_copy_xattr(dentry->d_sb, &upperpath, opaquedir);
        if (err)
                goto out_cleanup;
 
@@ -405,17 +407,17 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
                goto out_cleanup;
 
        inode_lock(opaquedir->d_inode);
-       err = ovl_set_attr(opaquedir, &stat);
+       err = ovl_set_attr(ofs, opaquedir, &stat);
        inode_unlock(opaquedir->d_inode);
        if (err)
                goto out_cleanup;
 
-       err = ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE);
+       err = ovl_do_rename(ofs, wdir, opaquedir, udir, upper, RENAME_EXCHANGE);
        if (err)
                goto out_cleanup;
 
-       ovl_cleanup_whiteouts(upper, list);
-       ovl_cleanup(wdir, upper);
+       ovl_cleanup_whiteouts(ofs, upper, list);
+       ovl_cleanup(ofs, wdir, upper);
        unlock_rename(workdir, upperdir);
 
        /* dentry's upper doesn't match now, get rid of it */
@@ -424,7 +426,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
        return opaquedir;
 
 out_cleanup:
-       ovl_cleanup(wdir, opaquedir);
+       ovl_cleanup(ofs, wdir, opaquedir);
        dput(opaquedir);
 out_unlock:
        unlock_rename(workdir, upperdir);
@@ -432,8 +434,8 @@ out:
        return ERR_PTR(err);
 }
 
-static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
-                            const struct posix_acl *acl)
+static int ovl_set_upper_acl(struct ovl_fs *ofs, struct dentry *upperdentry,
+                            const char *name, const struct posix_acl *acl)
 {
        void *buffer;
        size_t size;
@@ -451,7 +453,7 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
        if (err < 0)
                goto out_free;
 
-       err = vfs_setxattr(&init_user_ns, upperdentry, name, buffer, size, XATTR_CREATE);
+       err = ovl_do_setxattr(ofs, upperdentry, name, buffer, size, XATTR_CREATE);
 out_free:
        kfree(buffer);
        return err;
@@ -460,6 +462,7 @@ out_free:
 static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
                                    struct ovl_cattr *cattr)
 {
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        struct dentry *workdir = ovl_workdir(dentry);
        struct inode *wdir = workdir->d_inode;
        struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
@@ -484,8 +487,8 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
        if (err)
                goto out;
 
-       upper = lookup_one_len(dentry->d_name.name, upperdir,
-                              dentry->d_name.len);
+       upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
+                                dentry->d_name.len);
        err = PTR_ERR(upper);
        if (IS_ERR(upper))
                goto out_unlock;
@@ -494,7 +497,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
        if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
                goto out_dput;
 
-       newdentry = ovl_create_temp(workdir, cattr);
+       newdentry = ovl_create_temp(ofs, workdir, cattr);
        err = PTR_ERR(newdentry);
        if (IS_ERR(newdentry))
                goto out_dput;
@@ -510,19 +513,19 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
                        .ia_mode = cattr->mode,
                };
                inode_lock(newdentry->d_inode);
-               err = notify_change(&init_user_ns, newdentry, &attr, NULL);
+               err = ovl_do_notify_change(ofs, newdentry, &attr);
                inode_unlock(newdentry->d_inode);
                if (err)
                        goto out_cleanup;
        }
        if (!hardlink) {
-               err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_ACCESS,
-                                       acl);
+               err = ovl_set_upper_acl(ofs, newdentry,
+                                       XATTR_NAME_POSIX_ACL_ACCESS, acl);
                if (err)
                        goto out_cleanup;
 
-               err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_DEFAULT,
-                                       default_acl);
+               err = ovl_set_upper_acl(ofs, newdentry,
+                                       XATTR_NAME_POSIX_ACL_DEFAULT, default_acl);
                if (err)
                        goto out_cleanup;
        }
@@ -532,20 +535,20 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
                if (err)
                        goto out_cleanup;
 
-               err = ovl_do_rename(wdir, newdentry, udir, upper,
+               err = ovl_do_rename(ofs, wdir, newdentry, udir, upper,
                                    RENAME_EXCHANGE);
                if (err)
                        goto out_cleanup;
 
-               ovl_cleanup(wdir, upper);
+               ovl_cleanup(ofs, wdir, upper);
        } else {
-               err = ovl_do_rename(wdir, newdentry, udir, upper, 0);
+               err = ovl_do_rename(ofs, wdir, newdentry, udir, upper, 0);
                if (err)
                        goto out_cleanup;
        }
        err = ovl_instantiate(dentry, inode, newdentry, hardlink);
        if (err) {
-               ovl_cleanup(udir, newdentry);
+               ovl_cleanup(ofs, udir, newdentry);
                dput(newdentry);
        }
 out_dput:
@@ -560,7 +563,7 @@ out:
        return err;
 
 out_cleanup:
-       ovl_cleanup(wdir, newdentry);
+       ovl_cleanup(ofs, wdir, newdentry);
        dput(newdentry);
        goto out_dput;
 }
@@ -767,8 +770,8 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
        if (err)
                goto out_dput;
 
-       upper = lookup_one_len(dentry->d_name.name, upperdir,
-                              dentry->d_name.len);
+       upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
+                                dentry->d_name.len);
        err = PTR_ERR(upper);
        if (IS_ERR(upper))
                goto out_unlock;
@@ -800,6 +803,7 @@ out:
 static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
                            struct list_head *list)
 {
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
        struct inode *dir = upperdir->d_inode;
        struct dentry *upper;
@@ -814,8 +818,8 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
        }
 
        inode_lock_nested(dir, I_MUTEX_PARENT);
-       upper = lookup_one_len(dentry->d_name.name, upperdir,
-                              dentry->d_name.len);
+       upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
+                                dentry->d_name.len);
        err = PTR_ERR(upper);
        if (IS_ERR(upper))
                goto out_unlock;
@@ -826,9 +830,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
                goto out_dput_upper;
 
        if (is_dir)
-               err = vfs_rmdir(&init_user_ns, dir, upper);
+               err = ovl_do_rmdir(ofs, dir, upper);
        else
-               err = vfs_unlink(&init_user_ns, dir, upper, NULL);
+               err = ovl_do_unlink(ofs, dir, upper);
        ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry));
 
        /*
@@ -880,7 +884,6 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
 {
        int err;
        const struct cred *old_cred;
-       struct dentry *upperdentry;
        bool lower_positive = ovl_lower_positive(dentry);
        LIST_HEAD(list);
 
@@ -923,9 +926,8 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
         * Note: we fail to update ctime if there was no copy-up, only a
         * whiteout
         */
-       upperdentry = ovl_dentry_upper(dentry);
-       if (upperdentry)
-               ovl_copyattr(d_inode(upperdentry), d_inode(dentry));
+       if (ovl_dentry_upper(dentry))
+               ovl_copyattr(d_inode(dentry));
 
 out_drop_write:
        ovl_drop_write(dentry);
@@ -1095,6 +1097,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
        bool samedir = olddir == newdir;
        struct dentry *opaquedir = NULL;
        const struct cred *old_cred = NULL;
+       struct ovl_fs *ofs = OVL_FS(old->d_sb);
        LIST_HEAD(list);
 
        err = -EINVAL;
@@ -1189,8 +1192,8 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
 
        trap = lock_rename(new_upperdir, old_upperdir);
 
-       olddentry = lookup_one_len(old->d_name.name, old_upperdir,
-                                  old->d_name.len);
+       olddentry = ovl_lookup_upper(ofs, old->d_name.name, old_upperdir,
+                                    old->d_name.len);
        err = PTR_ERR(olddentry);
        if (IS_ERR(olddentry))
                goto out_unlock;
@@ -1199,8 +1202,8 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
        if (!ovl_matches_upper(old, olddentry))
                goto out_dput_old;
 
-       newdentry = lookup_one_len(new->d_name.name, new_upperdir,
-                                  new->d_name.len);
+       newdentry = ovl_lookup_upper(ofs, new->d_name.name, new_upperdir,
+                                    new->d_name.len);
        err = PTR_ERR(newdentry);
        if (IS_ERR(newdentry))
                goto out_dput_old;
@@ -1251,13 +1254,13 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
        if (err)
                goto out_dput;
 
-       err = ovl_do_rename(old_upperdir->d_inode, olddentry,
+       err = ovl_do_rename(ofs, old_upperdir->d_inode, olddentry,
                            new_upperdir->d_inode, newdentry, flags);
        if (err)
                goto out_dput;
 
        if (cleanup_whiteout)
-               ovl_cleanup(old_upperdir->d_inode, newdentry);
+               ovl_cleanup(ofs, old_upperdir->d_inode, newdentry);
 
        if (overwrite && d_inode(new)) {
                if (new_is_dir)
@@ -1272,9 +1275,9 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
                         (d_inode(new) && ovl_type_origin(new)));
 
        /* copy ctime: */
-       ovl_copyattr(d_inode(olddentry), d_inode(old));
+       ovl_copyattr(d_inode(old));
        if (d_inode(new) && ovl_dentry_upper(new))
-               ovl_copyattr(d_inode(newdentry), d_inode(new));
+               ovl_copyattr(d_inode(new));
 
 out_dput:
        dput(newdentry);
index ebde05c9cf62e8e5a69fc87fb62c6431c434b8dc..2eada97bbd23fbd845bc384f6853cf31e51448a3 100644 (file)
@@ -391,6 +391,11 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
         * pointer because we hold no lock on the real dentry.
         */
        take_dentry_name_snapshot(&name, real);
+       /*
+        * No mnt_userns handling here: it's an internal lookup.  Could skip
+        * permission checking altogether, but for now just use non-mnt_userns
+        * transformed ids.
+        */
        this = lookup_one_len(name.name.name, connected, name.name.len);
        release_dentry_name_snapshot(&name);
        err = PTR_ERR(this);
index 9d69b4dbb8c4cbc269598676540e1cf3c34f0e15..daff601b5c41011c1285f760e0c9b589503d0075 100644 (file)
@@ -38,9 +38,11 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
 #define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY)
 
 static struct file *ovl_open_realfile(const struct file *file,
-                                     struct inode *realinode)
+                                     struct path *realpath)
 {
+       struct inode *realinode = d_inode(realpath->dentry);
        struct inode *inode = file_inode(file);
+       struct user_namespace *real_mnt_userns;
        struct file *realfile;
        const struct cred *old_cred;
        int flags = file->f_flags | OVL_OPEN_FLAGS;
@@ -51,11 +53,12 @@ static struct file *ovl_open_realfile(const struct file *file,
                acc_mode |= MAY_APPEND;
 
        old_cred = ovl_override_creds(inode->i_sb);
-       err = inode_permission(&init_user_ns, realinode, MAY_OPEN | acc_mode);
+       real_mnt_userns = mnt_user_ns(realpath->mnt);
+       err = inode_permission(real_mnt_userns, realinode, MAY_OPEN | acc_mode);
        if (err) {
                realfile = ERR_PTR(err);
        } else {
-               if (!inode_owner_or_capable(&init_user_ns, realinode))
+               if (!inode_owner_or_capable(real_mnt_userns, realinode))
                        flags &= ~O_NOATIME;
 
                realfile = open_with_fake_path(&file->f_path, flags, realinode,
@@ -101,21 +104,21 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
 static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
                               bool allow_meta)
 {
-       struct inode *inode = file_inode(file);
-       struct inode *realinode;
+       struct dentry *dentry = file_dentry(file);
+       struct path realpath;
 
        real->flags = 0;
        real->file = file->private_data;
 
        if (allow_meta)
-               realinode = ovl_inode_real(inode);
+               ovl_path_real(dentry, &realpath);
        else
-               realinode = ovl_inode_realdata(inode);
+               ovl_path_realdata(dentry, &realpath);
 
        /* Has it been copied up since we'd opened it? */
-       if (unlikely(file_inode(real->file) != realinode)) {
+       if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) {
                real->flags = FDPUT_FPUT;
-               real->file = ovl_open_realfile(file, realinode);
+               real->file = ovl_open_realfile(file, &realpath);
 
                return PTR_ERR_OR_ZERO(real->file);
        }
@@ -141,17 +144,20 @@ static int ovl_real_fdget(const struct file *file, struct fd *real)
 
 static int ovl_open(struct inode *inode, struct file *file)
 {
+       struct dentry *dentry = file_dentry(file);
        struct file *realfile;
+       struct path realpath;
        int err;
 
-       err = ovl_maybe_copy_up(file_dentry(file), file->f_flags);
+       err = ovl_maybe_copy_up(dentry, file->f_flags);
        if (err)
                return err;
 
        /* No longer need these flags, so don't pass them on to underlying fs */
        file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
-       realfile = ovl_open_realfile(file, ovl_inode_realdata(inode));
+       ovl_path_realdata(dentry, &realpath);
+       realfile = ovl_open_realfile(file, &realpath);
        if (IS_ERR(realfile))
                return PTR_ERR(realfile);
 
@@ -270,7 +276,7 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
                __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb,
                                      SB_FREEZE_WRITE);
                file_end_write(iocb->ki_filp);
-               ovl_copyattr(ovl_inode_real(inode), inode);
+               ovl_copyattr(inode);
        }
 
        orig_iocb->ki_pos = iocb->ki_pos;
@@ -352,7 +358,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
 
        inode_lock(inode);
        /* Update mode */
-       ovl_copyattr(ovl_inode_real(inode), inode);
+       ovl_copyattr(inode);
        ret = file_remove_privs(file);
        if (ret)
                goto out_unlock;
@@ -376,7 +382,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
                                     ovl_iocb_to_rwf(ifl));
                file_end_write(real.file);
                /* Update size */
-               ovl_copyattr(ovl_inode_real(inode), inode);
+               ovl_copyattr(inode);
        } else {
                struct ovl_aio_req *aio_req;
 
@@ -426,12 +432,11 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
        struct fd real;
        const struct cred *old_cred;
        struct inode *inode = file_inode(out);
-       struct inode *realinode = ovl_inode_real(inode);
        ssize_t ret;
 
        inode_lock(inode);
        /* Update mode */
-       ovl_copyattr(realinode, inode);
+       ovl_copyattr(inode);
        ret = file_remove_privs(out);
        if (ret)
                goto out_unlock;
@@ -447,7 +452,7 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
 
        file_end_write(real.file);
        /* Update size */
-       ovl_copyattr(realinode, inode);
+       ovl_copyattr(inode);
        revert_creds(old_cred);
        fdput(real);
 
@@ -521,7 +526,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
        revert_creds(old_cred);
 
        /* Update size */
-       ovl_copyattr(ovl_inode_real(inode), inode);
+       ovl_copyattr(inode);
 
        fdput(real);
 
@@ -593,7 +598,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
        revert_creds(old_cred);
 
        /* Update size */
-       ovl_copyattr(ovl_inode_real(inode_out), inode_out);
+       ovl_copyattr(inode_out);
 
        fdput(real_in);
        fdput(real_out);
index 1f36158c7dbe2f8782a2dcb6f15a4f0c4bbef28c..492eddeb481f3b8aa8b6e1b2feefb287ccfda481 100644 (file)
@@ -21,6 +21,7 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
                struct iattr *attr)
 {
        int err;
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        bool full_copy_up = false;
        struct dentry *upperdentry;
        const struct cred *old_cred;
@@ -77,10 +78,10 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 
                inode_lock(upperdentry->d_inode);
                old_cred = ovl_override_creds(dentry->d_sb);
-               err = notify_change(&init_user_ns, upperdentry, attr, NULL);
+               err = ovl_do_notify_change(ofs, upperdentry, attr);
                revert_creds(old_cred);
                if (!err)
-                       ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
+                       ovl_copyattr(dentry->d_inode);
                inode_unlock(upperdentry->d_inode);
 
                if (winode)
@@ -279,12 +280,14 @@ int ovl_permission(struct user_namespace *mnt_userns,
                   struct inode *inode, int mask)
 {
        struct inode *upperinode = ovl_inode_upper(inode);
-       struct inode *realinode = upperinode ?: ovl_inode_lower(inode);
+       struct inode *realinode;
+       struct path realpath;
        const struct cred *old_cred;
        int err;
 
        /* Careful in RCU walk mode */
-       if (!realinode) {
+       ovl_i_path_real(inode, &realpath);
+       if (!realpath.dentry) {
                WARN_ON(!(mask & MAY_NOT_BLOCK));
                return -ECHILD;
        }
@@ -297,6 +300,7 @@ int ovl_permission(struct user_namespace *mnt_userns,
        if (err)
                return err;
 
+       realinode = d_inode(realpath.dentry);
        old_cred = ovl_override_creds(inode->i_sb);
        if (!upperinode &&
            !special_file(realinode->i_mode) && mask & MAY_WRITE) {
@@ -304,7 +308,7 @@ int ovl_permission(struct user_namespace *mnt_userns,
                /* Make sure mounter can read file for copy up later */
                mask |= MAY_READ;
        }
-       err = inode_permission(&init_user_ns, realinode, mask);
+       err = inode_permission(mnt_user_ns(realpath.mnt), realinode, mask);
        revert_creds(old_cred);
 
        return err;
@@ -342,8 +346,10 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
                  const void *value, size_t size, int flags)
 {
        int err;
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        struct dentry *upperdentry = ovl_i_dentry_upper(inode);
        struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
+       struct path realpath;
        const struct cred *old_cred;
 
        err = ovl_want_write(dentry);
@@ -351,8 +357,9 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
                goto out;
 
        if (!value && !upperdentry) {
+               ovl_path_lower(dentry, &realpath);
                old_cred = ovl_override_creds(dentry->d_sb);
-               err = vfs_getxattr(&init_user_ns, realdentry, name, NULL, 0);
+               err = vfs_getxattr(mnt_user_ns(realpath.mnt), realdentry, name, NULL, 0);
                revert_creds(old_cred);
                if (err < 0)
                        goto out_drop_write;
@@ -367,17 +374,17 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
        }
 
        old_cred = ovl_override_creds(dentry->d_sb);
-       if (value)
-               err = vfs_setxattr(&init_user_ns, realdentry, name, value, size,
-                                  flags);
-       else {
+       if (value) {
+               err = ovl_do_setxattr(ofs, realdentry, name, value, size,
+                                     flags);
+       else {
                WARN_ON(flags != XATTR_REPLACE);
-               err = vfs_removexattr(&init_user_ns, realdentry, name);
+               err = ovl_do_removexattr(ofs, realdentry, name);
        }
        revert_creds(old_cred);
 
        /* copy c/mtime */
-       ovl_copyattr(d_inode(realdentry), inode);
+       ovl_copyattr(inode);
 
 out_drop_write:
        ovl_drop_write(dentry);
@@ -390,11 +397,11 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
 {
        ssize_t res;
        const struct cred *old_cred;
-       struct dentry *realdentry =
-               ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
+       struct path realpath;
 
+       ovl_i_path_real(inode, &realpath);
        old_cred = ovl_override_creds(dentry->d_sb);
-       res = vfs_getxattr(&init_user_ns, realdentry, name, value, size);
+       res = vfs_getxattr(mnt_user_ns(realpath.mnt), realpath.dentry, name, value, size);
        revert_creds(old_cred);
        return res;
 }
@@ -535,7 +542,7 @@ int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa)
        if (err)
                return err;
 
-       return vfs_fileattr_set(&init_user_ns, realpath->dentry, fa);
+       return vfs_fileattr_set(mnt_user_ns(realpath->mnt), realpath->dentry, fa);
 }
 
 int ovl_fileattr_set(struct user_namespace *mnt_userns,
@@ -579,7 +586,7 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
                inode_set_flags(inode, flags, OVL_COPY_I_FLAGS_MASK);
 
                /* Update ctime */
-               ovl_copyattr(ovl_inode_real(inode), inode);
+               ovl_copyattr(inode);
        }
        ovl_drop_write(dentry);
 out:
@@ -777,16 +784,19 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip,
                    unsigned long ino, int fsid)
 {
        struct inode *realinode;
+       struct ovl_inode *oi = OVL_I(inode);
 
        if (oip->upperdentry)
-               OVL_I(inode)->__upperdentry = oip->upperdentry;
-       if (oip->lowerpath && oip->lowerpath->dentry)
-               OVL_I(inode)->lower = igrab(d_inode(oip->lowerpath->dentry));
+               oi->__upperdentry = oip->upperdentry;
+       if (oip->lowerpath && oip->lowerpath->dentry) {
+               oi->lowerpath.dentry = dget(oip->lowerpath->dentry);
+               oi->lowerpath.layer = oip->lowerpath->layer;
+       }
        if (oip->lowerdata)
-               OVL_I(inode)->lowerdata = igrab(d_inode(oip->lowerdata));
+               oi->lowerdata = igrab(d_inode(oip->lowerdata));
 
        realinode = ovl_inode_real(inode);
-       ovl_copyattr(realinode, inode);
+       ovl_copyattr(inode);
        ovl_copyflags(realinode, inode);
        ovl_map_ino(inode, ino, fsid);
 }
@@ -871,8 +881,8 @@ static int ovl_set_nlink_common(struct dentry *dentry,
        if (WARN_ON(len >= sizeof(buf)))
                return -EIO;
 
-       return ovl_do_setxattr(OVL_FS(inode->i_sb), ovl_dentry_upper(dentry),
-                              OVL_XATTR_NLINK, buf, len);
+       return ovl_setxattr(OVL_FS(inode->i_sb), ovl_dentry_upper(dentry),
+                           OVL_XATTR_NLINK, buf, len);
 }
 
 int ovl_set_nlink_upper(struct dentry *dentry)
@@ -897,8 +907,8 @@ unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
        if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
                return fallback;
 
-       err = ovl_do_getxattr(ofs, upperdentry, OVL_XATTR_NLINK,
-                             &buf, sizeof(buf) - 1);
+       err = ovl_getxattr_upper(ofs, upperdentry, OVL_XATTR_NLINK,
+                                &buf, sizeof(buf) - 1);
        if (err < 0)
                goto fail;
 
@@ -1102,6 +1112,10 @@ struct inode *ovl_get_inode(struct super_block *sb,
        struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
        struct inode *inode;
        struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL;
+       struct path realpath = {
+               .dentry = upperdentry ?: lowerdentry,
+               .mnt = upperdentry ? ovl_upper_mnt(ofs) : lowerpath->layer->mnt,
+       };
        bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry,
                                        oip->index);
        int fsid = bylower ? lowerpath->layer->fsid : 0;
@@ -1175,7 +1189,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
        /* Check for non-merge dir that may have whiteouts */
        if (is_dir) {
                if (((upperdentry && lowerdentry) || oip->numlower > 1) ||
-                   ovl_check_origin_xattr(ofs, upperdentry ?: lowerdentry)) {
+                   ovl_path_check_origin_xattr(ofs, &realpath)) {
                        ovl_set_flag(OVL_WHITEOUTS, inode);
                }
        }
index 1a9b515fc45d468bcd435931a99444aed9eca634..65c4346a5b4312602eb98e19947690bf780003d6 100644 (file)
@@ -16,6 +16,7 @@
 
 struct ovl_lookup_data {
        struct super_block *sb;
+       struct vfsmount *mnt;
        struct qstr name;
        bool is_dir;
        bool opaque;
@@ -25,14 +26,14 @@ struct ovl_lookup_data {
        bool metacopy;
 };
 
-static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
+static int ovl_check_redirect(struct path *path, struct ovl_lookup_data *d,
                              size_t prelen, const char *post)
 {
        int res;
        char *buf;
        struct ovl_fs *ofs = OVL_FS(d->sb);
 
-       buf = ovl_get_redirect_xattr(ofs, dentry, prelen + strlen(post));
+       buf = ovl_get_redirect_xattr(ofs, path, prelen + strlen(post));
        if (IS_ERR_OR_NULL(buf))
                return PTR_ERR(buf);
 
@@ -105,13 +106,13 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len)
        return 0;
 }
 
-static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry,
+static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *upperdentry,
                                 enum ovl_xattr ox)
 {
        int res, err;
        struct ovl_fh *fh = NULL;
 
-       res = ovl_do_getxattr(ofs, dentry, ox, NULL, 0);
+       res = ovl_getxattr_upper(ofs, upperdentry, ox, NULL, 0);
        if (res < 0) {
                if (res == -ENODATA || res == -EOPNOTSUPP)
                        return NULL;
@@ -125,7 +126,7 @@ static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry,
        if (!fh)
                return ERR_PTR(-ENOMEM);
 
-       res = ovl_do_getxattr(ofs, dentry, ox, fh->buf, res);
+       res = ovl_getxattr_upper(ofs, upperdentry, ox, fh->buf, res);
        if (res < 0)
                goto fail;
 
@@ -193,16 +194,17 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
        return real;
 }
 
-static bool ovl_is_opaquedir(struct super_block *sb, struct dentry *dentry)
+static bool ovl_is_opaquedir(struct ovl_fs *ofs, struct path *path)
 {
-       return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_OPAQUE);
+       return ovl_path_check_dir_xattr(ofs, path, OVL_XATTR_OPAQUE);
 }
 
-static struct dentry *ovl_lookup_positive_unlocked(const char *name,
+static struct dentry *ovl_lookup_positive_unlocked(struct ovl_lookup_data *d,
+                                                  const char *name,
                                                   struct dentry *base, int len,
                                                   bool drop_negative)
 {
-       struct dentry *ret = lookup_one_len_unlocked(name, base, len);
+       struct dentry *ret = lookup_one_unlocked(mnt_user_ns(d->mnt), name, base, len);
 
        if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
                if (drop_negative && ret->d_lockref.count == 1) {
@@ -224,10 +226,11 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                             struct dentry **ret, bool drop_negative)
 {
        struct dentry *this;
+       struct path path;
        int err;
        bool last_element = !post[0];
 
-       this = ovl_lookup_positive_unlocked(name, base, namelen, drop_negative);
+       this = ovl_lookup_positive_unlocked(d, name, base, namelen, drop_negative);
        if (IS_ERR(this)) {
                err = PTR_ERR(this);
                this = NULL;
@@ -253,12 +256,15 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                d->stop = true;
                goto put_and_out;
        }
+
+       path.dentry = this;
+       path.mnt = d->mnt;
        if (!d_can_lookup(this)) {
                if (d->is_dir || !last_element) {
                        d->stop = true;
                        goto put_and_out;
                }
-               err = ovl_check_metacopy_xattr(OVL_FS(d->sb), this);
+               err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path);
                if (err < 0)
                        goto out_err;
 
@@ -278,14 +284,14 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                if (d->last)
                        goto out;
 
-               if (ovl_is_opaquedir(d->sb, this)) {
+               if (ovl_is_opaquedir(OVL_FS(d->sb), &path)) {
                        d->stop = true;
                        if (last_element)
                                d->opaque = true;
                        goto out;
                }
        }
-       err = ovl_check_redirect(this, d, prelen, post);
+       err = ovl_check_redirect(&path, d, prelen, post);
        if (err)
                goto out_err;
 out:
@@ -464,7 +470,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
 
        err = ovl_verify_fh(ofs, dentry, ox, fh);
        if (set && err == -ENODATA)
-               err = ovl_do_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
+               err = ovl_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
        if (err)
                goto fail;
 
@@ -704,7 +710,8 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
        if (err)
                return ERR_PTR(err);
 
-       index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len);
+       index = lookup_one_positive_unlocked(ovl_upper_mnt_userns(ofs), name.name,
+                                            ofs->indexdir, name.len);
        if (IS_ERR(index)) {
                err = PTR_ERR(index);
                if (err == -ENOENT) {
@@ -856,6 +863,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
        old_cred = ovl_override_creds(dentry->d_sb);
        upperdir = ovl_dentry_upper(dentry->d_parent);
        if (upperdir) {
+               d.mnt = ovl_upper_mnt(ofs);
                err = ovl_lookup_layer(upperdir, &d, &upperdentry, true);
                if (err)
                        goto out;
@@ -911,6 +919,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                else
                        d.last = lower.layer->idx == roe->numlower;
 
+               d.mnt = lower.layer->mnt;
                err = ovl_lookup_layer(lower.dentry, &d, &this, false);
                if (err)
                        goto out_put;
@@ -1071,14 +1080,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
        if (upperdentry)
                ovl_dentry_set_upper_alias(dentry);
        else if (index) {
-               upperdentry = dget(index);
-               upperredirect = ovl_get_redirect_xattr(ofs, upperdentry, 0);
+               struct path upperpath = {
+                       .dentry = upperdentry = dget(index),
+                       .mnt = ovl_upper_mnt(ofs),
+               };
+
+               upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
                if (IS_ERR(upperredirect)) {
                        err = PTR_ERR(upperredirect);
                        upperredirect = NULL;
                        goto out_free_oe;
                }
-               err = ovl_check_metacopy_xattr(ofs, upperdentry);
+               err = ovl_check_metacopy_xattr(ofs, &upperpath);
                if (err < 0)
                        goto out_free_oe;
                uppermetacopy = err;
@@ -1163,8 +1176,8 @@ bool ovl_lower_positive(struct dentry *dentry)
                struct dentry *this;
                struct dentry *lowerdir = poe->lowerstack[i].dentry;
 
-               this = lookup_positive_unlocked(name->name, lowerdir,
-                                              name->len);
+               this = lookup_one_positive_unlocked(mnt_user_ns(poe->lowerstack[i].layer->mnt),
+                                                  name->name, lowerdir, name->len);
                if (IS_ERR(this)) {
                        switch (PTR_ERR(this)) {
                        case -ENOENT:
index 2cd5741c873b60592a139273017b669075bac72f..4f34b7e02eeeb5659345c7929afa147646a4f594 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/kernel.h>
 #include <linux/uuid.h>
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include "ovl_entry.h"
 
 #undef pr_fmt
@@ -122,109 +123,180 @@ static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
        return ovl_xattr_table[ox][ofs->config.userxattr];
 }
 
-static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
+/*
+ * When changing ownership of an upper object map the intended ownership
+ * according to the upper layer's idmapping. When an upper mount idmaps files
+ * that are stored on-disk as owned by id 1001 to id 1000 this means stat on
+ * this object will report it as being owned by id 1000 when calling stat via
+ * the upper mount.
+ * In order to change ownership of an object so stat reports id 1000 when
+ * called on an idmapped upper mount the value written to disk - i.e., the
+ * value stored in ia_*id - must 1001. The mount mapping helper will thus take
+ * care to map 1000 to 1001.
+ * The mnt idmapping helpers are nops if the upper layer isn't idmapped.
+ */
+static inline int ovl_do_notify_change(struct ovl_fs *ofs,
+                                      struct dentry *upperdentry,
+                                      struct iattr *attr)
+{
+       struct user_namespace *upper_mnt_userns = ovl_upper_mnt_userns(ofs);
+       struct user_namespace *fs_userns = i_user_ns(d_inode(upperdentry));
+
+       if (attr->ia_valid & ATTR_UID)
+               attr->ia_uid = mapped_kuid_user(upper_mnt_userns,
+                                               fs_userns, attr->ia_uid);
+       if (attr->ia_valid & ATTR_GID)
+               attr->ia_gid = mapped_kgid_user(upper_mnt_userns,
+                                               fs_userns, attr->ia_gid);
+
+       return notify_change(upper_mnt_userns, upperdentry, attr, NULL);
+}
+
+static inline int ovl_do_rmdir(struct ovl_fs *ofs,
+                              struct inode *dir, struct dentry *dentry)
 {
-       int err = vfs_rmdir(&init_user_ns, dir, dentry);
+       int err = vfs_rmdir(ovl_upper_mnt_userns(ofs), dir, dentry);
 
        pr_debug("rmdir(%pd2) = %i\n", dentry, err);
        return err;
 }
 
-static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry)
+static inline int ovl_do_unlink(struct ovl_fs *ofs, struct inode *dir,
+                               struct dentry *dentry)
 {
-       int err = vfs_unlink(&init_user_ns, dir, dentry, NULL);
+       int err = vfs_unlink(ovl_upper_mnt_userns(ofs), dir, dentry, NULL);
 
        pr_debug("unlink(%pd2) = %i\n", dentry, err);
        return err;
 }
 
-static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir,
-                             struct dentry *new_dentry)
+static inline int ovl_do_link(struct ovl_fs *ofs, struct dentry *old_dentry,
+                             struct inode *dir, struct dentry *new_dentry)
 {
-       int err = vfs_link(old_dentry, &init_user_ns, dir, new_dentry, NULL);
+       int err = vfs_link(old_dentry, ovl_upper_mnt_userns(ofs), dir, new_dentry, NULL);
 
        pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err);
        return err;
 }
 
-static inline int ovl_do_create(struct inode *dir, struct dentry *dentry,
+static inline int ovl_do_create(struct ovl_fs *ofs,
+                               struct inode *dir, struct dentry *dentry,
                                umode_t mode)
 {
-       int err = vfs_create(&init_user_ns, dir, dentry, mode, true);
+       int err = vfs_create(ovl_upper_mnt_userns(ofs), dir, dentry, mode, true);
 
        pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err);
        return err;
 }
 
-static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry,
+static inline int ovl_do_mkdir(struct ovl_fs *ofs,
+                              struct inode *dir, struct dentry *dentry,
                               umode_t mode)
 {
-       int err = vfs_mkdir(&init_user_ns, dir, dentry, mode);
+       int err = vfs_mkdir(ovl_upper_mnt_userns(ofs), dir, dentry, mode);
        pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err);
        return err;
 }
 
-static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry,
+static inline int ovl_do_mknod(struct ovl_fs *ofs,
+                              struct inode *dir, struct dentry *dentry,
                               umode_t mode, dev_t dev)
 {
-       int err = vfs_mknod(&init_user_ns, dir, dentry, mode, dev);
+       int err = vfs_mknod(ovl_upper_mnt_userns(ofs), dir, dentry, mode, dev);
 
        pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err);
        return err;
 }
 
-static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry,
+static inline int ovl_do_symlink(struct ovl_fs *ofs,
+                                struct inode *dir, struct dentry *dentry,
                                 const char *oldname)
 {
-       int err = vfs_symlink(&init_user_ns, dir, dentry, oldname);
+       int err = vfs_symlink(ovl_upper_mnt_userns(ofs), dir, dentry, oldname);
 
        pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
        return err;
 }
 
-static inline ssize_t ovl_do_getxattr(struct ovl_fs *ofs, struct dentry *dentry,
-                                     enum ovl_xattr ox, void *value,
-                                     size_t size)
+static inline ssize_t ovl_do_getxattr(struct path *path, const char *name,
+                                     void *value, size_t size)
 {
-       const char *name = ovl_xattr(ofs, ox);
-       int err = vfs_getxattr(&init_user_ns, dentry, name, value, size);
-       int len = (value && err > 0) ? err : 0;
+       int err, len;
+
+       WARN_ON(path->dentry->d_sb != path->mnt->mnt_sb);
+
+       err = vfs_getxattr(mnt_user_ns(path->mnt), path->dentry,
+                              name, value, size);
+       len = (value && err > 0) ? err : 0;
 
        pr_debug("getxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
-                dentry, name, min(len, 48), value, size, err);
+                path->dentry, name, min(len, 48), value, size, err);
        return err;
 }
 
+static inline ssize_t ovl_getxattr_upper(struct ovl_fs *ofs,
+                                        struct dentry *upperdentry,
+                                        enum ovl_xattr ox, void *value,
+                                        size_t size)
+{
+       struct path upperpath = {
+               .dentry = upperdentry,
+               .mnt = ovl_upper_mnt(ofs),
+       };
+
+       return ovl_do_getxattr(&upperpath, ovl_xattr(ofs, ox), value, size);
+}
+
+static inline ssize_t ovl_path_getxattr(struct ovl_fs *ofs,
+                                        struct path *path,
+                                        enum ovl_xattr ox, void *value,
+                                        size_t size)
+{
+       return ovl_do_getxattr(path, ovl_xattr(ofs, ox), value, size);
+}
+
 static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
-                                 enum ovl_xattr ox, const void *value,
-                                 size_t size)
+                                 const char *name, const void *value,
+                                 size_t size, int flags)
 {
-       const char *name = ovl_xattr(ofs, ox);
-       int err = vfs_setxattr(&init_user_ns, dentry, name, value, size, 0);
-       pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
-                dentry, name, min((int)size, 48), value, size, err);
+       int err = vfs_setxattr(ovl_upper_mnt_userns(ofs), dentry, name, value, size, flags);
+
+       pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, %d) = %i\n",
+                dentry, name, min((int)size, 48), value, size, flags, err);
        return err;
 }
 
+static inline int ovl_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
+                              enum ovl_xattr ox, const void *value,
+                              size_t size)
+{
+       return ovl_do_setxattr(ofs, dentry, ovl_xattr(ofs, ox), value, size, 0);
+}
+
 static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
-                                    enum ovl_xattr ox)
+                                    const char *name)
 {
-       const char *name = ovl_xattr(ofs, ox);
-       int err = vfs_removexattr(&init_user_ns, dentry, name);
+       int err = vfs_removexattr(ovl_upper_mnt_userns(ofs), dentry, name);
        pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
        return err;
 }
 
-static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry,
-                               struct inode *newdir, struct dentry *newdentry,
-                               unsigned int flags)
+static inline int ovl_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
+                                 enum ovl_xattr ox)
+{
+       return ovl_do_removexattr(ofs, dentry, ovl_xattr(ofs, ox));
+}
+
+static inline int ovl_do_rename(struct ovl_fs *ofs, struct inode *olddir,
+                               struct dentry *olddentry, struct inode *newdir,
+                               struct dentry *newdentry, unsigned int flags)
 {
        int err;
        struct renamedata rd = {
-               .old_mnt_userns = &init_user_ns,
+               .old_mnt_userns = ovl_upper_mnt_userns(ofs),
                .old_dir        = olddir,
                .old_dentry     = olddentry,
-               .new_mnt_userns = &init_user_ns,
+               .new_mnt_userns = ovl_upper_mnt_userns(ofs),
                .new_dir        = newdir,
                .new_dentry     = newdentry,
                .flags          = flags,
@@ -239,22 +311,31 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry,
        return err;
 }
 
-static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry)
+static inline int ovl_do_whiteout(struct ovl_fs *ofs,
+                                 struct inode *dir, struct dentry *dentry)
 {
-       int err = vfs_whiteout(&init_user_ns, dir, dentry);
+       int err = vfs_whiteout(ovl_upper_mnt_userns(ofs), dir, dentry);
        pr_debug("whiteout(%pd2) = %i\n", dentry, err);
        return err;
 }
 
-static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode)
+static inline struct dentry *ovl_do_tmpfile(struct ovl_fs *ofs,
+                                           struct dentry *dentry, umode_t mode)
 {
-       struct dentry *ret = vfs_tmpfile(&init_user_ns, dentry, mode, 0);
+       struct dentry *ret = vfs_tmpfile(ovl_upper_mnt_userns(ofs), dentry, mode, 0);
        int err = PTR_ERR_OR_ZERO(ret);
 
        pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err);
        return ret;
 }
 
+static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
+                                             const char *name,
+                                             struct dentry *base, int len)
+{
+       return lookup_one(ovl_upper_mnt_userns(ofs), name, base, len);
+}
+
 static inline bool ovl_open_flags_need_copy_up(int flags)
 {
        if (!flags)
@@ -293,10 +374,13 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry);
 void ovl_path_upper(struct dentry *dentry, struct path *path);
 void ovl_path_lower(struct dentry *dentry, struct path *path);
 void ovl_path_lowerdata(struct dentry *dentry, struct path *path);
+void ovl_i_path_real(struct inode *inode, struct path *path);
 enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
+enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path);
 struct dentry *ovl_dentry_upper(struct dentry *dentry);
 struct dentry *ovl_dentry_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_lowerdata(struct dentry *dentry);
+const struct ovl_layer *ovl_i_layer_lower(struct inode *inode);
 const struct ovl_layer *ovl_layer_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_real(struct dentry *dentry);
 struct dentry *ovl_i_dentry_upper(struct inode *inode);
@@ -330,9 +414,20 @@ struct file *ovl_path_open(struct path *path, int flags);
 int ovl_copy_up_start(struct dentry *dentry, int flags);
 void ovl_copy_up_end(struct dentry *dentry);
 bool ovl_already_copied_up(struct dentry *dentry, int flags);
-bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry);
-bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
-                        enum ovl_xattr ox);
+bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, struct path *path,
+                             enum ovl_xattr ox);
+bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, struct path *path);
+
+static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
+                                         struct dentry *upperdentry)
+{
+       struct path upperpath = {
+               .dentry = upperdentry,
+               .mnt = ovl_upper_mnt(ofs),
+       };
+       return ovl_path_check_origin_xattr(ofs, &upperpath);
+}
+
 int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
                       enum ovl_xattr ox, const void *value, size_t size,
                       int xerr);
@@ -344,10 +439,9 @@ bool ovl_need_index(struct dentry *dentry);
 int ovl_nlink_start(struct dentry *dentry);
 void ovl_nlink_end(struct dentry *dentry);
 int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
-int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry);
+int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct path *path);
 bool ovl_is_metacopy_dentry(struct dentry *dentry);
-char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
-                            int padding);
+char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct path *path, int padding);
 int ovl_sync_status(struct ovl_fs *ofs);
 
 static inline void ovl_set_flag(unsigned long flag, struct inode *inode)
@@ -366,9 +460,15 @@ static inline bool ovl_test_flag(unsigned long flag, struct inode *inode)
 }
 
 static inline bool ovl_is_impuredir(struct super_block *sb,
-                                   struct dentry *dentry)
+                                   struct dentry *upperdentry)
 {
-       return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_IMPURE);
+       struct ovl_fs *ofs = OVL_FS(sb);
+       struct path upperpath = {
+               .dentry = upperdentry,
+               .mnt = ovl_upper_mnt(ofs),
+       };
+
+       return ovl_path_check_dir_xattr(ofs, &upperpath, OVL_XATTR_IMPURE);
 }
 
 /*
@@ -461,12 +561,13 @@ static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index,
 extern const struct file_operations ovl_dir_operations;
 struct file *ovl_dir_real_file(const struct file *file, bool want_upper);
 int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
-void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list);
+void ovl_cleanup_whiteouts(struct ovl_fs *ofs, struct dentry *upper,
+                          struct list_head *list);
 void ovl_cache_free(struct list_head *list);
 void ovl_dir_cache_free(struct inode *inode);
 int ovl_check_d_type_supported(struct path *realpath);
-int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
-                       struct dentry *dentry, int level);
+int ovl_workdir_cleanup(struct ovl_fs *ofs, struct inode *dir,
+                       struct vfsmount *mnt, struct dentry *dentry, int level);
 int ovl_indexdir_cleanup(struct ovl_fs *ofs);
 
 /*
@@ -520,16 +621,7 @@ bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir);
 struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir);
 struct inode *ovl_get_inode(struct super_block *sb,
                            struct ovl_inode_params *oip);
-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;
-       i_size_write(to, i_size_read(from));
-}
+void ovl_copyattr(struct inode *to);
 
 /* vfs inode flags copied from real to ovl inode */
 #define OVL_COPY_I_FLAGS_MASK  (S_SYNC | S_NOATIME | S_APPEND | S_IMMUTABLE)
@@ -570,12 +662,15 @@ struct ovl_cattr {
 
 #define OVL_CATTR(m) (&(struct ovl_cattr) { .mode = (m) })
 
-int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode);
-struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
+int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
+                  struct dentry **newdentry, umode_t mode);
+struct dentry *ovl_create_real(struct ovl_fs *ofs,
+                              struct inode *dir, struct dentry *newdentry,
+                              struct ovl_cattr *attr);
+int ovl_cleanup(struct ovl_fs *ofs, struct inode *dir, struct dentry *dentry);
+struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir);
+struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
                               struct ovl_cattr *attr);
-int ovl_cleanup(struct inode *dir, struct dentry *dentry);
-struct dentry *ovl_lookup_temp(struct dentry *workdir);
-struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
 
 /* file.c */
 extern const struct file_operations ovl_file_operations;
@@ -591,9 +686,8 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
 int ovl_copy_up(struct dentry *dentry);
 int ovl_copy_up_with_data(struct dentry *dentry);
 int ovl_maybe_copy_up(struct dentry *dentry, int flags);
-int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
-                  struct dentry *new);
-int ovl_set_attr(struct dentry *upper, struct kstat *stat);
+int ovl_copy_xattr(struct super_block *sb, struct path *path, struct dentry *new);
+int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upper, struct kstat *stat);
 struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
                                  bool is_upper);
 int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
index 63efee554f69a7c1e862650d8f7b388576fda2cd..e1af8f66069843c04ab3b4e1c1be384d965a7ecb 100644 (file)
@@ -90,6 +90,11 @@ static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs)
        return ofs->layers[0].mnt;
 }
 
+static inline struct user_namespace *ovl_upper_mnt_userns(struct ovl_fs *ofs)
+{
+       return mnt_user_ns(ovl_upper_mnt(ofs));
+}
+
 static inline struct ovl_fs *OVL_FS(struct super_block *sb)
 {
        return (struct ovl_fs *)sb->s_fs_info;
@@ -129,7 +134,7 @@ struct ovl_inode {
        unsigned long flags;
        struct inode vfs_inode;
        struct dentry *__upperdentry;
-       struct inode *lower;
+       struct ovl_path lowerpath;
 
        /* synchronize copy up and more */
        struct mutex lock;
index 150fdf3bc68d4c812fe45a886c724e58c56f84aa..78f62cc1797becb9caac4d1842a5278f81cf2f71 100644 (file)
@@ -264,11 +264,11 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name,
                return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type);
 }
 
-static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
+static int ovl_check_whiteouts(struct path *path, struct ovl_readdir_data *rdd)
 {
        int err;
        struct ovl_cache_entry *p;
-       struct dentry *dentry;
+       struct dentry *dentry, *dir = path->dentry;
        const struct cred *old_cred;
 
        old_cred = ovl_override_creds(rdd->dentry->d_sb);
@@ -278,7 +278,7 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
                while (rdd->first_maybe_whiteout) {
                        p = rdd->first_maybe_whiteout;
                        rdd->first_maybe_whiteout = p->next_maybe_whiteout;
-                       dentry = lookup_one_len(p->name, dir, p->len);
+                       dentry = lookup_one(mnt_user_ns(path->mnt), p->name, dir, p->len);
                        if (!IS_ERR(dentry)) {
                                p->is_whiteout = ovl_is_whiteout(dentry);
                                dput(dentry);
@@ -312,7 +312,7 @@ static inline int ovl_dir_read(struct path *realpath,
        } while (!err && rdd->count);
 
        if (!err && rdd->first_maybe_whiteout && rdd->dentry)
-               err = ovl_check_whiteouts(realpath->dentry, rdd);
+               err = ovl_check_whiteouts(realpath, rdd);
 
        fput(realfile);
 
@@ -479,7 +479,7 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
                        goto get;
                }
        }
-       this = lookup_one_len(p->name, dir, p->len);
+       this = lookup_one(mnt_user_ns(path->mnt), p->name, dir, p->len);
        if (IS_ERR_OR_NULL(this) || !this->d_inode) {
                /* Mark a stale entry */
                p->is_whiteout = true;
@@ -623,8 +623,8 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
                 * Removing the "impure" xattr is best effort.
                 */
                if (!ovl_want_write(dentry)) {
-                       ovl_do_removexattr(ofs, ovl_dentry_upper(dentry),
-                                          OVL_XATTR_IMPURE);
+                       ovl_removexattr(ofs, ovl_dentry_upper(dentry),
+                                       OVL_XATTR_IMPURE);
                        ovl_drop_write(dentry);
                }
                ovl_clear_flag(OVL_IMPURE, d_inode(dentry));
@@ -1001,7 +1001,8 @@ del_entry:
        return err;
 }
 
-void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
+void ovl_cleanup_whiteouts(struct ovl_fs *ofs, struct dentry *upper,
+                          struct list_head *list)
 {
        struct ovl_cache_entry *p;
 
@@ -1012,7 +1013,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
                if (WARN_ON(!p->is_whiteout || !p->is_upper))
                        continue;
 
-               dentry = lookup_one_len(p->name, upper, p->len);
+               dentry = ovl_lookup_upper(ofs, p->name, upper, p->len);
                if (IS_ERR(dentry)) {
                        pr_err("lookup '%s/%.*s' failed (%i)\n",
                               upper->d_name.name, p->len, p->name,
@@ -1020,7 +1021,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
                        continue;
                }
                if (dentry->d_inode)
-                       ovl_cleanup(upper->d_inode, dentry);
+                       ovl_cleanup(ofs, upper->d_inode, dentry);
                dput(dentry);
        }
        inode_unlock(upper->d_inode);
@@ -1064,7 +1065,8 @@ int ovl_check_d_type_supported(struct path *realpath)
 
 #define OVL_INCOMPATDIR_NAME "incompat"
 
-static int ovl_workdir_cleanup_recurse(struct path *path, int level)
+static int ovl_workdir_cleanup_recurse(struct ovl_fs *ofs, struct path *path,
+                                      int level)
 {
        int err;
        struct inode *dir = path->dentry->d_inode;
@@ -1111,11 +1113,11 @@ static int ovl_workdir_cleanup_recurse(struct path *path, int level)
                        err = -EINVAL;
                        break;
                }
-               dentry = lookup_one_len(p->name, path->dentry, p->len);
+               dentry = ovl_lookup_upper(ofs, p->name, path->dentry, p->len);
                if (IS_ERR(dentry))
                        continue;
                if (dentry->d_inode)
-                       err = ovl_workdir_cleanup(dir, path->mnt, dentry, level);
+                       err = ovl_workdir_cleanup(ofs, dir, path->mnt, dentry, level);
                dput(dentry);
                if (err)
                        break;
@@ -1126,24 +1128,24 @@ out:
        return err;
 }
 
-int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
-                        struct dentry *dentry, int level)
+int ovl_workdir_cleanup(struct ovl_fs *ofs, struct inode *dir,
+                       struct vfsmount *mnt, struct dentry *dentry, int level)
 {
        int err;
 
        if (!d_is_dir(dentry) || level > 1) {
-               return ovl_cleanup(dir, dentry);
+               return ovl_cleanup(ofs, dir, dentry);
        }
 
-       err = ovl_do_rmdir(dir, dentry);
+       err = ovl_do_rmdir(ofs, dir, dentry);
        if (err) {
                struct path path = { .mnt = mnt, .dentry = dentry };
 
                inode_unlock(dir);
-               err = ovl_workdir_cleanup_recurse(&path, level + 1);
+               err = ovl_workdir_cleanup_recurse(ofs, &path, level + 1);
                inode_lock_nested(dir, I_MUTEX_PARENT);
                if (!err)
-                       err = ovl_cleanup(dir, dentry);
+                       err = ovl_cleanup(ofs, dir, dentry);
        }
 
        return err;
@@ -1179,7 +1181,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
                        if (p->len == 2 && p->name[1] == '.')
                                continue;
                }
-               index = lookup_one_len(p->name, indexdir, p->len);
+               index = ovl_lookup_upper(ofs, p->name, indexdir, p->len);
                if (IS_ERR(index)) {
                        err = PTR_ERR(index);
                        index = NULL;
@@ -1187,7 +1189,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
                }
                /* Cleanup leftover from index create/cleanup attempt */
                if (index->d_name.name[0] == '#') {
-                       err = ovl_workdir_cleanup(dir, path.mnt, index, 1);
+                       err = ovl_workdir_cleanup(ofs, dir, path.mnt, index, 1);
                        if (err)
                                break;
                        goto next;
@@ -1197,7 +1199,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
                        goto next;
                } else if (err == -ESTALE) {
                        /* Cleanup stale index entries */
-                       err = ovl_cleanup(dir, index);
+                       err = ovl_cleanup(ofs, dir, index);
                } else if (err != -ENOENT) {
                        /*
                         * Abort mount to avoid corrupting the index if
@@ -1213,7 +1215,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
                        err = ovl_cleanup_and_whiteout(ofs, dir, index);
                } else {
                        /* Cleanup orphan index entries */
-                       err = ovl_cleanup(dir, index);
+                       err = ovl_cleanup(ofs, dir, index);
                }
 
                if (err)
index 001cdbb8f015212b3bf2ade964adaf82294954b1..e0a2e0468ee7f1e4bb668df4a9d13cea9d34082b 100644 (file)
@@ -184,7 +184,8 @@ static struct inode *ovl_alloc_inode(struct super_block *sb)
        oi->version = 0;
        oi->flags = 0;
        oi->__upperdentry = NULL;
-       oi->lower = NULL;
+       oi->lowerpath.dentry = NULL;
+       oi->lowerpath.layer = NULL;
        oi->lowerdata = NULL;
        mutex_init(&oi->lock);
 
@@ -205,7 +206,7 @@ static void ovl_destroy_inode(struct inode *inode)
        struct ovl_inode *oi = OVL_I(inode);
 
        dput(oi->__upperdentry);
-       iput(oi->lower);
+       dput(oi->lowerpath.dentry);
        if (S_ISDIR(inode->i_mode))
                ovl_dir_cache_free(inode);
        else
@@ -761,7 +762,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
 
        inode_lock_nested(dir, I_MUTEX_PARENT);
 retry:
-       work = lookup_one_len(name, ofs->workbasedir, strlen(name));
+       work = ovl_lookup_upper(ofs, name, ofs->workbasedir, strlen(name));
 
        if (!IS_ERR(work)) {
                struct iattr attr = {
@@ -778,7 +779,7 @@ retry:
                                goto out_unlock;
 
                        retried = true;
-                       err = ovl_workdir_cleanup(dir, mnt, work, 0);
+                       err = ovl_workdir_cleanup(ofs, dir, mnt, work, 0);
                        dput(work);
                        if (err == -EINVAL) {
                                work = ERR_PTR(err);
@@ -787,7 +788,7 @@ retry:
                        goto retry;
                }
 
-               err = ovl_mkdir_real(dir, &work, attr.ia_mode);
+               err = ovl_mkdir_real(ofs, dir, &work, attr.ia_mode);
                if (err)
                        goto out_dput;
 
@@ -809,19 +810,19 @@ retry:
                 * allowed as upper are limited to "normal" ones, where checking
                 * for the above two errors is sufficient.
                 */
-               err = vfs_removexattr(&init_user_ns, work,
-                                     XATTR_NAME_POSIX_ACL_DEFAULT);
+               err = ovl_do_removexattr(ofs, work,
+                                        XATTR_NAME_POSIX_ACL_DEFAULT);
                if (err && err != -ENODATA && err != -EOPNOTSUPP)
                        goto out_dput;
 
-               err = vfs_removexattr(&init_user_ns, work,
-                                     XATTR_NAME_POSIX_ACL_ACCESS);
+               err = ovl_do_removexattr(ofs, work,
+                                        XATTR_NAME_POSIX_ACL_ACCESS);
                if (err && err != -ENODATA && err != -EOPNOTSUPP)
                        goto out_dput;
 
                /* Clear any inherited mode bits */
                inode_lock(work->d_inode);
-               err = notify_change(&init_user_ns, work, &attr, NULL);
+               err = ovl_do_notify_change(ofs, work, &attr);
                inode_unlock(work->d_inode);
                if (err)
                        goto out_dput;
@@ -873,10 +874,6 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
                pr_err("filesystem on '%s' not supported\n", name);
                goto out_put;
        }
-       if (is_idmapped_mnt(path->mnt)) {
-               pr_err("idmapped layers are currently not supported\n");
-               goto out_put;
-       }
        if (!d_is_dir(path->dentry)) {
                pr_err("'%s' not a directory\n", name);
                goto out_put;
@@ -1256,8 +1253,9 @@ out:
  * Returns 1 if RENAME_WHITEOUT is supported, 0 if not supported and
  * negative values if error is encountered.
  */
-static int ovl_check_rename_whiteout(struct dentry *workdir)
+static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
 {
+       struct dentry *workdir = ofs->workdir;
        struct inode *dir = d_inode(workdir);
        struct dentry *temp;
        struct dentry *dest;
@@ -1267,12 +1265,12 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
 
        inode_lock_nested(dir, I_MUTEX_PARENT);
 
-       temp = ovl_create_temp(workdir, OVL_CATTR(S_IFREG | 0));
+       temp = ovl_create_temp(ofs, workdir, OVL_CATTR(S_IFREG | 0));
        err = PTR_ERR(temp);
        if (IS_ERR(temp))
                goto out_unlock;
 
-       dest = ovl_lookup_temp(workdir);
+       dest = ovl_lookup_temp(ofs, workdir);
        err = PTR_ERR(dest);
        if (IS_ERR(dest)) {
                dput(temp);
@@ -1281,14 +1279,14 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
 
        /* Name is inline and stable - using snapshot as a copy helper */
        take_dentry_name_snapshot(&name, temp);
-       err = ovl_do_rename(dir, temp, dir, dest, RENAME_WHITEOUT);
+       err = ovl_do_rename(ofs, dir, temp, dir, dest, RENAME_WHITEOUT);
        if (err) {
                if (err == -EINVAL)
                        err = 0;
                goto cleanup_temp;
        }
 
-       whiteout = lookup_one_len(name.name.name, workdir, name.name.len);
+       whiteout = ovl_lookup_upper(ofs, name.name.name, workdir, name.name.len);
        err = PTR_ERR(whiteout);
        if (IS_ERR(whiteout))
                goto cleanup_temp;
@@ -1297,11 +1295,11 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
 
        /* Best effort cleanup of whiteout and temp file */
        if (err)
-               ovl_cleanup(dir, whiteout);
+               ovl_cleanup(ofs, dir, whiteout);
        dput(whiteout);
 
 cleanup_temp:
-       ovl_cleanup(dir, temp);
+       ovl_cleanup(ofs, dir, temp);
        release_dentry_name_snapshot(&name);
        dput(temp);
        dput(dest);
@@ -1312,16 +1310,17 @@ out_unlock:
        return err;
 }
 
-static struct dentry *ovl_lookup_or_create(struct dentry *parent,
+static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
+                                          struct dentry *parent,
                                           const char *name, umode_t mode)
 {
        size_t len = strlen(name);
        struct dentry *child;
 
        inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
-       child = lookup_one_len(name, parent, len);
+       child = ovl_lookup_upper(ofs, name, parent, len);
        if (!IS_ERR(child) && !child->d_inode)
-               child = ovl_create_real(parent->d_inode, child,
+               child = ovl_create_real(ofs, parent->d_inode, child,
                                        OVL_CATTR(mode));
        inode_unlock(parent->d_inode);
        dput(parent);
@@ -1343,7 +1342,7 @@ static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
        const char *const *name = volatile_path;
 
        for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) {
-               d = ovl_lookup_or_create(d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
+               d = ovl_lookup_or_create(ofs, d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
                if (IS_ERR(d))
                        return PTR_ERR(d);
        }
@@ -1391,7 +1390,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
                pr_warn("upper fs needs to support d_type.\n");
 
        /* Check if upper/work fs supports O_TMPFILE */
-       temp = ovl_do_tmpfile(ofs->workdir, S_IFREG | 0);
+       temp = ovl_do_tmpfile(ofs, ofs->workdir, S_IFREG | 0);
        ofs->tmpfile = !IS_ERR(temp);
        if (ofs->tmpfile)
                dput(temp);
@@ -1400,7 +1399,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
 
 
        /* Check if upper/work fs supports RENAME_WHITEOUT */
-       err = ovl_check_rename_whiteout(ofs->workdir);
+       err = ovl_check_rename_whiteout(ofs);
        if (err < 0)
                goto out;
 
@@ -1411,7 +1410,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
        /*
         * Check if upper/work fs supports (trusted|user).overlay.* xattr
         */
-       err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
+       err = ovl_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
        if (err) {
                ofs->noxattr = true;
                if (ofs->config.index || ofs->config.metacopy) {
@@ -1429,7 +1428,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
                }
                err = 0;
        } else {
-               ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
+               ovl_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
        }
 
        /*
index f48284a2a89600d32ef96ec600cdbc47b04781d4..87f811c089e4ff395ad8722ad7a0222504bf5b6b 100644 (file)
@@ -194,6 +194,20 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
        return type;
 }
 
+enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path)
+{
+       enum ovl_path_type type = ovl_path_type(dentry);
+
+       WARN_ON_ONCE(d_is_dir(dentry));
+
+       if (!OVL_TYPE_UPPER(type) || OVL_TYPE_MERGE(type))
+               ovl_path_lowerdata(dentry, path);
+       else
+               ovl_path_upper(dentry, path);
+
+       return type;
+}
+
 struct dentry *ovl_dentry_upper(struct dentry *dentry)
 {
        return ovl_upperdentry_dereference(OVL_I(d_inode(dentry)));
@@ -236,6 +250,17 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode)
        return ovl_upperdentry_dereference(OVL_I(inode));
 }
 
+void ovl_i_path_real(struct inode *inode, struct path *path)
+{
+       path->dentry = ovl_i_dentry_upper(inode);
+       if (!path->dentry) {
+               path->dentry = OVL_I(inode)->lowerpath.dentry;
+               path->mnt = OVL_I(inode)->lowerpath.layer->mnt;
+       } else {
+               path->mnt = ovl_upper_mnt(OVL_FS(inode->i_sb));
+       }
+}
+
 struct inode *ovl_inode_upper(struct inode *inode)
 {
        struct dentry *upperdentry = ovl_i_dentry_upper(inode);
@@ -245,7 +270,9 @@ struct inode *ovl_inode_upper(struct inode *inode)
 
 struct inode *ovl_inode_lower(struct inode *inode)
 {
-       return OVL_I(inode)->lower;
+       struct dentry *lowerdentry = OVL_I(inode)->lowerpath.dentry;
+
+       return lowerdentry ? d_inode(lowerdentry) : NULL;
 }
 
 struct inode *ovl_inode_real(struct inode *inode)
@@ -443,7 +470,7 @@ static void ovl_dir_version_inc(struct dentry *dentry, bool impurity)
 void ovl_dir_modified(struct dentry *dentry, bool impurity)
 {
        /* Copy mtime/ctime */
-       ovl_copyattr(d_inode(ovl_dentry_upper(dentry)), d_inode(dentry));
+       ovl_copyattr(d_inode(dentry));
 
        ovl_dir_version_inc(dentry, impurity);
 }
@@ -466,6 +493,7 @@ bool ovl_is_whiteout(struct dentry *dentry)
 struct file *ovl_path_open(struct path *path, int flags)
 {
        struct inode *inode = d_inode(path->dentry);
+       struct user_namespace *real_mnt_userns = mnt_user_ns(path->mnt);
        int err, acc_mode;
 
        if (flags & ~(O_ACCMODE | O_LARGEFILE))
@@ -482,12 +510,12 @@ struct file *ovl_path_open(struct path *path, int flags)
                BUG();
        }
 
-       err = inode_permission(&init_user_ns, inode, acc_mode | MAY_OPEN);
+       err = inode_permission(real_mnt_userns, inode, acc_mode | MAY_OPEN);
        if (err)
                return ERR_PTR(err);
 
        /* O_NOATIME is an optimization, don't fail if not permitted */
-       if (inode_owner_or_capable(&init_user_ns, inode))
+       if (inode_owner_or_capable(real_mnt_userns, inode))
                flags |= O_NOATIME;
 
        return dentry_open(path, flags, current_cred());
@@ -550,11 +578,11 @@ void ovl_copy_up_end(struct dentry *dentry)
        ovl_inode_unlock(d_inode(dentry));
 }
 
-bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry)
+bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, struct path *path)
 {
        int res;
 
-       res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_ORIGIN, NULL, 0);
+       res = ovl_path_getxattr(ofs, path, OVL_XATTR_ORIGIN, NULL, 0);
 
        /* Zero size value means "copied up but origin unknown" */
        if (res >= 0)
@@ -563,16 +591,16 @@ bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry)
        return false;
 }
 
-bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
-                        enum ovl_xattr ox)
+bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, struct path *path,
+                              enum ovl_xattr ox)
 {
        int res;
        char val;
 
-       if (!d_is_dir(dentry))
+       if (!d_is_dir(path->dentry))
                return false;
 
-       res = ovl_do_getxattr(OVL_FS(sb), dentry, ox, &val, 1);
+       res = ovl_path_getxattr(ofs, path, ox, &val, 1);
        if (res == 1 && val == 'y')
                return true;
 
@@ -612,7 +640,7 @@ int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
        if (ofs->noxattr)
                return xerr;
 
-       err = ovl_do_setxattr(ofs, upperdentry, ox, value, size);
+       err = ovl_setxattr(ofs, upperdentry, ox, value, size);
 
        if (err == -EOPNOTSUPP) {
                pr_warn("cannot set %s xattr on upper\n", ovl_xattr(ofs, ox));
@@ -652,8 +680,8 @@ void ovl_check_protattr(struct inode *inode, struct dentry *upper)
        char buf[OVL_PROTATTR_MAX+1];
        int res, n;
 
-       res = ovl_do_getxattr(ofs, upper, OVL_XATTR_PROTATTR, buf,
-                             OVL_PROTATTR_MAX);
+       res = ovl_getxattr_upper(ofs, upper, OVL_XATTR_PROTATTR, buf,
+                                OVL_PROTATTR_MAX);
        if (res < 0)
                return;
 
@@ -708,7 +736,7 @@ int ovl_set_protattr(struct inode *inode, struct dentry *upper,
                err = ovl_check_setxattr(ofs, upper, OVL_XATTR_PROTATTR,
                                         buf, len, -EPERM);
        } else if (inode->i_flags & OVL_PROT_I_FLAGS_MASK) {
-               err = ovl_do_removexattr(ofs, upper, OVL_XATTR_PROTATTR);
+               err = ovl_removexattr(ofs, upper, OVL_XATTR_PROTATTR);
                if (err == -EOPNOTSUPP || err == -ENODATA)
                        err = 0;
        }
@@ -824,7 +852,7 @@ static void ovl_cleanup_index(struct dentry *dentry)
        }
 
        inode_lock_nested(dir, I_MUTEX_PARENT);
-       index = lookup_one_len(name.name, indexdir, name.len);
+       index = ovl_lookup_upper(ofs, name.name, indexdir, name.len);
        err = PTR_ERR(index);
        if (IS_ERR(index)) {
                index = NULL;
@@ -834,7 +862,7 @@ static void ovl_cleanup_index(struct dentry *dentry)
                                               dir, index);
        } else {
                /* Cleanup orphan index entries */
-               err = ovl_cleanup(dir, index);
+               err = ovl_cleanup(ofs, dir, index);
        }
 
        inode_unlock(dir);
@@ -943,15 +971,15 @@ err:
 }
 
 /* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */
-int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry)
+int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct path *path)
 {
        int res;
 
        /* Only regular files can have metacopy xattr */
-       if (!S_ISREG(d_inode(dentry)->i_mode))
+       if (!S_ISREG(d_inode(path->dentry)->i_mode))
                return 0;
 
-       res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_METACOPY, NULL, 0);
+       res = ovl_path_getxattr(ofs, path, OVL_XATTR_METACOPY, NULL, 0);
        if (res < 0) {
                if (res == -ENODATA || res == -EOPNOTSUPP)
                        return 0;
@@ -987,13 +1015,12 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)
        return (oe->numlower > 1);
 }
 
-char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
-                            int padding)
+char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct path *path, int padding)
 {
        int res;
        char *s, *next, *buf = NULL;
 
-       res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, NULL, 0);
+       res = ovl_path_getxattr(ofs, path, OVL_XATTR_REDIRECT, NULL, 0);
        if (res == -ENODATA || res == -EOPNOTSUPP)
                return NULL;
        if (res < 0)
@@ -1005,7 +1032,7 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
        if (!buf)
                return ERR_PTR(-ENOMEM);
 
-       res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, buf, res);
+       res = ovl_path_getxattr(ofs, path, OVL_XATTR_REDIRECT, buf, res);
        if (res < 0)
                goto fail;
        if (res == 0)
@@ -1060,3 +1087,33 @@ int ovl_sync_status(struct ovl_fs *ofs)
 
        return errseq_check(&mnt->mnt_sb->s_wb_err, ofs->errseq);
 }
+
+/*
+ * ovl_copyattr() - copy inode attributes from layer to ovl inode
+ *
+ * When overlay copies inode information from an upper or lower layer to the
+ * relevant overlay inode it will apply the idmapping of the upper or lower
+ * layer when doing so ensuring that the ovl inode ownership will correctly
+ * reflect the ownership of the idmapped upper or lower layer. For example, an
+ * idmapped upper or lower layer mapping id 1001 to id 1000 will take care to
+ * map any lower or upper inode owned by id 1001 to id 1000. These mapping
+ * helpers are nops when the relevant layer isn't idmapped.
+ */
+void ovl_copyattr(struct inode *inode)
+{
+       struct path realpath;
+       struct inode *realinode;
+       struct user_namespace *real_mnt_userns;
+
+       ovl_i_path_real(inode, &realpath);
+       realinode = d_inode(realpath.dentry);
+       real_mnt_userns = mnt_user_ns(realpath.mnt);
+
+       inode->i_uid = i_uid_into_mnt(real_mnt_userns, realinode);
+       inode->i_gid = i_gid_into_mnt(real_mnt_userns, realinode);
+       inode->i_mode = realinode->i_mode;
+       inode->i_atime = realinode->i_atime;
+       inode->i_mtime = realinode->i_mtime;
+       inode->i_ctime = realinode->i_ctime;
+       i_size_write(inode, i_size_read(realinode));
+}
index e89329bb3134e8b99add6f91db42b9bd3b33185c..caeb08a98536cc43b53652b4ab70d0adb99252c3 100644 (file)
@@ -69,6 +69,12 @@ extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
 extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
 extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int);
 struct dentry *lookup_one(struct user_namespace *, const char *, struct dentry *, int);
+struct dentry *lookup_one_unlocked(struct user_namespace *mnt_userns,
+                                  const char *name, struct dentry *base,
+                                  int len);
+struct dentry *lookup_one_positive_unlocked(struct user_namespace *mnt_userns,
+                                           const char *name,
+                                           struct dentry *base, int len);
 
 extern int follow_down_one(struct path *);
 extern int follow_down(struct path *);