Merge branch 'lineage-18.0' into followmsi-11

This commit is contained in:
followmsi 2020-12-08 14:20:33 +01:00
commit 5aa785e6e7
59 changed files with 1904 additions and 836 deletions

View File

@ -59,7 +59,6 @@ prototypes:
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *); int (*removexattr) (struct dentry *, const char *);
void (*truncate_range)(struct inode *, loff_t, loff_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
void (*update_time)(struct inode *, struct timespec *, int); void (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *, int (*atomic_open)(struct inode *, struct dentry *,
@ -91,7 +90,6 @@ setxattr: yes
getxattr: no getxattr: no
listxattr: no listxattr: no
removexattr: yes removexattr: yes
truncate_range: yes
fiemap: no fiemap: no
update_time: no update_time: no
atomic_open: yes atomic_open: yes

View File

@ -363,7 +363,6 @@ struct inode_operations {
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *); int (*removexattr) (struct dentry *, const char *);
void (*truncate_range)(struct inode *, loff_t, loff_t);
void (*update_time)(struct inode *, struct timespec *, int); void (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *, struct file *, int (*atomic_open)(struct inode *, struct dentry *, struct file *,
unsigned open_flag, umode_t create_mode, int *opened); unsigned open_flag, umode_t create_mode, int *opened);
@ -476,9 +475,6 @@ otherwise noted.
removexattr: called by the VFS to remove an extended attribute from removexattr: called by the VFS to remove an extended attribute from
a file. This method is called by removexattr(2) system call. a file. This method is called by removexattr(2) system call.
truncate_range: a method provided by the underlying filesystem to truncate a
range of blocks , i.e. punch a hole somewhere in a file.
update_time: called by the VFS to update a specific time or the i_version of update_time: called by the VFS to update a specific time or the i_version of
an inode. If this is not defined the VFS will update the inode itself an inode. If this is not defined the VFS will update the inode itself
and call mark_inode_dirty_sync. and call mark_inode_dirty_sync.
@ -780,7 +776,7 @@ struct file_operations
---------------------- ----------------------
This describes how the VFS can manipulate an open file. As of kernel This describes how the VFS can manipulate an open file. As of kernel
2.6.22, the following members are defined: 3.5, the following members are defined:
struct file_operations { struct file_operations {
struct module *owner; struct module *owner;
@ -810,6 +806,8 @@ struct file_operations {
int (*flock) (struct file *, int, struct file_lock *); int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long arg, struct file_lock **);
long (*fallocate)(struct file *, int mode, loff_t offset, loff_t len);
}; };
Again, all methods are called without any locks being held, unless Again, all methods are called without any locks being held, unless
@ -878,6 +876,11 @@ otherwise noted.
splice_read: called by the VFS to splice data from file to a pipe. This splice_read: called by the VFS to splice data from file to a pipe. This
method is used by the splice(2) system call method is used by the splice(2) system call
setlease: called by the VFS to set or release a file lock lease.
setlease has the file_lock_lock held and must not sleep.
fallocate: called by the VFS to preallocate blocks or punch a hole.
Note that the file operations are implemented by the specific Note that the file operations are implemented by the specific
filesystem in which the inode resides. When opening a device node filesystem in which the inode resides. When opening a device node
(character or block special) most filesystems will call special (character or block special) most filesystems will call special

View File

@ -350,7 +350,7 @@ struct io_cq *ioc_create_icq(struct request_queue *q, gfp_t gfp_mask)
if (!icq) if (!icq)
return NULL; return NULL;
if (radix_tree_preload(gfp_mask) < 0) { if (radix_tree_maybe_preload(gfp_mask) < 0) {
kmem_cache_free(et->icq_cache, icq); kmem_cache_free(et->icq_cache, icq);
return NULL; return NULL;
} }

View File

@ -19,6 +19,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/falloc.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/mm.h> #include <linux/mm.h>
@ -374,11 +375,12 @@ static int ashmem_shrink(struct shrinker *s, struct shrink_control *sc)
return -1; return -1;
list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) { list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
struct inode *inode = range->asma->file->f_dentry->d_inode;
loff_t start = range->pgstart * PAGE_SIZE; loff_t start = range->pgstart * PAGE_SIZE;
loff_t end = (range->pgend + 1) * PAGE_SIZE - 1; loff_t end = (range->pgend + 1) * PAGE_SIZE;
vmtruncate_range(inode, start, end); do_fallocate(range->asma->file,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
start, end - start);
range->purged = ASHMEM_WAS_PURGED; range->purged = ASHMEM_WAS_PURGED;
lru_del(range); lru_del(range);

View File

@ -37,7 +37,7 @@ static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
size = v9fs_fid_xattr_get(fid, name, value, size); size = v9fs_fid_xattr_get(fid, name, value, size);
if (size > 0) { if (size > 0) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
goto err_out; goto err_out;
} }
@ -131,7 +131,7 @@ static int v9fs_set_acl(struct dentry *dentry, int type, struct posix_acl *acl)
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) if (!buffer)
return -ENOMEM; return -ENOMEM;
retval = posix_acl_to_xattr(acl, buffer, size); retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
if (retval < 0) if (retval < 0)
goto err_free_out; goto err_free_out;
switch (type) { switch (type) {
@ -160,7 +160,7 @@ int v9fs_acl_chmod(struct dentry *dentry)
return -EOPNOTSUPP; return -EOPNOTSUPP;
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
if (acl) { if (acl) {
retval = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (retval) if (retval)
return retval; return retval;
retval = v9fs_set_acl(dentry, ACL_TYPE_ACCESS, acl); retval = v9fs_set_acl(dentry, ACL_TYPE_ACCESS, acl);
@ -199,7 +199,7 @@ int v9fs_acl_mode(struct inode *dir, umode_t *modep,
if (acl) { if (acl) {
if (S_ISDIR(mode)) if (S_ISDIR(mode))
*dpacl = posix_acl_dup(acl); *dpacl = posix_acl_dup(acl);
retval = posix_acl_create(&acl, GFP_NOFS, &mode); retval = __posix_acl_create(&acl, GFP_NOFS, &mode);
if (retval < 0) if (retval < 0)
return retval; return retval;
if (retval > 0) if (retval > 0)
@ -251,7 +251,7 @@ static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -304,7 +304,7 @@ static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name,
return -EPERM; return -EPERM;
if (value) { if (value) {
/* update the cached acl value */ /* update the cached acl value */
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
else if (acl) { else if (acl) {

View File

@ -81,10 +81,6 @@ config CUSE
If you want to develop or use userspace character device If you want to develop or use userspace character device
based on CUSE, answer Y or M. based on CUSE, answer Y or M.
config GENERIC_ACL
bool
select FS_POSIX_ACL
menu "Caches" menu "Caches"
source "fs/fscache/Kconfig" source "fs/fscache/Kconfig"
@ -133,7 +129,7 @@ config TMPFS_POSIX_ACL
bool "Tmpfs POSIX Access Control Lists" bool "Tmpfs POSIX Access Control Lists"
depends on TMPFS depends on TMPFS
select TMPFS_XATTR select TMPFS_XATTR
select GENERIC_ACL select FS_POSIX_ACL
help help
POSIX Access Control Lists (ACLs) support additional access rights POSIX Access Control Lists (ACLs) support additional access rights
for users and groups beyond the standard owner/group/world scheme, for users and groups beyond the standard owner/group/world scheme,

View File

@ -46,9 +46,8 @@ obj-$(CONFIG_BINFMT_SOM) += binfmt_som.o
obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o
obj-$(CONFIG_FS_MBCACHE) += mbcache.o obj-$(CONFIG_FS_MBCACHE) += mbcache.o
obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/ obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
obj-$(CONFIG_FHANDLE) += fhandle.o obj-$(CONFIG_FHANDLE) += fhandle.o

View File

@ -292,7 +292,6 @@ static const struct inode_operations bad_inode_ops =
.getxattr = bad_inode_getxattr, .getxattr = bad_inode_getxattr,
.listxattr = bad_inode_listxattr, .listxattr = bad_inode_listxattr,
.removexattr = bad_inode_removexattr, .removexattr = bad_inode_removexattr,
/* truncate_range returns void */
}; };

View File

@ -61,7 +61,7 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
size = __btrfs_getxattr(inode, name, value, size); size = __btrfs_getxattr(inode, name, value, size);
} }
if (size > 0) { if (size > 0) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
} else if (size == -ENOENT || size == -ENODATA || size == 0) { } else if (size == -ENOENT || size == -ENODATA || size == 0) {
/* FIXME, who returns -ENOENT? I think nobody */ /* FIXME, who returns -ENOENT? I think nobody */
acl = NULL; acl = NULL;
@ -91,7 +91,7 @@ static int btrfs_xattr_acl_get(struct dentry *dentry, const char *name,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
ret = posix_acl_to_xattr(acl, value, size); ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
posix_acl_release(acl); posix_acl_release(acl);
return ret; return ret;
@ -141,7 +141,7 @@ static int btrfs_set_acl(struct btrfs_trans_handle *trans,
goto out; goto out;
} }
ret = posix_acl_to_xattr(acl, value, size); ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
if (ret < 0) if (ret < 0)
goto out; goto out;
} }
@ -169,7 +169,7 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
@ -220,7 +220,7 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans,
if (ret) if (ret)
goto failed; goto failed;
} }
ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -250,7 +250,7 @@ int btrfs_acl_chmod(struct inode *inode)
if (IS_ERR_OR_NULL(acl)) if (IS_ERR_OR_NULL(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (ret) if (ret)
return ret; return ret;
ret = btrfs_set_acl(NULL, inode, acl, ACL_TYPE_ACCESS); ret = btrfs_set_acl(NULL, inode, acl, ACL_TYPE_ACCESS);

View File

@ -252,7 +252,7 @@ ext2_init_acl(struct inode *inode, struct inode *dir)
if (error) if (error)
goto cleanup; goto cleanup;
} }
error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); error = __posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (error < 0) if (error < 0)
return error; return error;
if (error > 0) { if (error > 0) {
@ -292,7 +292,7 @@ ext2_acl_chmod(struct inode *inode)
acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); error = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (error) if (error)
return error; return error;
error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl); error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl);
@ -346,7 +346,7 @@ ext2_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -367,7 +367,7 @@ ext2_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
else if (acl) { else if (acl) {

View File

@ -255,7 +255,7 @@ ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
if (error) if (error)
goto cleanup; goto cleanup;
} }
error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); error = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (error < 0) if (error < 0)
return error; return error;
@ -298,7 +298,7 @@ ext3_acl_chmod(struct inode *inode)
acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); error = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (error) if (error)
return error; return error;
retry: retry:
@ -365,7 +365,7 @@ ext3_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -388,7 +388,7 @@ ext3_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
else if (acl) { else if (acl) {

View File

@ -259,7 +259,7 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
if (error) if (error)
goto cleanup; goto cleanup;
} }
error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); error = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (error < 0) if (error < 0)
return error; return error;
@ -303,7 +303,7 @@ ext4_acl_chmod(struct inode *inode)
acl = ext4_get_acl(inode, ACL_TYPE_ACCESS); acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); error = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (error) if (error)
return error; return error;
retry: retry:
@ -370,7 +370,7 @@ ext4_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -393,7 +393,7 @@ ext4_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
else if (acl) { else if (acl) {

View File

@ -272,7 +272,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
if (error) if (error)
goto cleanup; goto cleanup;
} }
error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); error = __posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (error < 0) if (error < 0)
return error; return error;
if (error > 0) if (error > 0)
@ -298,7 +298,7 @@ int f2fs_acl_chmod(struct inode *inode)
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
error = posix_acl_chmod(&acl, GFP_KERNEL, mode); error = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
if (error) if (error)
return error; return error;
@ -343,7 +343,7 @@ static int f2fs_xattr_get_acl(struct dentry *dentry, const char *name,
return PTR_ERR(acl); return PTR_ERR(acl);
if (!acl) if (!acl)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -365,7 +365,7 @@ static int f2fs_xattr_set_acl(struct dentry *dentry, const char *name,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {

View File

@ -20,6 +20,7 @@
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/pid_namespace.h> #include <linux/pid_namespace.h>
#include <linux/shmem_fs.h>
#include <asm/poll.h> #include <asm/poll.h>
#include <asm/siginfo.h> #include <asm/siginfo.h>
@ -420,6 +421,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
case F_GETPIPE_SZ: case F_GETPIPE_SZ:
err = pipe_fcntl(filp, cmd, arg); err = pipe_fcntl(filp, cmd, arg);
break; break;
case F_ADD_SEALS:
case F_GET_SEALS:
err = shmem_fcntl(filp, cmd, arg);
break;
default: default:
break; break;
} }

View File

@ -767,7 +767,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
fscache_release_write_op); fscache_release_write_op);
op->op.flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_WAITING); op->op.flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_WAITING);
ret = radix_tree_preload(gfp & ~__GFP_HIGHMEM); ret = radix_tree_maybe_preload(gfp & ~__GFP_HIGHMEM);
if (ret < 0) if (ret < 0)
goto nomem_free; goto nomem_free;

