Files
libfuse/example/ioctl_ll_client.c
Bernd Schubert a32cec008c Add lowlevel ioctl examples and tests
Examples help to understand what needs to be done for ioctls.

Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
2026-01-02 14:58:18 +01:00

177 lines
3.8 KiB
C

/*
* FUSE fioc_ll_client: FUSE ioctl example client for low-level API
* Copyright (C) 2008 SUSE Linux Products GmbH
* Copyright (C) 2008 Tejun Heo <teheo@suse.de>
* Copyright (C) 2024 libfuse maintainers
*
* This program can be distributed under the terms of the GNU GPLv2.
* See the file GPL2.txt.
*/
/** @file
*
* This program tests the ioctl_ll.c example file system.
* It tests both restricted ioctls (FIOC_GET_SIZE, FIOC_SET_SIZE) and
* unrestricted ioctls (FIOC_READ, FIOC_WRITE).
*
* Compile with:
*
* gcc -Wall ioctl_ll_client.c -o ioctl_ll_client
*
* ## Source code ##
* \include ioctl_ll_client.c
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ioctl.h"
static const char *usage =
"Usage: ioctl_ll_client <command> <fioc_file> [args]\n"
"\n"
"Commands:\n"
" get_size Get the current size (restricted ioctl)\n"
" set_size <size> Set the size (restricted ioctl)\n"
" read <offset> <size> Read data via ioctl (unrestricted)\n"
" write <offset> <data> Write data via ioctl (unrestricted)\n"
"\n"
"Examples:\n"
" ioctl_ll_client get_size /mnt/fioc\n"
" ioctl_ll_client set_size /mnt/fioc 100\n"
" ioctl_ll_client read /mnt/fioc 0 10\n"
" ioctl_ll_client write /mnt/fioc 0 hello\n"
"\n";
static int do_get_size(int fd)
{
size_t size;
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl(FIOC_GET_SIZE)");
return 1;
}
printf("%zu\n", size);
return 0;
}
static int do_set_size(int fd, const char *size_str)
{
size_t size = strtoul(size_str, NULL, 0);
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl(FIOC_SET_SIZE)");
return 1;
}
printf("Size set to %zu\n", size);
return 0;
}
static int do_read(int fd, const char *offset_str, const char *size_str)
{
struct fioc_rw_arg arg;
char *buf;
ssize_t ret;
size_t sz = strtoul(size_str, NULL, 0);
buf = malloc(sz + 1);
if (!buf) {
perror("malloc");
return 1;
}
memset(&arg, 0, sizeof(arg));
arg.offset = strtol(offset_str, NULL, 0);
arg.buf = buf;
arg.size = sz;
ret = ioctl(fd, FIOC_READ, &arg);
if (ret < 0) {
perror("ioctl(FIOC_READ)");
free(buf);
return 1;
}
buf[ret] = '\0';
printf("Read %zd bytes (prev_size=%zu, new_size=%zu): %s\n",
ret, arg.prev_size, arg.new_size, buf);
free(buf);
return 0;
}
static int do_write(int fd, const char *offset_str, const char *data)
{
struct fioc_rw_arg arg;
ssize_t ret;
memset(&arg, 0, sizeof(arg));
arg.offset = strtol(offset_str, NULL, 0);
arg.buf = (void *)data;
arg.size = strlen(data);
ret = ioctl(fd, FIOC_WRITE, &arg);
if (ret < 0) {
perror("ioctl(FIOC_WRITE)");
return 1;
}
printf("Wrote %zd bytes (prev_size=%zu, new_size=%zu)\n",
ret, arg.prev_size, arg.new_size);
return 0;
}
int main(int argc, char **argv)
{
int fd;
int ret = 0;
if (argc < 3) {
fprintf(stderr, "%s", usage);
return 1;
}
fd = open(argv[2], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
if (strcmp(argv[1], "get_size") == 0) {
ret = do_get_size(fd);
} else if (strcmp(argv[1], "set_size") == 0) {
if (argc < 4) {
fprintf(stderr, "set_size requires a size argument\n");
ret = 1;
} else {
ret = do_set_size(fd, argv[3]);
}
} else if (strcmp(argv[1], "read") == 0) {
if (argc < 5) {
fprintf(stderr, "read requires offset and size\n");
ret = 1;
} else {
ret = do_read(fd, argv[3], argv[4]);
}
} else if (strcmp(argv[1], "write") == 0) {
if (argc < 5) {
fprintf(stderr, "write requires offset and data\n");
ret = 1;
} else {
ret = do_write(fd, argv[3], argv[4]);
}
} else {
fprintf(stderr, "Unknown command: %s\n", argv[1]);
fprintf(stderr, "%s", usage);
ret = 1;
}
close(fd);
return ret;
}