16881f68fSopenharmony_ci/* 26881f68fSopenharmony_ci FUSE: Filesystem in Userspace 36881f68fSopenharmony_ci Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org> 46881f68fSopenharmony_ci 56881f68fSopenharmony_ci This program can be distributed under the terms of the GNU GPLv2. 66881f68fSopenharmony_ci See the file COPYING. 76881f68fSopenharmony_ci*/ 86881f68fSopenharmony_ci 96881f68fSopenharmony_ci/** @file 106881f68fSopenharmony_ci * 116881f68fSopenharmony_ci * This example implements a file system with a single file whose 126881f68fSopenharmony_ci * contents change dynamically: it always contains the current time. 136881f68fSopenharmony_ci * 146881f68fSopenharmony_ci * While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to 156881f68fSopenharmony_ci * actively push the updated data into the kernel cache, this example 166881f68fSopenharmony_ci * uses fuse_lowlevel_notify_inval_inode() to notify the kernel that 176881f68fSopenharmony_ci * the cache has to be invalidated - but the kernel still has to 186881f68fSopenharmony_ci * explicitly request the updated data on the next read. 196881f68fSopenharmony_ci * 206881f68fSopenharmony_ci * To see the effect, first start the file system with the 216881f68fSopenharmony_ci * ``--no-notify`` option: 226881f68fSopenharmony_ci * 236881f68fSopenharmony_ci * $ notify_inval_inode --update-interval=1 --no-notify mnt/ 246881f68fSopenharmony_ci * 256881f68fSopenharmony_ci * Observe that the output never changes, even though the file system 266881f68fSopenharmony_ci * updates it once per second. This is because the contents are cached 276881f68fSopenharmony_ci * in the kernel: 286881f68fSopenharmony_ci * 296881f68fSopenharmony_ci * $ for i in 1 2 3 4 5; do 306881f68fSopenharmony_ci * > cat mnt/current_time 316881f68fSopenharmony_ci * > sleep 1 326881f68fSopenharmony_ci * > done 336881f68fSopenharmony_ci * The current time is 15:58:18 346881f68fSopenharmony_ci * The current time is 15:58:18 356881f68fSopenharmony_ci * The current time is 15:58:18 366881f68fSopenharmony_ci * The current time is 15:58:18 376881f68fSopenharmony_ci * The current time is 15:58:18 386881f68fSopenharmony_ci * 396881f68fSopenharmony_ci * If you instead enable the notification functions, the changes become 406881f68fSopenharmony_ci * visible: 416881f68fSopenharmony_ci * 426881f68fSopenharmony_ci * $ notify_inval_inode --update-interval=1 mnt/ 436881f68fSopenharmony_ci * $ for i in 1 2 3 4 5; do 446881f68fSopenharmony_ci * > cat mnt/current_time 456881f68fSopenharmony_ci * > sleep 1 466881f68fSopenharmony_ci * > done 476881f68fSopenharmony_ci * The current time is 15:58:40 486881f68fSopenharmony_ci * The current time is 15:58:41 496881f68fSopenharmony_ci * The current time is 15:58:42 506881f68fSopenharmony_ci * The current time is 15:58:43 516881f68fSopenharmony_ci * The current time is 15:58:44 526881f68fSopenharmony_ci * 536881f68fSopenharmony_ci * ## Compilation ## 546881f68fSopenharmony_ci * 556881f68fSopenharmony_ci * gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode 566881f68fSopenharmony_ci * 576881f68fSopenharmony_ci * ## Source code ## 586881f68fSopenharmony_ci * \include notify_inval_inode.c 596881f68fSopenharmony_ci */ 606881f68fSopenharmony_ci 616881f68fSopenharmony_ci 626881f68fSopenharmony_ci#define FUSE_USE_VERSION 34 636881f68fSopenharmony_ci 646881f68fSopenharmony_ci#include <fuse_lowlevel.h> 656881f68fSopenharmony_ci#include <stdio.h> 666881f68fSopenharmony_ci#include <stdlib.h> 676881f68fSopenharmony_ci#include <string.h> 686881f68fSopenharmony_ci#include <errno.h> 696881f68fSopenharmony_ci#include <fcntl.h> 706881f68fSopenharmony_ci#include <assert.h> 716881f68fSopenharmony_ci#include <stddef.h> 726881f68fSopenharmony_ci#include <unistd.h> 736881f68fSopenharmony_ci#include <pthread.h> 746881f68fSopenharmony_ci 756881f68fSopenharmony_ci/* We can't actually tell the kernel that there is no 766881f68fSopenharmony_ci timeout, so we just send a big value */ 776881f68fSopenharmony_ci#define NO_TIMEOUT 500000 786881f68fSopenharmony_ci 796881f68fSopenharmony_ci#define MAX_STR_LEN 128 806881f68fSopenharmony_ci#define FILE_INO 2 816881f68fSopenharmony_ci#define FILE_NAME "current_time" 826881f68fSopenharmony_cistatic char file_contents[MAX_STR_LEN]; 836881f68fSopenharmony_cistatic int lookup_cnt = 0; 846881f68fSopenharmony_cistatic size_t file_size; 856881f68fSopenharmony_ci 866881f68fSopenharmony_ci/* Command line parsing */ 876881f68fSopenharmony_cistruct options { 886881f68fSopenharmony_ci int no_notify; 896881f68fSopenharmony_ci int update_interval; 906881f68fSopenharmony_ci}; 916881f68fSopenharmony_cistatic struct options options = { 926881f68fSopenharmony_ci .no_notify = 0, 936881f68fSopenharmony_ci .update_interval = 1, 946881f68fSopenharmony_ci}; 956881f68fSopenharmony_ci 966881f68fSopenharmony_ci#define OPTION(t, p) \ 976881f68fSopenharmony_ci { t, offsetof(struct options, p), 1 } 986881f68fSopenharmony_cistatic const struct fuse_opt option_spec[] = { 996881f68fSopenharmony_ci OPTION("--no-notify", no_notify), 1006881f68fSopenharmony_ci OPTION("--update-interval=%d", update_interval), 1016881f68fSopenharmony_ci FUSE_OPT_END 1026881f68fSopenharmony_ci}; 1036881f68fSopenharmony_ci 1046881f68fSopenharmony_cistatic int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { 1056881f68fSopenharmony_ci stbuf->st_ino = ino; 1066881f68fSopenharmony_ci if (ino == FUSE_ROOT_ID) { 1076881f68fSopenharmony_ci stbuf->st_mode = S_IFDIR | 0755; 1086881f68fSopenharmony_ci stbuf->st_nlink = 1; 1096881f68fSopenharmony_ci } 1106881f68fSopenharmony_ci 1116881f68fSopenharmony_ci else if (ino == FILE_INO) { 1126881f68fSopenharmony_ci stbuf->st_mode = S_IFREG | 0444; 1136881f68fSopenharmony_ci stbuf->st_nlink = 1; 1146881f68fSopenharmony_ci stbuf->st_size = file_size; 1156881f68fSopenharmony_ci } 1166881f68fSopenharmony_ci 1176881f68fSopenharmony_ci else 1186881f68fSopenharmony_ci return -1; 1196881f68fSopenharmony_ci 1206881f68fSopenharmony_ci return 0; 1216881f68fSopenharmony_ci} 1226881f68fSopenharmony_ci 1236881f68fSopenharmony_cistatic void tfs_lookup(fuse_req_t req, fuse_ino_t parent, 1246881f68fSopenharmony_ci const char *name) { 1256881f68fSopenharmony_ci struct fuse_entry_param e; 1266881f68fSopenharmony_ci memset(&e, 0, sizeof(e)); 1276881f68fSopenharmony_ci 1286881f68fSopenharmony_ci if (parent != FUSE_ROOT_ID) 1296881f68fSopenharmony_ci goto err_out; 1306881f68fSopenharmony_ci else if (strcmp(name, FILE_NAME) == 0) { 1316881f68fSopenharmony_ci e.ino = FILE_INO; 1326881f68fSopenharmony_ci lookup_cnt++; 1336881f68fSopenharmony_ci } else 1346881f68fSopenharmony_ci goto err_out; 1356881f68fSopenharmony_ci 1366881f68fSopenharmony_ci e.attr_timeout = NO_TIMEOUT; 1376881f68fSopenharmony_ci e.entry_timeout = NO_TIMEOUT; 1386881f68fSopenharmony_ci if (tfs_stat(e.ino, &e.attr) != 0) 1396881f68fSopenharmony_ci goto err_out; 1406881f68fSopenharmony_ci fuse_reply_entry(req, &e); 1416881f68fSopenharmony_ci return; 1426881f68fSopenharmony_ci 1436881f68fSopenharmony_cierr_out: 1446881f68fSopenharmony_ci fuse_reply_err(req, ENOENT); 1456881f68fSopenharmony_ci} 1466881f68fSopenharmony_ci 1476881f68fSopenharmony_cistatic void tfs_forget (fuse_req_t req, fuse_ino_t ino, 1486881f68fSopenharmony_ci uint64_t nlookup) { 1496881f68fSopenharmony_ci (void) req; 1506881f68fSopenharmony_ci if(ino == FILE_INO) 1516881f68fSopenharmony_ci lookup_cnt -= nlookup; 1526881f68fSopenharmony_ci else 1536881f68fSopenharmony_ci assert(ino == FUSE_ROOT_ID); 1546881f68fSopenharmony_ci fuse_reply_none(req); 1556881f68fSopenharmony_ci} 1566881f68fSopenharmony_ci 1576881f68fSopenharmony_cistatic void tfs_getattr(fuse_req_t req, fuse_ino_t ino, 1586881f68fSopenharmony_ci struct fuse_file_info *fi) { 1596881f68fSopenharmony_ci struct stat stbuf; 1606881f68fSopenharmony_ci 1616881f68fSopenharmony_ci (void) fi; 1626881f68fSopenharmony_ci 1636881f68fSopenharmony_ci memset(&stbuf, 0, sizeof(stbuf)); 1646881f68fSopenharmony_ci if (tfs_stat(ino, &stbuf) != 0) 1656881f68fSopenharmony_ci fuse_reply_err(req, ENOENT); 1666881f68fSopenharmony_ci else 1676881f68fSopenharmony_ci fuse_reply_attr(req, &stbuf, NO_TIMEOUT); 1686881f68fSopenharmony_ci} 1696881f68fSopenharmony_ci 1706881f68fSopenharmony_cistruct dirbuf { 1716881f68fSopenharmony_ci char *p; 1726881f68fSopenharmony_ci size_t size; 1736881f68fSopenharmony_ci}; 1746881f68fSopenharmony_ci 1756881f68fSopenharmony_cistatic void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, 1766881f68fSopenharmony_ci fuse_ino_t ino) { 1776881f68fSopenharmony_ci struct stat stbuf; 1786881f68fSopenharmony_ci size_t oldsize = b->size; 1796881f68fSopenharmony_ci b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0); 1806881f68fSopenharmony_ci b->p = (char *) realloc(b->p, b->size); 1816881f68fSopenharmony_ci memset(&stbuf, 0, sizeof(stbuf)); 1826881f68fSopenharmony_ci stbuf.st_ino = ino; 1836881f68fSopenharmony_ci fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, 1846881f68fSopenharmony_ci b->size); 1856881f68fSopenharmony_ci} 1866881f68fSopenharmony_ci 1876881f68fSopenharmony_ci#define min(x, y) ((x) < (y) ? (x) : (y)) 1886881f68fSopenharmony_ci 1896881f68fSopenharmony_cistatic int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, 1906881f68fSopenharmony_ci off_t off, size_t maxsize) { 1916881f68fSopenharmony_ci if (off < bufsize) 1926881f68fSopenharmony_ci return fuse_reply_buf(req, buf + off, 1936881f68fSopenharmony_ci min(bufsize - off, maxsize)); 1946881f68fSopenharmony_ci else 1956881f68fSopenharmony_ci return fuse_reply_buf(req, NULL, 0); 1966881f68fSopenharmony_ci} 1976881f68fSopenharmony_ci 1986881f68fSopenharmony_cistatic void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, 1996881f68fSopenharmony_ci off_t off, struct fuse_file_info *fi) { 2006881f68fSopenharmony_ci (void) fi; 2016881f68fSopenharmony_ci 2026881f68fSopenharmony_ci if (ino != FUSE_ROOT_ID) 2036881f68fSopenharmony_ci fuse_reply_err(req, ENOTDIR); 2046881f68fSopenharmony_ci else { 2056881f68fSopenharmony_ci struct dirbuf b; 2066881f68fSopenharmony_ci 2076881f68fSopenharmony_ci memset(&b, 0, sizeof(b)); 2086881f68fSopenharmony_ci dirbuf_add(req, &b, FILE_NAME, FILE_INO); 2096881f68fSopenharmony_ci reply_buf_limited(req, b.p, b.size, off, size); 2106881f68fSopenharmony_ci free(b.p); 2116881f68fSopenharmony_ci } 2126881f68fSopenharmony_ci} 2136881f68fSopenharmony_ci 2146881f68fSopenharmony_cistatic void tfs_open(fuse_req_t req, fuse_ino_t ino, 2156881f68fSopenharmony_ci struct fuse_file_info *fi) { 2166881f68fSopenharmony_ci 2176881f68fSopenharmony_ci /* Make cache persistent even if file is closed, 2186881f68fSopenharmony_ci this makes it easier to see the effects */ 2196881f68fSopenharmony_ci fi->keep_cache = 1; 2206881f68fSopenharmony_ci 2216881f68fSopenharmony_ci if (ino == FUSE_ROOT_ID) 2226881f68fSopenharmony_ci fuse_reply_err(req, EISDIR); 2236881f68fSopenharmony_ci else if ((fi->flags & O_ACCMODE) != O_RDONLY) 2246881f68fSopenharmony_ci fuse_reply_err(req, EACCES); 2256881f68fSopenharmony_ci else if (ino == FILE_INO) 2266881f68fSopenharmony_ci fuse_reply_open(req, fi); 2276881f68fSopenharmony_ci else { 2286881f68fSopenharmony_ci // This should not happen 2296881f68fSopenharmony_ci fprintf(stderr, "Got open for non-existing inode!\n"); 2306881f68fSopenharmony_ci fuse_reply_err(req, ENOENT); 2316881f68fSopenharmony_ci } 2326881f68fSopenharmony_ci} 2336881f68fSopenharmony_ci 2346881f68fSopenharmony_cistatic void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, 2356881f68fSopenharmony_ci off_t off, struct fuse_file_info *fi) { 2366881f68fSopenharmony_ci (void) fi; 2376881f68fSopenharmony_ci 2386881f68fSopenharmony_ci assert(ino == FILE_INO); 2396881f68fSopenharmony_ci reply_buf_limited(req, file_contents, file_size, off, size); 2406881f68fSopenharmony_ci} 2416881f68fSopenharmony_ci 2426881f68fSopenharmony_cistatic const struct fuse_lowlevel_ops tfs_oper = { 2436881f68fSopenharmony_ci .lookup = tfs_lookup, 2446881f68fSopenharmony_ci .getattr = tfs_getattr, 2456881f68fSopenharmony_ci .readdir = tfs_readdir, 2466881f68fSopenharmony_ci .open = tfs_open, 2476881f68fSopenharmony_ci .read = tfs_read, 2486881f68fSopenharmony_ci .forget = tfs_forget, 2496881f68fSopenharmony_ci}; 2506881f68fSopenharmony_ci 2516881f68fSopenharmony_cistatic void update_fs(void) { 2526881f68fSopenharmony_ci struct tm *now; 2536881f68fSopenharmony_ci time_t t; 2546881f68fSopenharmony_ci t = time(NULL); 2556881f68fSopenharmony_ci now = localtime(&t); 2566881f68fSopenharmony_ci assert(now != NULL); 2576881f68fSopenharmony_ci 2586881f68fSopenharmony_ci file_size = strftime(file_contents, MAX_STR_LEN, 2596881f68fSopenharmony_ci "The current time is %H:%M:%S\n", now); 2606881f68fSopenharmony_ci assert(file_size != 0); 2616881f68fSopenharmony_ci} 2626881f68fSopenharmony_ci 2636881f68fSopenharmony_cistatic void* update_fs_loop(void *data) { 2646881f68fSopenharmony_ci struct fuse_session *se = (struct fuse_session*) data; 2656881f68fSopenharmony_ci 2666881f68fSopenharmony_ci while(1) { 2676881f68fSopenharmony_ci update_fs(); 2686881f68fSopenharmony_ci if (!options.no_notify && lookup_cnt) { 2696881f68fSopenharmony_ci /* Only send notification if the kernel 2706881f68fSopenharmony_ci is aware of the inode */ 2716881f68fSopenharmony_ci assert(fuse_lowlevel_notify_inval_inode 2726881f68fSopenharmony_ci (se, FILE_INO, 0, 0) == 0); 2736881f68fSopenharmony_ci } 2746881f68fSopenharmony_ci sleep(options.update_interval); 2756881f68fSopenharmony_ci } 2766881f68fSopenharmony_ci return NULL; 2776881f68fSopenharmony_ci} 2786881f68fSopenharmony_ci 2796881f68fSopenharmony_cistatic void show_help(const char *progname) 2806881f68fSopenharmony_ci{ 2816881f68fSopenharmony_ci printf("usage: %s [options] <mountpoint>\n\n", progname); 2826881f68fSopenharmony_ci printf("File-system specific options:\n" 2836881f68fSopenharmony_ci " --update-interval=<secs> Update-rate of file system contents\n" 2846881f68fSopenharmony_ci " --no-notify Disable kernel notifications\n" 2856881f68fSopenharmony_ci "\n"); 2866881f68fSopenharmony_ci} 2876881f68fSopenharmony_ci 2886881f68fSopenharmony_ciint main(int argc, char *argv[]) { 2896881f68fSopenharmony_ci struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 2906881f68fSopenharmony_ci struct fuse_session *se; 2916881f68fSopenharmony_ci struct fuse_cmdline_opts opts; 2926881f68fSopenharmony_ci struct fuse_loop_config config; 2936881f68fSopenharmony_ci pthread_t updater; 2946881f68fSopenharmony_ci int ret = -1; 2956881f68fSopenharmony_ci 2966881f68fSopenharmony_ci if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) 2976881f68fSopenharmony_ci return 1; 2986881f68fSopenharmony_ci 2996881f68fSopenharmony_ci if (fuse_parse_cmdline(&args, &opts) != 0) { 3006881f68fSopenharmony_ci ret = 1; 3016881f68fSopenharmony_ci goto err_out1; 3026881f68fSopenharmony_ci } 3036881f68fSopenharmony_ci 3046881f68fSopenharmony_ci if (opts.show_help) { 3056881f68fSopenharmony_ci show_help(argv[0]); 3066881f68fSopenharmony_ci fuse_cmdline_help(); 3076881f68fSopenharmony_ci fuse_lowlevel_help(); 3086881f68fSopenharmony_ci ret = 0; 3096881f68fSopenharmony_ci goto err_out1; 3106881f68fSopenharmony_ci } else if (opts.show_version) { 3116881f68fSopenharmony_ci printf("FUSE library version %s\n", fuse_pkgversion()); 3126881f68fSopenharmony_ci fuse_lowlevel_version(); 3136881f68fSopenharmony_ci ret = 0; 3146881f68fSopenharmony_ci goto err_out1; 3156881f68fSopenharmony_ci } 3166881f68fSopenharmony_ci 3176881f68fSopenharmony_ci /* Initial contents */ 3186881f68fSopenharmony_ci update_fs(); 3196881f68fSopenharmony_ci 3206881f68fSopenharmony_ci se = fuse_session_new(&args, &tfs_oper, 3216881f68fSopenharmony_ci sizeof(tfs_oper), NULL); 3226881f68fSopenharmony_ci if (se == NULL) 3236881f68fSopenharmony_ci goto err_out1; 3246881f68fSopenharmony_ci 3256881f68fSopenharmony_ci if (fuse_set_signal_handlers(se) != 0) 3266881f68fSopenharmony_ci goto err_out2; 3276881f68fSopenharmony_ci 3286881f68fSopenharmony_ci if (fuse_session_mount(se, opts.mountpoint) != 0) 3296881f68fSopenharmony_ci goto err_out3; 3306881f68fSopenharmony_ci 3316881f68fSopenharmony_ci fuse_daemonize(opts.foreground); 3326881f68fSopenharmony_ci 3336881f68fSopenharmony_ci /* Start thread to update file contents */ 3346881f68fSopenharmony_ci ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se); 3356881f68fSopenharmony_ci if (ret != 0) { 3366881f68fSopenharmony_ci fprintf(stderr, "pthread_create failed with %s\n", 3376881f68fSopenharmony_ci strerror(ret)); 3386881f68fSopenharmony_ci goto err_out3; 3396881f68fSopenharmony_ci } 3406881f68fSopenharmony_ci 3416881f68fSopenharmony_ci /* Block until ctrl+c or fusermount -u */ 3426881f68fSopenharmony_ci if (opts.singlethread) 3436881f68fSopenharmony_ci ret = fuse_session_loop(se); 3446881f68fSopenharmony_ci else { 3456881f68fSopenharmony_ci config.clone_fd = opts.clone_fd; 3466881f68fSopenharmony_ci config.max_idle_threads = opts.max_idle_threads; 3476881f68fSopenharmony_ci ret = fuse_session_loop_mt(se, &config); 3486881f68fSopenharmony_ci } 3496881f68fSopenharmony_ci 3506881f68fSopenharmony_ci fuse_session_unmount(se); 3516881f68fSopenharmony_cierr_out3: 3526881f68fSopenharmony_ci fuse_remove_signal_handlers(se); 3536881f68fSopenharmony_cierr_out2: 3546881f68fSopenharmony_ci fuse_session_destroy(se); 3556881f68fSopenharmony_cierr_out1: 3566881f68fSopenharmony_ci fuse_opt_free_args(&args); 3576881f68fSopenharmony_ci free(opts.mountpoint); 3586881f68fSopenharmony_ci 3596881f68fSopenharmony_ci return ret ? 1 : 0; 3606881f68fSopenharmony_ci} 3616881f68fSopenharmony_ci 3626881f68fSopenharmony_ci 3636881f68fSopenharmony_ci/** 3646881f68fSopenharmony_ci * Local Variables: 3656881f68fSopenharmony_ci * mode: c 3666881f68fSopenharmony_ci * indent-tabs-mode: nil 3676881f68fSopenharmony_ci * c-basic-offset: 4 3686881f68fSopenharmony_ci * End: 3696881f68fSopenharmony_ci */ 370