"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.3/example/notify_store_retrieve.c" (11 May 2018, 11693 Bytes) of package /linux/misc/fuse-3.2.3.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 last Fossies "Diffs" side-by-side code changes report for "notify_store_retrieve.c": 3.2.1_vs_3.2.2.

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