ops.c revision 1.6 1 /* $NetBSD: ops.c,v 1.6 2010/09/02 08:58:06 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, perfuse_msg_t *);
53 static const char *basename_r(const char *);
54 static ssize_t fuse_to_dirent(struct puffs_usermount *, puffs_cookie_t,
55 struct fuse_dirent *, size_t);
56 static int readdir_buffered(struct perfuse_state *, puffs_cookie_t,
57 struct dirent *, off_t *, size_t *, const struct puffs_cred *,
58 int *, off_t *, size_t *);
59 static void requeue_request(struct puffs_usermount *,
60 puffs_cookie_t opc, enum perfuse_qtype);
61 static void dequeue_requests(struct perfuse_state *,
62 puffs_cookie_t opc, enum perfuse_qtype, int);
63 #define DEQUEUE_ALL 0
64
65 /*
66 * From <sys/vnode>, inside #ifdef _KERNEL section
67 */
68 #define IO_SYNC (0x40|IO_DSYNC)
69 #define IO_DSYNC 0x00200
70 #define IO_DIRECT 0x02000
71
72 /*
73 * From <fcntl>, inside #ifdef _KERNEL section
74 */
75 #define F_WAIT 0x010
76 #define F_FLOCK 0x020
77
78 /*
79 * Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h
80 */
81 const enum vtype iftovt_tab[16] = {
82 VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
83 VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
84 };
85 const int vttoif_tab[9] = {
86 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
87 S_IFSOCK, S_IFIFO, S_IFMT,
88 };
89
90 #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
91 #define VTTOIF(indx) (vttoif_tab[(int)(indx)])
92
93
94 static int
95 no_access(opc, pcr, mode)
96 puffs_cookie_t opc;
97 const struct puffs_cred *pcr;
98 mode_t mode;
99 {
100 struct puffs_node *pn;
101 struct vattr *va;
102
103 pn = (struct puffs_node *)opc;
104 va = puffs_pn_getvap(pn);
105
106 return puffs_access(va->va_type, va->va_mode,
107 va->va_uid, va->va_gid,
108 mode, pcr);
109 }
110
111 static void
112 fuse_attr_to_vap(ps, vap, fa)
113 struct perfuse_state *ps;
114 struct vattr *vap;
115 struct fuse_attr *fa;
116 {
117 vap->va_type = IFTOVT(fa->mode);
118 vap->va_mode = fa->mode;
119 vap->va_nlink = fa->nlink;
120 vap->va_uid = fa->uid;
121 vap->va_gid = fa->gid;
122 vap->va_fsid = ps->ps_fsid;
123 vap->va_fileid = fa->ino;
124 vap->va_size = fa->size;
125 vap->va_blocksize = fa->blksize;
126 vap->va_atime.tv_sec = (time_t)fa->atime;
127 vap->va_atime.tv_nsec = (long) fa->atimensec;
128 vap->va_mtime.tv_sec = (time_t)fa->mtime;
129 vap->va_mtime.tv_nsec = (long)fa->mtimensec;
130 vap->va_ctime.tv_sec = (time_t)fa->ctime;
131 vap->va_ctime.tv_nsec = (long)fa->ctimensec;
132 vap->va_birthtime.tv_sec = 0;
133 vap->va_birthtime.tv_nsec = 0;
134 vap->va_gen = 0;
135 vap->va_flags = 0;
136 vap->va_rdev = fa->rdev;
137 vap->va_bytes = fa->size;
138 vap->va_filerev = 0;
139 vap->va_vaflags = 0;
140
141 if (vap->va_blocksize == 0)
142 vap->va_blocksize = DEV_BSIZE;
143
144 if (vap->va_size == (size_t)-1) /* XXX */
145 vap->va_size = 0;
146
147 return;
148 }
149
150
151 /*
152 * Lookup name in directory opc
153 * We take special care of name being . or ..
154 * These are returned by readdir and deserve tweaks.
155 */
156 static int
157 node_lookup_dir_nodot(pu, opc, name, namelen, pnp)
158 struct puffs_usermount *pu;
159 puffs_cookie_t opc;
160 char *name;
161 size_t namelen;
162 struct puffs_node **pnp;
163 {
164 char *path;
165 struct puffs_node *dpn = (struct puffs_node *)opc;
166 int error;
167
168 /*
169 * is easy as we already know it
170 */
171 if (strncmp(name, ".", namelen) == 0) {
172 *pnp = (struct puffs_node *)opc;
173 return 0;
174 }
175
176 /*
177 * For .. we just forget the name part
178 */
179 if (strncmp(name, "..", namelen) == 0)
180 namelen = 0;
181
182 namelen = PNPLEN(dpn) + 1 + namelen + 1;
183 if ((path = malloc(namelen)) == NULL)
184 DERR(EX_OSERR, "malloc failed");
185 (void)snprintf(path, namelen, "%s/%s", (char *)PNPATH(dpn), name);
186
187 error = node_lookup_common(pu, opc, path, pnp);
188
189 free(path);
190
191 return error;
192 }
193
194 static int
195 node_lookup_common(pu, opc, path, pnp)
196 struct puffs_usermount *pu;
197 puffs_cookie_t opc;
198 const char *path;
199 struct puffs_node **pnp;
200 {
201 struct perfuse_state *ps;
202 perfuse_msg_t *pm;
203 struct fuse_entry_out *feo;
204 struct puffs_node *pn;
205 size_t len;
206 int error;
207
208 ps = puffs_getspecific(pu);
209
210 path = basename_r(path);
211 len = strlen(path) + 1;
212
213 pm = ps->ps_new_msg(pu, opc, FUSE_LOOKUP, len, NULL);
214 (void)strlcpy(_GET_INPAYLOAD(ps, pm, char *), path, len);
215
216 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0)
217 goto out;
218
219 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
220
221 pn = perfuse_new_pn(pu, opc);
222 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
223
224 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
225
226 if (pnp != NULL)
227 *pnp = pn;
228
229 out:
230 ps->ps_destroy_msg(pm);
231
232 return error;
233 }
234
235
236 /*
237 * Common final code for methods that create objects:
238 * perfuse_node_mkdir
239 * perfuse_node_mknod
240 * perfuse_node_symlink
241 */
242 static int
243 node_mk_common(pu, opc, pni, pcn, pm)
244 struct puffs_usermount *pu;
245 puffs_cookie_t opc;
246 struct puffs_newinfo *pni;
247 const struct puffs_cn *pcn;
248 perfuse_msg_t *pm;
249 {
250 struct perfuse_state *ps;
251 struct puffs_node *pn;
252 struct fuse_entry_out *feo;
253 struct fuse_setattr_in *fsi;
254 int error;
255
256 ps = puffs_getspecific(pu);
257
258 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0)
259 goto out;
260
261 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
262 if (feo->nodeid == PERFUSE_UNKNOWN_INO)
263 DERRX(EX_SOFTWARE, "%s: no ino", __func__);
264
265 pn = perfuse_new_pn(pu, opc);
266 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
267
268 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
269 puffs_newinfo_setcookie(pni, pn);
270 ps->ps_destroy_msg(pm);
271
272 /*
273 * Set owner and group
274 */
275 (void)puffs_cred_getuid(pcn->pcn_cred, &pn->pn_va.va_uid);
276 (void)puffs_cred_getgid(pcn->pcn_cred, &pn->pn_va.va_gid);
277
278 pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn,
279 FUSE_SETATTR, sizeof(*fsi), NULL);
280 fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
281 fsi->uid = pn->pn_va.va_uid;
282 fsi->gid = pn->pn_va.va_gid;
283 fsi->valid = FUSE_FATTR_UID|FUSE_FATTR_GID;
284
285 /*
286 * A fuse_attr_out is returned, but we ignore it.
287 */
288 error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out));
289
290 out:
291 ps->ps_destroy_msg(pm);
292
293 return error;
294 }
295
296 static const char *
297 basename_r(string)
298 const char *string;
299 {
300 char *result;
301
302 if ((result = rindex(string, '/')) == NULL)
303 return string;
304
305 /*
306 * We are finished if this is not a trailing /
307 */
308 if (result[1] != '\0')
309 return result + 1;
310
311
312 /*
313 * Go back until we found something else than a /
314 */
315 while (result != string) {
316 result--;
317 if (result[0] != '/')
318 break;
319 }
320
321 if (result == string)
322 return string;
323
324 if ((result = rindex(string, '/')) == NULL)
325 return string;
326
327 return result + 1;
328
329 }
330
331 static ssize_t
332 fuse_to_dirent(pu, opc, fd, fd_len)
333 struct puffs_usermount *pu;
334 puffs_cookie_t opc;
335 struct fuse_dirent *fd;
336 size_t fd_len;
337 {
338 struct dirent *dents;
339 size_t dents_len;
340 ssize_t written;
341 uint64_t fd_offset;
342 struct fuse_dirent *fd_base;
343 size_t len;
344
345 fd_base = fd;
346 fd_offset = 0;
347 written = 0;
348 dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
349 dents_len = PERFUSE_NODE_DATA(opc)->pnd_dirent_len;
350
351 do {
352 char *ndp;
353 size_t reclen;
354
355 reclen = _DIRENT_RECLEN(dents, fd->namelen);
356
357 /*
358 * Check we do not overflow the output buffer
359 * struct fuse_dirent is bigger than struct dirent,
360 * so we should always use fd_len and never reallocate
361 * later.
362 * If we have to reallocate,try to double the buffer
363 * each time so that we do not have to do it too often.
364 */
365 if (written + reclen > dents_len) {
366 if (dents_len == 0)
367 dents_len = fd_len;
368 else
369 dents_len =
370 MAX(2 * dents_len, written + reclen);
371
372 dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
373 if ((dents = realloc(dents, dents_len)) == NULL)
374 DERR(EX_OSERR, "malloc failed");
375
376 PERFUSE_NODE_DATA(opc)->pnd_dirent = dents;
377 PERFUSE_NODE_DATA(opc)->pnd_dirent_len = dents_len;
378
379 /*
380 * (void *) for delint
381 */
382 ndp = (char *)(void *)dents + written;
383 dents = (struct dirent *)(void *)ndp;
384 }
385
386
387
388 /*
389 * Filesystem was mounted without -o use_ino
390 * Perform a lookup to find it.
391 * XXX still broken
392 */
393 if (fd->ino == PERFUSE_UNKNOWN_INO) {
394 struct puffs_node *pn;
395
396 if (node_lookup_dir_nodot(pu, opc, fd->name,
397 fd->namelen, &pn) != 0)
398 DERRX(EX_SOFTWARE,
399 "node_lookup_dir_nodot failed");
400
401 fd->ino = PERFUSE_NODE_DATA(pn)->pnd_ino;
402 }
403
404 dents->d_fileno = fd->ino;
405 dents->d_reclen = (unsigned short)reclen;
406 dents->d_namlen = fd->namelen;
407 dents->d_type = fd->type;
408 strlcpy(dents->d_name, fd->name, fd->namelen + 1);
409
410 #ifdef PERFUSE_DEBUG
411 if (perfuse_diagflags & PDF_READDIR)
412 DPRINTF("%s: translated \"%s\" ino = %"PRId64"\n",
413 __func__, dents->d_name, dents->d_fileno);
414 #endif
415
416 dents = _DIRENT_NEXT(dents);
417 written += reclen;
418
419 /*
420 * Move to the next record.
421 * fd->off seems unreliable, for instance, flusterfs
422 * does not clear the unused bits, and we get
423 * 0xffffffffb9b95040 instead of just 0x40. Use
424 * record alignement instead.
425 */
426 len = FUSE_DIRENT_ALIGN(sizeof(*fd) + fd->namelen);
427 #ifdef PERFUSE_DEBUG
428 if (perfuse_diagflags & PDF_READDIR)
429 DPRINTF("%s: record at %"PRId64"/0x%"PRIx64" "
430 "length = %zd/0x%zx. "
431 "next record at %"PRId64"/0x%"PRIx64" "
432 "max %zd/0x%zx\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 = %"PRId64", pnd->pnd_dirent_len = %zd\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 TAILQ_REMOVE(&pnd->pnd_pcq, &pcq, pcq_next);
547
548 #ifdef PERFUSE_DEBUG
549 if (perfuse_diagflags & PDF_REQUEUE)
550 DPRINTF("%s: RESUME opc = %p, pcc = %p\n",
551 __func__, (void *)opc, pcq.pcq_cc);
552 #endif
553
554 return;
555 }
556
557 /* ARGSUSED0 */
558 static void
559 dequeue_requests(ps, opc, type, max)
560 struct perfuse_state *ps;
561 puffs_cookie_t opc;
562 enum perfuse_qtype type;
563 int max;
564 {
565 struct perfuse_cc_queue *pcq;
566 struct perfuse_node_data *pnd;
567 int dequeued;
568
569 /*
570 * XXX Add a lock he day we go multithreaded
571 */
572 pnd = PERFUSE_NODE_DATA(opc);
573 dequeued = 0;
574 TAILQ_FOREACH(pcq, &pnd->pnd_pcq, pcq_next) {
575 if (pcq->pcq_type != type)
576 continue;
577
578 #ifdef PERFUSE_DEBUG
579 if (perfuse_diagflags & PDF_REQUEUE)
580 DPRINTF("%s: SCHEDULE opc = %p, pcc = %p\n",
581 __func__, (void *)opc, pcq->pcq_cc);
582 #endif
583 puffs_cc_schedule(pcq->pcq_cc);
584
585 if (++dequeued == max)
586 break;
587 }
588
589 #ifdef PERFUSE_DEBUG
590 if (perfuse_diagflags & PDF_REQUEUE)
591 DPRINTF("%s: DONE opc = %p\n", __func__, (void *)opc);
592 #endif
593
594 return;
595 }
596
597 void
598 perfuse_fs_init(pu)
599 struct puffs_usermount *pu;
600 {
601 struct perfuse_state *ps;
602 perfuse_msg_t *pm;
603 struct fuse_init_in *fii;
604 struct fuse_init_out *fio;
605 int error;
606
607 ps = puffs_getspecific(pu);
608
609 if (puffs_mount(pu, ps->ps_target, ps->ps_mountflags, ps->ps_root) != 0)
610 DERR(EX_OSERR, "puffs_mount failed");
611
612 /*
613 * Linux 2.6.34.1 sends theses flags:
614 * FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC
615 * FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK
616 *
617 * Linux also sets max_readahead at 32 pages (128 kB)
618 */
619 pm = ps->ps_new_msg(pu, 0, FUSE_INIT, sizeof(*fii), NULL);
620 fii = GET_INPAYLOAD(ps, pm, fuse_init_in);
621 fii->major = FUSE_KERNEL_VERSION;
622 fii->minor = FUSE_KERNEL_MINOR_VERSION;
623 fii->max_readahead = 32 * PAGE_SIZE;
624 fii->flags = (FUSE_ASYNC_READ|FUSE_POSIX_LOCKS|FUSE_ATOMIC_O_TRUNC);
625
626 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fio))) != 0)
627 DERRX(EX_SOFTWARE, "init message exchange failed (%d)", error);
628
629 fio = GET_OUTPAYLOAD(ps, pm, fuse_init_out);
630 ps->ps_max_readahead = fio->max_readahead;
631 ps->ps_max_write = fio->max_write;
632
633 ps->ps_destroy_msg(pm);
634
635 return;
636 }
637
638 int
639 perfuse_fs_unmount(pu, flags)
640 struct puffs_usermount *pu;
641 int flags;
642 {
643 perfuse_msg_t *pm;
644 struct perfuse_state *ps;
645 puffs_cookie_t opc;
646 int error;
647
648 ps = puffs_getspecific(pu);
649
650 opc = (puffs_cookie_t)puffs_getroot(pu);
651 pm = ps->ps_new_msg(pu, opc, FUSE_DESTROY, 0, NULL);
652
653 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) {
654 DWARN("unmount %s", ps->ps_target);
655 if (!(flags & MNT_FORCE))
656 goto out;
657 }
658
659 DPRINTF("%s unmounted, exit\n", ps->ps_target);
660
661 exit(0);
662 out:
663 ps->ps_destroy_msg(pm);
664
665 return error;
666 }
667
668 int
669 perfuse_fs_statvfs(pu, svfsb)
670 struct puffs_usermount *pu;
671 struct statvfs *svfsb;
672 {
673 struct perfuse_state *ps;
674 perfuse_msg_t *pm;
675 puffs_cookie_t opc;
676 struct fuse_statfs_out *fso;
677 int error;
678
679 ps = puffs_getspecific(pu);
680 opc = (puffs_cookie_t)puffs_getroot(pu);
681 pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL);
682
683 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fso))) != 0)
684 goto out;
685
686 fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out);
687 svfsb->f_flag = ps->ps_mountflags;
688 svfsb->f_bsize = fso->st.bsize;
689 svfsb->f_frsize = fso->st.frsize;
690 svfsb->f_iosize = ((struct puffs_node *)opc)->pn_va.va_blocksize;
691 svfsb->f_blocks = fso->st.blocks;
692 svfsb->f_bfree = fso->st.bfree;
693 svfsb->f_bavail = fso->st.bavail;
694 svfsb->f_bresvd = fso->st.bfree - fso->st.bavail;
695 svfsb->f_files = fso->st.files;
696 svfsb->f_ffree = fso->st.ffree;
697 svfsb->f_favail = fso->st.ffree;/* files not reserved for root */
698 svfsb->f_fresvd = 0; /* files reserved for root */
699
700 svfsb->f_syncreads = ps->ps_syncreads;
701 svfsb->f_syncwrites = ps->ps_syncwrites;
702
703 svfsb->f_asyncreads = ps->ps_asyncreads;
704 svfsb->f_asyncwrites = ps->ps_asyncwrites;
705
706 svfsb->f_fsidx.__fsid_val[0] = (int32_t)ps->ps_fsid;
707 svfsb->f_fsidx.__fsid_val[1] = 0;
708 svfsb->f_fsid = ps->ps_fsid;
709 svfsb->f_namemax = MAXPATHLEN; /* XXX */
710 svfsb->f_owner = ps->ps_owner_uid;
711
712 (void)strlcpy(svfsb->f_mntonname, ps->ps_target, _VFS_NAMELEN);
713
714 if (ps->ps_filesystemtype != NULL)
715 (void)strlcpy(svfsb->f_fstypename,
716 ps->ps_filesystemtype, _VFS_NAMELEN);
717 else
718 (void)strlcpy(svfsb->f_fstypename, "fuse", _VFS_NAMELEN);
719
720 if (ps->ps_source != NULL)
721 strlcpy(svfsb->f_mntfromname, ps->ps_source, _VFS_NAMELEN);
722 else
723 strlcpy(svfsb->f_mntfromname, _PATH_FUSE, _VFS_NAMELEN);
724 out:
725 ps->ps_destroy_msg(pm);
726
727 return error;
728 }
729
730 int
731 perfuse_fs_sync(pu, waitfor, pcr)
732 struct puffs_usermount *pu;
733 int waitfor;
734 const struct puffs_cred *pcr;
735 {
736 /*
737 * FUSE does not seem to have a FS sync callback.
738 * Maybe do not even register this callback
739 */
740 return puffs_fsnop_sync(pu, waitfor, pcr);
741 }
742
743 /* ARGSUSED0 */
744 int
745 perfuse_fs_fhtonode(pu, fid, fidsize, pni)
746 struct puffs_usermount *pu;
747 void *fid;
748 size_t fidsize;
749 struct puffs_newinfo *pni;
750 {
751 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
752 return 0;
753 }
754
755 /* ARGSUSED0 */
756 int
757 perfuse_fs_nodetofh(pu, cookie, fid, fidsize)
758 struct puffs_usermount *pu;
759 puffs_cookie_t cookie;
760 void *fid;
761 size_t *fidsize;
762 {
763 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
764 return 0;
765 }
766
767 #if 0
768 /* ARGSUSED0 */
769 void
770 perfuse_fs_extattrctl(pu, cmd, cookie, flags, namespace, attrname)
771 struct puffs_usermount *pu;
772 int cmd,
773 puffs_cookie_t *cookie;
774 int flags;
775 int namespace;
776 const char *attrname;
777 {
778 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
779 return 0;
780 }
781 #endif /* 0 */
782
783 /* ARGSUSED0 */
784 void
785 perfuse_fs_suspend(pu, status)
786 struct puffs_usermount *pu;
787 int status;
788 {
789 return;
790 }
791
792
793
794 int
795 perfuse_node_lookup(pu, opc, pni, pcn)
796 struct puffs_usermount *pu;
797 puffs_cookie_t opc;
798 struct puffs_newinfo *pni;
799 const struct puffs_cn *pcn;
800 {
801 struct puffs_node *pn;
802 int error;
803
804 /*
805 * XXX This is borrowed from librefuse,
806 * and __UNCONST is said to be fixed.
807 */
808 pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
809 __UNCONST(&pcn->pcn_po_full));
810
811 if (pn == NULL) {
812 error = node_lookup_common(pu, opc, (char *)PCNPATH(pcn), &pn);
813 if (error != 0)
814 return error;
815 }
816
817 /*
818 * If that node had a pending reclaim, wipe it out.
819 */
820 PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED;
821
822 puffs_newinfo_setcookie(pni, pn);
823 puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
824 puffs_newinfo_setsize(pni, (voff_t)pn->pn_va.va_size);
825 puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
826
827 return 0;
828 }
829
830 int
831 perfuse_node_create(pu, opc, pni, pcn, vap)
832 struct puffs_usermount *pu;
833 puffs_cookie_t opc;
834 struct puffs_newinfo *pni;
835 const struct puffs_cn *pcn;
836 const struct vattr *vap;
837 {
838 perfuse_msg_t *pm;
839 struct perfuse_state *ps;
840 struct fuse_create_in *fci;
841 struct fuse_entry_out *feo;
842 struct fuse_open_out *foo;
843 struct puffs_node *pn;
844 const char *name;
845 size_t namelen;
846 size_t len;
847 int error;
848
849 /*
850 * Create an object require -WX permission in the parent directory
851 */
852 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
853 return EACCES;
854
855 /*
856 * If create is unimplemented: Check that it does not
857 * already exists, and if not, do mknod and open
858 */
859 ps = puffs_getspecific(pu);
860 if (ps->ps_flags & PS_NO_CREAT) {
861 error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn);
862 if (error == 0)
863 return EEXIST;
864
865 error = perfuse_node_mknod(pu, opc, pni, pcn, vap);
866 if (error != 0)
867 return error;
868
869 error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred);
870 if (error != 0)
871 return error;
872
873 return 0;
874 }
875
876 name = basename_r((char *)PCNPATH(pcn));
877 namelen = strlen(name) + 1;
878 len = sizeof(*fci) + namelen;
879
880 pm = ps->ps_new_msg(pu, opc, FUSE_CREATE, len, pcn->pcn_cred);
881 fci = GET_INPAYLOAD(ps, pm, fuse_create_in);
882 fci->flags = 0; /* No flags seems available */
883 fci->mode = vap->va_mode;
884 fci->umask = 0; /* Seems unused bu libfuse */
885 (void)strlcpy((char*)(void *)(fci + 1), name, namelen);
886
887 len = sizeof(*feo) + sizeof(*foo);
888 if ((error = XCHG_MSG(ps, pu, pm, len)) != 0)
889 goto out;
890
891 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
892 foo = (struct fuse_open_out *)(void *)(feo + 1);
893 if (feo->nodeid == PERFUSE_UNKNOWN_INO)
894 DERRX(EX_SOFTWARE, "%s: no ino", __func__);
895
896 /*
897 * Save the file handle and inode in node private data
898 * so that we can reuse it later
899 */
900 pn = perfuse_new_pn(pu, opc);
901 perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
902 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
903
904 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
905 puffs_newinfo_setcookie(pni, pn);
906
907 out:
908 ps->ps_destroy_msg(pm);
909
910 /*
911 * create is unimplmented, remember it for later,
912 * and start over using mknod and open instead.
913 */
914 if (error == ENOSYS) {
915 ps->ps_flags |= PS_NO_CREAT;
916 return perfuse_node_create(pu, opc, pni, pcn, vap);
917 }
918
919 return error;
920 }
921
922
923 int
924 perfuse_node_mknod(pu, opc, pni, pcn, vap)
925 struct puffs_usermount *pu;
926 puffs_cookie_t opc;
927 struct puffs_newinfo *pni;
928 const struct puffs_cn *pcn;
929 const struct vattr *vap;
930 {
931 struct perfuse_state *ps;
932 perfuse_msg_t *pm;
933 struct fuse_mknod_in *fmi;
934 const char* path;
935 size_t len;
936
937 /*
938 * Only superuser can mknod objects other than
939 * directories, files, socks, fifo and links.
940 *
941 * Create an object require -WX permission in the parent directory
942 */
943 switch (vap->va_type) {
944 case VDIR: /* FALLTHROUGH */
945 case VREG: /* FALLTHROUGH */
946 case VFIFO: /* FALLTHROUGH */
947 case VSOCK: /* FALLTHROUGH */
948 case VLNK:
949 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
950 return EACCES;
951 break;
952 default: /* VNON, VBLK, VCHR, VBAD */
953 if (!puffs_cred_isjuggernaut(pcn->pcn_cred))
954 return EACCES;
955 break;
956 }
957
958
959 ps = puffs_getspecific(pu);
960 path = basename_r((char *)PCNPATH(pcn));
961 len = sizeof(*fmi) + strlen(path) + 1;
962
963 pm = ps->ps_new_msg(pu, opc, FUSE_MKNOD, len, pcn->pcn_cred);
964 fmi = GET_INPAYLOAD(ps, pm, fuse_mknod_in);
965 fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
966 fmi->rdev = vap->va_rdev;
967 fmi->umask = 0; /* Seems unused bu libfuse */
968 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
969
970 return node_mk_common(pu, opc, pni, pcn, pm);
971 }
972
973
974 int
975 perfuse_node_open(pu, opc, mode, pcr)
976 struct puffs_usermount *pu;
977 puffs_cookie_t opc;
978 int mode;
979 const struct puffs_cred *pcr;
980 {
981 struct perfuse_state *ps;
982 perfuse_msg_t *pm;
983 mode_t pmode;
984 int op;
985 struct fuse_open_in *foi;
986 struct fuse_open_out *foo;
987 struct puffs_node *pn;
988 int error;
989
990 ps = puffs_getspecific(pu);
991
992 pn = (struct puffs_node *)opc;
993 if (puffs_pn_getvap(pn)->va_type == VDIR) {
994 op = FUSE_OPENDIR;
995 pmode = PUFFS_VREAD|PUFFS_VEXEC;
996 } else {
997 op = FUSE_OPEN;
998 if (mode & (O_RDWR|O_WRONLY))
999 pmode = PUFFS_VWRITE;
1000 else
1001 pmode = PUFFS_VREAD;
1002 }
1003
1004 /*
1005 * Opening a directory require R-X on the directory
1006 * Opening a file requires R-- for reading, -W- for writing
1007 * In both cases, --X is required on the parent.
1008 */
1009 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1010 pcr, PUFFS_VEXEC))
1011 return EACCES;
1012
1013 if (no_access(opc, pcr, pmode))
1014 return EACCES;
1015
1016 /*
1017 * libfuse docs say O_CREAT should not be set.
1018 */
1019 mode &= ~O_CREAT;
1020
1021 pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr);
1022 foi = GET_INPAYLOAD(ps, pm, fuse_open_in);
1023 foi->flags = mode & ~O_ACCMODE;
1024 switch (mode & (FREAD|FWRITE)) {
1025 case FREAD|FWRITE:
1026 foi->flags |= O_RDWR;
1027 break;
1028 case FREAD:
1029 foi->flags |= O_RDONLY;
1030 break;
1031 case FWRITE:
1032 foi->flags |= O_WRONLY;
1033 break;
1034 default:
1035 break;
1036 }
1037 foi->unused = 0;
1038
1039 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0)
1040 goto out;
1041
1042 foo = GET_OUTPAYLOAD(ps, pm, fuse_open_out);
1043
1044 /*
1045 * Save the file handle in node private data
1046 * so that we can reuse it later
1047 */
1048 perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
1049
1050 #ifdef PERFUSE_DEBUG
1051 if (perfuse_diagflags & PDF_FH)
1052 DPRINTF("%s: opc = %p, file = \"%s\", "
1053 "ino = %"PRId64", fh = 0x%"PRIx64"\n",
1054 __func__, (void *)opc,
1055 (char *)PNPATH((struct puffs_node *)opc),
1056 PERFUSE_NODE_DATA(opc)->pnd_ino, foo->fh);
1057 #endif
1058 out:
1059 ps->ps_destroy_msg(pm);
1060
1061 return error;
1062 }
1063
1064 /* ARGSUSED2 */
1065 int
1066 perfuse_node_close(pu, opc, flags, pcr)
1067 struct puffs_usermount *pu;
1068 puffs_cookie_t opc;
1069 int flags;
1070 const struct puffs_cred *pcr;
1071 {
1072 struct perfuse_state *ps;
1073 perfuse_msg_t *pm;
1074 int op;
1075 uint64_t fh;
1076 struct perfuse_node_data *pnd;
1077 struct fuse_release_in *fri;
1078 struct puffs_node *pn;
1079 int error;
1080
1081 ps = puffs_getspecific(pu);
1082 pn = (struct puffs_node *)opc;
1083 pnd = PERFUSE_NODE_DATA(pn);
1084
1085 if (puffs_pn_getvap(pn)->va_type == VDIR)
1086 op = FUSE_RELEASEDIR;
1087 else
1088 op = FUSE_RELEASE;
1089
1090 if (!(pnd->pnd_flags & PND_OPEN))
1091 return EBADF;
1092
1093 fh = perfuse_get_fh(opc);
1094
1095 /*
1096 * Sync before close for files
1097 */
1098 if ((op == FUSE_RELEASE) && (pnd->pnd_flags & PND_DIRTY)) {
1099 #ifdef PERFUSE_DEBUG
1100 if (perfuse_diagflags & PDF_SYNC)
1101 DPRINTF("%s: SYNC opc = %p, file = \"%s\"\n",
1102 __func__, (void*)opc, (char *)PNPATH(pn));
1103 #endif
1104 if ((error = perfuse_node_fsync(pu, opc, pcr, 0, 0, 0)) != 0)
1105 return error;
1106
1107 pnd->pnd_flags &= ~PND_DIRTY;
1108
1109 #ifdef PERFUSE_DEBUG
1110 if (perfuse_diagflags & PDF_SYNC)
1111 DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
1112 __func__, (void*)opc, (char *)PNPATH((pn)));
1113 #endif
1114 }
1115
1116 /*
1117 * Destroy the filehandle before sending the
1118 * request to the FUSE filesystem, otherwise
1119 * we may get a second close() while we wait
1120 * for the reply, and we would end up closing
1121 * the same fh twice instead of closng both.
1122 */
1123 perfuse_destroy_fh(pn, fh);
1124
1125 #ifdef PERFUSE_DEBUG
1126 if (perfuse_diagflags & PDF_FH)
1127 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
1128 __func__, (void *)opc, pnd->pnd_ino, fh);
1129 #endif
1130
1131 /*
1132 * release_flags may be set to FUSE_RELEASE_FLUSH
1133 * to flush locks. lock_owner must be set in that case
1134 */
1135 pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL);
1136 fri = GET_INPAYLOAD(ps, pm, fuse_release_in);
1137 fri->fh = fh;
1138 fri->flags = 0;
1139 fri->release_flags = 0;
1140 fri->lock_owner = PERFUSE_NODE_DATA(pn)->pnd_lock_owner;
1141 fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0;
1142
1143 #ifdef PERFUSE_DEBUG
1144 if (perfuse_diagflags & PDF_FH)
1145 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
1146 __func__, (void *)opc, pnd->pnd_ino, fri->fh);
1147 #endif
1148
1149 if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
1150 goto out;
1151
1152 out:
1153 if (error != 0)
1154 DWARNX("%s: freed fh = 0x%"PRIx64" but filesystem "
1155 "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 = %"PRId64", "
1208 "fh = 0x%"PRIx64"\n", __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 = (uint32_t)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 = (uint32_t)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 = %"PRId64", fh = 0x%"PRIx64"\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 = %"PRId64", fh = 0x%"PRIx64"\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, 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, 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 = %"PRId64", fh = 0x%"PRIx64"\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 = %zd 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 %"PRId64") 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 %"PRId64") 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 %"PRId64") %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 = %"PRId64", fh = 0x%"PRIx64"\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 = %zd\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 = (uint32_t)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 = %"PRId64", fh = 0x%"PRIx64"\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 = %zd\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 = (uint32_t)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 = %"PRId64", fh = 0x%"PRIx64"\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