ops.c revision 1.4 1 /* $NetBSD: ops.c,v 1.4 2010/08/28 03:46:21 manu Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Emmanuel Dreyfus. 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <libgen.h>
32 #include <errno.h>
33 #include <err.h>
34 #include <sysexits.h>
35 #include <syslog.h>
36 #include <puffs.h>
37 #include <sys/vnode.h>
38 #include <sys/socket.h>
39 #include <machine/vmparam.h>
40
41 #include "perfuse_priv.h"
42 #include "fuse.h"
43
44 static int no_access(puffs_cookie_t, const struct puffs_cred *, mode_t);
45 static void fuse_attr_to_vap(struct perfuse_state *,
46 struct vattr *, struct fuse_attr *);
47 static int node_lookup_dir_nodot(struct puffs_usermount *,
48 puffs_cookie_t, char *, size_t, struct puffs_node **);
49 static int node_lookup_common(struct puffs_usermount *, puffs_cookie_t,
50 const char*, struct puffs_node **);
51 static int node_mk_common(struct puffs_usermount *, puffs_cookie_t,
52 struct puffs_newinfo *, const struct puffs_cn *pcn,
53 const struct vattr *, perfuse_msg_t *);
54 static const char *basename_r(const char *);
55 static ssize_t fuse_to_dirent(struct puffs_usermount *, puffs_cookie_t,
56 struct fuse_dirent *, size_t);
57 static int readdir_buffered(struct perfuse_state *, puffs_cookie_t,
58 struct dirent *, off_t *, size_t *, const struct puffs_cred *,
59 int *, off_t *, size_t *);
60 static void requeue_request(struct puffs_usermount *,
61 puffs_cookie_t opc, enum perfuse_qtype);
62 static void dequeue_requests(struct perfuse_state *,
63 puffs_cookie_t opc, enum perfuse_qtype, int);
64 #define DEQUEUE_ALL 0
65
66 /*
67 * From <sys/vnode>, inside #ifdef _KERNEL section
68 */
69 #define IO_SYNC (0x40|IO_DSYNC)
70 #define IO_DSYNC 0x00200
71 #define IO_DIRECT 0x02000
72
73 /*
74 * From <fcntl>, inside #ifdef _KERNEL section
75 */
76 #define F_WAIT 0x010
77 #define F_FLOCK 0x020
78
79 /*
80 * Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h
81 */
82 const enum vtype iftovt_tab[16] = {
83 VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
84 VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
85 };
86 const int vttoif_tab[9] = {
87 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
88 S_IFSOCK, S_IFIFO, S_IFMT,
89 };
90
91 #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
92 #define VTTOIF(indx) (vttoif_tab[(int)(indx)])
93
94
95 static int
96 no_access(opc, pcr, mode)
97 puffs_cookie_t opc;
98 const struct puffs_cred *pcr;
99 mode_t mode;
100 {
101 struct puffs_node *pn;
102 struct vattr *va;
103
104 pn = (struct puffs_node *)opc;
105 va = puffs_pn_getvap(pn);
106
107 return puffs_access(va->va_type, va->va_mode,
108 va->va_uid, va->va_gid,
109 mode, pcr);
110 }
111
112 static void
113 fuse_attr_to_vap(ps, vap, fa)
114 struct perfuse_state *ps;
115 struct vattr *vap;
116 struct fuse_attr *fa;
117 {
118 vap->va_type = IFTOVT(fa->mode);
119 vap->va_mode = fa->mode;
120 vap->va_nlink = fa->nlink;
121 vap->va_uid = fa->uid;
122 vap->va_gid = fa->gid;
123 vap->va_fsid = ps->ps_fsid;
124 vap->va_fileid = fa->ino;
125 vap->va_size = fa->size;
126 vap->va_blocksize = fa->blksize;
127 vap->va_atime.tv_sec = (long)fa->atime;
128 vap->va_atime.tv_nsec = fa->atimensec;
129 vap->va_mtime.tv_sec = (long)fa->mtime;
130 vap->va_mtime.tv_nsec = fa->mtimensec;
131 vap->va_ctime.tv_sec = (long)fa->ctime;
132 vap->va_ctime.tv_nsec = fa->ctimensec;
133 vap->va_birthtime.tv_sec = 0;
134 vap->va_birthtime.tv_nsec = 0;
135 vap->va_gen = 0;
136 vap->va_flags = 0;
137 vap->va_rdev = fa->rdev;
138 vap->va_bytes = fa->size;
139 vap->va_filerev = 0;
140 vap->va_vaflags = 0;
141
142 if (vap->va_blocksize == 0)
143 vap->va_blocksize = DEV_BSIZE;
144
145 if (vap->va_size == (size_t)-1) /* XXX */
146 vap->va_size = 0;
147
148 return;
149 }
150
151
152 /*
153 * Lookup name in directory opc
154 * We take special care of name being . or ..
155 * These are returned by readdir and deserve tweaks.
156 */
157 static int
158 node_lookup_dir_nodot(pu, opc, name, namelen, pnp)
159 struct puffs_usermount *pu;
160 puffs_cookie_t opc;
161 char *name;
162 size_t namelen;
163 struct puffs_node **pnp;
164 {
165 char *path;
166 struct puffs_node *dpn = (struct puffs_node *)opc;
167 int error;
168
169 /*
170 * is easy as we already know it
171 */
172 if (strncmp(name, ".", namelen) == 0) {
173 *pnp = (struct puffs_node *)opc;
174 return 0;
175 }
176
177 /*
178 * For .. we just forget the name part
179 */
180 if (strncmp(name, "..", namelen) == 0)
181 namelen = 0;
182
183 namelen = PNPLEN(dpn) + 1 + namelen + 1;
184 if ((path = malloc(namelen)) == NULL)
185 DERR(EX_OSERR, "malloc failed");
186 (void)snprintf(path, namelen, "%s/%s", (char *)PNPATH(dpn), name);
187
188 error = node_lookup_common(pu, opc, path, pnp);
189
190 free(path);
191
192 return error;
193 }
194
195 static int
196 node_lookup_common(pu, opc, path, pnp)
197 struct puffs_usermount *pu;
198 puffs_cookie_t opc;
199 const char *path;
200 struct puffs_node **pnp;
201 {
202 struct perfuse_state *ps;
203 perfuse_msg_t *pm;
204 struct fuse_entry_out *feo;
205 struct puffs_node *pn;
206 size_t len;
207 int error;
208
209 ps = puffs_getspecific(pu);
210
211 path = basename_r(path);
212 len = strlen(path) + 1;
213
214 pm = ps->ps_new_msg(pu, opc, FUSE_LOOKUP, len, NULL);
215 (void)strlcpy(_GET_INPAYLOAD(ps, pm, char *), path, len);
216
217 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0)
218 goto out;
219
220 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
221
222 pn = perfuse_new_pn(pu, opc);
223 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
224
225 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
226
227 if (pnp != NULL)
228 *pnp = pn;
229
230 out:
231 ps->ps_destroy_msg(pm);
232
233 return error;
234 }
235
236
237 /*
238 * Common final code for methods that create objects:
239 * perfuse_node_mkdir
240 * perfuse_node_mknod
241 * perfuse_node_symlink
242 */
243 static int
244 node_mk_common(pu, opc, pni, pcn, vap, pm)
245 struct puffs_usermount *pu;
246 puffs_cookie_t opc;
247 struct puffs_newinfo *pni;
248 const struct puffs_cn *pcn;
249 const struct vattr *vap;
250 perfuse_msg_t *pm;
251 {
252 struct perfuse_state *ps;
253 struct puffs_node *pn;
254 struct fuse_entry_out *feo;
255 struct fuse_setattr_in *fsi;
256 int error;
257
258 ps = puffs_getspecific(pu);
259
260 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0)
261 goto out;
262
263 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
264 if (feo->nodeid == PERFUSE_UNKNOWN_INO)
265 DERRX(EX_SOFTWARE, "%s: no ino", __func__);
266
267 pn = perfuse_new_pn(pu, opc);
268 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
269
270 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
271 puffs_newinfo_setcookie(pni, pn);
272 ps->ps_destroy_msg(pm);
273
274 /*
275 * Set owner and group
276 */
277 (void)puffs_cred_getuid(pcn->pcn_cred, &pn->pn_va.va_uid);
278 (void)puffs_cred_getgid(pcn->pcn_cred, &pn->pn_va.va_gid);
279
280 pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn,
281 FUSE_SETATTR, sizeof(*fsi), NULL);
282 fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
283 fsi->uid = pn->pn_va.va_uid;
284 fsi->gid = pn->pn_va.va_gid;
285 fsi->valid = FUSE_FATTR_UID|FUSE_FATTR_GID;
286
287 /*
288 * A fuse_attr_out is returned, but we ignore it.
289 */
290 error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out));
291
292 out:
293 ps->ps_destroy_msg(pm);
294
295 return error;
296 }
297
298 static const char *
299 basename_r(string)
300 const char *string;
301 {
302 char *result;
303
304 if ((result = rindex(string, '/')) == NULL)
305 return string;
306
307 /*
308 * We are finished if this is not a trailing /
309 */
310 if (result[1] != '\0')
311 return result + 1;
312
313
314 /*
315 * Go back until we found something else than a /
316 */
317 while (result != string) {
318 result--;
319 if (result[0] != '/')
320 break;
321 }
322
323 if (result == string)
324 return string;
325
326 if ((result = rindex(string, '/')) == NULL)
327 return string;
328
329 return result + 1;
330
331 }
332
333 static ssize_t
334 fuse_to_dirent(pu, opc, fd, fd_len)
335 struct puffs_usermount *pu;
336 puffs_cookie_t opc;
337 struct fuse_dirent *fd;
338 size_t fd_len;
339 {
340 struct dirent *dents;
341 size_t dents_len;
342 ssize_t written;
343 uint64_t fd_offset;
344 struct fuse_dirent *fd_base;
345 size_t len;
346
347 fd_base = fd;
348 fd_offset = 0;
349 written = 0;
350 dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
351 dents_len = PERFUSE_NODE_DATA(opc)->pnd_dirent_len;
352
353 do {
354 char *ndp;
355 size_t reclen;
356
357 reclen = _DIRENT_RECLEN(dents, fd->namelen);
358
359 /*
360 * Check we do not overflow the output buffer
361 * struct fuse_dirent is bigger than struct dirent,
362 * so we should always use fd_len and never reallocate
363 * later.
364 * If we have to reallocate,try to double the buffer
365 * each time so that we do not have to do it too often.
366 */
367 if (written + reclen > dents_len) {
368 if (dents_len == 0)
369 dents_len = fd_len;
370 else
371 dents_len =
372 MAX(2 * dents_len, written + reclen);
373
374 dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
375 if ((dents = realloc(dents, dents_len)) == NULL)
376 DERR(EX_OSERR, "malloc failed");
377
378 PERFUSE_NODE_DATA(opc)->pnd_dirent = dents;
379 PERFUSE_NODE_DATA(opc)->pnd_dirent_len = dents_len;
380
381 /*
382 * (void *) for delint
383 */
384 ndp = (char *)(void *)dents + written;
385 dents = (struct dirent *)(void *)ndp;
386 }
387
388
389
390 /*
391 * Filesystem was mounted without -o use_ino
392 * Perform a lookup to find it.
393 * XXX still broken
394 */
395 if (fd->ino == PERFUSE_UNKNOWN_INO) {
396 struct puffs_node *pn;
397
398 if (node_lookup_dir_nodot(pu, opc, fd->name,
399 fd->namelen, &pn) != 0)
400 DERRX(EX_SOFTWARE,
401 "node_lookup_dir_nodot failed");
402
403 fd->ino = PERFUSE_NODE_DATA(pn)->pnd_ino;
404 }
405
406 dents->d_fileno = fd->ino;
407 dents->d_reclen = reclen;
408 dents->d_namlen = fd->namelen;
409 dents->d_type = fd->type;
410 strlcpy(dents->d_name, fd->name, fd->namelen + 1);
411
412 #ifdef PERFUSE_DEBUG
413 if (perfuse_diagflags & PDF_READDIR)
414 DPRINTF("%s: translated \"%s\" ino = %lld\n",
415 __func__, dents->d_name, dents->d_fileno);
416 #endif
417
418 dents = _DIRENT_NEXT(dents);
419 written += reclen;
420
421 /*
422 * Move to the next record.
423 * fd->off seems unreliable, for instance, flusterfs
424 * does not clear the unused bits, and we get
425 * 0xffffffffb9b95040 instead of just 0x40. Use
426 * record alignement instead.
427 */
428 len = FUSE_DIRENT_ALIGN(sizeof(*fd) + fd->namelen);
429 #ifdef PERFUSE_DEBUG
430 if (perfuse_diagflags & PDF_READDIR)
431 DPRINTF("%s: record at %lld/0x%llx length = %d/0x%x. "
432 "next record at %lld/0x%llx, max %d/0x%x\n",
433 __func__, fd_offset, fd_offset, len, len,
434 fd_offset + len, fd_offset + len,
435 fd_len, fd_len);
436 #endif
437 fd_offset += len;
438
439 /*
440 * Check if next record is still within the packet
441 * If it is not, we reached the end of the buffer.
442 */
443 if (fd_offset >= fd_len)
444 break;
445
446 /*
447 * (void *) for delint
448 */
449 ndp = (char *)(void *)fd_base + (size_t)fd_offset;
450 fd = (struct fuse_dirent *)(void *)ndp;
451
452 } while (1 /* CONSTCOND */);
453
454 /*
455 * Adjust the dirent output length
456 */
457 if (written != -1)
458 PERFUSE_NODE_DATA(opc)->pnd_dirent_len = written;
459
460 return written;
461 }
462
463 /* ARGSUSED0 */
464 static int
465 readdir_buffered(ps, opc, dent, readoff,
466 reslen, pcr, eofflag, cookies, ncookies)
467 struct perfuse_state *ps;
468 puffs_cookie_t opc;
469 struct dirent *dent;
470 off_t *readoff;
471 size_t *reslen;
472 const struct puffs_cred *pcr;
473 int *eofflag;
474 off_t *cookies;
475 size_t *ncookies;
476 {
477 struct dirent *fromdent;
478 struct perfuse_node_data *pnd;
479 char *ndp;
480
481 pnd = PERFUSE_NODE_DATA(opc);
482
483 while (*readoff < pnd->pnd_dirent_len) {
484 /*
485 * (void *) for delint
486 */
487 ndp = (char *)(void *)pnd->pnd_dirent + (size_t)*readoff;
488 fromdent = (struct dirent *)(void *)ndp;
489
490 if (*reslen < _DIRENT_SIZE(fromdent))
491 break;
492
493 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
494 *readoff += _DIRENT_SIZE(fromdent);
495 *reslen -= _DIRENT_SIZE(fromdent);
496
497 dent = _DIRENT_NEXT(dent);
498 }
499
500 #ifdef PERFUSE_DEBUG
501 if (perfuse_diagflags & PDF_READDIR)
502 DPRINTF("%s: readoff = %lld, pnd->pnd_dirent_len = %d\n",
503 __func__, *readoff, pnd->pnd_dirent_len);
504 #endif
505 if (*readoff >= pnd->pnd_dirent_len) {
506 free(pnd->pnd_dirent);
507 pnd->pnd_dirent = NULL;
508 pnd->pnd_dirent_len = 0;
509 *eofflag = 1;
510 }
511
512 return 0;
513 }
514
515 /* ARGSUSED0 */
516 static void
517 requeue_request(pu, opc, type)
518 struct puffs_usermount *pu;
519 puffs_cookie_t opc;
520 enum perfuse_qtype type;
521 {
522 struct perfuse_cc_queue pcq;
523 struct perfuse_node_data *pnd;
524 #ifdef PERFUSE_DEBUG
525 struct perfuse_state *ps;
526
527 ps = perfuse_getspecific(pu);
528 #endif
529
530 /*
531 * XXX Add a lock he day we go multithreaded
532 */
533 pnd = PERFUSE_NODE_DATA(opc);
534 pcq.pcq_type = type;
535 pcq.pcq_cc = puffs_cc_getcc(pu);
536 TAILQ_INSERT_TAIL(&pnd->pnd_pcq, &pcq, pcq_next);
537
538 #ifdef PERFUSE_DEBUG
539
540 if (perfuse_diagflags & PDF_REQUEUE)
541 DPRINTF("%s: REQUEUE opc = %p, pcc = %p\n",
542 __func__, (void *)opc, pcq.pcq_cc);
543 #endif
544
545 puffs_cc_yield(pcq.pcq_cc);
546
547 #ifdef PERFUSE_DEBUG
548 if (perfuse_diagflags & PDF_REQUEUE)
549 DPRINTF("%s: RESUME opc = %p, pcc = %p\n",
550 __func__, (void *)opc, pcq.pcq_cc);
551 #endif
552
553 return;
554 }
555
556 /* ARGSUSED0 */
557 static void
558 dequeue_requests(ps, opc, type, max)
559 struct perfuse_state *ps;
560 puffs_cookie_t opc;
561 enum perfuse_qtype type;
562 int max;
563 {
564 struct perfuse_cc_queue *pcq;
565 struct perfuse_node_data *pnd;
566 int dequeued;
567
568 /*
569 * XXX Add a lock he day we go multithreaded
570 */
571 pnd = PERFUSE_NODE_DATA(opc);
572 dequeued = 0;
573 TAILQ_FOREACH(pcq, &pnd->pnd_pcq, pcq_next) {
574 if (pcq->pcq_type != type)
575 continue;
576
577 puffs_cc_schedule(pcq->pcq_cc);
578 TAILQ_REMOVE(&pnd->pnd_pcq, pcq, pcq_next);
579
580 #ifdef PERFUSE_DEBUG
581 if (perfuse_diagflags & PDF_REQUEUE)
582 DPRINTF("%s: SCHEDULE opc = %p, pcc = %p\n",
583 __func__, (void *)opc, pcq->pcq_cc);
584 #endif
585
586 if (++dequeued == max)
587 break;
588 }
589
590 #ifdef PERFUSE_DEBUG
591 if (perfuse_diagflags & PDF_REQUEUE)
592 DPRINTF("%s: DONE opc = %p\n", __func__, (void *)opc);
593 #endif
594
595 return;
596 }
597
598 void
599 perfuse_fs_init(pu)
600 struct puffs_usermount *pu;
601 {
602 struct perfuse_state *ps;
603 perfuse_msg_t *pm;
604 struct fuse_init_in *fii;
605 struct fuse_init_out *fio;
606 int error;
607
608 ps = puffs_getspecific(pu);
609
610 if (puffs_mount(pu, ps->ps_target, ps->ps_mountflags, ps->ps_root) != 0)
611 DERR(EX_OSERR, "puffs_mount failed");
612
613 /*
614 * Linux 2.6.34.1 sends theses flags:
615 * FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC
616 * FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK
617 *
618 * Linux also sets max_readahead at 32 pages (128 kB)
619 */
620 pm = ps->ps_new_msg(pu, 0, FUSE_INIT, sizeof(*fii), NULL);
621 fii = GET_INPAYLOAD(ps, pm, fuse_init_in);
622 fii->major = FUSE_KERNEL_VERSION;
623 fii->minor = FUSE_KERNEL_MINOR_VERSION;
624 fii->max_readahead = 32 * PAGE_SIZE;
625 fii->flags = (FUSE_ASYNC_READ|FUSE_POSIX_LOCKS|FUSE_ATOMIC_O_TRUNC);
626
627 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fio))) != 0)
628 DERRX(EX_SOFTWARE, "init message exchange failed (%d)", error);
629
630 fio = GET_OUTPAYLOAD(ps, pm, fuse_init_out);
631 ps->ps_max_readahead = fio->max_readahead;
632 ps->ps_max_write = fio->max_write;
633
634 ps->ps_destroy_msg(pm);
635
636 return;
637 }
638
639 int
640 perfuse_fs_unmount(pu, flags)
641 struct puffs_usermount *pu;
642 int flags;
643 {
644 perfuse_msg_t *pm;
645 struct perfuse_state *ps;
646 puffs_cookie_t opc;
647 int error;
648
649 ps = puffs_getspecific(pu);
650
651 opc = (puffs_cookie_t)puffs_getroot(pu);
652 pm = ps->ps_new_msg(pu, opc, FUSE_DESTROY, 0, NULL);
653
654 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) {
655 DWARN("unmount %s", ps->ps_target);
656 if (!(flags & MNT_FORCE))
657 goto out;
658 }
659
660 DPRINTF("%s unmounted, exit\n", ps->ps_target);
661
662 exit(0);
663 out:
664 ps->ps_destroy_msg(pm);
665
666 return error;
667 }
668
669 int
670 perfuse_fs_statvfs(pu, svfsb)
671 struct puffs_usermount *pu;
672 struct statvfs *svfsb;
673 {
674 struct perfuse_state *ps;
675 perfuse_msg_t *pm;
676 puffs_cookie_t opc;
677 struct fuse_statfs_out *fso;
678 int error;
679
680 ps = puffs_getspecific(pu);
681 opc = (puffs_cookie_t)puffs_getroot(pu);
682 pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL);
683
684 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fso))) != 0)
685 goto out;
686
687 fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out);
688 svfsb->f_flag = ps->ps_mountflags;
689 svfsb->f_bsize = fso->st.bsize;
690 svfsb->f_frsize = fso->st.frsize;
691 svfsb->f_iosize = ((struct puffs_node *)opc)->pn_va.va_blocksize;
692 svfsb->f_blocks = fso->st.blocks;
693 svfsb->f_bfree = fso->st.bfree;
694 svfsb->f_bavail = fso->st.bavail;
695 svfsb->f_bresvd = fso->st.bfree - fso->st.bavail;
696 svfsb->f_files = fso->st.files;
697 svfsb->f_ffree = fso->st.ffree;
698 svfsb->f_favail = fso->st.ffree;/* files not reserved for root */
699 svfsb->f_fresvd = 0; /* files reserved for root */
700
701 svfsb->f_syncreads = ps->ps_syncreads;
702 svfsb->f_syncwrites = ps->ps_syncwrites;
703
704 svfsb->f_asyncreads = ps->ps_asyncreads;
705 svfsb->f_asyncwrites = ps->ps_asyncwrites;
706
707 svfsb->f_fsidx.__fsid_val[0] = ps->ps_fsid;
708 svfsb->f_fsidx.__fsid_val[1] = 0;
709 svfsb->f_fsid = ps->ps_fsid;
710 svfsb->f_namemax = MAXPATHLEN; /* XXX */
711 svfsb->f_owner = ps->ps_owner_uid;
712
713 (void)strlcpy(svfsb->f_mntonname, ps->ps_target, _VFS_NAMELEN);
714
715 if (ps->ps_filesystemtype != NULL)
716 (void)strlcpy(svfsb->f_fstypename,
717 ps->ps_filesystemtype, _VFS_NAMELEN);
718 else
719 (void)strlcpy(svfsb->f_fstypename, "fuse", _VFS_NAMELEN);
720
721 if (ps->ps_source != NULL)
722 strlcpy(svfsb->f_mntfromname, ps->ps_source, _VFS_NAMELEN);
723 else
724 strlcpy(svfsb->f_mntfromname, _PATH_FUSE, _VFS_NAMELEN);
725 out:
726 ps->ps_destroy_msg(pm);
727
728 return error;
729 }
730
731 int
732 perfuse_fs_sync(pu, waitfor, pcr)
733 struct puffs_usermount *pu;
734 int waitfor;
735 const struct puffs_cred *pcr;
736 {
737 /*
738 * FUSE does not seem to have a FS sync callback.
739 * Maybe do not even register this callback
740 */
741 return puffs_fsnop_sync(pu, waitfor, pcr);
742 }
743
744 /* ARGSUSED0 */
745 int
746 perfuse_fs_fhtonode(pu, fid, fidsize, pni)
747 struct puffs_usermount *pu;
748 void *fid;
749 size_t fidsize;
750 struct puffs_newinfo *pni;
751 {
752 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
753 return 0;
754 }
755
756 /* ARGSUSED0 */
757 int
758 perfuse_fs_nodetofh(pu, cookie, fid, fidsize)
759 struct puffs_usermount *pu;
760 puffs_cookie_t cookie;
761 void *fid;
762 size_t *fidsize;
763 {
764 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
765 return 0;
766 }
767
768 #if 0
769 /* ARGSUSED0 */
770 void
771 perfuse_fs_extattrctl(pu, cmd, cookie, flags, namespace, attrname)
772 struct puffs_usermount *pu;
773 int cmd,
774 puffs_cookie_t *cookie;
775 int flags;
776 int namespace;
777 const char *attrname;
778 {
779 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
780 return 0;
781 }
782 #endif /* 0 */
783
784 /* ARGSUSED0 */
785 void
786 perfuse_fs_suspend(pu, status)
787 struct puffs_usermount *pu;
788 int status;
789 {
790 return;
791 }
792
793
794
795 int
796 perfuse_node_lookup(pu, opc, pni, pcn)
797 struct puffs_usermount *pu;
798 puffs_cookie_t opc;
799 struct puffs_newinfo *pni;
800 const struct puffs_cn *pcn;
801 {
802 struct puffs_node *pn;
803 int error;
804
805 /*
806 * XXX This is borrowed from librefuse,
807 * and __UNCONST is said to be fixed.
808 */
809 pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
810 __UNCONST(&pcn->pcn_po_full));
811
812 if (pn == NULL) {
813 error = node_lookup_common(pu, opc, (char *)PCNPATH(pcn), &pn);
814 if (error != 0)
815 return error;
816 }
817
818 /*
819 * If that node had a pending reclaim, wipe it out.
820 */
821 PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED;
822
823 puffs_newinfo_setcookie(pni, pn);
824 puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
825 puffs_newinfo_setsize(pni, (voff_t)pn->pn_va.va_size);
826 puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
827
828 return 0;
829 }
830
831 int
832 perfuse_node_create(pu, opc, pni, pcn, vap)
833 struct puffs_usermount *pu;
834 puffs_cookie_t opc;
835 struct puffs_newinfo *pni;
836 const struct puffs_cn *pcn;
837 const struct vattr *vap;
838 {
839 perfuse_msg_t *pm;
840 struct perfuse_state *ps;
841 struct fuse_create_in *fci;
842 struct fuse_entry_out *feo;
843 struct fuse_open_out *foo;
844 struct puffs_node *pn;
845 const char *name;
846 size_t namelen;
847 size_t len;
848 int error;
849
850 /*
851 * Create an object require -WX permission in the parent directory
852 */
853 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
854 return EACCES;
855
856 /*
857 * If create is unimplemented: Check that it does not
858 * already exists, and if not, do mknod and open
859 */
860 ps = puffs_getspecific(pu);
861 if (ps->ps_flags & PS_NO_CREAT) {
862 error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn);
863 if (error == 0)
864 return EEXIST;
865
866 error = perfuse_node_mknod(pu, opc, pni, pcn, vap);
867 if (error != 0)
868 return error;
869
870 error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred);
871 if (error != 0)
872 return error;
873
874 return 0;
875 }
876
877 name = basename_r((char *)PCNPATH(pcn));
878 namelen = strlen(name) + 1;
879 len = sizeof(*fci) + namelen;
880
881 pm = ps->ps_new_msg(pu, opc, FUSE_CREATE, len, pcn->pcn_cred);
882 fci = GET_INPAYLOAD(ps, pm, fuse_create_in);
883 fci->flags = 0; /* No flags seems available */
884 fci->mode = vap->va_mode;
885 fci->umask = 0; /* Seems unused bu libfuse */
886 (void)strlcpy((char*)(void *)(fci + 1), name, namelen);
887
888 len = sizeof(*feo) + sizeof(*foo);
889 if ((error = XCHG_MSG(ps, pu, pm, len)) != 0)
890 goto out;
891
892 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
893 foo = (struct fuse_open_out *)(void *)(feo + 1);
894 if (feo->nodeid == PERFUSE_UNKNOWN_INO)
895 DERRX(EX_SOFTWARE, "%s: no ino", __func__);
896
897 /*
898 * Save the file handle and inode in node private data
899 * so that we can reuse it later
900 */
901 pn = perfuse_new_pn(pu, opc);
902 perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
903 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
904
905 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
906 puffs_newinfo_setcookie(pni, pn);
907
908 out:
909 ps->ps_destroy_msg(pm);
910
911 /*
912 * create is unimplmented, remember it for later,
913 * and start over using mknod and open instead.
914 */
915 if (error == ENOSYS) {
916 ps->ps_flags |= PS_NO_CREAT;
917 return perfuse_node_create(pu, opc, pni, pcn, vap);
918 }
919
920 return error;
921 }
922
923
924 int
925 perfuse_node_mknod(pu, opc, pni, pcn, vap)
926 struct puffs_usermount *pu;
927 puffs_cookie_t opc;
928 struct puffs_newinfo *pni;
929 const struct puffs_cn *pcn;
930 const struct vattr *vap;
931 {
932 struct perfuse_state *ps;
933 perfuse_msg_t *pm;
934 struct fuse_mknod_in *fmi;
935 const char* path;
936 size_t len;
937
938 /*
939 * Only superuser can mknod objects other than
940 * directories, files, socks, fifo and links.
941 *
942 * Create an object require -WX permission in the parent directory
943 */
944 switch (vap->va_type) {
945 case VDIR: /* FALLTHROUGH */
946 case VREG: /* FALLTHROUGH */
947 case VFIFO: /* FALLTHROUGH */
948 case VSOCK: /* FALLTHROUGH */
949 case VLNK:
950 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
951 return EACCES;
952 break;
953 default: /* VNON, VBLK, VCHR, VBAD */
954 if (!puffs_cred_isjuggernaut(pcn->pcn_cred))
955 return EACCES;
956 break;
957 }
958
959
960 ps = puffs_getspecific(pu);
961 path = basename_r((char *)PCNPATH(pcn));
962 len = sizeof(*fmi) + strlen(path) + 1;
963
964 pm = ps->ps_new_msg(pu, opc, FUSE_MKNOD, len, pcn->pcn_cred);
965 fmi = GET_INPAYLOAD(ps, pm, fuse_mknod_in);
966 fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
967 fmi->rdev = vap->va_rdev;
968 fmi->umask = 0; /* Seems unused bu libfuse */
969 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
970
971 return node_mk_common(pu, opc, pni, pcn, vap, pm);
972 }
973
974
975 int
976 perfuse_node_open(pu, opc, mode, pcr)
977 struct puffs_usermount *pu;
978 puffs_cookie_t opc;
979 int mode;
980 const struct puffs_cred *pcr;
981 {
982 struct perfuse_state *ps;
983 perfuse_msg_t *pm;
984 mode_t pmode;
985 int op;
986 struct fuse_open_in *foi;
987 struct fuse_open_out *foo;
988 struct puffs_node *pn;
989 int error;
990
991 ps = puffs_getspecific(pu);
992
993 pn = (struct puffs_node *)opc;
994 if (puffs_pn_getvap(pn)->va_type == VDIR) {
995 op = FUSE_OPENDIR;
996 pmode = PUFFS_VREAD|PUFFS_VEXEC;
997 } else {
998 op = FUSE_OPEN;
999 if (mode & (O_RDWR|O_WRONLY))
1000 pmode = PUFFS_VWRITE;
1001 else
1002 pmode = PUFFS_VREAD;
1003 }
1004
1005 /*
1006 * Opening a directory require R-X on the directory
1007 * Opening a file requires R-- for reading, -W- for writing
1008 * In both cases, --X is required on the parent.
1009 */
1010 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1011 pcr, PUFFS_VEXEC))
1012 return EACCES;
1013
1014 if (no_access(opc, pcr, pmode))
1015 return EACCES;
1016
1017 /*
1018 * libfuse docs say O_CREAT should not be set.
1019 */
1020 mode &= ~O_CREAT;
1021
1022 pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr);
1023 foi = GET_INPAYLOAD(ps, pm, fuse_open_in);
1024 foi->flags = mode & ~O_ACCMODE;
1025 switch (mode & (FREAD|FWRITE)) {
1026 case FREAD|FWRITE:
1027 foi->flags |= O_RDWR;
1028 break;
1029 case FREAD:
1030 foi->flags |= O_RDONLY;
1031 break;
1032 case FWRITE:
1033 foi->flags |= O_WRONLY;
1034 break;
1035 default:
1036 break;
1037 }
1038 foi->unused = 0;
1039
1040 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0)
1041 goto out;
1042
1043 foo = GET_OUTPAYLOAD(ps, pm, fuse_open_out);
1044
1045 /*
1046 * Save the file handle in node private data
1047 * so that we can reuse it later
1048 */
1049 perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
1050
1051 #ifdef PERFUSE_DEBUG
1052 if (perfuse_diagflags & PDF_FH)
1053 DPRINTF("%s: opc = %p, file = \"%s\", "
1054 "ino = %lld, fh = 0x%llx\n",
1055 __func__, (void *)opc,
1056 (char *)PNPATH((struct puffs_node *)opc),
1057 PERFUSE_NODE_DATA(opc)->pnd_ino, foo->fh);
1058 #endif
1059 out:
1060 ps->ps_destroy_msg(pm);
1061
1062 return error;
1063 }
1064
1065 /* ARGSUSED2 */
1066 int
1067 perfuse_node_close(pu, opc, flags, pcr)
1068 struct puffs_usermount *pu;
1069 puffs_cookie_t opc;
1070 int flags;
1071 const struct puffs_cred *pcr;
1072 {
1073 struct perfuse_state *ps;
1074 perfuse_msg_t *pm;
1075 int op;
1076 uint64_t fh;
1077 struct perfuse_node_data *pnd;
1078 struct fuse_release_in *fri;
1079 struct puffs_node *pn;
1080 int error;
1081
1082 ps = puffs_getspecific(pu);
1083 pn = (struct puffs_node *)opc;
1084 pnd = PERFUSE_NODE_DATA(pn);
1085
1086 if (puffs_pn_getvap(pn)->va_type == VDIR)
1087 op = FUSE_RELEASEDIR;
1088 else
1089 op = FUSE_RELEASE;
1090
1091 if (!(pnd->pnd_flags & PND_OPEN))
1092 return EBADF;
1093
1094 fh = perfuse_get_fh(opc);
1095
1096 /*
1097 * Sync before close for files
1098 */
1099 if ((op == FUSE_RELEASE) && (pnd->pnd_flags & PND_DIRTY)) {
1100 #ifdef PERFUSE_DEBUG
1101 if (perfuse_diagflags & PDF_SYNC)
1102 DPRINTF("%s: SYNC opc = %p, file = \"%s\"\n",
1103 __func__, (void*)opc, (char *)PNPATH(pn));
1104 #endif
1105 if ((error = perfuse_node_fsync(pu, opc, pcr, 0, 0, 0)) != 0)
1106 return error;
1107
1108 pnd->pnd_flags &= ~PND_DIRTY;
1109
1110 #ifdef PERFUSE_DEBUG
1111 if (perfuse_diagflags & PDF_SYNC)
1112 DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
1113 __func__, (void*)opc, (char *)PNPATH((pn)));
1114 #endif
1115 }
1116
1117 /*
1118 * Destroy the filehandle before sending the
1119 * request to the FUSE filesystem, otherwise
1120 * we may get a second close() while we wait
1121 * for the reply, and we would end up closing
1122 * the same fh twice instead of closng both.
1123 */
1124 perfuse_destroy_fh(pn, fh);
1125
1126 #ifdef PERFUSE_DEBUG
1127 if (perfuse_diagflags & PDF_FH)
1128 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1129 __func__, (void *)opc, pnd->pnd_ino, fh);
1130 #endif
1131
1132 /*
1133 * release_flags may be set to FUSE_RELEASE_FLUSH
1134 * to flush locks. lock_owner must be set in that case
1135 */
1136 pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL);
1137 fri = GET_INPAYLOAD(ps, pm, fuse_release_in);
1138 fri->fh = fh;
1139 fri->flags = 0;
1140 fri->release_flags = 0;
1141 fri->lock_owner = PERFUSE_NODE_DATA(pn)->pnd_lock_owner;
1142 fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0;
1143
1144 #ifdef PERFUSE_DEBUG
1145 if (perfuse_diagflags & PDF_FH)
1146 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1147 __func__, (void *)opc, pnd->pnd_ino, fri->fh);
1148 #endif
1149
1150 if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
1151 goto out;
1152
1153 out:
1154 if (error != 0)
1155 DWARNX("%s: freed fh = 0x%llx but filesystem returned error = %d",
1156 __func__, fh, error);
1157
1158 ps->ps_destroy_msg(pm);
1159
1160 return error;
1161 }
1162
1163 int
1164 perfuse_node_access(pu, opc, mode, pcr)
1165 struct puffs_usermount *pu;
1166 puffs_cookie_t opc;
1167 int mode;
1168 const struct puffs_cred *pcr;
1169 {
1170 perfuse_msg_t *pm;
1171 struct perfuse_state *ps;
1172 struct fuse_access_in *fai;
1173 int error;
1174
1175 /*
1176 * If we previously detected the filesystem does not
1177 * implement access(), short-circuit the call and skip
1178 * to libpffs access() emulation.
1179 */
1180 ps = puffs_getspecific(pu);
1181 if (ps->ps_flags & PS_NO_ACCESS) {
1182 error = ENOSYS;
1183 } else {
1184 pm = ps->ps_new_msg(pu, opc, FUSE_ACCESS, sizeof(*fai), pcr);
1185 fai = GET_INPAYLOAD(ps, pm, fuse_access_in);
1186 fai->mask = mode;
1187
1188 error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN);
1189 ps->ps_destroy_msg(pm);
1190 }
1191
1192 if (error == ENOSYS) {
1193 struct fuse_getattr_in *fgi;
1194 struct fuse_attr_out *fao;
1195
1196 ps->ps_flags |= PS_NO_ACCESS;
1197
1198 pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR,
1199 sizeof(*fgi), NULL);
1200 fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
1201 fgi->getattr_flags = 0;
1202 fgi->dummy = 0;
1203 fgi->fh = perfuse_get_fh(opc);
1204
1205 #ifdef PERFUSE_DEBUG
1206 if (perfuse_diagflags & PDF_FH)
1207 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1208 __func__, (void *)opc,
1209 PERFUSE_NODE_DATA(opc)->pnd_ino, fgi->fh);
1210 #endif
1211 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0) {
1212 ps->ps_destroy_msg(pm);
1213 goto out;
1214 }
1215
1216 fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
1217
1218 error = puffs_access(VREG, fao->attr.mode, fao->attr.uid,
1219 fao->attr.gid, (mode_t)mode, pcr);
1220
1221 ps->ps_destroy_msg(pm);
1222 }
1223
1224 out:
1225 return error;
1226 }
1227
1228 int
1229 perfuse_node_getattr(pu, opc, vap, pcr)
1230 struct puffs_usermount *pu;
1231 puffs_cookie_t opc;
1232 struct vattr *vap;
1233 const struct puffs_cred *pcr;
1234 {
1235 perfuse_msg_t *pm;
1236 struct perfuse_state *ps;
1237 struct fuse_getattr_in *fgi;
1238 struct fuse_attr_out *fao;
1239 int error;
1240
1241 /*
1242 * getattr requires --X on the parent directory
1243 */
1244 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1245 pcr, PUFFS_VEXEC))
1246 return EACCES;
1247
1248 ps = puffs_getspecific(pu);
1249
1250 /*
1251 * FUSE_GETATTR_FH must be set in fgi->flags
1252 * if we use for fgi->fh, but we do not.
1253 */
1254 pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR, sizeof(*fgi), pcr);
1255 fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
1256 fgi->getattr_flags = 0;
1257 fgi->dummy = 0;
1258 fgi->fh = 0;
1259
1260 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0)
1261 goto out;
1262
1263 fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
1264
1265 /*
1266 * The message from filesystem has a cache timeout
1267 * XXX this is ignored yet, is that right?
1268 *
1269 * We also set birthtime, flags, filerev,vaflags to 0.
1270 * This seems the best bet, since the information is
1271 * not available from filesystem.
1272 */
1273 fuse_attr_to_vap(ps, vap, &fao->attr);
1274
1275 out:
1276 ps->ps_destroy_msg(pm);
1277
1278 return error;
1279 }
1280
1281 int
1282 perfuse_node_setattr(pu, opc, vap, pcr)
1283 struct puffs_usermount *pu;
1284 puffs_cookie_t opc;
1285 const struct vattr *vap;
1286 const struct puffs_cred *pcr;
1287 {
1288 perfuse_msg_t *pm;
1289 uint64_t fh;
1290 struct perfuse_state *ps;
1291 struct fuse_setattr_in *fsi;
1292 int error;
1293 struct vattr *old_va;
1294
1295 /*
1296 * setattr requires --X on the parent directory
1297 */
1298 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1299 pcr, PUFFS_VEXEC))
1300 return EACCES;
1301
1302 old_va = puffs_pn_getvap((struct puffs_node *)opc);
1303
1304 /*
1305 * Check for permission to change size
1306 */
1307 if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) &&
1308 no_access(opc, pcr, PUFFS_VWRITE))
1309 return EACCES;
1310
1311 /*
1312 * Check for permission to change dates
1313 */
1314 if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) ||
1315 (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) &&
1316 (puffs_access_times(old_va->va_uid, old_va->va_gid,
1317 old_va->va_mode, 0, pcr) != 0))
1318 return EACCES;
1319
1320 /*
1321 * Check for permission to change owner and group
1322 */
1323 if (((vap->va_uid != (uid_t)PUFFS_VNOVAL) ||
1324 (vap->va_gid != (gid_t)PUFFS_VNOVAL)) &&
1325 (puffs_access_chown(old_va->va_uid, old_va->va_gid,
1326 vap->va_uid, vap->va_gid, pcr)) != 0)
1327 return EACCES;
1328
1329 /*
1330 * Check for permission to change permissions
1331 */
1332 if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
1333 (puffs_access_chmod(old_va->va_uid, old_va->va_gid,
1334 old_va->va_type, vap->va_mode, pcr)) != 0)
1335 return EACCES;
1336
1337
1338 ps = puffs_getspecific(pu);
1339
1340 pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr);
1341 fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
1342 fsi->valid = 0;
1343
1344 if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN) {
1345 fh = perfuse_get_fh(opc);
1346 fsi->fh = fh;
1347 if (fh != FUSE_UNKNOWN_FH)
1348 fsi->valid |= FUSE_FATTR_FH;
1349 }
1350
1351 if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) {
1352 fsi->size = vap->va_size;
1353 fsi->valid |= FUSE_FATTR_SIZE;
1354 }
1355
1356 if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) {
1357 fsi->atime = vap->va_atime.tv_sec;;
1358 fsi->atimensec = vap->va_atime.tv_nsec;;
1359 fsi->valid |= (FUSE_FATTR_ATIME|FUSE_FATTR_ATIME_NOW);
1360 }
1361
1362 if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) {
1363 fsi->mtime = vap->va_mtime.tv_sec;;
1364 fsi->mtimensec = vap->va_mtime.tv_nsec;;
1365 fsi->valid |= (FUSE_FATTR_MTIME|FUSE_FATTR_MTIME_NOW);
1366 }
1367
1368 if (vap->va_mode != (mode_t)PUFFS_VNOVAL) {
1369 fsi->mode = vap->va_mode;
1370 fsi->valid |= FUSE_FATTR_MODE;
1371 }
1372
1373 if (vap->va_uid != (uid_t)PUFFS_VNOVAL) {
1374 fsi->uid = vap->va_uid;
1375 fsi->valid |= FUSE_FATTR_UID;
1376 }
1377
1378 if (vap->va_gid != (gid_t)PUFFS_VNOVAL) {
1379 fsi->gid = vap->va_gid;
1380 fsi->valid |= FUSE_FATTR_GID;
1381 }
1382
1383 if (PERFUSE_NODE_DATA(opc)->pnd_lock_owner != 0) {
1384 fsi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
1385 fsi->valid |= FUSE_FATTR_LOCKOWNER;
1386 }
1387
1388 /*
1389 * A fuse_attr_out is returned, but we ignore it.
1390 */
1391 error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out));
1392
1393 ps->ps_destroy_msg(pm);
1394
1395 return error;
1396 }
1397
1398 int
1399 perfuse_node_poll(pu, opc, events)
1400 struct puffs_usermount *pu;
1401 puffs_cookie_t opc;
1402 int *events;
1403 {
1404 struct perfuse_state *ps;
1405 perfuse_msg_t *pm;
1406 struct fuse_poll_in *fpi;
1407 struct fuse_poll_out *fpo;
1408 int error;
1409
1410 ps = puffs_getspecific(pu);
1411 /*
1412 * kh is set if FUSE_POLL_SCHEDULE_NOTIFY is set.
1413 */
1414 pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL);
1415 fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in);
1416 fpi->fh = perfuse_get_fh(opc);
1417 fpi->kh = 0;
1418 fpi->flags = 0;
1419
1420 #ifdef PERFUSE_DEBUG
1421 if (perfuse_diagflags & PDF_FH)
1422 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1423 __func__, (void *)opc,
1424 PERFUSE_NODE_DATA(opc)->pnd_ino, fpi->fh);
1425 #endif
1426 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fpo))) != 0)
1427 goto out;
1428
1429 fpo = GET_OUTPAYLOAD(ps, pm, fuse_poll_out);
1430 *events = fpo->revents;
1431 out:
1432 ps->ps_destroy_msg(pm);
1433
1434 return error;
1435 }
1436
1437 /* ARGSUSED0 */
1438 int
1439 perfuse_node_mmap(pu, opc, flags, pcr)
1440 struct puffs_usermount *pu;
1441 puffs_cookie_t opc;
1442 int flags;
1443 const struct puffs_cred *pcr;
1444 {
1445 /*
1446 * Not implemented anymore in libfuse
1447 */
1448 return ENOSYS;
1449 }
1450
1451 /* ARGSUSED2 */
1452 int
1453 perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi)
1454 struct puffs_usermount *pu;
1455 puffs_cookie_t opc;
1456 const struct puffs_cred *pcr;
1457 int flags;
1458 off_t offlo;
1459 off_t offhi;
1460 {
1461 perfuse_msg_t *pm;
1462 struct perfuse_state *ps;
1463 struct perfuse_node_data *pnd;
1464 struct fuse_fsync_in *ffi;
1465 uint64_t fh;
1466 int open_self;
1467 int error;
1468
1469 pm = NULL;
1470 open_self = 0;
1471
1472 /*
1473 * If we previously detected it as unimplemented,
1474 * skip the call to the filesystem.
1475 */
1476 ps = puffs_getspecific(pu);
1477 if (ps->ps_flags == PS_NO_FSYNC)
1478 return ENOSYS;
1479
1480 /*
1481 * Do not sync if there are no change to sync
1482 * XXX remove that testif we implement mmap
1483 */
1484 pnd = PERFUSE_NODE_DATA(opc);
1485 #ifdef PERFUSE_DEBUG
1486 if (perfuse_diagflags & PDF_SYNC)
1487 DPRINTF("%s: TEST opc = %p, file = \"%s\" is %sdirty\n",
1488 __func__, (void*)opc,
1489 (char *)PNPATH((struct puffs_node *)opc),
1490 pnd->pnd_flags & PND_DIRTY ? "" : "not ");
1491 #endif
1492 if (!(pnd->pnd_flags & PND_DIRTY))
1493 return 0;
1494
1495 /*
1496 * It seems NetBSD can call fsync without open first
1497 * glusterfs complain in such a situation:
1498 * "FSYNC() ERR => -1 (Invalid argument)"
1499 */
1500 if (!(pnd->pnd_flags & PND_OPEN)) {
1501 if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
1502 goto out;
1503 open_self = 1;
1504 }
1505
1506 fh = perfuse_get_fh(opc);
1507
1508 /*
1509 * If fsync_flags is set, meta data should not be flushed.
1510 */
1511 pm = ps->ps_new_msg(pu, opc, FUSE_FSYNC, sizeof(*ffi), NULL);
1512 ffi = GET_INPAYLOAD(ps, pm, fuse_fsync_in);
1513 ffi->fh = fh;
1514 ffi->fsync_flags = (flags & FFILESYNC) ? 0 : 1;
1515
1516 #ifdef PERFUSE_DEBUG
1517 if (perfuse_diagflags & PDF_FH)
1518 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1519 __func__, (void *)opc,
1520 PERFUSE_NODE_DATA(opc)->pnd_ino, ffi->fh);
1521 #endif
1522
1523 if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
1524 goto out;
1525
1526 /*
1527 * No reply beyond fuse_out_header: nothing to do on success
1528 * just clear the dirty flag
1529 */
1530 pnd->pnd_flags &= ~PND_DIRTY;
1531
1532 #ifdef PERFUSE_DEBUG
1533 if (perfuse_diagflags & PDF_SYNC)
1534 DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
1535 __func__, (void*)opc,
1536 (char *)PNPATH((struct puffs_node *)opc));
1537 #endif
1538
1539 out:
1540 if (error == ENOSYS)
1541 ps->ps_flags |= PS_NO_FSYNC;
1542
1543 if (pm != NULL)
1544 ps->ps_destroy_msg(pm);
1545
1546 if (open_self)
1547 (void)perfuse_node_close(pu, opc, 0, pcr);
1548
1549 return error;
1550 }
1551
1552 /* ARGSUSED0 */
1553 int
1554 perfuse_node_seek(pu, opc, oldoff, newoff, pcr)
1555 struct puffs_usermount *pu;
1556 puffs_cookie_t opc;
1557 off_t oldoff;
1558 off_t newoff;
1559 const struct puffs_cred *pcr;
1560 {
1561 /*
1562 * XXX what should I do with oldoff?
1563 * XXX where is the newoffset returned?
1564 * XXX the held seek pointer seems just unused
1565 */
1566 PERFUSE_NODE_DATA(opc)->pnd_offset = newoff;
1567
1568 return 0;
1569 }
1570
1571 int
1572 perfuse_node_remove(pu, opc, targ, pcn)
1573 struct puffs_usermount *pu;
1574 puffs_cookie_t opc;
1575 puffs_cookie_t targ;
1576 const struct puffs_cn *pcn;
1577 {
1578 struct perfuse_state *ps;
1579 perfuse_msg_t *pm;
1580 struct puffs_node *pn;
1581 char *path;
1582 const char *name;
1583 size_t len;
1584 int error;
1585
1586 /*
1587 * remove requires -WX on the parent directory
1588 * no right required on the object.
1589 */
1590 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1591 pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1592 return EACCES;
1593
1594 ps = puffs_getspecific(pu);
1595
1596 if (targ == NULL)
1597 DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__);
1598
1599 pn = (struct puffs_node *)targ;
1600 name = basename_r((char *)PNPATH(pn));
1601 len = strlen(name) + 1;
1602
1603 pm = ps->ps_new_msg(pu, opc, FUSE_UNLINK, len, pcn->pcn_cred);
1604 path = _GET_INPAYLOAD(ps, pm, char *);
1605 (void)strlcpy(path, name, len);
1606
1607 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1608 goto out;
1609
1610 if (puffs_inval_namecache_dir(pu, opc) != 0)
1611 DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
1612
1613 if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0)
1614 DERR(EX_OSERR, "puffs_inval_namecache_node failed");
1615
1616 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
1617
1618 /*
1619 * Reclaim should take care of decreasing pnd_childcount
1620 */
1621 out:
1622 ps->ps_destroy_msg(pm);
1623
1624 return error;
1625 }
1626
1627 int
1628 perfuse_node_link(pu, opc, targ, pcn)
1629 struct puffs_usermount *pu;
1630 puffs_cookie_t opc;
1631 puffs_cookie_t targ;
1632 const struct puffs_cn *pcn;
1633 {
1634 struct perfuse_state *ps;
1635 perfuse_msg_t *pm;
1636 const char *name;
1637 size_t len;
1638 struct puffs_node *pn;
1639 struct fuse_link_in *fli;
1640 int error;
1641
1642 /*
1643 * Create an object require -WX permission in the parent directory
1644 */
1645 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1646 return EACCES;
1647
1648
1649 ps = puffs_getspecific(pu);
1650 pn = (struct puffs_node *)targ;
1651 name = basename_r((char *)PCNPATH(pcn));
1652 len = sizeof(*fli) + strlen(name) + 1;
1653
1654 pm = ps->ps_new_msg(pu, opc, FUSE_LINK, len, pcn->pcn_cred);
1655 fli = GET_INPAYLOAD(ps, pm, fuse_link_in);
1656 fli->oldnodeid = PERFUSE_NODE_DATA(pn)->pnd_ino;
1657 (void)strlcpy((char *)(void *)(fli + 1), name, len - sizeof(*fli));
1658
1659 error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
1660
1661 ps->ps_destroy_msg(pm);
1662
1663 return error;
1664 }
1665
1666 /* targ is unused since the name is in pcn_targ */
1667 /* ARGSUSED5 */
1668 int
1669 perfuse_node_rename(pu, opc, src, pcn_src, targ_dir, targ, pcn_targ)
1670 struct puffs_usermount *pu;
1671 puffs_cookie_t opc;
1672 puffs_cookie_t src;
1673 const struct puffs_cn *pcn_src;
1674 puffs_cookie_t targ_dir;
1675 puffs_cookie_t targ;
1676 const struct puffs_cn *pcn_targ;
1677 {
1678 struct perfuse_state *ps;
1679 perfuse_msg_t *pm;
1680 struct fuse_rename_in *fri;
1681 const char *newname;
1682 const char *oldname;
1683 char *np;
1684 int error;
1685 size_t len;
1686 size_t newname_len;
1687 size_t oldname_len;
1688
1689 /*
1690 * move requires -WX on source and destination directory
1691 */
1692 if (no_access(src, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC) ||
1693 no_access(targ, pcn_targ->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1694 return EACCES;
1695
1696 ps = puffs_getspecific(pu);
1697 newname = basename_r((char *)PCNPATH(pcn_targ));
1698 newname_len = strlen(newname) + 1;
1699 oldname = basename_r((char *)PCNPATH(pcn_src));
1700 oldname_len = strlen(oldname) + 1;
1701
1702 len = sizeof(*fri) + oldname_len + newname_len;
1703 pm = ps->ps_new_msg(pu, opc, FUSE_RENAME, len, pcn_src->pcn_cred);
1704 fri = GET_INPAYLOAD(ps, pm, fuse_rename_in);
1705 fri->newdir = PERFUSE_NODE_DATA(targ_dir)->pnd_ino;
1706 np = (char *)(void *)(fri + 1);
1707 (void)strlcpy(np, oldname, oldname_len);
1708 np += oldname_len;
1709 (void)strlcpy(np, newname, newname_len);
1710
1711 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1712 goto out;
1713
1714 /*
1715 * Update source and destination directories child count
1716 * Update moved object parent directory
1717 */
1718 PERFUSE_NODE_DATA(opc)->pnd_childcount--;
1719 PERFUSE_NODE_DATA(targ_dir)->pnd_childcount++;
1720 PERFUSE_NODE_DATA(src)->pnd_parent = targ_dir;
1721
1722 out:
1723 ps->ps_destroy_msg(pm);
1724
1725 return error;
1726 }
1727
1728 int
1729 perfuse_node_mkdir(pu, opc, pni, pcn, vap)
1730 struct puffs_usermount *pu;
1731 puffs_cookie_t opc;
1732 struct puffs_newinfo *pni;
1733 const struct puffs_cn *pcn;
1734 const struct vattr *vap;
1735 {
1736 struct perfuse_state *ps;
1737 perfuse_msg_t *pm;
1738 struct fuse_mkdir_in *fmi;
1739 const char *path;
1740 size_t len;
1741
1742 /*
1743 * Create an object require -WX permission in the parent directory
1744 */
1745 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1746 return EACCES;
1747
1748 ps = puffs_getspecific(pu);
1749 path = basename_r((char *)PCNPATH(pcn));
1750 len = sizeof(*fmi) + strlen(path) + 1;
1751
1752 pm = ps->ps_new_msg(pu, opc, FUSE_MKDIR, len, pcn->pcn_cred);
1753 fmi = GET_INPAYLOAD(ps, pm, fuse_mkdir_in);
1754 fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
1755 fmi->umask = 0; /* Seems unused bu libfuse? */
1756 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
1757
1758 return node_mk_common(pu, opc, pni, pcn, vap, pm);
1759 }
1760
1761
1762 int
1763 perfuse_node_rmdir(pu, opc, targ, pcn)
1764 struct puffs_usermount *pu;
1765 puffs_cookie_t opc;
1766 puffs_cookie_t targ;
1767 const struct puffs_cn *pcn;
1768 {
1769 struct perfuse_state *ps;
1770 perfuse_msg_t *pm;
1771 struct puffs_node *pn;
1772 char *path;
1773 const char *name;
1774 size_t len;
1775 int error;
1776
1777 /*
1778 * remove requires -WX on the parent directory
1779 * no right required on the object.
1780 */
1781 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1782 pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1783 return EACCES;
1784
1785 ps = puffs_getspecific(pu);
1786 pn = (struct puffs_node *)targ;
1787 name = basename_r((char *)PNPATH(pn));
1788 len = strlen(name) + 1;
1789
1790 pm = ps->ps_new_msg(pu, opc, FUSE_RMDIR, len, pcn->pcn_cred);
1791 path = _GET_INPAYLOAD(ps, pm, char *);
1792 (void)strlcpy(path, name, len);
1793
1794 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1795 goto out;
1796
1797 if (puffs_inval_namecache_dir(pu, opc) != 0)
1798 DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
1799
1800 if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0)
1801 DERR(EX_OSERR, "puffs_inval_namecache_node failed");
1802
1803 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
1804
1805 out:
1806 ps->ps_destroy_msg(pm);
1807
1808 return error;
1809 }
1810
1811 /* vap is unused */
1812 /* ARGSUSED4 */
1813 int
1814 perfuse_node_symlink(pu, opc, pni, pcn_src, vap, link_target)
1815 struct puffs_usermount *pu;
1816 puffs_cookie_t opc;
1817 struct puffs_newinfo *pni;
1818 const struct puffs_cn *pcn_src;
1819 const struct vattr *vap;
1820 const char *link_target;
1821 {
1822 struct perfuse_state *ps;
1823 perfuse_msg_t *pm;
1824 char *np;
1825 const char *path;
1826 size_t path_len;
1827 size_t linkname_len;
1828 size_t len;
1829
1830 /*
1831 * Create an object require -WX permission in the parent directory
1832 */
1833 if (no_access(opc, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1834 return EACCES;
1835
1836 ps = puffs_getspecific(pu);
1837 path = basename_r((char *)PCNPATH(pcn_src));
1838 path_len = strlen(path) + 1;
1839 linkname_len = strlen(link_target) + 1;
1840 len = path_len + linkname_len;
1841
1842 pm = ps->ps_new_msg(pu, opc, FUSE_SYMLINK, len, pcn_src->pcn_cred);
1843 np = _GET_INPAYLOAD(ps, pm, char *);
1844 (void)strlcpy(np, path, path_len);
1845 np += path_len;
1846 (void)strlcpy(np, link_target, linkname_len);
1847
1848 return node_mk_common(pu, opc, pni, pcn_src, vap, pm);
1849 }
1850
1851 int
1852 perfuse_node_readdir(pu, opc, dent, readoff,
1853 reslen, pcr, eofflag, cookies, ncookies)
1854 struct puffs_usermount *pu;
1855 puffs_cookie_t opc;
1856 struct dirent *dent;
1857 off_t *readoff;
1858 size_t *reslen;
1859 const struct puffs_cred *pcr;
1860 int *eofflag;
1861 off_t *cookies;
1862 size_t *ncookies;
1863 {
1864 perfuse_msg_t *pm;
1865 uint64_t fh;
1866 struct perfuse_state *ps;
1867 struct perfuse_node_data *pnd;
1868 struct fuse_read_in *fri;
1869 struct fuse_out_header *foh;
1870 struct fuse_dirent *fd;
1871 size_t foh_len;
1872 int error;
1873 int open_self;
1874 uint64_t fd_offset;
1875
1876 pm = NULL;
1877 error = 0;
1878 open_self = 0;
1879 ps = puffs_getspecific(pu);
1880
1881 /*
1882 * readdir state is kept at node level, and several readdir
1883 * requests can be issued at the same time on the same node.
1884 * We need to queue requests so that only one is in readdir
1885 * code at the same time.
1886 */
1887 pnd = PERFUSE_NODE_DATA(opc);
1888 while (pnd->pnd_flags & PND_INREADDIR)
1889 requeue_request(pu, opc, PCQ_READDIR);
1890 pnd->pnd_flags |= PND_INREADDIR;
1891
1892 #ifdef PERFUSE_DEBUG
1893 if (perfuse_diagflags & PDF_READDIR)
1894 DPRINTF("%s: READDIR opc = %p enter critical section\n",
1895 __func__, (void *)opc);
1896 #endif
1897 /*
1898 * Do we already have the data bufered?
1899 */
1900 if (pnd->pnd_dirent != NULL)
1901 goto out;
1902 pnd->pnd_dirent_len = 0;
1903
1904 /*
1905 * It seems NetBSD can call readdir without open first
1906 * libfuse will crash if it is done that way, hence open first.
1907 */
1908 if (!(PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN)) {
1909 if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
1910 goto out;
1911 open_self = 1;
1912 }
1913
1914 fh = perfuse_get_fh(opc);
1915
1916 #ifdef PERFUSE_DEBUG
1917 if (perfuse_diagflags & PDF_FH)
1918 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1919 __func__, (void *)opc,
1920 PERFUSE_NODE_DATA(opc)->pnd_ino, fh);
1921 #endif
1922
1923 pnd->pnd_all_fd = NULL;
1924 pnd->pnd_all_fd_len = 0;
1925 fd_offset = 0;
1926
1927 do {
1928 size_t fd_len;
1929 char *afdp;
1930
1931 pm = ps->ps_new_msg(pu, opc, FUSE_READDIR, sizeof(*fri), pcr);
1932
1933 /*
1934 * read_flags, lock_owner and flags are unused in libfuse
1935 *
1936 * XXX if fri->size is too big (bigger than PAGE_SIZE?), * we get strange bugs. ktrace shows 16 bytes or garbage
1937 * at the end of sent frames, but perfused does not receive
1938 * that data. The data length is hoverver the same, which
1939 * cause perfused to use the last 16 bytes of the frame
1940 * as the frame header of the next frame.
1941 *
1942 * This may be a kernel bug.
1943 */
1944 fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
1945 fri->fh = fh;
1946 fri->offset = fd_offset;
1947 fri->size = PAGE_SIZE - sizeof(struct fuse_out_header);
1948 fri->read_flags = 0;
1949 fri->lock_owner = 0;
1950 fri->flags = 0;
1951
1952 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1953 goto out;
1954
1955 /*
1956 * There are many puffs_framebufs calls later,
1957 * therefore foh will not be valid for a long time.
1958 * Just get the length and forget it.
1959 */
1960 foh = GET_OUTHDR(ps, pm);
1961 foh_len = foh->len;
1962
1963 /*
1964 * It seems that the only way to discover the end
1965 * of the buffer is to get an empty read
1966 */
1967 if (foh_len == sizeof(*foh))
1968 break;
1969
1970 /*
1971 * Corrupted message.
1972 */
1973 if (foh_len < sizeof(*foh) + sizeof(*fd)) {
1974 DWARNX("readdir reply too short");
1975 error = EIO;
1976 goto out;
1977 }
1978
1979
1980 fd = GET_OUTPAYLOAD(ps, pm, fuse_dirent);
1981 fd_len = foh_len - sizeof(*foh);
1982
1983 pnd->pnd_all_fd = realloc(pnd->pnd_all_fd,
1984 pnd->pnd_all_fd_len + fd_len);
1985 if (pnd->pnd_all_fd == NULL)
1986 DERR(EX_OSERR, "malloc failed");
1987
1988 afdp = (char *)(void *)pnd->pnd_all_fd + pnd->pnd_all_fd_len;
1989 (void)memcpy(afdp, fd, fd_len);
1990
1991 pnd->pnd_all_fd_len += fd_len;
1992 fd_offset += fd_len;
1993
1994 ps->ps_destroy_msg(pm);
1995 pm = NULL;
1996 } while (1 /* CONSTCOND */);
1997
1998 if (fuse_to_dirent(pu, opc, pnd->pnd_all_fd, pnd->pnd_all_fd_len) == -1)
1999 error = EIO;
2000
2001 out:
2002 if (pnd->pnd_all_fd != NULL) {
2003 free(pnd->pnd_all_fd);
2004 pnd->pnd_all_fd = NULL;
2005 pnd->pnd_all_fd_len = 0;
2006 }
2007
2008 if (pm != NULL)
2009 ps->ps_destroy_msg(pm);
2010
2011 /*
2012 * If we opened the directory ourselves, close now
2013 * errors are ignored.
2014 */
2015 if (open_self)
2016 (void)perfuse_node_close(pu, opc, 0, pcr);
2017
2018 if (error == 0)
2019 error = readdir_buffered(ps, opc, dent, readoff,
2020 reslen, pcr, eofflag, cookies, ncookies);
2021
2022 /*
2023 * Schedule queued readdir requests
2024 */
2025 dequeue_requests(ps, opc, PCQ_READDIR, DEQUEUE_ALL);
2026 pnd->pnd_flags &= ~PND_INREADDIR;
2027
2028 #ifdef PERFUSE_DEBUG
2029 if (perfuse_diagflags & PDF_READDIR)
2030 DPRINTF("%s: READDIR opc = %p exit critical section\n",
2031 __func__, (void *)opc);
2032 #endif
2033
2034 return error;
2035 }
2036
2037 int
2038 perfuse_node_readlink(pu, opc, pcr, linkname, linklen)
2039 struct puffs_usermount *pu;
2040 puffs_cookie_t opc;
2041 const struct puffs_cred *pcr;
2042 char *linkname;
2043 size_t *linklen;
2044 {
2045 struct perfuse_state *ps;
2046 perfuse_msg_t *pm;
2047 int error;
2048 size_t len;
2049 struct fuse_out_header *foh;
2050
2051 /*
2052 * --X required on parent, R-- required on link
2053 */
2054 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
2055 pcr, PUFFS_VEXEC) ||
2056 no_access(opc, pcr, PUFFS_VREAD))
2057 return EACCES;
2058
2059 ps = puffs_getspecific(pu);
2060
2061 pm = ps->ps_new_msg(pu, opc, FUSE_READLINK, 0, pcr);
2062
2063 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
2064 goto out;
2065
2066 foh = GET_OUTHDR(ps, pm);
2067 len = foh->len - sizeof(*foh) + 1;
2068 if (len > *linklen)
2069 DERRX(EX_PROTOCOL, "path len = %d too long", len);
2070
2071 *linklen = len;
2072 (void)strlcpy(linkname, _GET_OUTPAYLOAD(ps, pm, char *), len);
2073 out:
2074 ps->ps_destroy_msg(pm);
2075
2076 return error;
2077 }
2078
2079 int
2080 perfuse_node_reclaim(pu, opc)
2081 struct puffs_usermount *pu;
2082 puffs_cookie_t opc;
2083 {
2084 struct perfuse_state *ps;
2085 perfuse_msg_t *pm;
2086 struct perfuse_node_data *pnd;
2087 struct fuse_forget_in *ffi;
2088 struct puffs_node *pn;
2089 struct puffs_node *pn_root;
2090
2091 ps = puffs_getspecific(pu);
2092 pnd = PERFUSE_NODE_DATA(opc);
2093
2094 /*
2095 * Make sure open files are properly closed when reclaimed.
2096 */
2097 while (pnd->pnd_flags & PND_OPEN)
2098 (void)perfuse_node_close(pu, opc, 0, NULL);
2099
2100 /*
2101 * Never forget the root.
2102 */
2103 if (pnd->pnd_ino == FUSE_ROOT_ID)
2104 return 0;
2105
2106 pnd->pnd_flags |= PND_RECLAIMED;
2107
2108 #ifdef PERFUSE_DEBUG
2109 if (perfuse_diagflags & PDF_RECLAIM)
2110 DPRINTF("%s (nodeid %lld) reclaimed\n",
2111 (char *)PNPATH((struct puffs_node *)opc), pnd->pnd_ino);
2112 #endif
2113
2114 pn_root = puffs_getroot(pu);
2115 pn = (struct puffs_node *)opc;
2116 while (pn != pn_root) {
2117 struct puffs_node *parent_pn;
2118
2119 pnd = PERFUSE_NODE_DATA(pn);
2120
2121 #ifdef PERFUSE_DEBUG
2122 if (perfuse_diagflags & PDF_RECLAIM)
2123 DPRINTF("%s (nodeid %lld) is %sreclaimed, "
2124 "has childcount %d, %sopen\n",
2125 (char *)PNPATH(pn), pnd->pnd_ino,
2126 pnd->pnd_flags & PND_RECLAIMED ? "" : "not ",
2127 pnd->pnd_childcount,
2128 pnd->pnd_flags & PND_OPEN ? "" : "not ");
2129
2130 if (pnd->pnd_flags & PND_OPEN)
2131 DWARNX("%s: (nodeid %lld) %s is still open",
2132 __func__, pnd->pnd_ino, (char *)PNPATH(pn));
2133 #endif
2134
2135 if (!(pnd->pnd_flags & PND_RECLAIMED) ||
2136 (pnd->pnd_childcount != 0))
2137 return 0;
2138
2139 /*
2140 * If the file is still open, close all file handles
2141 * XXX no pcr arguement to send.
2142 */
2143 while(pnd->pnd_flags & PND_OPEN)
2144 (void)perfuse_node_close(pu, opc, 0, NULL);
2145
2146 pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, FUSE_FORGET,
2147 sizeof(*ffi), NULL);
2148 ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in);
2149 ffi->nlookup = pnd->pnd_nlookup;
2150
2151 /*
2152 * No reply is expected, pm is freed in XCHG_MSG
2153 */
2154 (void)XCHG_MSG_NOREPLY(ps, pu, pm, UNSPEC_REPLY_LEN);
2155
2156 parent_pn = pnd->pnd_parent;
2157
2158 perfuse_destroy_pn(pn);
2159 puffs_pn_put(pn);
2160
2161 pn = parent_pn;
2162 }
2163
2164 return 0;
2165 }
2166
2167 /* ARGSUSED0 */
2168 int
2169 perfuse_node_inactive(pu, opc)
2170 struct puffs_usermount *pu;
2171 puffs_cookie_t opc;
2172 {
2173 return 0;
2174 }
2175
2176
2177 /* ARGSUSED0 */
2178 int
2179 perfuse_node_print(pu, opc)
2180 struct puffs_usermount *pu;
2181 puffs_cookie_t opc;
2182 {
2183 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
2184 return 0;
2185 }
2186
2187 /* ARGSUSED0 */
2188 int
2189 perfuse_node_pathconf(pu, opc, name, retval)
2190 struct puffs_usermount *pu;
2191 puffs_cookie_t opc;
2192 int name;
2193 int *retval;
2194 {
2195 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
2196 return 0;
2197 }
2198
2199 /* id is unused */
2200 /* ARGSUSED2 */
2201 int
2202 perfuse_node_advlock(pu, opc, id, op, fl, flags)
2203 struct puffs_usermount *pu;
2204 puffs_cookie_t opc;
2205 void *id;
2206 int op;
2207 struct flock *fl;
2208 int flags;
2209 {
2210 struct perfuse_state *ps;
2211 int fop;
2212 perfuse_msg_t *pm;
2213 struct fuse_lk_in *fli;
2214 struct fuse_lk_out *flo;
2215 int error;
2216
2217 ps = puffs_getspecific(pu);
2218
2219 if (op == F_GETLK)
2220 fop = FUSE_GETLK;
2221 else
2222 fop = (flags & F_WAIT) ? FUSE_SETLKW : FUSE_SETLK;
2223
2224 pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL);
2225 fli = GET_INPAYLOAD(ps, pm, fuse_lk_in);
2226 fli->fh = perfuse_get_fh(opc);
2227 fli->owner = fl->l_pid;
2228 fli->lk.start = fl->l_start;
2229 fli->lk.end = fl->l_start + fl->l_len;
2230 fli->lk.type = fl->l_type;
2231 fli->lk.pid = fl->l_pid;
2232 fli->lk_flags = (flags & F_FLOCK) ? FUSE_LK_FLOCK : 0;
2233
2234 #ifdef PERFUSE_DEBUG
2235 if (perfuse_diagflags & PDF_FH)
2236 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
2237 __func__, (void *)opc,
2238 PERFUSE_NODE_DATA(opc)->pnd_ino, fli->fh);
2239 #endif
2240
2241 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*flo))) != 0)
2242 goto out;
2243
2244 flo = GET_OUTPAYLOAD(ps, pm, fuse_lk_out);
2245 fl->l_start = flo->lk.start;
2246 fl->l_len = flo->lk.end - flo->lk.start;
2247 fl->l_pid = flo->lk.pid;
2248 fl->l_type = flo->lk.type;
2249 fl->l_whence = SEEK_SET; /* libfuse hardcodes it */
2250
2251 /*
2252 * Save or clear the lock
2253 */
2254 switch (op) {
2255 case F_SETLK:
2256 PERFUSE_NODE_DATA(opc)->pnd_lock_owner = flo->lk.pid;
2257 break;
2258 case F_UNLCK:
2259 PERFUSE_NODE_DATA(opc)->pnd_lock_owner = 0;
2260 break;
2261 default:
2262 break;
2263 }
2264
2265 out:
2266 ps->ps_destroy_msg(pm);
2267
2268 return error;
2269 }
2270
2271 int
2272 perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag)
2273 struct puffs_usermount *pu;
2274 puffs_cookie_t opc;
2275 uint8_t *buf;
2276 off_t offset;
2277 size_t *resid;
2278 const struct puffs_cred *pcr;
2279 int ioflag;
2280 {
2281 struct perfuse_state *ps;
2282 perfuse_msg_t *pm;
2283 struct fuse_read_in *fri;
2284 struct fuse_out_header *foh;
2285 size_t readen;
2286 size_t requested;
2287 int error;
2288
2289 ps = puffs_getspecific(pu);
2290 pm = NULL;
2291
2292 requested = *resid;
2293 if ((ps->ps_readahead + requested) > ps->ps_max_readahead) {
2294 if (perfuse_diagflags & PDF_REQUEUE)
2295 DPRINTF("readahead = %d\n", ps->ps_readahead);
2296 requeue_request(pu, opc, PCQ_READ);
2297 }
2298 ps->ps_readahead += requested;
2299
2300 do {
2301 /*
2302 * flags may be set to FUSE_READ_LOCKOWNER
2303 * if lock_owner is provided.
2304 *
2305 * XXX See comment about fri->size in perfuse_node_readdir
2306 * We encounter the same bug here.
2307 */
2308 pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr);
2309 fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
2310 fri->fh = perfuse_get_fh(opc);
2311 fri->offset = offset;
2312 fri->size = MIN(*resid, PAGE_SIZE - sizeof(*foh));
2313 fri->read_flags = 0; /* XXX Unused by libfuse? */
2314 fri->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
2315 fri->flags = 0;
2316 fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0;
2317
2318 #ifdef PERFUSE_DEBUG
2319 if (perfuse_diagflags & PDF_FH)
2320 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
2321 __func__, (void *)opc,
2322 PERFUSE_NODE_DATA(opc)->pnd_ino, fri->fh);
2323 #endif
2324 error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
2325
2326 if (error != 0)
2327 goto out;
2328
2329 foh = GET_OUTHDR(ps, pm);
2330 readen = foh->len - sizeof(*foh);
2331
2332 (void)memcpy(buf, _GET_OUTPAYLOAD(ps, pm, char *), readen);
2333
2334 buf += readen;
2335 offset += readen;
2336 *resid -= readen;
2337
2338 ps->ps_destroy_msg(pm);
2339 pm = NULL;
2340 } while ((*resid != 0) && (readen != 0));
2341
2342 if (ioflag & (IO_SYNC|IO_DSYNC))
2343 ps->ps_syncreads++;
2344 else
2345 ps->ps_asyncreads++;
2346
2347 out:
2348 if (pm != NULL)
2349 ps->ps_destroy_msg(pm);
2350
2351 ps->ps_readahead -= requested;
2352 dequeue_requests(ps, opc, PCQ_READ, 1);
2353
2354 return error;
2355 }
2356
2357 int
2358 perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
2359 struct puffs_usermount *pu;
2360 puffs_cookie_t opc;
2361 uint8_t *buf;
2362 off_t offset;
2363 size_t *resid;
2364 const struct puffs_cred *pcr;
2365 int ioflag;
2366 {
2367 struct perfuse_state *ps;
2368 perfuse_msg_t *pm;
2369 struct fuse_write_in *fwi;
2370 struct fuse_write_out *fwo;
2371 size_t data_len;
2372 size_t payload_len;
2373 size_t written;
2374 size_t requested;
2375 int error;
2376
2377 ps = puffs_getspecific(pu);
2378 pm = NULL;
2379 written = 0;
2380
2381 requested = *resid;
2382 if ((ps->ps_write + requested) > ps->ps_max_write) {
2383 if (perfuse_diagflags & PDF_REQUEUE)
2384 DPRINTF("write = %d\n", ps->ps_write);
2385 requeue_request(pu, opc, PCQ_WRITE);
2386 }
2387 ps->ps_write += requested;
2388
2389 do {
2390 /*
2391 * It seems libfuse does not expects big chunks, so
2392 * send it page per page. The writepage feature is
2393 * probably there to minmize data movement.
2394 * XXX use ps->ps_maxwrite?
2395 */
2396 data_len = MIN(*resid, PAGE_SIZE);
2397 payload_len = data_len + sizeof(*fwi);
2398
2399 /*
2400 * flags may be set to FUSE_WRITE_CACHE (XXX usage?)
2401 * or FUSE_WRITE_LOCKOWNER, if lock_owner is provided.
2402 * write_flags is set to 1 for writepage.
2403 */
2404 pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr);
2405 fwi = GET_INPAYLOAD(ps, pm, fuse_write_in);
2406 fwi->fh = perfuse_get_fh(opc);
2407 fwi->offset = offset;
2408 fwi->size = data_len;
2409 fwi->write_flags = (fwi->size % PAGE_SIZE) ? 0 : 1;
2410 fwi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
2411 fwi->flags = 0;
2412 fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0;
2413 fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE;
2414 (void)memcpy((fwi + 1), buf + written, data_len);
2415
2416 #ifdef PERFUSE_DEBUG
2417 if (perfuse_diagflags & PDF_FH)
2418 DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
2419 __func__, (void *)opc,
2420 PERFUSE_NODE_DATA(opc)->pnd_ino, fwi->fh);
2421 #endif
2422 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fwo))) != 0)
2423 goto out;
2424
2425 fwo = GET_OUTPAYLOAD(ps, pm, fuse_write_out);
2426 written = fwo->size;
2427 *resid -= written;
2428 offset += written;
2429 buf += written;
2430
2431 ps->ps_destroy_msg(pm);
2432 pm = NULL;
2433 } while (*resid != 0);
2434
2435 /*
2436 * puffs_ops(3) says
2437 * "everything must be written or an error will be generated"
2438 */
2439 if (*resid != 0)
2440 error = EFBIG;
2441
2442 if (ioflag & (IO_SYNC|IO_DSYNC))
2443 ps->ps_syncwrites++;
2444 else
2445 ps->ps_asyncwrites++;
2446
2447 /*
2448 * Remember to sync the file
2449 */
2450 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
2451
2452 #ifdef PERFUSE_DEBUG
2453 if (perfuse_diagflags & PDF_SYNC)
2454 DPRINTF("%s: DIRTY opc = %p, file = \"%s\"\n",
2455 __func__, (void*)opc,
2456 (char *)PNPATH((struct puffs_node *)opc));
2457 #endif
2458 out:
2459 if (pm != NULL)
2460 ps->ps_destroy_msg(pm);
2461
2462 ps->ps_write -= requested;
2463 dequeue_requests(ps, opc, PCQ_WRITE, 1);
2464
2465 return error;
2466 }
2467
2468 /* ARGSUSED0 */
2469 void
2470 perfuse_cache_write(pu, opc, size, runs)
2471 struct puffs_usermount *pu;
2472 puffs_cookie_t opc;
2473 size_t size;
2474 struct puffs_cacherun *runs;
2475 {
2476 return;
2477 }
2478
2479