"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.3/example/notify_inval_entry.c" (11 May 2018, 9421 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_inval_entry.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  * file name changes dynamically to reflect the current time.
   13  *
   14  * It illustrates the use of the fuse_lowlevel_notify_inval_entry()
   15  * function.
   16  *
   17  * To see the effect, first start the file system with the
   18  * ``--no-notify``
   19  *
   20  *     $ notify_inval_entry --update-interval=1 --timeout 30 --no-notify mnt/
   21  *
   22  * Observe that `ls` always prints the correct directory contents
   23  * (since `readdir` output is not cached)::
   24  *
   25  *     $ ls mnt; sleep 1; ls mnt; sleep 1; ls mnt
   26  *     Time_is_15h_48m_33s  current_time
   27  *     Time_is_15h_48m_34s  current_time
   28  *     Time_is_15h_48m_35s  current_time
   29  *
   30  * However, if you try to access a file by name the kernel will
   31  * report that it still exists:
   32  *
   33  *     $ file=$(ls mnt/); echo $file
   34  *     Time_is_15h_50m_09s
   35  *     $ sleep 5; stat mnt/$file
   36  *       File: ‘mnt/Time_is_15h_50m_09s’
   37  *       Size: 32                Blocks: 0          IO Block: 4096   regular file
   38  *     Device: 2ah/42d  Inode: 3           Links: 1
   39  *     Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
   40  *     Access: 1969-12-31 16:00:00.000000000 -0800
   41  *     Modify: 1969-12-31 16:00:00.000000000 -0800
   42  *     Change: 1969-12-31 16:00:00.000000000 -0800
   43  *      Birth: -
   44  *
   45  * Only once the kernel cache timeout has been reached will the stat
   46  * call fail:
   47  *
   48  *     $ sleep 30; stat mnt/$file
   49  *     stat: cannot stat ‘mnt/Time_is_15h_50m_09s’: No such file or directory
   50  *
   51  * In contrast, if you enable notifications you will be unable to stat
   52  * the file as soon as the file system updates its name:
   53  *
   54  *     $ notify_inval_entry --update-interval=1 --timeout 30 --no-notify mnt/
   55  *     $ file=$(ls mnt/); stat mnt/$file
   56  *       File: ‘mnt/Time_is_20h_42m_11s’
   57  *       Size: 0                 Blocks: 0          IO Block: 4096   regular empty file
   58  *     Device: 2ah/42d  Inode: 2           Links: 1
   59  *     Access: (0000/----------)  Uid: (    0/    root)   Gid: (    0/    root)
   60  *     Access: 1969-12-31 16:00:00.000000000 -0800
   61  *     Modify: 1969-12-31 16:00:00.000000000 -0800
   62  *     Change: 1969-12-31 16:00:00.000000000 -0800
   63  *      Birth: -
   64  *     $ sleep 1; stat mnt/$file
   65  *     stat: cannot stat ‘mnt/Time_is_20h_42m_11s’: No such file or directory
   66  *
   67  * ## Compilation ##
   68  *
   69  *     gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry
   70  *
   71  * ## Source code ##
   72  * \include notify_inval_entry.c
   73  */
   74 
   75 
   76 #define FUSE_USE_VERSION 31
   77 
   78 #include <fuse_lowlevel.h>
   79 #include <stdio.h>
   80 #include <stdlib.h>
   81 #include <string.h>
   82 #include <errno.h>
   83 #include <fcntl.h>
   84 #include <assert.h>
   85 #include <stddef.h>
   86 #include <unistd.h>
   87 #include <pthread.h>
   88 
   89 #define MAX_STR_LEN 128
   90 static char file_name[MAX_STR_LEN];
   91 static fuse_ino_t file_ino = 2;
   92 static int lookup_cnt = 0;
   93 
   94 /* Command line parsing */
   95 struct options {
   96     int no_notify;
   97     float timeout;
   98     int update_interval;
   99 };
  100 static struct options options = {
  101     .timeout = 5,
  102     .no_notify = 0,
  103     .update_interval = 1,
  104 };
  105 
  106 #define OPTION(t, p)                           \
  107     { t, offsetof(struct options, p), 1 }
  108 static const struct fuse_opt option_spec[] = {
  109     OPTION("--no-notify", no_notify),
  110     OPTION("--update-interval=%d", update_interval),
  111     OPTION("--timeout=%f", timeout),
  112     FUSE_OPT_END
  113 };
  114 
  115 static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
  116     stbuf->st_ino = ino;
  117     if (ino == FUSE_ROOT_ID) {
  118         stbuf->st_mode = S_IFDIR | 0755;
  119         stbuf->st_nlink = 1;
  120     }
  121 
  122     else if (ino == file_ino) {
  123         stbuf->st_mode = S_IFREG | 0000;
  124         stbuf->st_nlink = 1;
  125         stbuf->st_size = 0;
  126     }
  127 
  128     else
  129         return -1;
  130 
  131     return 0;
  132 }
  133 
  134 static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
  135                        const char *name) {
  136     struct fuse_entry_param e;
  137     memset(&e, 0, sizeof(e));
  138 
  139     if (parent != FUSE_ROOT_ID)
  140         goto err_out;
  141     else if (strcmp(name, file_name) == 0) {
  142         e.ino = file_ino;
  143         lookup_cnt++;
  144     } else
  145         goto err_out;
  146 
  147     e.attr_timeout = options.timeout;
  148     e.entry_timeout = options.timeout;
  149     if (tfs_stat(e.ino, &e.attr) != 0)
  150         goto err_out;
  151     fuse_reply_entry(req, &e);
  152     return;
  153 
  154 err_out:
  155     fuse_reply_err(req, ENOENT);
  156 }
  157 
  158 static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
  159                         uint64_t nlookup) {
  160     (void) req;
  161     if(ino == file_ino)
  162         lookup_cnt -= nlookup;
  163     else
  164         assert(ino == FUSE_ROOT_ID);
  165     fuse_reply_none(req);
  166 }
  167 
  168 static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
  169                         struct fuse_file_info *fi) {
  170     struct stat stbuf;
  171 
  172     (void) fi;
  173 
  174     memset(&stbuf, 0, sizeof(stbuf));
  175     if (tfs_stat(ino, &stbuf) != 0)
  176         fuse_reply_err(req, ENOENT);
  177     else
  178         fuse_reply_attr(req, &stbuf, options.timeout);
  179 }
  180 
  181 struct dirbuf {
  182     char *p;
  183     size_t size;
  184 };
  185 
  186 static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
  187                        fuse_ino_t ino) {
  188     struct stat stbuf;
  189     size_t oldsize = b->size;
  190     b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
  191     b->p = (char *) realloc(b->p, b->size);
  192     memset(&stbuf, 0, sizeof(stbuf));
  193     stbuf.st_ino = ino;
  194     fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
  195                       b->size);
  196 }
  197 
  198 #define min(x, y) ((x) < (y) ? (x) : (y))
  199 
  200 static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
  201                              off_t off, size_t maxsize) {
  202     if (off < bufsize)
  203         return fuse_reply_buf(req, buf + off,
  204                               min(bufsize - off, maxsize));
  205     else
  206         return fuse_reply_buf(req, NULL, 0);
  207 }
  208 
  209 static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
  210                         off_t off, struct fuse_file_info *fi) {
  211     (void) fi;
  212 
  213     if (ino != FUSE_ROOT_ID)
  214         fuse_reply_err(req, ENOTDIR);
  215     else {
  216         struct dirbuf b;
  217 
  218         memset(&b, 0, sizeof(b));
  219         dirbuf_add(req, &b, file_name, file_ino);
  220         reply_buf_limited(req, b.p, b.size, off, size);
  221         free(b.p);
  222     }
  223 }
  224 
  225 static struct fuse_lowlevel_ops tfs_oper = {
  226     .lookup = tfs_lookup,
  227     .getattr    = tfs_getattr,
  228     .readdir    = tfs_readdir,
  229     .forget     = tfs_forget,
  230 };
  231 
  232 static void update_fs(void) {
  233     time_t t;
  234     struct tm *now;
  235     ssize_t ret;
  236 
  237     t = time(NULL);
  238     now = localtime(&t);
  239     assert(now != NULL);
  240 
  241     ret = strftime(file_name, MAX_STR_LEN,
  242                    "Time_is_%Hh_%Mm_%Ss", now);
  243     assert(ret != 0);
  244 }
  245 
  246 static void* update_fs_loop(void *data) {
  247     struct fuse_session *se = (struct fuse_session*) data;
  248     char *old_name;
  249 
  250     while(1) {
  251         old_name = strdup(file_name);
  252         update_fs();
  253         if (!options.no_notify && lookup_cnt)
  254             assert(fuse_lowlevel_notify_inval_entry
  255                    (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
  256         free(old_name);
  257         sleep(options.update_interval);
  258     }
  259     return NULL;
  260 }
  261 
  262 static void show_help(const char *progname)
  263 {
  264     printf("usage: %s [options] <mountpoint>\n\n", progname);
  265     printf("File-system specific options:\n"
  266                "    --timeout=<secs>       Timeout for kernel caches\n"
  267                "    --update-interval=<secs>  Update-rate of file system contents\n"
  268                "    --no-notify            Disable kernel notifications\n"
  269                "\n");
  270 }
  271 
  272 int main(int argc, char *argv[]) {
  273     struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
  274     struct fuse_session *se;
  275     struct fuse_cmdline_opts opts;
  276     pthread_t updater;
  277     int ret = -1;
  278 
  279     if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
  280         return 1;
  281 
  282     if (fuse_parse_cmdline(&args, &opts) != 0)
  283         return 1;
  284     if (opts.show_help) {
  285         show_help(argv[0]);
  286         fuse_cmdline_help();
  287         fuse_lowlevel_help();
  288         ret = 0;
  289         goto err_out1;
  290     } else if (opts.show_version) {
  291         printf("FUSE library version %s\n", fuse_pkgversion());
  292         fuse_lowlevel_version();
  293         ret = 0;
  294         goto err_out1;
  295     }
  296 
  297     /* Initial contents */
  298     update_fs();
  299 
  300     se = fuse_session_new(&args, &tfs_oper,
  301                           sizeof(tfs_oper), NULL);
  302     if (se == NULL)
  303         goto err_out1;
  304 
  305     if (fuse_set_signal_handlers(se) != 0)
  306         goto err_out2;
  307 
  308     if (fuse_session_mount(se, opts.mountpoint) != 0)
  309         goto err_out3;
  310 
  311     fuse_daemonize(opts.foreground);
  312 
  313     /* Start thread to update file contents */
  314     ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
  315     if (ret != 0) {
  316         fprintf(stderr, "pthread_create failed with %s\n",
  317                 strerror(ret));
  318         goto err_out3;
  319     }
  320 
  321     /* Block until ctrl+c or fusermount -u */
  322     if (opts.singlethread)
  323         ret = fuse_session_loop(se);
  324     else
  325         ret = fuse_session_loop_mt(se, opts.clone_fd);
  326 
  327     fuse_session_unmount(se);
  328 err_out3:
  329     fuse_remove_signal_handlers(se);
  330 err_out2:
  331     fuse_session_destroy(se);
  332 err_out1:
  333     free(opts.mountpoint);
  334     fuse_opt_free_args(&args);
  335 
  336     return ret ? 1 : 0;
  337 }
  338 
  339 
  340 /**
  341  * Local Variables:
  342  * mode: c
  343  * indent-tabs-mode: nil
  344  * c-basic-offset: 4
  345  * End:
  346  */