1 /* 2 FUSE: Filesystem in Userspace 3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 5 This program can be distributed under the terms of the GNU GPLv2. 6 See the file COPYING. 7 */ 8 9 /** @file 10 * 11 * minimal example filesystem using low-level API 12 * 13 * Compile with: 14 * 15 * gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll 16 * 17 * ## Source code ## 18 * \include hello_ll.c 19 */ 20 21 #define FUSE_USE_VERSION 34 22 23 #include <fuse_lowlevel.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <unistd.h> 30 #include <assert.h> 31 32 static const char *hello_str = "Hello World!\n"; 33 static const char *hello_name = "hello"; 34 35 static int hello_stat(fuse_ino_t ino, struct stat *stbuf) 36 { 37 stbuf->st_ino = ino; 38 switch (ino) { 39 case 1: 40 stbuf->st_mode = S_IFDIR | 0755; 41 stbuf->st_nlink = 2; 42 break; 43 44 case 2: 45 stbuf->st_mode = S_IFREG | 0444; 46 stbuf->st_nlink = 1; 47 stbuf->st_size = strlen(hello_str); 48 break; 49 50 default: 51 return -1; 52 } 53 return 0; 54 } 55 56 static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino, 57 struct fuse_file_info *fi) 58 { 59 struct stat stbuf; 60 61 (void) fi; 62 63 memset(&stbuf, 0, sizeof(stbuf)); 64 if (hello_stat(ino, &stbuf) == -1) 65 fuse_reply_err(req, ENOENT); 66 else 67 fuse_reply_attr(req, &stbuf, 1.0); 68 } 69 70 static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) 71 { 72 struct fuse_entry_param e; 73 74 if (parent != 1 || strcmp(name, hello_name) != 0) 75 fuse_reply_err(req, ENOENT); 76 else { 77 memset(&e, 0, sizeof(e)); 78 e.ino = 2; 79 e.attr_timeout = 1.0; 80 e.entry_timeout = 1.0; 81 hello_stat(e.ino, &e.attr); 82 83 fuse_reply_entry(req, &e); 84 } 85 } 86 87 struct dirbuf { 88 char *p; 89 size_t size; 90 }; 91 92 static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, 93 fuse_ino_t ino) 94 { 95 struct stat stbuf; 96 size_t oldsize = b->size; 97 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0); 98 b->p = (char *) realloc(b->p, b->size); 99 memset(&stbuf, 0, sizeof(stbuf)); 100 stbuf.st_ino = ino; 101 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, 102 b->size); 103 } 104 105 #define min(x, y) ((x) < (y) ? (x) : (y)) 106 107 static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, 108 off_t off, size_t maxsize) 109 { 110 if (off < bufsize) 111 return fuse_reply_buf(req, buf + off, 112 min(bufsize - off, maxsize)); 113 else 114 return fuse_reply_buf(req, NULL, 0); 115 } 116 117 static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, 118 off_t off, struct fuse_file_info *fi) 119 { 120 (void) fi; 121 122 if (ino != 1) 123 fuse_reply_err(req, ENOTDIR); 124 else { 125 struct dirbuf b; 126 127 memset(&b, 0, sizeof(b)); 128 dirbuf_add(req, &b, ".", 1); 129 dirbuf_add(req, &b, "..", 1); 130 dirbuf_add(req, &b, hello_name, 2); 131 reply_buf_limited(req, b.p, b.size, off, size); 132 free(b.p); 133 } 134 } 135 136 static void hello_ll_open(fuse_req_t req, fuse_ino_t ino, 137 struct fuse_file_info *fi) 138 { 139 if (ino != 2) 140 fuse_reply_err(req, EISDIR); 141 else if ((fi->flags & O_ACCMODE) != O_RDONLY) 142 fuse_reply_err(req, EACCES); 143 else 144 fuse_reply_open(req, fi); 145 } 146 147 static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, 148 off_t off, struct fuse_file_info *fi) 149 { 150 (void) fi; 151 152 assert(ino == 2); 153 reply_buf_limited(req, hello_str, strlen(hello_str), off, size); 154 } 155 156 static const struct fuse_lowlevel_ops hello_ll_oper = { 157 .lookup = hello_ll_lookup, 158 .getattr = hello_ll_getattr, 159 .readdir = hello_ll_readdir, 160 .open = hello_ll_open, 161 .read = hello_ll_read, 162 }; 163 164 int main(int argc, char *argv[]) 165 { 166 struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 167 struct fuse_session *se; 168 struct fuse_cmdline_opts opts; 169 struct fuse_loop_config config; 170 int ret = -1; 171 172 if (fuse_parse_cmdline(&args, &opts) != 0) 173 return 1; 174 if (opts.show_help) { 175 printf("usage: %s [options] <mountpoint>\n\n", argv[0]); 176 fuse_cmdline_help(); 177 fuse_lowlevel_help(); 178 ret = 0; 179 goto err_out1; 180 } else if (opts.show_version) { 181 printf("FUSE library version %s\n", fuse_pkgversion()); 182 fuse_lowlevel_version(); 183 ret = 0; 184 goto err_out1; 185 } 186 187 if(opts.mountpoint == NULL) { 188 printf("usage: %s [options] <mountpoint>\n", argv[0]); 189 printf(" %s --help\n", argv[0]); 190 ret = 1; 191 goto err_out1; 192 } 193 194 se = fuse_session_new(&args, &hello_ll_oper, 195 sizeof(hello_ll_oper), NULL); 196 if (se == NULL) 197 goto err_out1; 198 199 if (fuse_set_signal_handlers(se) != 0) 200 goto err_out2; 201 202 if (fuse_session_mount(se, opts.mountpoint) != 0) 203 goto err_out3; 204 205 fuse_daemonize(opts.foreground); 206 207 /* Block until ctrl+c or fusermount -u */ 208 if (opts.singlethread) 209 ret = fuse_session_loop(se); 210 else { 211 config.clone_fd = opts.clone_fd; 212 config.max_idle_threads = opts.max_idle_threads; 213 ret = fuse_session_loop_mt(se, &config); 214 } 215 216 fuse_session_unmount(se); 217 err_out3: 218 fuse_remove_signal_handlers(se); 219 err_out2: 220 fuse_session_destroy(se); 221 err_out1: 222 free(opts.mountpoint); 223 fuse_opt_free_args(&args); 224 225 return ret ? 1 : 0; 226 } 227