View File

@ -1,180 +0,0 @@
/*
* (C) 2005 Andreas Gruenbacher <agruen@suse.de>
*
* This file is released under the GPL.
*
* Generic ACL support for in-memory filesystems.
*/
#include <linux/sched.h>
#include <linux/gfp.h>
#include <linux/fs.h>
#include <linux/generic_acl.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
static size_t
generic_acl_list(struct dentry *dentry, char *list, size_t list_size,
const char *name, size_t name_len, int type)
{
struct posix_acl *acl;
const char *xname;
size_t size;
acl = get_cached_acl(dentry->d_inode, type);
if (!acl)
return 0;
posix_acl_release(acl);
switch (type) {
case ACL_TYPE_ACCESS:
xname = POSIX_ACL_XATTR_ACCESS;
break;
case ACL_TYPE_DEFAULT:
xname = POSIX_ACL_XATTR_DEFAULT;
break;
default:
return 0;
}
size = strlen(xname) + 1;
if (list && size <= list_size)
memcpy(list, xname, size);
return size;
}
static int
generic_acl_get(struct dentry *dentry, const char *name, void *buffer,
size_t size, int type)
{
struct posix_acl *acl;
int error;
if (strcmp(name, "") != 0)
return -EINVAL;
acl = get_cached_acl(dentry->d_inode, type);
if (!acl)
return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size);
posix_acl_release(acl);
return error;
}
static int
generic_acl_set(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags, int type)
{
struct inode *inode = dentry->d_inode;
struct posix_acl *acl = NULL;
int error;
if (strcmp(name, "") != 0)
return -EINVAL;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
if (!inode_owner_or_capable(inode))
return -EPERM;
if (value) {
acl = posix_acl_from_xattr(value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
}
if (acl) {
error = posix_acl_valid(acl);
if (error)
goto failed;
switch (type) {
case ACL_TYPE_ACCESS:
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
if (error)
goto failed;
inode->i_ctime = CURRENT_TIME;
break;
case ACL_TYPE_DEFAULT:
if (!S_ISDIR(inode->i_mode)) {
error = -EINVAL;
goto failed;
}
break;
}
}
set_cached_acl(inode, type, acl);
error = 0;
failed:
posix_acl_release(acl);
return error;
}
/**
* generic_acl_init - Take care of acl inheritance at @inode create time
*
* Files created inside a directory with a default ACL inherit the
* directory's default ACL.
*/
int
generic_acl_init(struct inode *inode, struct inode *dir)
{
struct posix_acl *acl = NULL;
int error;
if (!S_ISLNK(inode->i_mode))
acl = get_cached_acl(dir, ACL_TYPE_DEFAULT);
if (acl) {
if (S_ISDIR(inode->i_mode))
set_cached_acl(inode, ACL_TYPE_DEFAULT, acl);
error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (error < 0)
return error;
if (error > 0)
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
} else {
inode->i_mode &= ~current_umask();
}
error = 0;
posix_acl_release(acl);
return error;
}
/**
* generic_acl_chmod - change the access acl of @inode upon chmod()
*
* A chmod also changes the permissions of the owner, group/mask, and
* other ACL entries.
*/
int
generic_acl_chmod(struct inode *inode)
{
struct posix_acl *acl;
int error = 0;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
if (acl) {
error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (error)
return error;
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
posix_acl_release(acl);
}
return error;
}
const struct xattr_handler generic_acl_access_handler = {
.prefix = POSIX_ACL_XATTR_ACCESS,
.flags = ACL_TYPE_ACCESS,
.list = generic_acl_list,
.get = generic_acl_get,
.set = generic_acl_set,
};
const struct xattr_handler generic_acl_default_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.flags = ACL_TYPE_DEFAULT,
.list = generic_acl_list,
.get = generic_acl_get,
.set = generic_acl_set,
};

View File

