16881f68fSopenharmony_ci/*
26881f68fSopenharmony_ci  FUSE fioc: FUSE ioctl example
36881f68fSopenharmony_ci  Copyright (C) 2008       SUSE Linux Products GmbH
46881f68fSopenharmony_ci  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
56881f68fSopenharmony_ci
66881f68fSopenharmony_ci  This program can be distributed under the terms of the GNU GPLv2.
76881f68fSopenharmony_ci  See the file COPYING.
86881f68fSopenharmony_ci*/
96881f68fSopenharmony_ci
106881f68fSopenharmony_ci/** @file
116881f68fSopenharmony_ci * @tableofcontents
126881f68fSopenharmony_ci *
136881f68fSopenharmony_ci * This example illustrates how to write a FUSE file system that can
146881f68fSopenharmony_ci * process (a restricted set of) ioctls. It can be tested with the
156881f68fSopenharmony_ci * ioctl_client.c program.
166881f68fSopenharmony_ci *
176881f68fSopenharmony_ci * Compile with:
186881f68fSopenharmony_ci *
196881f68fSopenharmony_ci *     gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl
206881f68fSopenharmony_ci *
216881f68fSopenharmony_ci * ## Source code ##
226881f68fSopenharmony_ci * \include ioctl.c
236881f68fSopenharmony_ci */
246881f68fSopenharmony_ci
256881f68fSopenharmony_ci#define FUSE_USE_VERSION 35
266881f68fSopenharmony_ci
276881f68fSopenharmony_ci#include <fuse.h>
286881f68fSopenharmony_ci#include <stdlib.h>
296881f68fSopenharmony_ci#include <stdio.h>
306881f68fSopenharmony_ci#include <string.h>
316881f68fSopenharmony_ci#include <unistd.h>
326881f68fSopenharmony_ci#include <time.h>
336881f68fSopenharmony_ci#include <errno.h>
346881f68fSopenharmony_ci
356881f68fSopenharmony_ci#include "ioctl.h"
366881f68fSopenharmony_ci
376881f68fSopenharmony_ci#define FIOC_NAME	"fioc"
386881f68fSopenharmony_ci
396881f68fSopenharmony_cienum {
406881f68fSopenharmony_ci	FIOC_NONE,
416881f68fSopenharmony_ci	FIOC_ROOT,
426881f68fSopenharmony_ci	FIOC_FILE,
436881f68fSopenharmony_ci};
446881f68fSopenharmony_ci
456881f68fSopenharmony_cistatic void *fioc_buf;
466881f68fSopenharmony_cistatic size_t fioc_size;
476881f68fSopenharmony_ci
486881f68fSopenharmony_cistatic int fioc_resize(size_t new_size)
496881f68fSopenharmony_ci{
506881f68fSopenharmony_ci	void *new_buf;
516881f68fSopenharmony_ci
526881f68fSopenharmony_ci	if (new_size == fioc_size)
536881f68fSopenharmony_ci		return 0;
546881f68fSopenharmony_ci
556881f68fSopenharmony_ci	new_buf = realloc(fioc_buf, new_size);
566881f68fSopenharmony_ci	if (!new_buf && new_size)
576881f68fSopenharmony_ci		return -ENOMEM;
586881f68fSopenharmony_ci
596881f68fSopenharmony_ci	if (new_size > fioc_size)
606881f68fSopenharmony_ci		memset(new_buf + fioc_size, 0, new_size - fioc_size);
616881f68fSopenharmony_ci
626881f68fSopenharmony_ci	fioc_buf = new_buf;
636881f68fSopenharmony_ci	fioc_size = new_size;
646881f68fSopenharmony_ci
656881f68fSopenharmony_ci	return 0;
666881f68fSopenharmony_ci}
676881f68fSopenharmony_ci
686881f68fSopenharmony_cistatic int fioc_expand(size_t new_size)
696881f68fSopenharmony_ci{
706881f68fSopenharmony_ci	if (new_size > fioc_size)
716881f68fSopenharmony_ci		return fioc_resize(new_size);
726881f68fSopenharmony_ci	return 0;
736881f68fSopenharmony_ci}
746881f68fSopenharmony_ci
756881f68fSopenharmony_cistatic int fioc_file_type(const char *path)
766881f68fSopenharmony_ci{
776881f68fSopenharmony_ci	if (strcmp(path, "/") == 0)
786881f68fSopenharmony_ci		return FIOC_ROOT;
796881f68fSopenharmony_ci	if (strcmp(path, "/" FIOC_NAME) == 0)
806881f68fSopenharmony_ci		return FIOC_FILE;
816881f68fSopenharmony_ci	return FIOC_NONE;
826881f68fSopenharmony_ci}
836881f68fSopenharmony_ci
846881f68fSopenharmony_cistatic int fioc_getattr(const char *path, struct stat *stbuf,
856881f68fSopenharmony_ci			struct fuse_file_info *fi)
866881f68fSopenharmony_ci{
876881f68fSopenharmony_ci	(void) fi;
886881f68fSopenharmony_ci	stbuf->st_uid = getuid();
896881f68fSopenharmony_ci	stbuf->st_gid = getgid();
906881f68fSopenharmony_ci	stbuf->st_atime = stbuf->st_mtime = time(NULL);
916881f68fSopenharmony_ci
926881f68fSopenharmony_ci	switch (fioc_file_type(path)) {
936881f68fSopenharmony_ci	case FIOC_ROOT:
946881f68fSopenharmony_ci		stbuf->st_mode = S_IFDIR | 0755;
956881f68fSopenharmony_ci		stbuf->st_nlink = 2;
966881f68fSopenharmony_ci		break;
976881f68fSopenharmony_ci	case FIOC_FILE:
986881f68fSopenharmony_ci		stbuf->st_mode = S_IFREG | 0644;
996881f68fSopenharmony_ci		stbuf->st_nlink = 1;
1006881f68fSopenharmony_ci		stbuf->st_size = fioc_size;
1016881f68fSopenharmony_ci		break;
1026881f68fSopenharmony_ci	case FIOC_NONE:
1036881f68fSopenharmony_ci		return -ENOENT;
1046881f68fSopenharmony_ci	}
1056881f68fSopenharmony_ci
1066881f68fSopenharmony_ci	return 0;
1076881f68fSopenharmony_ci}
1086881f68fSopenharmony_ci
1096881f68fSopenharmony_cistatic int fioc_open(const char *path, struct fuse_file_info *fi)
1106881f68fSopenharmony_ci{
1116881f68fSopenharmony_ci	(void) fi;
1126881f68fSopenharmony_ci
1136881f68fSopenharmony_ci	if (fioc_file_type(path) != FIOC_NONE)
1146881f68fSopenharmony_ci		return 0;
1156881f68fSopenharmony_ci	return -ENOENT;
1166881f68fSopenharmony_ci}
1176881f68fSopenharmony_ci
1186881f68fSopenharmony_cistatic int fioc_do_read(char *buf, size_t size, off_t offset)
1196881f68fSopenharmony_ci{
1206881f68fSopenharmony_ci	if (offset >= fioc_size)
1216881f68fSopenharmony_ci		return 0;
1226881f68fSopenharmony_ci
1236881f68fSopenharmony_ci	if (size > fioc_size - offset)
1246881f68fSopenharmony_ci		size = fioc_size - offset;
1256881f68fSopenharmony_ci
1266881f68fSopenharmony_ci	memcpy(buf, fioc_buf + offset, size);
1276881f68fSopenharmony_ci
1286881f68fSopenharmony_ci	return size;
1296881f68fSopenharmony_ci}
1306881f68fSopenharmony_ci
1316881f68fSopenharmony_cistatic int fioc_read(const char *path, char *buf, size_t size,
1326881f68fSopenharmony_ci		     off_t offset, struct fuse_file_info *fi)
1336881f68fSopenharmony_ci{
1346881f68fSopenharmony_ci	(void) fi;
1356881f68fSopenharmony_ci
1366881f68fSopenharmony_ci	if (fioc_file_type(path) != FIOC_FILE)
1376881f68fSopenharmony_ci		return -EINVAL;
1386881f68fSopenharmony_ci
1396881f68fSopenharmony_ci	return fioc_do_read(buf, size, offset);
1406881f68fSopenharmony_ci}
1416881f68fSopenharmony_ci
1426881f68fSopenharmony_cistatic int fioc_do_write(const char *buf, size_t size, off_t offset)
1436881f68fSopenharmony_ci{
1446881f68fSopenharmony_ci	if (fioc_expand(offset + size))
1456881f68fSopenharmony_ci		return -ENOMEM;
1466881f68fSopenharmony_ci
1476881f68fSopenharmony_ci	memcpy(fioc_buf + offset, buf, size);
1486881f68fSopenharmony_ci
1496881f68fSopenharmony_ci	return size;
1506881f68fSopenharmony_ci}
1516881f68fSopenharmony_ci
1526881f68fSopenharmony_cistatic int fioc_write(const char *path, const char *buf, size_t size,
1536881f68fSopenharmony_ci		      off_t offset, struct fuse_file_info *fi)
1546881f68fSopenharmony_ci{
1556881f68fSopenharmony_ci	(void) fi;
1566881f68fSopenharmony_ci
1576881f68fSopenharmony_ci	if (fioc_file_type(path) != FIOC_FILE)
1586881f68fSopenharmony_ci		return -EINVAL;
1596881f68fSopenharmony_ci
1606881f68fSopenharmony_ci	return fioc_do_write(buf, size, offset);
1616881f68fSopenharmony_ci}
1626881f68fSopenharmony_ci
1636881f68fSopenharmony_cistatic int fioc_truncate(const char *path, off_t size,
1646881f68fSopenharmony_ci			 struct fuse_file_info *fi)
1656881f68fSopenharmony_ci{
1666881f68fSopenharmony_ci	(void) fi;
1676881f68fSopenharmony_ci	if (fioc_file_type(path) != FIOC_FILE)
1686881f68fSopenharmony_ci		return -EINVAL;
1696881f68fSopenharmony_ci
1706881f68fSopenharmony_ci	return fioc_resize(size);
1716881f68fSopenharmony_ci}
1726881f68fSopenharmony_ci
1736881f68fSopenharmony_cistatic int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
1746881f68fSopenharmony_ci			off_t offset, struct fuse_file_info *fi,
1756881f68fSopenharmony_ci			enum fuse_readdir_flags flags)
1766881f68fSopenharmony_ci{
1776881f68fSopenharmony_ci	(void) fi;
1786881f68fSopenharmony_ci	(void) offset;
1796881f68fSopenharmony_ci	(void) flags;
1806881f68fSopenharmony_ci
1816881f68fSopenharmony_ci	if (fioc_file_type(path) != FIOC_ROOT)
1826881f68fSopenharmony_ci		return -ENOENT;
1836881f68fSopenharmony_ci
1846881f68fSopenharmony_ci	filler(buf, ".", NULL, 0, 0);
1856881f68fSopenharmony_ci	filler(buf, "..", NULL, 0, 0);
1866881f68fSopenharmony_ci	filler(buf, FIOC_NAME, NULL, 0, 0);
1876881f68fSopenharmony_ci
1886881f68fSopenharmony_ci	return 0;
1896881f68fSopenharmony_ci}
1906881f68fSopenharmony_ci
1916881f68fSopenharmony_cistatic int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
1926881f68fSopenharmony_ci		      struct fuse_file_info *fi, unsigned int flags, void *data)
1936881f68fSopenharmony_ci{
1946881f68fSopenharmony_ci	(void) arg;
1956881f68fSopenharmony_ci	(void) fi;
1966881f68fSopenharmony_ci	(void) flags;
1976881f68fSopenharmony_ci
1986881f68fSopenharmony_ci	if (fioc_file_type(path) != FIOC_FILE)
1996881f68fSopenharmony_ci		return -EINVAL;
2006881f68fSopenharmony_ci
2016881f68fSopenharmony_ci	if (flags & FUSE_IOCTL_COMPAT)
2026881f68fSopenharmony_ci		return -ENOSYS;
2036881f68fSopenharmony_ci
2046881f68fSopenharmony_ci	switch (cmd) {
2056881f68fSopenharmony_ci	case FIOC_GET_SIZE:
2066881f68fSopenharmony_ci		*(size_t *)data = fioc_size;
2076881f68fSopenharmony_ci		return 0;
2086881f68fSopenharmony_ci
2096881f68fSopenharmony_ci	case FIOC_SET_SIZE:
2106881f68fSopenharmony_ci		fioc_resize(*(size_t *)data);
2116881f68fSopenharmony_ci		return 0;
2126881f68fSopenharmony_ci	}
2136881f68fSopenharmony_ci
2146881f68fSopenharmony_ci	return -EINVAL;
2156881f68fSopenharmony_ci}
2166881f68fSopenharmony_ci
2176881f68fSopenharmony_cistatic const struct fuse_operations fioc_oper = {
2186881f68fSopenharmony_ci	.getattr	= fioc_getattr,
2196881f68fSopenharmony_ci	.readdir	= fioc_readdir,
2206881f68fSopenharmony_ci	.truncate	= fioc_truncate,
2216881f68fSopenharmony_ci	.open		= fioc_open,
2226881f68fSopenharmony_ci	.read		= fioc_read,
2236881f68fSopenharmony_ci	.write		= fioc_write,
2246881f68fSopenharmony_ci	.ioctl		= fioc_ioctl,
2256881f68fSopenharmony_ci};
2266881f68fSopenharmony_ci
2276881f68fSopenharmony_ciint main(int argc, char *argv[])
2286881f68fSopenharmony_ci{
2296881f68fSopenharmony_ci	return fuse_main(argc, argv, &fioc_oper, NULL);
2306881f68fSopenharmony_ci}
231