Wrapfs: port to new user_namespace and mmap APIs
authorErez Zadok <ezk@cs.sunysb.edu>
Fri, 13 May 2022 23:01:59 +0000 (19:01 -0400)
committerErez Zadok <ezk@cs.sunysb.edu>
Fri, 13 May 2022 23:01:59 +0000 (19:01 -0400)
New struct user_namespace arg is now passed to many VFS ops.  See details
here: https://lwn.net/Articles/842423.  Like eCryptfs, we can't currently
stack on top of idmapped file systems, so check for it at mount and
prohibit.  Instead, we use the default init_user_ns global

Also at mount, we prohibit mount on top of another wrapfs mount, due to
known incompatibilities.

mmap ops ->fault and ->page_mkwrite take a different vma struct with some
const args, so extract the right fields as needed.

Signed-off-by: Andrew Burford <aburford@cs.stonybrook.edu>
Signed-off-by: Erez Zadok <ezk@cs.stonybrook.edu>
fs/wrapfs/inode.c
fs/wrapfs/main.c
fs/wrapfs/mmap.c

index ab0aff1abbd90e1f1281c82f3cbb91becb8ef4b4..634b863995e3e8b87bba86a47d34dd5e6e641360 100644 (file)
@@ -8,7 +8,8 @@
 
 #include "wrapfs.h"
 
-static int wrapfs_create(struct inode *dir, struct dentry *dentry,
+static int wrapfs_create(struct user_namespace *mnt_userns,
+                        struct inode *dir, struct dentry *dentry,
                         umode_t mode, bool want_excl)
 {
        int err;
@@ -20,7 +21,8 @@ static int wrapfs_create(struct inode *dir, struct dentry *dentry,
        lower_dentry = lower_path.dentry;
        lower_parent_dentry = lock_parent(lower_dentry);
 
-       err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode,
+       err = vfs_create(&init_user_ns, d_inode(lower_parent_dentry),
+                        lower_dentry, mode,
                         want_excl);
        if (err)
                goto out;
@@ -53,8 +55,8 @@ static int wrapfs_link(struct dentry *old_dentry, struct inode *dir,
        lower_new_dentry = lower_new_path.dentry;
        lower_dir_dentry = lock_parent(lower_new_dentry);
 
-       err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry),
-                      lower_new_dentry, NULL);
+       err = vfs_link(lower_old_dentry, &init_user_ns,
+                      d_inode(lower_dir_dentry), lower_new_dentry, NULL);
        if (err || d_really_is_negative(lower_new_dentry))
                goto out;
 
@@ -91,7 +93,8 @@ static int wrapfs_unlink(struct inode *dir, struct dentry *dentry)
                goto out;
        }
 
-       err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
+       err = vfs_unlink(&init_user_ns, lower_dir_inode, lower_dentry,
+                        NULL);
 
        /*
         * Note: unlinking on top of NFS can cause silly-renamed files.
@@ -117,7 +120,8 @@ out:
        return err;
 }
 
-static int wrapfs_symlink(struct inode *dir, struct dentry *dentry,
+static int wrapfs_symlink(struct user_namespace *mnt_userns,
+                         struct inode *dir, struct dentry *dentry,
                          const char *symname)
 {
        int err;
@@ -129,7 +133,8 @@ static int wrapfs_symlink(struct inode *dir, struct dentry *dentry,
        lower_dentry = lower_path.dentry;
        lower_parent_dentry = lock_parent(lower_dentry);
 
-       err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname);
+       err = vfs_symlink(&init_user_ns, d_inode(lower_parent_dentry),
+                         lower_dentry, symname);
        if (err)
                goto out;
        err = wrapfs_interpose(dentry, dir->i_sb, &lower_path);
@@ -144,7 +149,8 @@ out:
        return err;
 }
 
-static int wrapfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+static int wrapfs_mkdir(struct user_namespace *mnt_userns,
+                       struct inode *dir, struct dentry *dentry, umode_t mode)
 {
        int err;
        struct dentry *lower_dentry;
@@ -155,7 +161,8 @@ static int wrapfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        lower_dentry = lower_path.dentry;
        lower_parent_dentry = lock_parent(lower_dentry);
 
-       err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode);
+       err = vfs_mkdir(&init_user_ns, d_inode(lower_parent_dentry),
+                       lower_dentry, mode);
        if (err)
                goto out;
 
@@ -190,7 +197,8 @@ static int wrapfs_rmdir(struct inode *dir, struct dentry *dentry)
                goto out;
        }
 
-       err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
+       err = vfs_rmdir(&init_user_ns, d_inode(lower_dir_dentry),
+                       lower_dentry);
        if (err)
                goto out;
 
@@ -207,8 +215,8 @@ out:
        return err;
 }
 
-static int wrapfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
-                       dev_t dev)
+static int wrapfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
+                       struct dentry *dentry, umode_t mode, dev_t dev)
 {
        int err;
        struct dentry *lower_dentry;
@@ -219,7 +227,8 @@ static int wrapfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
        lower_dentry = lower_path.dentry;
        lower_parent_dentry = lock_parent(lower_dentry);
 
-       err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev);
+       err = vfs_mknod(&init_user_ns, d_inode(lower_parent_dentry),
+                       lower_dentry, mode, dev);
        if (err)
                goto out;
 
@@ -239,7 +248,8 @@ out:
  * The locking rules in wrapfs_rename are complex.  We could use a simpler
  * superblock-level name-space lock for renames and copy-ups.
  */
-static int wrapfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+static int wrapfs_rename(struct user_namespace *mnt_userns,
+                        struct inode *old_dir, struct dentry *old_dentry,
                         struct inode *new_dir, struct dentry *new_dentry,
                         unsigned int flags)
 {
@@ -250,6 +260,7 @@ static int wrapfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct dentry *lower_new_dir_dentry = NULL;
        struct dentry *trap = NULL;
        struct path lower_old_path, lower_new_path;
+       struct renamedata rd = {};
 
        if (flags)
                return -EINVAL;
@@ -280,9 +291,13 @@ static int wrapfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto out;
        }
 
-       err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
-                        d_inode(lower_new_dir_dentry), lower_new_dentry,
-                        NULL, 0);
+       rd.old_mnt_userns       = &init_user_ns;
+       rd.old_dir              = d_inode(lower_old_dir_dentry);
+       rd.old_dentry           = lower_old_dentry;
+       rd.new_mnt_userns       = &init_user_ns;
+       rd.new_dir              = d_inode(lower_new_dir_dentry);
+       rd.new_dentry           = lower_new_dentry;
+       err = vfs_rename(&rd);
        if (err)
                goto out;
 