@ -63,7 +63,7 @@ struct posix_acl *gfs2_get_acl(struct inode *inode, int type)
if (len == 0) if (len == 0)
return NULL; return NULL;
acl = posix_acl_from_xattr(data, len); acl = posix_acl_from_xattr(&init_user_ns, data, len);
kfree(data); kfree(data);
return acl; return acl;
} }
@ -92,13 +92,13 @@ static int gfs2_acl_set(struct inode *inode, int type, struct posix_acl *acl)
const char *name = gfs2_acl_name(type); const char *name = gfs2_acl_name(type);
BUG_ON(name == NULL); BUG_ON(name == NULL);
len = posix_acl_to_xattr(acl, NULL, 0); len = posix_acl_to_xattr(&init_user_ns, acl, NULL, 0);
if (len == 0) if (len == 0)
return 0; return 0;
data = kmalloc(len, GFP_NOFS); data = kmalloc(len, GFP_NOFS);
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;
error = posix_acl_to_xattr(acl, data, len); error = posix_acl_to_xattr(&init_user_ns, acl, data, len);
if (error < 0) if (error < 0)
goto out; goto out;
error = __gfs2_xattr_set(inode, name, data, len, 0, GFS2_EATYPE_SYS); error = __gfs2_xattr_set(inode, name, data, len, 0, GFS2_EATYPE_SYS);
@ -137,7 +137,7 @@ int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode)
goto out; goto out;
} }
error = posix_acl_create(&acl, GFP_NOFS, &mode); error = __posix_acl_create(&acl, GFP_NOFS, &mode);
if (error < 0) if (error < 0)
return error; return error;
@ -168,16 +168,16 @@ int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
if (!acl) if (!acl)
return gfs2_setattr_simple(inode, attr); return gfs2_setattr_simple(inode, attr);
error = posix_acl_chmod(&acl, GFP_NOFS, attr->ia_mode); error = __posix_acl_chmod(&acl, GFP_NOFS, attr->ia_mode);
if (error) if (error)
return error; return error;
len = posix_acl_to_xattr(acl, NULL, 0); len = posix_acl_to_xattr(&init_user_ns, acl, NULL, 0);
data = kmalloc(len, GFP_NOFS); data = kmalloc(len, GFP_NOFS);
error = -ENOMEM; error = -ENOMEM;
if (data == NULL) if (data == NULL)
goto out; goto out;
posix_acl_to_xattr(acl, data, len); posix_acl_to_xattr(&init_user_ns, acl, data, len);
error = gfs2_xattr_acl_chmod(ip, attr, data); error = gfs2_xattr_acl_chmod(ip, attr, data);
kfree(data); kfree(data);
set_cached_acl(&ip->i_inode, ACL_TYPE_ACCESS, acl); set_cached_acl(&ip->i_inode, ACL_TYPE_ACCESS, acl);
@ -218,7 +218,7 @@ static int gfs2_xattr_system_get(struct dentry *dentry, const char *name,
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -251,7 +251,7 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name,
if (!value) if (!value)
goto set_acl; goto set_acl;
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (!acl) { if (!acl) {
/* /*
* acl_set_file(3) may request that we set default ACLs with * acl_set_file(3) may request that we set default ACLs with

View File

@ -164,6 +164,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
mapping->a_ops = &empty_aops; mapping->a_ops = &empty_aops;
mapping->host = inode; mapping->host = inode;
mapping->flags = 0; mapping->flags = 0;
atomic_set(&mapping->i_mmap_writable, 0);
mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE);
mapping->assoc_mapping = NULL; mapping->assoc_mapping = NULL;
mapping->backing_dev_info = &default_backing_dev_info; mapping->backing_dev_info = &default_backing_dev_info;

View File

@ -280,7 +280,7 @@ int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, umode_t *i_mode
if (S_ISDIR(*i_mode)) if (S_ISDIR(*i_mode))
set_cached_acl(inode, ACL_TYPE_DEFAULT, acl); set_cached_acl(inode, ACL_TYPE_DEFAULT, acl);
rc = posix_acl_create(&acl, GFP_KERNEL, i_mode); rc = __posix_acl_create(&acl, GFP_KERNEL, i_mode);
if (rc < 0) if (rc < 0)
return rc; return rc;
if (rc > 0) if (rc > 0)
@ -320,7 +320,7 @@ int jffs2_acl_chmod(struct inode *inode)
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
rc = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); rc = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (rc) if (rc)
return rc; return rc;
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, acl); rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, acl);
@ -362,7 +362,7 @@ static int jffs2_acl_getxattr(struct dentry *dentry, const char *name,
return PTR_ERR(acl); return PTR_ERR(acl);
if (!acl) if (!acl)
return -ENODATA; return -ENODATA;
rc = posix_acl_to_xattr(acl, buffer, size); rc = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return rc; return rc;
@ -380,7 +380,7 @@ static int jffs2_acl_setxattr(struct dentry *dentry, const char *name,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl) { if (acl) {

View File

@ -64,7 +64,7 @@ struct posix_acl *jfs_get_acl(struct inode *inode, int type)
else else
acl = ERR_PTR(size); acl = ERR_PTR(size);
} else { } else {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
} }
kfree(value); kfree(value);
if (!IS_ERR(acl)) if (!IS_ERR(acl))
@ -100,7 +100,7 @@ static int jfs_set_acl(tid_t tid, struct inode *inode, int type,
value = kmalloc(size, GFP_KERNEL); value = kmalloc(size, GFP_KERNEL);
if (!value) if (!value)
return -ENOMEM; return -ENOMEM;
rc = posix_acl_to_xattr(acl, value, size); rc = posix_acl_to_xattr(&init_user_ns, acl, value, size);
if (rc < 0) if (rc < 0)
goto out; goto out;
} }
@ -132,7 +132,7 @@ int jfs_init_acl(tid_t tid, struct inode *inode, struct inode *dir)
if (rc) if (rc)
goto cleanup; goto cleanup;
} }
rc = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); rc = __posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (rc < 0) if (rc < 0)
goto cleanup; /* posix_acl_release(NULL) is no-op */ goto cleanup; /* posix_acl_release(NULL) is no-op */
if (rc > 0) if (rc > 0)
@ -161,7 +161,7 @@ int jfs_acl_chmod(struct inode *inode)
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
rc = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); rc = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (rc) if (rc)
return rc; return rc;

View File

@ -685,7 +685,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name,
* POSIX_ACL_XATTR_ACCESS is tied to i_mode * POSIX_ACL_XATTR_ACCESS is tied to i_mode
*/ */
if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) { if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) {
acl = posix_acl_from_xattr(value, value_len); acl = posix_acl_from_xattr(&init_user_ns, value, value_len);
if (IS_ERR(acl)) { if (IS_ERR(acl)) {
rc = PTR_ERR(acl); rc = PTR_ERR(acl);
printk(KERN_ERR "posix_acl_from_xattr returned %d\n", printk(KERN_ERR "posix_acl_from_xattr returned %d\n",
@ -710,7 +710,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name,
return 0; return 0;
} else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) { } else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) {
acl = posix_acl_from_xattr(value, value_len); acl = posix_acl_from_xattr(&init_user_ns, value, value_len);
if (IS_ERR(acl)) { if (IS_ERR(acl)) {
rc = PTR_ERR(acl); rc = PTR_ERR(acl);
printk(KERN_ERR "posix_acl_from_xattr returned %d\n", printk(KERN_ERR "posix_acl_from_xattr returned %d\n",

View File

@ -230,27 +230,9 @@ static int check_acl(struct inode *inode, int mask)
return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK); return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
} }
acl = get_cached_acl(inode, ACL_TYPE_ACCESS); acl = get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
/* return PTR_ERR(acl);
* A filesystem can force a ACL callback by just never filling the
* ACL cache. But normally you'd fill the cache either at inode
* instantiation time, or on the first ->get_acl call.
*
* If the filesystem doesn't have a get_acl() function at all, we'll
* just create the negative cache entry.
*/
if (acl == ACL_NOT_CACHED) {
if (inode->i_op->get_acl) {
acl = inode->i_op->get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
} else {
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
return -EAGAIN;
}
}
if (acl) { if (acl) {
int error = posix_acl_permission(inode, acl, mask); int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl); posix_acl_release(acl);

View File

@ -70,7 +70,7 @@ ssize_t nfs3_getxattr(struct dentry *dentry, const char *name,
if (type == ACL_TYPE_ACCESS && acl->a_count == 0) if (type == ACL_TYPE_ACCESS && acl->a_count == 0)
error = -ENODATA; error = -ENODATA;
else else
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
} else } else
error = -ENODATA; error = -ENODATA;
@ -92,7 +92,7 @@ int nfs3_setxattr(struct dentry *dentry, const char *name,
else else
return -EOPNOTSUPP; return -EOPNOTSUPP;
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
error = nfs3_proc_setacl(inode, type, acl); error = nfs3_proc_setacl(inode, type, acl);
@ -428,7 +428,7 @@ int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
if (!dfacl) if (!dfacl)
return 0; return 0;
acl = posix_acl_dup(dfacl); acl = posix_acl_dup(dfacl);
error = posix_acl_create(&acl, GFP_KERNEL, &mode); error = __posix_acl_create(&acl, GFP_KERNEL, &mode);
if (error < 0) if (error < 0)
goto out_release_dfacl; goto out_release_dfacl;
error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ? error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ?

View File

@ -517,7 +517,7 @@ set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
if (buf == NULL) if (buf == NULL)
goto out; goto out;
len = posix_acl_to_xattr(pacl, buf, buflen); len = posix_acl_to_xattr(&init_user_ns, pacl, buf, buflen);
if (len < 0) { if (len < 0) {
error = len; error = len;
goto out; goto out;
@ -586,7 +586,7 @@ _get_posix_acl(struct dentry *dentry, char *key)
if (buflen <= 0) if (buflen <= 0)
return ERR_PTR(buflen); return ERR_PTR(buflen);
pacl = posix_acl_from_xattr(buf, buflen); pacl = posix_acl_from_xattr(&init_user_ns, buf, buflen);
kfree(buf); kfree(buf);
return pacl; return pacl;
} }
@ -2299,7 +2299,7 @@ nfsd_get_posix_acl(struct svc_fh *fhp, int type)
if (size < 0) if (size < 0)
return ERR_PTR(size); return ERR_PTR(size);
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
kfree(value); kfree(value);
return acl; return acl;
} }
@ -2332,7 +2332,7 @@ nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
value = kmalloc(size, GFP_KERNEL); value = kmalloc(size, GFP_KERNEL);
if (!value) if (!value)
return -ENOMEM; return -ENOMEM;
error = posix_acl_to_xattr(acl, value, size); error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
if (error < 0) if (error < 0)
goto getout; goto getout;
size = error; size = error;

View File

@ -321,7 +321,7 @@ int ocfs2_acl_chmod(struct inode *inode)
acl = ocfs2_get_acl(inode, ACL_TYPE_ACCESS); acl = ocfs2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (ret) if (ret)
return ret; return ret;
ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS, ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS,
@ -372,7 +372,7 @@ int ocfs2_init_acl(handle_t *handle,
goto cleanup; goto cleanup;
} }
mode = inode->i_mode; mode = inode->i_mode;
ret = posix_acl_create(&acl, GFP_NOFS, &mode); ret = __posix_acl_create(&acl, GFP_NOFS, &mode);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -446,7 +446,7 @@ static int ocfs2_xattr_get_acl(struct dentry *dentry, const char *name,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
ret = posix_acl_to_xattr(acl, buffer, size); ret = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return ret; return ret;
@ -469,7 +469,7 @@ static int ocfs2_xattr_set_acl(struct dentry *dentry, const char *name,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
else if (acl) { else if (acl) {

View File

@ -1,10 +1,8 @@
/* /*
* linux/fs/posix_acl.c * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org>
* *
* Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org> * Fixes from William Schumacher incorporated on 15 March 2001.
* * (Reported by Charles Bertsch, <CBertsch@microtest.com>).
* Fixes from William Schumacher incorporated on 15 March 2001.
* (Reported by Charles Bertsch, <CBertsch@microtest.com>).
*/ */
/* /*
@ -18,9 +16,10 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/xattr.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/user_namespace.h>
#include <linux/errno.h>
EXPORT_SYMBOL(posix_acl_init); EXPORT_SYMBOL(posix_acl_init);
EXPORT_SYMBOL(posix_acl_alloc); EXPORT_SYMBOL(posix_acl_alloc);
@ -28,6 +27,33 @@ EXPORT_SYMBOL(posix_acl_valid);
EXPORT_SYMBOL(posix_acl_equiv_mode); EXPORT_SYMBOL(posix_acl_equiv_mode);
EXPORT_SYMBOL(posix_acl_from_mode); EXPORT_SYMBOL(posix_acl_from_mode);
struct posix_acl *get_acl(struct inode *inode, int type)
{
struct posix_acl *acl;
acl = get_cached_acl(inode, type);
if (acl != ACL_NOT_CACHED)
return acl;
if (!IS_POSIXACL(inode))
return NULL;
/*
* A filesystem can force a ACL callback by just never filling the
* ACL cache. But normally you'd fill the cache either at inode
* instantiation time, or on the first ->get_acl call.
*
* If the filesystem doesn't have a get_acl() function at all, we'll
* just create the negative cache entry.
*/
if (!inode->i_op->get_acl) {
set_cached_acl(inode, type, NULL);
return NULL;
}
return inode->i_op->get_acl(inode, type);
}
EXPORT_SYMBOL(get_acl);
/* /*
* Init a fresh posix_acl * Init a fresh posix_acl
*/ */
@ -78,7 +104,6 @@ posix_acl_valid(const struct posix_acl *acl)
{ {
const struct posix_acl_entry *pa, *pe; const struct posix_acl_entry *pa, *pe;
int state = ACL_USER_OBJ; int state = ACL_USER_OBJ;
unsigned int id = 0; /* keep gcc happy */
int needs_mask = 0; int needs_mask = 0;
FOREACH_ACL_ENTRY(pa, acl, pe) { FOREACH_ACL_ENTRY(pa, acl, pe) {
@ -87,7 +112,6 @@ posix_acl_valid(const struct posix_acl *acl)
switch (pa->e_tag) { switch (pa->e_tag) {
case ACL_USER_OBJ: case ACL_USER_OBJ:
if (state == ACL_USER_OBJ) { if (state == ACL_USER_OBJ) {
id = 0;
state = ACL_USER; state = ACL_USER;
break; break;
} }
@ -96,16 +120,13 @@ posix_acl_valid(const struct posix_acl *acl)
case ACL_USER: case ACL_USER:
if (state != ACL_USER) if (state != ACL_USER)
return -EINVAL; return -EINVAL;
if (pa->e_id == ACL_UNDEFINED_ID || if (!uid_valid(pa->e_uid))
pa->e_id < id)
return -EINVAL; return -EINVAL;
id = pa->e_id + 1;
needs_mask = 1; needs_mask = 1;
break; break;
case ACL_GROUP_OBJ: case ACL_GROUP_OBJ:
if (state == ACL_USER) { if (state == ACL_USER) {
id = 0;
state = ACL_GROUP; state = ACL_GROUP;
break; break;
} }
@ -114,10 +135,8 @@ posix_acl_valid(const struct posix_acl *acl)
case ACL_GROUP: case ACL_GROUP:
if (state != ACL_GROUP) if (state != ACL_GROUP)
return -EINVAL; return -EINVAL;
if (pa->e_id == ACL_UNDEFINED_ID || if (!gid_valid(pa->e_gid))
pa->e_id < id)
return -EINVAL; return -EINVAL;
id = pa->e_id + 1;
needs_mask = 1; needs_mask = 1;
break; break;
@ -201,15 +220,12 @@ posix_acl_from_mode(umode_t mode, gfp_t flags)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
acl->a_entries[0].e_tag = ACL_USER_OBJ; acl->a_entries[0].e_tag = ACL_USER_OBJ;
acl->a_entries[0].e_id = ACL_UNDEFINED_ID;
acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
acl->a_entries[1].e_tag = ACL_GROUP_OBJ; acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
acl->a_entries[1].e_id = ACL_UNDEFINED_ID;
acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
acl->a_entries[2].e_tag = ACL_OTHER; acl->a_entries[2].e_tag = ACL_OTHER;
acl->a_entries[2].e_id = ACL_UNDEFINED_ID;
acl->a_entries[2].e_perm = (mode & S_IRWXO); acl->a_entries[2].e_perm = (mode & S_IRWXO);
return acl; return acl;
} }
@ -230,11 +246,11 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
switch(pa->e_tag) { switch(pa->e_tag) {
case ACL_USER_OBJ: case ACL_USER_OBJ:
/* (May have been checked already) */ /* (May have been checked already) */
if (inode->i_uid == current_fsuid()) if (uid_eq(inode->i_uid, current_fsuid()))
goto check_perm; goto check_perm;
break; break;
case ACL_USER: case ACL_USER:
if (pa->e_id == current_fsuid()) if (uid_eq(pa->e_uid, current_fsuid()))
goto mask; goto mask;
break; break;
case ACL_GROUP_OBJ: case ACL_GROUP_OBJ:
@ -245,7 +261,7 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
} }
break; break;
case ACL_GROUP: case ACL_GROUP:
if (in_group_p(pa->e_id)) { if (in_group_p(pa->e_gid)) {
found = 1; found = 1;
if ((pa->e_perm & want) == want) if ((pa->e_perm & want) == want)
goto mask; goto mask;
@ -375,7 +391,7 @@ EXPORT_SYMBOL(posix_acl_update_mode);
/* /*
* Modify the ACL for the chmod syscall. * Modify the ACL for the chmod syscall.
*/ */
static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
{ {
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
struct posix_acl_entry *pa, *pe; struct posix_acl_entry *pa, *pe;
@ -421,7 +437,7 @@ static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
} }
int int
posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) __posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
{ {
struct posix_acl *clone = posix_acl_clone(*acl, gfp); struct posix_acl *clone = posix_acl_clone(*acl, gfp);
int err = -ENOMEM; int err = -ENOMEM;
@ -436,15 +452,15 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
*acl = clone; *acl = clone;
return err; return err;
} }
EXPORT_SYMBOL(posix_acl_create); EXPORT_SYMBOL(__posix_acl_create);
int int
posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
{ {
struct posix_acl *clone = posix_acl_clone(*acl, gfp); struct posix_acl *clone = posix_acl_clone(*acl, gfp);
int err = -ENOMEM; int err = -ENOMEM;
if (clone) { if (clone) {
err = posix_acl_chmod_masq(clone, mode); err = __posix_acl_chmod_masq(clone, mode);
if (err) { if (err) {
posix_acl_release(clone); posix_acl_release(clone);
clone = NULL; clone = NULL;
@ -454,4 +470,389 @@ posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
*acl = clone; *acl = clone;
return err; return err;
} }
EXPORT_SYMBOL(__posix_acl_chmod);
int
posix_acl_chmod(struct inode *inode, umode_t mode)
{
struct posix_acl *acl;
int ret = 0;
if (!IS_POSIXACL(inode))
return 0;
if (!inode->i_op->set_acl)
return -EOPNOTSUPP;
acl = get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR_OR_NULL(acl)) {
if (acl == ERR_PTR(-EOPNOTSUPP))
return 0;
return PTR_ERR(acl);
}
ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
if (ret)
return ret;
ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS);
posix_acl_release(acl);
return ret;
}
EXPORT_SYMBOL(posix_acl_chmod); EXPORT_SYMBOL(posix_acl_chmod);
int
posix_acl_create(struct inode *dir, umode_t *mode,
struct posix_acl **default_acl, struct posix_acl **acl)
{
struct posix_acl *p;
int ret;
if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
goto no_acl;
p = get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(p)) {
if (p == ERR_PTR(-EOPNOTSUPP))
goto apply_umask;
return PTR_ERR(p);
}
if (!p)
goto apply_umask;
*acl = posix_acl_clone(p, GFP_NOFS);
if (!*acl)
return -ENOMEM;
ret = posix_acl_create_masq(*acl, mode);
if (ret < 0) {
posix_acl_release(*acl);
return -ENOMEM;
}
if (ret == 0) {
posix_acl_release(*acl);
*acl = NULL;
}
if (!S_ISDIR(*mode)) {
posix_acl_release(p);
*default_acl = NULL;
} else {
*default_acl = p;
}
return 0;
apply_umask:
*mode &= ~current_umask();
no_acl:
*default_acl = NULL;
*acl = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(posix_acl_create);
/*
* Fix up the uids and gids in posix acl extended attributes in place.
*/
static void posix_acl_fix_xattr_userns(
struct user_namespace *to, struct user_namespace *from,
void *value, size_t size)
{
posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
int count;
kuid_t uid;
kgid_t gid;
if (!value)
return;
if (size < sizeof(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:
uid = make_kuid(from, le32_to_cpu(entry->e_id));
entry->e_id = cpu_to_le32(from_kuid(to, uid));
break;
case ACL_GROUP:
gid = make_kgid(from, le32_to_cpu(entry->e_id));
entry->e_id = cpu_to_le32(from_kgid(to, gid));
break;
default:
break;
}
}
}
void posix_acl_fix_xattr_from_user(void *value, size_t size)
{
struct user_namespace *user_ns = current_user_ns();
if (user_ns == &init_user_ns)
return;
posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
}
void posix_acl_fix_xattr_to_user(void *value, size_t size)
{
struct user_namespace *user_ns = current_user_ns();
if (user_ns == &init_user_ns)
return;
posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
}
/*
* Convert from extended attribute to in-memory representation.
*/
struct posix_acl *
posix_acl_from_xattr(struct user_namespace *user_ns,
const void *value, size_t size)
{
posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
int count;
struct posix_acl *acl;
struct posix_acl_entry *acl_e;
if (!value)
return NULL;
if (size < sizeof(posix_acl_xattr_header))
return ERR_PTR(-EINVAL);
if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
return ERR_PTR(-EOPNOTSUPP);
count = posix_acl_xattr_count(size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
return NULL;
acl = posix_acl_alloc(count, GFP_NOFS);
if (!acl)
return ERR_PTR(-ENOMEM);
acl_e = acl->a_entries;
for (end = entry + count; entry != end; acl_e++, entry++) {
acl_e->e_tag = le16_to_cpu(entry->e_tag);
acl_e->e_perm = le16_to_cpu(entry->e_perm);
switch(acl_e->e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
break;
case ACL_USER:
acl_e->e_uid =
make_kuid(user_ns,
le32_to_cpu(entry->e_id));
if (!uid_valid(acl_e->e_uid))
goto fail;
break;
case ACL_GROUP:
acl_e->e_gid =
make_kgid(user_ns,
le32_to_cpu(entry->e_id));
if (!gid_valid(acl_e->e_gid))
goto fail;
break;
default:
goto fail;
}
}
return acl;
fail:
posix_acl_release(acl);
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL (posix_acl_from_xattr);
/*
* Convert from in-memory to extended attribute representation.
*/
int
posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl,
void *buffer, size_t size)
{
posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
posix_acl_xattr_entry *ext_entry;
int real_size, n;
real_size = posix_acl_xattr_size(acl->a_count);
if (!buffer)
return real_size;
if (real_size > size)
return -ERANGE;
ext_entry = ext_acl->a_entries;
ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
for (n=0; n < acl->a_count; n++, ext_entry++) {
const struct posix_acl_entry *acl_e = &acl->a_entries[n];
ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
switch(acl_e->e_tag) {
case ACL_USER:
ext_entry->e_id =
cpu_to_le32(from_kuid(user_ns, acl_e->e_uid));
break;
case ACL_GROUP:
ext_entry->e_id =
cpu_to_le32(from_kgid(user_ns, acl_e->e_gid));
break;
default:
ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
break;
}
}
return real_size;
}
EXPORT_SYMBOL (posix_acl_to_xattr);
static int
posix_acl_xattr_get(struct dentry *dentry, const char *name,
void *value, size_t size, int type)
{
struct posix_acl *acl;
int error;
if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;
if (S_ISLNK(dentry->d_inode->i_mode))
return -EOPNOTSUPP;
acl = get_acl(dentry->d_inode, type);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl == NULL)
return -ENODATA;
error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
posix_acl_release(acl);
return error;
}
static int
posix_acl_xattr_set(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags, int type)
{
struct inode *inode = dentry->d_inode;
struct posix_acl *acl = NULL;
int ret;
if (!IS_POSIXACL(inode))
return -EOPNOTSUPP;
if (!inode->i_op->set_acl)
return -EOPNOTSUPP;
if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
return value ? -EACCES : 0;
if (!inode_owner_or_capable(inode))
return -EPERM;
if (value) {
acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
ret = posix_acl_valid(acl);
if (ret)
goto out;
}
}
ret = inode->i_op->set_acl(inode, acl, type);
out:
posix_acl_release(acl);
return ret;
}
static size_t
posix_acl_xattr_list(struct dentry *dentry, char *list, size_t list_size,
const char *name, size_t name_len, int type)
{
const char *xname;
size_t size;
if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;
if (S_ISLNK(dentry->d_inode->i_mode))
return -EOPNOTSUPP;
if (type == ACL_TYPE_ACCESS)
xname = POSIX_ACL_XATTR_ACCESS;
else
xname = POSIX_ACL_XATTR_DEFAULT;
size = strlen(xname) + 1;
if (list && size <= list_size)
memcpy(list, xname, size);
return size;
}
const struct xattr_handler posix_acl_access_xattr_handler = {
.prefix = POSIX_ACL_XATTR_ACCESS,
.flags = ACL_TYPE_ACCESS,
.list = posix_acl_xattr_list,
.get = posix_acl_xattr_get,
.set = posix_acl_xattr_set,
};
EXPORT_SYMBOL_GPL(posix_acl_access_xattr_handler);
const struct xattr_handler posix_acl_default_xattr_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.flags = ACL_TYPE_DEFAULT,
.list = posix_acl_xattr_list,
.get = posix_acl_xattr_get,
.set = posix_acl_xattr_set,
};
EXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler);
int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int error;
if (type == ACL_TYPE_ACCESS) {
error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
return 0;
if (error == 0)
acl = NULL;
}
inode->i_ctime = CURRENT_TIME;
set_cached_acl(inode, type, acl);
return 0;
}
int simple_acl_create(struct inode *dir, struct inode *inode)
{
struct posix_acl *default_acl, *acl;
int error;
error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
if (error)
return error;
set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl);
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
if (default_acl)
posix_acl_release(default_acl);
if (acl)
posix_acl_release(acl);
return 0;
}

View File

@ -30,7 +30,7 @@ posix_acl_set(struct dentry *dentry, const char *name, const void *value,
return -EPERM; return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl)) { if (IS_ERR(acl)) {
return PTR_ERR(acl); return PTR_ERR(acl);
} else if (acl) { } else if (acl) {
@ -77,7 +77,7 @@ posix_acl_get(struct dentry *dentry, const char *name, void *buffer,
return PTR_ERR(acl); return PTR_ERR(acl);
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -358,7 +358,7 @@ reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th,
/* Now we reconcile the new ACL and the mode, /* Now we reconcile the new ACL and the mode,
potentially modifying both */ potentially modifying both */
err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); err = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (err < 0) if (err < 0)
return err; return err;
@ -440,7 +440,7 @@ int reiserfs_acl_chmod(struct inode *inode)
return 0; return 0;
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
error = posix_acl_chmod(&acl, GFP_NOFS, inode->i_mode); error = __posix_acl_chmod(&acl, GFP_NOFS, inode->i_mode);
if (error) if (error)
return error; return error;

View File

@ -20,6 +20,7 @@
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/posix_acl_xattr.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
@ -295,11 +296,13 @@ vfs_removexattr(struct dentry *dentry, const char *name)
if (error) if (error)
return error; return error;
error = security_inode_removexattr(dentry, name);
if (error)
return error;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
error = security_inode_removexattr(dentry, name);
if (error) {
mutex_unlock(&inode->i_mutex);
return error;
}
error = inode->i_op->removexattr(dentry, name); error = inode->i_op->removexattr(dentry, name);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
@ -347,6 +350,9 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
error = -EFAULT; error = -EFAULT;
goto out; goto out;
} }
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
posix_acl_fix_xattr_from_user(kvalue, size);
} }
error = vfs_setxattr(d, kname, kvalue, size, flags); error = vfs_setxattr(d, kname, kvalue, size, flags);
@ -399,11 +405,12 @@ SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname,
SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
const void __user *,value, size_t, size, int, flags) const void __user *,value, size_t, size, int, flags)
{ {
int fput_needed;
struct file *f; struct file *f;
struct dentry *dentry; struct dentry *dentry;
int error = -EBADF; int error = -EBADF;
f = fget(fd); f = fget_light(fd, &fput_needed);
if (!f) if (!f)
return error; return error;
dentry = f->f_path.dentry; dentry = f->f_path.dentry;
@ -413,7 +420,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
error = setxattr(dentry, name, value, size, flags); error = setxattr(dentry, name, value, size, flags);
mnt_drop_write_file(f); mnt_drop_write_file(f);
} }
fput(f); fput_light(f, fput_needed);
return error; return error;
} }
@ -444,6 +451,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
error = vfs_getxattr(d, kname, kvalue, size); error = vfs_getxattr(d, kname, kvalue, size);
if (error > 0) { 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(kvalue, size);
if (size && copy_to_user(value, kvalue, error)) if (size && copy_to_user(value, kvalue, error))
error = -EFAULT; error = -EFAULT;
} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
@ -486,15 +496,16 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
void __user *, value, size_t, size) void __user *, value, size_t, size)
{ {
int fput_needed;
struct file *f; struct file *f;
ssize_t error = -EBADF; ssize_t error = -EBADF;
f = fget(fd); f = fget_light(fd, &fput_needed);
if (!f) if (!f)
return error; return error;
audit_inode(NULL, f->f_path.dentry); audit_inode(NULL, f->f_path.dentry);
error = getxattr(f->f_path.dentry, name, value, size); error = getxattr(f->f_path.dentry, name, value, size);
fput(f); fput_light(f, fput_needed);
return error; return error;
} }
@ -566,15 +577,16 @@ SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
{ {
int fput_needed;
struct file *f; struct file *f;
ssize_t error = -EBADF; ssize_t error = -EBADF;
f = fget(fd); f = fget_light(fd, &fput_needed);
if (!f) if (!f)
return error; return error;
audit_inode(NULL, f->f_path.dentry); audit_inode(NULL, f->f_path.dentry);
error = listxattr(f->f_path.dentry, list, size); error = listxattr(f->f_path.dentry, list, size);
fput(f); fput_light(f, fput_needed);
return error; return error;
} }
@ -634,11 +646,12 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
{ {
int fput_needed;
struct file *f; struct file *f;
struct dentry *dentry; struct dentry *dentry;
int error = -EBADF; int error = -EBADF;
f = fget(fd); f = fget_light(fd, &fput_needed);
if (!f) if (!f)
return error; return error;
dentry = f->f_path.dentry; dentry = f->f_path.dentry;
@ -648,7 +661,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
error = removexattr(dentry, name); error = removexattr(dentry, name);
mnt_drop_write_file(f); mnt_drop_write_file(f);
} }
fput(f); fput_light(f, fput_needed);
return error; return error;
} }
@ -779,3 +792,183 @@ EXPORT_SYMBOL(generic_getxattr);
EXPORT_SYMBOL(generic_listxattr); EXPORT_SYMBOL(generic_listxattr);
EXPORT_SYMBOL(generic_setxattr); EXPORT_SYMBOL(generic_setxattr);
EXPORT_SYMBOL(generic_removexattr); EXPORT_SYMBOL(generic_removexattr);
/*
* Allocate new xattr and copy in the value; but leave the name to callers.
*/
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size)
{
struct simple_xattr *new_xattr;
size_t len;
/* wrap around? */
len = sizeof(*new_xattr) + size;
if (len <= sizeof(*new_xattr))
return NULL;
new_xattr = kmalloc(len, GFP_KERNEL);
if (!new_xattr)
return NULL;
new_xattr->size = size;
memcpy(new_xattr->value, value, size);
return new_xattr;
}
/*
* xattr GET operation for in-memory/pseudo filesystems
*/
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
void *buffer, size_t size)
{
struct simple_xattr *xattr;
int ret = -ENODATA;
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
if (strcmp(name, xattr->name))
continue;
ret = xattr->size;
if (buffer) {
if (size < xattr->size)
ret = -ERANGE;
else
memcpy(buffer, xattr->value, xattr->size);
}
break;
}
spin_unlock(&xattrs->lock);
return ret;
}
static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
const void *value, size_t size, int flags)
{
struct simple_xattr *xattr;
struct simple_xattr *new_xattr = NULL;
int err = 0;
/* value == NULL means remove */
if (value) {
new_xattr = simple_xattr_alloc(value, size);
if (!new_xattr)
return -ENOMEM;
new_xattr->name = kstrdup(name, GFP_KERNEL);
if (!new_xattr->name) {
kfree(new_xattr);
return -ENOMEM;
}
}
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
if (!strcmp(name, xattr->name)) {
if (flags & XATTR_CREATE) {
xattr = new_xattr;
err = -EEXIST;
} else if (new_xattr) {
list_replace(&xattr->list, &new_xattr->list);
} else {
list_del(&xattr->list);
}
goto out;
}
}
if (flags & XATTR_REPLACE) {
xattr = new_xattr;
err = -ENODATA;
} else {
list_add(&new_xattr->list, &xattrs->head);
xattr = NULL;
}
out:
spin_unlock(&xattrs->lock);
if (xattr) {
kfree(xattr->name);
kfree(xattr);
}
return err;
}
/**
* simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems
* @xattrs: target simple_xattr list
* @name: name of the new extended attribute
* @value: value of the new xattr. If %NULL, will remove the attribute
* @size: size of the new xattr
* @flags: %XATTR_{CREATE|REPLACE}
*
* %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails
* with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist;
* otherwise, fails with -ENODATA.
*
* Returns 0 on success, -errno on failure.
*/
int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
const void *value, size_t size, int flags)
{
if (size == 0)
value = ""; /* empty EA, do not remove */
return __simple_xattr_set(xattrs, name, value, size, flags);
}
/*
* xattr REMOVE operation for in-memory/pseudo filesystems
*/
int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name)
{
return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE);
}
static bool xattr_is_trusted(const char *name)
{
return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
}
/*
* xattr LIST operation for in-memory/pseudo filesystems
*/
ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer,
size_t size)
{
bool trusted = capable(CAP_SYS_ADMIN);
struct simple_xattr *xattr;
size_t used = 0;
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
size_t len;
/* skip "trusted." attributes for unprivileged callers */
if (!trusted && xattr_is_trusted(xattr->name))
continue;
len = strlen(xattr->name) + 1;
used += len;
if (buffer) {
if (size < used) {
used = -ERANGE;
break;
}
memcpy(buffer, xattr->name, len);
buffer += len;
}
}
spin_unlock(&xattrs->lock);
return used;
}
/*
* Adds an extended attribute to the list
*/
void simple_xattr_list_add(struct simple_xattrs *xattrs,
struct simple_xattr *new_xattr)
{
spin_lock(&xattrs->lock);
list_add(&new_xattr->list, &xattrs->head);
spin_unlock(&xattrs->lock);
}

