blob: ee58f034518b8475dd77fbf0064da62d13602486 [file] [log] [blame]
/*
FUSE fioc: FUSE ioctl example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
/** @file
* @tableofcontents
*
* This example illustrates how to write a FUSE file system that can
* process (a restricted set of) ioctls. It can be tested with the
* ioctl_client.c program.
*
* \section section_compile compiling this example
*
* gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl
*
* \section section_source the complete source
* \include ioctl.c
*/
#define FUSE_USE_VERSION 30
#include <config.h>
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"
#define FIOC_NAME "fioc"
enum {
FIOC_NONE,
FIOC_ROOT,
FIOC_FILE,
};
static void *fioc_buf;
static size_t fioc_size;
static int fioc_resize(size_t new_size)
{
void *new_buf;
if (new_size == fioc_size)
return 0;
new_buf = realloc(fioc_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > fioc_size)
memset(new_buf + fioc_size, 0, new_size - fioc_size);
fioc_buf = new_buf;
fioc_size = new_size;
return 0;
}
static int fioc_expand(size_t new_size)
{
if (new_size > fioc_size)
return fioc_resize(new_size);
return 0;
}
static int fioc_file_type(const char *path)
{
if (strcmp(path, "/") == 0)
return FIOC_ROOT;
if (strcmp(path, "/" FIOC_NAME) == 0)
return FIOC_FILE;
return FIOC_NONE;
}
static int fioc_getattr(const char *path, struct stat *stbuf)
{
stbuf->st_uid = getuid();
stbuf->st_gid = getgid();
stbuf->st_atime = stbuf->st_mtime = time(NULL);
switch (fioc_file_type(path)) {
case FIOC_ROOT:
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
break;
case FIOC_FILE:
stbuf->st_mode = S_IFREG | 0644;
stbuf->st_nlink = 1;
stbuf->st_size = fioc_size;
break;
case FIOC_NONE:
return -ENOENT;
}
return 0;
}
static int fioc_open(const char *path, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_NONE)
return 0;
return -ENOENT;
}
static int fioc_do_read(char *buf, size_t size, off_t offset)
{
if (offset >= fioc_size)
return 0;
if (size > fioc_size - offset)
size = fioc_size - offset;
memcpy(buf, fioc_buf + offset, size);
return size;
}
static int fioc_read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_read(buf, size, offset);
}
static int fioc_do_write(const char *buf, size_t size, off_t offset)
{
if (fioc_expand(offset + size))
return -ENOMEM;
memcpy(fioc_buf + offset, buf, size);
return size;
}
static int fioc_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_write(buf, size, offset);
}
static int fioc_truncate(const char *path, off_t size)
{
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_resize(size);
}
static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) fi;
(void) offset;
(void) flags;
if (fioc_file_type(path) != FIOC_ROOT)
return -ENOENT;
filler(buf, ".", NULL, 0, 0);
filler(buf, "..", NULL, 0, 0);
filler(buf, FIOC_NAME, NULL, 0, 0);
return 0;
}
static int fioc_ioctl(const char *path, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags, void *data)
{
(void) arg;
(void) fi;
(void) flags;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
if (flags & FUSE_IOCTL_COMPAT)
return -ENOSYS;
switch (cmd) {
case FIOC_GET_SIZE:
*(size_t *)data = fioc_size;
return 0;
case FIOC_SET_SIZE:
fioc_resize(*(size_t *)data);
return 0;
}
return -EINVAL;
}
static struct fuse_operations fioc_oper = {
.getattr = fioc_getattr,
.readdir = fioc_readdir,
.truncate = fioc_truncate,
.open = fioc_open,
.read = fioc_read,
.write = fioc_write,
.ioctl = fioc_ioctl,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &fioc_oper, NULL);
}