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. */ 46int 47fuse_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 67free_args: 68 fuse_opt_free_args(&args); 69 return nominal_fd; 70} 71 72static bool 73is_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 79static bool 80is_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 */ 115void 116fuse_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 147struct fuse * 148fuse_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 158void 159fuse_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 178int 179fuse_loop_mt_v11(struct fuse *fuse) { 180 return __fuse_loop_mt(fuse, 0); 181} 182