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