CUSE patches from Tejun Heo

This commit is contained in:
Miklos Szeredi
2009-06-18 11:11:54 +00:00
parent bc53eddb2f
commit ae9bfde712
14 changed files with 345 additions and 137 deletions
+31
View File
@@ -1,3 +1,34 @@
2009-06-18 Miklos Szeredi <miklos@szeredi.hu>
* CUSE patches from Tejun Heo:
* Unrestricted ioctl support left some debris. Clean them up:
o No reason to pass around pointer to flags. Pass flags directly.
o Clean up comment and prototype parameter names.
o fuse_lib_ioctl() didn't reply when get_path() failed. Fix it.
o Remove unused variables {in|out}_iov from fuse_lib_ioctl().
* Add fuse_reply_ioctl_iov()
* Move fuse_session, fuse_req and fuse_ll definitions to fuse_i.h
and make send_reply_iov() and fuse_setup_common() global (also in
fuse_i.h). These will be used by CUSE support.
* Restructure fuse_ll_process()
* Implement libfuse side of CUSE support. CUSE uses subset of FUSE
operations as dir operations don't make sense for CUSE where one
instance implements single character device.
CUSE support comes with its own cuse_lowevel_ops and related
initialization and helper functions. Except for initialization, it
usage is basically identical to FUSE.
This patch also adds example/cusexmp.c which can create a character
device with name and device number specified on command line. The
created device itself is pretty boring. It's a bit bucket supporting
read, write and access via ioctl.
2009-06-16 Miklos Szeredi <miklos@szeredi.hu>
* Add missing fuse_reply_bmap to versionscript. Debian
+1 -1
View File
@@ -3,7 +3,7 @@
AM_CPPFLAGS = -I$(top_srcdir)/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT
noinst_HEADERS = fioc.h
noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll fioc fioclient \
fsel fselclient
fsel fselclient cusexmp
LDADD = ../lib/libfuse.la @libfuse_libs@
fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la @libfuse_libs@
+17 -2
View File
@@ -12,6 +12,21 @@
#include <sys/ioctl.h>
enum {
FIOC_GET_SIZE = _IOR('E', 0, size_t),
FIOC_SET_SIZE = _IOW('E', 1, size_t),
FIOC_GET_SIZE = _IOR('E', 0, size_t),
FIOC_SET_SIZE = _IOW('E', 1, size_t),
/*
* The following two ioctls don't follow usual encoding rules
* and transfer variable amount of data.
*/
FIOC_READ = _IO('E', 2),
FIOC_WRITE = _IO('E', 3),
};
struct fioc_rw_arg {
off_t offset;
void *buf;
size_t size;
size_t prev_size; /* out param for previous total size */
size_t new_size; /* out param for new total size */
};
+79 -23
View File
@@ -20,19 +20,56 @@
#include "fioc.h"
const char *usage =
"Usage: fioclient FIOC_FILE [SIZE]\n"
"Usage: fioclient FIOC_FILE COMMAND\n"
"\n"
" get size if SIZE is omitted, set size otherwise\n"
"COMMANDS\n"
" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
"\n";
static int do_rw(int fd, int is_read, size_t size, off_t offset,
size_t *prev_size, size_t *new_size)
{
struct fioc_rw_arg arg = { .offset = offset };
ssize_t ret;
arg.buf = calloc(1, size);
if (!arg.buf) {
fprintf(stderr, "failed to allocated %zu bytes\n", size);
return -1;
}
if (is_read) {
arg.size = size;
ret = ioctl(fd, FIOC_READ, &arg);
if (ret >= 0)
fwrite(arg.buf, 1, ret, stdout);
} else {
arg.size = fread(arg.buf, 1, size, stdin);
fprintf(stderr, "Writing %zu bytes\n", arg.size);
ret = ioctl(fd, FIOC_WRITE, &arg);
}
if (ret >= 0) {
*prev_size = arg.prev_size;
*new_size = arg.new_size;
} else
perror("ioctl");
free(arg.buf);
return ret;
}
int main(int argc, char **argv)
{
size_t size;
int fd;
size_t param[2] = { };
size_t size, prev_size = 0, new_size = 0;
char cmd;
int fd, i, rc;
if (argc < 2) {
if (argc < 3)
goto usage;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
@@ -40,27 +77,46 @@ int main(int argc, char **argv)
return 1;
}
if (argc == 2) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
return 1;
}
printf("%zu\n", size);
} else {
cmd = tolower(argv[2][0]);
argc -= 3;
argv += 3;
for (i = 0; i < argc; i++) {
char *endp;
size = strtoul(argv[2], &endp, 0);
if (endp == argv[2] || *endp != '\0')
param[i] = strtoul(argv[i], &endp, 0);
if (endp == argv[i] || *endp != '\0')
goto usage;
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
return 1;
}
}
return 0;
usage:
switch (cmd) {
case 's':
if (!argc) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
return 1;
}
printf("%zu\n", size);
} else {
size = param[0];
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
return 1;
}
}
return 0;
case 'r':
case 'w':
rc = do_rw(fd, cmd == 'r', param[0], param[1],
&prev_size, &new_size);
if (rc < 0)
return 1;
fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
rc, prev_size, new_size);
return 0;
}
usage:
fprintf(stderr, usage);
return 1;
}
+2 -1
View File
@@ -9,7 +9,8 @@ fuseinclude_HEADERS = \
fuse_common_compat.h \
fuse_lowlevel.h \
fuse_lowlevel_compat.h \
fuse_opt.h
fuse_opt.h \
cuse_lowlevel.h
include_HEADERS = old/fuse.h ulockmgr.h
+31
View File
@@ -149,6 +149,13 @@ struct fuse_file_lock {
#define FUSE_EXPORT_SUPPORT (1 << 4)
#define FUSE_BIG_WRITES (1 << 5)
/**
* CUSE INIT request/reply flags
*
* CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl
*/
#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
/**
* Release flags
*/
@@ -239,6 +246,9 @@ enum fuse_opcode {
FUSE_DESTROY = 38,
FUSE_IOCTL = 39,
FUSE_POLL = 40,
/* CUSE specific operations */
CUSE_INIT = 4096,
};
enum fuse_notify_code {
@@ -430,6 +440,27 @@ struct fuse_init_out {
__u32 max_write;
};
#define CUSE_INIT_INFO_MAX 4096
struct cuse_init_in {
__u32 major;
__u32 minor;
__u32 unused;
__u32 flags;
};
struct cuse_init_out {
__u32 major;
__u32 minor;
__u32 unused;
__u32 flags;
__u32 max_read;
__u32 max_write;
__u32 dev_major; /* chardev major */
__u32 dev_minor; /* chardev minor */
__u32 spare[10];
};
struct fuse_interrupt_in {
__u64 unique;
};
+21 -6
View File
@@ -815,7 +815,7 @@ struct fuse_lowlevel_ops {
*
* Note: For unrestricted ioctls (not allowed for FUSE
* servers), data in and out areas can be discovered by giving
* iovs and setting FUSE_IOCTL_RETRY in *flagsp. For
* iovs and setting FUSE_IOCTL_RETRY in @flags. For
* restricted ioctls, kernel prepares in/out data area
* according to the information encoded in cmd.
*
@@ -824,6 +824,7 @@ struct fuse_lowlevel_ops {
* Valid replies:
* fuse_reply_ioctl_retry
* fuse_reply_ioctl
* fuse_reply_ioctl_iov
* fuse_reply_err
*
* @param req request handle
@@ -831,14 +832,14 @@ struct fuse_lowlevel_ops {
* @param cmd ioctl command
* @param arg ioctl argument
* @param fi file information
* @param flagsp io/out parameter for FUSE_IOCTL_* flags
* @param flags for FUSE_IOCTL_* flags
* @param in_buf data fetched from the caller
* @param in_size number of fetched bytes
* @param out_size maximum size of output data
* @param in_bufsz number of fetched bytes
* @param out_bufsz maximum size of output data
*/
void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
struct fuse_file_info *fi, unsigned *flagsp,
const void *in_buf, size_t in_bufsz, size_t out_bufszp);
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz);
/**
* Poll for IO readiness
@@ -1115,6 +1116,20 @@ int fuse_reply_ioctl_retry(fuse_req_t req,
*/
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
/**
* Reply to finish ioctl with iov buffer
*
* Possible requests:
* ioctl
*
* @param req request handle
* @param result result to be passed to the caller
* @param iov the vector containing the data
* @param count the size of vector
*/
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
int count);
/**
* Reply with poll result event mask
*
+1
View File
@@ -29,6 +29,7 @@ libfuse_la_SOURCES = \
fuse_opt.c \
fuse_session.c \
fuse_signals.c \
cuse_lowlevel.c \
helper.c \
modules/subdir.c \
$(iconv_source) \
+11 -17
View File
@@ -3211,25 +3211,24 @@ static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
}
static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int *flagsp,
struct fuse_file_info *fi, unsigned int flags,
const void *in_buf, size_t in_bufsz,
size_t out_bufsz)
{
struct fuse *f = req_fuse_prepare(req);
struct fuse_intr_data d;
char *path, *out_buf = NULL;
struct iovec *in_iov = NULL, *out_iov = NULL;
int err;
if (*flagsp & FUSE_IOCTL_UNRESTRICTED) {
reply_err(req, -EPERM);
return;
}
err = -EPERM;
if (flags & FUSE_IOCTL_UNRESTRICTED)
goto err;
if (out_bufsz) {
err = -ENOMEM;
out_buf = malloc(out_bufsz);
if (!out_buf)
goto enomem;
goto err;
}
assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
@@ -3238,27 +3237,22 @@ static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
err = get_path(f, ino, &path);
if (err)
goto out;
goto err;
fuse_prepare_interrupt(f, req, &d);
err = fuse_fs_ioctl(f->fs, path, cmd, arg, fi, *flagsp,
err = fuse_fs_ioctl(f->fs, path, cmd, arg, fi, flags,
out_buf ?: (void *)in_buf);
fuse_finish_interrupt(f, req, &d);
free_path(f, ino, path);
fuse_reply_ioctl(req, err, out_buf, out_bufsz);
goto out;
err:
reply_err(req, err);
out:
free(out_buf);
free(in_iov);
free(out_iov);
return;
enomem:
reply_err(req, -ENOMEM);
goto out;
}
static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
+66 -3
View File
@@ -7,11 +7,58 @@
*/
#include "fuse.h"
#include "fuse_lowlevel.h"
struct fuse_session;
struct fuse_chan;
struct fuse_lowlevel_ops;
struct fuse_req;
struct fuse_ll;
struct fuse_session {
struct fuse_session_ops op;
void *data;
volatile int exited;
struct fuse_chan *ch;
};
struct fuse_req {
struct fuse_ll *f;
uint64_t unique;
int ctr;
pthread_mutex_t lock;
struct fuse_ctx ctx;
struct fuse_chan *ch;
int interrupted;
union {
struct {
uint64_t unique;
} i;
struct {
fuse_interrupt_func_t func;
void *data;
} ni;
} u;
struct fuse_req *next;
struct fuse_req *prev;
};
struct fuse_ll {
int debug;
int allow_root;
int atomic_o_trunc;
int big_writes;
struct fuse_lowlevel_ops op;
int got_init;
struct cuse_data *cuse_data;
void *userdata;
uid_t owner;
struct fuse_conn_info conn;
struct fuse_req list;
struct fuse_req interrupts;
pthread_mutex_t lock;
int got_destroy;
};
struct fuse_cmd {
char *buf;
@@ -34,3 +81,19 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
void fuse_kern_unmount_compat22(const char *mountpoint);
void fuse_kern_unmount(const char *mountpoint, int fd);
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, int count);
int send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, int count);
void free_req(fuse_req_t req);
struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *multithreaded,
int *fd,
void *user_data,
int compat);
void do_cuse_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
+71 -65
View File
@@ -6,10 +6,9 @@
See the file COPYING.LIB
*/
#include "fuse_lowlevel.h"
#include "fuse_i.h"
#include "fuse_kernel.h"
#include "fuse_opt.h"
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_common_compat.h"
#include "fuse_lowlevel_compat.h"
@@ -25,45 +24,6 @@
#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
#define OFFSET_MAX 0x7fffffffffffffffLL
struct fuse_ll;
struct fuse_req {
struct fuse_ll *f;
uint64_t unique;
int ctr;
pthread_mutex_t lock;
struct fuse_ctx ctx;
struct fuse_chan *ch;
int interrupted;
union {
struct {
uint64_t unique;
} i;
struct {
fuse_interrupt_func_t func;
void *data;
} ni;
} u;
struct fuse_req *next;
struct fuse_req *prev;
};
struct fuse_ll {
int debug;
int allow_root;
int atomic_o_trunc;
int big_writes;
struct fuse_lowlevel_ops op;
int got_init;
void *userdata;
uid_t owner;
struct fuse_conn_info conn;
struct fuse_req list;
struct fuse_req interrupts;
pthread_mutex_t lock;
int got_destroy;
};
struct fuse_pollhandle {
uint64_t kh;
struct fuse_chan *ch;
@@ -140,7 +100,7 @@ static void destroy_req(fuse_req_t req)
free(req);
}
static void free_req(fuse_req_t req)
void free_req(fuse_req_t req)
{
int ctr;
struct fuse_ll *f = req->f;
@@ -155,11 +115,10 @@ static void free_req(fuse_req_t req)
destroy_req(req);
}
static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
int send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
int count)
{
struct fuse_out_header out;
int res;
if (error <= -1000 || error > 0) {
fprintf(stderr, "fuse: bad error value: %i\n", error);
@@ -184,9 +143,16 @@ static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
(unsigned long long) out.unique, out.len);
}
}
res = fuse_chan_send(req->ch, iov, count);
free_req(req);
return fuse_chan_send(req->ch, iov, count);
}
int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, int count)
{
int res;
res = send_reply_iov_nofree(req, error, iov, count);
free_req(req);
return res;
}
@@ -509,6 +475,30 @@ int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
return send_reply_iov(req, 0, iov, count);
}
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
int count)
{
struct iovec *padded_iov;
struct fuse_ioctl_out arg;
int res;
padded_iov = malloc((count + 2) * sizeof(struct iovec));
if (padded_iov == NULL)
return fuse_reply_err(req, -ENOMEM);
memset(&arg, 0, sizeof(arg));
arg.result = result;
padded_iov[1].iov_base = &arg;
padded_iov[1].iov_len = sizeof(arg);
memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
res = send_reply_iov(req, 0, padded_iov, count + 2);
free(padded_iov);
return res;
}
int fuse_reply_poll(fuse_req_t req, unsigned revents)
{
struct fuse_poll_out arg;
@@ -1095,7 +1085,7 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (req->f->op.ioctl)
req->f->op.ioctl(req, nodeid, arg->cmd,
(void *)(uintptr_t)arg->arg, &fi, &flags,
(void *)(uintptr_t)arg->arg, &fi, flags,
in_buf, arg->in_size, arg->out_size);
else
fuse_reply_err(req, ENOSYS);
@@ -1356,6 +1346,7 @@ static struct {
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_POLL] = { do_poll, "POLL" },
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
[CUSE_INIT] = { do_cuse_init, "CUSE_INIT" },
};
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
@@ -1375,6 +1366,7 @@ static void fuse_ll_process(void *data, const char *buf, size_t len,
struct fuse_in_header *in = (struct fuse_in_header *) buf;
const void *inarg = buf + sizeof(struct fuse_in_header);
struct fuse_req *req;
int err;
if (f->debug)
fprintf(stderr,
@@ -1399,28 +1391,41 @@ static void fuse_ll_process(void *data, const char *buf, size_t len,
list_init_req(req);
fuse_mutex_init(&req->lock);
if (!f->got_init && in->opcode != FUSE_INIT)
fuse_reply_err(req, EIO);
else if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
err = EIO;
if (!f->got_init) {
enum fuse_opcode expected;
expected = f->cuse_data ? CUSE_INIT : FUSE_INIT;
if (in->opcode != expected)
goto reply_err;
} else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
goto reply_err;
err = EACCES;
if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
fuse_reply_err(req, EACCES);
} else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
fuse_reply_err(req, ENOSYS);
else {
if (in->opcode != FUSE_INTERRUPT) {
struct fuse_req *intr;
pthread_mutex_lock(&f->lock);
intr = check_interrupt(f, req);
list_add_req(req, &f->list);
pthread_mutex_unlock(&f->lock);
if (intr)
fuse_reply_err(intr, EAGAIN);
}
fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR)
goto reply_err;
err = ENOSYS;
if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
goto reply_err;
if (in->opcode != FUSE_INTERRUPT) {
struct fuse_req *intr;
pthread_mutex_lock(&f->lock);
intr = check_interrupt(f, req);
list_add_req(req, &f->list);
pthread_mutex_unlock(&f->lock);
if (intr)
fuse_reply_err(intr, EAGAIN);
}
fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
return;
reply_err:
fuse_reply_err(req, err);
}
enum {
@@ -1499,6 +1504,7 @@ static void fuse_ll_destroy(void *data)
}
pthread_mutex_destroy(&f->lock);
free(f->cuse_data);
free(f);
}
+1 -11
View File
@@ -6,7 +6,7 @@
See the file COPYING.LIB
*/
#include "fuse_lowlevel.h"
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_common_compat.h"
#include "fuse_lowlevel_compat.h"
@@ -17,16 +17,6 @@
#include <assert.h>
#include <errno.h>
struct fuse_session {
struct fuse_session_ops op;
void *data;
volatile int exited;
struct fuse_chan *ch;
};
struct fuse_chan {
struct fuse_chan_ops op;
+5
View File
@@ -160,6 +160,10 @@ FUSE_2.7.5 {
FUSE_2.8 {
global:
cuse_lowlevel_new;
cuse_lowlevel_main;
cuse_lowlevel_setup;
cuse_lowlevel_teardown;
fuse_fs_ioctl;
fuse_fs_poll;
fuse_lowlevel_notify_poll;
@@ -167,6 +171,7 @@ FUSE_2.8 {
fuse_opt_add_opt_escaped;
fuse_pollhandle_destroy;
fuse_reply_ioctl;
fuse_reply_ioctl_iov;
fuse_reply_ioctl_retry;
fuse_reply_poll;
+8 -8
View File
@@ -235,14 +235,14 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
fuse_unmount_common(mountpoint, ch);
}
static struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *multithreaded,
int *fd,
void *user_data,
int compat)
struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *multithreaded,
int *fd,
void *user_data,
int compat)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;