Merge 7ea65c89d8 ("Merge tag 'vfs-6.9.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs") into android-mainline
Steps on the way to v6.9-rc1 Signed-off-by: Lee Jones <joneslee@google.com> Change-Id: I39dd3bdcb4b032fcd59ec6368c78c0d67387a724
This commit is contained in:
@@ -116,7 +116,7 @@ before and after the reference count increment. This pattern can be seen
|
||||
in get_file_rcu() and __files_get_rcu().
|
||||
|
||||
In addition, it isn't possible to access or check fields in struct file
|
||||
without first aqcuiring a reference on it under rcu lookup. Not doing
|
||||
without first acquiring a reference on it under rcu lookup. Not doing
|
||||
that was always very dodgy and it was only usable for non-pointer data
|
||||
in struct file. With SLAB_TYPESAFE_BY_RCU it is necessary that callers
|
||||
either first acquire a reference or they must hold the files_lock of the
|
||||
|
||||
@@ -29,7 +29,7 @@ prototypes::
|
||||
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
|
||||
struct vfsmount *(*d_automount)(struct path *path);
|
||||
int (*d_manage)(const struct path *, bool);
|
||||
struct dentry *(*d_real)(struct dentry *, const struct inode *);
|
||||
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
||||
|
||||
locking rules:
|
||||
|
||||
|
||||
@@ -1264,7 +1264,7 @@ defined:
|
||||
char *(*d_dname)(struct dentry *, char *, int);
|
||||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(const struct path *, bool);
|
||||
struct dentry *(*d_real)(struct dentry *, const struct inode *);
|
||||
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
||||
};
|
||||
|
||||
``d_revalidate``
|
||||
@@ -1419,16 +1419,14 @@ defined:
|
||||
the dentry being transited from.
|
||||
|
||||
``d_real``
|
||||
overlay/union type filesystems implement this method to return
|
||||
one of the underlying dentries hidden by the overlay. It is
|
||||
used in two different modes:
|
||||
overlay/union type filesystems implement this method to return one
|
||||
of the underlying dentries of a regular file hidden by the overlay.
|
||||
|
||||
Called from file_dentry() it returns the real dentry matching
|
||||
the inode argument. The real dentry may be from a lower layer
|
||||
already copied up, but still referenced from the file. This
|
||||
mode is selected with a non-NULL inode argument.
|
||||
The 'type' argument takes the values D_REAL_DATA or D_REAL_METADATA
|
||||
for returning the real underlying dentry that refers to the inode
|
||||
hosting the file's data or metadata respectively.
|
||||
|
||||
With NULL inode the topmost real underlying dentry is returned.
|
||||
For non-regular files, the 'dentry' argument is returned.
|
||||
|
||||
Each dentry has a pointer to its parent dentry, as well as a hash list
|
||||
of child dentries. Child dentries are basically like files in a
|
||||
|
||||
@@ -352,7 +352,7 @@ int may_setattr(struct mnt_idmap *idmap, struct inode *inode,
|
||||
EXPORT_SYMBOL(may_setattr);
|
||||
|
||||
/**
|
||||
* notify_change - modify attributes of a filesytem object
|
||||
* notify_change - modify attributes of a filesystem object
|
||||
* @idmap: idmap of the mount the inode was found from
|
||||
* @dentry: object affected
|
||||
* @attr: new attributes
|
||||
|
||||
+1
-3
@@ -325,9 +325,7 @@ EXPORT_SYMBOL_GPL(backing_file_mmap);
|
||||
|
||||
static int __init backing_aio_init(void)
|
||||
{
|
||||
backing_aio_cachep = kmem_cache_create("backing_aio",
|
||||
sizeof(struct backing_aio),
|
||||
0, SLAB_HWCACHE_ALIGN, NULL);
|
||||
backing_aio_cachep = KMEM_CACHE(backing_aio, SLAB_HWCACHE_ALIGN);
|
||||
if (!backing_aio_cachep)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
+3
-7
@@ -464,7 +464,7 @@ EXPORT_SYMBOL(mark_buffer_async_write);
|
||||
* a successful fsync(). For example, ext2 indirect blocks need to be
|
||||
* written back and waited upon before fsync() returns.
|
||||
*
|
||||
* The functions mark_buffer_inode_dirty(), fsync_inode_buffers(),
|
||||
* The functions mark_buffer_dirty_inode(), fsync_inode_buffers(),
|
||||
* inode_has_buffers() and invalidate_inode_buffers() are provided for the
|
||||
* management of a list of dependent buffers at ->i_mapping->i_private_list.
|
||||
*
|
||||
@@ -3121,12 +3121,8 @@ void __init buffer_init(void)
|
||||
unsigned long nrpages;
|
||||
int ret;
|
||||
|
||||
bh_cachep = kmem_cache_create("buffer_head",
|
||||
sizeof(struct buffer_head), 0,
|
||||
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
|
||||
SLAB_MEM_SPREAD),
|
||||
NULL);
|
||||
|
||||
bh_cachep = KMEM_CACHE(buffer_head,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC);
|
||||
/*
|
||||
* Limit the bh occupancy to 10% of ZONE_NORMAL
|
||||
*/
|
||||
|
||||
+101
-48
@@ -24,6 +24,8 @@
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <linux/coda.h>
|
||||
@@ -87,10 +89,10 @@ void coda_destroy_inodecache(void)
|
||||
kmem_cache_destroy(coda_inode_cachep);
|
||||
}
|
||||
|
||||
static int coda_remount(struct super_block *sb, int *flags, char *data)
|
||||
static int coda_reconfigure(struct fs_context *fc)
|
||||
{
|
||||
sync_filesystem(sb);
|
||||
*flags |= SB_NOATIME;
|
||||
sync_filesystem(fc->root->d_sb);
|
||||
fc->sb_flags |= SB_NOATIME;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,78 +104,102 @@ static const struct super_operations coda_super_operations =
|
||||
.evict_inode = coda_evict_inode,
|
||||
.put_super = coda_put_super,
|
||||
.statfs = coda_statfs,
|
||||
.remount_fs = coda_remount,
|
||||
};
|
||||
|
||||
static int get_device_index(struct coda_mount_data *data)
|
||||
struct coda_fs_context {
|
||||
int idx;
|
||||
};
|
||||
|
||||
enum {
|
||||
Opt_fd,
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec coda_param_specs[] = {
|
||||
fsparam_fd ("fd", Opt_fd),
|
||||
{}
|
||||
};
|
||||
|
||||
static int coda_parse_fd(struct fs_context *fc, int fd)
|
||||
{
|
||||
struct coda_fs_context *ctx = fc->fs_private;
|
||||
struct fd f;
|
||||
struct inode *inode;
|
||||
int idx;
|
||||
|
||||
if (data == NULL) {
|
||||
pr_warn("%s: Bad mount data\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->version != CODA_MOUNT_VERSION) {
|
||||
pr_warn("%s: Bad mount version\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = fdget(data->fd);
|
||||
f = fdget(fd);
|
||||
if (!f.file)
|
||||
goto Ebadf;
|
||||
return -EBADF;
|
||||
inode = file_inode(f.file);
|
||||
if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
|
||||
fdput(f);
|
||||
goto Ebadf;
|
||||
return invalf(fc, "code: Not coda psdev");
|
||||
}
|
||||
|
||||
idx = iminor(inode);
|
||||
fdput(f);
|
||||
|
||||
if (idx < 0 || idx >= MAX_CODADEVS) {
|
||||
pr_warn("%s: Bad minor number\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return idx;
|
||||
Ebadf:
|
||||
pr_warn("%s: Bad file\n", __func__);
|
||||
return -1;
|
||||
if (idx < 0 || idx >= MAX_CODADEVS)
|
||||
return invalf(fc, "coda: Bad minor number");
|
||||
ctx->idx = idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coda_fill_super(struct super_block *sb, void *data, int silent)
|
||||
static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
struct fs_parse_result result;
|
||||
int opt;
|
||||
|
||||
opt = fs_parse(fc, coda_param_specs, param, &result);
|
||||
if (opt < 0)
|
||||
return opt;
|
||||
|
||||
switch (opt) {
|
||||
case Opt_fd:
|
||||
return coda_parse_fd(fc, result.uint_32);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse coda's binary mount data form. We ignore any errors and go with index
|
||||
* 0 if we get one for backward compatibility.
|
||||
*/
|
||||
static int coda_parse_monolithic(struct fs_context *fc, void *_data)
|
||||
{
|
||||
struct coda_mount_data *data = _data;
|
||||
|
||||
if (!data)
|
||||
return invalf(fc, "coda: Bad mount data");
|
||||
|
||||
if (data->version != CODA_MOUNT_VERSION)
|
||||
return invalf(fc, "coda: Bad mount version");
|
||||
|
||||
coda_parse_fd(fc, data->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coda_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
{
|
||||
struct coda_fs_context *ctx = fc->fs_private;
|
||||
struct inode *root = NULL;
|
||||
struct venus_comm *vc;
|
||||
struct CodaFid fid;
|
||||
int error;
|
||||
int idx;
|
||||
|
||||
if (task_active_pid_ns(current) != &init_pid_ns)
|
||||
return -EINVAL;
|
||||
infof(fc, "coda: device index: %i\n", ctx->idx);
|
||||
|
||||
idx = get_device_index((struct coda_mount_data *) data);
|
||||
|
||||
/* Ignore errors in data, for backward compatibility */
|
||||
if(idx == -1)
|
||||
idx = 0;
|
||||
|
||||
pr_info("%s: device index: %i\n", __func__, idx);
|
||||
|
||||
vc = &coda_comms[idx];
|
||||
vc = &coda_comms[ctx->idx];
|
||||
mutex_lock(&vc->vc_mutex);
|
||||
|
||||
if (!vc->vc_inuse) {
|
||||
pr_warn("%s: No pseudo device\n", __func__);
|
||||
errorf(fc, "coda: No pseudo device");
|
||||
error = -EINVAL;
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
if (vc->vc_sb) {
|
||||
pr_warn("%s: Device already mounted\n", __func__);
|
||||
errorf(fc, "coda: Device already mounted");
|
||||
error = -EBUSY;
|
||||
goto unlock_out;
|
||||
}
|
||||
@@ -313,18 +339,45 @@ static int coda_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init_coda: used by filesystems.c to register coda */
|
||||
|
||||
static struct dentry *coda_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
static int coda_get_tree(struct fs_context *fc)
|
||||
{
|
||||
return mount_nodev(fs_type, flags, data, coda_fill_super);
|
||||
if (task_active_pid_ns(current) != &init_pid_ns)
|
||||
return -EINVAL;
|
||||
|
||||
return get_tree_nodev(fc, coda_fill_super);
|
||||
}
|
||||
|
||||
static void coda_free_fc(struct fs_context *fc)
|
||||
{
|
||||
kfree(fc->fs_private);
|
||||
}
|
||||
|
||||
static const struct fs_context_operations coda_context_ops = {
|
||||
.free = coda_free_fc,
|
||||
.parse_param = coda_parse_param,
|
||||
.parse_monolithic = coda_parse_monolithic,
|
||||
.get_tree = coda_get_tree,
|
||||
.reconfigure = coda_reconfigure,
|
||||
};
|
||||
|
||||
static int coda_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
struct coda_fs_context *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(struct coda_fs_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
fc->fs_private = ctx;
|
||||
fc->ops = &coda_context_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file_system_type coda_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "coda",
|
||||
.mount = coda_mount,
|
||||
.init_fs_context = coda_init_fs_context,
|
||||
.parameters = coda_param_specs,
|
||||
.kill_sb = kill_anon_super,
|
||||
.fs_flags = FS_BINARY_MOUNTDATA,
|
||||
};
|
||||
|
||||
+1
-7
@@ -74,13 +74,7 @@ struct fscrypt_nokey_name {
|
||||
|
||||
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
||||
{
|
||||
if (str->len == 1 && str->name[0] == '.')
|
||||
return true;
|
||||
|
||||
if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return is_dot_dotdot(str->name, str->len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-10
@@ -102,11 +102,8 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
|
||||
if (err && err != -ENOENT)
|
||||
return err;
|
||||
|
||||
if (fname->is_nokey_name) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_NOKEY_NAME;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
fscrypt_prepare_dentry(dentry, fname->is_nokey_name);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
|
||||
@@ -131,12 +128,10 @@ EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
|
||||
int fscrypt_prepare_lookup_partial(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int err = fscrypt_get_encryption_info(dir, true);
|
||||
bool is_nokey_name = (!err && !fscrypt_has_encryption_key(dir));
|
||||
|
||||
fscrypt_prepare_dentry(dentry, is_nokey_name);
|
||||
|
||||
if (!err && !fscrypt_has_encryption_key(dir)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_NOKEY_NAME;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_prepare_lookup_partial);
|
||||
|
||||
+1
-1
@@ -3139,7 +3139,7 @@ static void __init dcache_init(void)
|
||||
* of the dcache.
|
||||
*/
|
||||
dentry_cache = KMEM_CACHE_USERCOPY(dentry,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_ACCOUNT,
|
||||
d_iname);
|
||||
|
||||
/* Hash may have been set up in dcache_init_early */
|
||||
|
||||
@@ -1949,16 +1949,6 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool is_dot_dotdot(const char *name, size_t name_size)
|
||||
{
|
||||
if (name_size == 1 && name[0] == '.')
|
||||
return true;
|
||||
else if (name_size == 2 && name[0] == '.' && name[1] == '.')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_decode_and_decrypt_filename - converts the encoded cipher text name to decoded plaintext
|
||||
* @plaintext_name: The plaintext name
|
||||
|
||||
+86
-32
@@ -14,19 +14,14 @@
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include "efs.h"
|
||||
#include <linux/efs_vh.h>
|
||||
#include <linux/efs_fs_sb.h>
|
||||
|
||||
static int efs_statfs(struct dentry *dentry, struct kstatfs *buf);
|
||||
static int efs_fill_super(struct super_block *s, void *d, int silent);
|
||||
|
||||
static struct dentry *efs_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return mount_bdev(fs_type, flags, dev_name, data, efs_fill_super);
|
||||
}
|
||||
static int efs_init_fs_context(struct fs_context *fc);
|
||||
|
||||
static void efs_kill_sb(struct super_block *s)
|
||||
{
|
||||
@@ -35,15 +30,6 @@ static void efs_kill_sb(struct super_block *s)
|
||||
kfree(sbi);
|
||||
}
|
||||
|
||||
static struct file_system_type efs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "efs",
|
||||
.mount = efs_mount,
|
||||
.kill_sb = efs_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
MODULE_ALIAS_FS("efs");
|
||||
|
||||
static struct pt_types sgi_pt_types[] = {
|
||||
{0x00, "SGI vh"},
|
||||
{0x01, "SGI trkrepl"},
|
||||
@@ -63,6 +49,27 @@ static struct pt_types sgi_pt_types[] = {
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
enum {
|
||||
Opt_explicit_open,
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec efs_param_spec[] = {
|
||||
fsparam_flag ("explicit-open", Opt_explicit_open),
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* File system definition and registration.
|
||||
*/
|
||||
static struct file_system_type efs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "efs",
|
||||
.kill_sb = efs_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
.init_fs_context = efs_init_fs_context,
|
||||
.parameters = efs_param_spec,
|
||||
};
|
||||
MODULE_ALIAS_FS("efs");
|
||||
|
||||
static struct kmem_cache * efs_inode_cachep;
|
||||
|
||||
@@ -91,8 +98,8 @@ static int __init init_inodecache(void)
|
||||
{
|
||||
efs_inode_cachep = kmem_cache_create("efs_inode_cache",
|
||||
sizeof(struct efs_inode_info), 0,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
|
||||
SLAB_ACCOUNT, init_once);
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
|
||||
init_once);
|
||||
if (efs_inode_cachep == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
@@ -108,18 +115,10 @@ static void destroy_inodecache(void)
|
||||
kmem_cache_destroy(efs_inode_cachep);
|
||||
}
|
||||
|
||||
static int efs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
sync_filesystem(sb);
|
||||
*flags |= SB_RDONLY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations efs_superblock_operations = {
|
||||
.alloc_inode = efs_alloc_inode,
|
||||
.free_inode = efs_free_inode,
|
||||
.statfs = efs_statfs,
|
||||
.remount_fs = efs_remount,
|
||||
};
|
||||
|
||||
static const struct export_operations efs_export_ops = {
|
||||
@@ -249,26 +248,26 @@ static int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efs_fill_super(struct super_block *s, void *d, int silent)
|
||||
static int efs_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
struct efs_sb_info *sb;
|
||||
struct buffer_head *bh;
|
||||
struct inode *root;
|
||||
|
||||
sb = kzalloc(sizeof(struct efs_sb_info), GFP_KERNEL);
|
||||
sb = kzalloc(sizeof(struct efs_sb_info), GFP_KERNEL);
|
||||
if (!sb)
|
||||
return -ENOMEM;
|
||||
s->s_fs_info = sb;
|
||||
s->s_time_min = 0;
|
||||
s->s_time_max = U32_MAX;
|
||||
|
||||
|
||||
s->s_magic = EFS_SUPER_MAGIC;
|
||||
if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) {
|
||||
pr_err("device does not support %d byte blocks\n",
|
||||
EFS_BLOCKSIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/* read the vh (volume header) block */
|
||||
bh = sb_bread(s, 0);
|
||||
|
||||
@@ -294,7 +293,7 @@ static int efs_fill_super(struct super_block *s, void *d, int silent)
|
||||
pr_err("cannot read superblock\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
|
||||
#ifdef DEBUG
|
||||
pr_warn("invalid superblock at block %u\n",
|
||||
@@ -328,6 +327,61 @@ static int efs_fill_super(struct super_block *s, void *d, int silent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void efs_free_fc(struct fs_context *fc)
|
||||
{
|
||||
kfree(fc->fs_private);
|
||||
}
|
||||
|
||||
static int efs_get_tree(struct fs_context *fc)
|
||||
{
|
||||
return get_tree_bdev(fc, efs_fill_super);
|
||||
}
|
||||
|
||||
static int efs_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
int token;
|
||||
struct fs_parse_result result;
|
||||
|
||||
token = fs_parse(fc, efs_param_spec, param, &result);
|
||||
if (token < 0)
|
||||
return token;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efs_reconfigure(struct fs_context *fc)
|
||||
{
|
||||
sync_filesystem(fc->root->d_sb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct efs_context {
|
||||
unsigned long s_mount_opts;
|
||||
};
|
||||
|
||||
static const struct fs_context_operations efs_context_opts = {
|
||||
.parse_param = efs_parse_param,
|
||||
.get_tree = efs_get_tree,
|
||||
.reconfigure = efs_reconfigure,
|
||||
.free = efs_free_fc,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up the filesystem mount context.
|
||||
*/
|
||||
static int efs_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
struct efs_context *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(struct efs_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
fc->fs_private = ctx;
|
||||
fc->ops = &efs_context_opts;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) {
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct efs_sb_info *sbi = SUPER_INFO(sb);
|
||||
|
||||
+11
-5
@@ -251,7 +251,7 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c
|
||||
ssize_t res;
|
||||
__u64 ucnt;
|
||||
|
||||
if (count < sizeof(ucnt))
|
||||
if (count != sizeof(ucnt))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ucnt, buf, sizeof(ucnt)))
|
||||
return -EFAULT;
|
||||
@@ -283,13 +283,18 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c
|
||||
static void eventfd_show_fdinfo(struct seq_file *m, struct file *f)
|
||||
{
|
||||
struct eventfd_ctx *ctx = f->private_data;
|
||||
__u64 cnt;
|
||||
|
||||
spin_lock_irq(&ctx->wqh.lock);
|
||||
seq_printf(m, "eventfd-count: %16llx\n",
|
||||
(unsigned long long)ctx->count);
|
||||
cnt = ctx->count;
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
seq_printf(m, "eventfd-id: %d\n", ctx->id);
|
||||
seq_printf(m, "eventfd-semaphore: %d\n",
|
||||
|
||||
seq_printf(m,
|
||||
"eventfd-count: %16llx\n"
|
||||
"eventfd-id: %d\n"
|
||||
"eventfd-semaphore: %d\n",
|
||||
cnt,
|
||||
ctx->id,
|
||||
!!(ctx->flags & EFD_SEMAPHORE));
|
||||
}
|
||||
#endif
|
||||
@@ -383,6 +388,7 @@ static int do_eventfd(unsigned int count, int flags)
|
||||
/* Check the EFD_* constants for consistency. */
|
||||
BUILD_BUG_ON(EFD_CLOEXEC != O_CLOEXEC);
|
||||
BUILD_BUG_ON(EFD_NONBLOCK != O_NONBLOCK);
|
||||
BUILD_BUG_ON(EFD_SEMAPHORE != (1 << 0));
|
||||
|
||||
if (flags & ~EFD_FLAGS_SET)
|
||||
return -EINVAL;
|
||||
|
||||
+5
-11
@@ -206,7 +206,7 @@ struct eventpoll {
|
||||
*/
|
||||
struct epitem *ovflist;
|
||||
|
||||
/* wakeup_source used when ep_scan_ready_list is running */
|
||||
/* wakeup_source used when ep_send_events or __ep_eventpoll_poll is running */
|
||||
struct wakeup_source *ws;
|
||||
|
||||
/* The user that created the eventpoll descriptor */
|
||||
@@ -678,12 +678,6 @@ static void ep_done_scan(struct eventpoll *ep,
|
||||
write_unlock_irq(&ep->lock);
|
||||
}
|
||||
|
||||
static void epi_rcu_free(struct rcu_head *head)
|
||||
{
|
||||
struct epitem *epi = container_of(head, struct epitem, rcu);
|
||||
kmem_cache_free(epi_cache, epi);
|
||||
}
|
||||
|
||||
static void ep_get(struct eventpoll *ep)
|
||||
{
|
||||
refcount_inc(&ep->refcount);
|
||||
@@ -767,7 +761,7 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
|
||||
* ep->mtx. The rcu read side, reverse_path_check_proc(), does not make
|
||||
* use of the rbn field.
|
||||
*/
|
||||
call_rcu(&epi->rcu, epi_rcu_free);
|
||||
kfree_rcu(epi, rcu);
|
||||
|
||||
percpu_counter_dec(&ep->user->epoll_watches);
|
||||
return ep_refcount_dec_and_test(ep);
|
||||
@@ -1153,7 +1147,7 @@ static inline bool chain_epi_lockless(struct epitem *epi)
|
||||
* This callback takes a read lock in order not to contend with concurrent
|
||||
* events from another file descriptor, thus all modifications to ->rdllist
|
||||
* or ->ovflist are lockless. Read lock is paired with the write lock from
|
||||
* ep_scan_ready_list(), which stops all list modifications and guarantees
|
||||
* ep_start/done_scan(), which stops all list modifications and guarantees
|
||||
* that lists state is seen correctly.
|
||||
*
|
||||
* Another thing worth to mention is that ep_poll_callback() can be called
|
||||
@@ -1751,7 +1745,7 @@ static int ep_send_events(struct eventpoll *ep,
|
||||
* availability. At this point, no one can insert
|
||||
* into ep->rdllist besides us. The epoll_ctl()
|
||||
* callers are locked out by
|
||||
* ep_scan_ready_list() holding "mtx" and the
|
||||
* ep_send_events() holding "mtx" and the
|
||||
* poll callback will queue them in ep->ovflist.
|
||||
*/
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
@@ -1904,7 +1898,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
/*
|
||||
* Do the final check under the lock. ep_scan_ready_list()
|
||||
* Do the final check under the lock. ep_start/done_scan()
|
||||
* plays with two lists (->rdllist and ->ovflist) and there
|
||||
* is always a race when both lists are empty for short
|
||||
* period of time although events are pending, so lock is
|
||||
|
||||
+1
-1
@@ -255,7 +255,7 @@ static bool filldir_one(struct dir_context *ctx, const char *name, int len,
|
||||
container_of(ctx, struct getdents_callback, ctx);
|
||||
|
||||
buf->sequence++;
|
||||
if (buf->ino == ino && len <= NAME_MAX) {
|
||||
if (buf->ino == ino && len <= NAME_MAX && !is_dot_dotdot(name, len)) {
|
||||
memcpy(buf->name, name, len);
|
||||
buf->name[len] = '\0';
|
||||
buf->found = 1;
|
||||
|
||||
@@ -1762,7 +1762,6 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir,
|
||||
struct buffer_head *bh;
|
||||
|
||||
err = ext4_fname_prepare_lookup(dir, dentry, &fname);
|
||||
generic_set_encrypted_ci_d_ops(dentry);
|
||||
if (err == -ENOENT)
|
||||
return NULL;
|
||||
if (err)
|
||||
|
||||
@@ -5484,6 +5484,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
|
||||
goto failed_mount4;
|
||||
}
|
||||
|
||||
generic_set_sb_d_ops(sb);
|
||||
sb->s_root = d_make_root(root);
|
||||
if (!sb->s_root) {
|
||||
ext4_msg(sb, KERN_ERR, "get root dentry failed");
|
||||
|
||||
@@ -3364,17 +3364,6 @@ static inline bool f2fs_cp_error(struct f2fs_sb_info *sbi)
|
||||
return is_set_ckpt_flags(sbi, CP_ERROR_FLAG);
|
||||
}
|
||||
|
||||
static inline bool is_dot_dotdot(const u8 *name, size_t len)
|
||||
{
|
||||
if (len == 1 && name[0] == '.')
|
||||
return true;
|
||||
|
||||
if (len == 2 && name[0] == '.' && name[1] == '.')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi,
|
||||
size_t size, gfp_t flags)
|
||||
{
|
||||
|
||||
@@ -531,7 +531,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
}
|
||||
|
||||
err = f2fs_prepare_lookup(dir, dentry, &fname);
|
||||
generic_set_encrypted_ci_d_ops(dentry);
|
||||
if (err == -ENOENT)
|
||||
goto out_splice;
|
||||
if (err)
|
||||
|
||||
@@ -4660,6 +4660,7 @@ try_onemore:
|
||||
goto free_node_inode;
|
||||
}
|
||||
|
||||
generic_set_sb_d_ops(sb);
|
||||
sb->s_root = d_make_root(root); /* allocate root dentry */
|
||||
if (!sb->s_root) {
|
||||
err = -ENOMEM;
|
||||
|
||||
+1
-7
@@ -846,12 +846,6 @@ int send_sigurg(struct fown_struct *fown)
|
||||
static DEFINE_SPINLOCK(fasync_lock);
|
||||
static struct kmem_cache *fasync_cache __ro_after_init;
|
||||
|
||||
static void fasync_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
kmem_cache_free(fasync_cache,
|
||||
container_of(head, struct fasync_struct, fa_rcu));
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a fasync entry. If successfully removed, return
|
||||
* positive and clear the FASYNC flag. If no entry exists,
|
||||
@@ -877,7 +871,7 @@ int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp)
|
||||
write_unlock_irq(&fa->fa_lock);
|
||||
|
||||
*fp = fa->fa_next;
|
||||
call_rcu(&fa->fa_rcu, fasync_free_rcu);
|
||||
kfree_rcu(fa, fa_rcu);
|
||||
filp->f_flags &= ~FASYNC;
|
||||
result = 1;
|
||||
break;
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ static long do_sys_name_to_handle(const struct path *path,
|
||||
if (f_handle.handle_bytes > MAX_HANDLE_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
|
||||
handle = kzalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
|
||||
GFP_KERNEL);
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -141,6 +141,31 @@ static void wb_wakeup(struct bdi_writeback *wb)
|
||||
spin_unlock_irq(&wb->work_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is used when the first inode for this wb is marked dirty. It
|
||||
* wakes-up the corresponding bdi thread which should then take care of the
|
||||
* periodic background write-out of dirty inodes. Since the write-out would
|
||||
* starts only 'dirty_writeback_interval' centisecs from now anyway, we just
|
||||
* set up a timer which wakes the bdi thread up later.
|
||||
*
|
||||
* Note, we wouldn't bother setting up the timer, but this function is on the
|
||||
* fast-path (used by '__mark_inode_dirty()'), so we save few context switches
|
||||
* by delaying the wake-up.
|
||||
*
|
||||
* We have to be careful not to postpone flush work if it is scheduled for
|
||||
* earlier. Thus we use queue_delayed_work().
|
||||
*/
|
||||
static void wb_wakeup_delayed(struct bdi_writeback *wb)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
|
||||
spin_lock_irq(&wb->work_lock);
|
||||
if (test_bit(WB_registered, &wb->state))
|
||||
queue_delayed_work(bdi_wq, &wb->dwork, timeout);
|
||||
spin_unlock_irq(&wb->work_lock);
|
||||
}
|
||||
|
||||
static void finish_writeback_work(struct bdi_writeback *wb,
|
||||
struct wb_writeback_work *work)
|
||||
{
|
||||
|
||||
+2
-2
@@ -83,8 +83,8 @@ static const struct fs_parameter_spec *fs_lookup_key(
|
||||
}
|
||||
|
||||
/*
|
||||
* fs_parse - Parse a filesystem configuration parameter
|
||||
* @fc: The filesystem context to log errors through.
|
||||
* __fs_parse - Parse a filesystem configuration parameter
|
||||
* @log: The filesystem context to log errors through.
|
||||
* @desc: The parameter description to use.
|
||||
* @param: The parameter.
|
||||
* @result: Where to place the result of the parse
|
||||
|
||||
@@ -30,7 +30,7 @@ struct hfsplus_wd {
|
||||
* @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
|
||||
* @buf: buffer for I/O
|
||||
* @data: output pointer for location of requested data
|
||||
* @opf: request op flags
|
||||
* @opf: I/O operation type and flags
|
||||
*
|
||||
* The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
|
||||
* HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
|
||||
|
||||
+14
-9
@@ -933,7 +933,7 @@ static int hugetlbfs_setattr(struct mnt_idmap *idmap,
|
||||
unsigned int ia_valid = attr->ia_valid;
|
||||
struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode);
|
||||
|
||||
error = setattr_prepare(&nop_mnt_idmap, dentry, attr);
|
||||
error = setattr_prepare(idmap, dentry, attr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -950,7 +950,7 @@ static int hugetlbfs_setattr(struct mnt_idmap *idmap,
|
||||
hugetlb_vmtruncate(inode, newsize);
|
||||
}
|
||||
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
setattr_copy(idmap, inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
return 0;
|
||||
}
|
||||
@@ -985,6 +985,7 @@ static struct inode *hugetlbfs_get_root(struct super_block *sb,
|
||||
static struct lock_class_key hugetlbfs_i_mmap_rwsem_key;
|
||||
|
||||
static struct inode *hugetlbfs_get_inode(struct super_block *sb,
|
||||
struct mnt_idmap *idmap,
|
||||
struct inode *dir,
|
||||
umode_t mode, dev_t dev)
|
||||
{
|
||||
@@ -1006,7 +1007,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
|
||||
struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode);
|
||||
|
||||
inode->i_ino = get_next_ino();
|
||||
inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
|
||||
inode_init_owner(idmap, inode, dir, mode);
|
||||
lockdep_set_class(&inode->i_mapping->i_mmap_rwsem,
|
||||
&hugetlbfs_i_mmap_rwsem_key);
|
||||
inode->i_mapping->a_ops = &hugetlbfs_aops;
|
||||
@@ -1050,7 +1051,7 @@ static int hugetlbfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = hugetlbfs_get_inode(dir->i_sb, dir, mode, dev);
|
||||
inode = hugetlbfs_get_inode(dir->i_sb, idmap, dir, mode, dev);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
@@ -1062,7 +1063,7 @@ static int hugetlbfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
static int hugetlbfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
int retval = hugetlbfs_mknod(&nop_mnt_idmap, dir, dentry,
|
||||
int retval = hugetlbfs_mknod(idmap, dir, dentry,
|
||||
mode | S_IFDIR, 0);
|
||||
if (!retval)
|
||||
inc_nlink(dir);
|
||||
@@ -1073,7 +1074,7 @@ static int hugetlbfs_create(struct mnt_idmap *idmap,
|
||||
struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, bool excl)
|
||||
{
|
||||
return hugetlbfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFREG, 0);
|
||||
return hugetlbfs_mknod(idmap, dir, dentry, mode | S_IFREG, 0);
|
||||
}
|
||||
|
||||
static int hugetlbfs_tmpfile(struct mnt_idmap *idmap,
|
||||
@@ -1082,7 +1083,7 @@ static int hugetlbfs_tmpfile(struct mnt_idmap *idmap,
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = hugetlbfs_get_inode(dir->i_sb, dir, mode | S_IFREG, 0);
|
||||
inode = hugetlbfs_get_inode(dir->i_sb, idmap, dir, mode | S_IFREG, 0);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
@@ -1094,10 +1095,11 @@ static int hugetlbfs_symlink(struct mnt_idmap *idmap,
|
||||
struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
{
|
||||
const umode_t mode = S_IFLNK|S_IRWXUGO;
|
||||
struct inode *inode;
|
||||
int error = -ENOSPC;
|
||||
|
||||
inode = hugetlbfs_get_inode(dir->i_sb, dir, S_IFLNK|S_IRWXUGO, 0);
|
||||
inode = hugetlbfs_get_inode(dir->i_sb, idmap, dir, mode, 0);
|
||||
if (inode) {
|
||||
int l = strlen(symname)+1;
|
||||
error = page_symlink(inode, symname, l);
|
||||
@@ -1566,6 +1568,7 @@ static struct file_system_type hugetlbfs_fs_type = {
|
||||
.init_fs_context = hugetlbfs_init_fs_context,
|
||||
.parameters = hugetlb_fs_parameters,
|
||||
.kill_sb = kill_litter_super,
|
||||
.fs_flags = FS_ALLOW_IDMAP,
|
||||
};
|
||||
|
||||
static struct vfsmount *hugetlbfs_vfsmount[HUGE_MAX_HSTATE];
|
||||
@@ -1619,7 +1622,9 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
|
||||
}
|
||||
|
||||
file = ERR_PTR(-ENOSPC);
|
||||
inode = hugetlbfs_get_inode(mnt->mnt_sb, NULL, S_IFREG | S_IRWXUGO, 0);
|
||||
/* hugetlbfs_vfsmount[] mounts do not use idmapped mounts. */
|
||||
inode = hugetlbfs_get_inode(mnt->mnt_sb, &nop_mnt_idmap, NULL,
|
||||
S_IFREG | S_IRWXUGO, 0);
|
||||
if (!inode)
|
||||
goto out;
|
||||
if (creat_flags == HUGETLB_SHMFS_INODE)
|
||||
|
||||
+4
-3
@@ -588,7 +588,8 @@ void dump_mapping(const struct address_space *mapping)
|
||||
}
|
||||
|
||||
dentry_ptr = container_of(dentry_first, struct dentry, d_u.d_alias);
|
||||
if (get_kernel_nofault(dentry, dentry_ptr)) {
|
||||
if (get_kernel_nofault(dentry, dentry_ptr) ||
|
||||
!dentry.d_parent || !dentry.d_name.name) {
|
||||
pr_warn("aops:%ps ino:%lx invalid dentry:%px\n",
|
||||
a_ops, ino, dentry_ptr);
|
||||
return;
|
||||
@@ -2285,7 +2286,7 @@ void __init inode_init(void)
|
||||
sizeof(struct inode),
|
||||
0,
|
||||
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
|
||||
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
|
||||
SLAB_ACCOUNT),
|
||||
init_once);
|
||||
|
||||
/* Hash may have been set up in inode_init_early */
|
||||
@@ -2509,7 +2510,7 @@ struct timespec64 inode_set_ctime_current(struct inode *inode)
|
||||
{
|
||||
struct timespec64 now = current_time(inode);
|
||||
|
||||
inode_set_ctime(inode, now.tv_sec, now.tv_nsec);
|
||||
inode_set_ctime_to_ts(inode, now);
|
||||
return now;
|
||||
}
|
||||
EXPORT_SYMBOL(inode_set_ctime_current);
|
||||
|
||||
+1
-1
@@ -932,7 +932,7 @@ static int __init init_jfs_fs(void)
|
||||
|
||||
jfs_inode_cachep =
|
||||
kmem_cache_create_usercopy("jfs_ip", sizeof(struct jfs_inode_info),
|
||||
0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
|
||||
0, SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
|
||||
offsetof(struct jfs_inode_info, i_inline_all),
|
||||
sizeof_field(struct jfs_inode_info, i_inline_all),
|
||||
init_once);
|
||||
|
||||
+106
-94
@@ -240,17 +240,22 @@ const struct inode_operations simple_dir_inode_operations = {
|
||||
};
|
||||
EXPORT_SYMBOL(simple_dir_inode_operations);
|
||||
|
||||
static void offset_set(struct dentry *dentry, u32 offset)
|
||||
/* 0 is '.', 1 is '..', so always start with offset 2 or more */
|
||||
enum {
|
||||
DIR_OFFSET_MIN = 2,
|
||||
};
|
||||
|
||||
static void offset_set(struct dentry *dentry, long offset)
|
||||
{
|
||||
dentry->d_fsdata = (void *)((uintptr_t)(offset));
|
||||
dentry->d_fsdata = (void *)offset;
|
||||
}
|
||||
|
||||
static u32 dentry2offset(struct dentry *dentry)
|
||||
static long dentry2offset(struct dentry *dentry)
|
||||
{
|
||||
return (u32)((uintptr_t)(dentry->d_fsdata));
|
||||
return (long)dentry->d_fsdata;
|
||||
}
|
||||
|
||||
static struct lock_class_key simple_offset_xa_lock;
|
||||
static struct lock_class_key simple_offset_lock_class;
|
||||
|
||||
/**
|
||||
* simple_offset_init - initialize an offset_ctx
|
||||
@@ -259,11 +264,9 @@ static struct lock_class_key simple_offset_xa_lock;
|
||||
*/
|
||||
void simple_offset_init(struct offset_ctx *octx)
|
||||
{
|
||||
xa_init_flags(&octx->xa, XA_FLAGS_ALLOC1);
|
||||
lockdep_set_class(&octx->xa.xa_lock, &simple_offset_xa_lock);
|
||||
|
||||
/* 0 is '.', 1 is '..', so always start with offset 2 */
|
||||
octx->next_offset = 2;
|
||||
mt_init_flags(&octx->mt, MT_FLAGS_ALLOC_RANGE);
|
||||
lockdep_set_class(&octx->mt.ma_lock, &simple_offset_lock_class);
|
||||
octx->next_offset = DIR_OFFSET_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,20 +274,19 @@ void simple_offset_init(struct offset_ctx *octx)
|
||||
* @octx: directory offset ctx to be updated
|
||||
* @dentry: new dentry being added
|
||||
*
|
||||
* Returns zero on success. @so_ctx and the dentry offset are updated.
|
||||
* Returns zero on success. @octx and the dentry's offset are updated.
|
||||
* Otherwise, a negative errno value is returned.
|
||||
*/
|
||||
int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
|
||||
{
|
||||
static const struct xa_limit limit = XA_LIMIT(2, U32_MAX);
|
||||
u32 offset;
|
||||
unsigned long offset;
|
||||
int ret;
|
||||
|
||||
if (dentry2offset(dentry) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
ret = xa_alloc_cyclic(&octx->xa, &offset, dentry, limit,
|
||||
&octx->next_offset, GFP_KERNEL);
|
||||
ret = mtree_alloc_cyclic(&octx->mt, &offset, dentry, DIR_OFFSET_MIN,
|
||||
LONG_MAX, &octx->next_offset, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -300,16 +302,48 @@ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
|
||||
*/
|
||||
void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry)
|
||||
{
|
||||
u32 offset;
|
||||
long offset;
|
||||
|
||||
offset = dentry2offset(dentry);
|
||||
if (offset == 0)
|
||||
return;
|
||||
|
||||
xa_erase(&octx->xa, offset);
|
||||
mtree_erase(&octx->mt, offset);
|
||||
offset_set(dentry, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_offset_empty - Check if a dentry can be unlinked
|
||||
* @dentry: dentry to be tested
|
||||
*
|
||||
* Returns 0 if @dentry is a non-empty directory; otherwise returns 1.
|
||||
*/
|
||||
int simple_offset_empty(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct offset_ctx *octx;
|
||||
struct dentry *child;
|
||||
unsigned long index;
|
||||
int ret = 1;
|
||||
|
||||
if (!inode || !S_ISDIR(inode->i_mode))
|
||||
return ret;
|
||||
|
||||
index = DIR_OFFSET_MIN;
|
||||
octx = inode->i_op->get_offset_ctx(inode);
|
||||
mt_for_each(&octx->mt, child, index, LONG_MAX) {
|
||||
spin_lock(&child->d_lock);
|
||||
if (simple_positive(child)) {
|
||||
spin_unlock(&child->d_lock);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&child->d_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_offset_rename_exchange - exchange rename with directory offsets
|
||||
* @old_dir: parent of dentry being moved
|
||||
@@ -327,8 +361,8 @@ int simple_offset_rename_exchange(struct inode *old_dir,
|
||||
{
|
||||
struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
|
||||
struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
|
||||
u32 old_index = dentry2offset(old_dentry);
|
||||
u32 new_index = dentry2offset(new_dentry);
|
||||
long old_index = dentry2offset(old_dentry);
|
||||
long new_index = dentry2offset(new_dentry);
|
||||
int ret;
|
||||
|
||||
simple_offset_remove(old_ctx, old_dentry);
|
||||
@@ -354,9 +388,9 @@ int simple_offset_rename_exchange(struct inode *old_dir,
|
||||
|
||||
out_restore:
|
||||
offset_set(old_dentry, old_index);
|
||||
xa_store(&old_ctx->xa, old_index, old_dentry, GFP_KERNEL);
|
||||
mtree_store(&old_ctx->mt, old_index, old_dentry, GFP_KERNEL);
|
||||
offset_set(new_dentry, new_index);
|
||||
xa_store(&new_ctx->xa, new_index, new_dentry, GFP_KERNEL);
|
||||
mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -369,7 +403,7 @@ out_restore:
|
||||
*/
|
||||
void simple_offset_destroy(struct offset_ctx *octx)
|
||||
{
|
||||
xa_destroy(&octx->xa);
|
||||
mtree_destroy(&octx->mt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,15 +433,16 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence)
|
||||
|
||||
/* In this case, ->private_data is protected by f_pos_lock */
|
||||
file->private_data = NULL;
|
||||
return vfs_setpos(file, offset, U32_MAX);
|
||||
return vfs_setpos(file, offset, LONG_MAX);
|
||||
}
|
||||
|
||||
static struct dentry *offset_find_next(struct xa_state *xas)
|
||||
static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
|
||||
{
|
||||
MA_STATE(mas, &octx->mt, offset, offset);
|
||||
struct dentry *child, *found = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
child = xas_next_entry(xas, U32_MAX);
|
||||
child = mas_find(&mas, LONG_MAX);
|
||||
if (!child)
|
||||
goto out;
|
||||
spin_lock(&child->d_lock);
|
||||
@@ -421,8 +456,8 @@ out:
|
||||
|
||||
static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
|
||||
{
|
||||
u32 offset = dentry2offset(dentry);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
long offset = dentry2offset(dentry);
|
||||
|
||||
return ctx->actor(ctx, dentry->d_name.name, dentry->d_name.len, offset,
|
||||
inode->i_ino, fs_umode_to_dtype(inode->i_mode));
|
||||
@@ -430,12 +465,11 @@ static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
|
||||
|
||||
static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
|
||||
{
|
||||
struct offset_ctx *so_ctx = inode->i_op->get_offset_ctx(inode);
|
||||
XA_STATE(xas, &so_ctx->xa, ctx->pos);
|
||||
struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
|
||||
struct dentry *dentry;
|
||||
|
||||
while (true) {
|
||||
dentry = offset_find_next(&xas);
|
||||
dentry = offset_find_next(octx, ctx->pos);
|
||||
if (!dentry)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
@@ -444,8 +478,8 @@ static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->pos = dentry2offset(dentry) + 1;
|
||||
dput(dentry);
|
||||
ctx->pos = xas.xa_index + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -481,7 +515,7 @@ static int offset_readdir(struct file *file, struct dir_context *ctx)
|
||||
return 0;
|
||||
|
||||
/* In this case, ->private_data is protected by f_pos_lock */
|
||||
if (ctx->pos == 2)
|
||||
if (ctx->pos == DIR_OFFSET_MIN)
|
||||
file->private_data = NULL;
|
||||
else if (file->private_data == ERR_PTR(-ENOENT))
|
||||
return 0;
|
||||
@@ -1704,16 +1738,28 @@ bool is_empty_dir_inode(struct inode *inode)
|
||||
static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
const char *str, const struct qstr *name)
|
||||
{
|
||||
const struct dentry *parent = READ_ONCE(dentry->d_parent);
|
||||
const struct inode *dir = READ_ONCE(parent->d_inode);
|
||||
const struct super_block *sb = dentry->d_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
struct qstr qstr = QSTR_INIT(str, len);
|
||||
const struct dentry *parent;
|
||||
const struct inode *dir;
|
||||
char strbuf[DNAME_INLINE_LEN];
|
||||
int ret;
|
||||
struct qstr qstr;
|
||||
|
||||
/*
|
||||
* Attempt a case-sensitive match first. It is cheaper and
|
||||
* should cover most lookups, including all the sane
|
||||
* applications that expect a case-sensitive filesystem.
|
||||
*
|
||||
* This comparison is safe under RCU because the caller
|
||||
* guarantees the consistency between str and len. See
|
||||
* __d_lookup_rcu_op_compare() for details.
|
||||
*/
|
||||
if (len == name->len && !memcmp(str, name->name, len))
|
||||
return 0;
|
||||
|
||||
parent = READ_ONCE(dentry->d_parent);
|
||||
dir = READ_ONCE(parent->d_inode);
|
||||
if (!dir || !IS_CASEFOLDED(dir))
|
||||
goto fallback;
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If the dentry name is stored in-line, then it may be concurrently
|
||||
* modified by a rename. If this happens, the VFS will eventually retry
|
||||
@@ -1724,20 +1770,14 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
if (len <= DNAME_INLINE_LEN - 1) {
|
||||
memcpy(strbuf, str, len);
|
||||
strbuf[len] = 0;
|
||||
qstr.name = strbuf;
|
||||
str = strbuf;
|
||||
/* prevent compiler from optimizing out the temporary buffer */
|
||||
barrier();
|
||||
}
|
||||
ret = utf8_strncasecmp(um, name, &qstr);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
qstr.len = len;
|
||||
qstr.name = str;
|
||||
|
||||
if (sb_has_strict_encoding(sb))
|
||||
return -EINVAL;
|
||||
fallback:
|
||||
if (len != name->len)
|
||||
return 1;
|
||||
return !!memcmp(str, name->name, len);
|
||||
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1752,7 +1792,7 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
|
||||
const struct inode *dir = READ_ONCE(dentry->d_inode);
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!dir || !IS_CASEFOLDED(dir))
|
||||
return 0;
|
||||
@@ -1766,6 +1806,9 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
|
||||
static const struct dentry_operations generic_ci_dentry_ops = {
|
||||
.d_hash = generic_ci_d_hash,
|
||||
.d_compare = generic_ci_d_compare,
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1775,64 +1818,33 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_UNICODE)
|
||||
static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
|
||||
.d_hash = generic_ci_d_hash,
|
||||
.d_compare = generic_ci_d_compare,
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* generic_set_encrypted_ci_d_ops - helper for setting d_ops for given dentry
|
||||
* @dentry: dentry to set ops on
|
||||
* generic_set_sb_d_ops - helper for choosing the set of
|
||||
* filesystem-wide dentry operations for the enabled features
|
||||
* @sb: superblock to be configured
|
||||
*
|
||||
* Casefolded directories need d_hash and d_compare set, so that the dentries
|
||||
* contained in them are handled case-insensitively. Note that these operations
|
||||
* are needed on the parent directory rather than on the dentries in it, and
|
||||
* while the casefolding flag can be toggled on and off on an empty directory,
|
||||
* dentry_operations can't be changed later. As a result, if the filesystem has
|
||||
* casefolding support enabled at all, we have to give all dentries the
|
||||
* casefolding operations even if their inode doesn't have the casefolding flag
|
||||
* currently (and thus the casefolding ops would be no-ops for now).
|
||||
*
|
||||
* Encryption works differently in that the only dentry operation it needs is
|
||||
* d_revalidate, which it only needs on dentries that have the no-key name flag.
|
||||
* The no-key flag can't be set "later", so we don't have to worry about that.
|
||||
*
|
||||
* Finally, to maximize compatibility with overlayfs (which isn't compatible
|
||||
* with certain dentry operations) and to avoid taking an unnecessary
|
||||
* performance hit, we use custom dentry_operations for each possible
|
||||
* combination rather than always installing all operations.
|
||||
* Filesystems supporting casefolding and/or fscrypt can call this
|
||||
* helper at mount-time to configure sb->s_d_op to best set of dentry
|
||||
* operations required for the enabled features. The helper must be
|
||||
* called after these have been configured, but before the root dentry
|
||||
* is created.
|
||||
*/
|
||||
void generic_set_encrypted_ci_d_ops(struct dentry *dentry)
|
||||
void generic_set_sb_d_ops(struct super_block *sb)
|
||||
{
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
bool needs_encrypt_ops = dentry->d_flags & DCACHE_NOKEY_NAME;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
bool needs_ci_ops = dentry->d_sb->s_encoding;
|
||||
#endif
|
||||
#if defined(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_UNICODE)
|
||||
if (needs_encrypt_ops && needs_ci_ops) {
|
||||
d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops);
|
||||
if (sb->s_encoding) {
|
||||
sb->s_d_op = &generic_ci_dentry_ops;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
if (needs_encrypt_ops) {
|
||||
d_set_d_op(dentry, &generic_encrypted_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (needs_ci_ops) {
|
||||
d_set_d_op(dentry, &generic_ci_dentry_ops);
|
||||
if (sb->s_cop) {
|
||||
sb->s_d_op = &generic_encrypted_dentry_ops;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(generic_set_encrypted_ci_d_ops);
|
||||
EXPORT_SYMBOL(generic_set_sb_d_ops);
|
||||
|
||||
/**
|
||||
* inode_maybe_inc_iversion - increments i_version
|
||||
|
||||
+1
-3
@@ -426,9 +426,7 @@ EXPORT_SYMBOL(mb_cache_destroy);
|
||||
|
||||
static int __init mbcache_init(void)
|
||||
{
|
||||
mb_entry_cache = kmem_cache_create("mbcache",
|
||||
sizeof(struct mb_cache_entry), 0,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL);
|
||||
mb_entry_cache = KMEM_CACHE(mb_cache_entry, SLAB_RECLAIM_ACCOUNT);
|
||||
if (!mb_entry_cache)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ static int __init init_inodecache(void)
|
||||
minix_inode_cachep = kmem_cache_create("minix_inode_cache",
|
||||
sizeof(struct minix_inode_info),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
|
||||
SLAB_ACCOUNT),
|
||||
init_once);
|
||||
if (minix_inode_cachep == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ static int copy_mnt_idmap(struct uid_gid_map *map_from,
|
||||
* anything at all.
|
||||
*/
|
||||
if (nr_extents == 0)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Here we know that nr_extents is greater than zero which means
|
||||
|
||||
+2
-4
@@ -2680,10 +2680,8 @@ static int lookup_one_common(struct mnt_idmap *idmap,
|
||||
if (!len)
|
||||
return -EACCES;
|
||||
|
||||
if (unlikely(name[0] == '.')) {
|
||||
if (len < 2 || (len == 2 && name[1] == '.'))
|
||||
return -EACCES;
|
||||
}
|
||||
if (is_dot_dotdot(name, len))
|
||||
return -EACCES;
|
||||
|
||||
while (len--) {
|
||||
unsigned int c = *(const unsigned char *)name++;
|
||||
|
||||
+1
-1
@@ -431,7 +431,7 @@ static int ntfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
* fnd contains tree's path to insert to.
|
||||
* If fnd is not NULL then dir is locked.
|
||||
*/
|
||||
inode = ntfs_create_inode(mnt_idmap(file->f_path.mnt), dir, dentry, uni,
|
||||
inode = ntfs_create_inode(file_mnt_idmap(file), dir, dentry, uni,
|
||||
mode, 0, NULL, 0, fnd);
|
||||
err = IS_ERR(inode) ? PTR_ERR(inode) :
|
||||
finish_open(file, dentry, ntfs_file_open);
|
||||
|
||||
@@ -446,7 +446,7 @@ static int __init init_openprom_fs(void)
|
||||
sizeof(struct op_inode_info),
|
||||
0,
|
||||
(SLAB_RECLAIM_ACCOUNT |
|
||||
SLAB_MEM_SPREAD | SLAB_ACCOUNT),
|
||||
SLAB_ACCOUNT),
|
||||
op_inode_init_once);
|
||||
if (!op_inode_cachep)
|
||||
return -ENOMEM;
|
||||
|
||||
+11
-3
@@ -280,12 +280,20 @@ static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path,
|
||||
{
|
||||
struct ovl_fs_context *ctx = fc->fs_private;
|
||||
|
||||
if (ovl_dentry_weird(path->dentry))
|
||||
return invalfc(fc, "filesystem on %s not supported", name);
|
||||
|
||||
if (!d_is_dir(path->dentry))
|
||||
return invalfc(fc, "%s is not a directory", name);
|
||||
|
||||
/*
|
||||
* Root dentries of case-insensitive capable filesystems might
|
||||
* not have the dentry operations set, but still be incompatible
|
||||
* with overlayfs. Check explicitly to prevent post-mount
|
||||
* failures.
|
||||
*/
|
||||
if (sb_has_encoding(path->mnt->mnt_sb))
|
||||
return invalfc(fc, "case-insensitive capable filesystem on %s not supported", name);
|
||||
|
||||
if (ovl_dentry_weird(path->dentry))
|
||||
return invalfc(fc, "filesystem on %s not supported", name);
|
||||
|
||||
/*
|
||||
* Check whether upper path is read-only here to report failures
|
||||
|
||||
+22
-30
@@ -28,41 +28,38 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
struct ovl_dir_cache;
|
||||
|
||||
static struct dentry *ovl_d_real(struct dentry *dentry,
|
||||
const struct inode *inode)
|
||||
static struct dentry *ovl_d_real(struct dentry *dentry, enum d_real_type type)
|
||||
{
|
||||
struct dentry *real = NULL, *lower;
|
||||
struct dentry *upper, *lower;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* vfs is only expected to call d_real() with NULL from d_real_inode()
|
||||
* and with overlay inode from file_dentry() on an overlay file.
|
||||
*
|
||||
* TODO: remove @inode argument from d_real() API, remove code in this
|
||||
* function that deals with non-NULL @inode and remove d_real() call
|
||||
* from file_dentry().
|
||||
*/
|
||||
if (inode && d_inode(dentry) == inode)
|
||||
return dentry;
|
||||
else if (inode)
|
||||
switch (type) {
|
||||
case D_REAL_DATA:
|
||||
case D_REAL_METADATA:
|
||||
break;
|
||||
default:
|
||||
goto bug;
|
||||
}
|
||||
|
||||
if (!d_is_reg(dentry)) {
|
||||
/* d_real_inode() is only relevant for regular files */
|
||||
return dentry;
|
||||
}
|
||||
|
||||
real = ovl_dentry_upper(dentry);
|
||||
if (real && (inode == d_inode(real)))
|
||||
return real;
|
||||
upper = ovl_dentry_upper(dentry);
|
||||
if (upper && (type == D_REAL_METADATA ||
|
||||
ovl_has_upperdata(d_inode(dentry))))
|
||||
return upper;
|
||||
|
||||
if (real && !inode && ovl_has_upperdata(d_inode(dentry)))
|
||||
return real;
|
||||
if (type == D_REAL_METADATA) {
|
||||
lower = ovl_dentry_lower(dentry);
|
||||
goto real_lower;
|
||||
}
|
||||
|
||||
/*
|
||||
* Best effort lazy lookup of lowerdata for !inode case to return
|
||||
* Best effort lazy lookup of lowerdata for D_REAL_DATA case to return
|
||||
* the real lowerdata dentry. The only current caller of d_real() with
|
||||
* NULL inode is d_real_inode() from trace_uprobe and this caller is
|
||||
* D_REAL_DATA is d_real_inode() from trace_uprobe and this caller is
|
||||
* likely going to be followed reading from the file, before placing
|
||||
* uprobes on offset within the file, so lowerdata should be available
|
||||
* when setting the uprobe.
|
||||
@@ -73,18 +70,13 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
|
||||
lower = ovl_dentry_lowerdata(dentry);
|
||||
if (!lower)
|
||||
goto bug;
|
||||
real = lower;
|
||||
|
||||
/* Handle recursion */
|
||||
real = d_real(real, inode);
|
||||
real_lower:
|
||||
/* Handle recursion into stacked lower fs */
|
||||
return d_real(lower, type);
|
||||
|
||||
if (!inode || inode == d_inode(real))
|
||||
return real;
|
||||
bug:
|
||||
WARN(1, "%s(%pd4, %s:%lu): real dentry (%p/%lu) not found\n",
|
||||
__func__, dentry, inode ? inode->i_sb->s_id : "NULL",
|
||||
inode ? inode->i_ino : 0, real,
|
||||
real && d_inode(real) ? d_inode(real)->i_ino : 0);
|
||||
WARN(1, "%s(%pd4, %d): real dentry not found\n", __func__, dentry, type);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,18 +76,20 @@ static unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;
|
||||
* -- Manfred Spraul <manfred@colorfullife.com> 2002-05-09
|
||||
*/
|
||||
|
||||
static void pipe_lock_nested(struct pipe_inode_info *pipe, int subclass)
|
||||
#define cmp_int(l, r) ((l > r) - (l < r))
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
static int pipe_lock_cmp_fn(const struct lockdep_map *a,
|
||||
const struct lockdep_map *b)
|
||||
{
|
||||
if (pipe->files)
|
||||
mutex_lock_nested(&pipe->mutex, subclass);
|
||||
return cmp_int((unsigned long) a, (unsigned long) b);
|
||||
}
|
||||
#endif
|
||||
|
||||
void pipe_lock(struct pipe_inode_info *pipe)
|
||||
{
|
||||
/*
|
||||
* pipe_lock() nests non-pipe inode locks (for writing to a file)
|
||||
*/
|
||||
pipe_lock_nested(pipe, I_MUTEX_PARENT);
|
||||
if (pipe->files)
|
||||
mutex_lock(&pipe->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(pipe_lock);
|
||||
|
||||
@@ -98,28 +100,16 @@ void pipe_unlock(struct pipe_inode_info *pipe)
|
||||
}
|
||||
EXPORT_SYMBOL(pipe_unlock);
|
||||
|
||||
static inline void __pipe_lock(struct pipe_inode_info *pipe)
|
||||
{
|
||||
mutex_lock_nested(&pipe->mutex, I_MUTEX_PARENT);
|
||||
}
|
||||
|
||||
static inline void __pipe_unlock(struct pipe_inode_info *pipe)
|
||||
{
|
||||
mutex_unlock(&pipe->mutex);
|
||||
}
|
||||
|
||||
void pipe_double_lock(struct pipe_inode_info *pipe1,
|
||||
struct pipe_inode_info *pipe2)
|
||||
{
|
||||
BUG_ON(pipe1 == pipe2);
|
||||
|
||||
if (pipe1 < pipe2) {
|
||||
pipe_lock_nested(pipe1, I_MUTEX_PARENT);
|
||||
pipe_lock_nested(pipe2, I_MUTEX_CHILD);
|
||||
} else {
|
||||
pipe_lock_nested(pipe2, I_MUTEX_PARENT);
|
||||
pipe_lock_nested(pipe1, I_MUTEX_CHILD);
|
||||
}
|
||||
if (pipe1 > pipe2)
|
||||
swap(pipe1, pipe2);
|
||||
|
||||
pipe_lock(pipe1);
|
||||
pipe_lock(pipe2);
|
||||
}
|
||||
|
||||
static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
|
||||
@@ -271,7 +261,7 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
|
||||
return 0;
|
||||
|
||||
ret = 0;
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
|
||||
/*
|
||||
* We only wake up writers if the pipe was full when we started
|
||||
@@ -368,7 +358,7 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
/*
|
||||
* We only get here if we didn't actually read anything.
|
||||
@@ -400,13 +390,13 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
|
||||
if (wait_event_interruptible_exclusive(pipe->rd_wait, pipe_readable(pipe)) < 0)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
was_full = pipe_full(pipe->head, pipe->tail, pipe->max_usage);
|
||||
wake_next_reader = true;
|
||||
}
|
||||
if (pipe_empty(pipe->head, pipe->tail))
|
||||
wake_next_reader = false;
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
if (was_full)
|
||||
wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM);
|
||||
@@ -462,7 +452,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
if (unlikely(total_len == 0))
|
||||
return 0;
|
||||
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
|
||||
if (!pipe->readers) {
|
||||
send_sig(SIGPIPE, current, 0);
|
||||
@@ -582,19 +572,19 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
* after waiting we need to re-check whether the pipe
|
||||
* become empty while we dropped the lock.
|
||||
*/
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
if (was_empty)
|
||||
wake_up_interruptible_sync_poll(&pipe->rd_wait, EPOLLIN | EPOLLRDNORM);
|
||||
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
|
||||
wait_event_interruptible_exclusive(pipe->wr_wait, pipe_writable(pipe));
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
was_empty = pipe_empty(pipe->head, pipe->tail);
|
||||
wake_next_writer = true;
|
||||
}
|
||||
out:
|
||||
if (pipe_full(pipe->head, pipe->tail, pipe->max_usage))
|
||||
wake_next_writer = false;
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
/*
|
||||
* If we do do a wakeup event, we do a 'sync' wakeup, because we
|
||||
@@ -629,7 +619,7 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
switch (cmd) {
|
||||
case FIONREAD:
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
count = 0;
|
||||
head = pipe->head;
|
||||
tail = pipe->tail;
|
||||
@@ -639,16 +629,16 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
count += pipe->bufs[tail & mask].len;
|
||||
tail++;
|
||||
}
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
return put_user(count, (int __user *)arg);
|
||||
|
||||
#ifdef CONFIG_WATCH_QUEUE
|
||||
case IOC_WATCH_QUEUE_SET_SIZE: {
|
||||
int ret;
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
ret = watch_queue_set_size(pipe, arg);
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -734,7 +724,7 @@ pipe_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pipe_inode_info *pipe = file->private_data;
|
||||
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
if (file->f_mode & FMODE_READ)
|
||||
pipe->readers--;
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
@@ -747,7 +737,7 @@ pipe_release(struct inode *inode, struct file *file)
|
||||
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
|
||||
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
|
||||
}
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
put_pipe_info(inode, pipe);
|
||||
return 0;
|
||||
@@ -759,7 +749,7 @@ pipe_fasync(int fd, struct file *filp, int on)
|
||||
struct pipe_inode_info *pipe = filp->private_data;
|
||||
int retval = 0;
|
||||
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
if (filp->f_mode & FMODE_READ)
|
||||
retval = fasync_helper(fd, filp, on, &pipe->fasync_readers);
|
||||
if ((filp->f_mode & FMODE_WRITE) && retval >= 0) {
|
||||
@@ -768,7 +758,7 @@ pipe_fasync(int fd, struct file *filp, int on)
|
||||
/* this can happen only if on == T */
|
||||
fasync_helper(-1, filp, 0, &pipe->fasync_readers);
|
||||
}
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -834,6 +824,7 @@ struct pipe_inode_info *alloc_pipe_info(void)
|
||||
pipe->nr_accounted = pipe_bufs;
|
||||
pipe->user = user;
|
||||
mutex_init(&pipe->mutex);
|
||||
lock_set_cmp_fn(&pipe->mutex, pipe_lock_cmp_fn, NULL);
|
||||
return pipe;
|
||||
}
|
||||
|
||||
@@ -1144,7 +1135,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
|
||||
filp->private_data = pipe;
|
||||
/* OK, we have a pipe and it's pinned down */
|
||||
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
|
||||
/* We can only do regular read/write on fifos */
|
||||
stream_open(inode, filp);
|
||||
@@ -1214,7 +1205,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
|
||||
}
|
||||
|
||||
/* Ok! */
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
return 0;
|
||||
|
||||
err_rd:
|
||||
@@ -1230,7 +1221,7 @@ err_wr:
|
||||
goto err;
|
||||
|
||||
err:
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
put_pipe_info(inode, pipe);
|
||||
return ret;
|
||||
@@ -1411,7 +1402,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned int arg)
|
||||
if (!pipe)
|
||||
return -EBADF;
|
||||
|
||||
__pipe_lock(pipe);
|
||||
mutex_lock(&pipe->mutex);
|
||||
|
||||
switch (cmd) {
|
||||
case F_SETPIPE_SZ:
|
||||
@@ -1425,7 +1416,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned int arg)
|
||||
break;
|
||||
}
|
||||
|
||||
__pipe_unlock(pipe);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ void __init proc_init_kmemcache(void)
|
||||
proc_inode_cachep = kmem_cache_create("proc_inode_cache",
|
||||
sizeof(struct proc_inode),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD|SLAB_ACCOUNT|
|
||||
SLAB_ACCOUNT|
|
||||
SLAB_PANIC),
|
||||
init_once);
|
||||
pde_opener_cache =
|
||||
|
||||
+30
-17
@@ -21,6 +21,7 @@
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include "qnx4.h"
|
||||
|
||||
#define QNX4_VERSION 4
|
||||
@@ -30,28 +31,33 @@ static const struct super_operations qnx4_sops;
|
||||
|
||||
static struct inode *qnx4_alloc_inode(struct super_block *sb);
|
||||
static void qnx4_free_inode(struct inode *inode);
|
||||
static int qnx4_remount(struct super_block *sb, int *flags, char *data);
|
||||
static int qnx4_statfs(struct dentry *, struct kstatfs *);
|
||||
static int qnx4_get_tree(struct fs_context *fc);
|
||||
|
||||
static const struct super_operations qnx4_sops =
|
||||
{
|
||||
.alloc_inode = qnx4_alloc_inode,
|
||||
.free_inode = qnx4_free_inode,
|
||||
.statfs = qnx4_statfs,
|
||||
.remount_fs = qnx4_remount,
|
||||
};
|
||||
|
||||
static int qnx4_remount(struct super_block *sb, int *flags, char *data)
|
||||
static int qnx4_reconfigure(struct fs_context *fc)
|
||||
{
|
||||
struct super_block *sb = fc->root->d_sb;
|
||||
struct qnx4_sb_info *qs;
|
||||
|
||||
sync_filesystem(sb);
|
||||
qs = qnx4_sb(sb);
|
||||
qs->Version = QNX4_VERSION;
|
||||
*flags |= SB_RDONLY;
|
||||
fc->sb_flags |= SB_RDONLY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fs_context_operations qnx4_context_opts = {
|
||||
.get_tree = qnx4_get_tree,
|
||||
.reconfigure = qnx4_reconfigure,
|
||||
};
|
||||
|
||||
static int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_head *bh, int create )
|
||||
{
|
||||
unsigned long phys;
|
||||
@@ -183,12 +189,13 @@ static const char *qnx4_checkroot(struct super_block *sb,
|
||||
return "bitmap file not found.";
|
||||
}
|
||||
|
||||
static int qnx4_fill_super(struct super_block *s, void *data, int silent)
|
||||
static int qnx4_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct inode *root;
|
||||
const char *errmsg;
|
||||
struct qnx4_sb_info *qs;
|
||||
int silent = fc->sb_flags & SB_SILENT;
|
||||
|
||||
qs = kzalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL);
|
||||
if (!qs)
|
||||
@@ -216,7 +223,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent)
|
||||
errmsg = qnx4_checkroot(s, (struct qnx4_super_block *) bh->b_data);
|
||||
brelse(bh);
|
||||
if (errmsg != NULL) {
|
||||
if (!silent)
|
||||
if (!silent)
|
||||
printk(KERN_ERR "qnx4: %s\n", errmsg);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -235,6 +242,18 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qnx4_get_tree(struct fs_context *fc)
|
||||
{
|
||||
return get_tree_bdev(fc, qnx4_fill_super);
|
||||
}
|
||||
|
||||
static int qnx4_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
fc->ops = &qnx4_context_opts;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qnx4_kill_sb(struct super_block *sb)
|
||||
{
|
||||
struct qnx4_sb_info *qs = qnx4_sb(sb);
|
||||
@@ -376,18 +395,12 @@ static void destroy_inodecache(void)
|
||||
kmem_cache_destroy(qnx4_inode_cachep);
|
||||
}
|
||||
|
||||
static struct dentry *qnx4_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return mount_bdev(fs_type, flags, dev_name, data, qnx4_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type qnx4_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "qnx4",
|
||||
.mount = qnx4_mount,
|
||||
.kill_sb = qnx4_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
.owner = THIS_MODULE,
|
||||
.name = "qnx4",
|
||||
.kill_sb = qnx4_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
.init_fs_context = qnx4_init_fs_context,
|
||||
};
|
||||
MODULE_ALIAS_FS("qnx4");
|
||||
|
||||
|
||||
+1
-1
@@ -615,7 +615,7 @@ static int init_inodecache(void)
|
||||
qnx6_inode_cachep = kmem_cache_create("qnx6_inode_cache",
|
||||
sizeof(struct qnx6_inode_info),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
|
||||
SLAB_ACCOUNT),
|
||||
init_once);
|
||||
if (!qnx6_inode_cachep)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -670,7 +670,6 @@ static int __init init_inodecache(void)
|
||||
sizeof(struct
|
||||
reiserfs_inode_info),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD|
|
||||
SLAB_ACCOUNT),
|
||||
init_once);
|
||||
if (reiserfs_inode_cachep == NULL)
|
||||
|
||||
+2
-2
@@ -630,8 +630,8 @@ static int __init init_romfs_fs(void)
|
||||
romfs_inode_cachep =
|
||||
kmem_cache_create("romfs_i",
|
||||
sizeof(struct romfs_inode_info), 0,
|
||||
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD |
|
||||
SLAB_ACCOUNT, romfs_i_init_once);
|
||||
SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT,
|
||||
romfs_i_init_once);
|
||||
|
||||
if (!romfs_inode_cachep) {
|
||||
pr_err("Failed to initialise inode cache\n");
|
||||
|
||||
+10
-10
@@ -11,7 +11,7 @@
|
||||
* parameter to reflect time remaining.
|
||||
*
|
||||
* 24 January 2000
|
||||
* Changed sys_poll()/do_poll() to use PAGE_SIZE chunk-based allocation
|
||||
* Changed sys_poll()/do_poll() to use PAGE_SIZE chunk-based allocation
|
||||
* of fds to overcome nfds < 16390 descriptors limit (Tigran Aivazian).
|
||||
*/
|
||||
|
||||
@@ -476,8 +476,7 @@ static inline void wait_key_set(poll_table *wait, unsigned long in,
|
||||
wait->_key |= POLLOUT_SET;
|
||||
}
|
||||
|
||||
noinline_for_stack
|
||||
static int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
|
||||
static noinline_for_stack int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
|
||||
{
|
||||
ktime_t expire, *to = NULL;
|
||||
struct poll_wqueues table;
|
||||
@@ -649,7 +648,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
|
||||
/*
|
||||
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
|
||||
* since we used fdset we need to allocate memory in units of
|
||||
* long-words.
|
||||
* long-words.
|
||||
*/
|
||||
size = FDS_BYTES(n);
|
||||
bits = stack_fds;
|
||||
@@ -840,7 +839,7 @@ SYSCALL_DEFINE1(old_select, struct sel_arg_struct __user *, arg)
|
||||
|
||||
struct poll_list {
|
||||
struct poll_list *next;
|
||||
int len;
|
||||
unsigned int len;
|
||||
struct pollfd entries[];
|
||||
};
|
||||
|
||||
@@ -976,14 +975,15 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
|
||||
struct timespec64 *end_time)
|
||||
{
|
||||
struct poll_wqueues table;
|
||||
int err = -EFAULT, fdcount, len;
|
||||
int err = -EFAULT, fdcount;
|
||||
/* Allocate small arguments on the stack to save memory and be
|
||||
faster - use long to make sure the buffer is aligned properly
|
||||
on 64 bit archs to avoid unaligned access */
|
||||
long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
|
||||
struct poll_list *const head = (struct poll_list *)stack_pps;
|
||||
struct poll_list *walk = head;
|
||||
unsigned long todo = nfds;
|
||||
unsigned int todo = nfds;
|
||||
unsigned int len;
|
||||
|
||||
if (nfds > rlimit(RLIMIT_NOFILE))
|
||||
return -EINVAL;
|
||||
@@ -999,9 +999,9 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
|
||||
sizeof(struct pollfd) * walk->len))
|
||||
goto out_fds;
|
||||
|
||||
todo -= walk->len;
|
||||
if (!todo)
|
||||
if (walk->len >= todo)
|
||||
break;
|
||||
todo -= walk->len;
|
||||
|
||||
len = min(todo, POLLFD_PER_PAGE);
|
||||
walk = walk->next = kmalloc(struct_size(walk, entries, len),
|
||||
@@ -1021,7 +1021,7 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
|
||||
|
||||
for (walk = head; walk; walk = walk->next) {
|
||||
struct pollfd *fds = walk->entries;
|
||||
int j;
|
||||
unsigned int j;
|
||||
|
||||
for (j = walk->len; j; fds++, ufds++, j--)
|
||||
unsafe_put_user(fds->revents, &ufds->revents, Efault);
|
||||
|
||||
+1
-1
@@ -336,7 +336,7 @@ int __init sysv_init_icache(void)
|
||||
{
|
||||
sysv_inode_cachep = kmem_cache_create("sysv_inode_cache",
|
||||
sizeof(struct sysv_inode_info), 0,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
|
||||
init_once);
|
||||
if (!sysv_inode_cachep)
|
||||
return -ENOMEM;
|
||||
|
||||
+4
-6
@@ -83,9 +83,6 @@ static inline sysv_zone_t *block_end(struct buffer_head *bh)
|
||||
return (sysv_zone_t*)((char*)bh->b_data + bh->b_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Requires read_lock(&pointers_lock) or write_lock(&pointers_lock)
|
||||
*/
|
||||
static Indirect *get_branch(struct inode *inode,
|
||||
int depth,
|
||||
int offsets[],
|
||||
@@ -105,15 +102,18 @@ static Indirect *get_branch(struct inode *inode,
|
||||
bh = sb_bread(sb, block);
|
||||
if (!bh)
|
||||
goto failure;
|
||||
read_lock(&pointers_lock);
|
||||
if (!verify_chain(chain, p))
|
||||
goto changed;
|
||||
add_chain(++p, bh, (sysv_zone_t*)bh->b_data + *++offsets);
|
||||
read_unlock(&pointers_lock);
|
||||
if (!p->key)
|
||||
goto no_block;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
changed:
|
||||
read_unlock(&pointers_lock);
|
||||
brelse(bh);
|
||||
*err = -EAGAIN;
|
||||
goto no_block;
|
||||
@@ -219,9 +219,7 @@ static int get_block(struct inode *inode, sector_t iblock, struct buffer_head *b
|
||||
goto out;
|
||||
|
||||
reread:
|
||||
read_lock(&pointers_lock);
|
||||
partial = get_branch(inode, depth, offsets, chain, &err);
|
||||
read_unlock(&pointers_lock);
|
||||
|
||||
/* Simplest case - block found, no allocation needed */
|
||||
if (!partial) {
|
||||
@@ -291,9 +289,9 @@ static Indirect *find_shared(struct inode *inode,
|
||||
*top = 0;
|
||||
for (k = depth; k > 1 && !offsets[k-1]; k--)
|
||||
;
|
||||
partial = get_branch(inode, k, offsets, chain, &err);
|
||||
|
||||
write_lock(&pointers_lock);
|
||||
partial = get_branch(inode, k, offsets, chain, &err);
|
||||
if (!partial)
|
||||
partial = chain + k-1;
|
||||
/*
|
||||
|
||||
@@ -205,7 +205,6 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
|
||||
|
||||
err = fscrypt_prepare_lookup(dir, dentry, &nm);
|
||||
generic_set_encrypted_ci_d_ops(dentry);
|
||||
if (err == -ENOENT)
|
||||
return d_splice_alias(NULL, dentry);
|
||||
if (err)
|
||||
|
||||
@@ -2239,6 +2239,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto out_umount;
|
||||
}
|
||||
|
||||
generic_set_sb_d_ops(sb);
|
||||
sb->s_root = d_make_root(root);
|
||||
if (!sb->s_root) {
|
||||
err = -ENOMEM;
|
||||
|
||||
@@ -193,7 +193,6 @@ do { \
|
||||
#ifndef smp_store_release
|
||||
#define smp_store_release(p, v) \
|
||||
do { \
|
||||
compiletime_assert_atomic_type(*p); \
|
||||
barrier(); \
|
||||
WRITE_ONCE(*p, v); \
|
||||
} while (0)
|
||||
@@ -203,7 +202,6 @@ do { \
|
||||
#define smp_load_acquire(p) \
|
||||
({ \
|
||||
__unqual_scalar_typeof(*p) ___p1 = READ_ONCE(*p); \
|
||||
compiletime_assert_atomic_type(*p); \
|
||||
barrier(); \
|
||||
(typeof(*p))___p1; \
|
||||
})
|
||||
|
||||
@@ -38,7 +38,6 @@ struct backing_dev_info *bdi_alloc(int node_id);
|
||||
|
||||
void wb_start_background_writeback(struct bdi_writeback *wb);
|
||||
void wb_workfn(struct work_struct *work);
|
||||
void wb_wakeup_delayed(struct bdi_writeback *wb);
|
||||
|
||||
void wb_wait_for_completion(struct wb_completion *done);
|
||||
|
||||
|
||||
+12
-8
@@ -125,6 +125,11 @@ enum dentry_d_lock_class
|
||||
DENTRY_D_LOCK_NESTED
|
||||
};
|
||||
|
||||
enum d_real_type {
|
||||
D_REAL_DATA,
|
||||
D_REAL_METADATA,
|
||||
};
|
||||
|
||||
struct dentry_operations {
|
||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||
@@ -139,7 +144,7 @@ struct dentry_operations {
|
||||
char *(*d_dname)(struct dentry *, char *, int);
|
||||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(const struct path *, bool);
|
||||
struct dentry *(*d_real)(struct dentry *, const struct inode *);
|
||||
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
||||
void (*d_canonical_path)(const struct path *, struct path *);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
@@ -258,7 +263,7 @@ extern int path_has_submounts(const struct path *);
|
||||
* This adds the entry to the hash queues.
|
||||
*/
|
||||
extern void d_rehash(struct dentry *);
|
||||
|
||||
|
||||
extern void d_add(struct dentry *, struct inode *);
|
||||
|
||||
/* used for rename() and baskets */
|
||||
@@ -548,24 +553,23 @@ static inline struct inode *d_backing_inode(const struct dentry *upper)
|
||||
/**
|
||||
* d_real - Return the real dentry
|
||||
* @dentry: the dentry to query
|
||||
* @inode: inode to select the dentry from multiple layers (can be NULL)
|
||||
* @type: the type of real dentry (data or metadata)
|
||||
*
|
||||
* If dentry is on a union/overlay, then return the underlying, real dentry.
|
||||
* Otherwise return the dentry itself.
|
||||
*
|
||||
* See also: Documentation/filesystems/vfs.rst
|
||||
*/
|
||||
static inline struct dentry *d_real(struct dentry *dentry,
|
||||
const struct inode *inode)
|
||||
static inline struct dentry *d_real(struct dentry *dentry, enum d_real_type type)
|
||||
{
|
||||
if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
|
||||
return dentry->d_op->d_real(dentry, inode);
|
||||
return dentry->d_op->d_real(dentry, type);
|
||||
else
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* d_real_inode - Return the real inode
|
||||
* d_real_inode - Return the real inode hosting the data
|
||||
* @dentry: The dentry to query
|
||||
*
|
||||
* If dentry is on a union/overlay, then return the underlying, real inode.
|
||||
@@ -574,7 +578,7 @@ static inline struct dentry *d_real(struct dentry *dentry,
|
||||
static inline struct inode *d_real_inode(const struct dentry *dentry)
|
||||
{
|
||||
/* This usage of d_real() results in const dentry */
|
||||
return d_backing_inode(d_real((struct dentry *) dentry, NULL));
|
||||
return d_inode(d_real((struct dentry *) dentry, D_REAL_DATA));
|
||||
}
|
||||
|
||||
struct name_snapshot {
|
||||
|
||||
+54
-7
@@ -43,6 +43,7 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/mnt_idmapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/maple_tree.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <uapi/linux/fs.h>
|
||||
@@ -484,10 +485,10 @@ struct address_space {
|
||||
pgoff_t writeback_index;
|
||||
const struct address_space_operations *a_ops;
|
||||
unsigned long flags;
|
||||
struct rw_semaphore i_mmap_rwsem;
|
||||
errseq_t wb_err;
|
||||
spinlock_t i_private_lock;
|
||||
struct list_head i_private_list;
|
||||
struct rw_semaphore i_mmap_rwsem;
|
||||
void * i_private_data;
|
||||
} __attribute__((aligned(sizeof(long)))) __randomize_layout;
|
||||
/*
|
||||
@@ -909,7 +910,8 @@ static inline loff_t i_size_read(const struct inode *inode)
|
||||
preempt_enable();
|
||||
return i_size;
|
||||
#else
|
||||
return inode->i_size;
|
||||
/* Pairs with smp_store_release() in i_size_write() */
|
||||
return smp_load_acquire(&inode->i_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -931,7 +933,12 @@ static inline void i_size_write(struct inode *inode, loff_t i_size)
|
||||
inode->i_size = i_size;
|
||||
preempt_enable();
|
||||
#else
|
||||
inode->i_size = i_size;
|
||||
/*
|
||||
* Pairs with smp_load_acquire() in i_size_read() to ensure
|
||||
* changes related to inode size (such as page contents) are
|
||||
* visible before we see the changed inode size.
|
||||
*/
|
||||
smp_store_release(&inode->i_size, i_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1080,9 +1087,20 @@ static inline struct inode *file_inode(const struct file *f)
|
||||
return f->f_inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* file_dentry() is a relic from the days that overlayfs was using files with a
|
||||
* "fake" path, meaning, f_path on overlayfs and f_inode on underlying fs.
|
||||
* In those days, file_dentry() was needed to get the underlying fs dentry that
|
||||
* matches f_inode.
|
||||
* Files with "fake" path should not exist nowadays, so use an assertion to make
|
||||
* sure that file_dentry() was not papering over filesystem bugs.
|
||||
*/
|
||||
static inline struct dentry *file_dentry(const struct file *file)
|
||||
{
|
||||
return d_real(file->f_path.dentry, file_inode(file));
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
|
||||
WARN_ON_ONCE(d_inode(dentry) != file_inode(file));
|
||||
return dentry;
|
||||
}
|
||||
|
||||
struct fasync_struct {
|
||||
@@ -2927,6 +2945,17 @@ extern bool path_is_under(const struct path *, const struct path *);
|
||||
|
||||
extern char *file_path(struct file *, char *, int);
|
||||
|
||||
/**
|
||||
* is_dot_dotdot - returns true only if @name is "." or ".."
|
||||
* @name: file name to check
|
||||
* @len: length of file name, in bytes
|
||||
*/
|
||||
static inline bool is_dot_dotdot(const char *name, size_t len)
|
||||
{
|
||||
return len && unlikely(name[0] == '.') &&
|
||||
(len == 1 || (len == 2 && name[1] == '.'));
|
||||
}
|
||||
|
||||
#include <linux/err.h>
|
||||
|
||||
/* needed for stackable file system support */
|
||||
@@ -3259,13 +3288,14 @@ extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
|
||||
const void __user *from, size_t count);
|
||||
|
||||
struct offset_ctx {
|
||||
struct xarray xa;
|
||||
u32 next_offset;
|
||||
struct maple_tree mt;
|
||||
unsigned long next_offset;
|
||||
};
|
||||
|
||||
void simple_offset_init(struct offset_ctx *octx);
|
||||
int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
|
||||
void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
|
||||
int simple_offset_empty(struct dentry *dentry);
|
||||
int simple_offset_rename_exchange(struct inode *old_dir,
|
||||
struct dentry *old_dentry,
|
||||
struct inode *new_dir,
|
||||
@@ -3279,7 +3309,16 @@ extern int generic_file_fsync(struct file *, loff_t, loff_t, int);
|
||||
|
||||
extern int generic_check_addressable(unsigned, u64);
|
||||
|
||||
extern void generic_set_encrypted_ci_d_ops(struct dentry *dentry);
|
||||
extern void generic_set_sb_d_ops(struct super_block *sb);
|
||||
|
||||
static inline bool sb_has_encoding(const struct super_block *sb)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
return !!sb->s_encoding;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int may_setattr(struct mnt_idmap *idmap, struct inode *inode,
|
||||
unsigned int ia_valid);
|
||||
@@ -3334,6 +3373,8 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags)
|
||||
return 0;
|
||||
if (unlikely(flags & ~RWF_SUPPORTED))
|
||||
return -EOPNOTSUPP;
|
||||
if (unlikely((flags & RWF_APPEND) && (flags & RWF_NOAPPEND)))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & RWF_NOWAIT) {
|
||||
if (!(ki->ki_filp->f_mode & FMODE_NOWAIT))
|
||||
@@ -3344,6 +3385,12 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags)
|
||||
if (flags & RWF_SYNC)
|
||||
kiocb_flags |= IOCB_DSYNC;
|
||||
|
||||
if ((flags & RWF_NOAPPEND) && (ki->ki_flags & IOCB_APPEND)) {
|
||||
if (IS_APPEND(file_inode(ki->ki_filp)))
|
||||
return -EPERM;
|
||||
ki->ki_flags &= ~IOCB_APPEND;
|
||||
}
|
||||
|
||||
ki->ki_flags |= kiocb_flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
+59
-7
@@ -192,6 +192,8 @@ struct fscrypt_operations {
|
||||
unsigned int *num_devs);
|
||||
};
|
||||
|
||||
int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags);
|
||||
|
||||
static inline struct fscrypt_inode_info *
|
||||
fscrypt_get_inode_info(const struct inode *inode)
|
||||
{
|
||||
@@ -221,15 +223,29 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
|
||||
}
|
||||
|
||||
/*
|
||||
* When d_splice_alias() moves a directory's no-key alias to its plaintext alias
|
||||
* as a result of the encryption key being added, DCACHE_NOKEY_NAME must be
|
||||
* cleared. Note that we don't have to support arbitrary moves of this flag
|
||||
* because fscrypt doesn't allow no-key names to be the source or target of a
|
||||
* rename().
|
||||
* When d_splice_alias() moves a directory's no-key alias to its
|
||||
* plaintext alias as a result of the encryption key being added,
|
||||
* DCACHE_NOKEY_NAME must be cleared and there might be an opportunity
|
||||
* to disable d_revalidate. Note that we don't have to support the
|
||||
* inverse operation because fscrypt doesn't allow no-key names to be
|
||||
* the source or target of a rename().
|
||||
*/
|
||||
static inline void fscrypt_handle_d_move(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags &= ~DCACHE_NOKEY_NAME;
|
||||
/*
|
||||
* VFS calls fscrypt_handle_d_move even for non-fscrypt
|
||||
* filesystems.
|
||||
*/
|
||||
if (dentry->d_flags & DCACHE_NOKEY_NAME) {
|
||||
dentry->d_flags &= ~DCACHE_NOKEY_NAME;
|
||||
|
||||
/*
|
||||
* Other filesystem features might be handling dentry
|
||||
* revalidation, in which case it cannot be disabled.
|
||||
*/
|
||||
if (dentry->d_op->d_revalidate == fscrypt_d_revalidate)
|
||||
dentry->d_flags &= ~DCACHE_OP_REVALIDATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,6 +277,35 @@ static inline bool fscrypt_is_nokey_name(const struct dentry *dentry)
|
||||
return dentry->d_flags & DCACHE_NOKEY_NAME;
|
||||
}
|
||||
|
||||
static inline void fscrypt_prepare_dentry(struct dentry *dentry,
|
||||
bool is_nokey_name)
|
||||
{
|
||||
/*
|
||||
* This code tries to only take ->d_lock when necessary to write
|
||||
* to ->d_flags. We shouldn't be peeking on d_flags for
|
||||
* DCACHE_OP_REVALIDATE unlocked, but in the unlikely case
|
||||
* there is a race, the worst it can happen is that we fail to
|
||||
* unset DCACHE_OP_REVALIDATE and pay the cost of an extra
|
||||
* d_revalidate.
|
||||
*/
|
||||
if (is_nokey_name) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_NOKEY_NAME;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
} else if (dentry->d_flags & DCACHE_OP_REVALIDATE &&
|
||||
dentry->d_op->d_revalidate == fscrypt_d_revalidate) {
|
||||
/*
|
||||
* Unencrypted dentries and encrypted dentries where the
|
||||
* key is available are always valid from fscrypt
|
||||
* perspective. Avoid the cost of calling
|
||||
* fscrypt_d_revalidate unnecessarily.
|
||||
*/
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags &= ~DCACHE_OP_REVALIDATE;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* crypto.c */
|
||||
void fscrypt_enqueue_decrypt_work(struct work_struct *);
|
||||
|
||||
@@ -368,7 +413,6 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode,
|
||||
bool fscrypt_match_name(const struct fscrypt_name *fname,
|
||||
const u8 *de_name, u32 de_name_len);
|
||||
u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name);
|
||||
int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags);
|
||||
|
||||
/* bio.c */
|
||||
bool fscrypt_decrypt_bio(struct bio *bio);
|
||||
@@ -425,6 +469,11 @@ static inline bool fscrypt_is_nokey_name(const struct dentry *dentry)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void fscrypt_prepare_dentry(struct dentry *dentry,
|
||||
bool is_nokey_name)
|
||||
{
|
||||
}
|
||||
|
||||
/* crypto.c */
|
||||
static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work)
|
||||
{
|
||||
@@ -996,6 +1045,9 @@ static inline int fscrypt_prepare_lookup(struct inode *dir,
|
||||
fname->usr_fname = &dentry->d_name;
|
||||
fname->disk_name.name = (unsigned char *)dentry->d_name.name;
|
||||
fname->disk_name.len = dentry->d_name.len;
|
||||
|
||||
fscrypt_prepare_dentry(dentry, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ enum maple_type {
|
||||
#define MT_FLAGS_LOCK_IRQ 0x100
|
||||
#define MT_FLAGS_LOCK_BH 0x200
|
||||
#define MT_FLAGS_LOCK_EXTERN 0x300
|
||||
#define MT_FLAGS_ALLOC_WRAPPED 0x0800
|
||||
|
||||
#define MAPLE_HEIGHT_MAX 31
|
||||
|
||||
@@ -319,6 +320,9 @@ int mtree_insert_range(struct maple_tree *mt, unsigned long first,
|
||||
int mtree_alloc_range(struct maple_tree *mt, unsigned long *startp,
|
||||
void *entry, unsigned long size, unsigned long min,
|
||||
unsigned long max, gfp_t gfp);
|
||||
int mtree_alloc_cyclic(struct maple_tree *mt, unsigned long *startp,
|
||||
void *entry, unsigned long range_lo, unsigned long range_hi,
|
||||
unsigned long *next, gfp_t gfp);
|
||||
int mtree_alloc_rrange(struct maple_tree *mt, unsigned long *startp,
|
||||
void *entry, unsigned long size, unsigned long min,
|
||||
unsigned long max, gfp_t gfp);
|
||||
@@ -499,6 +503,9 @@ void *mas_find_range(struct ma_state *mas, unsigned long max);
|
||||
void *mas_find_rev(struct ma_state *mas, unsigned long min);
|
||||
void *mas_find_range_rev(struct ma_state *mas, unsigned long max);
|
||||
int mas_preallocate(struct ma_state *mas, void *entry, gfp_t gfp);
|
||||
int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp,
|
||||
void *entry, unsigned long range_lo, unsigned long range_hi,
|
||||
unsigned long *next, gfp_t gfp);
|
||||
|
||||
bool mas_nomem(struct ma_state *mas, gfp_t gfp);
|
||||
void mas_pause(struct ma_state *mas);
|
||||
|
||||
@@ -301,9 +301,12 @@ typedef int __bitwise __kernel_rwf_t;
|
||||
/* per-IO O_APPEND */
|
||||
#define RWF_APPEND ((__force __kernel_rwf_t)0x00000010)
|
||||
|
||||
/* per-IO negation of O_APPEND */
|
||||
#define RWF_NOAPPEND ((__force __kernel_rwf_t)0x00000020)
|
||||
|
||||
/* mask of flags supported by the kernel */
|
||||
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\
|
||||
RWF_APPEND)
|
||||
RWF_APPEND | RWF_NOAPPEND)
|
||||
|
||||
/* Pagemap ioctl */
|
||||
#define PAGEMAP_SCAN _IOWR('f', 16, struct pm_scan_arg)
|
||||
|
||||
@@ -679,8 +679,6 @@ static void __init populate_initrd_image(char *err)
|
||||
struct file *file;
|
||||
loff_t pos = 0;
|
||||
|
||||
unpack_to_rootfs(__initramfs_start, __initramfs_size);
|
||||
|
||||
printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd\n",
|
||||
err);
|
||||
file = filp_open("/initrd.image", O_WRONLY | O_CREAT, 0700);
|
||||
|
||||
+31
-29
@@ -691,12 +691,11 @@ EXPORT_SYMBOL(iov_iter_discard);
|
||||
static bool iov_iter_aligned_iovec(const struct iov_iter *i, unsigned addr_mask,
|
||||
unsigned len_mask)
|
||||
{
|
||||
const struct iovec *iov = iter_iov(i);
|
||||
size_t size = i->count;
|
||||
size_t skip = i->iov_offset;
|
||||
unsigned k;
|
||||
|
||||
for (k = 0; k < i->nr_segs; k++, skip = 0) {
|
||||
const struct iovec *iov = iter_iov(i) + k;
|
||||
do {
|
||||
size_t len = iov->iov_len - skip;
|
||||
|
||||
if (len > size)
|
||||
@@ -706,34 +705,36 @@ static bool iov_iter_aligned_iovec(const struct iov_iter *i, unsigned addr_mask,
|
||||
if ((unsigned long)(iov->iov_base + skip) & addr_mask)
|
||||
return false;
|
||||
|
||||
iov++;
|
||||
size -= len;
|
||||
if (!size)
|
||||
break;
|
||||
}
|
||||
skip = 0;
|
||||
} while (size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool iov_iter_aligned_bvec(const struct iov_iter *i, unsigned addr_mask,
|
||||
unsigned len_mask)
|
||||
{
|
||||
size_t size = i->count;
|
||||
const struct bio_vec *bvec = i->bvec;
|
||||
unsigned skip = i->iov_offset;
|
||||
unsigned k;
|
||||
size_t size = i->count;
|
||||
|
||||
for (k = 0; k < i->nr_segs; k++, skip = 0) {
|
||||
size_t len = i->bvec[k].bv_len - skip;
|
||||
do {
|
||||
size_t len = bvec->bv_len;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
if (len & len_mask)
|
||||
return false;
|
||||
if ((unsigned long)(i->bvec[k].bv_offset + skip) & addr_mask)
|
||||
if ((unsigned long)(bvec->bv_offset + skip) & addr_mask)
|
||||
return false;
|
||||
|
||||
bvec++;
|
||||
size -= len;
|
||||
if (!size)
|
||||
break;
|
||||
}
|
||||
skip = 0;
|
||||
} while (size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -777,13 +778,12 @@ EXPORT_SYMBOL_GPL(iov_iter_is_aligned);
|
||||
|
||||
static unsigned long iov_iter_alignment_iovec(const struct iov_iter *i)
|
||||
{
|
||||
const struct iovec *iov = iter_iov(i);
|
||||
unsigned long res = 0;
|
||||
size_t size = i->count;
|
||||
size_t skip = i->iov_offset;
|
||||
unsigned k;
|
||||
|
||||
for (k = 0; k < i->nr_segs; k++, skip = 0) {
|
||||
const struct iovec *iov = iter_iov(i) + k;
|
||||
do {
|
||||
size_t len = iov->iov_len - skip;
|
||||
if (len) {
|
||||
res |= (unsigned long)iov->iov_base + skip;
|
||||
@@ -791,30 +791,31 @@ static unsigned long iov_iter_alignment_iovec(const struct iov_iter *i)
|
||||
len = size;
|
||||
res |= len;
|
||||
size -= len;
|
||||
if (!size)
|
||||
break;
|
||||
}
|
||||
}
|
||||
iov++;
|
||||
skip = 0;
|
||||
} while (size);
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned long iov_iter_alignment_bvec(const struct iov_iter *i)
|
||||
{
|
||||
const struct bio_vec *bvec = i->bvec;
|
||||
unsigned res = 0;
|
||||
size_t size = i->count;
|
||||
unsigned skip = i->iov_offset;
|
||||
unsigned k;
|
||||
|
||||
for (k = 0; k < i->nr_segs; k++, skip = 0) {
|
||||
size_t len = i->bvec[k].bv_len - skip;
|
||||
res |= (unsigned long)i->bvec[k].bv_offset + skip;
|
||||
do {
|
||||
size_t len = bvec->bv_len - skip;
|
||||
res |= (unsigned long)bvec->bv_offset + skip;
|
||||
if (len > size)
|
||||
len = size;
|
||||
res |= len;
|
||||
bvec++;
|
||||
size -= len;
|
||||
if (!size)
|
||||
break;
|
||||
}
|
||||
skip = 0;
|
||||
} while (size);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1143,11 +1144,12 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
|
||||
EXPORT_SYMBOL(dup_iter);
|
||||
|
||||
static __noclone int copy_compat_iovec_from_user(struct iovec *iov,
|
||||
const struct iovec __user *uvec, unsigned long nr_segs)
|
||||
const struct iovec __user *uvec, u32 nr_segs)
|
||||
{
|
||||
const struct compat_iovec __user *uiov =
|
||||
(const struct compat_iovec __user *)uvec;
|
||||
int ret = -EFAULT, i;
|
||||
int ret = -EFAULT;
|
||||
u32 i;
|
||||
|
||||
if (!user_access_begin(uiov, nr_segs * sizeof(*uiov)))
|
||||
return -EFAULT;
|
||||
|
||||
@@ -4290,6 +4290,56 @@ exists:
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* mas_alloc_cyclic() - Internal call to find somewhere to store an entry
|
||||
* @mas: The maple state.
|
||||
* @startp: Pointer to ID.
|
||||
* @range_lo: Lower bound of range to search.
|
||||
* @range_hi: Upper bound of range to search.
|
||||
* @entry: The entry to store.
|
||||
* @next: Pointer to next ID to allocate.
|
||||
* @gfp: The GFP_FLAGS to use for allocations.
|
||||
*
|
||||
* Return: 0 if the allocation succeeded without wrapping, 1 if the
|
||||
* allocation succeeded after wrapping, or -EBUSY if there are no
|
||||
* free entries.
|
||||
*/
|
||||
int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp,
|
||||
void *entry, unsigned long range_lo, unsigned long range_hi,
|
||||
unsigned long *next, gfp_t gfp)
|
||||
{
|
||||
unsigned long min = range_lo;
|
||||
int ret = 0;
|
||||
|
||||
range_lo = max(min, *next);
|
||||
ret = mas_empty_area(mas, range_lo, range_hi, 1);
|
||||
if ((mas->tree->ma_flags & MT_FLAGS_ALLOC_WRAPPED) && ret == 0) {
|
||||
mas->tree->ma_flags &= ~MT_FLAGS_ALLOC_WRAPPED;
|
||||
ret = 1;
|
||||
}
|
||||
if (ret < 0 && range_lo > min) {
|
||||
ret = mas_empty_area(mas, min, range_hi, 1);
|
||||
if (ret == 0)
|
||||
ret = 1;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
do {
|
||||
mas_insert(mas, entry);
|
||||
} while (mas_nomem(mas, gfp));
|
||||
if (mas_is_err(mas))
|
||||
return xa_err(mas->node);
|
||||
|
||||
*startp = mas->index;
|
||||
*next = *startp + 1;
|
||||
if (*next == 0)
|
||||
mas->tree->ma_flags |= MT_FLAGS_ALLOC_WRAPPED;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mas_alloc_cyclic);
|
||||
|
||||
static __always_inline void mas_rewalk(struct ma_state *mas, unsigned long index)
|
||||
{
|
||||
retry:
|
||||
@@ -6443,6 +6493,49 @@ unlock:
|
||||
}
|
||||
EXPORT_SYMBOL(mtree_alloc_range);
|
||||
|
||||
/**
|
||||
* mtree_alloc_cyclic() - Find somewhere to store this entry in the tree.
|
||||
* @mt: The maple tree.
|
||||
* @startp: Pointer to ID.
|
||||
* @range_lo: Lower bound of range to search.
|
||||
* @range_hi: Upper bound of range to search.
|
||||
* @entry: The entry to store.
|
||||
* @next: Pointer to next ID to allocate.
|
||||
* @gfp: The GFP_FLAGS to use for allocations.
|
||||
*
|
||||
* Finds an empty entry in @mt after @next, stores the new index into
|
||||
* the @id pointer, stores the entry at that index, then updates @next.
|
||||
*
|
||||
* @mt must be initialized with the MT_FLAGS_ALLOC_RANGE flag.
|
||||
*
|
||||
* Context: Any context. Takes and releases the mt.lock. May sleep if
|
||||
* the @gfp flags permit.
|
||||
*
|
||||
* Return: 0 if the allocation succeeded without wrapping, 1 if the
|
||||
* allocation succeeded after wrapping, -ENOMEM if memory could not be
|
||||
* allocated, -EINVAL if @mt cannot be used, or -EBUSY if there are no
|
||||
* free entries.
|
||||
*/
|
||||
int mtree_alloc_cyclic(struct maple_tree *mt, unsigned long *startp,
|
||||
void *entry, unsigned long range_lo, unsigned long range_hi,
|
||||
unsigned long *next, gfp_t gfp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
MA_STATE(mas, mt, 0, 0);
|
||||
|
||||
if (!mt_is_alloc(mt))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(mt_is_reserved(entry)))
|
||||
return -EINVAL;
|
||||
mtree_lock(mt);
|
||||
ret = mas_alloc_cyclic(&mas, startp, entry, range_lo, range_hi,
|
||||
next, gfp);
|
||||
mtree_unlock(mt);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mtree_alloc_cyclic);
|
||||
|
||||
int mtree_alloc_rrange(struct maple_tree *mt, unsigned long *startp,
|
||||
void *entry, unsigned long size, unsigned long min,
|
||||
unsigned long max, gfp_t gfp)
|
||||
|
||||
@@ -3599,6 +3599,45 @@ static noinline void __init check_state_handling(struct maple_tree *mt)
|
||||
mas_unlock(&mas);
|
||||
}
|
||||
|
||||
static noinline void __init alloc_cyclic_testing(struct maple_tree *mt)
|
||||
{
|
||||
unsigned long location;
|
||||
unsigned long next;
|
||||
int ret = 0;
|
||||
MA_STATE(mas, mt, 0, 0);
|
||||
|
||||
next = 0;
|
||||
mtree_lock(mt);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
mas_alloc_cyclic(&mas, &location, mt, 2, ULONG_MAX, &next, GFP_KERNEL);
|
||||
MAS_BUG_ON(&mas, i != location - 2);
|
||||
MAS_BUG_ON(&mas, mas.index != location);
|
||||
MAS_BUG_ON(&mas, mas.last != location);
|
||||
MAS_BUG_ON(&mas, i != next - 3);
|
||||
}
|
||||
|
||||
mtree_unlock(mt);
|
||||
mtree_destroy(mt);
|
||||
next = 0;
|
||||
mt_init_flags(mt, MT_FLAGS_ALLOC_RANGE);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
mtree_alloc_cyclic(mt, &location, mt, 2, ULONG_MAX, &next, GFP_KERNEL);
|
||||
MT_BUG_ON(mt, i != location - 2);
|
||||
MT_BUG_ON(mt, i != next - 3);
|
||||
MT_BUG_ON(mt, mtree_load(mt, location) != mt);
|
||||
}
|
||||
|
||||
mtree_destroy(mt);
|
||||
/* Overflow test */
|
||||
next = ULONG_MAX - 1;
|
||||
ret = mtree_alloc_cyclic(mt, &location, mt, 2, ULONG_MAX, &next, GFP_KERNEL);
|
||||
MT_BUG_ON(mt, ret != 0);
|
||||
ret = mtree_alloc_cyclic(mt, &location, mt, 2, ULONG_MAX, &next, GFP_KERNEL);
|
||||
MT_BUG_ON(mt, ret != 0);
|
||||
ret = mtree_alloc_cyclic(mt, &location, mt, 2, ULONG_MAX, &next, GFP_KERNEL);
|
||||
MT_BUG_ON(mt, ret != 1);
|
||||
}
|
||||
|
||||
static DEFINE_MTREE(tree);
|
||||
static int __init maple_tree_seed(void)
|
||||
{
|
||||
@@ -3880,6 +3919,11 @@ static int __init maple_tree_seed(void)
|
||||
check_state_handling(&tree);
|
||||
mtree_destroy(&tree);
|
||||
|
||||
mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE);
|
||||
alloc_cyclic_testing(&tree);
|
||||
mtree_destroy(&tree);
|
||||
|
||||
|
||||
#if defined(BENCH)
|
||||
skip:
|
||||
#endif
|
||||
|
||||
@@ -372,31 +372,6 @@ static int __init default_bdi_init(void)
|
||||
}
|
||||
subsys_initcall(default_bdi_init);
|
||||
|
||||
/*
|
||||
* This function is used when the first inode for this wb is marked dirty. It
|
||||
* wakes-up the corresponding bdi thread which should then take care of the
|
||||
* periodic background write-out of dirty inodes. Since the write-out would
|
||||
* starts only 'dirty_writeback_interval' centisecs from now anyway, we just
|
||||
* set up a timer which wakes the bdi thread up later.
|
||||
*
|
||||
* Note, we wouldn't bother setting up the timer, but this function is on the
|
||||
* fast-path (used by '__mark_inode_dirty()'), so we save few context switches
|
||||
* by delaying the wake-up.
|
||||
*
|
||||
* We have to be careful not to postpone flush work if it is scheduled for
|
||||
* earlier. Thus we use queue_delayed_work().
|
||||
*/
|
||||
void wb_wakeup_delayed(struct bdi_writeback *wb)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
|
||||
spin_lock_irq(&wb->work_lock);
|
||||
if (test_bit(WB_registered, &wb->state))
|
||||
queue_delayed_work(bdi_wq, &wb->dwork, timeout);
|
||||
spin_unlock_irq(&wb->work_lock);
|
||||
}
|
||||
|
||||
static void wb_update_bandwidth_workfn(struct work_struct *work)
|
||||
{
|
||||
struct bdi_writeback *wb = container_of(to_delayed_work(work),
|
||||
|
||||
@@ -2608,15 +2608,6 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,
|
||||
goto put_folios;
|
||||
end_offset = min_t(loff_t, isize, iocb->ki_pos + iter->count);
|
||||
|
||||
/*
|
||||
* Pairs with a barrier in
|
||||
* block_write_end()->mark_buffer_dirty() or other page
|
||||
* dirtying routines like iomap_write_end() to ensure
|
||||
* changes to page contents are visible before we see
|
||||
* increased inode size.
|
||||
*/
|
||||
smp_rmb();
|
||||
|
||||
/*
|
||||
* Once we start copying data, we don't want to be touching any
|
||||
* cachelines that might be contended:
|
||||
|
||||
+2
-2
@@ -3374,7 +3374,7 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
if (!simple_empty(dentry))
|
||||
if (!simple_offset_empty(dentry))
|
||||
return -ENOTEMPTY;
|
||||
|
||||
drop_nlink(d_inode(dentry));
|
||||
@@ -3431,7 +3431,7 @@ static int shmem_rename2(struct mnt_idmap *idmap,
|
||||
return simple_offset_rename_exchange(old_dir, old_dentry,
|
||||
new_dir, new_dentry);
|
||||
|
||||
if (!simple_empty(new_dentry))
|
||||
if (!simple_offset_empty(new_dentry))
|
||||
return -ENOTEMPTY;
|
||||
|
||||
if (flags & RENAME_WHITEOUT) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <linux/mount.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sched.h>
|
||||
#include <fcntl.h>
|
||||
@@ -32,7 +31,11 @@ static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags)
|
||||
{
|
||||
return syscall(__NR_fsmount, fd, flags, attr_flags);
|
||||
}
|
||||
|
||||
static int sys_mount(const char *src, const char *tgt, const char *fst,
|
||||
unsigned long flags, const void *data)
|
||||
{
|
||||
return syscall(__NR_mount, src, tgt, fst, flags, data);
|
||||
}
|
||||
static int sys_move_mount(int from_dfd, const char *from_pathname,
|
||||
int to_dfd, const char *to_pathname,
|
||||
unsigned int flags)
|
||||
@@ -166,8 +169,7 @@ int main(int argc, char **argv)
|
||||
ksft_test_result_skip("unable to create a new mount namespace\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) {
|
||||
if (sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) {
|
||||
pr_perror("mount");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ static bool move_mount_set_group_supported(void)
|
||||
if (mount(NULL, SET_GROUP_FROM, NULL, MS_SHARED, 0))
|
||||
return -1;
|
||||
|
||||
ret = syscall(SYS_move_mount, AT_FDCWD, SET_GROUP_FROM,
|
||||
ret = syscall(__NR_move_mount, AT_FDCWD, SET_GROUP_FROM,
|
||||
AT_FDCWD, SET_GROUP_TO, MOVE_MOUNT_SET_GROUP);
|
||||
umount2("/tmp", MNT_DETACH);
|
||||
|
||||
@@ -363,7 +363,7 @@ TEST_F(move_mount_set_group, complex_sharing_copying)
|
||||
CLONE_VM | CLONE_FILES); ASSERT_GT(pid, 0);
|
||||
ASSERT_EQ(wait_for_pid(pid), 0);
|
||||
|
||||
ASSERT_EQ(syscall(SYS_move_mount, ca_from.mntfd, "",
|
||||
ASSERT_EQ(syscall(__NR_move_mount, ca_from.mntfd, "",
|
||||
ca_to.mntfd, "", MOVE_MOUNT_SET_GROUP
|
||||
| MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_T_EMPTY_PATH),
|
||||
0);
|
||||
|
||||
Reference in New Issue
Block a user