@@ -378,7 +378,7 @@ int notify_mapped_change(struct user_namespace *user_ns, struct dentry *dentry,
return error;
if (inode->i_op->setattr)
- error = inode->i_op->setattr(dentry, attr);
+ error = iop_setattr(inode, user_ns, dentry, attr);
else
error = simple_setattr(dentry, attr);
@@ -407,7 +407,7 @@ static inline int do_inode_permission(struct user_namespace *user_ns, struct ino
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
if (likely(inode->i_op->permission))
- return inode->i_op->permission(inode, mask);
+ return iop_permission(inode, user_ns, inode, mask);
/* This gets set once for the inode lifetime */
spin_lock(&inode->i_lock);
@@ -2872,7 +2872,7 @@ int vfs_mapped_create(struct user_namespace *user_ns, struct inode *dir,
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
- error = dir->i_op->create(dir, dentry, mode, want_excl);
+ error = iop_create(dir, user_ns, dir, dentry, mode, want_excl);
if (!error)
fsnotify_create(dir, dentry);
return error;
@@ -3175,14 +3175,18 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
/* Negative dentry, just create the file */
if (!dentry->d_inode && (open_flag & O_CREAT)) {
+ struct user_namespace *user_ns;
+
file->f_mode |= FMODE_CREATED;
audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
if (!dir_inode->i_op->create) {
error = -EACCES;
goto out_dput;
}
- error = dir_inode->i_op->create(dir_inode, dentry, mode,
- open_flag & O_EXCL);
+
+ user_ns = mnt_user_ns(nd->path.mnt);
+ error = iop_create(dir_inode, user_ns, dir_inode, dentry, mode,
+ open_flag & O_EXCL);
if (error)
goto out_dput;
}
@@ -3363,7 +3367,7 @@ struct dentry *vfs_mapped_tmpfile(struct user_namespace *user_ns,
child = d_alloc(dentry, &slash_name);
if (unlikely(!child))
goto out_err;
- error = dir->i_op->tmpfile(dir, child, mode);
+ error = iop_tmpfile(dir, user_ns, dir, child, mode);
if (error)
goto out_err;
error = -ENOENT;
@@ -3640,7 +3644,7 @@ int vfs_mapped_mknod(struct user_namespace *user_ns, struct inode *dir,
if (error)
return error;
- error = dir->i_op->mknod(dir, dentry, mode, dev);
+ error = iop_mknod(dir, user_ns, dir, dentry, mode, dev);
if (!error)
fsnotify_create(dir, dentry);
return error;
@@ -3750,7 +3754,7 @@ int vfs_mapped_mkdir(struct user_namespace *user_ns, struct inode *dir,
if (max_links && dir->i_nlink >= max_links)
return -EMLINK;
- error = dir->i_op->mkdir(dir, dentry, mode);
+ error = iop_mkdir(dir, user_ns, dir, dentry, mode);
if (!error)
fsnotify_mkdir(dir, dentry);
return error;
@@ -4089,7 +4093,7 @@ int vfs_mapped_symlink(struct user_namespace *user_ns, struct inode *dir,
if (error)
return error;
- error = dir->i_op->symlink(dir, dentry, oldname);
+ error = iop_symlink(dir, user_ns, dir, dentry, oldname);
if (!error)
fsnotify_create(dir, dentry);
return error;
@@ -4435,8 +4439,8 @@ int vfs_mapped_rename(struct renamedata *rd)
if (error)
goto out;
}
- error = old_dir->i_op->rename(old_dir, old_dentry,
- new_dir, new_dentry, flags);
+ error = iop_rename(old_dir, rd->new_user_ns, old_dir, old_dentry,
+ new_dir, new_dentry, flags);
if (error)
goto out;
@@ -578,7 +578,7 @@ posix_mapped_acl_chmod(struct user_namespace *user_ns, struct inode *inode, umod
ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
if (ret)
return ret;
- ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS);
+ ret = iop_set_acl(inode, user_ns, inode, acl, ACL_TYPE_ACCESS);
posix_acl_release(acl);
return ret;
}
@@ -925,7 +925,7 @@ set_posix_mapped_acl(struct user_namespace *user_ns, struct inode *inode,
if (ret)
return ret;
}
- return inode->i_op->set_acl(inode, acl, type);
+ return iop_set_acl(inode, user_ns, inode, acl, type);
}
int
@@ -1978,8 +1978,137 @@ struct inode_operations {
umode_t create_mode);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
+#ifdef CONFIG_IDMAP_MOUNTS
+ int (*permission_mapped) (struct user_namespace *, struct inode *, int);
+ int (*create_mapped) (struct user_namespace *, struct inode *,
+ struct dentry *, umode_t, bool);
+ int (*mknod_mapped) (struct user_namespace *, struct inode *,
+ struct dentry *, umode_t, dev_t);
+ int (*mkdir_mapped) (struct user_namespace *, struct inode *,
+ struct dentry *, umode_t);
+ int (*tmpfile_mapped) (struct user_namespace *, struct inode *,
+ struct dentry *, umode_t);
+ int (*symlink_mapped) (struct user_namespace *, struct inode *,
+ struct dentry *, const char *);
+ int (*rename_mapped) (struct user_namespace *, struct inode *,
+ struct dentry *, struct inode *, struct dentry *,
+ unsigned int);
+ int (*setattr_mapped) (struct user_namespace *, struct dentry *,
+ struct iattr *);
+ int (*set_acl_mapped)(struct user_namespace *, struct inode *,
+ struct posix_acl *, int);
+#endif
} ____cacheline_aligned;
+static inline int iop_permission(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct inode *inode, int mask)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->permission_mapped)
+ return caller->i_op->permission_mapped(user_ns, inode, mask);
+#endif
+ return caller->i_op->permission(inode, mask);
+}
+
+static inline int iop_create(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct inode *inode, struct dentry *dentry,
+ umode_t mode, bool excl)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->create_mapped)
+ return caller->i_op->create_mapped(user_ns, inode, dentry,
+ mode, excl);
+#endif
+ return caller->i_op->create(inode, dentry, mode, excl);
+}
+
+static inline int iop_mknod(struct inode *caller,
+ struct user_namespace *user_ns, struct inode *inode,
+ struct dentry *dentry, umode_t mode, dev_t dev)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->mknod_mapped)
+ return caller->i_op->mknod_mapped(user_ns, inode, dentry, mode, dev);
+#endif
+ return caller->i_op->mknod(inode, dentry, mode, dev);
+}
+
+static inline int iop_mkdir(struct inode *caller,
+ struct user_namespace *user_ns, struct inode *inode,
+ struct dentry *dentry, umode_t mode)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->mkdir_mapped)
+ return caller->i_op->mkdir_mapped(user_ns, inode, dentry, mode);
+#endif
+ return caller->i_op->mkdir(inode, dentry, mode);
+}
+
+static inline int iop_tmpfile(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct inode *inode, struct dentry *dentry,
+ umode_t mode)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->tmpfile_mapped)
+ return caller->i_op->tmpfile_mapped(user_ns, inode, dentry, mode);
+#endif
+ return caller->i_op->tmpfile(inode, dentry, mode);
+}
+
+static inline int iop_symlink(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct inode *inode, struct dentry *dentry,
+ const char *name)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->symlink_mapped)
+ return caller->i_op->symlink_mapped(user_ns, inode, dentry, name);
+#endif
+ return caller->i_op->symlink(inode, dentry, name);
+}
+
+static inline int iop_rename(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry,
+ unsigned int flags)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->rename_mapped)
+ return caller->i_op->rename_mapped(user_ns, old_inode,
+ old_dentry, new_inode,
+ new_dentry, flags);
+#endif
+ return caller->i_op->rename(old_inode, old_dentry, new_inode,
+ new_dentry, flags);
+}
+
+static inline int iop_setattr(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct dentry *dentry, struct iattr *attr)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->setattr_mapped)
+ return caller->i_op->setattr_mapped(user_ns, dentry, attr);
+#endif
+ return caller->i_op->setattr(dentry, attr);
+}
+
+static inline int iop_set_acl(struct inode *caller,
+ struct user_namespace *user_ns,
+ struct inode *inode, struct posix_acl *acl,
+ int type)
+{
+#ifdef CONFIG_IDMAP_MOUNTS
+ if (caller->i_op->set_acl_mapped)
+ return caller->i_op->set_acl_mapped(user_ns, inode, acl, type);
+#endif
+ return caller->i_op->set_acl(inode, acl, type);
+}
+
static inline ssize_t call_read_iter(struct file *file, struct kiocb *kio,
struct iov_iter *iter)
{
When the kernel is configured with CONFIG_IDMAP_MOUNTS additional inode methods are provided. A filesystem that is aware of idmapped mounts will receive the user namespace the mount has been marked with as an additional argument. This can be used for additional permission checking and also to enable filesystems to translate between uids and gids if they need to. We have implemented all relevant helpers in earlier patches. In this iteration I've decided to add a set of new inode methods instead of adapting the existing ones. This is mainly done to keep the noise-level as low as possible. But we're very happy to adapt the existing methods and all filesystems using it instead of adding dedicated new helpers. In any case we expect to be done to a single set of inode methods ones we've transitioned filesystems whether or not we add new methods or not. Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> --- fs/attr.c | 2 +- fs/namei.c | 24 +++++---- fs/posix_acl.c | 4 +- include/linux/fs.h | 129 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 13 deletions(-)