View File

@ -1,98 +0,0 @@
/*
* linux/fs/xattr_acl.c
*
* Almost all from linux/fs/ext2/acl.c:
* Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/posix_acl_xattr.h>
#include <linux/gfp.h>
/*
* Convert from extended attribute to in-memory representation.
*/
struct posix_acl *
posix_acl_from_xattr(const void *value, size_t size)
{
posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
int count;
struct posix_acl *acl;
struct posix_acl_entry *acl_e;
if (!value)
return NULL;
if (size < sizeof(posix_acl_xattr_header))
return ERR_PTR(-EINVAL);
if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
return ERR_PTR(-EOPNOTSUPP);
count = posix_acl_xattr_count(size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
return NULL;
acl = posix_acl_alloc(count, GFP_NOFS);
if (!acl)
return ERR_PTR(-ENOMEM);
acl_e = acl->a_entries;
for (end = entry + count; entry != end; acl_e++, entry++) {
acl_e->e_tag = le16_to_cpu(entry->e_tag);
acl_e->e_perm = le16_to_cpu(entry->e_perm);
switch(acl_e->e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
acl_e->e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
case ACL_GROUP:
acl_e->e_id = le32_to_cpu(entry->e_id);
break;
default:
goto fail;
}
}
return acl;
fail:
posix_acl_release(acl);
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL (posix_acl_from_xattr);
/*
* Convert from in-memory to extended attribute representation.
*/
int
posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size)
{
posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
posix_acl_xattr_entry *ext_entry = ext_acl->a_entries;
int real_size, n;
real_size = posix_acl_xattr_size(acl->a_count);
if (!buffer)
return real_size;
if (real_size > size)
return -ERANGE;
ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
for (n=0; n < acl->a_count; n++, ext_entry++) {
ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id);
}
return real_size;
}
EXPORT_SYMBOL (posix_acl_to_xattr);

