Home | History | Annotate | Line # | Download | only in refuse
      1 /* $NetBSD: v11.c,v 1.1 2022/01/22 08:09:40 pho Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote
     16  *    products derived from this software without specific prior written
     17  *    permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     25  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #if !defined(lint)
     34 __RCSID("$NetBSD: v11.c,v 1.1 2022/01/22 08:09:40 pho Exp $");
     35 #endif /* !lint */
     36 
     37 #include <err.h>
     38 #include <fuse_internal.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 
     42 /* FUSE < 3.0 had a very strange interface. Filesystems were supposed
     43  * to be mounted first, before creating an instance of struct
     44  * fuse. They revised the interface SO MANY TIMES but the fundamental
     45  * weirdness stayed the same. */
     46 int
     47 fuse_mount_v11(const char *mountpoint, const char *argv[]) {
     48     struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
     49     int nominal_fd = -1;
     50 
     51     /* The argv is supposed to be a NULL-terminated array of
     52      * additional arguments to fusermount(8), and should not have a
     53      * program name at argv[0]. Our __fuse_new() expects one. So
     54      * prepend a dummy name. */
     55     if (fuse_opt_add_arg(&args, "dummy") != 0)
     56         goto free_args;
     57 
     58     if (argv) {
     59         for (size_t i = 0; argv[i] != NULL; i++) {
     60             if (fuse_opt_add_arg(&args, argv[i]) != 0)
     61                 goto free_args;
     62         }
     63     }
     64 
     65     nominal_fd = fuse_mount_v25(mountpoint, &args);
     66 
     67 free_args:
     68     fuse_opt_free_args(&args);
     69     return nominal_fd;
     70 }
     71 
     72 static bool
     73 is_same_mountpoint(struct fuse_chan* chan, void* priv) {
     74     const char* mountpoint = priv;
     75 
     76     return strcmp(fuse_chan_mountpoint(chan), mountpoint) == 0;
     77 }
     78 
     79 static bool
     80 is_same_fuse(struct fuse_chan* chan, void* priv) {
     81     struct fuse* fuse = priv;
     82 
     83     return fuse_chan_fuse(chan) == fuse;
     84 }
     85 
     86 /* FUSE < 3.0 didn't require filesystems to call fuse_unmount()
     87  * before fuse_destroy(). That is, it was completely legal to call
     88  * fuse_unmount() *after* fuse_destroy(), and it was even legal to
     89  * call fuse_mount() and then fuse_unmount() without calling
     90  * fuse_new() in the first place. On the other hand, our libpuffs
     91  * (like FUSE 3.0) wants a context in order to unmount a
     92  * filesystem. So, we have to do a workaround as follows:
     93  *
     94  * 1. fuse_mount() creates a struct fuse_chan and stashes it in a
     95  *    global channel list, but without actually mounting a filesystem.
     96  *
     97  * 2. fuse_new() fetches the stashed fuse_chan and creates a fuse
     98  *    object out of it, then mounts a filesystem. The fuse object is
     99  *    also stored in fuse_chan.
    100  *
    101  * 3. When fuse_destroy() is called without first unmounting the
    102  *    filesystem, it doesn't actually destroy the fuse object but it
    103  *    merely schedules it for destruction.
    104  *
    105  * 4. fuse_unmount() searches for the corresponding fuse_chan in the
    106  *    global list. If it's scheduled for destruction, destroy the fuse
    107  *    object after unmounting the filesystem. It then removes and
    108  *    deallocates the fuse_chan from the list.
    109  *
    110  * Note that there will be a space leak if a user calls fuse_destroy()
    111  * but never calls fuse_unmount(). The fuse_chan will forever be in
    112  * the global list in this case. There's nothing we can do about it,
    113  * and users aren't supposed to do it after all.
    114  */
    115 void
    116 fuse_unmount_v11(const char *mountpoint) {
    117     int idx;
    118     struct fuse_chan* chan;
    119     struct fuse* fuse;
    120 
    121     /* Search for the fuse_chan having the given mountpoint. It must
    122      * be in the global list. */
    123     chan = fuse_chan_find(is_same_mountpoint, &idx, __UNCONST(mountpoint));
    124     if (!chan)
    125         errx(EXIT_FAILURE, "%s: cannot find a channel for the mountpoint: %s",
    126              __func__, mountpoint);
    127 
    128     fuse = fuse_chan_fuse(chan);
    129     if (fuse) {
    130         /* The user did call fuse_new() after fuse_mount(). */
    131         fuse_unmount_v30(fuse);
    132     }
    133 
    134     if (fuse_chan_is_to_be_destroyed(chan)) {
    135         /* The user called fuse_destroy() before
    136          * fuse_unmount(). Destroy it now. */
    137         fuse_destroy_v30(fuse);
    138     }
    139 
    140     /* Remove the channel from the global list so that fuse_destroy(),
    141      * if it's called after this, can know that it's already been
    142      * unmounted. */
    143     fuse_chan_take(idx);
    144     fuse_chan_destroy(chan);
    145 }
    146 
    147 struct fuse *
    148 fuse_new_v11(int fd, int flags, const void *op, int op_version) {
    149     const char *opts = NULL;
    150 
    151     /* FUSE_DEBUG was the only option allowed in this era. */
    152     if (flags & FUSE_DEBUG)
    153         opts = "debug";
    154 
    155     return fuse_new_v21(fd, opts, op, op_version, NULL);
    156 }
    157 
    158 void
    159 fuse_destroy_v11(struct fuse *fuse) {
    160     struct fuse_chan* chan;
    161 
    162     /* Search for the fuse_chan that was used while creating this
    163      * struct fuse*. If it's not there it means the filesystem was
    164      * first unmounted before destruction. */
    165     chan = fuse_chan_find(is_same_fuse, NULL, fuse);
    166     if (chan) {
    167         /* The filesystem is still mounted and the user may later call
    168          * fuse_unmount() on it. Can't destroy the fuse object atm. */
    169         fuse_chan_set_to_be_destroyed(chan, true);
    170     }
    171     else {
    172         /* It's already been unmounted. Safe to destroy the fuse
    173          * object right now. */
    174         fuse_destroy_v30(fuse);
    175     }
    176 }
    177 
    178 int
    179 fuse_loop_mt_v11(struct fuse *fuse) {
    180     return __fuse_loop_mt(fuse, 0);
    181 }
    182