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