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: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
1b929c02af
commit
15d937d7ca
|
@ -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.opcode = args->opcode;
|
||||||
req->in.h.nodeid = args->nodeid;
|
req->in.h.nodeid = args->nodeid;
|
||||||
req->args = args;
|
req->args = args;
|
||||||
|
if (args->is_ext)
|
||||||
|
req->in.h.total_extlen = args->in_args[args->ext_idx].size / 8;
|
||||||
if (args->end)
|
if (args->end)
|
||||||
__set_bit(FR_ASYNC, &req->flags);
|
__set_bit(FR_ASYNC, &req->flags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
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 *fctx;
|
||||||
struct fuse_secctx_header *header;
|
struct fuse_secctx_header *header;
|
||||||
|
@ -513,14 +513,42 @@ static int get_security_context(struct dentry *entry, umode_t mode,
|
||||||
|
|
||||||
memcpy(ptr, ctx, ctxlen);
|
memcpy(ptr, ctx, ctxlen);
|
||||||
}
|
}
|
||||||
*security_ctxlen = total_len;
|
ext->size = total_len;
|
||||||
*security_ctx = header;
|
ext->value = header;
|
||||||
err = 0;
|
err = 0;
|
||||||
out_err:
|
out_err:
|
||||||
kfree(ctx);
|
kfree(ctx);
|
||||||
return err;
|
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
|
* 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_entry_out outentry;
|
||||||
struct fuse_inode *fi;
|
struct fuse_inode *fi;
|
||||||
struct fuse_file *ff;
|
struct fuse_file *ff;
|
||||||
void *security_ctx = NULL;
|
|
||||||
u32 security_ctxlen;
|
|
||||||
bool trunc = flags & O_TRUNC;
|
bool trunc = flags & O_TRUNC;
|
||||||
|
|
||||||
/* Userspace expects S_IFREG in create mode */
|
/* 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].size = sizeof(outopen);
|
||||||
args.out_args[1].value = &outopen;
|
args.out_args[1].value = &outopen;
|
||||||
|
|
||||||
if (fm->fc->init_security) {
|
err = get_create_ext(&args, entry, mode);
|
||||||
err = get_security_context(entry, mode, &security_ctx,
|
|
||||||
&security_ctxlen);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put_forget_req;
|
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);
|
err = fuse_simple_request(fm, &args);
|
||||||
kfree(security_ctx);
|
free_ext_value(&args);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free_ff;
|
goto out_free_ff;
|
||||||
|
|
||||||
|
@ -705,8 +724,6 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
struct dentry *d;
|
struct dentry *d;
|
||||||
int err;
|
int err;
|
||||||
struct fuse_forget_link *forget;
|
struct fuse_forget_link *forget;
|
||||||
void *security_ctx = NULL;
|
|
||||||
u32 security_ctxlen;
|
|
||||||
|
|
||||||
if (fuse_is_bad(dir))
|
if (fuse_is_bad(dir))
|
||||||
return -EIO;
|
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].size = sizeof(outarg);
|
||||||
args->out_args[0].value = &outarg;
|
args->out_args[0].value = &outarg;
|
||||||
|
|
||||||
if (fm->fc->init_security && args->opcode != FUSE_LINK) {
|
if (args->opcode != FUSE_LINK) {
|
||||||
err = get_security_context(entry, mode, &security_ctx,
|
err = get_create_ext(args, entry, mode);
|
||||||
&security_ctxlen);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put_forget_req;
|
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);
|
err = fuse_simple_request(fm, args);
|
||||||
kfree(security_ctx);
|
free_ext_value(args);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put_forget_req;
|
goto out_put_forget_req;
|
||||||
|
|
||||||
|
|
|
@ -249,8 +249,9 @@ struct fuse_page_desc {
|
||||||
struct fuse_args {
|
struct fuse_args {
|
||||||
uint64_t nodeid;
|
uint64_t nodeid;
|
||||||
uint32_t opcode;
|
uint32_t opcode;
|
||||||
unsigned short in_numargs;
|
uint8_t in_numargs;
|
||||||
unsigned short out_numargs;
|
uint8_t out_numargs;
|
||||||
|
uint8_t ext_idx;
|
||||||
bool force:1;
|
bool force:1;
|
||||||
bool noreply:1;
|
bool noreply:1;
|
||||||
bool nocreds:1;
|
bool nocreds:1;
|
||||||
|
@ -261,6 +262,7 @@ struct fuse_args {
|
||||||
bool page_zeroing:1;
|
bool page_zeroing:1;
|
||||||
bool page_replace:1;
|
bool page_replace:1;
|
||||||
bool may_block:1;
|
bool may_block:1;
|
||||||
|
bool is_ext:1;
|
||||||
struct fuse_in_arg in_args[3];
|
struct fuse_in_arg in_args[3];
|
||||||
struct fuse_arg out_args[2];
|
struct fuse_arg out_args[2];
|
||||||
void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
|
void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
|
||||||
|
|
|
@ -201,6 +201,9 @@
|
||||||
* 7.38
|
* 7.38
|
||||||
* - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
|
* - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
|
||||||
* - add FOPEN_PARALLEL_DIRECT_WRITES
|
* - add FOPEN_PARALLEL_DIRECT_WRITES
|
||||||
|
* - add total_extlen to fuse_in_header
|
||||||
|
* - add FUSE_MAX_NR_SECCTX
|
||||||
|
* - add extension header
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
@ -503,6 +506,15 @@ struct fuse_file_lock {
|
||||||
*/
|
*/
|
||||||
#define FUSE_EXPIRE_ONLY (1 << 0)
|
#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 {
|
enum fuse_opcode {
|
||||||
FUSE_LOOKUP = 1,
|
FUSE_LOOKUP = 1,
|
||||||
FUSE_FORGET = 2, /* no reply */
|
FUSE_FORGET = 2, /* no reply */
|
||||||
|
@ -886,7 +898,8 @@ struct fuse_in_header {
|
||||||
uint32_t uid;
|
uint32_t uid;
|
||||||
uint32_t gid;
|
uint32_t gid;
|
||||||
uint32_t pid;
|
uint32_t pid;
|
||||||
uint32_t padding;
|
uint16_t total_extlen; /* length of extensions in 8byte units */
|
||||||
|
uint16_t padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_out_header {
|
struct fuse_out_header {
|
||||||
|
@ -1047,4 +1060,17 @@ struct fuse_secctx_header {
|
||||||
uint32_t nr_secctx;
|
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 */
|
#endif /* _LINUX_FUSE_H */
|
||||||
|
|
Loading…
Reference in New Issue