Commit 15d937d7 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

fuse: add request extension



Will need to add supplementary groups to create messages, so add the
general concept of a request extension.  A request extension is appended to
the end of the main request.  It has a header indicating the size and type
of the extension.

The create security context (fuse_secctx_*) is similar to the generic
request extension, so include that as well in a backward compatible manner.

Add the total extension length to the request header.  The offset of the
extension block within the request can be calculated by:

  inh->len - inh->total_extlen * 8

Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 1b929c02
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -476,6 +476,8 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
	req->in.h.opcode = args->opcode;
	req->in.h.nodeid = args->nodeid;
	req->args = args;
	if (args->is_ext)
		req->in.h.total_extlen = args->in_args[args->ext_idx].size / 8;
	if (args->end)
		__set_bit(FR_ASYNC, &req->flags);
}
+38 −28
Original line number Diff line number Diff line
@@ -466,7 +466,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
}

static int get_security_context(struct dentry *entry, umode_t mode,
				void **security_ctx, u32 *security_ctxlen)
				struct fuse_in_arg *ext)
{
	struct fuse_secctx *fctx;
	struct fuse_secctx_header *header;
@@ -513,14 +513,42 @@ static int get_security_context(struct dentry *entry, umode_t mode,

		memcpy(ptr, ctx, ctxlen);
	}
	*security_ctxlen = total_len;
	*security_ctx = header;
	ext->size = total_len;
	ext->value = header;
	err = 0;
out_err:
	kfree(ctx);
	return err;
}

static int get_create_ext(struct fuse_args *args, struct dentry *dentry,
			  umode_t mode)
{
	struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb);
	struct fuse_in_arg ext = { .size = 0, .value = NULL };
	int err = 0;

	if (fc->init_security)
		err = get_security_context(dentry, mode, &ext);

	if (!err && ext.size) {
		WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args));
		args->is_ext = true;
		args->ext_idx = args->in_numargs++;
		args->in_args[args->ext_idx] = ext;
	} else {
		kfree(ext.value);
	}

	return err;
}

static void free_ext_value(struct fuse_args *args)
{
	if (args->is_ext)
		kfree(args->in_args[args->ext_idx].value);
}

/*
 * Atomic create+open operation
 *
@@ -541,8 +569,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	struct fuse_entry_out outentry;
	struct fuse_inode *fi;
	struct fuse_file *ff;
	void *security_ctx = NULL;
	u32 security_ctxlen;
	bool trunc = flags & O_TRUNC;

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

	if (fm->fc->init_security) {
		err = get_security_context(entry, mode, &security_ctx,
					   &security_ctxlen);
	err = get_create_ext(&args, entry, mode);
	if (err)
		goto out_put_forget_req;

		args.in_numargs = 3;
		args.in_args[2].size = security_ctxlen;
		args.in_args[2].value = security_ctx;
	}

	err = fuse_simple_request(fm, &args);
	kfree(security_ctx);
	free_ext_value(&args);
	if (err)
		goto out_free_ff;

@@ -705,8 +724,6 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
	struct dentry *d;
	int err;
	struct fuse_forget_link *forget;
	void *security_ctx = NULL;
	u32 security_ctxlen;

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

	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
		err = get_security_context(entry, mode, &security_ctx,
					   &security_ctxlen);
	if (args->opcode != FUSE_LINK) {
		err = get_create_ext(args, entry, mode);
		if (err)
			goto out_put_forget_req;

		BUG_ON(args->in_numargs != 2);

		args->in_numargs = 3;
		args->in_args[2].size = security_ctxlen;
		args->in_args[2].value = security_ctx;
	}

	err = fuse_simple_request(fm, args);
	kfree(security_ctx);
	free_ext_value(args);
	if (err)
		goto out_put_forget_req;

+4 −2
Original line number Diff line number Diff line
@@ -249,8 +249,9 @@ struct fuse_page_desc {
struct fuse_args {
	uint64_t nodeid;
	uint32_t opcode;
	unsigned short in_numargs;
	unsigned short out_numargs;
	uint8_t in_numargs;
	uint8_t out_numargs;
	uint8_t ext_idx;
	bool force:1;
	bool noreply:1;
	bool nocreds:1;
@@ -261,6 +262,7 @@ struct fuse_args {
	bool page_zeroing:1;
	bool page_replace:1;
	bool may_block:1;
	bool is_ext:1;
	struct fuse_in_arg in_args[3];
	struct fuse_arg out_args[2];
	void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
+27 −1
Original line number Diff line number Diff line
@@ -201,6 +201,9 @@
 *  7.38
 *  - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
 *  - add FOPEN_PARALLEL_DIRECT_WRITES
 *  - add total_extlen to fuse_in_header
 *  - add FUSE_MAX_NR_SECCTX
 *  - add extension header
 */

#ifndef _LINUX_FUSE_H
@@ -503,6 +506,15 @@ struct fuse_file_lock {
 */
#define FUSE_EXPIRE_ONLY		(1 << 0)

/**
 * extension type
 * FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx
 */
enum fuse_ext_type {
	/* Types 0..31 are reserved for fuse_secctx_header */
	FUSE_MAX_NR_SECCTX	= 31,
};

enum fuse_opcode {
	FUSE_LOOKUP		= 1,
	FUSE_FORGET		= 2,  /* no reply */
@@ -886,7 +898,8 @@ struct fuse_in_header {
	uint32_t	uid;
	uint32_t	gid;
	uint32_t	pid;
	uint32_t	padding;
	uint16_t	total_extlen; /* length of extensions in 8byte units */
	uint16_t	padding;
};

struct fuse_out_header {
@@ -1047,4 +1060,17 @@ struct fuse_secctx_header {
	uint32_t	nr_secctx;
};

/**
 * struct fuse_ext_header - extension header
 * @size: total size of this extension including this header
 * @type: type of extension
 *
 * This is made compatible with fuse_secctx_header by using type values >
 * FUSE_MAX_NR_SECCTX
 */
struct fuse_ext_header {
	uint32_t	size;
	uint32_t	type;
};

#endif /* _LINUX_FUSE_H */