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