summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pagano <mpagano@gentoo.org>2021-02-23 12:00:47 -0500
committerMike Pagano <mpagano@gentoo.org>2021-02-23 12:00:47 -0500
commite616fa25700e101a7b268272ca8d29c9f3bb22ae (patch)
tree44a251ed279a065152c11f52936d2c72571e5987 /5000_shifts-ubuntu-20.04.patch
parentLinux patch 5.4.100 (diff)
downloadlinux-patches-e616fa25700e101a7b268272ca8d29c9f3bb22ae.tar.gz
linux-patches-e616fa25700e101a7b268272ca8d29c9f3bb22ae.tar.bz2
linux-patches-e616fa25700e101a7b268272ca8d29c9f3bb22ae.zip
Remove shiftfs, broken patch, I will fix soon5.4-103
Signed-off-by: Mike Pagano <mpagano@gentoo.org>
Diffstat (limited to '5000_shifts-ubuntu-20.04.patch')
-rw-r--r--5000_shifts-ubuntu-20.04.patch2202
1 files changed, 0 insertions, 2202 deletions
diff --git a/5000_shifts-ubuntu-20.04.patch b/5000_shifts-ubuntu-20.04.patch
deleted file mode 100644
index f213d939..00000000
--- a/5000_shifts-ubuntu-20.04.patch
+++ /dev/null
@@ -1,2202 +0,0 @@
---- /dev/null 2021-01-06 15:31:07.232620794 -0500
-+++ b/fs/shiftfs.c 2021-01-06 19:04:01.754354287 -0500
-@@ -0,0 +1,2156 @@
-+#include <linux/btrfs.h>
-+#include <linux/capability.h>
-+#include <linux/cred.h>
-+#include <linux/mount.h>
-+#include <linux/fdtable.h>
-+#include <linux/file.h>
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/magic.h>
-+#include <linux/parser.h>
-+#include <linux/security.h>
-+#include <linux/seq_file.h>
-+#include <linux/statfs.h>
-+#include <linux/slab.h>
-+#include <linux/user_namespace.h>
-+#include <linux/uidgid.h>
-+#include <linux/xattr.h>
-+#include <linux/posix_acl.h>
-+#include <linux/posix_acl_xattr.h>
-+#include <linux/uio.h>
-+
-+struct shiftfs_super_info {
-+ struct vfsmount *mnt;
-+ struct user_namespace *userns;
-+ /* creds of process who created the super block */
-+ const struct cred *creator_cred;
-+ bool mark;
-+ unsigned int passthrough;
-+ unsigned int passthrough_mark;
-+};
-+
-+static void shiftfs_fill_inode(struct inode *inode, unsigned long ino,
-+ umode_t mode, dev_t dev, struct dentry *dentry);
-+
-+#define SHIFTFS_PASSTHROUGH_NONE 0
-+#define SHIFTFS_PASSTHROUGH_STAT 1
-+#define SHIFTFS_PASSTHROUGH_IOCTL 2
-+#define SHIFTFS_PASSTHROUGH_ALL \
-+ (SHIFTFS_PASSTHROUGH_STAT | SHIFTFS_PASSTHROUGH_IOCTL)
-+
-+static inline bool shiftfs_passthrough_ioctls(struct shiftfs_super_info *info)
-+{
-+ if (!(info->passthrough & SHIFTFS_PASSTHROUGH_IOCTL))
-+ return false;
-+
-+ return true;
-+}
-+
-+static inline bool shiftfs_passthrough_statfs(struct shiftfs_super_info *info)
-+{
-+ if (!(info->passthrough & SHIFTFS_PASSTHROUGH_STAT))
-+ return false;
-+
-+ return true;
-+}
-+
-+enum {
-+ OPT_MARK,
-+ OPT_PASSTHROUGH,
-+ OPT_LAST,
-+};
-+
-+/* global filesystem options */
-+static const match_table_t tokens = {
-+ { OPT_MARK, "mark" },
-+ { OPT_PASSTHROUGH, "passthrough=%u" },
-+ { OPT_LAST, NULL }
-+};
-+
-+static const struct cred *shiftfs_override_creds(const struct super_block *sb)
-+{
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+
-+ return override_creds(sbinfo->creator_cred);
-+}
-+
-+static inline void shiftfs_revert_object_creds(const struct cred *oldcred,
-+ struct cred *newcred)
-+{
-+ revert_creds(oldcred);
-+ put_cred(newcred);
-+}
-+
-+static kuid_t shift_kuid(struct user_namespace *from, struct user_namespace *to,
-+ kuid_t kuid)
-+{
-+ uid_t uid = from_kuid(from, kuid);
-+ return make_kuid(to, uid);
-+}
-+
-+static kgid_t shift_kgid(struct user_namespace *from, struct user_namespace *to,
-+ kgid_t kgid)
-+{
-+ gid_t gid = from_kgid(from, kgid);
-+ return make_kgid(to, gid);
-+}
-+
-+static int shiftfs_override_object_creds(const struct super_block *sb,
-+ const struct cred **oldcred,
-+ struct cred **newcred,
-+ struct dentry *dentry, umode_t mode,
-+ bool hardlink)
-+{
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+ kuid_t fsuid = current_fsuid();
-+ kgid_t fsgid = current_fsgid();
-+
-+ *oldcred = shiftfs_override_creds(sb);
-+
-+ *newcred = prepare_creds();
-+ if (!*newcred) {
-+ revert_creds(*oldcred);
-+ return -ENOMEM;
-+ }
-+
-+ (*newcred)->fsuid = shift_kuid(sb->s_user_ns, sbinfo->userns, fsuid);
-+ (*newcred)->fsgid = shift_kgid(sb->s_user_ns, sbinfo->userns, fsgid);
-+
-+ if (!hardlink) {
-+ int err = security_dentry_create_files_as(dentry, mode,
-+ &dentry->d_name,
-+ *oldcred, *newcred);
-+ if (err) {
-+ shiftfs_revert_object_creds(*oldcred, *newcred);
-+ return err;
-+ }
-+ }
-+
-+ put_cred(override_creds(*newcred));
-+ return 0;
-+}
-+
-+static void shiftfs_copyattr(struct inode *from, struct inode *to)
-+{
-+ struct user_namespace *from_ns = from->i_sb->s_user_ns;
-+ struct user_namespace *to_ns = to->i_sb->s_user_ns;
-+
-+ to->i_uid = shift_kuid(from_ns, to_ns, from->i_uid);
-+ to->i_gid = shift_kgid(from_ns, to_ns, 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));
-+}
-+
-+static void shiftfs_copyflags(struct inode *from, struct inode *to)
-+{
-+ unsigned int mask = S_SYNC | S_IMMUTABLE | S_APPEND | S_NOATIME;
-+
-+ inode_set_flags(to, from->i_flags & mask, mask);
-+}
-+
-+static void shiftfs_file_accessed(struct file *file)
-+{
-+ struct inode *upperi, *loweri;
-+
-+ if (file->f_flags & O_NOATIME)
-+ return;
-+
-+ upperi = file_inode(file);
-+ loweri = upperi->i_private;
-+
-+ if (!loweri)
-+ return;
-+
-+ upperi->i_mtime = loweri->i_mtime;
-+ upperi->i_ctime = loweri->i_ctime;
-+
-+ touch_atime(&file->f_path);
-+}
-+
-+static int shiftfs_parse_mount_options(struct shiftfs_super_info *sbinfo,
-+ char *options)
-+{
-+ char *p;
-+ substring_t args[MAX_OPT_ARGS];
-+
-+ sbinfo->mark = false;
-+ sbinfo->passthrough = 0;
-+
-+ while ((p = strsep(&options, ",")) != NULL) {
-+ int err, intarg, token;
-+
-+ if (!*p)
-+ continue;
-+
-+ token = match_token(p, tokens, args);
-+ switch (token) {
-+ case OPT_MARK:
-+ sbinfo->mark = true;
-+ break;
-+ case OPT_PASSTHROUGH:
-+ err = match_int(&args[0], &intarg);
-+ if (err)
-+ return err;
-+
-+ if (intarg & ~SHIFTFS_PASSTHROUGH_ALL)
-+ return -EINVAL;
-+
-+ sbinfo->passthrough = intarg;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static void shiftfs_d_release(struct dentry *dentry)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+
-+ if (lowerd)
-+ dput(lowerd);
-+}
-+
-+static struct dentry *shiftfs_d_real(struct dentry *dentry,
-+ const struct inode *inode)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+
-+ if (inode && d_inode(dentry) == inode)
-+ return dentry;
-+
-+ lowerd = d_real(lowerd, inode);
-+ if (lowerd && (!inode || inode == d_inode(lowerd)))
-+ return lowerd;
-+
-+ WARN(1, "shiftfs_d_real(%pd4, %s:%lu): real dentry not found\n", dentry,
-+ inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0);
-+ return dentry;
-+}
-+
-+static int shiftfs_d_weak_revalidate(struct dentry *dentry, unsigned int flags)
-+{
-+ int err = 1;
-+ struct dentry *lowerd = dentry->d_fsdata;
-+
-+ if (d_is_negative(lowerd) != d_is_negative(dentry))
-+ return 0;
-+
-+ if ((lowerd->d_flags & DCACHE_OP_WEAK_REVALIDATE))
-+ err = lowerd->d_op->d_weak_revalidate(lowerd, flags);
-+
-+ if (d_really_is_positive(dentry)) {
-+ struct inode *inode = d_inode(dentry);
-+ struct inode *loweri = d_inode(lowerd);
-+
-+ shiftfs_copyattr(loweri, inode);
-+ }
-+
-+ return err;
-+}
-+
-+static int shiftfs_d_revalidate(struct dentry *dentry, unsigned int flags)
-+{
-+ int err = 1;
-+ struct dentry *lowerd = dentry->d_fsdata;
-+
-+ if (d_unhashed(lowerd) ||
-+ ((d_is_negative(lowerd) != d_is_negative(dentry))))
-+ return 0;
-+
-+ if (flags & LOOKUP_RCU)
-+ return -ECHILD;
-+
-+ if ((lowerd->d_flags & DCACHE_OP_REVALIDATE))
-+ err = lowerd->d_op->d_revalidate(lowerd, flags);
-+
-+ if (d_really_is_positive(dentry)) {
-+ struct inode *inode = d_inode(dentry);
-+ struct inode *loweri = d_inode(lowerd);
-+
-+ shiftfs_copyattr(loweri, inode);
-+ }
-+
-+ return err;
-+}
-+
-+static const struct dentry_operations shiftfs_dentry_ops = {
-+ .d_release = shiftfs_d_release,
-+ .d_real = shiftfs_d_real,
-+ .d_revalidate = shiftfs_d_revalidate,
-+ .d_weak_revalidate = shiftfs_d_weak_revalidate,
-+};
-+
-+static const char *shiftfs_get_link(struct dentry *dentry, struct inode *inode,
-+ struct delayed_call *done)
-+{
-+ const char *p;
-+ const struct cred *oldcred;
-+ struct dentry *lowerd;
-+
-+ /* RCU lookup not supported */
-+ if (!dentry)
-+ return ERR_PTR(-ECHILD);
-+
-+ lowerd = dentry->d_fsdata;
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ p = vfs_get_link(lowerd, done);
-+ revert_creds(oldcred);
-+
-+ return p;
-+}
-+
-+static int shiftfs_setxattr(struct dentry *dentry, struct inode *inode,
-+ const char *name, const void *value,
-+ size_t size, int flags)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ int err;
-+ const struct cred *oldcred;
-+
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ err = vfs_setxattr(lowerd, name, value, size, flags);
-+ revert_creds(oldcred);
-+
-+ shiftfs_copyattr(lowerd->d_inode, inode);
-+
-+ return err;
-+}
-+
-+static int shiftfs_xattr_get(const struct xattr_handler *handler,
-+ struct dentry *dentry, struct inode *inode,
-+ const char *name, void *value, size_t size)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ int err;
-+ const struct cred *oldcred;
-+
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ err = vfs_getxattr(lowerd, name, value, size);
-+ revert_creds(oldcred);
-+
-+ return err;
-+}
-+
-+static ssize_t shiftfs_listxattr(struct dentry *dentry, char *list,
-+ size_t size)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ int err;
-+ const struct cred *oldcred;
-+
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ err = vfs_listxattr(lowerd, list, size);
-+ revert_creds(oldcred);
-+
-+ return err;
-+}
-+
-+static int shiftfs_removexattr(struct dentry *dentry, const char *name)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ int err;
-+ const struct cred *oldcred;
-+
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ err = vfs_removexattr(lowerd, name);
-+ revert_creds(oldcred);
-+
-+ /* update c/mtime */
-+ shiftfs_copyattr(lowerd->d_inode, d_inode(dentry));
-+
-+ return err;
-+}
-+
-+static int shiftfs_xattr_set(const struct xattr_handler *handler,
-+ struct dentry *dentry, struct inode *inode,
-+ const char *name, const void *value, size_t size,
-+ int flags)
-+{
-+ if (!value)
-+ return shiftfs_removexattr(dentry, name);
-+ return shiftfs_setxattr(dentry, inode, name, value, size, flags);
-+}
-+
-+static int shiftfs_inode_test(struct inode *inode, void *data)
-+{
-+ return inode->i_private == data;
-+}
-+
-+static int shiftfs_inode_set(struct inode *inode, void *data)
-+{
-+ inode->i_private = data;
-+ return 0;
-+}
-+
-+static int shiftfs_create_object(struct inode *diri, struct dentry *dentry,
-+ umode_t mode, const char *symlink,
-+ struct dentry *hardlink, bool excl)
-+{
-+ int err;
-+ const struct cred *oldcred;
-+ struct cred *newcred;
-+ void *loweri_iop_ptr = NULL;
-+ umode_t modei = mode;
-+ struct super_block *dir_sb = diri->i_sb;
-+ struct dentry *lowerd_new = dentry->d_fsdata;
-+ struct inode *inode = NULL, *loweri_dir = diri->i_private;
-+ const struct inode_operations *loweri_dir_iop = loweri_dir->i_op;
-+ struct dentry *lowerd_link = NULL;
-+
-+ if (hardlink) {
-+ loweri_iop_ptr = loweri_dir_iop->link;
-+ } else {
-+ switch (mode & S_IFMT) {
-+ case S_IFDIR:
-+ loweri_iop_ptr = loweri_dir_iop->mkdir;
-+ break;
-+ case S_IFREG:
-+ loweri_iop_ptr = loweri_dir_iop->create;
-+ break;
-+ case S_IFLNK:
-+ loweri_iop_ptr = loweri_dir_iop->symlink;
-+ break;
-+ case S_IFSOCK:
-+ /* fall through */
-+ case S_IFIFO:
-+ loweri_iop_ptr = loweri_dir_iop->mknod;
-+ break;
-+ }
-+ }
-+ if (!loweri_iop_ptr) {
-+ err = -EINVAL;
-+ goto out_iput;
-+ }
-+
-+ inode_lock_nested(loweri_dir, I_MUTEX_PARENT);
-+
-+ if (!hardlink) {
-+ inode = new_inode(dir_sb);
-+ if (!inode) {
-+ err = -ENOMEM;
-+ goto out_iput;
-+ }
-+
-+ /*
-+ * new_inode() will have added the new inode to the super
-+ * block's list of inodes. Further below we will call
-+ * inode_insert5() Which would perform the same operation again
-+ * thereby corrupting the list. To avoid this raise I_CREATING
-+ * in i_state which will cause inode_insert5() to skip this
-+ * step. I_CREATING will be cleared by d_instantiate_new()
-+ * below.
-+ */
-+ spin_lock(&inode->i_lock);
-+ inode->i_state |= I_CREATING;
-+ spin_unlock(&inode->i_lock);
-+
-+ inode_init_owner(inode, diri, mode);
-+ modei = inode->i_mode;
-+ }
-+
-+ err = shiftfs_override_object_creds(dentry->d_sb, &oldcred, &newcred,
-+ dentry, modei, hardlink != NULL);
-+ if (err)
-+ goto out_iput;
-+
-+ if (hardlink) {
-+ lowerd_link = hardlink->d_fsdata;
-+ err = vfs_link(lowerd_link, loweri_dir, lowerd_new, NULL);
-+ } else {
-+ switch (modei & S_IFMT) {
-+ case S_IFDIR:
-+ err = vfs_mkdir(loweri_dir, lowerd_new, modei);
-+ break;
-+ case S_IFREG:
-+ err = vfs_create(loweri_dir, lowerd_new, modei, excl);
-+ break;
-+ case S_IFLNK:
-+ err = vfs_symlink(loweri_dir, lowerd_new, symlink);
-+ break;
-+ case S_IFSOCK:
-+ /* fall through */
-+ case S_IFIFO:
-+ err = vfs_mknod(loweri_dir, lowerd_new, modei, 0);
-+ break;
-+ default:
-+ err = -EINVAL;
-+ break;
-+ }
-+ }
-+
-+ shiftfs_revert_object_creds(oldcred, newcred);
-+
-+ if (!err && WARN_ON(!lowerd_new->d_inode))
-+ err = -EIO;
-+ if (err)
-+ goto out_iput;
-+
-+ if (hardlink) {
-+ inode = d_inode(hardlink);
-+ ihold(inode);
-+
-+ /* copy up times from lower inode */
-+ shiftfs_copyattr(d_inode(lowerd_link), inode);
-+ set_nlink(d_inode(hardlink), d_inode(lowerd_link)->i_nlink);
-+ d_instantiate(dentry, inode);
-+ } else {
-+ struct inode *inode_tmp;
-+ struct inode *loweri_new = d_inode(lowerd_new);
-+
-+ inode_tmp = inode_insert5(inode, (unsigned long)loweri_new,
-+ shiftfs_inode_test, shiftfs_inode_set,
-+ loweri_new);
-+ if (unlikely(inode_tmp != inode)) {
-+ pr_err_ratelimited("shiftfs: newly created inode found in cache\n");
-+ iput(inode_tmp);
-+ err = -EINVAL;
-+ goto out_iput;
-+ }
-+
-+ ihold(loweri_new);
-+ shiftfs_fill_inode(inode, loweri_new->i_ino, loweri_new->i_mode,
-+ 0, lowerd_new);
-+ d_instantiate_new(dentry, inode);
-+ }
-+
-+ shiftfs_copyattr(loweri_dir, diri);
-+ if (loweri_iop_ptr == loweri_dir_iop->mkdir)
-+ set_nlink(diri, loweri_dir->i_nlink);
-+
-+ inode = NULL;
-+
-+out_iput:
-+ iput(inode);
-+ inode_unlock(loweri_dir);
-+
-+ return err;
-+}
-+
-+static int shiftfs_create(struct inode *dir, struct dentry *dentry,
-+ umode_t mode, bool excl)
-+{
-+ mode |= S_IFREG;
-+
-+ return shiftfs_create_object(dir, dentry, mode, NULL, NULL, excl);
-+}
-+
-+static int shiftfs_mkdir(struct inode *dir, struct dentry *dentry,
-+ umode_t mode)
-+{
-+ mode |= S_IFDIR;
-+
-+ return shiftfs_create_object(dir, dentry, mode, NULL, NULL, false);
-+}
-+
-+static int shiftfs_link(struct dentry *hardlink, struct inode *dir,
-+ struct dentry *dentry)
-+{
-+ return shiftfs_create_object(dir, dentry, 0, NULL, hardlink, false);
-+}
-+
-+static int shiftfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
-+ dev_t rdev)
-+{
-+ if (!S_ISFIFO(mode) && !S_ISSOCK(mode))
-+ return -EPERM;
-+
-+ return shiftfs_create_object(dir, dentry, mode, NULL, NULL, false);
-+}
-+
-+static int shiftfs_symlink(struct inode *dir, struct dentry *dentry,
-+ const char *symlink)
-+{
-+ return shiftfs_create_object(dir, dentry, S_IFLNK, symlink, NULL, false);
-+}
-+
-+static int shiftfs_rm(struct inode *dir, struct dentry *dentry, bool rmdir)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ struct inode *loweri = dir->i_private;
-+ struct inode *inode = d_inode(dentry);
-+ int err;
-+ const struct cred *oldcred;
-+
-+ dget(lowerd);
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ inode_lock_nested(loweri, I_MUTEX_PARENT);
-+ if (rmdir)
-+ err = vfs_rmdir(loweri, lowerd);
-+ else
-+ err = vfs_unlink(loweri, lowerd, NULL);
-+ revert_creds(oldcred);
-+
-+ if (!err) {
-+ d_drop(dentry);
-+
-+ if (rmdir)
-+ clear_nlink(inode);
-+ else
-+ drop_nlink(inode);
-+ }
-+ inode_unlock(loweri);
-+
-+ shiftfs_copyattr(loweri, dir);
-+ dput(lowerd);
-+
-+ return err;
-+}
-+
-+static int shiftfs_unlink(struct inode *dir, struct dentry *dentry)
-+{
-+ return shiftfs_rm(dir, dentry, false);
-+}
-+
-+static int shiftfs_rmdir(struct inode *dir, struct dentry *dentry)
-+{
-+ return shiftfs_rm(dir, dentry, true);
-+}
-+
-+static int shiftfs_rename(struct inode *olddir, struct dentry *old,
-+ struct inode *newdir, struct dentry *new,
-+ unsigned int flags)
-+{
-+ struct dentry *lowerd_dir_old = old->d_parent->d_fsdata,
-+ *lowerd_dir_new = new->d_parent->d_fsdata,
-+ *lowerd_old = old->d_fsdata, *lowerd_new = new->d_fsdata,
-+ *trapd;
-+ struct inode *loweri_dir_old = lowerd_dir_old->d_inode,
-+ *loweri_dir_new = lowerd_dir_new->d_inode;
-+ int err = -EINVAL;
-+ const struct cred *oldcred;
-+
-+ trapd = lock_rename(lowerd_dir_new, lowerd_dir_old);
-+
-+ if (trapd == lowerd_old || trapd == lowerd_new)
-+ goto out_unlock;
-+
-+ oldcred = shiftfs_override_creds(old->d_sb);
-+ err = vfs_rename(loweri_dir_old, lowerd_old, loweri_dir_new, lowerd_new,
-+ NULL, flags);
-+ revert_creds(oldcred);
-+
-+ shiftfs_copyattr(loweri_dir_old, olddir);
-+ shiftfs_copyattr(loweri_dir_new, newdir);
-+
-+out_unlock:
-+ unlock_rename(lowerd_dir_new, lowerd_dir_old);
-+
-+ return err;
-+}
-+
-+static struct dentry *shiftfs_lookup(struct inode *dir, struct dentry *dentry,
-+ unsigned int flags)
-+{
-+ struct dentry *new;
-+ struct inode *newi;
-+ const struct cred *oldcred;
-+ struct dentry *lowerd = dentry->d_parent->d_fsdata;
-+ struct inode *inode = NULL, *loweri = lowerd->d_inode;
-+
-+ inode_lock(loweri);
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ new = lookup_one_len(dentry->d_name.name, lowerd, dentry->d_name.len);
-+ revert_creds(oldcred);
-+ inode_unlock(loweri);
-+
-+ if (IS_ERR(new))
-+ return new;
-+
-+ dentry->d_fsdata = new;
-+
-+ newi = new->d_inode;
-+ if (!newi)
-+ goto out;
-+
-+ inode = iget5_locked(dentry->d_sb, (unsigned long)newi,
-+ shiftfs_inode_test, shiftfs_inode_set, newi);
-+ if (!inode) {
-+ dput(new);
-+ return ERR_PTR(-ENOMEM);
-+ }
-+ if (inode->i_state & I_NEW) {
-+ /*
-+ * inode->i_private set by shiftfs_inode_set(), but we still
-+ * need to take a reference
-+ */
-+ ihold(newi);
-+ shiftfs_fill_inode(inode, newi->i_ino, newi->i_mode, 0, new);
-+ unlock_new_inode(inode);
-+ }
-+
-+out:
-+ return d_splice_alias(inode, dentry);
-+}
-+
-+static int shiftfs_permission(struct inode *inode, int mask)
-+{
-+ int err;
-+ const struct cred *oldcred;
-+ struct inode *loweri = inode->i_private;
-+
-+ if (!loweri) {
-+ WARN_ON(!(mask & MAY_NOT_BLOCK));
-+ return -ECHILD;
-+ }
-+
-+ err = generic_permission(inode, mask);
-+ if (err)
-+ return err;
-+
-+ oldcred = shiftfs_override_creds(inode->i_sb);
-+ err = inode_permission(loweri, mask);
-+ revert_creds(oldcred);
-+
-+ return err;
-+}
-+
-+static int shiftfs_fiemap(struct inode *inode,
-+ struct fiemap_extent_info *fieinfo, u64 start,
-+ u64 len)
-+{
-+ int err;
-+ const struct cred *oldcred;
-+ struct inode *loweri = inode->i_private;
-+
-+ if (!loweri->i_op->fiemap)
-+ return -EOPNOTSUPP;
-+
-+ oldcred = shiftfs_override_creds(inode->i_sb);
-+ if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)
-+ filemap_write_and_wait(loweri->i_mapping);
-+ err = loweri->i_op->fiemap(loweri, fieinfo, start, len);
-+ revert_creds(oldcred);
-+
-+ return err;
-+}
-+
-+static int shiftfs_tmpfile(struct inode *dir, struct dentry *dentry,
-+ umode_t mode)
-+{
-+ int err;
-+ const struct cred *oldcred;
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ struct inode *loweri = dir->i_private;
-+
-+ if (!loweri->i_op->tmpfile)
-+ return -EOPNOTSUPP;
-+
-+ oldcred = shiftfs_override_creds(dir->i_sb);
-+ err = loweri->i_op->tmpfile(loweri, lowerd, mode);
-+ revert_creds(oldcred);
-+
-+ return err;
-+}
-+
-+static int shiftfs_setattr(struct dentry *dentry, struct iattr *attr)
-+{
-+ struct dentry *lowerd = dentry->d_fsdata;
-+ struct inode *loweri = lowerd->d_inode;
-+ struct iattr newattr;
-+ const struct cred *oldcred;
-+ struct super_block *sb = dentry->d_sb;
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+ int err;
-+
-+ err = setattr_prepare(dentry, attr);
-+ if (err)
-+ return err;
-+
-+ newattr = *attr;
-+ newattr.ia_uid = shift_kuid(sb->s_user_ns, sbinfo->userns, attr->ia_uid);
-+ newattr.ia_gid = shift_kgid(sb->s_user_ns, sbinfo->userns, attr->ia_gid);
-+
-+ /*
-+ * mode change is for clearing setuid/setgid bits. Allow lower fs
-+ * to interpret this in its own way.
-+ */
-+ if (newattr.ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
-+ newattr.ia_valid &= ~ATTR_MODE;
-+
-+ inode_lock(loweri);
-+ oldcred = shiftfs_override_creds(dentry->d_sb);
-+ err = notify_change(lowerd, &newattr, NULL);
-+ revert_creds(oldcred);
-+ inode_unlock(loweri);
-+
-+ shiftfs_copyattr(loweri, d_inode(dentry));
-+
-+ return err;
-+}
-+
-+static int shiftfs_getattr(const struct path *path, struct kstat *stat,
-+ u32 request_mask, unsigned int query_flags)
-+{
-+ struct inode *inode = path->dentry->d_inode;
-+ struct dentry *lowerd = path->dentry->d_fsdata;
-+ struct inode *loweri = lowerd->d_inode;
-+ struct shiftfs_super_info *info = path->dentry->d_sb->s_fs_info;
-+ struct path newpath = { .mnt = info->mnt, .dentry = lowerd };
-+ struct user_namespace *from_ns = loweri->i_sb->s_user_ns;
-+ struct user_namespace *to_ns = inode->i_sb->s_user_ns;
-+ const struct cred *oldcred;
-+ int err;
-+
-+ oldcred = shiftfs_override_creds(inode->i_sb);
-+ err = vfs_getattr(&newpath, stat, request_mask, query_flags);
-+ revert_creds(oldcred);
-+
-+ if (err)
-+ return err;
-+
-+ /* transform the underlying id */
-+ stat->uid = shift_kuid(from_ns, to_ns, stat->uid);
-+ stat->gid = shift_kgid(from_ns, to_ns, stat->gid);
-+ return 0;
-+}
-+
-+#ifdef CONFIG_SHIFT_FS_POSIX_ACL
-+
-+static int
-+shift_acl_ids(struct user_namespace *from, struct user_namespace *to,
-+ struct posix_acl *acl)
-+{
-+ int i;
-+
-+ for (i = 0; i < acl->a_count; i++) {
-+ struct posix_acl_entry *e = &acl->a_entries[i];
-+ switch(e->e_tag) {
-+ case ACL_USER:
-+ e->e_uid = shift_kuid(from, to, e->e_uid);
-+ if (!uid_valid(e->e_uid))
-+ return -EOVERFLOW;
-+ break;
-+ case ACL_GROUP:
-+ e->e_gid = shift_kgid(from, to, e->e_gid);
-+ if (!gid_valid(e->e_gid))
-+ return -EOVERFLOW;
-+ break;
-+ }
-+ }
-+ return 0;
-+}
-+
-+static void
-+shift_acl_xattr_ids(struct user_namespace *from, struct user_namespace *to,
-+ void *value, size_t size)
-+{
-+ struct posix_acl_xattr_header *header = value;
-+ struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end;
-+ int count;
-+ kuid_t kuid;
-+ kgid_t kgid;
-+
-+ if (!value)
-+ return;
-+ if (size < sizeof(struct posix_acl_xattr_header))
-+ return;
-+ if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
-+ return;
-+
-+ count = posix_acl_xattr_count(size);
-+ if (count < 0)
-+ return;
-+ if (count == 0)
-+ return;
-+
-+ for (end = entry + count; entry != end; entry++) {
-+ switch(le16_to_cpu(entry->e_tag)) {
-+ case ACL_USER:
-+ kuid = make_kuid(&init_user_ns, le32_to_cpu(entry->e_id));
-+ kuid = shift_kuid(from, to, kuid);
-+ entry->e_id = cpu_to_le32(from_kuid(&init_user_ns, kuid));
-+ break;
-+ case ACL_GROUP:
-+ kgid = make_kgid(&init_user_ns, le32_to_cpu(entry->e_id));
-+ kgid = shift_kgid(from, to, kgid);
-+ entry->e_id = cpu_to_le32(from_kgid(&init_user_ns, kgid));
-+ break;
-+ default:
-+ break;
-+ }
-+ }
-+}
-+
-+static struct posix_acl *shiftfs_get_acl(struct inode *inode, int type)
-+{
-+ struct inode *loweri = inode->i_private;
-+ const struct cred *oldcred;
-+ struct posix_acl *lower_acl, *acl = NULL;
-+ struct user_namespace *from_ns = loweri->i_sb->s_user_ns;
-+ struct user_namespace *to_ns = inode->i_sb->s_user_ns;
-+ int size;
-+ int err;
-+
-+ if (!IS_POSIXACL(loweri))
-+ return NULL;
-+
-+ oldcred = shiftfs_override_creds(inode->i_sb);
-+ lower_acl = get_acl(loweri, type);
-+ revert_creds(oldcred);
-+
-+ if (lower_acl && !IS_ERR(lower_acl)) {
-+ /* XXX: export posix_acl_clone? */
-+ size = sizeof(struct posix_acl) +
-+ lower_acl->a_count * sizeof(struct posix_acl_entry);
-+ acl = kmemdup(lower_acl, size, GFP_KERNEL);
-+ posix_acl_release(lower_acl);
-+
-+ if (!acl)
-+ return ERR_PTR(-ENOMEM);
-+
-+ refcount_set(&acl->a_refcount, 1);
-+
-+ err = shift_acl_ids(from_ns, to_ns, acl);
-+ if (err) {
-+ kfree(acl);
-+ return ERR_PTR(err);
-+ }
-+ }
-+
-+ return acl;
-+}
-+
-+static int
-+shiftfs_posix_acl_xattr_get(const struct xattr_handler *handler,
-+ struct dentry *dentry, struct inode *inode,
-+ const char *name, void *buffer, size_t size)
-+{
-+ struct inode *loweri = inode->i_private;
-+ int ret;
-+
-+ ret = shiftfs_xattr_get(NULL, dentry, inode, handler->name,
-+ buffer, size);
-+ if (ret < 0)
-+ return ret;
-+
-+ inode_lock(loweri);
-+ shift_acl_xattr_ids(loweri->i_sb->s_user_ns, inode->i_sb->s_user_ns,
-+ buffer, size);
-+ inode_unlock(loweri);
-+ return ret;
-+}
-+
-+static int
-+shiftfs_posix_acl_xattr_set(const struct xattr_handler *handler,
-+ struct dentry *dentry, struct inode *inode,
-+ const char *name, const void *value,
-+ size_t size, int flags)
-+{
-+ struct inode *loweri = inode->i_private;
-+ int err;
-+
-+ if (!IS_POSIXACL(loweri) || !loweri->i_op->set_acl)
-+ return -EOPNOTSUPP;
-+ if (handler->flags == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
-+ return value ? -EACCES : 0;
-+ if (!inode_owner_or_capable(inode))
-+ return -EPERM;
-+
-+ if (value) {
-+ shift_acl_xattr_ids(inode->i_sb->s_user_ns,
-+ loweri->i_sb->s_user_ns,
-+ (void *)value, size);
-+ err = shiftfs_setxattr(dentry, inode, handler->name, value,
-+ size, flags);
-+ } else {
-+ err = shiftfs_removexattr(dentry, handler->name);
-+ }
-+
-+ if (!err)
-+ shiftfs_copyattr(loweri, inode);
-+
-+ return err;
-+}
-+
-+static const struct xattr_handler
-+shiftfs_posix_acl_access_xattr_handler = {
-+ .name = XATTR_NAME_POSIX_ACL_ACCESS,
-+ .flags = ACL_TYPE_ACCESS,
-+ .get = shiftfs_posix_acl_xattr_get,
-+ .set = shiftfs_posix_acl_xattr_set,
-+};
-+
-+static const struct xattr_handler
-+shiftfs_posix_acl_default_xattr_handler = {
-+ .name = XATTR_NAME_POSIX_ACL_DEFAULT,
-+ .flags = ACL_TYPE_DEFAULT,
-+ .get = shiftfs_posix_acl_xattr_get,
-+ .set = shiftfs_posix_acl_xattr_set,
-+};
-+
-+#else /* !CONFIG_SHIFT_FS_POSIX_ACL */
-+
-+#define shiftfs_get_acl NULL
-+
-+#endif /* CONFIG_SHIFT_FS_POSIX_ACL */
-+
-+static const struct inode_operations shiftfs_dir_inode_operations = {
-+ .lookup = shiftfs_lookup,
-+ .mkdir = shiftfs_mkdir,
-+ .symlink = shiftfs_symlink,
-+ .unlink = shiftfs_unlink,
-+ .rmdir = shiftfs_rmdir,
-+ .rename = shiftfs_rename,
-+ .link = shiftfs_link,
-+ .setattr = shiftfs_setattr,
-+ .create = shiftfs_create,
-+ .mknod = shiftfs_mknod,
-+ .permission = shiftfs_permission,
-+ .getattr = shiftfs_getattr,
-+ .listxattr = shiftfs_listxattr,
-+ .get_acl = shiftfs_get_acl,
-+};
-+
-+static const struct inode_operations shiftfs_file_inode_operations = {
-+ .fiemap = shiftfs_fiemap,
-+ .getattr = shiftfs_getattr,
-+ .get_acl = shiftfs_get_acl,
-+ .listxattr = shiftfs_listxattr,
-+ .permission = shiftfs_permission,
-+ .setattr = shiftfs_setattr,
-+ .tmpfile = shiftfs_tmpfile,
-+};
-+
-+static const struct inode_operations shiftfs_special_inode_operations = {
-+ .getattr = shiftfs_getattr,
-+ .get_acl = shiftfs_get_acl,
-+ .listxattr = shiftfs_listxattr,
-+ .permission = shiftfs_permission,
-+ .setattr = shiftfs_setattr,
-+};
-+
-+static const struct inode_operations shiftfs_symlink_inode_operations = {
-+ .getattr = shiftfs_getattr,
-+ .get_link = shiftfs_get_link,
-+ .listxattr = shiftfs_listxattr,
-+ .setattr = shiftfs_setattr,
-+};
-+
-+static struct file *shiftfs_open_realfile(const struct file *file,
-+ struct inode *realinode)
-+{
-+ struct file *realfile;
-+ const struct cred *old_cred;
-+ struct inode *inode = file_inode(file);
-+ struct dentry *lowerd = file->f_path.dentry->d_fsdata;
-+ struct shiftfs_super_info *info = inode->i_sb->s_fs_info;
-+ struct path realpath = { .mnt = info->mnt, .dentry = lowerd };
-+
-+ old_cred = shiftfs_override_creds(inode->i_sb);
-+ realfile = open_with_fake_path(&realpath, file->f_flags, realinode,
-+ info->creator_cred);
-+ revert_creds(old_cred);
-+
-+ return realfile;
-+}
-+
-+#define SHIFTFS_SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT)
-+
-+static int shiftfs_change_flags(struct file *file, unsigned int flags)
-+{
-+ struct inode *inode = file_inode(file);
-+ int err;
-+
-+ /* if some flag changed that cannot be changed then something's amiss */
-+ if (WARN_ON((file->f_flags ^ flags) & ~SHIFTFS_SETFL_MASK))
-+ return -EIO;
-+
-+ flags &= SHIFTFS_SETFL_MASK;
-+
-+ if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode))
-+ return -EPERM;
-+
-+ if (flags & O_DIRECT) {
-+ if (!file->f_mapping->a_ops ||
-+ !file->f_mapping->a_ops->direct_IO)
-+ return -EINVAL;
-+ }
-+
-+ if (file->f_op->check_flags) {
-+ err = file->f_op->check_flags(flags);
-+ if (err)
-+ return err;
-+ }
-+
-+ spin_lock(&file->f_lock);
-+ file->f_flags = (file->f_flags & ~SHIFTFS_SETFL_MASK) | flags;
-+ spin_unlock(&file->f_lock);
-+
-+ return 0;
-+}
-+
-+static int shiftfs_open(struct inode *inode, struct file *file)
-+{
-+ struct file *realfile;
-+
-+ realfile = shiftfs_open_realfile(file, inode->i_private);
-+ if (IS_ERR(realfile))
-+ return PTR_ERR(realfile);
-+
-+ file->private_data = realfile;
-+ /* For O_DIRECT dentry_open() checks f_mapping->a_ops->direct_IO. */
-+ file->f_mapping = realfile->f_mapping;
-+
-+ return 0;
-+}
-+
-+static int shiftfs_dir_open(struct inode *inode, struct file *file)
-+{
-+ struct file *realfile;
-+ const struct cred *oldcred;
-+ struct dentry *lowerd = file->f_path.dentry->d_fsdata;
-+ struct shiftfs_super_info *info = inode->i_sb->s_fs_info;
-+ struct path realpath = { .mnt = info->mnt, .dentry = lowerd };
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ realfile = dentry_open(&realpath, file->f_flags | O_NOATIME,
-+ info->creator_cred);
-+ revert_creds(oldcred);
-+ if (IS_ERR(realfile))
-+ return PTR_ERR(realfile);
-+
-+ file->private_data = realfile;
-+
-+ return 0;
-+}
-+
-+static int shiftfs_release(struct inode *inode, struct file *file)
-+{
-+ struct file *realfile = file->private_data;
-+
-+ if (realfile)
-+ fput(realfile);
-+
-+ return 0;
-+}
-+
-+static int shiftfs_dir_release(struct inode *inode, struct file *file)
-+{
-+ return shiftfs_release(inode, file);
-+}
-+
-+static loff_t shiftfs_dir_llseek(struct file *file, loff_t offset, int whence)
-+{
-+ struct file *realfile = file->private_data;
-+
-+ return vfs_llseek(realfile, offset, whence);
-+}
-+
-+static loff_t shiftfs_file_llseek(struct file *file, loff_t offset, int whence)
-+{
-+ struct inode *realinode = file_inode(file)->i_private;
-+
-+ return generic_file_llseek_size(file, offset, whence,
-+ realinode->i_sb->s_maxbytes,
-+ i_size_read(realinode));
-+}
-+
-+/* XXX: Need to figure out what to to about atime updates, maybe other
-+ * timestamps too ... ref. ovl_file_accessed() */
-+
-+static rwf_t shiftfs_iocb_to_rwf(struct kiocb *iocb)
-+{
-+ int ifl = iocb->ki_flags;
-+ rwf_t flags = 0;
-+
-+ if (ifl & IOCB_NOWAIT)
-+ flags |= RWF_NOWAIT;
-+ if (ifl & IOCB_HIPRI)
-+ flags |= RWF_HIPRI;
-+ if (ifl & IOCB_DSYNC)
-+ flags |= RWF_DSYNC;
-+ if (ifl & IOCB_SYNC)
-+ flags |= RWF_SYNC;
-+
-+ return flags;
-+}
-+
-+static int shiftfs_real_fdget(const struct file *file, struct fd *lowerfd)
-+{
-+ struct file *realfile;
-+
-+ if (file->f_op->open != shiftfs_open &&
-+ file->f_op->open != shiftfs_dir_open)
-+ return -EINVAL;
-+
-+ realfile = file->private_data;
-+ lowerfd->flags = 0;
-+ lowerfd->file = realfile;
-+
-+ /* Did the flags change since open? */
-+ if (unlikely(file->f_flags & ~lowerfd->file->f_flags))
-+ return shiftfs_change_flags(lowerfd->file, file->f_flags);
-+
-+ return 0;
-+}
-+
-+static ssize_t shiftfs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
-+{
-+ struct file *file = iocb->ki_filp;
-+ struct fd lowerfd;
-+ const struct cred *oldcred;
-+ ssize_t ret;
-+
-+ if (!iov_iter_count(iter))
-+ return 0;
-+
-+ ret = shiftfs_real_fdget(file, &lowerfd);
-+ if (ret)
-+ return ret;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ ret = vfs_iter_read(lowerfd.file, iter, &iocb->ki_pos,
-+ shiftfs_iocb_to_rwf(iocb));
-+ revert_creds(oldcred);
-+
-+ shiftfs_file_accessed(file);
-+
-+ fdput(lowerfd);
-+ return ret;
-+}
-+
-+static ssize_t shiftfs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
-+{
-+ struct file *file = iocb->ki_filp;
-+ struct inode *inode = file_inode(file);
-+ struct fd lowerfd;
-+ const struct cred *oldcred;
-+ ssize_t ret;
-+
-+ if (!iov_iter_count(iter))
-+ return 0;
-+
-+ inode_lock(inode);
-+ /* Update mode */
-+ shiftfs_copyattr(inode->i_private, inode);
-+ ret = file_remove_privs(file);
-+ if (ret)
-+ goto out_unlock;
-+
-+ ret = shiftfs_real_fdget(file, &lowerfd);
-+ if (ret)
-+ goto out_unlock;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ file_start_write(lowerfd.file);
-+ ret = vfs_iter_write(lowerfd.file, iter, &iocb->ki_pos,
-+ shiftfs_iocb_to_rwf(iocb));
-+ file_end_write(lowerfd.file);
-+ revert_creds(oldcred);
-+
-+ /* Update size */
-+ shiftfs_copyattr(inode->i_private, inode);
-+
-+ fdput(lowerfd);
-+
-+out_unlock:
-+ inode_unlock(inode);
-+ return ret;
-+}
-+
-+static int shiftfs_fsync(struct file *file, loff_t start, loff_t end,
-+ int datasync)
-+{
-+ struct fd lowerfd;
-+ const struct cred *oldcred;
-+ int ret;
-+
-+ ret = shiftfs_real_fdget(file, &lowerfd);
-+ if (ret)
-+ return ret;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ ret = vfs_fsync_range(lowerfd.file, start, end, datasync);
-+ revert_creds(oldcred);
-+
-+ fdput(lowerfd);
-+ return ret;
-+}
-+
-+static int shiftfs_mmap(struct file *file, struct vm_area_struct *vma)
-+{
-+ struct file *realfile = file->private_data;
-+ const struct cred *oldcred;
-+ int ret;
-+
-+ if (!realfile->f_op->mmap)
-+ return -ENODEV;
-+
-+ if (WARN_ON(file != vma->vm_file))
-+ return -EIO;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ vma->vm_file = get_file(realfile);
-+ ret = call_mmap(vma->vm_file, vma);
-+ revert_creds(oldcred);
-+
-+ shiftfs_file_accessed(file);
-+
-+ if (ret) {
-+ /*
-+ * Drop refcount from new vm_file value and restore original
-+ * vm_file value
-+ */
-+ vma->vm_file = file;
-+ fput(realfile);
-+ } else {
-+ /* Drop refcount from previous vm_file value */
-+ fput(file);
-+ }
-+
-+ return ret;
-+}
-+
-+static long shiftfs_fallocate(struct file *file, int mode, loff_t offset,
-+ loff_t len)
-+{
-+ struct inode *inode = file_inode(file);
-+ struct inode *loweri = inode->i_private;
-+ struct fd lowerfd;
-+ const struct cred *oldcred;
-+ int ret;
-+
-+ ret = shiftfs_real_fdget(file, &lowerfd);
-+ if (ret)
-+ return ret;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ ret = vfs_fallocate(lowerfd.file, mode, offset, len);
-+ revert_creds(oldcred);
-+
-+ /* Update size */
-+ shiftfs_copyattr(loweri, inode);
-+
-+ fdput(lowerfd);
-+ return ret;
-+}
-+
-+static int shiftfs_fadvise(struct file *file, loff_t offset, loff_t len,
-+ int advice)
-+{
-+ struct fd lowerfd;
-+ const struct cred *oldcred;
-+ int ret;
-+
-+ ret = shiftfs_real_fdget(file, &lowerfd);
-+ if (ret)
-+ return ret;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ ret = vfs_fadvise(lowerfd.file, offset, len, advice);
-+ revert_creds(oldcred);
-+
-+ fdput(lowerfd);
-+ return ret;
-+}
-+
-+static int shiftfs_override_ioctl_creds(int cmd, const struct super_block *sb,
-+ const struct cred **oldcred,
-+ struct cred **newcred)
-+{
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+ kuid_t fsuid = current_fsuid();
-+ kgid_t fsgid = current_fsgid();
-+
-+ *oldcred = shiftfs_override_creds(sb);
-+
-+ *newcred = prepare_creds();
-+ if (!*newcred) {
-+ revert_creds(*oldcred);
-+ return -ENOMEM;
-+ }
-+
-+ (*newcred)->fsuid = shift_kuid(sb->s_user_ns, sbinfo->userns, fsuid);
-+ (*newcred)->fsgid = shift_kgid(sb->s_user_ns, sbinfo->userns, fsgid);
-+
-+ /* clear all caps to prevent bypassing capable() checks */
-+ cap_clear((*newcred)->cap_bset);
-+ cap_clear((*newcred)->cap_effective);
-+ cap_clear((*newcred)->cap_inheritable);
-+ cap_clear((*newcred)->cap_permitted);
-+
-+ if (cmd == BTRFS_IOC_SNAP_DESTROY) {
-+ kuid_t kuid_root = make_kuid(sb->s_user_ns, 0);
-+ /*
-+ * Allow the root user in the container to remove subvolumes
-+ * from other users.
-+ */
-+ if (uid_valid(kuid_root) && uid_eq(fsuid, kuid_root))
-+ cap_raise((*newcred)->cap_effective, CAP_DAC_OVERRIDE);
-+ }
-+
-+ put_cred(override_creds(*newcred));
-+ return 0;
-+}
-+
-+static inline void shiftfs_revert_ioctl_creds(const struct cred *oldcred,
-+ struct cred *newcred)
-+{
-+ return shiftfs_revert_object_creds(oldcred, newcred);
-+}
-+
-+static inline bool is_btrfs_snap_ioctl(int cmd)
-+{
-+ if ((cmd == BTRFS_IOC_SNAP_CREATE) || (cmd == BTRFS_IOC_SNAP_CREATE_V2))
-+ return true;
-+
-+ return false;
-+}
-+
-+static int shiftfs_btrfs_ioctl_fd_restore(int cmd, int fd, void __user *arg,
-+ struct btrfs_ioctl_vol_args *v1,
-+ struct btrfs_ioctl_vol_args_v2 *v2)
-+{
-+ int ret;
-+
-+ if (!is_btrfs_snap_ioctl(cmd))
-+ return 0;
-+
-+ if (cmd == BTRFS_IOC_SNAP_CREATE)
-+ ret = copy_to_user(arg, v1, sizeof(*v1));
-+ else
-+ ret = copy_to_user(arg, v2, sizeof(*v2));
-+
-+ __close_fd(current->files, fd);
-+ kfree(v1);
-+ kfree(v2);
-+
-+ return ret;
-+}
-+
-+static int shiftfs_btrfs_ioctl_fd_replace(int cmd, void __user *arg,
-+ struct btrfs_ioctl_vol_args **b1,
-+ struct btrfs_ioctl_vol_args_v2 **b2,
-+ int *newfd)
-+{
-+ int oldfd, ret;
-+ struct fd src;
-+ struct fd lfd = {};
-+ struct btrfs_ioctl_vol_args *v1 = NULL;
-+ struct btrfs_ioctl_vol_args_v2 *v2 = NULL;
-+
-+ if (!is_btrfs_snap_ioctl(cmd))
-+ return 0;
-+
-+ if (cmd == BTRFS_IOC_SNAP_CREATE) {
-+ v1 = memdup_user(arg, sizeof(*v1));
-+ if (IS_ERR(v1))
-+ return PTR_ERR(v1);
-+ oldfd = v1->fd;
-+ *b1 = v1;
-+ } else {
-+ v2 = memdup_user(arg, sizeof(*v2));
-+ if (IS_ERR(v2))
-+ return PTR_ERR(v2);
-+ oldfd = v2->fd;
-+ *b2 = v2;
-+ }
-+
-+ src = fdget(oldfd);
-+ if (!src.file)
-+ return -EINVAL;
-+
-+ ret = shiftfs_real_fdget(src.file, &lfd);
-+ if (ret) {
-+ fdput(src);
-+ return ret;
-+ }
-+
-+ /*
-+ * shiftfs_real_fdget() does not take a reference to lfd.file, so
-+ * take a reference here to offset the one which will be put by
-+ * __close_fd(), and make sure that reference is put on fdput(lfd).
-+ */
-+ get_file(lfd.file);
-+ lfd.flags |= FDPUT_FPUT;
-+ fdput(src);
-+
-+ *newfd = get_unused_fd_flags(lfd.file->f_flags);
-+ if (*newfd < 0) {
-+ fdput(lfd);
-+ return *newfd;
-+ }
-+
-+ fd_install(*newfd, lfd.file);
-+
-+ if (cmd == BTRFS_IOC_SNAP_CREATE) {
-+ v1->fd = *newfd;
-+ ret = copy_to_user(arg, v1, sizeof(*v1));
-+ v1->fd = oldfd;
-+ } else {
-+ v2->fd = *newfd;
-+ ret = copy_to_user(arg, v2, sizeof(*v2));
-+ v2->fd = oldfd;
-+ }
-+
-+ if (ret)
-+ shiftfs_btrfs_ioctl_fd_restore(cmd, *newfd, arg, v1, v2);
-+
-+ return ret;
-+}
-+
-+static long shiftfs_real_ioctl(struct file *file, unsigned int cmd,
-+ unsigned long arg)
-+{
-+ struct fd lowerfd;
-+ struct cred *newcred;
-+ const struct cred *oldcred;
-+ int newfd = -EBADF;
-+ long err = 0, ret = 0;
-+ void __user *argp = (void __user *)arg;
-+ struct super_block *sb = file->f_path.dentry->d_sb;
-+ struct btrfs_ioctl_vol_args *btrfs_v1 = NULL;
-+ struct btrfs_ioctl_vol_args_v2 *btrfs_v2 = NULL;
-+
-+ ret = shiftfs_btrfs_ioctl_fd_replace(cmd, argp, &btrfs_v1, &btrfs_v2,
-+ &newfd);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = shiftfs_real_fdget(file, &lowerfd);
-+ if (ret)
-+ goto out_restore;
-+
-+ ret = shiftfs_override_ioctl_creds(cmd, sb, &oldcred, &newcred);
-+ if (ret)
-+ goto out_fdput;
-+
-+ ret = vfs_ioctl(lowerfd.file, cmd, arg);
-+
-+ shiftfs_revert_ioctl_creds(oldcred, newcred);
-+
-+ shiftfs_copyattr(file_inode(lowerfd.file), file_inode(file));
-+ shiftfs_copyflags(file_inode(lowerfd.file), file_inode(file));
-+
-+out_fdput:
-+ fdput(lowerfd);
-+
-+out_restore:
-+ err = shiftfs_btrfs_ioctl_fd_restore(cmd, newfd, argp,
-+ btrfs_v1, btrfs_v2);
-+ if (!ret)
-+ ret = err;
-+
-+ return ret;
-+}
-+
-+static bool in_ioctl_whitelist(int flag, unsigned long arg)
-+{
-+ void __user *argp = (void __user *)arg;
-+ u64 flags = 0;
-+
-+ switch (flag) {
-+ case BTRFS_IOC_FS_INFO:
-+ return true;
-+ case BTRFS_IOC_SNAP_CREATE:
-+ return true;
-+ case BTRFS_IOC_SNAP_CREATE_V2:
-+ return true;
-+ case BTRFS_IOC_SUBVOL_CREATE:
-+ return true;
-+ case BTRFS_IOC_SUBVOL_CREATE_V2:
-+ return true;
-+ case BTRFS_IOC_SUBVOL_GETFLAGS:
-+ return true;
-+ case BTRFS_IOC_SUBVOL_SETFLAGS:
-+ if (copy_from_user(&flags, argp, sizeof(flags)))
-+ return false;
-+
-+ if (flags & ~BTRFS_SUBVOL_RDONLY)
-+ return false;
-+
-+ return true;
-+ case BTRFS_IOC_SNAP_DESTROY:
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+static long shiftfs_ioctl(struct file *file, unsigned int cmd,
-+ unsigned long arg)
-+{
-+ switch (cmd) {
-+ case FS_IOC_GETVERSION:
-+ /* fall through */
-+ case FS_IOC_GETFLAGS:
-+ /* fall through */
-+ case FS_IOC_SETFLAGS:
-+ break;
-+ default:
-+ if (!in_ioctl_whitelist(cmd, arg) ||
-+ !shiftfs_passthrough_ioctls(file->f_path.dentry->d_sb->s_fs_info))
-+ return -ENOTTY;
-+ }
-+
-+ return shiftfs_real_ioctl(file, cmd, arg);
-+}
-+
-+static long shiftfs_compat_ioctl(struct file *file, unsigned int cmd,
-+ unsigned long arg)
-+{
-+ switch (cmd) {
-+ case FS_IOC32_GETVERSION:
-+ /* fall through */
-+ case FS_IOC32_GETFLAGS:
-+ /* fall through */
-+ case FS_IOC32_SETFLAGS:
-+ break;
-+ default:
-+ if (!in_ioctl_whitelist(cmd, arg) ||
-+ !shiftfs_passthrough_ioctls(file->f_path.dentry->d_sb->s_fs_info))
-+ return -ENOIOCTLCMD;
-+ }
-+
-+ return shiftfs_real_ioctl(file, cmd, arg);
-+}
-+
-+enum shiftfs_copyop {
-+ SHIFTFS_COPY,
-+ SHIFTFS_CLONE,
-+ SHIFTFS_DEDUPE,
-+};
-+
-+static ssize_t shiftfs_copyfile(struct file *file_in, loff_t pos_in,
-+ struct file *file_out, loff_t pos_out, u64 len,
-+ unsigned int flags, enum shiftfs_copyop op)
-+{
-+ ssize_t ret;
-+ struct fd real_in, real_out;
-+ const struct cred *oldcred;
-+ struct inode *inode_out = file_inode(file_out);
-+ struct inode *loweri = inode_out->i_private;
-+
-+ ret = shiftfs_real_fdget(file_out, &real_out);
-+ if (ret)
-+ return ret;
-+
-+ ret = shiftfs_real_fdget(file_in, &real_in);
-+ if (ret) {
-+ fdput(real_out);
-+ return ret;
-+ }
-+
-+ oldcred = shiftfs_override_creds(inode_out->i_sb);
-+ switch (op) {
-+ case SHIFTFS_COPY:
-+ ret = vfs_copy_file_range(real_in.file, pos_in, real_out.file,
-+ pos_out, len, flags);
-+ break;
-+
-+ case SHIFTFS_CLONE:
-+ ret = vfs_clone_file_range(real_in.file, pos_in, real_out.file,
-+ pos_out, len, flags);
-+ break;
-+
-+ case SHIFTFS_DEDUPE:
-+ ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
-+ real_out.file, pos_out, len,
-+ flags);
-+ break;
-+ }
-+ revert_creds(oldcred);
-+
-+ /* Update size */
-+ shiftfs_copyattr(loweri, inode_out);
-+
-+ fdput(real_in);
-+ fdput(real_out);
-+
-+ return ret;
-+}
-+
-+static ssize_t shiftfs_copy_file_range(struct file *file_in, loff_t pos_in,
-+ struct file *file_out, loff_t pos_out,
-+ size_t len, unsigned int flags)
-+{
-+ return shiftfs_copyfile(file_in, pos_in, file_out, pos_out, len, flags,
-+ SHIFTFS_COPY);
-+}
-+
-+static loff_t shiftfs_remap_file_range(struct file *file_in, loff_t pos_in,
-+ struct file *file_out, loff_t pos_out,
-+ loff_t len, unsigned int remap_flags)
-+{
-+ enum shiftfs_copyop op;
-+
-+ if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
-+ return -EINVAL;
-+
-+ if (remap_flags & REMAP_FILE_DEDUP)
-+ op = SHIFTFS_DEDUPE;
-+ else
-+ op = SHIFTFS_CLONE;
-+
-+ return shiftfs_copyfile(file_in, pos_in, file_out, pos_out, len,
-+ remap_flags, op);
-+}
-+
-+static int shiftfs_iterate_shared(struct file *file, struct dir_context *ctx)
-+{
-+ const struct cred *oldcred;
-+ int err = -ENOTDIR;
-+ struct file *realfile = file->private_data;
-+
-+ oldcred = shiftfs_override_creds(file->f_path.dentry->d_sb);
-+ err = iterate_dir(realfile, ctx);
-+ revert_creds(oldcred);
-+
-+ return err;
-+}
-+
-+const struct file_operations shiftfs_file_operations = {
-+ .open = shiftfs_open,
-+ .release = shiftfs_release,
-+ .llseek = shiftfs_file_llseek,
-+ .read_iter = shiftfs_read_iter,
-+ .write_iter = shiftfs_write_iter,
-+ .fsync = shiftfs_fsync,
-+ .mmap = shiftfs_mmap,
-+ .fallocate = shiftfs_fallocate,
-+ .fadvise = shiftfs_fadvise,
-+ .unlocked_ioctl = shiftfs_ioctl,
-+ .compat_ioctl = shiftfs_compat_ioctl,
-+ .copy_file_range = shiftfs_copy_file_range,
-+ .remap_file_range = shiftfs_remap_file_range,
-+};
-+
-+const struct file_operations shiftfs_dir_operations = {
-+ .open = shiftfs_dir_open,
-+ .release = shiftfs_dir_release,
-+ .compat_ioctl = shiftfs_compat_ioctl,
-+ .fsync = shiftfs_fsync,
-+ .iterate_shared = shiftfs_iterate_shared,
-+ .llseek = shiftfs_dir_llseek,
-+ .read = generic_read_dir,
-+ .unlocked_ioctl = shiftfs_ioctl,
-+};
-+
-+static const struct address_space_operations shiftfs_aops = {
-+ /* For O_DIRECT dentry_open() checks f_mapping->a_ops->direct_IO */
-+ .direct_IO = noop_direct_IO,
-+};
-+
-+static void shiftfs_fill_inode(struct inode *inode, unsigned long ino,
-+ umode_t mode, dev_t dev, struct dentry *dentry)
-+{
-+ struct inode *loweri;
-+
-+ inode->i_ino = ino;
-+ inode->i_flags |= S_NOCMTIME;
-+
-+ mode &= S_IFMT;
-+ inode->i_mode = mode;
-+ switch (mode & S_IFMT) {
-+ case S_IFDIR:
-+ inode->i_op = &shiftfs_dir_inode_operations;
-+ inode->i_fop = &shiftfs_dir_operations;
-+ break;
-+ case S_IFLNK:
-+ inode->i_op = &shiftfs_symlink_inode_operations;
-+ break;
-+ case S_IFREG:
-+ inode->i_op = &shiftfs_file_inode_operations;
-+ inode->i_fop = &shiftfs_file_operations;
-+ inode->i_mapping->a_ops = &shiftfs_aops;
-+ break;
-+ default:
-+ inode->i_op = &shiftfs_special_inode_operations;
-+ init_special_inode(inode, mode, dev);
-+ break;
-+ }
-+
-+ if (!dentry)
-+ return;
-+
-+ loweri = dentry->d_inode;
-+ if (!loweri->i_op->get_link)
-+ inode->i_opflags |= IOP_NOFOLLOW;
-+
-+ shiftfs_copyattr(loweri, inode);
-+ shiftfs_copyflags(loweri, inode);
-+ set_nlink(inode, loweri->i_nlink);
-+}
-+
-+static int shiftfs_show_options(struct seq_file *m, struct dentry *dentry)
-+{
-+ struct super_block *sb = dentry->d_sb;
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+
-+ if (sbinfo->mark)
-+ seq_show_option(m, "mark", NULL);
-+
-+ if (sbinfo->passthrough)
-+ seq_printf(m, ",passthrough=%u", sbinfo->passthrough);
-+
-+ return 0;
-+}
-+
-+static int shiftfs_statfs(struct dentry *dentry, struct kstatfs *buf)
-+{
-+ struct super_block *sb = dentry->d_sb;
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+ struct dentry *root = sb->s_root;
-+ struct dentry *realroot = root->d_fsdata;
-+ struct path realpath = { .mnt = sbinfo->mnt, .dentry = realroot };
-+ int err;
-+
-+ err = vfs_statfs(&realpath, buf);
-+ if (err)
-+ return err;
-+
-+ if (!shiftfs_passthrough_statfs(sbinfo))
-+ buf->f_type = sb->s_magic;
-+
-+ return 0;
-+}
-+
-+static void shiftfs_evict_inode(struct inode *inode)
-+{
-+ struct inode *loweri = inode->i_private;
-+
-+ clear_inode(inode);
-+
-+ if (loweri)
-+ iput(loweri);
-+}
-+
-+static void shiftfs_put_super(struct super_block *sb)
-+{
-+ struct shiftfs_super_info *sbinfo = sb->s_fs_info;
-+
-+ if (sbinfo) {
-+ mntput(sbinfo->mnt);
-+ put_cred(sbinfo->creator_cred);
-+ kfree(sbinfo);
-+ }
-+}
-+
-+static const struct xattr_handler shiftfs_xattr_handler = {
-+ .prefix = "",
-+ .get = shiftfs_xattr_get,
-+ .set = shiftfs_xattr_set,
-+};
-+
-+const struct xattr_handler *shiftfs_xattr_handlers[] = {
-+#ifdef CONFIG_SHIFT_FS_POSIX_ACL
-+ &shiftfs_posix_acl_access_xattr_handler,
-+ &shiftfs_posix_acl_default_xattr_handler,
-+#endif
-+ &shiftfs_xattr_handler,
-+ NULL
-+};
-+
-+static inline bool passthrough_is_subset(int old_flags, int new_flags)
-+{
-+ if ((new_flags & old_flags) != new_flags)
-+ return false;
-+
-+ return true;
-+}
-+
-+static int shiftfs_super_check_flags(unsigned long old_flags,
-+ unsigned long new_flags)
-+{
-+ if ((old_flags & SB_RDONLY) && !(new_flags & SB_RDONLY))
-+ return -EPERM;
-+
-+ if ((old_flags & SB_NOSUID) && !(new_flags & SB_NOSUID))
-+ return -EPERM;
-+
-+ if ((old_flags & SB_NODEV) && !(new_flags & SB_NODEV))
-+ return -EPERM;
-+
-+ if ((old_flags & SB_NOEXEC) && !(new_flags & SB_NOEXEC))
-+ return -EPERM;
-+
-+ if ((old_flags & SB_NOATIME) && !(new_flags & SB_NOATIME))
-+ return -EPERM;
-+
-+ if ((old_flags & SB_NODIRATIME) && !(new_flags & SB_NODIRATIME))
-+ return -EPERM;
-+
-+ if (!(old_flags & SB_POSIXACL) && (new_flags & SB_POSIXACL))
-+ return -EPERM;
-+
-+ return 0;
-+}
-+
-+static int shiftfs_remount(struct super_block *sb, int *flags, char *data)
-+{
-+ int err;
-+ struct shiftfs_super_info new = {};
-+ struct shiftfs_super_info *info = sb->s_fs_info;
-+
-+ err = shiftfs_parse_mount_options(&new, data);
-+ if (err)
-+ return err;
-+
-+ err = shiftfs_super_check_flags(sb->s_flags, *flags);
-+ if (err)
-+ return err;
-+
-+ /* Mark mount option cannot be changed. */
-+ if (info->mark || (info->mark != new.mark))
-+ return -EPERM;
-+
-+ if (info->passthrough != new.passthrough) {
-+ /* Don't allow exceeding passthrough options of mark mount. */
-+ if (!passthrough_is_subset(info->passthrough_mark,
-+ info->passthrough))
-+ return -EPERM;
-+
-+ info->passthrough = new.passthrough;
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct super_operations shiftfs_super_ops = {
-+ .put_super = shiftfs_put_super,
-+ .show_options = shiftfs_show_options,
-+ .statfs = shiftfs_statfs,
-+ .remount_fs = shiftfs_remount,
-+ .evict_inode = shiftfs_evict_inode,
-+};
-+
-+struct shiftfs_data {
-+ void *data;
-+ const char *path;
-+};
-+
-+static void shiftfs_super_force_flags(struct super_block *sb,
-+ unsigned long lower_flags)
-+{
-+ sb->s_flags |= lower_flags & (SB_RDONLY | SB_NOSUID | SB_NODEV |
-+ SB_NOEXEC | SB_NOATIME | SB_NODIRATIME);
-+
-+ if (!(lower_flags & SB_POSIXACL))
-+ sb->s_flags &= ~SB_POSIXACL;
-+}
-+
-+static int shiftfs_fill_super(struct super_block *sb, void *raw_data,
-+ int silent)
-+{
-+ int err;
-+ struct path path = {};
-+ struct shiftfs_super_info *sbinfo_mp;
-+ char *name = NULL;
-+ struct inode *inode = NULL;
-+ struct dentry *dentry = NULL;
-+ struct shiftfs_data *data = raw_data;
-+ struct shiftfs_super_info *sbinfo = NULL;
-+
-+ if (!data->path)
-+ return -EINVAL;
-+
-+ sb->s_fs_info = kzalloc(sizeof(*sbinfo), GFP_KERNEL);
-+ if (!sb->s_fs_info)
-+ return -ENOMEM;
-+ sbinfo = sb->s_fs_info;
-+
-+ err = shiftfs_parse_mount_options(sbinfo, data->data);
-+ if (err)
-+ return err;
-+
-+ /* to mount a mark, must be userns admin */
-+ if (!sbinfo->mark && !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
-+ return -EPERM;
-+
-+ name = kstrdup(data->path, GFP_KERNEL);
-+ if (!name)
-+ return -ENOMEM;
-+
-+ err = kern_path(name, LOOKUP_FOLLOW, &path);
-+ if (err)
-+ goto out_free_name;
-+
-+ if (!S_ISDIR(path.dentry->d_inode->i_mode)) {
-+ err = -ENOTDIR;
-+ goto out_put_path;
-+ }
-+
-+ sb->s_flags |= SB_POSIXACL;
-+
-+ if (sbinfo->mark) {
-+ struct cred *cred_tmp;
-+ struct super_block *lower_sb = path.mnt->mnt_sb;
-+
-+ /* to mark a mount point, must root wrt lower s_user_ns */
-+ if (!ns_capable(lower_sb->s_user_ns, CAP_SYS_ADMIN)) {
-+ err = -EPERM;
-+ goto out_put_path;
-+ }
-+
-+ /*
-+ * this part is visible unshifted, so make sure no
-+ * executables that could be used to give suid
-+ * privileges
-+ */
-+ sb->s_iflags = SB_I_NOEXEC;
-+
-+ shiftfs_super_force_flags(sb, lower_sb->s_flags);
-+
-+ /*
-+ * Handle nesting of shiftfs mounts by referring this mark
-+ * mount back to the original mark mount. This is more
-+ * efficient and alleviates concerns about stack depth.
-+ */
-+ if (lower_sb->s_magic == SHIFTFS_MAGIC) {
-+ sbinfo_mp = lower_sb->s_fs_info;
-+
-+ /* Doesn't make sense to mark a mark mount */
-+ if (sbinfo_mp->mark) {
-+ err = -EINVAL;
-+ goto out_put_path;
-+ }
-+
-+ if (!passthrough_is_subset(sbinfo_mp->passthrough,
-+ sbinfo->passthrough)) {
-+ err = -EPERM;
-+ goto out_put_path;
-+ }
-+
-+ sbinfo->mnt = mntget(sbinfo_mp->mnt);
-+ dentry = dget(path.dentry->d_fsdata);
-+ /*
-+ * Copy up the passthrough mount options from the
-+ * parent mark mountpoint.
-+ */
-+ sbinfo->passthrough_mark = sbinfo_mp->passthrough_mark;
-+ sbinfo->creator_cred = get_cred(sbinfo_mp->creator_cred);
-+ } else {
-+ sbinfo->mnt = mntget(path.mnt);
-+ dentry = dget(path.dentry);
-+ /*
-+ * For a new mark passthrough_mark and passthrough
-+ * are identical.
-+ */
-+ sbinfo->passthrough_mark = sbinfo->passthrough;
-+
-+ cred_tmp = prepare_creds();
-+ if (!cred_tmp) {
-+ err = -ENOMEM;
-+ goto out_put_path;
-+ }
-+ /* Don't override disk quota limits or use reserved space. */
-+ cap_lower(cred_tmp->cap_effective, CAP_SYS_RESOURCE);
-+ sbinfo->creator_cred = cred_tmp;
-+ }
-+ } else {
-+ /*
-+ * This leg executes if we're admin capable in the namespace,
-+ * so be very careful.
-+ */
-+ err = -EPERM;
-+ if (path.dentry->d_sb->s_magic != SHIFTFS_MAGIC)
-+ goto out_put_path;
-+
-+ sbinfo_mp = path.dentry->d_sb->s_fs_info;
-+ if (!sbinfo_mp->mark)
-+ goto out_put_path;
-+
-+ if (!passthrough_is_subset(sbinfo_mp->passthrough,
-+ sbinfo->passthrough))
-+ goto out_put_path;
-+
-+ sbinfo->mnt = mntget(sbinfo_mp->mnt);
-+ sbinfo->creator_cred = get_cred(sbinfo_mp->creator_cred);
-+ dentry = dget(path.dentry->d_fsdata);
-+ /*
-+ * Copy up passthrough settings from mark mountpoint so we can
-+ * verify when the overlay wants to remount with different
-+ * passthrough settings.
-+ */
-+ sbinfo->passthrough_mark = sbinfo_mp->passthrough;
-+ shiftfs_super_force_flags(sb, path.mnt->mnt_sb->s_flags);
-+ }
-+
-+ sb->s_stack_depth = dentry->d_sb->s_stack_depth + 1;
-+ if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
-+ printk(KERN_ERR "shiftfs: maximum stacking depth exceeded\n");
-+ err = -EINVAL;
-+ goto out_put_path;
-+ }
-+
-+ inode = new_inode(sb);
-+ if (!inode) {
-+ err = -ENOMEM;
-+ goto out_put_path;
-+ }
-+ shiftfs_fill_inode(inode, dentry->d_inode->i_ino, S_IFDIR, 0, dentry);
-+
-+ ihold(dentry->d_inode);
-+ inode->i_private = dentry->d_inode;
-+
-+ sb->s_magic = SHIFTFS_MAGIC;
-+ sb->s_maxbytes = MAX_LFS_FILESIZE;
-+ sb->s_op = &shiftfs_super_ops;
-+ sb->s_xattr = shiftfs_xattr_handlers;
-+ sb->s_d_op = &shiftfs_dentry_ops;
-+ sb->s_root = d_make_root(inode);
-+ if (!sb->s_root) {
-+ err = -ENOMEM;
-+ goto out_put_path;
-+ }
-+
-+ sb->s_root->d_fsdata = dentry;
-+ sbinfo->userns = get_user_ns(dentry->d_sb->s_user_ns);
-+ shiftfs_copyattr(dentry->d_inode, sb->s_root->d_inode);
-+
-+ dentry = NULL;
-+ err = 0;
-+
-+out_put_path:
-+ path_put(&path);
-+
-+out_free_name:
-+ kfree(name);
-+
-+ dput(dentry);
-+
-+ return err;
-+}
-+
-+static struct dentry *shiftfs_mount(struct file_system_type *fs_type,
-+ int flags, const char *dev_name, void *data)
-+{
-+ struct shiftfs_data d = { data, dev_name };
-+
-+ return mount_nodev(fs_type, flags, &d, shiftfs_fill_super);
-+}
-+
-+static struct file_system_type shiftfs_type = {
-+ .owner = THIS_MODULE,
-+ .name = "shiftfs",
-+ .mount = shiftfs_mount,
-+ .kill_sb = kill_anon_super,
-+ .fs_flags = FS_USERNS_MOUNT,
-+};
-+
-+static int __init shiftfs_init(void)
-+{
-+ return register_filesystem(&shiftfs_type);
-+}
-+
-+static void __exit shiftfs_exit(void)
-+{
-+ unregister_filesystem(&shiftfs_type);
-+}
-+
-+MODULE_ALIAS_FS("shiftfs");
-+MODULE_AUTHOR("James Bottomley");
-+MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
-+MODULE_AUTHOR("Christian Brauner <christian.brauner@ubuntu.com>");
-+MODULE_DESCRIPTION("id shifting filesystem");
-+MODULE_LICENSE("GPL v2");
-+module_init(shiftfs_init)
-+module_exit(shiftfs_exit)
---- a/include/uapi/linux/magic.h 2021-01-06 19:08:45.234777659 -0500
-+++ b/include/uapi/linux/magic.h 2021-01-06 19:09:53.900375394 -0500
-@@ -96,4 +96,6 @@
- #define DEVMEM_MAGIC 0x454d444d /* "DMEM" */
- #define Z3FOLD_MAGIC 0x33
-
-+#define SHIFTFS_MAGIC 0x6a656a62
-+
- #endif /* __LINUX_MAGIC_H__ */
---- a/fs/Makefile 2021-01-06 19:10:56.009918778 -0500
-+++ b/fs/Makefile 2021-01-06 19:11:55.632442564 -0500
-@@ -132,3 +132,4 @@ obj-$(CONFIG_CEPH_FS) += ceph/
- obj-$(CONFIG_PSTORE) += pstore/
- obj-$(CONFIG_EFIVAR_FS) += efivarfs/
- obj-$(CONFIG_EROFS_FS) += erofs/
-+obj-$(CONFIG_SHIFT_FS) += shiftfs.o
---- a/fs/Kconfig 2021-01-06 19:14:17.709697891 -0500
-+++ b/fs/Kconfig 2021-01-06 19:15:23.413281282 -0500
-@@ -122,6 +122,24 @@ source "fs/autofs/Kconfig"
- source "fs/fuse/Kconfig"
- source "fs/overlayfs/Kconfig"
-
-+config SHIFT_FS
-+ tristate "UID/GID shifting overlay filesystem for containers"
-+ help
-+ This filesystem can overlay any mounted filesystem and shift
-+ the uid/gid the files appear at. The idea is that
-+ unprivileged containers can use this to mount root volumes
-+ using this technique.
-+
-+config SHIFT_FS_POSIX_ACL
-+ bool "shiftfs POSIX Access Control Lists"
-+ depends on SHIFT_FS
-+ select FS_POSIX_ACL
-+ help
-+ POSIX Access Control Lists (ACLs) support permissions for users and
-+ groups beyond the owner/group/world scheme.
-+
-+ If you don't know what Access Control Lists are, say N.
-+
- menu "Caches"
-
- source "fs/fscache/Kconfig"