Merge remote-tracking branch 'origin/vendor/LIBEDIT'
[dragonfly.git] / sys / vfs / fuse / fuse_node.c
1 /*-
2  * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2019 The DragonFly Project
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include "fuse.h"
29
30 static MALLOC_DEFINE(M_FUSE_NODE, "fuse_node", "FUSE node");
31
32 static struct objcache *fuse_node_objcache = NULL;
33 static struct objcache_malloc_args fuse_node_args = {
34         sizeof(struct fuse_node), M_FUSE_NODE,
35 };
36
37 static MALLOC_DEFINE(M_FUSE_DENT, "fuse_dent", "FUSE dent");
38
39 static struct objcache *fuse_dent_objcache = NULL;
40 static struct objcache_malloc_args fuse_dent_args = {
41         sizeof(struct fuse_dent), M_FUSE_DENT,
42 };
43
44 static int
45 fuse_dent_cmp(struct fuse_dent *p1, struct fuse_dent *p2)
46 {
47         return strcmp(p1->name, p2->name);
48 }
49
50 RB_PROTOTYPE_STATIC(fuse_dent_tree, fuse_dent, entry, fuse_dent_cmp);
51 RB_GENERATE_STATIC(fuse_dent_tree, fuse_dent, dent_entry, fuse_dent_cmp);
52
53 void
54 fuse_node_new(struct fuse_mount *fmp, uint64_t ino, enum vtype vtyp,
55     struct fuse_node **fnpp)
56 {
57         struct fuse_node *fnp;
58
59         fnp = objcache_get(fuse_node_objcache, M_WAITOK);
60         KKASSERT(fnp);
61
62         memset(fnp, 0, sizeof(*fnp));
63         fnp->vp = NULL;
64         fnp->fmp = fmp;
65         fnp->pfnp = NULL;
66
67         mtx_init(&fnp->node_lock, "fuse_node_lock");
68         RB_INIT(&fnp->dent_head);
69
70         fnp->ino = ino;
71         fnp->type = vtyp;
72         fnp->nlink = 0;
73         fnp->size = 0;
74         fnp->nlookup = 0;
75         fnp->fh = 0;
76         fnp->closed = false;
77
78         *fnpp = fnp;
79         KKASSERT(*fnpp);
80 }
81
82 void
83 fuse_node_free(struct fuse_node *fnp)
84 {
85         struct fuse_node *dfnp = fnp->pfnp;
86         struct fuse_dent *fep;
87
88         fuse_dbg("free ino=%ju\n", fnp->ino);
89
90         if (dfnp) {
91                 KKASSERT(dfnp->type == VDIR);
92                 mtx_lock(&dfnp->node_lock);
93                 RB_FOREACH(fep, fuse_dent_tree, &dfnp->dent_head) {
94                         if (fep->fnp == fnp) {
95                                 fuse_dent_detach(dfnp, fep);
96                                 fuse_dent_free(fep);
97                                 break;
98                         }
99                 }
100                 mtx_unlock(&dfnp->node_lock);
101         }
102
103         mtx_lock(&fnp->node_lock);
104         if (fnp->type == VDIR) {
105                 while ((fep = RB_ROOT(&fnp->dent_head))) {
106                         fuse_dent_detach(fnp, fep);
107                         fuse_dent_free(fep);
108                 }
109         }
110         fnp->vp->v_data = NULL;
111         fnp->vp = NULL;
112         fnp->nlink = -123; /* debug */
113         mtx_unlock(&fnp->node_lock);
114
115         objcache_put(fuse_node_objcache, fnp);
116 }
117
118 void
119 fuse_dent_new(struct fuse_node *fnp, const char *name, int namelen,
120     struct fuse_dent **fepp)
121 {
122         struct fuse_dent *fep;
123
124         fep = objcache_get(fuse_dent_objcache, M_WAITOK);
125         KKASSERT(fep);
126
127         if (namelen >= 0)
128                 fep->name = kstrndup(name, namelen, M_TEMP);
129         else
130                 fep->name = kstrdup(name, M_TEMP);
131         KKASSERT(fep->name);
132         fep->fnp = fnp;
133
134         KASSERT(fnp->nlink >= 0, ("new ino=%ju nlink=%d dent=\"%s\"",
135             fnp->ino, fnp->nlink, fep->name));
136         KKASSERT(fnp->nlink < LINK_MAX);
137         fnp->nlink++;
138
139         *fepp = fep;
140         KKASSERT(*fepp);
141 }
142
143 void
144 fuse_dent_free(struct fuse_dent *fep)
145 {
146         struct fuse_node *fnp = fep->fnp;
147
148         fuse_dbg("free dent=\"%s\"\n", fep->name);
149
150         KASSERT(fnp->nlink > 0, ("free ino=%ju nlink=%d dent=\"%s\"",
151             fnp->ino, fnp->nlink, fep->name));
152
153         if (fep->name) {
154                 kfree(fep->name, M_TEMP);
155                 fep->name = NULL;
156         }
157
158         KKASSERT(fnp->nlink <= LINK_MAX);
159         fnp->nlink--;
160
161         fep->fnp = NULL;
162         objcache_put(fuse_dent_objcache, fep);
163 }
164
165 void
166 fuse_dent_attach(struct fuse_node *dfnp, struct fuse_dent *fep)
167 {
168         KKASSERT(dfnp);
169         KKASSERT(dfnp->type == VDIR);
170         KKASSERT(mtx_islocked_ex(&dfnp->node_lock));
171
172         RB_INSERT(fuse_dent_tree, &dfnp->dent_head, fep);
173 }
174
175 void
176 fuse_dent_detach(struct fuse_node *dfnp, struct fuse_dent *fep)
177 {
178         KKASSERT(dfnp);
179         KKASSERT(dfnp->type == VDIR);
180         KKASSERT(mtx_islocked_ex(&dfnp->node_lock));
181
182         RB_REMOVE(fuse_dent_tree, &dfnp->dent_head, fep);
183 }
184
185 int
186 fuse_dent_find(struct fuse_node *dfnp, const char *name, int namelen,
187     struct fuse_dent **fepp)
188 {
189         struct fuse_dent *fep, find;
190         int error;
191
192         if (namelen >= 0)
193                 find.name = kstrndup(name, namelen, M_TEMP);
194         else
195                 find.name = kstrdup(name, M_TEMP);
196         KKASSERT(find.name);
197
198         fep = RB_FIND(fuse_dent_tree, &dfnp->dent_head, &find);
199         if (fep) {
200                 error = 0;
201                 if (fepp)
202                         *fepp = fep;
203         } else {
204                 error = ENOENT;
205                 fuse_dbg("dent=\"%s\" not found\n", find.name);
206         }
207
208         kfree(find.name, M_TEMP);
209
210         return error;
211 }
212
213 int
214 fuse_alloc_node(struct fuse_node *dfnp, uint64_t ino, const char *name,
215     int namelen, enum vtype vtyp, struct vnode **vpp)
216 {
217         struct fuse_node *fnp = NULL;
218         struct fuse_dent *fep = NULL;
219         int error;
220
221         KKASSERT(dfnp->type == VDIR);
222         if (vtyp == VBLK || vtyp == VCHR || vtyp == VFIFO)
223                 return EINVAL;
224
225         mtx_lock(&dfnp->node_lock);
226         error = fuse_dent_find(dfnp, name, namelen, &fep);
227         if (!error) {
228                 mtx_unlock(&dfnp->node_lock);
229                 return EEXIST;
230         } else if (error == ENOENT) {
231                 fuse_node_new(dfnp->fmp, ino, vtyp, &fnp);
232                 mtx_lock(&fnp->node_lock);
233                 fnp->pfnp = dfnp;
234                 fuse_dent_new(fnp, name, namelen, &fep);
235                 fuse_dent_attach(dfnp, fep);
236                 mtx_unlock(&fnp->node_lock);
237         } else
238                 KKASSERT(0);
239         mtx_unlock(&dfnp->node_lock);
240
241         error = fuse_node_vn(fnp, LK_EXCLUSIVE, vpp);
242         if (error) {
243                 mtx_lock(&dfnp->node_lock);
244                 fuse_dent_detach(dfnp, fep);
245                 fuse_dent_free(fep);
246                 mtx_unlock(&dfnp->node_lock);
247                 fuse_node_free(fnp);
248                 return error;
249         }
250         KKASSERT(*vpp);
251
252         fuse_dbg("fnp=%p ino=%ju dent=\"%s\"\n", fnp, fnp->ino, fep->name);
253
254         return 0;
255 }
256
257 int
258 fuse_node_vn(struct fuse_node *fnp, int flags, struct vnode **vpp)
259 {
260         struct mount *mp = fnp->fmp->mp;
261         struct vnode *vp;
262         int error;
263 retry:
264         mtx_lock(&fnp->node_lock);
265         vp = fnp->vp;
266         if (vp) {
267                 vhold(vp);
268                 mtx_unlock(&fnp->node_lock);
269
270                 error = vget(vp, flags | LK_RETRY);
271                 if (error) {
272                         vdrop(vp);
273                         goto retry;
274                 }
275                 vdrop(vp);
276                 *vpp = vp;
277                 return 0;
278         }
279         mtx_unlock(&fnp->node_lock);
280
281         error = getnewvnode(VT_FUSE, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
282         if (error)
283                 return error;
284         vp->v_type = fnp->type;
285         vp->v_data = fnp;
286
287         switch (vp->v_type) {
288         case VREG:
289                 vinitvmio(vp, fnp->size, FUSE_BLKSIZE, -1);
290                 break;
291         case VDIR:
292                 break;
293         case VBLK:
294         case VCHR:
295                 KKASSERT(0);
296                 vp->v_ops = &mp->mnt_vn_spec_ops;
297                 addaliasu(vp, umajor(0), uminor(0)); /* XXX CUSE */
298                 break;
299         case VLNK:
300                 break;
301         case VSOCK:
302                 break;
303         case VFIFO:
304                 KKASSERT(0);
305         case VDATABASE:
306                 break;
307         default:
308                 KKASSERT(0);
309         }
310
311         KKASSERT(vn_islocked(vp) == LK_EXCLUSIVE);
312         KASSERT(!fnp->vp, ("lost race"));
313         fnp->vp = vp;
314         *vpp = vp;
315
316         return 0;
317 }
318
319 int
320 fuse_node_truncate(struct fuse_node *fnp, size_t oldsize, size_t newsize)
321 {
322         struct vnode *vp = fnp->vp;
323         int error;
324
325         fuse_dbg("ino=%ju update size %ju -> %ju\n",
326             fnp->ino, oldsize, newsize);
327
328         fnp->attr.va_size = fnp->size = newsize;
329
330         if (newsize < oldsize)
331                 error = nvtruncbuf(vp, newsize, FUSE_BLKSIZE, -1, 0);
332         else
333                 error = nvextendbuf(vp, oldsize, newsize, FUSE_BLKSIZE,
334                     FUSE_BLKSIZE, -1, -1, 0);
335         return error;
336 }
337
338 void
339 fuse_node_init(void)
340 {
341         fuse_node_objcache = objcache_create("fuse_node", 0, 0,
342             NULL, NULL, NULL,
343             objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_node_args);
344
345         fuse_dent_objcache = objcache_create("fuse_dent", 0, 0,
346             NULL, NULL, NULL,
347             objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_dent_args);
348 }
349
350 void
351 fuse_node_cleanup(void)
352 {
353         objcache_destroy(fuse_node_objcache);
354         objcache_destroy(fuse_dent_objcache);
355 }