Skip to content

Commit d40b2f4

Browse files
committed
Merge tag 'fuse-update-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse updates from Miklos Szeredi: - Fix regression in fileattr permission checking - Fix possible hang during PID namespace destruction - Add generic support for request extensions - Add supplementary group list extension - Add limited support for supplying supplementary groups in create requests - Documentation fixes * tag 'fuse-update-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: add inode/permission checks to fileattr_get/fileattr_set fuse: fix all W=1 kernel-doc warnings fuse: in fuse_flush only wait if someone wants the return code fuse: optional supplementary group in create requests fuse: add request extension
2 parents da15efe + 1cc4606 commit d40b2f4

File tree

8 files changed

+225
-62
lines changed

8 files changed

+225
-62
lines changed

fs/fuse/cuse.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
256256
}
257257

258258
/**
259-
* cuse_parse_dev_info - parse device info
259+
* cuse_parse_devinfo - parse device info
260260
* @p: device info string
261261
* @len: length of device info string
262262
* @devinfo: out parameter for parsed device info

fs/fuse/dev.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ static unsigned int fuse_req_hash(u64 unique)
204204
return hash_long(unique & ~FUSE_INT_REQ_BIT, FUSE_PQ_HASH_BITS);
205205
}
206206

207-
/**
207+
/*
208208
* A new request is available, wake fiq->waitq
209209
*/
210210
static void fuse_dev_wake_and_unlock(struct fuse_iqueue *fiq)
@@ -476,6 +476,8 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
476476
req->in.h.opcode = args->opcode;
477477
req->in.h.nodeid = args->nodeid;
478478
req->args = args;
479+
if (args->is_ext)
480+
req->in.h.total_extlen = args->in_args[args->ext_idx].size / 8;
479481
if (args->end)
480482
__set_bit(FR_ASYNC, &req->flags);
481483
}

fs/fuse/dir.c

Lines changed: 97 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ static void fuse_dir_changed(struct inode *dir)
145145
inode_maybe_inc_iversion(dir, false);
146146
}
147147

148-
/**
148+
/*
149149
* Mark the attributes as stale due to an atime change. Avoid the invalidate if
150150
* atime is not used.
151151
*/
@@ -466,7 +466,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
466466
}
467467

468468
static int get_security_context(struct dentry *entry, umode_t mode,
469-
void **security_ctx, u32 *security_ctxlen)
469+
struct fuse_in_arg *ext)
470470
{
471471
struct fuse_secctx *fctx;
472472
struct fuse_secctx_header *header;
@@ -513,14 +513,100 @@ static int get_security_context(struct dentry *entry, umode_t mode,
513513

514514
memcpy(ptr, ctx, ctxlen);
515515
}
516-
*security_ctxlen = total_len;
517-
*security_ctx = header;
516+
ext->size = total_len;
517+
ext->value = header;
518518
err = 0;
519519
out_err:
520520
kfree(ctx);
521521
return err;
522522
}
523523

524+
static void *extend_arg(struct fuse_in_arg *buf, u32 bytes)
525+
{
526+
void *p;
527+
u32 newlen = buf->size + bytes;
528+
529+
p = krealloc(buf->value, newlen, GFP_KERNEL);
530+
if (!p) {
531+
kfree(buf->value);
532+
buf->size = 0;
533+
buf->value = NULL;
534+
return NULL;
535+
}
536+
537+
memset(p + buf->size, 0, bytes);
538+
buf->value = p;
539+
buf->size = newlen;
540+
541+
return p + newlen - bytes;
542+
}
543+
544+
static u32 fuse_ext_size(size_t size)
545+
{
546+
return FUSE_REC_ALIGN(sizeof(struct fuse_ext_header) + size);
547+
}
548+
549+
/*
550+
* This adds just a single supplementary group that matches the parent's group.
551+
*/
552+
static int get_create_supp_group(struct inode *dir, struct fuse_in_arg *ext)
553+
{
554+
struct fuse_conn *fc = get_fuse_conn(dir);
555+
struct fuse_ext_header *xh;
556+
struct fuse_supp_groups *sg;
557+
kgid_t kgid = dir->i_gid;
558+
gid_t parent_gid = from_kgid(fc->user_ns, kgid);
559+
u32 sg_len = fuse_ext_size(sizeof(*sg) + sizeof(sg->groups[0]));
560+
561+
if (parent_gid == (gid_t) -1 || gid_eq(kgid, current_fsgid()) ||
562+
!in_group_p(kgid))
563+
return 0;
564+
565+
xh = extend_arg(ext, sg_len);
566+
if (!xh)
567+
return -ENOMEM;
568+
569+
xh->size = sg_len;
570+
xh->type = FUSE_EXT_GROUPS;
571+
572+
sg = (struct fuse_supp_groups *) &xh[1];
573+
sg->nr_groups = 1;
574+
sg->groups[0] = parent_gid;
575+
576+
return 0;
577+
}
578+
579+
static int get_create_ext(struct fuse_args *args,
580+
struct inode *dir, struct dentry *dentry,
581+
umode_t mode)
582+
{
583+
struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb);
584+
struct fuse_in_arg ext = { .size = 0, .value = NULL };
585+
int err = 0;
586+
587+
if (fc->init_security)
588+
err = get_security_context(dentry, mode, &ext);
589+
if (!err && fc->create_supp_group)
590+
err = get_create_supp_group(dir, &ext);
591+
592+
if (!err && ext.size) {
593+
WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args));
594+
args->is_ext = true;
595+
args->ext_idx = args->in_numargs++;
596+
args->in_args[args->ext_idx] = ext;
597+
} else {
598+
kfree(ext.value);
599+
}
600+
601+
return err;
602+
}
603+
604+
static void free_ext_value(struct fuse_args *args)
605+
{
606+
if (args->is_ext)
607+
kfree(args->in_args[args->ext_idx].value);
608+
}
609+
524610
/*
525611
* Atomic create+open operation
526612
*
@@ -541,8 +627,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
541627
struct fuse_entry_out outentry;
542628
struct fuse_inode *fi;
543629
struct fuse_file *ff;
544-
void *security_ctx = NULL;
545-
u32 security_ctxlen;
546630
bool trunc = flags & O_TRUNC;
547631

548632
/* Userspace expects S_IFREG in create mode */
@@ -586,19 +670,12 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
586670
args.out_args[1].size = sizeof(outopen);
587671
args.out_args[1].value = &outopen;
588672

