1/* 2 FUSE fioc: FUSE ioctl example 3 Copyright (C) 2008 SUSE Linux Products GmbH 4 Copyright (C) 2008 Tejun Heo <teheo@suse.de> 5 6 This program can be distributed under the terms of the GNU GPLv2. 7 See the file COPYING. 8*/ 9 10/** @file 11 * @tableofcontents 12 * 13 * This example illustrates how to write a FUSE file system that can 14 * process (a restricted set of) ioctls. It can be tested with the 15 * ioctl_client.c program. 16 * 17 * Compile with: 18 * 19 * gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl 20 * 21 * ## Source code ## 22 * \include ioctl.c 23 */ 24 25#define FUSE_USE_VERSION 35 26 27#include <fuse.h> 28#include <stdlib.h> 29#include <stdio.h> 30#include <string.h> 31#include <unistd.h> 32#include <time.h> 33#include <errno.h> 34 35#include "ioctl.h" 36 37#define FIOC_NAME "fioc" 38 39enum { 40 FIOC_NONE, 41 FIOC_ROOT, 42 FIOC_FILE, 43}; 44 45static void *fioc_buf; 46static size_t fioc_size; 47 48static int fioc_resize(size_t new_size) 49{ 50 void *new_buf; 51 52 if (new_size == fioc_size) 53 return 0; 54 55 new_buf = realloc(fioc_buf, new_size); 56 if (!new_buf && new_size) 57 return -ENOMEM; 58 59 if (new_size > fioc_size) 60 memset(new_buf + fioc_size, 0, new_size - fioc_size); 61 62 fioc_buf = new_buf; 63 fioc_size = new_size; 64 65 return 0; 66} 67 68static int fioc_expand(size_t new_size) 69{ 70 if (new_size > fioc_size) 71 return fioc_resize(new_size); 72 return 0; 73} 74 75static int fioc_file_type(const char *path) 76{ 77 if (strcmp(path, "/") == 0) 78 return FIOC_ROOT; 79 if (strcmp(path, "/" FIOC_NAME) == 0) 80 return FIOC_FILE; 81 return FIOC_NONE; 82} 83 84static int fioc_getattr(const char *path, struct stat *stbuf, 85 struct fuse_file_info *fi) 86{ 87 (void) fi; 88 stbuf->st_uid = getuid(); 89 stbuf->st_gid = getgid(); 90 stbuf->st_atime = stbuf->st_mtime = time(NULL); 91 92 switch (fioc_file_type(path)) { 93 case FIOC_ROOT: 94 stbuf->st_mode = S_IFDIR | 0755; 95 stbuf->st_nlink = 2; 96 break; 97 case FIOC_FILE: 98 stbuf->st_mode = S_IFREG | 0644; 99 stbuf->st_nlink = 1; 100 stbuf->st_size = fioc_size; 101 break; 102 case FIOC_NONE: 103 return -ENOENT; 104 } 105 106 return 0; 107} 108 109static int fioc_open(const char *path, struct fuse_file_info *fi) 110{ 111 (void) fi; 112 113 if (fioc_file_type(path) != FIOC_NONE) 114 return 0; 115 return -ENOENT; 116} 117 118static int fioc_do_read(char *buf, size_t size, off_t offset) 119{ 120 if (offset >= fioc_size) 121 return 0; 122 123 if (size > fioc_size - offset) 124 size = fioc_size - offset; 125 126 memcpy(buf, fioc_buf + offset, size); 127 128 return size; 129} 130 131static int fioc_read(const char *path, char *buf, size_t size, 132 off_t offset, struct fuse_file_info *fi) 133{ 134 (void) fi; 135 136 if (fioc_file_type(path) != FIOC_FILE) 137 return -EINVAL; 138 139 return fioc_do_read(buf, size, offset); 140} 141 142static int fioc_do_write(const char *buf, size_t size, off_t offset) 143{ 144 if (fioc_expand(offset + size)) 145 return -ENOMEM; 146 147 memcpy(fioc_buf + offset, buf, size); 148 149 return size; 150} 151 152static int fioc_write(const char *path, const char *buf, size_t size, 153 off_t offset, struct fuse_file_info *fi) 154{ 155 (void) fi; 156 157 if (fioc_file_type(path) != FIOC_FILE) 158 return -EINVAL; 159 160 return fioc_do_write(buf, size, offset); 161} 162 163static int fioc_truncate(const char *path, off_t size, 164 struct fuse_file_info *fi) 165{ 166 (void) fi; 167 if (fioc_file_type(path) != FIOC_FILE) 168 return -EINVAL; 169 170 return fioc_resize(size); 171} 172 173static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 174 off_t offset, struct fuse_file_info *fi, 175 enum fuse_readdir_flags flags) 176{ 177 (void) fi; 178 (void) offset; 179 (void) flags; 180 181 if (fioc_file_type(path) != FIOC_ROOT) 182 return -ENOENT; 183 184 filler(buf, ".", NULL, 0, 0); 185 filler(buf, "..", NULL, 0, 0); 186 filler(buf, FIOC_NAME, NULL, 0, 0); 187 188 return 0; 189} 190 191static int fioc_ioctl(const char *path, unsigned int cmd, void *arg, 192 struct fuse_file_info *fi, unsigned int flags, void *data) 193{ 194 (void) arg; 195 (void) fi; 196 (void) flags; 197 198 if (fioc_file_type(path) != FIOC_FILE) 199 return -EINVAL; 200 201 if (flags & FUSE_IOCTL_COMPAT) 202 return -ENOSYS; 203 204 switch (cmd) { 205 case FIOC_GET_SIZE: 206 *(size_t *)data = fioc_size; 207 return 0; 208 209 case FIOC_SET_SIZE: 210 fioc_resize(*(size_t *)data); 211 return 0; 212 } 213 214 return -EINVAL; 215} 216 217static const struct fuse_operations fioc_oper = { 218 .getattr = fioc_getattr, 219 .readdir = fioc_readdir, 220 .truncate = fioc_truncate, 221 .open = fioc_open, 222 .read = fioc_read, 223 .write = fioc_write, 224 .ioctl = fioc_ioctl, 225}; 226 227int main(int argc, char *argv[]) 228{ 229 return fuse_main(argc, argv, &fioc_oper, NULL); 230} 231