diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/aio.c | 2 | ||||
-rw-r--r-- | fs/ceph/super.c | 4 | ||||
-rw-r--r-- | fs/ceph/super.h | 4 | ||||
-rw-r--r-- | fs/fuse/dir.c | 26 | ||||
-rw-r--r-- | fs/fuse/file.c | 64 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 4 | ||||
-rw-r--r-- | fs/fuse/inode.c | 3 | ||||
-rw-r--r-- | fs/overlayfs/dir.c | 14 | ||||
-rw-r--r-- | fs/overlayfs/export.c | 6 | ||||
-rw-r--r-- | fs/overlayfs/inode.c | 17 | ||||
-rw-r--r-- | fs/userfaultfd.c | 3 |
11 files changed, 89 insertions, 58 deletions
@@ -45,6 +45,7 @@ #include <asm/kmap_types.h> #include <linux/uaccess.h> +#include <linux/nospec.h> #include "internal.h" @@ -1038,6 +1039,7 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) if (!table || id >= table->nr) goto out; + id = array_index_nospec(id, table->nr); ctx = rcu_dereference(table->table[id]); if (ctx && ctx->user_id == ctx_id) { if (percpu_ref_tryget_live(&ctx->users)) diff --git a/fs/ceph/super.c b/fs/ceph/super.c index b5ecd6f50360..4e9a7cc488da 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -563,8 +563,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) seq_puts(m, ",noacl"); #endif - if (fsopt->flags & CEPH_MOUNT_OPT_NOCOPYFROM) - seq_puts(m, ",nocopyfrom"); + if ((fsopt->flags & CEPH_MOUNT_OPT_NOCOPYFROM) == 0) + seq_puts(m, ",copyfrom"); if (fsopt->mds_namespace) seq_show_option(m, "mds_namespace", fsopt->mds_namespace); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index c005a5400f2e..79a265ba9200 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -42,7 +42,9 @@ #define CEPH_MOUNT_OPT_NOQUOTADF (1<<13) /* no root dir quota in statfs */ #define CEPH_MOUNT_OPT_NOCOPYFROM (1<<14) /* don't use RADOS 'copy-from' op */ -#define CEPH_MOUNT_OPT_DEFAULT CEPH_MOUNT_OPT_DCACHE +#define CEPH_MOUNT_OPT_DEFAULT \ + (CEPH_MOUNT_OPT_DCACHE | \ + CEPH_MOUNT_OPT_NOCOPYFROM) #define ceph_set_mount_opt(fsc, opt) \ (fsc)->mount_options->flags |= CEPH_MOUNT_OPT_##opt; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 47395b0c3b35..e909678afa2d 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1119,8 +1119,10 @@ static int fuse_permission(struct inode *inode, int mask) if (fc->default_permissions || ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { struct fuse_inode *fi = get_fuse_inode(inode); + u32 perm_mask = STATX_MODE | STATX_UID | STATX_GID; - if (time_before64(fi->i_time, get_jiffies_64())) { + if (perm_mask & READ_ONCE(fi->inval_mask) || + time_before64(fi->i_time, get_jiffies_64())) { refreshed = true; err = fuse_perm_getattr(inode, mask); @@ -1241,7 +1243,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file) static int fuse_dir_release(struct inode *inode, struct file *file) { - fuse_release_common(file, FUSE_RELEASEDIR); + fuse_release_common(file, true); return 0; } @@ -1249,7 +1251,25 @@ static int fuse_dir_release(struct inode *inode, struct file *file) static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync) { - return fuse_fsync_common(file, start, end, datasync, 1); + struct inode *inode = file->f_mapping->host; + struct fuse_conn *fc = get_fuse_conn(inode); + int err; + + if (is_bad_inode(inode)) + return -EIO; + + if (fc->no_fsyncdir) + return 0; + + inode_lock(inode); + err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNCDIR); + if (err == -ENOSYS) { + fc->no_fsyncdir = 1; + err = 0; + } + inode_unlock(inode); + + return err; } static long fuse_dir_ioctl(struct file *file, unsigned int cmd, diff --git a/fs/fuse/file.c b/fs/fuse/file.c index b52f9baaa3e7..ffaffe18352a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -89,12 +89,12 @@ static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) iput(req->misc.release.inode); } -static void fuse_file_put(struct fuse_file *ff, bool sync) +static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir) { if (refcount_dec_and_test(&ff->count)) { struct fuse_req *req = ff->reserved_req; - if (ff->fc->no_open) { + if (ff->fc->no_open && !isdir) { /* * Drop the release request when client does not * implement 'open' @@ -247,10 +247,11 @@ static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode) req->in.args[0].value = inarg; } -void fuse_release_common(struct file *file, int opcode) +void fuse_release_common(struct file *file, bool isdir) { struct fuse_file *ff = file->private_data; struct fuse_req *req = ff->reserved_req; + int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; fuse_prepare_release(ff, file->f_flags, opcode); @@ -272,7 +273,7 @@ void fuse_release_common(struct file *file, int opcode) * synchronous RELEASE is allowed (and desirable) in this case * because the server can be trusted not to screw up. */ - fuse_file_put(ff, ff->fc->destroy_req != NULL); + fuse_file_put(ff, ff->fc->destroy_req != NULL, isdir); } static int fuse_open(struct inode *inode, struct file *file) @@ -288,7 +289,7 @@ static int fuse_release(struct inode *inode, struct file *file) if (fc->writeback_cache) write_inode_now(inode, 1); - fuse_release_common(file, FUSE_RELEASE); + fuse_release_common(file, false); /* return value is ignored by VFS */ return 0; @@ -302,7 +303,7 @@ void fuse_sync_release(struct fuse_file *ff, int flags) * iput(NULL) is a no-op and since the refcount is 1 and everything's * synchronous, we are fine with not doing igrab() here" */ - fuse_file_put(ff, true); + fuse_file_put(ff, true, false); } EXPORT_SYMBOL_GPL(fuse_sync_release); @@ -441,13 +442,30 @@ static int fuse_flush(struct file *file, fl_owner_t id) } int fuse_fsync_common(struct file *file, loff_t start, loff_t end, - int datasync, int isdir) + int datasync, int opcode) { struct inode *inode = file->f_mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; FUSE_ARGS(args); struct fuse_fsync_in inarg; + + memset(&inarg, 0, sizeof(inarg)); + inarg.fh = ff->fh; + inarg.fsync_flags = datasync ? 1 : 0; + args.in.h.opcode = opcode; + args.in.h.nodeid = get_node_id(inode); + args.in.numargs = 1; + args.in.args[0].size = sizeof(inarg); + args.in.args[0].value = &inarg; + return fuse_simple_request(fc, &args); +} + +static int fuse_fsync(struct file *file, loff_t start, loff_t end, + int datasync) +{ + struct inode *inode = file->f_mapping->host; + struct fuse_conn *fc = get_fuse_conn(inode); int err; if (is_bad_inode(inode)) @@ -479,34 +497,18 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, if (err) goto out; - if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) + if (fc->no_fsync) goto out; - memset(&inarg, 0, sizeof(inarg)); - inarg.fh = ff->fh; - inarg.fsync_flags = datasync ? 1 : 0; - args.in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC; - args.in.h.nodeid = get_node_id(inode); - args.in.numargs = 1; - args.in.args[0].size = sizeof(inarg); - args.in.args[0].value = &inarg; - err = fuse_simple_request(fc, &args); + err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNC); if (err == -ENOSYS) { - if (isdir) - fc->no_fsyncdir = 1; - else - fc->no_fsync = 1; + fc->no_fsync = 1; err = 0; } out: inode_unlock(inode); - return err; -} -static int fuse_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - return fuse_fsync_common(file, start, end, datasync, 0); + return err; } void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos, @@ -807,7 +809,7 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) put_page(page); } if (req->ff) - fuse_file_put(req->ff, false); + fuse_file_put(req->ff, false, false); } static void fuse_send_readpages(struct fuse_req *req, struct file *file) @@ -1460,7 +1462,7 @@ static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) __free_page(req->pages[i]); if (req->ff) - fuse_file_put(req->ff, false); + fuse_file_put(req->ff, false, false); } static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) @@ -1619,7 +1621,7 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) ff = __fuse_write_file_get(fc, fi); err = fuse_flush_times(inode, ff); if (ff) - fuse_file_put(ff, 0); + fuse_file_put(ff, false, false); return err; } @@ -1940,7 +1942,7 @@ static int fuse_writepages(struct address_space *mapping, err = 0; } if (data.ff) - fuse_file_put(data.ff, false); + fuse_file_put(data.ff, false, false); kfree(data.orig_pages); out: diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e9f712e81c7d..2f2c92e6f8cb 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -822,13 +822,13 @@ void fuse_sync_release(struct fuse_file *ff, int flags); /** * Send RELEASE or RELEASEDIR request */ -void fuse_release_common(struct file *file, int opcode); +void fuse_release_common(struct file *file, bool isdir); /** * Send FSYNC or FSYNCDIR request */ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, - int datasync, int isdir); + int datasync, int opcode); /** * Notify poll wakeup diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 0b94b23b02d4..568abed20eb2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -115,7 +115,7 @@ static void fuse_i_callback(struct rcu_head *head) static void fuse_destroy_inode(struct inode *inode) { struct fuse_inode *fi = get_fuse_inode(inode); - if (S_ISREG(inode->i_mode)) { + if (S_ISREG(inode->i_mode) && !is_bad_inode(inode)) { WARN_ON(!list_empty(&fi->write_files)); WARN_ON(!list_empty(&fi->queued_writes)); } @@ -1068,6 +1068,7 @@ void fuse_dev_free(struct fuse_dev *fud) fuse_conn_put(fc); } + kfree(fud->pq.processing); kfree(fud); } EXPORT_SYMBOL_GPL(fuse_dev_free); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c6289147c787..82c129bfe58d 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -651,6 +651,18 @@ static int ovl_symlink(struct inode *dir, struct dentry *dentry, return ovl_create_object(dentry, S_IFLNK, 0, link); } +static int ovl_set_link_redirect(struct dentry *dentry) +{ + const struct cred *old_cred; + int err; + + old_cred = ovl_override_creds(dentry->d_sb); + err = ovl_set_redirect(dentry, false); + revert_creds(old_cred); + + return err; +} + static int ovl_link(struct dentry *old, struct inode *newdir, struct dentry *new) { @@ -670,7 +682,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, goto out_drop_write; if (ovl_is_metacopy_dentry(old)) { - err = ovl_set_redirect(old, false); + err = ovl_set_link_redirect(old); if (err) goto out_drop_write; } diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 8fa37cd7818a..54e5d17d7f3e 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -754,9 +754,8 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, goto out; } - /* Otherwise, get a connected non-upper dir or disconnected non-dir */ - if (d_is_dir(origin.dentry) && - (origin.dentry->d_flags & DCACHE_DISCONNECTED)) { + /* Find origin.dentry again with ovl_acceptable() layer check */ + if (d_is_dir(origin.dentry)) { dput(origin.dentry); origin.dentry = NULL; err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack); @@ -769,6 +768,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, goto out_err; } + /* Get a connected non-upper dir or disconnected non-dir */ dentry = ovl_get_dentry(sb, NULL, &origin, index); out: diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 6bcc9dedc342..3b7ed5d2279c 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -286,22 +286,13 @@ int ovl_permission(struct inode *inode, int mask) if (err) return err; - /* No need to do any access on underlying for special files */ - if (special_file(realinode->i_mode)) - return 0; - - /* No need to access underlying for execute */ - mask &= ~MAY_EXEC; - if ((mask & (MAY_READ | MAY_WRITE)) == 0) - return 0; - - /* Lower files get copied up, so turn write access into read */ - if (!upperinode && mask & MAY_WRITE) { + old_cred = ovl_override_creds(inode->i_sb); + if (!upperinode && + !special_file(realinode->i_mode) && mask & MAY_WRITE) { mask &= ~(MAY_WRITE | MAY_APPEND); + /* Make sure mounter can read file for copy up later */ mask |= MAY_READ; } - - old_cred = ovl_override_creds(inode->i_sb); err = inode_permission(realinode, mask); revert_creds(old_cred); diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index cd58939dc977..7a85e609fc27 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -1566,7 +1566,6 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, cond_resched(); BUG_ON(!vma_can_userfault(vma)); - WARN_ON(!(vma->vm_flags & VM_MAYWRITE)); /* * Nothing to do: this vma is already registered into this @@ -1575,6 +1574,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, if (!vma->vm_userfaultfd_ctx.ctx) goto skip; + WARN_ON(!(vma->vm_flags & VM_MAYWRITE)); + if (vma->vm_start > start) start = vma->vm_start; vma_end = min(end, vma->vm_end); |