View File

@ -278,12 +278,12 @@ xfs_inherit_acl(struct inode *inode, struct posix_acl *acl)
goto out; goto out;
} }
error = posix_acl_create(&acl, GFP_KERNEL, &mode); error = __posix_acl_create(&acl, GFP_KERNEL, &mode);
if (error < 0) if (error < 0)
return error; return error;
/* /*
* If posix_acl_create returns a positive value we need to * If __posix_acl_create returns a positive value we need to
* inherit a permission that can't be represented using the Unix * inherit a permission that can't be represented using the Unix
* mode bits and we actually need to set an ACL. * mode bits and we actually need to set an ACL.
*/ */
@ -315,7 +315,7 @@ xfs_acl_chmod(struct inode *inode)
if (IS_ERR(acl) || !acl) if (IS_ERR(acl) || !acl)
return PTR_ERR(acl); return PTR_ERR(acl);
error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); error = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
if (error) if (error)
return error; return error;
@ -337,7 +337,7 @@ xfs_xattr_acl_get(struct dentry *dentry, const char *name,
if (acl == NULL) if (acl == NULL)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, value, size); error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
posix_acl_release(acl); posix_acl_release(acl);
return error; return error;
@ -361,7 +361,7 @@ xfs_xattr_acl_set(struct dentry *dentry, const char *name,
if (!value) if (!value)
goto set_acl; goto set_acl;
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (!acl) { if (!acl) {
/* /*
* acl_set_file(3) may request that we set default ACLs with * acl_set_file(3) may request that we set default ACLs with

View File

@ -11,3 +11,4 @@ header-y += drm/
header-y += xen/ header-y += xen/
header-y += scsi/ header-y += scsi/
header-y += media/ header-y += media/
header-y += uapi/linux/memfd.h

View File

@ -17,6 +17,7 @@
#include <linux/key.h> #include <linux/key.h>
#include <linux/selinux.h> #include <linux/selinux.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/uidgid.h>
struct user_struct; struct user_struct;
struct cred; struct cred;

View File

@ -23,6 +23,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/uidgid.h>
#include <asm/device.h> #include <asm/device.h>
struct device; struct device;

View File

@ -27,6 +27,21 @@
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) #define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) #define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
/*
* Set/Get seals
*/
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
/*
* Types of seals
*/
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#define F_SEAL_WRITE 0x0008 /* prevent writes */
/* (1U << 31) is reserved for signed error codes */
/* /*
* Types of directory notifications that may be requested. * Types of directory notifications that may be requested.
*/ */

View File

@ -428,6 +428,7 @@ struct fscrypt_policy {
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/shrinker.h> #include <linux/shrinker.h>
#include <linux/migrate_mode.h> #include <linux/migrate_mode.h>
#include <linux/uidgid.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
@ -683,7 +684,7 @@ struct address_space {
struct inode *host; /* owner: inode, block_device */ struct inode *host; /* owner: inode, block_device */
struct radix_tree_root page_tree; /* radix tree of all pages */ struct radix_tree_root page_tree; /* radix tree of all pages */
spinlock_t tree_lock; /* and lock protecting it */ spinlock_t tree_lock; /* and lock protecting it */
unsigned int i_mmap_writable;/* count VM_SHARED mappings */ atomic_t i_mmap_writable;/* count VM_SHARED mappings */
struct prio_tree_root i_mmap; /* tree of private and shared mappings */ struct prio_tree_root i_mmap; /* tree of private and shared mappings */
struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
struct mutex i_mmap_mutex; /* protect tree, count, list */ struct mutex i_mmap_mutex; /* protect tree, count, list */
@ -765,10 +766,35 @@ static inline int mapping_mapped(struct address_space *mapping)
* Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap_pgoff * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap_pgoff
* marks vma as VM_SHARED if it is shared, and the file was opened for * marks vma as VM_SHARED if it is shared, and the file was opened for
* writing i.e. vma may be mprotected writable even if now readonly. * writing i.e. vma may be mprotected writable even if now readonly.
*
* If i_mmap_writable is negative, no new writable mappings are allowed. You
* can only deny writable mappings, if none exists right now.
*/ */
static inline int mapping_writably_mapped(struct address_space *mapping) static inline int mapping_writably_mapped(struct address_space *mapping)
{ {
return mapping->i_mmap_writable != 0; return atomic_read(&mapping->i_mmap_writable) > 0;
}
static inline int mapping_map_writable(struct address_space *mapping)
{
return atomic_inc_unless_negative(&mapping->i_mmap_writable) ?
0 : -EPERM;
}
static inline void mapping_unmap_writable(struct address_space *mapping)
{
atomic_dec(&mapping->i_mmap_writable);
}
static inline int mapping_deny_writable(struct address_space *mapping)
{
return atomic_dec_unless_positive(&mapping->i_mmap_writable) ?
0 : -EBUSY;
}
static inline void mapping_allow_writable(struct address_space *mapping)
{
atomic_inc(&mapping->i_mmap_writable);
} }
/* /*
@ -1708,10 +1734,10 @@ struct inode_operations {
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *); int (*removexattr) (struct dentry *, const char *);
void (*truncate_range)(struct inode *, loff_t, loff_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len); u64 len);
int (*update_time)(struct inode *, struct timespec *, int); int (*update_time)(struct inode *, struct timespec *, int);
int (*set_acl)(struct inode *, struct posix_acl *, int);
int (*atomic_open)(struct inode *, struct dentry *, int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag, struct file *, unsigned open_flag,
umode_t create_mode, int *opened); umode_t create_mode, int *opened);

View File

@ -1,14 +0,0 @@
#ifndef LINUX_GENERIC_ACL_H
#define LINUX_GENERIC_ACL_H
#include <linux/xattr.h>
struct inode;
extern const struct xattr_handler generic_acl_access_handler;
extern const struct xattr_handler generic_acl_default_handler;
int generic_acl_init(struct inode *, struct inode *);
int generic_acl_chmod(struct inode *);
#endif /* LINUX_GENERIC_ACL_H */

View File

@ -79,6 +79,7 @@ struct ipc_kludge {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/uidgid.h>
#define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */

View File

@ -24,6 +24,7 @@
#include <linux/atomic.h> #include <linux/atomic.h>
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/uidgid.h>
/* key handle serial number */ /* key handle serial number */
typedef int32_t key_serial_t; typedef int32_t key_serial_t;

View File

@ -879,8 +879,6 @@ extern void pagefault_out_of_memory(void);
extern void show_free_areas(unsigned int flags); extern void show_free_areas(unsigned int flags);
extern bool skip_free_areas_node(unsigned int flags, int nid); extern bool skip_free_areas_node(unsigned int flags, int nid);
int shmem_lock(struct file *file, int lock, struct user_struct *user);
struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags);
void shmem_set_file(struct vm_area_struct *vma, struct file *file); void shmem_set_file(struct vm_area_struct *vma, struct file *file);
int shmem_zero_setup(struct vm_area_struct *); int shmem_zero_setup(struct vm_area_struct *);
@ -963,11 +961,9 @@ extern void truncate_pagecache(struct inode *inode, loff_t old, loff_t new);
extern void truncate_setsize(struct inode *inode, loff_t newsize); extern void truncate_setsize(struct inode *inode, loff_t newsize);
void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to); void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to);
extern int vmtruncate(struct inode *inode, loff_t offset); extern int vmtruncate(struct inode *inode, loff_t offset);
extern int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end);
void truncate_pagecache_range(struct inode *inode, loff_t offset, loff_t end); void truncate_pagecache_range(struct inode *inode, loff_t offset, loff_t end);
int truncate_inode_page(struct address_space *mapping, struct page *page); int truncate_inode_page(struct address_space *mapping, struct page *page);
int generic_error_remove_page(struct address_space *mapping, struct page *page); int generic_error_remove_page(struct address_space *mapping, struct page *page);
int invalidate_inode_page(struct page *page); int invalidate_inode_page(struct page *page);
#ifdef CONFIG_MMU #ifdef CONFIG_MMU

View File

@ -167,6 +167,7 @@ enum nfs4_acl_whotype {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/list.h> #include <linux/list.h>
#include <linux/uidgid.h>
struct nfs4_ace { struct nfs4_ace {
uint32_t type; uint32_t type;

View File

@ -37,6 +37,7 @@
#ifndef NFS_IDMAP_H #ifndef NFS_IDMAP_H
#define NFS_IDMAP_H #define NFS_IDMAP_H
#include <linux/uidgid.h>
#include <linux/types.h> #include <linux/types.h>
/* XXX from bits/utmp.h */ /* XXX from bits/utmp.h */

View File

@ -36,7 +36,13 @@
struct posix_acl_entry { struct posix_acl_entry {
short e_tag; short e_tag;
unsigned short e_perm; unsigned short e_perm;
unsigned int e_id; union {
kuid_t e_uid;
kgid_t e_gid;
#ifndef CONFIG_UIDGID_STRICT_TYPE_CHECKS
unsigned int e_id;
#endif
};
}; };
struct posix_acl { struct posix_acl {
@ -82,14 +88,21 @@ extern int posix_acl_valid(const struct posix_acl *);
extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); extern int posix_acl_permission(struct inode *, const struct posix_acl *, int);
extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t); extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t);
extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *); extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *);
extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *); extern int __posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
extern int posix_acl_chmod(struct posix_acl **, gfp_t, umode_t); extern int __posix_acl_chmod(struct posix_acl **, gfp_t, umode_t);
extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **); extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **);
extern struct posix_acl *get_posix_acl(struct inode *, int); extern struct posix_acl *get_posix_acl(struct inode *, int);
extern int set_posix_acl(struct inode *, int, struct posix_acl *); extern int set_posix_acl(struct inode *, int, struct posix_acl *);
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
extern int posix_acl_chmod(struct inode *, umode_t);
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
struct posix_acl **);
extern int simple_set_acl(struct inode *, struct posix_acl *, int);
extern int simple_acl_create(struct inode *, struct inode *);
static inline struct posix_acl **acl_by_type(struct inode *inode, int type) static inline struct posix_acl **acl_by_type(struct inode *inode, int type)
{ {
switch (type) { switch (type) {
@ -160,14 +173,36 @@ static inline void forget_all_cached_acls(struct inode *inode)
if (old_default != ACL_NOT_CACHED) if (old_default != ACL_NOT_CACHED)
posix_acl_release(old_default); posix_acl_release(old_default);
} }
#endif
static inline void cache_no_acl(struct inode *inode) static inline void cache_no_acl(struct inode *inode)
{ {
#ifdef CONFIG_FS_POSIX_ACL
inode->i_acl = NULL; inode->i_acl = NULL;
inode->i_default_acl = NULL; inode->i_default_acl = NULL;
#endif }
#else
static inline int posix_acl_chmod(struct inode *inode, umode_t mode)
{
return 0;
} }
#define simple_set_acl NULL
static inline int simple_acl_create(struct inode *dir, struct inode *inode)
{
return 0;
}
static inline void cache_no_acl(struct inode *inode)
{
}
static inline int posix_acl_create(struct inode *inode, umode_t *mode,
struct posix_acl **default_acl, struct posix_acl **acl)
{
*default_acl = *acl = NULL;
return 0;
}
#endif /* CONFIG_FS_POSIX_ACL */
struct posix_acl *get_acl(struct inode *inode, int type);
#endif /* __LINUX_POSIX_ACL_H */ #endif /* __LINUX_POSIX_ACL_H */

View File

@ -52,7 +52,24 @@ posix_acl_xattr_count(size_t size)
return size / sizeof(posix_acl_xattr_entry); return size / sizeof(posix_acl_xattr_entry);
} }
struct posix_acl *posix_acl_from_xattr(const void *value, size_t size); #ifdef CONFIG_FS_POSIX_ACL
int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size); void posix_acl_fix_xattr_from_user(void *value, size_t size);
void posix_acl_fix_xattr_to_user(void *value, size_t size);
#else
static inline void posix_acl_fix_xattr_from_user(void *value, size_t size)
{
}
static inline void posix_acl_fix_xattr_to_user(void *value, size_t size)
{
}
#endif
struct posix_acl *posix_acl_from_xattr(struct user_namespace *user_ns,
const void *value, size_t size);
int posix_acl_to_xattr(struct user_namespace *user_ns,
const struct posix_acl *acl, void *buffer, size_t size);
extern const struct xattr_handler posix_acl_access_xattr_handler;
extern const struct xattr_handler posix_acl_default_xattr_handler;
#endif /* _POSIX_ACL_XATTR_H */ #endif /* _POSIX_ACL_XATTR_H */