589-
if (fm->fc->init_security) {
590-
err = get_security_context(entry, mode, &security_ctx,
591-
&security_ctxlen);
592-
if (err)
593-
goto out_put_forget_req;
594-
595-
args.in_numargs = 3;
596-
args.in_args[2].size = security_ctxlen;
597-
args.in_args[2].value = security_ctx;
598-
}
673+
err = get_create_ext(&args, dir, entry, mode);
674+
if (err)
675+
goto out_put_forget_req;
599676

600677
err = fuse_simple_request(fm, &args);
601-
kfree(security_ctx);
678+
free_ext_value(&args);
602679
if (err)
603680
goto out_free_ff;
604681

@@ -705,8 +782,6 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
705782
struct dentry *d;
706783
int err;
707784
struct fuse_forget_link *forget;
708-
void *security_ctx = NULL;
709-
u32 security_ctxlen;
710785

711786
if (fuse_is_bad(dir))
712787
return -EIO;
@@ -721,21 +796,14 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
721796
args->out_args[0].size = sizeof(outarg);
722797
args->out_args[0].value = &outarg;
723798

724-
if (fm->fc->init_security && args->opcode != FUSE_LINK) {
725-
err = get_security_context(entry, mode, &security_ctx,
726-
&security_ctxlen);
799+
if (args->opcode != FUSE_LINK) {
800+
err = get_create_ext(args, dir, entry, mode);
727801
if (err)
728802
goto out_put_forget_req;
729-
730-
BUG_ON(args->in_numargs != 2);
731-
732-
args->in_numargs = 3;
733-
args->in_args[2].size = security_ctxlen;
734-
args->in_args[2].value = security_ctx;
735803
}
736804

737805
err = fuse_simple_request(fm, args);
738-
kfree(security_ctx);
806+
free_ext_value(args);
739807
if (err)
740808
goto out_put_forget_req;
741809

fs/fuse/file.c

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/uio.h>
2020
#include <linux/fs.h>
2121
#include <linux/filelock.h>
22+
#include <linux/file.h>
2223

2324
static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
2425
unsigned int open_flags, int opcode,
@@ -478,48 +479,36 @@ static void fuse_sync_writes(struct inode *inode)
478479
fuse_release_nowrite(inode);
479480
}
480481

