16881f68fSopenharmony_ci/* 26881f68fSopenharmony_ci FUSE: Filesystem in Userspace 36881f68fSopenharmony_ci Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org> 46881f68fSopenharmony_ci (C) 2017 EditShare LLC <slawek.rudnicki@editshare.com> 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 * 126881f68fSopenharmony_ci * This example implements a file system with two files: 136881f68fSopenharmony_ci * * 'current-time', whose contents change dynamically: 146881f68fSopenharmony_ci * it always contains the current time (same as in 156881f68fSopenharmony_ci * notify_inval_inode.c). 166881f68fSopenharmony_ci * * 'growing', whose size changes dynamically, growing 176881f68fSopenharmony_ci * by 1 byte after each update. This aims to check 186881f68fSopenharmony_ci * if cached file metadata is also invalidated. 196881f68fSopenharmony_ci * 206881f68fSopenharmony_ci * ## Compilation ## 216881f68fSopenharmony_ci * 226881f68fSopenharmony_ci * gcc -Wall invalidate_path.c `pkg-config fuse3 --cflags --libs` -o invalidate_path 236881f68fSopenharmony_ci * 246881f68fSopenharmony_ci * ## Source code ## 256881f68fSopenharmony_ci * \include invalidate_path.c 266881f68fSopenharmony_ci */ 276881f68fSopenharmony_ci 286881f68fSopenharmony_ci#define FUSE_USE_VERSION 34 296881f68fSopenharmony_ci 306881f68fSopenharmony_ci#include <fuse.h> 316881f68fSopenharmony_ci#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */ 326881f68fSopenharmony_ci 336881f68fSopenharmony_ci#include <stdio.h> 346881f68fSopenharmony_ci#include <stdlib.h> 356881f68fSopenharmony_ci#include <string.h> 366881f68fSopenharmony_ci#include <errno.h> 376881f68fSopenharmony_ci#include <fcntl.h> 386881f68fSopenharmony_ci#include <assert.h> 396881f68fSopenharmony_ci#include <stddef.h> 406881f68fSopenharmony_ci#include <unistd.h> 416881f68fSopenharmony_ci#include <pthread.h> 426881f68fSopenharmony_ci 436881f68fSopenharmony_ci/* We can't actually tell the kernel that there is no 446881f68fSopenharmony_ci timeout, so we just send a big value */ 456881f68fSopenharmony_ci#define NO_TIMEOUT 500000 466881f68fSopenharmony_ci 476881f68fSopenharmony_ci#define MAX_STR_LEN 128 486881f68fSopenharmony_ci#define TIME_FILE_NAME "current_time" 496881f68fSopenharmony_ci#define TIME_FILE_INO 2 506881f68fSopenharmony_ci#define GROW_FILE_NAME "growing" 516881f68fSopenharmony_ci#define GROW_FILE_INO 3 526881f68fSopenharmony_ci 536881f68fSopenharmony_cistatic char time_file_contents[MAX_STR_LEN]; 546881f68fSopenharmony_cistatic size_t grow_file_size; 556881f68fSopenharmony_ci 566881f68fSopenharmony_ci/* Command line parsing */ 576881f68fSopenharmony_cistruct options { 586881f68fSopenharmony_ci int no_notify; 596881f68fSopenharmony_ci int update_interval; 606881f68fSopenharmony_ci}; 616881f68fSopenharmony_cistatic struct options options = { 626881f68fSopenharmony_ci .no_notify = 0, 636881f68fSopenharmony_ci .update_interval = 1, 646881f68fSopenharmony_ci}; 656881f68fSopenharmony_ci 666881f68fSopenharmony_ci#define OPTION(t, p) { t, offsetof(struct options, p), 1 } 676881f68fSopenharmony_cistatic const struct fuse_opt option_spec[] = { 686881f68fSopenharmony_ci OPTION("--no-notify", no_notify), 696881f68fSopenharmony_ci OPTION("--update-interval=%d", update_interval), 706881f68fSopenharmony_ci FUSE_OPT_END 716881f68fSopenharmony_ci}; 726881f68fSopenharmony_ci 736881f68fSopenharmony_cistatic void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) 746881f68fSopenharmony_ci{ 756881f68fSopenharmony_ci (void) conn; 766881f68fSopenharmony_ci cfg->entry_timeout = NO_TIMEOUT; 776881f68fSopenharmony_ci cfg->attr_timeout = NO_TIMEOUT; 786881f68fSopenharmony_ci cfg->negative_timeout = 0; 796881f68fSopenharmony_ci 806881f68fSopenharmony_ci return NULL; 816881f68fSopenharmony_ci} 826881f68fSopenharmony_ci 836881f68fSopenharmony_cistatic int xmp_getattr(const char *path, 846881f68fSopenharmony_ci struct stat *stbuf, struct fuse_file_info* fi) { 856881f68fSopenharmony_ci (void) fi; 866881f68fSopenharmony_ci if (strcmp(path, "/") == 0) { 876881f68fSopenharmony_ci stbuf->st_ino = 1; 886881f68fSopenharmony_ci stbuf->st_mode = S_IFDIR | 0755; 896881f68fSopenharmony_ci stbuf->st_nlink = 1; 906881f68fSopenharmony_ci } else if (strcmp(path, "/" TIME_FILE_NAME) == 0) { 916881f68fSopenharmony_ci stbuf->st_ino = TIME_FILE_INO; 926881f68fSopenharmony_ci stbuf->st_mode = S_IFREG | 0444; 936881f68fSopenharmony_ci stbuf->st_nlink = 1; 946881f68fSopenharmony_ci stbuf->st_size = strlen(time_file_contents); 956881f68fSopenharmony_ci } else if (strcmp(path, "/" GROW_FILE_NAME) == 0) { 966881f68fSopenharmony_ci stbuf->st_ino = GROW_FILE_INO; 976881f68fSopenharmony_ci stbuf->st_mode = S_IFREG | 0444; 986881f68fSopenharmony_ci stbuf->st_nlink = 1; 996881f68fSopenharmony_ci stbuf->st_size = grow_file_size; 1006881f68fSopenharmony_ci } else { 1016881f68fSopenharmony_ci return -ENOENT; 1026881f68fSopenharmony_ci } 1036881f68fSopenharmony_ci 1046881f68fSopenharmony_ci return 0; 1056881f68fSopenharmony_ci} 1066881f68fSopenharmony_ci 1076881f68fSopenharmony_cistatic int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 1086881f68fSopenharmony_ci off_t offset, struct fuse_file_info *fi, 1096881f68fSopenharmony_ci enum fuse_readdir_flags flags) { 1106881f68fSopenharmony_ci (void) fi; 1116881f68fSopenharmony_ci (void) offset; 1126881f68fSopenharmony_ci (void) flags; 1136881f68fSopenharmony_ci if (strcmp(path, "/") != 0) { 1146881f68fSopenharmony_ci return -ENOTDIR; 1156881f68fSopenharmony_ci } else { 1166881f68fSopenharmony_ci (void) filler; 1176881f68fSopenharmony_ci (void) buf; 1186881f68fSopenharmony_ci struct stat file_stat; 1196881f68fSopenharmony_ci xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL); 1206881f68fSopenharmony_ci filler(buf, TIME_FILE_NAME, &file_stat, 0, 0); 1216881f68fSopenharmony_ci xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL); 1226881f68fSopenharmony_ci filler(buf, GROW_FILE_NAME, &file_stat, 0, 0); 1236881f68fSopenharmony_ci return 0; 1246881f68fSopenharmony_ci } 1256881f68fSopenharmony_ci} 1266881f68fSopenharmony_ci 1276881f68fSopenharmony_cistatic int xmp_open(const char *path, struct fuse_file_info *fi) { 1286881f68fSopenharmony_ci (void) path; 1296881f68fSopenharmony_ci /* Make cache persistent even if file is closed, 1306881f68fSopenharmony_ci this makes it easier to see the effects */ 1316881f68fSopenharmony_ci fi->keep_cache = 1; 1326881f68fSopenharmony_ci return 0; 1336881f68fSopenharmony_ci} 1346881f68fSopenharmony_ci 1356881f68fSopenharmony_cistatic int xmp_read(const char *path, char *buf, size_t size, off_t offset, 1366881f68fSopenharmony_ci struct fuse_file_info *fi) { 1376881f68fSopenharmony_ci (void) fi; 1386881f68fSopenharmony_ci (void) offset; 1396881f68fSopenharmony_ci if (strcmp(path, "/" TIME_FILE_NAME) == 0) { 1406881f68fSopenharmony_ci int file_length = strlen(time_file_contents); 1416881f68fSopenharmony_ci int to_copy = offset + size <= file_length 1426881f68fSopenharmony_ci ? size 1436881f68fSopenharmony_ci : file_length - offset; 1446881f68fSopenharmony_ci memcpy(buf, time_file_contents, to_copy); 1456881f68fSopenharmony_ci return to_copy; 1466881f68fSopenharmony_ci } else { 1476881f68fSopenharmony_ci assert(strcmp(path, "/" GROW_FILE_NAME) == 0); 1486881f68fSopenharmony_ci int to_copy = offset + size <= grow_file_size 1496881f68fSopenharmony_ci ? size 1506881f68fSopenharmony_ci : grow_file_size - offset; 1516881f68fSopenharmony_ci memset(buf, 'x', to_copy); 1526881f68fSopenharmony_ci return to_copy; 1536881f68fSopenharmony_ci } 1546881f68fSopenharmony_ci} 1556881f68fSopenharmony_ci 1566881f68fSopenharmony_cistatic const struct fuse_operations xmp_oper = { 1576881f68fSopenharmony_ci .init = xmp_init, 1586881f68fSopenharmony_ci .getattr = xmp_getattr, 1596881f68fSopenharmony_ci .readdir = xmp_readdir, 1606881f68fSopenharmony_ci .open = xmp_open, 1616881f68fSopenharmony_ci .read = xmp_read, 1626881f68fSopenharmony_ci}; 1636881f68fSopenharmony_ci 1646881f68fSopenharmony_cistatic void update_fs(void) { 1656881f68fSopenharmony_ci static int count = 0; 1666881f68fSopenharmony_ci struct tm *now; 1676881f68fSopenharmony_ci time_t t; 1686881f68fSopenharmony_ci t = time(NULL); 1696881f68fSopenharmony_ci now = localtime(&t); 1706881f68fSopenharmony_ci assert(now != NULL); 1716881f68fSopenharmony_ci 1726881f68fSopenharmony_ci int time_file_size = strftime(time_file_contents, MAX_STR_LEN, 1736881f68fSopenharmony_ci "The current time is %H:%M:%S\n", now); 1746881f68fSopenharmony_ci assert(time_file_size != 0); 1756881f68fSopenharmony_ci 1766881f68fSopenharmony_ci grow_file_size = count++; 1776881f68fSopenharmony_ci} 1786881f68fSopenharmony_ci 1796881f68fSopenharmony_cistatic int invalidate(struct fuse *fuse, const char *path) { 1806881f68fSopenharmony_ci int status = fuse_invalidate_path(fuse, path); 1816881f68fSopenharmony_ci if (status == -ENOENT) { 1826881f68fSopenharmony_ci return 0; 1836881f68fSopenharmony_ci } else { 1846881f68fSopenharmony_ci return status; 1856881f68fSopenharmony_ci } 1866881f68fSopenharmony_ci} 1876881f68fSopenharmony_ci 1886881f68fSopenharmony_cistatic void* update_fs_loop(void *data) { 1896881f68fSopenharmony_ci struct fuse *fuse = (struct fuse*) data; 1906881f68fSopenharmony_ci 1916881f68fSopenharmony_ci while (1) { 1926881f68fSopenharmony_ci update_fs(); 1936881f68fSopenharmony_ci if (!options.no_notify) { 1946881f68fSopenharmony_ci assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0); 1956881f68fSopenharmony_ci assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0); 1966881f68fSopenharmony_ci } 1976881f68fSopenharmony_ci sleep(options.update_interval); 1986881f68fSopenharmony_ci } 1996881f68fSopenharmony_ci return NULL; 2006881f68fSopenharmony_ci} 2016881f68fSopenharmony_ci 2026881f68fSopenharmony_cistatic void show_help(const char *progname) 2036881f68fSopenharmony_ci{ 2046881f68fSopenharmony_ci printf("usage: %s [options] <mountpoint>\n\n", progname); 2056881f68fSopenharmony_ci printf("File-system specific options:\n" 2066881f68fSopenharmony_ci " --update-interval=<secs> Update-rate of file system contents\n" 2076881f68fSopenharmony_ci " --no-notify Disable kernel notifications\n" 2086881f68fSopenharmony_ci "\n"); 2096881f68fSopenharmony_ci} 2106881f68fSopenharmony_ci 2116881f68fSopenharmony_ciint main(int argc, char *argv[]) { 2126881f68fSopenharmony_ci struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 2136881f68fSopenharmony_ci struct fuse *fuse; 2146881f68fSopenharmony_ci struct fuse_cmdline_opts opts; 2156881f68fSopenharmony_ci struct fuse_loop_config config; 2166881f68fSopenharmony_ci int res; 2176881f68fSopenharmony_ci 2186881f68fSopenharmony_ci /* Initialize the files */ 2196881f68fSopenharmony_ci update_fs(); 2206881f68fSopenharmony_ci 2216881f68fSopenharmony_ci if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) 2226881f68fSopenharmony_ci return 1; 2236881f68fSopenharmony_ci 2246881f68fSopenharmony_ci if (fuse_parse_cmdline(&args, &opts) != 0) 2256881f68fSopenharmony_ci return 1; 2266881f68fSopenharmony_ci 2276881f68fSopenharmony_ci if (opts.show_version) { 2286881f68fSopenharmony_ci printf("FUSE library version %s\n", fuse_pkgversion()); 2296881f68fSopenharmony_ci fuse_lowlevel_version(); 2306881f68fSopenharmony_ci res = 0; 2316881f68fSopenharmony_ci goto out1; 2326881f68fSopenharmony_ci } else if (opts.show_help) { 2336881f68fSopenharmony_ci show_help(argv[0]); 2346881f68fSopenharmony_ci fuse_cmdline_help(); 2356881f68fSopenharmony_ci fuse_lib_help(&args); 2366881f68fSopenharmony_ci res = 0; 2376881f68fSopenharmony_ci goto out1; 2386881f68fSopenharmony_ci } else if (!opts.mountpoint) { 2396881f68fSopenharmony_ci fprintf(stderr, "error: no mountpoint specified\n"); 2406881f68fSopenharmony_ci res = 1; 2416881f68fSopenharmony_ci goto out1; 2426881f68fSopenharmony_ci } 2436881f68fSopenharmony_ci 2446881f68fSopenharmony_ci fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL); 2456881f68fSopenharmony_ci if (fuse == NULL) { 2466881f68fSopenharmony_ci res = 1; 2476881f68fSopenharmony_ci goto out1; 2486881f68fSopenharmony_ci } 2496881f68fSopenharmony_ci 2506881f68fSopenharmony_ci if (fuse_mount(fuse,opts.mountpoint) != 0) { 2516881f68fSopenharmony_ci res = 1; 2526881f68fSopenharmony_ci goto out2; 2536881f68fSopenharmony_ci } 2546881f68fSopenharmony_ci 2556881f68fSopenharmony_ci if (fuse_daemonize(opts.foreground) != 0) { 2566881f68fSopenharmony_ci res = 1; 2576881f68fSopenharmony_ci goto out3; 2586881f68fSopenharmony_ci } 2596881f68fSopenharmony_ci 2606881f68fSopenharmony_ci pthread_t updater; /* Start thread to update file contents */ 2616881f68fSopenharmony_ci int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse); 2626881f68fSopenharmony_ci if (ret != 0) { 2636881f68fSopenharmony_ci fprintf(stderr, "pthread_create failed with %s\n", strerror(ret)); 2646881f68fSopenharmony_ci return 1; 2656881f68fSopenharmony_ci }; 2666881f68fSopenharmony_ci 2676881f68fSopenharmony_ci struct fuse_session *se = fuse_get_session(fuse); 2686881f68fSopenharmony_ci if (fuse_set_signal_handlers(se) != 0) { 2696881f68fSopenharmony_ci res = 1; 2706881f68fSopenharmony_ci goto out3; 2716881f68fSopenharmony_ci } 2726881f68fSopenharmony_ci 2736881f68fSopenharmony_ci if (opts.singlethread) 2746881f68fSopenharmony_ci res = fuse_loop(fuse); 2756881f68fSopenharmony_ci else { 2766881f68fSopenharmony_ci config.clone_fd = opts.clone_fd; 2776881f68fSopenharmony_ci config.max_idle_threads = opts.max_idle_threads; 2786881f68fSopenharmony_ci res = fuse_loop_mt(fuse, &config); 2796881f68fSopenharmony_ci } 2806881f68fSopenharmony_ci if (res) 2816881f68fSopenharmony_ci res = 1; 2826881f68fSopenharmony_ci 2836881f68fSopenharmony_ci fuse_remove_signal_handlers(se); 2846881f68fSopenharmony_ciout3: 2856881f68fSopenharmony_ci fuse_unmount(fuse); 2866881f68fSopenharmony_ciout2: 2876881f68fSopenharmony_ci fuse_destroy(fuse); 2886881f68fSopenharmony_ciout1: 2896881f68fSopenharmony_ci free(opts.mountpoint); 2906881f68fSopenharmony_ci fuse_opt_free_args(&args); 2916881f68fSopenharmony_ci return res; 2926881f68fSopenharmony_ci} 293