"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.10.4/doc/html/cuse_8c.html" (9 Jun 2021, 29282 Bytes) of package /linux/misc/fuse-3.10.4.tar.xz:


Caution: In this restricted "Fossies" environment the current HTML page may not be correctly presentated and may have some non-functional links. You can here alternatively try to browse the pure source code or just view or download the uninterpreted raw source code. If the rendering is insufficient you may try to find and view the page on the project site itself.

libfuse
cuse.c File Reference
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example demonstrates how to implement a character device in userspace ("CUSE"). This is only allowed for root. The character device should appear in /dev under the specified name. It can be tested with the cuse_client.c program.

Mount the file system with:

cuse -f --name=mydevice

You should now have a new /dev/mydevice character device. To "unmount" it, kill the "cuse" process.

To compile this example, run

gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse

Source code

/*
CUSE example: Character device in Userspace
Copyright (C) 2008-2009 SUSE Linux Products GmbH
Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file COPYING.
*/
#define FUSE_USE_VERSION 31
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"
static void *cusexmp_buf;
static size_t cusexmp_size;
static const char *usage =
"usage: cusexmp [options]\n"
"\n"
"options:\n"
" --help|-h print this help message\n"
" --maj=MAJ|-M MAJ device major number\n"
" --min=MIN|-m MIN device minor number\n"
" --name=NAME|-n NAME device name (mandatory)\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
"\n";
static int cusexmp_resize(size_t new_size)
{
void *new_buf;
if (new_size == cusexmp_size)
return 0;
new_buf = realloc(cusexmp_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > cusexmp_size)
memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
cusexmp_buf = new_buf;
cusexmp_size = new_size;
return 0;
}
static int cusexmp_expand(size_t new_size)
{
if (new_size > cusexmp_size)
return cusexmp_resize(new_size);
return 0;
}
static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
{
fuse_reply_open(req, fi);
}
static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *fi)
{
(void)fi;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
fuse_reply_buf(req, cusexmp_buf + off, size);
}
static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void)fi;
if (cusexmp_expand(off + size)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + off, buf, size);
fuse_reply_write(req, size);
}
static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
size_t in_bufsz, size_t out_bufsz, int is_read)
{
const struct fioc_rw_arg *arg;
struct iovec in_iov[2], out_iov[3], iov[3];
size_t cur_size;
/* read in arg */
in_iov[0].iov_base = addr;
in_iov[0].iov_len = sizeof(*arg);
if (!in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
return;
}
arg = in_buf;
in_buf += sizeof(*arg);
in_bufsz -= sizeof(*arg);
/* prepare size outputs */
out_iov[0].iov_base =
addr + offsetof(struct fioc_rw_arg, prev_size);
out_iov[0].iov_len = sizeof(arg->prev_size);
out_iov[1].iov_base =
addr + offsetof(struct fioc_rw_arg, new_size);
out_iov[1].iov_len = sizeof(arg->new_size);
/* prepare client buf */
if (is_read) {
out_iov[2].iov_base = arg->buf;
out_iov[2].iov_len = arg->size;
if (!out_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
return;
}
} else {
in_iov[1].iov_base = arg->buf;
in_iov[1].iov_len = arg->size;
if (arg->size && !in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
return;
}
}
/* we're all set */
cur_size = cusexmp_size;
iov[0].iov_base = &cur_size;
iov[0].iov_len = sizeof(cur_size);
iov[1].iov_base = &cusexmp_size;
iov[1].iov_len = sizeof(cusexmp_size);
if (is_read) {
size_t off = arg->offset;
size_t size = arg->size;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
iov[2].iov_base = cusexmp_buf + off;
iov[2].iov_len = size;
fuse_reply_ioctl_iov(req, size, iov, 3);
} else {
if (cusexmp_expand(arg->offset + in_bufsz)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
}
}
static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
int is_read = 0;
(void)fi;
if (flags & FUSE_IOCTL_COMPAT) {
fuse_reply_err(req, ENOSYS);
return;
}
switch (cmd) {
case FIOC_GET_SIZE:
if (!out_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
} else
fuse_reply_ioctl(req, 0, &cusexmp_size,
sizeof(cusexmp_size));
break;
case FIOC_SET_SIZE:
if (!in_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
cusexmp_resize(*(size_t *)in_buf);
fuse_reply_ioctl(req, 0, NULL, 0);
}
break;
case FIOC_READ:
is_read = 1;
/* fall through */
case FIOC_WRITE:
fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
break;
default:
fuse_reply_err(req, EINVAL);
}
}
struct cusexmp_param {
unsigned major;
unsigned minor;
char *dev_name;
int is_help;
};
#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
static const struct fuse_opt cusexmp_opts[] = {
CUSEXMP_OPT("-M %u", major),
CUSEXMP_OPT("--maj=%u", major),
CUSEXMP_OPT("-m %u", minor),
CUSEXMP_OPT("--min=%u", minor),
CUSEXMP_OPT("-n %s", dev_name),
CUSEXMP_OPT("--name=%s", dev_name),
FUSE_OPT_KEY("-h", 0),
FUSE_OPT_KEY("--help", 0),
};
static int cusexmp_process_arg(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct cusexmp_param *param = data;
(void)outargs;
(void)arg;
switch (key) {
case 0:
param->is_help = 1;
fprintf(stderr, "%s", usage);
return fuse_opt_add_arg(outargs, "-ho");
default:
return 1;
}
}
static const struct cuse_lowlevel_ops cusexmp_clop = {
.open = cusexmp_open,
.read = cusexmp_read,
.write = cusexmp_write,
.ioctl = cusexmp_ioctl,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct cusexmp_param param = { 0, 0, NULL, 0 };
char dev_name[128] = "DEVNAME=";
const char *dev_info_argv[] = { dev_name };
struct cuse_info ci;
int ret = 1;
if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
printf("failed to parse option\n");
free(param.dev_name);
goto out;
}
if (!param.is_help) {
if (!param.dev_name) {
fprintf(stderr, "Error: device name missing\n");
goto out;
}
strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
free(param.dev_name);
}
memset(&ci, 0, sizeof(ci));
ci.dev_major = param.major;
ci.dev_minor = param.minor;
ci.dev_info_argc = 1;
ci.dev_info_argv = dev_info_argv;
ci.flags = CUSE_UNRESTRICTED_IOCTL;
ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
out:
return ret;
}

Definition in file cuse.c.