481-
static int fuse_flush(struct file *file, fl_owner_t id)
482-
{
483-
struct inode *inode = file_inode(file);
484-
struct fuse_mount *fm = get_fuse_mount(inode);
485-
struct fuse_file *ff = file->private_data;
482+
struct fuse_flush_args {
483+
struct fuse_args args;
486484
struct fuse_flush_in inarg;
487-
FUSE_ARGS(args);
488-
int err;
489-
490-
if (fuse_is_bad(inode))
491-
return -EIO;
485+
struct work_struct work;
486+
struct file *file;
487+
};
492488

493-
if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
494-
return 0;
489+
static int fuse_do_flush(struct fuse_flush_args *fa)
490+
{
491+
int err;
492+
struct inode *inode = file_inode(fa->file);
493+
struct fuse_mount *fm = get_fuse_mount(inode);
495494

496495
err = write_inode_now(inode, 1);
497496
if (err)
498-
return err;
497+
goto out;
499498

500499
inode_lock(inode);
501500
fuse_sync_writes(inode);
502501
inode_unlock(inode);
503502

504-
err = filemap_check_errors(file->f_mapping);
503+
err = filemap_check_errors(fa->file->f_mapping);
505504
if (err)
506-
return err;
505+
goto out;
507506

508507
err = 0;
509508
if (fm->fc->no_flush)
510509
goto inval_attr_out;
511510

512-
memset(&inarg, 0, sizeof(inarg));
513-
inarg.fh = ff->fh;
514-
inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);
515-
args.opcode = FUSE_FLUSH;
516-
args.nodeid = get_node_id(inode);
517-
args.in_numargs = 1;
518-
args.in_args[0].size = sizeof(inarg);
519-
args.in_args[0].value = &inarg;
520-
args.force = true;
521-
522-
err = fuse_simple_request(fm, &args);
511+
err = fuse_simple_request(fm, &fa->args);
523512
if (err == -ENOSYS) {
524513
fm->fc->no_flush = 1;
525514
err = 0;
@@ -532,9 +521,57 @@ static int fuse_flush(struct file *file, fl_owner_t id)
532521
*/
533522
if (!err && fm->fc->writeback_cache)
534523
fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
524+
525+
out:
526+
fput(fa->file);
527+
kfree(fa);
535528
return err;
536529
}
537530

531+
static void fuse_flush_async(struct work_struct *work)
532+
{
533+
struct fuse_flush_args *fa = container_of(work, typeof(*fa), work);
534+
535+
fuse_do_flush(fa);
536+
}
537+
538+
static int fuse_flush(struct file *file, fl_owner_t id)
539+
{
540+
struct fuse_flush_args *fa;
541+
struct inode *inode = file_inode(file);
542+
struct fuse_mount *fm = get_fuse_mount(inode);
543+
struct fuse_file *ff = file->private_data;
544+
545+
if (fuse_is_bad(inode))
546+
return -EIO;
547+
548+
if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
549+
return 0;
550+
551+
fa = kzalloc(sizeof(*fa), GFP_KERNEL);
552+
if (!fa)
553+
return -ENOMEM;
554+
555+
fa->inarg.fh = ff->fh;
556+
fa->inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);
557+
fa->args.opcode = FUSE_FLUSH;
558+
fa->args.nodeid = get_node_id(inode);
559+
fa->args.in_numargs = 1;
560+
fa->args.in_args[0].size = sizeof(fa->inarg);
561+
fa->args.in_args[0].value = &fa->inarg;
562+
fa->args.force = true;
563+
fa->file = get_file(file);
564+
565+
/* Don't wait if the task is exiting */
566+
if (current->flags & PF_EXITING) {
567+
INIT_WORK(&fa->work, fuse_flush_async);
568+
schedule_work(&fa->work);
569+
return 0;
570+
}
571+
572+
return fuse_do_flush(fa);
573+
}
574+
538575
int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
539576
int datasync, int opcode)
540577
{
@@ -653,7 +690,7 @@ static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io)
653690
return io->bytes < 0 ? io->size : io->bytes;
654691
}
655692

656-
/**
693+
/*
657694
* In case of short read, the caller sets 'pos' to the position of
658695
* actual end of fuse request in IO request. Otherwise, if bytes_requested
659696
* == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.

fs/fuse/fuse_i.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ struct fuse_page_desc {
249249
struct fuse_args {
250250
uint64_t nodeid;
251251
uint32_t opcode;
252-
unsigned short in_numargs;
253-
unsigned short out_numargs;
252+
uint8_t in_numargs;
253+
uint8_t out_numargs;
254+
uint8_t ext_idx;
254255
bool force:1;
255256
bool noreply:1;
256257
bool nocreds:1;
@@ -261,6 +262,7 @@ struct fuse_args {
261262
bool page_zeroing:1;
262263
bool page_replace:1;
263264
bool may_block:1;
265+
bool is_ext:1;
264266
struct fuse_in_arg in_args[3];
265267
struct fuse_arg out_args[2];
266268
void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
@@ -781,6 +783,9 @@ struct fuse_conn {
781783
/* Initialize security xattrs when creating a new inode */
782784
unsigned int init_security:1;
783785

786+
/* Add supplementary group info when creating a new inode */
787+
unsigned int create_supp_group:1;
788+
784789
/* Does the filesystem support per inode DAX? */
785790
unsigned int inode_dax:1;
786791

0 commit comments

Comments
 (0)