From fc83143867a37e34a51ce5a6d763b46715abf02d Mon Sep 17 00:00:00 2001 From: Nikolaus Rath Date: Thu, 24 Aug 2017 14:23:13 +0200 Subject: Renamed notify_inval_inode_fh to invalidate_path The previous name didn't make much sense. --- example/invalidate_path.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 example/invalidate_path.c (limited to 'example/invalidate_path.c') diff --git a/example/invalidate_path.c b/example/invalidate_path.c new file mode 100644 index 0000000..afa1339 --- /dev/null +++ b/example/invalidate_path.c @@ -0,0 +1,292 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2016 Nikolaus Rath + (C) 2017 EditShare LLC + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + */ + +/** @file + * + * This example implements a file system with two files: + * * 'current-time', whose contents change dynamically: + * it always contains the current time (same as in + * notify_inval_inode.c). + * * 'growing', whose size changes dynamically, growing + * by 1 byte after each update. This aims to check + * if cached file metadata is also invalidated. + * + * ## Compilation ## + * + * gcc -Wall @file `pkg-config fuse3 --cflags --libs` -o invalidate_path + * + * ## Source code ## + * \include @file + */ + +#define FUSE_USE_VERSION 31 + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include /* for fuse_cmdline_opts */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We can't actually tell the kernel that there is no + timeout, so we just send a big value */ +#define NO_TIMEOUT 500000 + +#define MAX_STR_LEN 128 +#define TIME_FILE_NAME "current_time" +#define TIME_FILE_INO 2 +#define GROW_FILE_NAME "growing" +#define GROW_FILE_INO 3 + +static char time_file_contents[MAX_STR_LEN]; +static size_t grow_file_size; + +/* Command line parsing */ +struct options { + int no_notify; + int update_interval; +}; +static struct options options = { + .no_notify = 0, + .update_interval = 1, +}; + +#define OPTION(t, p) { t, offsetof(struct options, p), 1 } +static const struct fuse_opt option_spec[] = { + OPTION("--no-notify", no_notify), + OPTION("--update-interval=%d", update_interval), + FUSE_OPT_END +}; + +static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) +{ + (void) conn; + cfg->entry_timeout = NO_TIMEOUT; + cfg->attr_timeout = NO_TIMEOUT; + cfg->negative_timeout = 0; + + return NULL; +} + +static int xmp_getattr(const char *path, + struct stat *stbuf, struct fuse_file_info* fi) { + (void) fi; + if (strcmp(path, "/") == 0) { + stbuf->st_ino = 1; + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 1; + } else if (strcmp(path, "/" TIME_FILE_NAME) == 0) { + stbuf->st_ino = TIME_FILE_INO; + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = strlen(time_file_contents); + } else if (strcmp(path, "/" GROW_FILE_NAME) == 0) { + stbuf->st_ino = GROW_FILE_INO; + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = grow_file_size; + } else { + return -ENOENT; + } + + return 0; +} + +static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) { + (void) fi; + (void) offset; + (void) flags; + if (strcmp(path, "/") != 0) { + return -ENOTDIR; + } else { + (void) filler; + (void) buf; + struct stat file_stat; + xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL); + filler(buf, TIME_FILE_NAME, &file_stat, 0, 0); + xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL); + filler(buf, GROW_FILE_NAME, &file_stat, 0, 0); + return 0; + } +} + +static int xmp_open(const char *path, struct fuse_file_info *fi) { + (void) path; + /* Make cache persistent even if file is closed, + this makes it easier to see the effects */ + fi->keep_cache = 1; + return 0; +} + +static int xmp_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) { + (void) fi; + (void) offset; + if (strcmp(path, "/" TIME_FILE_NAME) == 0) { + int file_length = strlen(time_file_contents); + int to_copy = offset + size <= file_length + ? size + : file_length - offset; + memcpy(buf, time_file_contents, to_copy); + return to_copy; + } else { + assert(strcmp(path, "/" GROW_FILE_NAME) == 0); + int to_copy = offset + size <= grow_file_size + ? size + : grow_file_size - offset; + memset(buf, 'x', to_copy); + return to_copy; + } +} + +static struct fuse_operations xmp_oper = { + .init = xmp_init, + .getattr = xmp_getattr, + .readdir = xmp_readdir, + .open = xmp_open, + .read = xmp_read, +}; + +static void update_fs(void) { + static int count = 0; + struct tm *now; + time_t t; + t = time(NULL); + now = localtime(&t); + assert(now != NULL); + + int time_file_size = strftime(time_file_contents, MAX_STR_LEN, + "The current time is %H:%M:%S\n", now); + assert(time_file_size != 0); + + grow_file_size = count++; +} + +static int invalidate(struct fuse *fuse, const char *path) { + int status = fuse_invalidate_path(fuse, path); + if (status == -ENOENT) { + return 0; + } else { + return status; + } +} + +static void* update_fs_loop(void *data) { + struct fuse *fuse = (struct fuse*) data; + + while (1) { + update_fs(); + if (!options.no_notify) { + assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0); + assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0); + } + sleep(options.update_interval); + } + return NULL; +} + +static void show_help(const char *progname) +{ + printf("usage: %s [options] \n\n", progname); + printf("File-system specific options:\n" + " --update-interval= Update-rate of file system contents\n" + " --no-notify Disable kernel notifications\n" + "\n"); +} + +int main(int argc, char *argv[]) { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse *fuse; + struct fuse_cmdline_opts opts; + int res; + + /* Initialize the files */ + update_fs(); + + if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) + return 1; + + if (fuse_parse_cmdline(&args, &opts) != 0) + return 1; + + if (opts.show_version) { + printf("FUSE library version %s\n", fuse_pkgversion()); + fuse_lowlevel_version(); + res = 0; + goto out1; + } else if (opts.show_help) { + show_help(argv[0]); + fuse_cmdline_help(); + fuse_lib_help(&args); + res = 0; + goto out1; + } else if (!opts.mountpoint) { + fprintf(stderr, "error: no mountpoint specified\n"); + res = 1; + goto out1; + } + + fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL); + if (fuse == NULL) { + res = 1; + goto out1; + } + + if (fuse_mount(fuse,opts.mountpoint) != 0) { + res = 1; + goto out2; + } + + if (fuse_daemonize(opts.foreground) != 0) { + res = 1; + goto out3; + } + + pthread_t updater; /* Start thread to update file contents */ + int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse); + if (ret != 0) { + fprintf(stderr, "pthread_create failed with %s\n", strerror(ret)); + return 1; + }; + + struct fuse_session *se = fuse_get_session(fuse); + if (fuse_set_signal_handlers(se) != 0) { + res = 1; + goto out3; + } + + if (opts.singlethread) + res = fuse_loop(fuse); + else + res = fuse_loop_mt(fuse, opts.clone_fd); + if (res) + res = 1; + + fuse_remove_signal_handlers(se); +out3: + fuse_unmount(fuse); +out2: + fuse_destroy(fuse); +out1: + free(opts.mountpoint); + fuse_opt_free_args(&args); + return res; +} -- cgit v1.2.3