View File

@ -35,6 +35,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/uidgid.h>
#define __DQUOT_VERSION__ "dquot_6.5.2" #define __DQUOT_VERSION__ "dquot_6.5.2"

View File

@ -231,6 +231,7 @@ unsigned long radix_tree_next_hole(struct radix_tree_root *root,
unsigned long radix_tree_prev_hole(struct radix_tree_root *root, unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
unsigned long index, unsigned long max_scan); unsigned long index, unsigned long max_scan);
int radix_tree_preload(gfp_t gfp_mask); int radix_tree_preload(gfp_t gfp_mask);
int radix_tree_maybe_preload(gfp_t gfp_mask);
void radix_tree_init(void); void radix_tree_init(void);
void *radix_tree_tag_set(struct radix_tree_root *root, void *radix_tree_tag_set(struct radix_tree_root *root,
unsigned long index, unsigned int tag); unsigned long index, unsigned int tag);

View File

@ -90,6 +90,7 @@ struct sched_param {
#include <linux/latencytop.h> #include <linux/latencytop.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/llist.h> #include <linux/llist.h>
#include <linux/uidgid.h>
#include <asm/processor.h> #include <asm/processor.h>
@ -2721,6 +2722,15 @@ extern int __cond_resched_softirq(void);
__cond_resched_softirq(); \ __cond_resched_softirq(); \
}) })
static inline void cond_resched_rcu(void)
{
#if defined(CONFIG_DEBUG_ATOMIC_SLEEP) || !defined(CONFIG_PREEMPT_RCU)
rcu_read_unlock();
cond_resched();
rcu_read_lock();
#endif
}
/* /*
* Does a critical section need to be broken due to another * Does a critical section need to be broken due to another
* task waiting?: (technically does not depend on CONFIG_PREEMPT, * task waiting?: (technically does not depend on CONFIG_PREEMPT,

View File

@ -1,15 +1,18 @@
#ifndef __SHMEM_FS_H #ifndef __SHMEM_FS_H
#define __SHMEM_FS_H #define __SHMEM_FS_H
#include <linux/file.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/mempolicy.h> #include <linux/mempolicy.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/percpu_counter.h> #include <linux/percpu_counter.h>
#include <linux/xattr.h>
/* inode in-kernel data */ /* inode in-kernel data */
struct shmem_inode_info { struct shmem_inode_info {
spinlock_t lock; spinlock_t lock;
unsigned int seals; /* shmem seals */
unsigned long flags; unsigned long flags;
unsigned long alloced; /* data pages alloced to file */ unsigned long alloced; /* data pages alloced to file */
union { union {
@ -18,7 +21,7 @@ struct shmem_inode_info {
}; };
struct shared_policy policy; /* NUMA memory alloc policy */ struct shared_policy policy; /* NUMA memory alloc policy */
struct list_head swaplist; /* chain of maybes on swap */ struct list_head swaplist; /* chain of maybes on swap */
struct list_head xattr_list; /* list of shmem_xattr */ struct simple_xattrs xattrs; /* list of xattrs */
struct inode vfs_inode; struct inode vfs_inode;
}; };
@ -46,6 +49,8 @@ extern int shmem_init(void);
extern int shmem_fill_super(struct super_block *sb, void *data, int silent); extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
extern struct file *shmem_file_setup(const char *name, extern struct file *shmem_file_setup(const char *name,
loff_t size, unsigned long flags); loff_t size, unsigned long flags);
extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
unsigned long flags);
extern int shmem_zero_setup(struct vm_area_struct *); extern int shmem_zero_setup(struct vm_area_struct *);
extern int shmem_lock(struct file *file, int lock, struct user_struct *user); extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
extern void shmem_unlock_mapping(struct address_space *mapping); extern void shmem_unlock_mapping(struct address_space *mapping);
@ -61,4 +66,19 @@ static inline struct page *shmem_read_mapping_page(
mapping_gfp_mask(mapping)); mapping_gfp_mask(mapping));
} }
#ifdef CONFIG_TMPFS
extern int shmem_add_seals(struct file *file, unsigned int seals);
extern int shmem_get_seals(struct file *file);
extern long shmem_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
#else
static inline long shmem_fcntl(struct file *f, unsigned int c, unsigned long a)
{
return -EINVAL;
}
#endif
#endif #endif

