summaryrefslogtreecommitdiff
path: root/fs/xattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xattr.c')
-rw-r--r--fs/xattr.c120
1 files changed, 69 insertions, 51 deletions
diff --git a/fs/xattr.c b/fs/xattr.c
index d777025121e0..a49541713b11 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -83,7 +83,8 @@ xattr_resolve_name(struct inode *inode, const char **name)
* because different namespaces have very different rules.
*/
static int
-xattr_permission(struct inode *inode, const char *name, int mask)
+xattr_permission(struct user_namespace *mnt_userns, struct inode *inode,
+ const char *name, int mask)
{
/*
* We can never set or remove an extended attribute on a read-only
@@ -128,11 +129,11 @@ xattr_permission(struct inode *inode, const char *name, int mask)
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
(mask & MAY_WRITE) &&
- !inode_owner_or_capable(&init_user_ns, inode))
+ !inode_owner_or_capable(mnt_userns, inode))
return -EPERM;
}
- return inode_permission(&init_user_ns, inode, mask);
+ return inode_permission(mnt_userns, inode, mask);
}
/*
@@ -163,8 +164,9 @@ xattr_supported_namespace(struct inode *inode, const char *prefix)
EXPORT_SYMBOL(xattr_supported_namespace);
int
-__vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
+__vfs_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ struct inode *inode, const char *name, const void *value,
+ size_t size, int flags)
{
const struct xattr_handler *handler;
@@ -175,7 +177,7 @@ __vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
return -EOPNOTSUPP;
if (size == 0)
value = ""; /* empty EA, do not remove */
- return handler->set(handler, &init_user_ns, dentry, inode, name, value,
+ return handler->set(handler, mnt_userns, dentry, inode, name, value,
size, flags);
}
EXPORT_SYMBOL(__vfs_setxattr);
@@ -184,6 +186,7 @@ EXPORT_SYMBOL(__vfs_setxattr);
* __vfs_setxattr_noperm - perform setxattr operation without performing
* permission checks.
*
+ * @mnt_userns - user namespace of the mount the inode was found from
* @dentry - object to perform setxattr on
* @name - xattr name to set
* @value - value to set @name to
@@ -196,8 +199,9 @@ EXPORT_SYMBOL(__vfs_setxattr);
* is executed. It also assumes that the caller will make the appropriate
* permission checks.
*/
-int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+int __vfs_setxattr_noperm(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
int error = -EAGAIN;
@@ -207,7 +211,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
if (issec)
inode->i_flags &= ~S_NOSEC;
if (inode->i_opflags & IOP_XATTR) {
- error = __vfs_setxattr(dentry, inode, name, value, size, flags);
+ error = __vfs_setxattr(mnt_userns, dentry, inode, name, value,
+ size, flags);
if (!error) {
fsnotify_xattr(dentry);
security_inode_post_setxattr(dentry, name, value,
@@ -246,14 +251,14 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
* a delegation was broken on, NULL if none.
*/
int
-__vfs_setxattr_locked(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags,
- struct inode **delegated_inode)
+__vfs_setxattr_locked(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *name, const void *value, size_t size,
+ int flags, struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
int error;
- error = xattr_permission(inode, name, MAY_WRITE);
+ error = xattr_permission(mnt_userns, inode, name, MAY_WRITE);
if (error)
return error;
@@ -265,7 +270,8 @@ __vfs_setxattr_locked(struct dentry *dentry, const char *name,
if (error)
goto out;
- error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
+ error = __vfs_setxattr_noperm(mnt_userns, dentry, name, value,
+ size, flags);
out:
return error;
@@ -273,8 +279,8 @@ out:
EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
int
-vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
- size_t size, int flags)
+vfs_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *name, const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct inode *delegated_inode = NULL;
@@ -282,7 +288,7 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
int error;
if (size && strcmp(name, XATTR_NAME_CAPS) == 0) {
- error = cap_convert_nscap(&init_user_ns, dentry, &value, size);
+ error = cap_convert_nscap(mnt_userns, dentry, &value, size);
if (error < 0)
return error;
size = error;
@@ -290,8 +296,8 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
retry_deleg:
inode_lock(inode);
- error = __vfs_setxattr_locked(dentry, name, value, size, flags,
- &delegated_inode);
+ error = __vfs_setxattr_locked(mnt_userns, dentry, name, value, size,
+ flags, &delegated_inode);
inode_unlock(inode);
if (delegated_inode) {
@@ -341,15 +347,16 @@ out_noalloc:
* Returns the result of alloc, if failed, or the getxattr operation.
*/
ssize_t
-vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
- size_t xattr_size, gfp_t flags)
+vfs_getxattr_alloc(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *name, char **xattr_value, size_t xattr_size,
+ gfp_t flags)
{
const struct xattr_handler *handler;
struct inode *inode = dentry->d_inode;
char *value = *xattr_value;
int error;
- error = xattr_permission(inode, name, MAY_READ);
+ error = xattr_permission(mnt_userns, inode, name, MAY_READ);
if (error)
return error;
@@ -390,12 +397,13 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
EXPORT_SYMBOL(__vfs_getxattr);
ssize_t
-vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
+vfs_getxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *name, void *value, size_t size)
{
struct inode *inode = dentry->d_inode;
int error;
- error = xattr_permission(inode, name, MAY_READ);
+ error = xattr_permission(mnt_userns, inode, name, MAY_READ);
if (error)
return error;
@@ -441,7 +449,8 @@ vfs_listxattr(struct dentry *dentry, char *list, size_t size)
EXPORT_SYMBOL_GPL(vfs_listxattr);
int
-__vfs_removexattr(struct dentry *dentry, const char *name)
+__vfs_removexattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *name)
{
struct inode *inode = d_inode(dentry);
const struct xattr_handler *handler;
@@ -451,8 +460,8 @@ __vfs_removexattr(struct dentry *dentry, const char *name)
return PTR_ERR(handler);
if (!handler->set)
return -EOPNOTSUPP;
- return handler->set(handler, &init_user_ns, dentry, inode, name, NULL,
- 0, XATTR_REPLACE);
+ return handler->set(handler, mnt_userns, dentry, inode, name, NULL, 0,
+ XATTR_REPLACE);
}
EXPORT_SYMBOL(__vfs_removexattr);
@@ -466,13 +475,14 @@ EXPORT_SYMBOL(__vfs_removexattr);
* a delegation was broken on, NULL if none.
*/
int
-__vfs_removexattr_locked(struct dentry *dentry, const char *name,
- struct inode **delegated_inode)
+__vfs_removexattr_locked(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
+ struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
int error;
- error = xattr_permission(inode, name, MAY_WRITE);
+ error = xattr_permission(mnt_userns, inode, name, MAY_WRITE);
if (error)
return error;
@@ -484,7 +494,7 @@ __vfs_removexattr_locked(struct dentry *dentry, const char *name,
if (error)
goto out;
- error = __vfs_removexattr(dentry, name);
+ error = __vfs_removexattr(mnt_userns, dentry, name);
if (!error) {
fsnotify_xattr(dentry);
@@ -497,7 +507,8 @@ out:
EXPORT_SYMBOL_GPL(__vfs_removexattr_locked);
int
-vfs_removexattr(struct dentry *dentry, const char *name)
+vfs_removexattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *name)
{
struct inode *inode = dentry->d_inode;
struct inode *delegated_inode = NULL;
@@ -505,7 +516,8 @@ vfs_removexattr(struct dentry *dentry, const char *name)
retry_deleg:
inode_lock(inode);
- error = __vfs_removexattr_locked(dentry, name, &delegated_inode);
+ error = __vfs_removexattr_locked(mnt_userns, dentry,
+ name, &delegated_inode);
inode_unlock(inode);
if (delegated_inode) {
@@ -522,8 +534,9 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
* Extended attribute SET operations
*/
static long
-setxattr(struct dentry *d, const char __user *name, const void __user *value,
- size_t size, int flags)
+setxattr(struct user_namespace *mnt_userns, struct dentry *d,
+ const char __user *name, const void __user *value, size_t size,
+ int flags)
{
int error;
void *kvalue = NULL;
@@ -550,11 +563,10 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
}
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
- posix_acl_fix_xattr_from_user(&init_user_ns, kvalue,
- size);
+ posix_acl_fix_xattr_from_user(mnt_userns, kvalue, size);
}
- error = vfs_setxattr(d, kname, kvalue, size, flags);
+ error = vfs_setxattr(mnt_userns, d, kname, kvalue, size, flags);
out:
kvfree(kvalue);
@@ -567,13 +579,15 @@ static int path_setxattr(const char __user *pathname,
{
struct path path;
int error;
+
retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
if (error)
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = setxattr(path.dentry, name, value, size, flags);
+ error = setxattr(mnt_user_ns(path.mnt), path.dentry, name,
+ value, size, flags);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -609,7 +623,9 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
audit_file(f.file);
error = mnt_want_write_file(f.file);
if (!error) {
- error = setxattr(f.file->f_path.dentry, name, value, size, flags);
+ error = setxattr(file_mnt_user_ns(f.file),
+ f.file->f_path.dentry, name,
+ value, size, flags);
mnt_drop_write_file(f.file);
}
fdput(f);
@@ -620,8 +636,8 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
* Extended attribute GET operations
*/
static ssize_t
-getxattr(struct dentry *d, const char __user *name, void __user *value,
- size_t size)
+getxattr(struct user_namespace *mnt_userns, struct dentry *d,
+ const char __user *name, void __user *value, size_t size)
{
ssize_t error;
void *kvalue = NULL;
@@ -641,12 +657,11 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
return -ENOMEM;
}
- error = vfs_getxattr(d, kname, kvalue, size);
+ error = vfs_getxattr(mnt_userns, d, kname, kvalue, size);
if (error > 0) {
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
- posix_acl_fix_xattr_to_user(&init_user_ns, kvalue,
- error);
+ posix_acl_fix_xattr_to_user(mnt_userns, kvalue, error);
if (size && copy_to_user(value, kvalue, error))
error = -EFAULT;
} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
@@ -670,7 +685,7 @@ retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
if (error)
return error;
- error = getxattr(path.dentry, name, value, size);
+ error = getxattr(mnt_user_ns(path.mnt), path.dentry, name, value, size);
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -700,7 +715,8 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
if (!f.file)
return error;
audit_file(f.file);
- error = getxattr(f.file->f_path.dentry, name, value, size);
+ error = getxattr(file_mnt_user_ns(f.file), f.file->f_path.dentry,
+ name, value, size);
fdput(f);
return error;
}
@@ -784,7 +800,8 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
* Extended attribute REMOVE operations
*/
static long
-removexattr(struct dentry *d, const char __user *name)
+removexattr(struct user_namespace *mnt_userns, struct dentry *d,
+ const char __user *name)
{
int error;
char kname[XATTR_NAME_MAX + 1];
@@ -795,7 +812,7 @@ removexattr(struct dentry *d, const char __user *name)
if (error < 0)
return error;
- return vfs_removexattr(d, kname);
+ return vfs_removexattr(mnt_userns, d, kname);
}
static int path_removexattr(const char __user *pathname,
@@ -809,7 +826,7 @@ retry:
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = removexattr(path.dentry, name);
+ error = removexattr(mnt_user_ns(path.mnt), path.dentry, name);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -842,7 +859,8 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
audit_file(f.file);
error = mnt_want_write_file(f.file);
if (!error) {
- error = removexattr(f.file->f_path.dentry, name);
+ error = removexattr(file_mnt_user_ns(f.file),
+ f.file->f_path.dentry, name);
mnt_drop_write_file(f.file);
}
fdput(f);