@@ -348,17 +363,19 @@ out:
        return buf;
 }
 
-static int wrapfs_permission(struct inode *inode, int mask)
+static int wrapfs_permission(struct user_namespace *mnt_userns,
+                            struct inode *inode, int mask)
 {
        struct inode *lower_inode;
        int err;
 
        lower_inode = wrapfs_lower_inode(inode);
-       err = inode_permission(lower_inode, mask);
+       err = inode_permission(&init_user_ns, lower_inode, mask);
        return err;
 }
 
-static int wrapfs_setattr(struct dentry *dentry, struct iattr *ia)
+static int wrapfs_setattr(struct user_namespace *mnt_userns,
+                         struct dentry *dentry, struct iattr *ia)
 {
        int err;
        struct dentry *lower_dentry;
@@ -374,7 +391,7 @@ static int wrapfs_setattr(struct dentry *dentry, struct iattr *ia)
         * this user can change the lower inode: that should happen when
         * calling notify_change on the lower inode.
         */
-       err = setattr_prepare(dentry, ia);
+       err = setattr_prepare(&init_user_ns, dentry, ia);
        if (err)
                goto out_err;
 
@@ -416,8 +433,7 @@ static int wrapfs_setattr(struct dentry *dentry, struct iattr *ia)
         * tries to open(), unlink(), then ftruncate() a file.
         */
        inode_lock(d_inode(lower_dentry));
-       err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
-                           NULL);
+       err = notify_change(&init_user_ns, lower_dentry, &lower_ia, NULL);
        inode_unlock(d_inode(lower_dentry));
        if (err)
                goto out;
@@ -436,7 +452,8 @@ out_err:
        return err;
 }
 
-static int wrapfs_getattr(const struct path *path, struct kstat *stat, 
+static int wrapfs_getattr(struct user_namespace *mnt_userns,
+                         const struct path *path, struct kstat *stat,
                           u32 request_mask, unsigned int flags)
 {
        int err;
@@ -450,7 +467,7 @@ static int wrapfs_getattr(const struct path *path, struct kstat *stat,
                goto out;
        fsstack_copy_attr_all(d_inode(dentry),
                              d_inode(lower_path.dentry));
-       generic_fillattr(d_inode(dentry), stat);
+       generic_fillattr(&init_user_ns, d_inode(dentry), stat);
        stat->blocks = lower_stat.blocks;
 out:
        wrapfs_put_lower_path(dentry, &lower_path);
@@ -470,7 +487,7 @@ wrapfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
                err = -EOPNOTSUPP;
                goto out;
        }
-       err = vfs_setxattr(lower_dentry, name, value, size, flags);
+       err = vfs_setxattr(&init_user_ns, lower_dentry, name, value, size, flags);
        if (err)
                goto out;
        fsstack_copy_attr_all(d_inode(dentry),
@@ -496,7 +513,7 @@ wrapfs_getxattr(struct dentry *dentry, struct inode *inode,
                err = -EOPNOTSUPP;
                goto out;
        }
-       err = vfs_getxattr(lower_dentry, name, buffer, size);
+       err = vfs_getxattr(&init_user_ns, lower_dentry, name, buffer, size);
        if (err)
                goto out;
        fsstack_copy_attr_atime(d_inode(dentry),
@@ -544,7 +561,7 @@ wrapfs_removexattr(struct dentry *dentry, struct inode *inode, const char *name)
                err = -EOPNOTSUPP;
                goto out;
        }
-       err = vfs_removexattr(lower_dentry, name);
+       err = vfs_removexattr(&init_user_ns, lower_dentry, name);
        if (err)
                goto out;
        fsstack_copy_attr_all(d_inode(dentry), lower_inode);
@@ -592,6 +609,7 @@ static int wrapfs_xattr_get(const struct xattr_handler *handler,
 }
 
 static int wrapfs_xattr_set(const struct xattr_handler *handler,
+                           struct user_namespace *mnt_userns,
                            struct dentry *dentry, struct inode *inode,
                            const char *name, const void *value, size_t size,
                            int flags)
index 3df02cc778b383280090ca4a00a20c01b8e7af77..15c97a78d7655f6e2d4bed9f8504ca85c00258d3 100644 (file)
@@ -9,6 +9,8 @@
 #include "wrapfs.h"
 #include <linux/module.h>
 
+static struct file_system_type wrapfs_fs_type;
+
 /*
  * There is no need to lock the wrapfs_super_info's rwsem as there is no
  * way anyone can have a reference to the superblock at this point in time.
@@ -37,6 +39,21 @@ static int wrapfs_read_super(struct super_block *sb, void *raw_data, int silent)
                goto out;
        }
 
+        if (lower_path.dentry->d_sb->s_type == &wrapfs_fs_type) {
+               err = -EINVAL;
+               printk(KERN_ERR "Mount on filesystem of type "
+                      "wrapfs explicitly disallowed due to "
+                      "known incompatibilities\n");
+               goto out_pput;
+       }
+
+       if (mnt_user_ns(lower_path.mnt) != &init_user_ns) {
+               err = -EINVAL;
+               printk(KERN_ERR "Mounting on idmapped mounts currently "
+                      "disallowed\n");
+               goto out_pput;
+       }
+
        /* allocate superblock private data */
        sb->s_fs_info = kzalloc(sizeof(struct wrapfs_sb_info), GFP_KERNEL);
        if (!WRAPFS_SB(sb)) {
index 9897fa585b977f2238cf8a1422dcf010541eefcc..5357a0b2ff78698fe1017cf6aa2c04ac8dc95375 100644 (file)
@@ -15,6 +15,7 @@ static vm_fault_t wrapfs_fault(struct vm_fault *vmf)
        struct file *file, *lower_file;
        const struct vm_operations_struct *lower_vm_ops;
        struct vm_area_struct lower_vma;
+       struct vm_area_struct **vma_p = (struct vm_area_struct **) &vmf->vma;
 
        memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
        file = lower_vma.vm_file;
@@ -33,9 +34,9 @@ static vm_fault_t wrapfs_fault(struct vm_fault *vmf)
         * take an explicit file pointer.
         */
        lower_vma.vm_file = lower_file;
-       vmf->vma = &lower_vma; /* override vma temporarily */
+       *vma_p = &lower_vma;    /* override vma temporarily */
        err = lower_vm_ops->fault(vmf);
-       vmf->vma = vma; /* restore vma*/
+       *vma_p = vma;           /* restore vma */
        return err;
 }
 
@@ -46,6 +47,7 @@ static vm_fault_t wrapfs_page_mkwrite(struct vm_fault *vmf)
        struct file *file, *lower_file;
        const struct vm_operations_struct *lower_vm_ops;
        struct vm_area_struct lower_vma;
+       struct vm_area_struct **vma_p = (struct vm_area_struct **) &vmf->vma;
 
        memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
        file = lower_vma.vm_file;
@@ -67,9 +69,9 @@ static vm_fault_t wrapfs_page_mkwrite(struct vm_fault *vmf)
         * ->page_mkwrite to take an explicit file pointer.
         */
        lower_vma.vm_file = lower_file;
-       vmf->vma = &lower_vma; /* override vma temporarily */
+       *vma_p = &lower_vma;    /* override vma temporarily */
        err = lower_vm_ops->page_mkwrite(vmf);
-       vmf->vma = vma; /* restore vma */
+       *vma_p = vma;           /* restore vma */
 out:
        return err;
 }