View File

@ -58,6 +58,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/uidgid.h>
struct kstat { struct kstat {
u64 ino; u64 ino;

View File

@ -17,6 +17,7 @@
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/uidgid.h>
/* size of the nodename buffer */ /* size of the nodename buffer */
#define UNX_MAXNODENAME 32 #define UNX_MAXNODENAME 32

View File

@ -59,7 +59,9 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/spinlock.h>
struct inode; struct inode;
struct dentry; struct dentry;
@ -96,6 +98,52 @@ ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
char **xattr_value, size_t size, gfp_t flags); char **xattr_value, size_t size, gfp_t flags);
int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
const char *value, size_t size, gfp_t flags); const char *value, size_t size, gfp_t flags);
struct simple_xattrs {
struct list_head head;
spinlock_t lock;
};
struct simple_xattr {
struct list_head list;
char *name;
size_t size;
char value[0];
};
/*
* initialize the simple_xattrs structure
*/
static inline void simple_xattrs_init(struct simple_xattrs *xattrs)
{
INIT_LIST_HEAD(&xattrs->head);
spin_lock_init(&xattrs->lock);
}
/*
* free all the xattrs
*/
static inline void simple_xattrs_free(struct simple_xattrs *xattrs)
{
struct simple_xattr *xattr, *node;
list_for_each_entry_safe(xattr, node, &xattrs->head, list) {
kfree(xattr->name);
kfree(xattr);
}
}
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size);
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
void *buffer, size_t size);
int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
const void *value, size_t size, int flags);
int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name);
ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer,
size_t size);
void simple_xattr_list_add(struct simple_xattrs *xattrs,
struct simple_xattr *new_xattr);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_XATTR_H */ #endif /* _LINUX_XATTR_H */

View File

@ -5,6 +5,7 @@
#ifndef __NETNS_IPV4_H__ #ifndef __NETNS_IPV4_H__
#define __NETNS_IPV4_H__ #define __NETNS_IPV4_H__
#include <linux/uidgid.h>
#include <net/inet_frag.h> #include <net/inet_frag.h>
struct ctl_table_header; struct ctl_table_header;

View File

@ -416,7 +416,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
atomic_dec(&inode->i_writecount); atomic_dec(&inode->i_writecount);
mutex_lock(&mapping->i_mmap_mutex); mutex_lock(&mapping->i_mmap_mutex);
if (tmp->vm_flags & VM_SHARED) if (tmp->vm_flags & VM_SHARED)
mapping->i_mmap_writable++; atomic_inc(&mapping->i_mmap_writable);
flush_dcache_mmap_lock(mapping); flush_dcache_mmap_lock(mapping);
/* insert tmp into the share list, just after mpnt */ /* insert tmp into the share list, just after mpnt */
vma_prio_tree_add(tmp, mpnt); vma_prio_tree_add(tmp, mpnt);

View File

