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
fuse_mount_v11(const char * mountpoint,const char * argv[])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
is_same_mountpoint(struct fuse_chan * chan,void * priv)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
is_same_fuse(struct fuse_chan * chan,void * priv)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
fuse_unmount_v11(const char * mountpoint)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 *
fuse_new_v11(int fd,int flags,const void * op,int op_version)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
fuse_destroy_v11(struct fuse * fuse)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
fuse_loop_mt_v11(struct fuse * fuse)179 fuse_loop_mt_v11(struct fuse *fuse) {
180 return __fuse_loop_mt(fuse, 0);
181 }
182