"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.3/example/passthrough_ll.c" (11 May 2018, 14060 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 latest Fossies "Diffs" side-by-side code changes report for "passthrough_ll.c": 3.2.2_vs_3.2.3.

    1 /*
    2   FUSE: Filesystem in Userspace
    3   Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
    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 file system mirrors the existing file system hierarchy of the
   12  * system, starting at the root file system. This is implemented by
   13  * just "passing through" all requests to the corresponding user-space
   14  * libc functions. In contrast to passthrough.c and passthrough_fh.c,
   15  * this implementation uses the low-level API. Its performance should
   16  * be the least bad among the three, but many operations are not
   17  * implemented. In particular, it is not possible to remove files (or
   18  * directories) because the code necessary to defer actual removal
   19  * until the file is not opened anymore would make the example much
   20  * more complicated.
   21  *
   22  * When writeback caching is enabled (-o writeback mount option), it
   23  * is only possible to write to files for which the mounting user has
   24  * read permissions. This is because the writeback cache requires the
   25  * kernel to be able to issue read requests for all files (which the
   26  * passthrough filesystem cannot satisfy if it can't read the file in
   27  * the underlying filesystem).
   28  *
   29  * Compile with:
   30  *
   31  *     gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll
   32  *
   33  * ## Source code ##
   34  * \include passthrough_ll.c
   35  */
   36 
   37 #define _GNU_SOURCE
   38 #define FUSE_USE_VERSION 31
   39 
   40 #include <fuse_lowlevel.h>
   41 #include <unistd.h>
   42 #include <stdlib.h>
   43 #include <stdio.h>
   44 #include <stddef.h>
   45 #include <stdbool.h>
   46 #include <string.h>
   47 #include <limits.h>
   48 #include <dirent.h>
   49 #include <assert.h>
   50 #include <errno.h>
   51 #include <err.h>
   52 #include <inttypes.h>
   53 
   54 /* We are re-using pointers to our `struct lo_inode` and `struct
   55    lo_dirp` elements as inodes. This means that we must be able to
   56    store uintptr_t values in a fuse_ino_t variable. The following
   57    incantation checks this condition at compile time. */
   58 #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
   59 _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
   60            "fuse_ino_t too small to hold uintptr_t values!");
   61 #else
   62 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
   63     { unsigned _uintptr_to_must_hold_fuse_ino_t:
   64             ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
   65 #endif
   66 
   67 struct lo_inode {
   68     struct lo_inode *next;
   69     struct lo_inode *prev;
   70     int fd;
   71     ino_t ino;
   72     dev_t dev;
   73     uint64_t nlookup;
   74 };
   75 
   76 struct lo_data {
   77     int debug;
   78     int writeback;
   79     struct lo_inode root;
   80 };
   81 
   82 static const struct fuse_opt lo_opts[] = {
   83     { "writeback",
   84       offsetof(struct lo_data, writeback), 1 },
   85     { "no_writeback",
   86       offsetof(struct lo_data, writeback), 0 },
   87     FUSE_OPT_END
   88 };
   89 
   90 static struct lo_data *lo_data(fuse_req_t req)
   91 {
   92     return (struct lo_data *) fuse_req_userdata(req);
   93 }
   94 
   95 static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
   96 {
   97     if (ino == FUSE_ROOT_ID)
   98         return &lo_data(req)->root;
   99     else
  100         return (struct lo_inode *) (uintptr_t) ino;
  101 }
  102 
  103 static int lo_fd(fuse_req_t req, fuse_ino_t ino)
  104 {
  105     return lo_inode(req, ino)->fd;
  106 }
  107 
  108 static bool lo_debug(fuse_req_t req)
  109 {
  110     return lo_data(req)->debug != 0;
  111 }
  112 
  113 static void lo_init(void *userdata,
  114             struct fuse_conn_info *conn)
  115 {
  116     struct lo_data *lo = (struct lo_data*) userdata;
  117 
  118     if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
  119         conn->want |= FUSE_CAP_EXPORT_SUPPORT;
  120 
  121     if (lo->writeback &&
  122         conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
  123         if (lo->debug)
  124             fprintf(stderr, "lo_init: activating writeback\n");
  125         conn->want |= FUSE_CAP_WRITEBACK_CACHE;
  126     }
  127 }
  128 
  129 static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
  130                  struct fuse_file_info *fi)
  131 {
  132     int res;
  133     struct stat buf;
  134     (void) fi;
  135 
  136     res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
  137     if (res == -1)
  138         return (void) fuse_reply_err(req, errno);
  139 
  140     fuse_reply_attr(req, &buf, 1.0);
  141 }
  142 
  143 static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
  144 {
  145     struct lo_inode *p;
  146 
  147     for (p = lo->root.next; p != &lo->root; p = p->next) {
  148         if (p->ino == st->st_ino && p->dev == st->st_dev)
  149             return p;
  150     }
  151     return NULL;
  152 }
  153 
  154 static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
  155              struct fuse_entry_param *e)
  156 {
  157     int newfd;
  158     int res;
  159     int saverr;
  160     struct lo_inode *inode;
  161 
  162     memset(e, 0, sizeof(*e));
  163     e->attr_timeout = 1.0;
  164     e->entry_timeout = 1.0;
  165 
  166     newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
  167     if (newfd == -1)
  168         goto out_err;
  169 
  170     res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
  171     if (res == -1)
  172         goto out_err;
  173 
  174     inode = lo_find(lo_data(req), &e->attr);
  175     if (inode) {
  176         close(newfd);
  177         newfd = -1;
  178     } else {
  179         struct lo_inode *prev = &lo_data(req)->root;
  180         struct lo_inode *next = prev->next;
  181         saverr = ENOMEM;
  182         inode = calloc(1, sizeof(struct lo_inode));
  183         if (!inode)
  184             goto out_err;
  185 
  186         inode->fd = newfd;
  187         inode->ino = e->attr.st_ino;
  188         inode->dev = e->attr.st_dev;
  189 
  190         next->prev = inode;
  191         inode->next = next;
  192         inode->prev = prev;
  193         prev->next = inode;
  194     }
  195     inode->nlookup++;
  196     e->ino = (uintptr_t) inode;
  197 
  198     if (lo_debug(req))
  199         fprintf(stderr, "  %lli/%s -> %lli\n",
  200             (unsigned long long) parent, name, (unsigned long long) e->ino);
  201 
  202     return 0;
  203 
  204 out_err:
  205     saverr = errno;
  206     if (newfd != -1)
  207         close(newfd);
  208     return saverr;
  209 }
  210 
  211 static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
  212 {
  213     struct fuse_entry_param e;
  214     int err;
  215 
  216     if (lo_debug(req))
  217         fprintf(stderr, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
  218             parent, name);
  219     
  220     err = lo_do_lookup(req, parent, name, &e);
  221     if (err)
  222         fuse_reply_err(req, err);
  223     else
  224         fuse_reply_entry(req, &e);
  225 }
  226 
  227 static void lo_free(struct lo_inode *inode)
  228 {
  229     struct lo_inode *prev = inode->prev;
  230     struct lo_inode *next = inode->next;
  231 
  232     next->prev = prev;
  233     prev->next = next;
  234     close(inode->fd);
  235     free(inode);
  236 }
  237 
  238 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
  239 {
  240     struct lo_inode *inode = lo_inode(req, ino);
  241 
  242     if (lo_debug(req)) {
  243         fprintf(stderr, "  forget %lli %lli -%lli\n",
  244             (unsigned long long) ino, (unsigned long long) inode->nlookup,
  245             (unsigned long long) nlookup);
  246     }
  247 
  248     assert(inode->nlookup >= nlookup);
  249     inode->nlookup -= nlookup;
  250 
  251     if (!inode->nlookup)
  252         lo_free(inode);
  253 
  254     fuse_reply_none(req);
  255 }
  256 
  257 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
  258 {
  259     char buf[PATH_MAX + 1];
  260     int res;
  261 
  262     res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
  263     if (res == -1)
  264         return (void) fuse_reply_err(req, errno);
  265 
  266     if (res == sizeof(buf))
  267         return (void) fuse_reply_err(req, ENAMETOOLONG);
  268 
  269     buf[res] = '\0';
  270 
  271     fuse_reply_readlink(req, buf);
  272 }
  273 
  274 struct lo_dirp {
  275     int fd;
  276     DIR *dp;
  277     struct dirent *entry;
  278     off_t offset;
  279 };
  280 
  281 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
  282 {
  283     return (struct lo_dirp *) (uintptr_t) fi->fh;
  284 }
  285 
  286 static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
  287 {
  288     int error = ENOMEM;
  289     struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp));
  290     if (d == NULL)
  291         goto out_err;
  292 
  293     d->fd = openat(lo_fd(req, ino), ".", O_RDONLY);
  294     if (d->fd == -1)
  295         goto out_errno;
  296 
  297     d->dp = fdopendir(d->fd);
  298     if (d->dp == NULL)
  299         goto out_errno;
  300 
  301     d->offset = 0;
  302     d->entry = NULL;
  303 
  304     fi->fh = (uintptr_t) d;
  305     fuse_reply_open(req, fi);
  306     return;
  307 
  308 out_errno:
  309     error = errno;
  310 out_err:
  311     if (d) {
  312         if (d->fd != -1)
  313             close(d->fd);
  314         free(d);
  315     }
  316     fuse_reply_err(req, error);
  317 }
  318 
  319 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
  320               off_t offset, struct fuse_file_info *fi, int plus)
  321 {
  322     struct lo_dirp *d = lo_dirp(fi);
  323     char *buf;
  324     char *p;
  325     size_t rem;
  326     int err;
  327 
  328     (void) ino;
  329 
  330     buf = calloc(1, size);
  331     if (!buf)
  332         return (void) fuse_reply_err(req, ENOMEM);
  333 
  334     if (offset != d->offset) {
  335         seekdir(d->dp, offset);
  336         d->entry = NULL;
  337         d->offset = offset;
  338     }
  339     p = buf;
  340     rem = size;
  341     while (1) {
  342         size_t entsize;
  343         off_t nextoff;
  344 
  345         if (!d->entry) {
  346             errno = 0;
  347             d->entry = readdir(d->dp);
  348             if (!d->entry) {
  349                 if (errno && rem == size) {
  350                     err = errno;
  351                     goto error;
  352                 }
  353                 break;
  354             }
  355         }
  356         nextoff = telldir(d->dp);
  357         if (plus) {
  358             struct fuse_entry_param e;
  359 
  360             err = lo_do_lookup(req, ino, d->entry->d_name, &e);
  361             if (err)
  362                 goto error;
  363 
  364             entsize = fuse_add_direntry_plus(req, p, rem,
  365                              d->entry->d_name,
  366                              &e, nextoff);
  367         } else {
  368             struct stat st = {
  369                 .st_ino = d->entry->d_ino,
  370                 .st_mode = d->entry->d_type << 12,
  371             };
  372             entsize = fuse_add_direntry(req, p, rem,
  373                             d->entry->d_name,
  374                             &st, nextoff);
  375         }
  376         if (entsize > rem)
  377             break;
  378 
  379         p += entsize;
  380         rem -= entsize;
  381 
  382         d->entry = NULL;
  383         d->offset = nextoff;
  384     }
  385 
  386     fuse_reply_buf(req, buf, size - rem);
  387     free(buf);
  388     return;
  389 
  390 error:
  391     free(buf);
  392     fuse_reply_err(req, err);
  393 }
  394 
  395 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
  396                off_t offset, struct fuse_file_info *fi)
  397 {
  398     lo_do_readdir(req, ino, size, offset, fi, 0);
  399 }
  400 
  401 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
  402                off_t offset, struct fuse_file_info *fi)
  403 {
  404     lo_do_readdir(req, ino, size, offset, fi, 1);
  405 }
  406 
  407 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
  408 {
  409     struct lo_dirp *d = lo_dirp(fi);
  410     (void) ino;
  411     closedir(d->dp);
  412     free(d);
  413     fuse_reply_err(req, 0);
  414 }
  415 
  416 static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
  417               mode_t mode, struct fuse_file_info *fi)
  418 {
  419     int fd;
  420     struct fuse_entry_param e;
  421     int err;
  422 
  423     if (lo_debug(req))
  424         fprintf(stderr, "lo_create(parent=%" PRIu64 ", name=%s)\n",
  425             parent, name);
  426             
  427     fd = openat(lo_fd(req, parent), name,
  428             (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
  429     if (fd == -1)
  430         return (void) fuse_reply_err(req, errno);
  431 
  432     fi->fh = fd;
  433 
  434     err = lo_do_lookup(req, parent, name, &e);
  435     if (err)
  436         fuse_reply_err(req, err);
  437     else
  438         fuse_reply_create(req, &e, fi);
  439 }
  440 
  441 static void lo_open(fuse_req_t req, fuse_ino_t ino,
  442             struct fuse_file_info *fi)
  443 {
  444     int fd;
  445     char buf[64];
  446 
  447     if (lo_debug(req))
  448         fprintf(stderr, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
  449             ino, fi->flags);
  450 
  451     /* With writeback cache, kernel may send read requests even
  452        when userspace opened write-only */
  453     if (lo_data(req)->writeback &&
  454         (fi->flags & O_ACCMODE) == O_WRONLY) {
  455         fi->flags &= ~O_ACCMODE;
  456         fi->flags |= O_RDWR;
  457     }
  458 
  459     /* With writeback cache, O_APPEND is handled by the kernel.
  460        This breaks atomicity (since the file may change in the
  461        underlying filesystem, so that the kernel's idea of the
  462        end of the file isn't accurate anymore). In this example,
  463        we just accept that. A more rigorous filesystem may want
  464        to return an error here */
  465     if (lo_data(req)->writeback && (fi->flags & O_APPEND))
  466         fi->flags &= ~O_APPEND;
  467 
  468     sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
  469     fd = open(buf, fi->flags & ~O_NOFOLLOW);
  470     if (fd == -1)
  471         return (void) fuse_reply_err(req, errno);
  472 
  473     fi->fh = fd;
  474     fuse_reply_open(req, fi);
  475 }
  476 
  477 static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
  478 {
  479     (void) ino;
  480 
  481     close(fi->fh);
  482     fuse_reply_err(req, 0);
  483 }
  484 
  485 static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
  486             off_t offset, struct fuse_file_info *fi)
  487 {
  488     struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
  489 
  490     if (lo_debug(req))
  491         fprintf(stderr, "lo_read(ino=%" PRIu64 ", size=%zd, "
  492             "off=%lu)\n", ino, size, (unsigned long) offset);
  493 
  494     buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
  495     buf.buf[0].fd = fi->fh;
  496     buf.buf[0].pos = offset;
  497 
  498     fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
  499 }
  500 
  501 static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
  502              struct fuse_bufvec *in_buf, off_t off,
  503              struct fuse_file_info *fi)
  504 {
  505     (void) ino;
  506     ssize_t res;
  507     struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
  508 
  509     out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
  510     out_buf.buf[0].fd = fi->fh;
  511     out_buf.buf[0].pos = off;
  512 
  513     if (lo_debug(req))
  514         fprintf(stderr, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
  515             ino, out_buf.buf[0].size, (unsigned long) off);
  516     
  517     res = fuse_buf_copy(&out_buf, in_buf, 0);
  518     if(res < 0)
  519         fuse_reply_err(req, -res);
  520     else
  521         fuse_reply_write(req, (size_t) res);
  522 }
  523 
  524 static struct fuse_lowlevel_ops lo_oper = {
  525     .init       = lo_init,
  526     .lookup     = lo_lookup,
  527     .forget     = lo_forget,
  528     .getattr    = lo_getattr,
  529     .readlink   = lo_readlink,
  530     .opendir    = lo_opendir,
  531     .readdir    = lo_readdir,
  532     .readdirplus    = lo_readdirplus,
  533     .releasedir = lo_releasedir,
  534     .create     = lo_create,
  535     .open       = lo_open,
  536     .release    = lo_release,
  537     .read       = lo_read,
  538     .write_buf      = lo_write_buf
  539 };
  540 
  541 int main(int argc, char *argv[])
  542 {
  543     struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
  544     struct fuse_session *se;
  545     struct fuse_cmdline_opts opts;
  546     struct lo_data lo = { .debug = 0,
  547                           .writeback = 0 };
  548     int ret = -1;
  549 
  550     lo.root.next = lo.root.prev = &lo.root;
  551     lo.root.fd = -1;
  552 
  553     if (fuse_parse_cmdline(&args, &opts) != 0)
  554         return 1;
  555     if (opts.show_help) {
  556         printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
  557         fuse_cmdline_help();
  558         fuse_lowlevel_help();
  559         ret = 0;
  560         goto err_out1;
  561     } else if (opts.show_version) {
  562         printf("FUSE library version %s\n", fuse_pkgversion());
  563         fuse_lowlevel_version();
  564         ret = 0;
  565         goto err_out1;
  566     }
  567 
  568     if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
  569         return 1;
  570     
  571     lo.debug = opts.debug;
  572     lo.root.fd = open("/", O_PATH);
  573     lo.root.nlookup = 2;
  574     if (lo.root.fd == -1)
  575         err(1, "open(\"/\", O_PATH)");
  576 
  577     se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
  578     if (se == NULL)
  579         goto err_out1;
  580 
  581     if (fuse_set_signal_handlers(se) != 0)
  582         goto err_out2;
  583 
  584     if (fuse_session_mount(se, opts.mountpoint) != 0)
  585         goto err_out3;
  586 
  587     fuse_daemonize(opts.foreground);
  588 
  589     /* Block until ctrl+c or fusermount -u */
  590     if (opts.singlethread)
  591         ret = fuse_session_loop(se);
  592     else
  593         ret = fuse_session_loop_mt(se, opts.clone_fd);
  594 
  595     fuse_session_unmount(se);
  596 err_out3:
  597     fuse_remove_signal_handlers(se);
  598 err_out2:
  599     fuse_session_destroy(se);
  600 err_out1:
  601     free(opts.mountpoint);
  602     fuse_opt_free_args(&args);
  603 
  604     while (lo.root.next != &lo.root)
  605         lo_free(lo.root.next);
  606     if (lo.root.fd >= 0)
  607         close(lo.root.fd);
  608 
  609     return ret ? 1 : 0;
  610 }