@ -32,6 +32,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/hardirq.h> /* in_interrupt() */
#ifdef __KERNEL__ #ifdef __KERNEL__
@ -194,7 +195,12 @@ radix_tree_node_alloc(struct radix_tree_root *root)
struct radix_tree_node *ret = NULL; struct radix_tree_node *ret = NULL;
gfp_t gfp_mask = root_gfp_mask(root); gfp_t gfp_mask = root_gfp_mask(root);
if (!(gfp_mask & __GFP_WAIT)) { /*
* Preload code isn't irq safe and it doesn't make sence to use
* preloading in the interrupt anyway as all the allocations have to
* be atomic. So just do normal allocation when in interrupt.
*/
if (!(gfp_mask & __GFP_WAIT) && !in_interrupt()) {
struct radix_tree_preload *rtp; struct radix_tree_preload *rtp;
/* /*
@ -251,7 +257,7 @@ radix_tree_node_free(struct radix_tree_node *node)
* To make use of this facility, the radix tree must be initialised without * To make use of this facility, the radix tree must be initialised without
* __GFP_WAIT being passed to INIT_RADIX_TREE(). * __GFP_WAIT being passed to INIT_RADIX_TREE().
*/ */
int radix_tree_preload(gfp_t gfp_mask) static int __radix_tree_preload(gfp_t gfp_mask)
{ {
struct radix_tree_preload *rtp; struct radix_tree_preload *rtp;
struct radix_tree_node *node; struct radix_tree_node *node;
@ -275,8 +281,39 @@ int radix_tree_preload(gfp_t gfp_mask)
out: out:
return ret; return ret;
} }
/*
* Load up this CPU's radix_tree_node buffer with sufficient objects to
* ensure that the addition of a single element in the tree cannot fail. On
* success, return zero, with preemption disabled. On error, return -ENOMEM
* with preemption not disabled.
*
* To make use of this facility, the radix tree must be initialised without
* __GFP_WAIT being passed to INIT_RADIX_TREE().
*/
int radix_tree_preload(gfp_t gfp_mask)
{
/* Warn on non-sensical use... */
WARN_ON_ONCE(!(gfp_mask & __GFP_WAIT));
return __radix_tree_preload(gfp_mask);
}
EXPORT_SYMBOL(radix_tree_preload); EXPORT_SYMBOL(radix_tree_preload);
/*
* The same as above function, except we don't guarantee preloading happens.
* We do it, if we decide it helps. On success, return zero with preemption
* disabled. On error, return -ENOMEM with preemption not disabled.
*/
int radix_tree_maybe_preload(gfp_t gfp_mask)
{
if (gfp_mask & __GFP_WAIT)
return __radix_tree_preload(gfp_mask);
/* Preloading doesn't help anything with this gfp mask, skip it */
preempt_disable();
return 0;
}
EXPORT_SYMBOL(radix_tree_maybe_preload);
/* /*
* Return the maximum key which can be store into a * Return the maximum key which can be store into a
* radix tree with height HEIGHT. * radix tree with height HEIGHT.

View File

@ -453,7 +453,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
if (error) if (error)
goto out; goto out;
error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); error = radix_tree_maybe_preload(gfp_mask & ~__GFP_HIGHMEM);
if (error == 0) { if (error == 0) {
page_cache_get(page); page_cache_get(page);
page->mapping = mapping; page->mapping = mapping;

View File

@ -11,8 +11,10 @@
#include <linux/mempolicy.h> #include <linux/mempolicy.h>
#include <linux/page-isolation.h> #include <linux/page-isolation.h>
#include <linux/hugetlb.h> #include <linux/hugetlb.h>
#include <linux/falloc.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/ksm.h> #include <linux/ksm.h>
#include <linux/fs.h>
#include <linux/file.h> #include <linux/file.h>
/* /*
@ -202,8 +204,7 @@ static long madvise_remove(struct vm_area_struct *vma,
struct vm_area_struct **prev, struct vm_area_struct **prev,
unsigned long start, unsigned long end) unsigned long start, unsigned long end)
{ {
struct address_space *mapping; loff_t offset;
loff_t offset, endoff;
int error; int error;
struct file *f; struct file *f;
@ -221,22 +222,20 @@ static long madvise_remove(struct vm_area_struct *vma,
if ((vma->vm_flags & (VM_SHARED|VM_WRITE)) != (VM_SHARED|VM_WRITE)) if ((vma->vm_flags & (VM_SHARED|VM_WRITE)) != (VM_SHARED|VM_WRITE))
return -EACCES; return -EACCES;
mapping = vma->vm_file->f_mapping;
offset = (loff_t)(start - vma->vm_start) offset = (loff_t)(start - vma->vm_start)
+ ((loff_t)vma->vm_pgoff << PAGE_SHIFT); + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
endoff = (loff_t)(end - vma->vm_start - 1)
+ ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
/* /*
* vmtruncate_range may need to take i_mutex. We need to * Filesystem's fallocate may need to take i_mutex. We need to
* explicitly grab a reference because the vma (and hence the * explicitly grab a reference because the vma (and hence the
* vma's reference to the file) can go away as soon as we drop * vma's reference to the file) can go away as soon as we drop
* mmap_sem. * mmap_sem.
*/ */
get_file(f); get_file(f);
up_read(&current->mm->mmap_sem); up_read(&current->mm->mmap_sem);
error = vmtruncate_range(mapping->host, offset, endoff); error = do_fallocate(f,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
offset, end - start);
fput(f); fput(f);
down_read(&current->mm->mmap_sem); down_read(&current->mm->mmap_sem);
return error; return error;

View File

@ -3372,7 +3372,7 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
void mem_cgroup_replace_page_cache(struct page *oldpage, void mem_cgroup_replace_page_cache(struct page *oldpage,
struct page *newpage) struct page *newpage)
{ {
struct mem_cgroup *memcg; struct mem_cgroup *memcg = NULL;
struct page_cgroup *pc; struct page_cgroup *pc;
enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE; enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
@ -3382,11 +3382,20 @@ void mem_cgroup_replace_page_cache(struct page *oldpage,
pc = lookup_page_cgroup(oldpage); pc = lookup_page_cgroup(oldpage);
/* fix accounting on old pages */ /* fix accounting on old pages */
lock_page_cgroup(pc); lock_page_cgroup(pc);
memcg = pc->mem_cgroup; if (PageCgroupUsed(pc)) {
mem_cgroup_charge_statistics(memcg, false, -1); memcg = pc->mem_cgroup;
ClearPageCgroupUsed(pc); mem_cgroup_charge_statistics(memcg, false, -1);
ClearPageCgroupUsed(pc);
}
unlock_page_cgroup(pc); unlock_page_cgroup(pc);
/*
* When called from shmem_replace_page(), in some cases the
* oldpage has already been charged, and in some cases not.
*/
if (!memcg)
return;
if (PageSwapBacked(oldpage)) if (PageSwapBacked(oldpage))
type = MEM_CGROUP_CHARGE_TYPE_SHMEM; type = MEM_CGROUP_CHARGE_TYPE_SHMEM;

View File

@ -207,7 +207,7 @@ static void __remove_shared_vm_struct(struct vm_area_struct *vma,
if (vma->vm_flags & VM_DENYWRITE) if (vma->vm_flags & VM_DENYWRITE)
atomic_inc(&file->f_path.dentry->d_inode->i_writecount); atomic_inc(&file->f_path.dentry->d_inode->i_writecount);
if (vma->vm_flags & VM_SHARED) if (vma->vm_flags & VM_SHARED)
mapping->i_mmap_writable--; mapping_unmap_writable(mapping);
flush_dcache_mmap_lock(mapping); flush_dcache_mmap_lock(mapping);
if (unlikely(vma->vm_flags & VM_NONLINEAR)) if (unlikely(vma->vm_flags & VM_NONLINEAR))
@ -453,7 +453,7 @@ static void __vma_link_file(struct vm_area_struct *vma)
if (vma->vm_flags & VM_DENYWRITE) if (vma->vm_flags & VM_DENYWRITE)
atomic_dec(&file->f_path.dentry->d_inode->i_writecount); atomic_dec(&file->f_path.dentry->d_inode->i_writecount);
if (vma->vm_flags & VM_SHARED) if (vma->vm_flags & VM_SHARED)
mapping->i_mmap_writable++; atomic_inc(&mapping->i_mmap_writable);
flush_dcache_mmap_lock(mapping); flush_dcache_mmap_lock(mapping);
if (unlikely(vma->vm_flags & VM_NONLINEAR)) if (unlikely(vma->vm_flags & VM_NONLINEAR))
@ -1290,11 +1290,9 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev; struct vm_area_struct *vma, *prev;
int correct_wcount = 0;
int error; int error;
struct rb_node **rb_link, *rb_parent; struct rb_node **rb_link, *rb_parent;
unsigned long charged = 0; unsigned long charged = 0;
struct inode *inode = file ? file->f_path.dentry->d_inode : NULL;
/* Check against address space limit. */ /* Check against address space limit. */
if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {
@ -1383,8 +1381,18 @@ munmap_back:
error = deny_write_access(file); error = deny_write_access(file);
if (error) if (error)
goto free_vma; goto free_vma;
correct_wcount = 1;
} }
if (vm_flags & VM_SHARED) {
error = mapping_map_writable(file->f_mapping);
if (error)
goto allow_write_and_free_vma;
}
/* ->mmap() can change vma->vm_file, but must guarantee that
* vma_link() below can deny write-access if VM_DENYWRITE is set
* and map writably if VM_SHARED is set. This usually means the
* new file must not have been exposed to user-space, yet.
*/
vma->vm_file = file; vma->vm_file = file;
get_file(file); get_file(file);
error = file->f_op->mmap(file, vma); error = file->f_op->mmap(file, vma);
@ -1425,11 +1433,14 @@ munmap_back:
} }
vma_link(mm, vma, prev, rb_link, rb_parent); vma_link(mm, vma, prev, rb_link, rb_parent);
file = vma->vm_file;
/* Once vma denies write, undo our temporary denial count */ /* Once vma denies write, undo our temporary denial count */
if (correct_wcount) if (file) {
atomic_inc(&inode->i_writecount); if (vm_flags & VM_SHARED)
mapping_unmap_writable(file->f_mapping);
if (vm_flags & VM_DENYWRITE)
allow_write_access(file);
}
file = vma->vm_file;
out: out:
perf_event_mmap(vma); perf_event_mmap(vma);
@ -1443,14 +1454,17 @@ out:
return addr; return addr;
unmap_and_free_vma: unmap_and_free_vma:
if (correct_wcount)
atomic_inc(&inode->i_writecount);
vma->vm_file = NULL; vma->vm_file = NULL;
fput(file); fput(file);
/* Undo any partial mapping done by a device driver. */ /* Undo any partial mapping done by a device driver. */
unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
charged = 0; charged = 0;
if (vm_flags & VM_SHARED)
mapping_unmap_writable(file->f_mapping);
allow_write_and_free_vma:
if (vm_flags & VM_DENYWRITE)
allow_write_access(file);
free_vma: free_vma:
kmem_cache_free(vm_area_cachep, vma); kmem_cache_free(vm_area_cachep, vma);
unacct_error: unacct_error:

1246
mm/shmem.c

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@ static struct backing_dev_info swap_backing_dev_info = {
struct address_space swapper_spaces[MAX_SWAPFILES] = { struct address_space swapper_spaces[MAX_SWAPFILES] = {
[0 ... MAX_SWAPFILES - 1] = { [0 ... MAX_SWAPFILES - 1] = {
.page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN),
.i_mmap_writable = ATOMIC_INIT(0),
.a_ops = &swap_aops, .a_ops = &swap_aops,
.backing_dev_info = &swap_backing_dev_info, .backing_dev_info = &swap_backing_dev_info,
} }
@ -120,7 +121,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
{ {
int error; int error;
error = radix_tree_preload(gfp_mask); error = radix_tree_maybe_preload(gfp_mask);
if (!error) { if (!error) {
error = __add_to_swap_cache(page, entry); error = __add_to_swap_cache(page, entry);
radix_tree_preload_end(); radix_tree_preload_end();
@ -326,7 +327,7 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
/* /*
* call radix_tree_preload() while we can wait. * call radix_tree_preload() while we can wait.
*/ */
err = radix_tree_preload(gfp_mask & GFP_KERNEL); err = radix_tree_maybe_preload(gfp_mask & GFP_KERNEL);
if (err) if (err)
break; break;