Commit 6521f891 authored by Christian Brauner's avatar Christian Brauner

namei: prepare for idmapped mounts

The various vfs_*() helpers are called by filesystems or by the vfs
itself to perform core operations such as create, link, mkdir, mknod, rename,
rmdir, tmpfile and unlink. Enable them to handle idmapped mounts. If the
inode is accessed through an idmapped mount map it into the
mount's user namespace and pass it down. Afterwards the checks and
operations are identical to non-idmapped mounts. If the initial user
namespace is passed nothing changes so non-idmapped mounts will see
identical behavior as before.

Link: https://lore.kernel.org/r/20210121131959.646623-15-christian.brauner@ubuntu.com
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Howells <dhowells@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 9fe61450
...@@ -162,7 +162,7 @@ static int dev_mkdir(const char *name, umode_t mode) ...@@ -162,7 +162,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
err = vfs_mkdir(d_inode(path.dentry), dentry, mode); err = vfs_mkdir(&init_user_ns, d_inode(path.dentry), dentry, mode);
if (!err) if (!err)
/* mark as kernel-created inode */ /* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread; d_inode(dentry)->i_private = &thread;
...@@ -212,7 +212,8 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid, ...@@ -212,7 +212,8 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
err = vfs_mknod(d_inode(path.dentry), dentry, mode, dev->devt); err = vfs_mknod(&init_user_ns, d_inode(path.dentry), dentry, mode,
dev->devt);
if (!err) { if (!err) {
struct iattr newattrs; struct iattr newattrs;
...@@ -242,7 +243,8 @@ static int dev_rmdir(const char *name) ...@@ -242,7 +243,8 @@ static int dev_rmdir(const char *name)
return PTR_ERR(dentry); return PTR_ERR(dentry);
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
if (d_inode(dentry)->i_private == &thread) if (d_inode(dentry)->i_private == &thread)
err = vfs_rmdir(d_inode(parent.dentry), dentry); err = vfs_rmdir(&init_user_ns, d_inode(parent.dentry),
dentry);
else else
err = -EPERM; err = -EPERM;
} else { } else {
...@@ -330,7 +332,8 @@ static int handle_remove(const char *nodename, struct device *dev) ...@@ -330,7 +332,8 @@ static int handle_remove(const char *nodename, struct device *dev)
inode_lock(d_inode(dentry)); inode_lock(d_inode(dentry));
notify_change(&init_user_ns, dentry, &newattrs, NULL); notify_change(&init_user_ns, dentry, &newattrs, NULL);
inode_unlock(d_inode(dentry)); inode_unlock(d_inode(dentry));
err = vfs_unlink(d_inode(parent.dentry), dentry, NULL); err = vfs_unlink(&init_user_ns, d_inode(parent.dentry),
dentry, NULL);
if (!err || err == -ENOENT) if (!err || err == -ENOENT)
deleted = 1; deleted = 1;
} }
......
...@@ -311,7 +311,8 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache, ...@@ -311,7 +311,8 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
cachefiles_io_error(cache, "Unlink security error"); cachefiles_io_error(cache, "Unlink security error");
} else { } else {
trace_cachefiles_unlink(object, rep, why); trace_cachefiles_unlink(object, rep, why);
ret = vfs_unlink(d_inode(dir), rep, NULL); ret = vfs_unlink(&init_user_ns, d_inode(dir), rep,
NULL);
if (preemptive) if (preemptive)
cachefiles_mark_object_buried(cache, rep, why); cachefiles_mark_object_buried(cache, rep, why);
...@@ -413,8 +414,10 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache, ...@@ -413,8 +414,10 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
cachefiles_io_error(cache, "Rename security error %d", ret); cachefiles_io_error(cache, "Rename security error %d", ret);
} else { } else {
struct renamedata rd = { struct renamedata rd = {
.old_mnt_userns = &init_user_ns,
.old_dir = d_inode(dir), .old_dir = d_inode(dir),
.old_dentry = rep, .old_dentry = rep,
.new_mnt_userns = &init_user_ns,
.new_dir = d_inode(cache->graveyard), .new_dir = d_inode(cache->graveyard),
.new_dentry = grave, .new_dentry = grave,
}; };
...@@ -566,7 +569,7 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent, ...@@ -566,7 +569,7 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent,
if (ret < 0) if (ret < 0)
goto create_error; goto create_error;
start = jiffies; start = jiffies;
ret = vfs_mkdir(d_inode(dir), next, 0); ret = vfs_mkdir(&init_user_ns, d_inode(dir), next, 0);
cachefiles_hist(cachefiles_mkdir_histogram, start); cachefiles_hist(cachefiles_mkdir_histogram, start);
if (!key) if (!key)
trace_cachefiles_mkdir(object, next, ret); trace_cachefiles_mkdir(object, next, ret);
...@@ -602,7 +605,8 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent, ...@@ -602,7 +605,8 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent,
if (ret < 0) if (ret < 0)
goto create_error; goto create_error;
start = jiffies; start = jiffies;
ret = vfs_create(d_inode(dir), next, S_IFREG, true); ret = vfs_create(&init_user_ns, d_inode(dir), next,
S_IFREG, true);
cachefiles_hist(cachefiles_create_histogram, start); cachefiles_hist(cachefiles_create_histogram, start);
trace_cachefiles_create(object, next, ret); trace_cachefiles_create(object, next, ret);
if (ret < 0) if (ret < 0)
...@@ -796,7 +800,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, ...@@ -796,7 +800,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
ret = security_path_mkdir(&path, subdir, 0700); ret = security_path_mkdir(&path, subdir, 0700);
if (ret < 0) if (ret < 0)
goto mkdir_error; goto mkdir_error;
ret = vfs_mkdir(d_inode(dir), subdir, 0700); ret = vfs_mkdir(&init_user_ns, d_inode(dir), subdir, 0700);
if (ret < 0) if (ret < 0)
goto mkdir_error; goto mkdir_error;
......
...@@ -141,7 +141,8 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry, ...@@ -141,7 +141,8 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
else if (d_unhashed(lower_dentry)) else if (d_unhashed(lower_dentry))
rc = -EINVAL; rc = -EINVAL;
else else
rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL); rc = vfs_unlink(&init_user_ns, lower_dir_inode, lower_dentry,
NULL);
if (rc) { if (rc) {
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
goto out_unlock; goto out_unlock;
...@@ -180,7 +181,8 @@ ecryptfs_do_create(struct inode *directory_inode, ...@@ -180,7 +181,8 @@ ecryptfs_do_create(struct inode *directory_inode,
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
lower_dir_dentry = lock_parent(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry);
rc = vfs_create(d_inode(lower_dir_dentry), lower_dentry, mode, true); rc = vfs_create(&init_user_ns, d_inode(lower_dir_dentry), lower_dentry,
mode, true);
if (rc) { if (rc) {
printk(KERN_ERR "%s: Failure to create dentry in lower fs; " printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
"rc = [%d]\n", __func__, rc); "rc = [%d]\n", __func__, rc);
...@@ -190,7 +192,8 @@ ecryptfs_do_create(struct inode *directory_inode, ...@@ -190,7 +192,8 @@ ecryptfs_do_create(struct inode *directory_inode,
inode = __ecryptfs_get_inode(d_inode(lower_dentry), inode = __ecryptfs_get_inode(d_inode(lower_dentry),
directory_inode->i_sb); directory_inode->i_sb);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
vfs_unlink(d_inode(lower_dir_dentry), lower_dentry, NULL); vfs_unlink(&init_user_ns, d_inode(lower_dir_dentry),
lower_dentry, NULL);
goto out_lock; goto out_lock;
} }
fsstack_copy_attr_times(directory_inode, d_inode(lower_dir_dentry)); fsstack_copy_attr_times(directory_inode, d_inode(lower_dir_dentry));
...@@ -436,8 +439,8 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, ...@@ -436,8 +439,8 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
dget(lower_old_dentry); dget(lower_old_dentry);
dget(lower_new_dentry); dget(lower_new_dentry);
lower_dir_dentry = lock_parent(lower_new_dentry); lower_dir_dentry = lock_parent(lower_new_dentry);
rc = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), rc = vfs_link(lower_old_dentry, &init_user_ns,
lower_new_dentry, NULL); d_inode(lower_dir_dentry), lower_new_dentry, NULL);
if (rc || d_really_is_negative(lower_new_dentry)) if (rc || d_really_is_negative(lower_new_dentry))
goto out_lock; goto out_lock;
rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb); rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb);
...@@ -481,7 +484,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -481,7 +484,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
strlen(symname)); strlen(symname));
if (rc) if (rc)
goto out_lock; goto out_lock;
rc = vfs_symlink(d_inode(lower_dir_dentry), lower_dentry, rc = vfs_symlink(&init_user_ns, d_inode(lower_dir_dentry), lower_dentry,
encoded_symname); encoded_symname);
kfree(encoded_symname); kfree(encoded_symname);
if (rc || d_really_is_negative(lower_dentry)) if (rc || d_really_is_negative(lower_dentry))
...@@ -507,7 +510,8 @@ static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode ...@@ -507,7 +510,8 @@ static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dentry = ecryptfs_dentry_to_lower(dentry);
lower_dir_dentry = lock_parent(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry);
rc = vfs_mkdir(d_inode(lower_dir_dentry), lower_dentry, mode); rc = vfs_mkdir(&init_user_ns, d_inode(lower_dir_dentry), lower_dentry,
mode);
if (rc || d_really_is_negative(lower_dentry)) if (rc || d_really_is_negative(lower_dentry))
goto out; goto out;
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
...@@ -541,7 +545,7 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -541,7 +545,7 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
else if (d_unhashed(lower_dentry)) else if (d_unhashed(lower_dentry))
rc = -EINVAL; rc = -EINVAL;
else else
rc = vfs_rmdir(lower_dir_inode, lower_dentry); rc = vfs_rmdir(&init_user_ns, lower_dir_inode, lower_dentry);
if (!rc) { if (!rc) {
clear_nlink(d_inode(dentry)); clear_nlink(d_inode(dentry));
fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_attr_times(dir, lower_dir_inode);
...@@ -563,7 +567,8 @@ ecryptfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev ...@@ -563,7 +567,8 @@ ecryptfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev
lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dentry = ecryptfs_dentry_to_lower(dentry);
lower_dir_dentry = lock_parent(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry);
rc = vfs_mknod(d_inode(lower_dir_dentry), lower_dentry, mode, dev); rc = vfs_mknod(&init_user_ns, d_inode(lower_dir_dentry), lower_dentry,
mode, dev);
if (rc || d_really_is_negative(lower_dentry)) if (rc || d_really_is_negative(lower_dentry))
goto out; goto out;
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
...@@ -621,8 +626,10 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -621,8 +626,10 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out_lock; goto out_lock;
} }
rd.old_mnt_userns = &init_user_ns;
rd.old_dir = d_inode(lower_old_dir_dentry); rd.old_dir = d_inode(lower_old_dir_dentry);
rd.old_dentry = lower_old_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_dir = d_inode(lower_new_dir_dentry);
rd.new_dentry = lower_new_dentry; rd.new_dentry = lower_new_dentry;
rc = vfs_rename(&rd); rc = vfs_rename(&rd);
......
...@@ -157,8 +157,8 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev) ...@@ -157,8 +157,8 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev)
mode &= ~current_umask(); mode &= ~current_umask();
error = security_path_mknod(&path, dentry, mode, dev); error = security_path_mknod(&path, dentry, mode, dev);
if (!error) if (!error)
error = vfs_mknod(path.dentry->d_inode, dentry, mode, error = vfs_mknod(&init_user_ns, path.dentry->d_inode, dentry,
new_decode_dev(dev)); mode, new_decode_dev(dev));
done_path_create(&path, dentry); done_path_create(&path, dentry);
return error; return error;
} }
...@@ -187,8 +187,8 @@ int __init init_link(const char *oldname, const char *newname) ...@@ -187,8 +187,8 @@ int __init init_link(const char *oldname, const char *newname)
error = security_path_link(old_path.dentry, &new_path, new_dentry); error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error) if (error)
goto out_dput; goto out_dput;
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, error = vfs_link(old_path.dentry, &init_user_ns,
NULL); new_path.dentry->d_inode, new_dentry, NULL);
out_dput: out_dput:
done_path_create(&new_path, new_dentry); done_path_create(&new_path, new_dentry);
out: out:
...@@ -207,7 +207,8 @@ int __init init_symlink(const char *oldname, const char *newname) ...@@ -207,7 +207,8 @@ int __init init_symlink(const char *oldname, const char *newname)
return PTR_ERR(dentry); return PTR_ERR(dentry);
error = security_path_symlink(&path, dentry, oldname); error = security_path_symlink(&path, dentry, oldname);
if (!error) if (!error)
error = vfs_symlink(path.dentry->d_inode, dentry, oldname); error = vfs_symlink(&init_user_ns, path.dentry->d_inode, dentry,
oldname);
done_path_create(&path, dentry); done_path_create(&path, dentry);
return error; return error;
} }
...@@ -230,7 +231,8 @@ int __init init_mkdir(const char *pathname, umode_t mode) ...@@ -230,7 +231,8 @@ int __init init_mkdir(const char *pathname, umode_t mode)
mode &= ~current_umask(); mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode); error = security_path_mkdir(&path, dentry, mode);
if (!error) if (!error)
error = vfs_mkdir(path.dentry->d_inode, dentry, mode); error = vfs_mkdir(&init_user_ns, path.dentry->d_inode, dentry,
mode);
done_path_create(&path, dentry); done_path_create(&path, dentry);
return error; return error;
} }
......
...@@ -2874,10 +2874,26 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) ...@@ -2874,10 +2874,26 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
} }
EXPORT_SYMBOL(unlock_rename); EXPORT_SYMBOL(unlock_rename);
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, /**
bool want_excl) * vfs_create - create new file
* @mnt_userns: user namespace of the mount the inode was found from
* @dir: inode of @dentry
* @dentry: pointer to dentry of the base directory
* @mode: mode of the new file
* @want_excl: whether the file must not yet exist
*
* Create a new file.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/
int vfs_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool want_excl)
{ {
int error = may_create(&init_user_ns, dir, dentry); int error = may_create(mnt_userns, dir, dentry);
if (error) if (error)
return error; return error;
...@@ -3353,7 +3369,23 @@ static int do_open(struct nameidata *nd, ...@@ -3353,7 +3369,23 @@ static int do_open(struct nameidata *nd,
return error; return error;
} }
struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag) /**
* vfs_tmpfile - create tmpfile
* @mnt_userns: user namespace of the mount the inode was found from
* @dentry: pointer to dentry of the base directory
* @mode: mode of the new tmpfile
* @open_flags: flags
*
* Create a temporary file.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/
struct dentry *vfs_tmpfile(struct user_namespace *mnt_userns,
struct dentry *dentry, umode_t mode, int open_flag)
{ {
struct dentry *child = NULL; struct dentry *child = NULL;
struct inode *dir = dentry->d_inode; struct inode *dir = dentry->d_inode;
...@@ -3361,7 +3393,7 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag) ...@@ -3361,7 +3393,7 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
int error; int error;
/* we want directory to be writable */ /* we want directory to be writable */
error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
if (error) if (error)
goto out_err; goto out_err;
error = -EOPNOTSUPP; error = -EOPNOTSUPP;
...@@ -3396,6 +3428,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, ...@@ -3396,6 +3428,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
const struct open_flags *op, const struct open_flags *op,
struct file *file) struct file *file)
{ {
struct user_namespace *mnt_userns;
struct dentry *child; struct dentry *child;
struct path path; struct path path;
int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path); int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path);
...@@ -3404,7 +3437,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, ...@@ -3404,7 +3437,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
error = mnt_want_write(path.mnt); error = mnt_want_write(path.mnt);
if (unlikely(error)) if (unlikely(error))
goto out; goto out;
child = vfs_tmpfile(path.dentry, op->mode, op->open_flag); mnt_userns = mnt_user_ns(path.mnt);
child = vfs_tmpfile(mnt_userns, path.dentry, op->mode, op->open_flag);
error = PTR_ERR(child); error = PTR_ERR(child);
if (IS_ERR(child)) if (IS_ERR(child))
goto out2; goto out2;
...@@ -3616,10 +3650,27 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname, ...@@ -3616,10 +3650,27 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
} }
EXPORT_SYMBOL(user_path_create); EXPORT_SYMBOL(user_path_create);
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) /**
* vfs_mknod - create device node or file
* @mnt_userns: user namespace of the mount the inode was found from
* @dir: inode of @dentry
* @dentry: pointer to dentry of the base directory
* @mode: mode of the new device node or file
* @dev: device number of device to create
*
* Create a device node or file.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/
int vfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t dev)
{ {
bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV; bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
int error = may_create(&init_user_ns, dir, dentry); int error = may_create(mnt_userns, dir, dentry);
if (error) if (error)
return error; return error;
...@@ -3666,6 +3717,7 @@ static int may_mknod(umode_t mode) ...@@ -3666,6 +3717,7 @@ static int may_mknod(umode_t mode)
static long do_mknodat(int dfd, const char __user *filename, umode_t mode, static long do_mknodat(int dfd, const char __user *filename, umode_t mode,
unsigned int dev) unsigned int dev)
{ {
struct user_namespace *mnt_userns;
struct dentry *dentry; struct dentry *dentry;
struct path path; struct path path;
int error; int error;
...@@ -3684,18 +3736,22 @@ static long do_mknodat(int dfd, const char __user *filename, umode_t mode, ...@@ -3684,18 +3736,22 @@ static long do_mknodat(int dfd, const char __user *filename, umode_t mode,
error = security_path_mknod(&path, dentry, mode, dev); error = security_path_mknod(&path, dentry, mode, dev);
if (error) if (error)
goto out; goto out;
mnt_userns = mnt_user_ns(path.mnt);
switch (mode & S_IFMT) { switch (mode & S_IFMT) {
case 0: case S_IFREG: case 0: case S_IFREG:
error = vfs_create(path.dentry->d_inode,dentry,mode,true); error = vfs_create(mnt_userns, path.dentry->d_inode,
dentry, mode, true);
if (!error) if (!error)
ima_post_path_mknod(dentry); ima_post_path_mknod(dentry);
break; break;
case S_IFCHR: case S_IFBLK: case S_IFCHR: case S_IFBLK:
error = vfs_mknod(path.dentry->d_inode,dentry,mode, error = vfs_mknod(mnt_userns, path.dentry->d_inode,
new_decode_dev(dev)); dentry, mode, new_decode_dev(dev));
break; break;
case S_IFIFO: case S_IFSOCK: case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(path.dentry->d_inode,dentry,mode,0); error = vfs_mknod(mnt_userns, path.dentry->d_inode,
dentry, mode, 0);
break; break;
} }
out: out:
...@@ -3718,9 +3774,25 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d ...@@ -3718,9 +3774,25 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
return do_mknodat(AT_FDCWD, filename, mode, dev); return do_mknodat(AT_FDCWD, filename, mode, dev);
} }
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) /**
* vfs_mkdir - create directory
* @mnt_userns: user namespace of the mount the inode was found from
* @dir: inode of @dentry
* @dentry: pointer to dentry of the base directory
* @mode: mode of the new directory
*
* Create a directory.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/
int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode)
{ {
int error = may_create(&init_user_ns, dir, dentry); int error = may_create(mnt_userns, dir, dentry);
unsigned max_links = dir->i_sb->s_max_links; unsigned max_links = dir->i_sb->s_max_links;
if (error) if (error)
...@@ -3759,8 +3831,11 @@ static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) ...@@ -3759,8 +3831,11 @@ static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
if (!IS_POSIXACL(path.dentry->d_inode)) if (!IS_POSIXACL(path.dentry->d_inode))
mode &= ~current_umask(); mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode); error = security_path_mkdir(&path, dentry, mode);
if (!error) if (!error) {
error = vfs_mkdir(path.dentry->d_inode, dentry, mode); struct user_namespace *mnt_userns;
mnt_userns = mnt_user_ns(path.mnt);
error = vfs_mkdir(mnt_userns, path.dentry->d_inode, dentry, mode);
}
done_path_create(&path, dentry); done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
...@@ -3779,9 +3854,24 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) ...@@ -3779,9 +3854,24 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
return do_mkdirat(AT_FDCWD, pathname, mode); return do_mkdirat(AT_FDCWD, pathname, mode);
} }
int vfs_rmdir(struct inode *dir, struct dentry *dentry) /**
* vfs_rmdir - remove directory
* @mnt_userns: user namespace of the mount the inode was found from
* @dir: inode of @dentry
* @dentry: pointer to dentry of the base directory
*
* Remove a directory.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/
int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry)
{ {
int error = may_delete(&init_user_ns, dir, dentry, 1); int error = may_delete(mnt_userns, dir, dentry, 1);
if (error) if (error)
return error; return error;
...@@ -3821,6 +3911,7 @@ EXPORT_SYMBOL(vfs_rmdir); ...@@ -3821,6 +3911,7 @@ EXPORT_SYMBOL(vfs_rmdir);
long do_rmdir(int dfd, struct filename *name) long do_rmdir(int dfd, struct filename *name)
{ {
struct user_namespace *mnt_userns;
int error = 0; int error = 0;
struct dentry *dentry; struct dentry *dentry;
struct path path; struct path path;
...@@ -3861,7 +3952,8 @@ long do_rmdir(int dfd, struct filename *name) ...@@ -3861,7 +3952,8 @@ long do_rmdir(int dfd, struct filename *name)
error = security_path_rmdir(&path, dentry); error = security_path_rmdir(&path, dentry);
if (error) if (error)
goto exit3; goto exit3;
error = vfs_rmdir(path.dentry->d_inode, dentry); mnt_userns = mnt_user_ns(path.mnt);
error = vfs_rmdir(mnt_userns, path.dentry->d_inode, dentry);
exit3: exit3:
dput(dentry); dput(dentry);
exit2: exit2:
...@@ -3884,6 +3976,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) ...@@ -3884,6 +3976,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
/** /**
* vfs_unlink - unlink a filesystem object * vfs_unlink - unlink a filesystem object
* @mnt_userns: user namespace of the mount the inode was found from
* @dir: parent directory * @dir: parent directory
* @dentry: victim * @dentry: victim
* @delegated_inode: returns victim inode, if the inode is delegated. * @delegated_inode: returns victim inode, if the inode is delegated.
...@@ -3899,11 +3992,18 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) ...@@ -3899,11 +3992,18 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
* Alternatively, a caller may pass NULL for delegated_inode. This may * Alternatively, a caller may pass NULL for delegated_inode. This may
* be appropriate for callers that expect the underlying filesystem not * be appropriate for callers that expect the underlying filesystem not
* to be NFS exported. * to be NFS exported.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/ */
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) int vfs_unlink(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, struct inode **delegated_inode)
{ {
struct inode *target = dentry->d_inode; struct inode *target = dentry->d_inode;
int error = may_delete(&init_user_ns, dir, dentry, 0); int error = may_delete(mnt_userns, dir, dentry, 0);
if (error) if (error)
return error; return error;
...@@ -3974,6 +4074,8 @@ long do_unlinkat(int dfd, struct filename *name) ...@@ -3974,6 +4074,8 @@ long do_unlinkat(int dfd, struct filename *name)
dentry = __lookup_hash(&last, path.dentry, lookup_flags); dentry = __lookup_hash(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
struct user_namespace *mnt_userns;
/* Why not before? Because we want correct error value */ /* Why not before? Because we want correct error value */
if (last.name[last.len]) if (last.name[last.len])
goto slashes; goto slashes;
...@@ -3984,7 +4086,8 @@ long do_unlinkat(int dfd, struct filename *name) ...@@ -3984,7 +4086,8 @@ long do_unlinkat(int dfd, struct filename *name)
error = security_path_unlink(&path, dentry); error = security_path_unlink(&path, dentry);
if (error) if (error)
goto exit2; goto exit2;
error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); mnt_userns = mnt_user_ns(path.mnt);
error = vfs_unlink(mnt_userns, path.dentry->d_inode, dentry, &delegated_inode);
exit2: exit2:
dput(dentry); dput(dentry);
} }
...@@ -4033,9 +4136,25 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) ...@@ -4033,9 +4136,25 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
return do_unlinkat(AT_FDCWD, getname(pathname)); return do_unlinkat(AT_FDCWD, getname(pathname));
} }
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) /**
* vfs_symlink - create symlink
* @mnt_userns: user namespace of the mount the inode was found from
* @dir: inode of @dentry
* @dentry: pointer to dentry of the base directory
* @oldname: name of the file to link to
*
* Create a symlink.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/
int vfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, const char *oldname)
{ {
int error = may_create(&init_user_ns, dir, dentry); int error = may_create(mnt_userns, dir, dentry);
if (error) if (error)
return error; return error;
...@@ -4073,8 +4192,13 @@ static long do_symlinkat(const char __user *oldname, int newdfd, ...@@ -4073,8 +4192,13 @@ static long do_symlinkat(const char __user *oldname, int newdfd,
goto out_putname; goto out_putname;
error = security_path_symlink(&path, dentry, from->name); error = security_path_symlink(&path, dentry, from->name);
if (!error) if (!error) {
error = vfs_symlink(path.dentry->d_inode, dentry, from->name); struct user_namespace *mnt_userns;
mnt_userns = mnt_user_ns(path.mnt);
error = vfs_symlink(mnt_userns, path.dentry->d_inode, dentry,
from->name);
}
done_path_create(&path, dentry); done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
...@@ -4099,6 +4223,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn ...@@ -4099,6 +4223,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
/** /**
* vfs_link - create a new link * vfs_link - create a new link
* @old_dentry: object to be linked * @old_dentry: object to be linked
* @mnt_userns: the user namespace of the mount
* @dir: new parent * @dir: new parent
* @new_dentry: where to create the new link * @new_dentry: where to create the new link
* @delegated_inode: returns inode needing a delegation break * @delegated_inode: returns inode needing a delegation break
...@@ -4114,8 +4239,16 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn ...@@ -4114,8 +4239,16 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
* Alternatively, a caller may pass NULL for delegated_inode. This may * Alternatively, a caller may pass NULL for delegated_inode. This may
* be appropriate for callers that expect the underlying filesystem not * be appropriate for callers that expect the underlying filesystem not
* to be NFS exported. * to be NFS exported.
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then take
* care to map the inode according to @mnt_userns before checking permissions.
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply passs init_user_ns.
*/ */
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) int vfs_link(struct dentry *old_dentry, struct user_namespace *mnt_userns,
struct inode *dir, struct dentry *new_dentry,
struct inode **delegated_inode)
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links; unsigned max_links = dir->i_sb->s_max_links;
...@@ -4124,7 +4257,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de ...@@ -4124,7 +4257,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode) if (!inode)
return -ENOENT; return -ENOENT;
error = may_create(&init_user_ns, dir, new_dentry); error = may_create(mnt_userns, dir, new_dentry);
if (error) if (error)
return error; return error;
...@@ -4141,7 +4274,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de ...@@ -4141,7 +4274,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
* be writen back improperly if their true value is unknown to * be writen back improperly if their true value is unknown to
* the vfs. * the vfs.
*/ */
if (HAS_UNMAPPED_ID(&init_user_ns, inode)) if (HAS_UNMAPPED_ID(mnt_userns, inode))
return -EPERM; return -EPERM;
if (!dir->i_op->link) if (!dir->i_op->link)
return -EPERM; return -EPERM;
...@@ -4188,6 +4321,7 @@ EXPORT_SYMBOL(vfs_link); ...@@ -4188,6 +4321,7 @@ EXPORT_SYMBOL(vfs_link);
static int do_linkat(int olddfd, const char __user *oldname, int newdfd, static int do_linkat(int olddfd, const char __user *oldname, int newdfd,
const char __user *newname, int flags) const char __user *newname, int flags)
{ {
struct user_namespace *mnt_userns;
struct dentry *new_dentry; struct dentry *new_dentry;
struct path old_path, new_path; struct path old_path, new_path;
struct inode *delegated_inode = NULL; struct inode *delegated_inode = NULL;
...@@ -4229,7 +4363,9 @@ static int do_linkat(int olddfd, const char __user *oldname, int newdfd, ...@@ -4229,7 +4363,9 @@ static int do_linkat(int olddfd, const char __user *oldname, int newdfd,
error = security_path_link(old_path.dentry, &new_path, new_dentry); error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error) if (error)
goto out_dput; goto out_dput;
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); mnt_userns = mnt_user_ns(new_path.mnt);
error = vfs_link(old_path.dentry, mnt_userns, new_path.dentry->d_inode,
new_dentry, &delegated_inode);
out_dput: out_dput:
done_path_create(&new_path, new_dentry); done_path_create(&new_path, new_dentry);
if (delegated_inode) { if (delegated_inode) {
...@@ -4263,8 +4399,10 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname ...@@ -4263,8 +4399,10 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
/** /**
* vfs_rename - rename a filesystem object * vfs_rename - rename a filesystem object
* @old_mnt_userns: old user namespace of the mount the inode was found from
* @old_dir: parent of source * @old_dir: parent of source
* @old_dentry: source * @old_dentry: source
* @new_mnt_userns: new user namespace of the mount the inode was found from
* @new_dir: parent of destination * @new_dir: parent of destination
* @new_dentry: destination * @new_dentry: destination
* @delegated_inode: returns an inode needing a delegation break * @delegated_inode: returns an inode needing a delegation break
...@@ -4314,7 +4452,6 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname ...@@ -4314,7 +4452,6 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
int vfs_rename(struct renamedata *rd) int vfs_rename(struct renamedata *rd)
{ {
int error; int error;
struct user_namespace *mnt_userns = &init_user_ns;
struct inode *old_dir = rd->old_dir, *new_dir = rd->new_dir; struct inode *old_dir = rd->old_dir, *new_dir = rd->new_dir;
struct dentry *old_dentry = rd->old_dentry; struct dentry *old_dentry = rd->old_dentry;
struct dentry *new_dentry = rd->new_dentry; struct dentry *new_dentry = rd->new_dentry;
...@@ -4330,19 +4467,21 @@ int vfs_rename(struct renamedata *rd) ...@@ -4330,19 +4467,21 @@ int vfs_rename(struct renamedata *rd)
if (source == target) if (source == target)
return 0; return 0;
error = may_delete(mnt_userns, old_dir, old_dentry, is_dir); error = may_delete(rd->old_mnt_userns, old_dir, old_dentry, is_dir);
if (error) if (error)
return error; return error;
if (!target) { if (!target) {
error = may_create(mnt_userns, new_dir, new_dentry); error = may_create(rd->new_mnt_userns, new_dir, new_dentry);
} else { } else {
new_is_dir = d_is_dir(new_dentry); new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE)) if (!(flags & RENAME_EXCHANGE))
error = may_delete(mnt_userns, new_dir, new_dentry, is_dir); error = may_delete(rd->new_mnt_userns, new_dir,
new_dentry, is_dir);
else else
error = may_delete(mnt_userns, new_dir, new_dentry, new_is_dir); error = may_delete(rd->new_mnt_userns, new_dir,
new_dentry, new_is_dir);
} }
if (error) if (error)
return error; return error;
...@@ -4356,13 +4495,13 @@ int vfs_rename(struct renamedata *rd) ...@@ -4356,13 +4495,13 @@ int vfs_rename(struct renamedata *rd)
*/ */
if (new_dir != old_dir) { if (new_dir != old_dir) {
if (is_dir) { if (is_dir) {
error = inode_permission(&init_user_ns, source, error = inode_permission(rd->old_mnt_userns, source,
MAY_WRITE); MAY_WRITE);
if (error) if (error)
return error; return error;
} }
if ((flags & RENAME_EXCHANGE) && new_is_dir) { if ((flags & RENAME_EXCHANGE) && new_is_dir) {
error = inode_permission(&init_user_ns, target, error = inode_permission(rd->new_mnt_userns, target,
MAY_WRITE); MAY_WRITE);
if (error) if (error)
return error; return error;
...@@ -4552,8 +4691,10 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, ...@@ -4552,8 +4691,10 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
rd.old_dir = old_path.dentry->d_inode; rd.old_dir = old_path.dentry->d_inode;
rd.old_dentry = old_dentry; rd.old_dentry = old_dentry;
rd.old_mnt_userns = mnt_user_ns(old_path.mnt);
rd.new_dir = new_path.dentry->d_inode; rd.new_dir = new_path.dentry->d_inode;
rd.new_dentry = new_dentry; rd.new_dentry = new_dentry;
rd.new_mnt_userns = mnt_user_ns(new_path.mnt);
rd.delegated_inode = &delegated_inode; rd.delegated_inode = &delegated_inode;
rd.flags = flags; rd.flags = flags;
error = vfs_rename(&rd); error = vfs_rename(&rd);
......
...@@ -233,7 +233,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -233,7 +233,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
* as well be forgiving and just succeed silently. * as well be forgiving and just succeed silently.
*/ */
goto out_put; goto out_put;
status = vfs_mkdir(d_inode(dir), dentry, S_IRWXU); status = vfs_mkdir(&init_user_ns, d_inode(dir), dentry, S_IRWXU);
out_put: out_put:
dput(dentry); dput(dentry);
out_unlock: out_unlock:
...@@ -353,7 +353,7 @@ nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn) ...@@ -353,7 +353,7 @@ nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
status = -ENOENT; status = -ENOENT;
if (d_really_is_negative(dentry)) if (d_really_is_negative(dentry))
goto out; goto out;
status = vfs_rmdir(d_inode(dir), dentry); status = vfs_rmdir(&init_user_ns, d_inode(dir), dentry);
out: out:
dput(dentry); dput(dentry);
out_unlock: out_unlock:
...@@ -443,7 +443,7 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ...@@ -443,7 +443,7 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
if (nfs4_has_reclaimed_state(name, nn)) if (nfs4_has_reclaimed_state(name, nn))
goto out_free; goto out_free;
status = vfs_rmdir(d_inode(parent), child); status = vfs_rmdir(&init_user_ns, d_inode(parent), child);
if (status) if (status)
printk("failed to remove client recovery directory %pd\n", printk("failed to remove client recovery directory %pd\n",
child); child);
......
...@@ -1255,12 +1255,12 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1255,12 +1255,12 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
host_err = 0; host_err = 0;
switch (type) { switch (type) {
case S_IFREG: case S_IFREG:
host_err = vfs_create(dirp, dchild, iap->ia_mode, true); host_err = vfs_create(&init_user_ns, dirp, dchild, iap->ia_mode, true);
if (!host_err) if (!host_err)
nfsd_check_ignore_resizing(iap); nfsd_check_ignore_resizing(iap);
break; break;
case S_IFDIR: case S_IFDIR:
host_err = vfs_mkdir(dirp, dchild, iap->ia_mode); host_err = vfs_mkdir(&init_user_ns, dirp, dchild, iap->ia_mode);
if (!host_err && unlikely(d_unhashed(dchild))) { if (!host_err && unlikely(d_unhashed(dchild))) {
struct dentry *d; struct dentry *d;
d = lookup_one_len(dchild->d_name.name, d = lookup_one_len(dchild->d_name.name,
...@@ -1288,7 +1288,8 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1288,7 +1288,8 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
case S_IFBLK: case S_IFBLK:
case S_IFIFO: case S_IFIFO:
case S_IFSOCK: case S_IFSOCK:
host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev); host_err = vfs_mknod(&init_user_ns, dirp, dchild,
iap->ia_mode, rdev);
break; break;
default: default:
printk(KERN_WARNING "nfsd: bad file type %o in nfsd_create\n", printk(KERN_WARNING "nfsd: bad file type %o in nfsd_create\n",
...@@ -1486,7 +1487,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1486,7 +1487,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (!IS_POSIXACL(dirp)) if (!IS_POSIXACL(dirp))
iap->ia_mode &= ~current_umask(); iap->ia_mode &= ~current_umask();
host_err = vfs_create(dirp, dchild, iap->ia_mode, true); host_err = vfs_create(&init_user_ns, dirp, dchild, iap->ia_mode, true);
if (host_err < 0) { if (host_err < 0) {
fh_drop_write(fhp); fh_drop_write(fhp);
goto out_nfserr; goto out_nfserr;
...@@ -1610,7 +1611,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1610,7 +1611,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (IS_ERR(dnew)) if (IS_ERR(dnew))
goto out_nfserr; goto out_nfserr;
host_err = vfs_symlink(d_inode(dentry), dnew, path); host_err = vfs_symlink(&init_user_ns, d_inode(dentry), dnew, path);
err = nfserrno(host_err); err = nfserrno(host_err);
if (!err) if (!err)
err = nfserrno(commit_metadata(fhp)); err = nfserrno(commit_metadata(fhp));
...@@ -1678,7 +1679,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, ...@@ -1678,7 +1679,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = nfserr_noent; err = nfserr_noent;
if (d_really_is_negative(dold)) if (d_really_is_negative(dold))
goto out_dput; goto out_dput;
host_err = vfs_link(dold, dirp, dnew, NULL); host_err = vfs_link(dold, &init_user_ns, dirp, dnew, NULL);
if (!host_err) { if (!host_err) {
err = nfserrno(commit_metadata(ffhp)); err = nfserrno(commit_metadata(ffhp));
if (!err) if (!err)
...@@ -1799,8 +1800,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, ...@@ -1799,8 +1800,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
goto out_dput_old; goto out_dput_old;
} else { } else {
struct renamedata rd = { struct renamedata rd = {
.old_mnt_userns = &init_user_ns,
.old_dir = fdir, .old_dir = fdir,
.old_dentry = odentry, .old_dentry = odentry,
.new_mnt_userns = &init_user_ns,
.new_dir = tdir, .new_dir = tdir,
.new_dentry = ndentry, .new_dentry = ndentry,
}; };
...@@ -1891,9 +1894,9 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -1891,9 +1894,9 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (type != S_IFDIR) { if (type != S_IFDIR) {
if (rdentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) if (rdentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK)
nfsd_close_cached_files(rdentry); nfsd_close_cached_files(rdentry);
host_err = vfs_unlink(dirp, rdentry, NULL); host_err = vfs_unlink(&init_user_ns, dirp, rdentry, NULL);
} else { } else {
host_err = vfs_rmdir(dirp, rdentry); host_err = vfs_rmdir(&init_user_ns, dirp, rdentry);
} }
if (!host_err) if (!host_err)
......
...@@ -821,9 +821,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir, ...@@ -821,9 +821,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
goto out_dput_upper; goto out_dput_upper;
if (is_dir) if (is_dir)
err = vfs_rmdir(dir, upper); err = vfs_rmdir(&init_user_ns, dir, upper);
else else
err = vfs_unlink(dir, upper, NULL); err = vfs_unlink(&init_user_ns, dir, upper, NULL);
ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry)); ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry));
/* /*
......
...@@ -123,7 +123,7 @@ static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox) ...@@ -123,7 +123,7 @@ static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{ {
int err = vfs_rmdir(dir, dentry); int err = vfs_rmdir(&init_user_ns, dir, dentry);
pr_debug("rmdir(%pd2) = %i\n", dentry, err); pr_debug("rmdir(%pd2) = %i\n", dentry, err);
return err; return err;
...@@ -131,7 +131,7 @@ static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -131,7 +131,7 @@ static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry) static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry)
{ {
int err = vfs_unlink(dir, dentry, NULL); int err = vfs_unlink(&init_user_ns, dir, dentry, NULL);
pr_debug("unlink(%pd2) = %i\n", dentry, err); pr_debug("unlink(%pd2) = %i\n", dentry, err);
return err; return err;
...@@ -140,7 +140,7 @@ static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry) ...@@ -140,7 +140,7 @@ static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry)
static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir, static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry) struct dentry *new_dentry)
{ {
int err = vfs_link(old_dentry, dir, new_dentry, NULL); int err = vfs_link(old_dentry, &init_user_ns, dir, new_dentry, NULL);
pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err); pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err);
return err; return err;
...@@ -149,7 +149,7 @@ static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir, ...@@ -149,7 +149,7 @@ static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir,
static inline int ovl_do_create(struct inode *dir, struct dentry *dentry, static inline int ovl_do_create(struct inode *dir, struct dentry *dentry,
umode_t mode) umode_t mode)
{ {
int err = vfs_create(dir, dentry, mode, true); int err = vfs_create(&init_user_ns, dir, dentry, mode, true);
pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err); pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err);
return err; return err;
...@@ -158,7 +158,7 @@ static inline int ovl_do_create(struct inode *dir, struct dentry *dentry, ...@@ -158,7 +158,7 @@ static inline int ovl_do_create(struct inode *dir, struct dentry *dentry,
static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry, static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry,
umode_t mode) umode_t mode)
{ {
int err = vfs_mkdir(dir, dentry, mode); int err = vfs_mkdir(&init_user_ns, dir, dentry, mode);
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err); pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err);
return err; return err;
} }
...@@ -166,7 +166,7 @@ static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry, ...@@ -166,7 +166,7 @@ static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry,
static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry, static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry,
umode_t mode, dev_t dev) umode_t mode, dev_t dev)
{ {
int err = vfs_mknod(dir, dentry, mode, dev); int err = vfs_mknod(&init_user_ns, dir, dentry, mode, dev);
pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err); pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err);
return err; return err;
...@@ -175,7 +175,7 @@ static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry, ...@@ -175,7 +175,7 @@ static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry,
static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry, static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry,
const char *oldname) const char *oldname)
{ {
int err = vfs_symlink(dir, dentry, oldname); int err = vfs_symlink(&init_user_ns, dir, dentry, oldname);
pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err); pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
return err; return err;
...@@ -215,8 +215,10 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry, ...@@ -215,8 +215,10 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry,
{ {
int err; int err;
struct renamedata rd = { struct renamedata rd = {
.old_mnt_userns = &init_user_ns,
.old_dir = olddir, .old_dir = olddir,
.old_dentry = olddentry, .old_dentry = olddentry,
.new_mnt_userns = &init_user_ns,
.new_dir = newdir, .new_dir = newdir,
.new_dentry = newdentry, .new_dentry = newdentry,
.flags = flags, .flags = flags,
...@@ -233,14 +235,14 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry, ...@@ -233,14 +235,14 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry,
static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry) static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry)
{ {
int err = vfs_whiteout(dir, dentry); int err = vfs_whiteout(&init_user_ns, dir, dentry);
pr_debug("whiteout(%pd2) = %i\n", dentry, err); pr_debug("whiteout(%pd2) = %i\n", dentry, err);
return err; return err;
} }
static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode) static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode)
{ {
struct dentry *ret = vfs_tmpfile(dentry, mode, 0); struct dentry *ret = vfs_tmpfile(&init_user_ns, dentry, mode, 0);
int err = PTR_ERR_OR_ZERO(ret); int err = PTR_ERR_OR_ZERO(ret);
pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err); pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err);
......
...@@ -1768,17 +1768,25 @@ bool inode_owner_or_capable(struct user_namespace *mnt_userns, ...@@ -1768,17 +1768,25 @@ bool inode_owner_or_capable(struct user_namespace *mnt_userns,
/* /*
* VFS helper functions.. * VFS helper functions..
*/ */
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); int vfs_create(struct user_namespace *, struct inode *,
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); struct dentry *, umode_t, bool);
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); int vfs_mkdir(struct user_namespace *, struct inode *,
extern int vfs_symlink(struct inode *, struct dentry *, const char *); struct dentry *, umode_t);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); int vfs_mknod(struct user_namespace *, struct inode *, struct dentry *,
extern int vfs_rmdir(struct inode *, struct dentry *); umode_t, dev_t);
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); int vfs_symlink(struct user_namespace *, struct inode *,
struct dentry *, const char *);
int vfs_link(struct dentry *, struct user_namespace *, struct inode *,
struct dentry *, struct inode **);
int vfs_rmdir(struct user_namespace *, struct inode *, struct dentry *);
int vfs_unlink(struct user_namespace *, struct inode *, struct dentry *,
struct inode **);
struct renamedata { struct renamedata {
struct user_namespace *old_mnt_userns;
struct inode *old_dir; struct inode *old_dir;
struct dentry *old_dentry; struct dentry *old_dentry;
struct user_namespace *new_mnt_userns;
struct inode *new_dir; struct inode *new_dir;
struct dentry *new_dentry; struct dentry *new_dentry;
struct inode **delegated_inode; struct inode **delegated_inode;
...@@ -1787,13 +1795,15 @@ struct renamedata { ...@@ -1787,13 +1795,15 @@ struct renamedata {
int vfs_rename(struct renamedata *); int vfs_rename(struct renamedata *);
static inline int vfs_whiteout(struct inode *dir, struct dentry *dentry) static inline int vfs_whiteout(struct user_namespace *mnt_userns,
struct inode *dir, struct dentry *dentry)
{ {
return vfs_mknod(dir, dentry, S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV); return vfs_mknod(mnt_userns, dir, dentry, S_IFCHR | WHITEOUT_MODE,
WHITEOUT_DEV);
} }
extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, struct dentry *vfs_tmpfile(struct user_namespace *mnt_userns,
int open_flag); struct dentry *dentry, umode_t mode, int open_flag);
int vfs_mkobj(struct dentry *, umode_t, int vfs_mkobj(struct dentry *, umode_t,
int (*f)(struct dentry *, umode_t, void *), int (*f)(struct dentry *, umode_t, void *),
......
...@@ -965,7 +965,8 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) ...@@ -965,7 +965,8 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
err = -ENOENT; err = -ENOENT;
} else { } else {
ihold(inode); ihold(inode);
err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL); err = vfs_unlink(&init_user_ns, d_inode(dentry->d_parent),
dentry, NULL);
} }
dput(dentry); dput(dentry);
......
...@@ -996,7 +996,8 @@ static int unix_mknod(const char *sun_path, umode_t mode, struct path *res) ...@@ -996,7 +996,8 @@ static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
*/ */
err = security_path_mknod(&path, dentry, mode, 0); err = security_path_mknod(&path, dentry, mode, 0);
if (!err) { if (!err) {
err = vfs_mknod(d_inode(path.dentry), dentry, mode, 0); err = vfs_mknod(&init_user_ns, d_inode(path.dentry), dentry,
mode, 0);
if (!err) { if (!err) {
res->mnt = mntget(path.mnt); res->mnt = mntget(path.mnt);
res->dentry = dget(dentry); res->dentry = dget(dentry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment