"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.1/example/notify_inval_inode.c" (14 Nov 2017, 9743 Bytes) of package /linux/misc/fuse-3.2.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "notify_inval_inode.c": 3.2.0_vs_3.2.1.

    1 /*
    2   FUSE: Filesystem in Userspace
    3   Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
    4 
    5   This program can be distributed under the terms of the GNU GPL.
    6   See the file COPYING.
    7 */
    8 
    9 /** @file
   10  *
   11  * This example implements a file system with a single file whose
   12  * contents change dynamically: it always contains the current time.
   13  *
   14  * While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to
   15  * actively push the updated data into the kernel cache, this example
   16  * uses fuse_lowlevel_notify_inval_inode() to notify the kernel that
   17  * the cache has to be invalidated - but the kernel still has to
   18  * explicitly request the updated data on the next read.
   19  *
   20  * To see the effect, first start the file system with the
   21  *  ``--no-notify`` option:
   22  *
   23  *     $ notify_inval_inode --update-interval=1 --no-notify mnt/
   24  *
   25  * Observe that the output never changes, even though the file system
   26  * updates it once per second. This is because the contents are cached
   27  * in the kernel:
   28  *
   29  *     $ for i in 1 2 3 4 5; do
   30  *     >     cat mnt/current_time
   31  *     >     sleep 1
   32  *     > done
   33  *     The current time is 15:58:18
   34  *     The current time is 15:58:18
   35  *     The current time is 15:58:18
   36  *     The current time is 15:58:18
   37  *     The current time is 15:58:18
   38  *
   39  * If you instead enable the notification functions, the changes become
   40  * visible:
   41  *
   42  *      $ notify_inval_inode --update-interval=1 mnt/
   43  *      $ for i in 1 2 3 4 5; do
   44  *      >     cat mnt/current_time
   45  *      >     sleep 1
   46  *      > done
   47  *      The current time is 15:58:40
   48  *      The current time is 15:58:41
   49  *      The current time is 15:58:42
   50  *      The current time is 15:58:43
   51  *      The current time is 15:58:44
   52  *
   53  * ## Compilation ##
   54  *
   55  *     gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode
   56  *
   57  * ## Source code ##
   58  * \include notify_inval_inode.c
   59  */
   60 
   61 
   62 #define FUSE_USE_VERSION 31
   63 
   64 #include <fuse_lowlevel.h>
   65 #include <stdio.h>
   66 #include <stdlib.h>
   67 #include <string.h>
   68 #include <errno.h>
   69 #include <fcntl.h>
   70 #include <assert.h>
   71 #include <stddef.h>
   72 #include <unistd.h>
   73 #include <pthread.h>
   74 
   75 /* We can't actually tell the kernel that there is no
   76    timeout, so we just send a big value */
   77 #define NO_TIMEOUT 500000
   78 
   79 /* We cannot check directly if e.g. O_RDONLY is set, since this is not
   80  * an individual bit (cf. open(2)) */
   81 #define ACCESS_MASK (O_RDONLY | O_WRONLY | O_RDWR)
   82 
   83 #define MAX_STR_LEN 128
   84 #define FILE_INO 2
   85 #define FILE_NAME "current_time"
   86 static char file_contents[MAX_STR_LEN];
   87 static int lookup_cnt = 0;
   88 static size_t file_size;
   89 
   90 /* Command line parsing */
   91 struct options {
   92     int no_notify;
   93     int update_interval;
   94 };
   95 static struct options options = {
   96     .no_notify = 0,
   97     .update_interval = 1,
   98 };
   99 
  100 #define OPTION(t, p)                           \
  101     { t, offsetof(struct options, p), 1 }
  102 static const struct fuse_opt option_spec[] = {
  103     OPTION("--no-notify", no_notify),
  104     OPTION("--update-interval=%d", update_interval),
  105     FUSE_OPT_END
  106 };
  107 
  108 static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
  109     stbuf->st_ino = ino;
  110     if (ino == FUSE_ROOT_ID) {
  111         stbuf->st_mode = S_IFDIR | 0755;
  112         stbuf->st_nlink = 1;
  113     }
  114 
  115     else if (ino == FILE_INO) {
  116         stbuf->st_mode = S_IFREG | 0444;
  117         stbuf->st_nlink = 1;
  118         stbuf->st_size = file_size;
  119     }
  120 
  121     else
  122         return -1;
  123 
  124     return 0;
  125 }
  126 
  127 static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
  128                        const char *name) {
  129     struct fuse_entry_param e;
  130     memset(&e, 0, sizeof(e));
  131 
  132     if (parent != FUSE_ROOT_ID)
  133         goto err_out;
  134     else if (strcmp(name, FILE_NAME) == 0) {
  135         e.ino = FILE_INO;
  136         lookup_cnt++;
  137     } else
  138         goto err_out;
  139 
  140     e.attr_timeout = NO_TIMEOUT;
  141     e.entry_timeout = NO_TIMEOUT;
  142     if (tfs_stat(e.ino, &e.attr) != 0)
  143         goto err_out;
  144     fuse_reply_entry(req, &e);
  145     return;
  146 
  147 err_out:
  148     fuse_reply_err(req, ENOENT);
  149 }
  150 
  151 static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
  152                         uint64_t nlookup) {
  153     (void) req;
  154     if(ino == FILE_INO)
  155         lookup_cnt -= nlookup;
  156     else
  157         assert(ino == FUSE_ROOT_ID);
  158     fuse_reply_none(req);
  159 }
  160 
  161 static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
  162                         struct fuse_file_info *fi) {
  163     struct stat stbuf;
  164 
  165     (void) fi;
  166 
  167     memset(&stbuf, 0, sizeof(stbuf));
  168     if (tfs_stat(ino, &stbuf) != 0)
  169         fuse_reply_err(req, ENOENT);
  170     else
  171         fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
  172 }
  173 
  174 struct dirbuf {
  175     char *p;
  176     size_t size;
  177 };
  178 
  179 static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
  180                        fuse_ino_t ino) {
  181     struct stat stbuf;
  182     size_t oldsize = b->size;
  183     b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
  184     b->p = (char *) realloc(b->p, b->size);
  185     memset(&stbuf, 0, sizeof(stbuf));
  186     stbuf.st_ino = ino;
  187     fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
  188                       b->size);
  189 }
  190 
  191 #define min(x, y) ((x) < (y) ? (x) : (y))
  192 
  193 static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
  194                              off_t off, size_t maxsize) {
  195     if (off < bufsize)
  196         return fuse_reply_buf(req, buf + off,
  197                               min(bufsize - off, maxsize));
  198     else
  199         return fuse_reply_buf(req, NULL, 0);
  200 }
  201 
  202 static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
  203                         off_t off, struct fuse_file_info *fi) {
  204     (void) fi;
  205 
  206     if (ino != FUSE_ROOT_ID)
  207         fuse_reply_err(req, ENOTDIR);
  208     else {
  209         struct dirbuf b;
  210 
  211         memset(&b, 0, sizeof(b));
  212         dirbuf_add(req, &b, FILE_NAME, FILE_INO);
  213         reply_buf_limited(req, b.p, b.size, off, size);
  214         free(b.p);
  215     }
  216 }
  217 
  218 static void tfs_open(fuse_req_t req, fuse_ino_t ino,
  219                      struct fuse_file_info *fi) {
  220 
  221     /* Make cache persistent even if file is closed,
  222        this makes it easier to see the effects */
  223     fi->keep_cache = 1;
  224 
  225     if (ino == FUSE_ROOT_ID)
  226         fuse_reply_err(req, EISDIR);
  227     else if ((fi->flags & ACCESS_MASK) != O_RDONLY)
  228         fuse_reply_err(req, EACCES);
  229     else if (ino == FILE_INO)
  230         fuse_reply_open(req, fi);
  231     else {
  232         // This should not happen
  233         fprintf(stderr, "Got open for non-existing inode!\n");
  234         fuse_reply_err(req, ENOENT);
  235     }
  236 }
  237 
  238 static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
  239                      off_t off, struct fuse_file_info *fi) {
  240     (void) fi;
  241 
  242     assert(ino == FILE_INO);
  243     reply_buf_limited(req, file_contents, file_size, off, size);
  244 }
  245 
  246 static struct fuse_lowlevel_ops tfs_oper = {
  247     .lookup = tfs_lookup,
  248     .getattr    = tfs_getattr,
  249     .readdir    = tfs_readdir,
  250     .open   = tfs_open,
  251     .read   = tfs_read,
  252     .forget     = tfs_forget,
  253 };
  254 
  255 static void update_fs(void) {
  256     struct tm *now;
  257     time_t t;
  258     t = time(NULL);
  259     now = localtime(&t);
  260     assert(now != NULL);
  261 
  262     file_size = strftime(file_contents, MAX_STR_LEN,
  263                          "The current time is %H:%M:%S\n", now);
  264     assert(file_size != 0);
  265 }
  266 
  267 static void* update_fs_loop(void *data) {
  268     struct fuse_session *se = (struct fuse_session*) data;
  269 
  270     while(1) {
  271         update_fs();
  272         if (!options.no_notify && lookup_cnt) {
  273             /* Only send notification if the kernel
  274                is aware of the inode */
  275             assert(fuse_lowlevel_notify_inval_inode
  276                    (se, FILE_INO, 0, 0) == 0);
  277         }
  278         sleep(options.update_interval);
  279     }
  280     return NULL;
  281 }
  282 
  283 static void show_help(const char *progname)
  284 {
  285     printf("usage: %s [options] <mountpoint>\n\n", progname);
  286     printf("File-system specific options:\n"
  287                "    --update-interval=<secs>  Update-rate of file system contents\n"
  288                "    --no-notify            Disable kernel notifications\n"
  289                "\n");
  290 }
  291 
  292 int main(int argc, char *argv[]) {
  293     struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
  294     struct fuse_session *se;
  295     struct fuse_cmdline_opts opts;
  296     pthread_t updater;
  297     int ret = -1;
  298 
  299     if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
  300         return 1;
  301 
  302     if (fuse_parse_cmdline(&args, &opts) != 0) {
  303         ret = 1;
  304         goto err_out1;
  305     }
  306 
  307     if (opts.show_help) {
  308         show_help(argv[0]);
  309         fuse_cmdline_help();
  310         fuse_lowlevel_help();
  311         ret = 0;
  312         goto err_out1;
  313     } else if (opts.show_version) {
  314         printf("FUSE library version %s\n", fuse_pkgversion());
  315         fuse_lowlevel_version();
  316         ret = 0;
  317         goto err_out1;
  318     }
  319 
  320     /* Initial contents */
  321     update_fs();
  322 
  323     se = fuse_session_new(&args, &tfs_oper,
  324                           sizeof(tfs_oper), NULL);
  325     if (se == NULL)
  326         goto err_out1;
  327 
  328     if (fuse_set_signal_handlers(se) != 0)
  329         goto err_out2;
  330 
  331     if (fuse_session_mount(se, opts.mountpoint) != 0)
  332         goto err_out3;
  333 
  334     fuse_daemonize(opts.foreground);
  335 
  336     /* Start thread to update file contents */
  337     ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
  338     if (ret != 0) {
  339         fprintf(stderr, "pthread_create failed with %s\n",
  340                 strerror(ret));
  341         goto err_out3;
  342     }
  343 
  344     /* Block until ctrl+c or fusermount -u */
  345     if (opts.singlethread)
  346         ret = fuse_session_loop(se);
  347     else
  348         ret = fuse_session_loop_mt(se, opts.clone_fd);
  349 
  350     fuse_session_unmount(se);
  351 err_out3:
  352     fuse_remove_signal_handlers(se);
  353 err_out2:
  354     fuse_session_destroy(se);
  355 err_out1:
  356     fuse_opt_free_args(&args);
  357     free(opts.mountpoint);
  358 
  359     return ret ? 1 : 0;
  360 }
  361 
  362 
  363 /**
  364  * Local Variables:
  365  * mode: c
  366  * indent-tabs-mode: nil
  367  * c-basic-offset: 4
  368  * End:
  369  */