ops.c revision 1.15 1 /* $NetBSD: ops.c,v 1.15 2010/09/15 01:51:43 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 if (perfuse_diagflags & PDF_REQUEUE)
617 DPRINTF("%s: REQUEUE opc = %p, pcc = %p (%s)\n",
618 __func__, (void *)opc, pcq.pcq_cc,
619 perfuse_qtypestr[type]);
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 (%s)\n",
628 __func__, (void *)opc, pcq.pcq_cc,
629 perfuse_qtypestr[type]);
630 #endif
631
632 return;
633 }
634
635 /* ARGSUSED0 */
636 static int
637 dequeue_requests(ps, opc, type, max)
638 struct perfuse_state *ps;
639 puffs_cookie_t opc;
640 enum perfuse_qtype type;
641 int max;
642 {
643 struct perfuse_cc_queue *pcq;
644 struct perfuse_node_data *pnd;
645 int dequeued;
646
647 /*
648 * XXX Add a lock he day we go multithreaded
649 */
650 pnd = PERFUSE_NODE_DATA(opc);
651 dequeued = 0;
652 TAILQ_FOREACH(pcq, &pnd->pnd_pcq, pcq_next) {
653 if (pcq->pcq_type != type)
654 continue;
655
656 #ifdef PERFUSE_DEBUG
657 if (perfuse_diagflags & PDF_REQUEUE)
658 DPRINTF("%s: SCHEDULE opc = %p, pcc = %p (%s)\n",
659 __func__, (void *)opc, pcq->pcq_cc,
660 perfuse_qtypestr[type]);
661 #endif
662 puffs_cc_schedule(pcq->pcq_cc);
663
664 if (++dequeued == max)
665 break;
666 }
667
668 #ifdef PERFUSE_DEBUG
669 if (perfuse_diagflags & PDF_REQUEUE)
670 DPRINTF("%s: DONE opc = %p\n", __func__, (void *)opc);
671 #endif
672
673 return dequeued;
674 }
675
676 void
677 perfuse_fs_init(pu)
678 struct puffs_usermount *pu;
679 {
680 struct perfuse_state *ps;
681 perfuse_msg_t *pm;
682 struct fuse_init_in *fii;
683 struct fuse_init_out *fio;
684 int error;
685
686 ps = puffs_getspecific(pu);
687
688 if (puffs_mount(pu, ps->ps_target, ps->ps_mountflags, ps->ps_root) != 0)
689 DERR(EX_OSERR, "puffs_mount failed");
690
691 /*
692 * Linux 2.6.34.1 sends theses flags:
693 * FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC
694 * FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK
695 *
696 * Linux also sets max_readahead at 32 pages (128 kB)
697 */
698 pm = ps->ps_new_msg(pu, 0, FUSE_INIT, sizeof(*fii), NULL);
699 fii = GET_INPAYLOAD(ps, pm, fuse_init_in);
700 fii->major = FUSE_KERNEL_VERSION;
701 fii->minor = FUSE_KERNEL_MINOR_VERSION;
702 fii->max_readahead = 32 * PAGE_SIZE;
703 fii->flags = (FUSE_ASYNC_READ|FUSE_POSIX_LOCKS|FUSE_ATOMIC_O_TRUNC);
704
705 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fio))) != 0)
706 DERRX(EX_SOFTWARE, "init message exchange failed (%d)", error);
707
708 fio = GET_OUTPAYLOAD(ps, pm, fuse_init_out);
709 ps->ps_max_readahead = fio->max_readahead;
710 ps->ps_max_write = fio->max_write;
711
712 ps->ps_destroy_msg(pm);
713
714 return;
715 }
716
717 int
718 perfuse_fs_unmount(pu, flags)
719 struct puffs_usermount *pu;
720 int flags;
721 {
722 perfuse_msg_t *pm;
723 struct perfuse_state *ps;
724 puffs_cookie_t opc;
725 int error;
726
727 ps = puffs_getspecific(pu);
728
729 opc = (puffs_cookie_t)puffs_getroot(pu);
730 pm = ps->ps_new_msg(pu, opc, FUSE_DESTROY, 0, NULL);
731
732 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) {
733 DWARN("unmount %s", ps->ps_target);
734 if (!(flags & MNT_FORCE))
735 goto out;
736 }
737
738 DPRINTF("%s unmounted, exit\n", ps->ps_target);
739
740 exit(0);
741 out:
742 ps->ps_destroy_msg(pm);
743
744 return error;
745 }
746
747 int
748 perfuse_fs_statvfs(pu, svfsb)
749 struct puffs_usermount *pu;
750 struct statvfs *svfsb;
751 {
752 struct perfuse_state *ps;
753 perfuse_msg_t *pm;
754 puffs_cookie_t opc;
755 struct fuse_statfs_out *fso;
756 int error;
757
758 ps = puffs_getspecific(pu);
759 opc = (puffs_cookie_t)puffs_getroot(pu);
760 pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL);
761
762 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fso))) != 0)
763 goto out;
764
765 fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out);
766 svfsb->f_flag = ps->ps_mountflags;
767 svfsb->f_bsize = fso->st.bsize;
768 svfsb->f_frsize = fso->st.frsize;
769 svfsb->f_iosize = ((struct puffs_node *)opc)->pn_va.va_blocksize;
770 svfsb->f_blocks = fso->st.blocks;
771 svfsb->f_bfree = fso->st.bfree;
772 svfsb->f_bavail = fso->st.bavail;
773 svfsb->f_bresvd = fso->st.bfree - fso->st.bavail;
774 svfsb->f_files = fso->st.files;
775 svfsb->f_ffree = fso->st.ffree;
776 svfsb->f_favail = fso->st.ffree;/* files not reserved for root */
777 svfsb->f_fresvd = 0; /* files reserved for root */
778
779 svfsb->f_syncreads = ps->ps_syncreads;
780 svfsb->f_syncwrites = ps->ps_syncwrites;
781
782 svfsb->f_asyncreads = ps->ps_asyncreads;
783 svfsb->f_asyncwrites = ps->ps_asyncwrites;
784
785 svfsb->f_fsidx.__fsid_val[0] = (int32_t)ps->ps_fsid;
786 svfsb->f_fsidx.__fsid_val[1] = 0;
787 svfsb->f_fsid = ps->ps_fsid;
788 svfsb->f_namemax = MAXPATHLEN; /* XXX */
789 svfsb->f_owner = ps->ps_owner_uid;
790
791 (void)strlcpy(svfsb->f_mntonname, ps->ps_target, _VFS_NAMELEN);
792
793 if (ps->ps_filesystemtype != NULL)
794 (void)strlcpy(svfsb->f_fstypename,
795 ps->ps_filesystemtype, _VFS_NAMELEN);
796 else
797 (void)strlcpy(svfsb->f_fstypename, "fuse", _VFS_NAMELEN);
798
799 if (ps->ps_source != NULL)
800 strlcpy(svfsb->f_mntfromname, ps->ps_source, _VFS_NAMELEN);
801 else
802 strlcpy(svfsb->f_mntfromname, _PATH_FUSE, _VFS_NAMELEN);
803 out:
804 ps->ps_destroy_msg(pm);
805
806 return error;
807 }
808
809 int
810 perfuse_fs_sync(pu, waitfor, pcr)
811 struct puffs_usermount *pu;
812 int waitfor;
813 const struct puffs_cred *pcr;
814 {
815 /*
816 * FUSE does not seem to have a FS sync callback.
817 * Maybe do not even register this callback
818 */
819 return puffs_fsnop_sync(pu, waitfor, pcr);
820 }
821
822 /* ARGSUSED0 */
823 int
824 perfuse_fs_fhtonode(pu, fid, fidsize, pni)
825 struct puffs_usermount *pu;
826 void *fid;
827 size_t fidsize;
828 struct puffs_newinfo *pni;
829 {
830 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
831 return 0;
832 }
833
834 /* ARGSUSED0 */
835 int
836 perfuse_fs_nodetofh(pu, cookie, fid, fidsize)
837 struct puffs_usermount *pu;
838 puffs_cookie_t cookie;
839 void *fid;
840 size_t *fidsize;
841 {
842 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
843 return 0;
844 }
845
846 #if 0
847 /* ARGSUSED0 */
848 void
849 perfuse_fs_extattrctl(pu, cmd, cookie, flags, namespace, attrname)
850 struct puffs_usermount *pu;
851 int cmd,
852 puffs_cookie_t *cookie;
853 int flags;
854 int namespace;
855 const char *attrname;
856 {
857 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
858 return 0;
859 }
860 #endif /* 0 */
861
862 /* ARGSUSED0 */
863 void
864 perfuse_fs_suspend(pu, status)
865 struct puffs_usermount *pu;
866 int status;
867 {
868 return;
869 }
870
871
872
873 int
874 perfuse_node_lookup(pu, opc, pni, pcn)
875 struct puffs_usermount *pu;
876 puffs_cookie_t opc;
877 struct puffs_newinfo *pni;
878 const struct puffs_cn *pcn;
879 {
880 struct puffs_node *pn;
881 int error;
882
883 /*
884 * Special case for ..
885 */
886 if (PCNISDOTDOT(pcn)) {
887 pn = PERFUSE_NODE_DATA(opc)->pnd_parent;
888 PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED;
889
890 puffs_newinfo_setcookie(pni, pn);
891 puffs_newinfo_setvtype(pni, VDIR);
892
893 return 0;
894 }
895
896 /*
897 * XXX This is borrowed from librefuse,
898 * and __UNCONST is said to be fixed.
899 */
900 pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
901 __UNCONST(&pcn->pcn_po_full));
902
903 if (pn == NULL) {
904 error = node_lookup_common(pu, opc, (char *)PCNPATH(pcn), &pn);
905 if (error != 0)
906 return error;
907 }
908
909 /*
910 * If that node had a pending reclaim, wipe it out.
911 */
912 PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED;
913
914 puffs_newinfo_setcookie(pni, pn);
915 puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
916 puffs_newinfo_setsize(pni, (voff_t)pn->pn_va.va_size);
917 puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
918
919 return 0;
920 }
921
922 int
923 perfuse_node_create(pu, opc, pni, pcn, vap)
924 struct puffs_usermount *pu;
925 puffs_cookie_t opc;
926 struct puffs_newinfo *pni;
927 const struct puffs_cn *pcn;
928 const struct vattr *vap;
929 {
930 perfuse_msg_t *pm;
931 struct perfuse_state *ps;
932 struct fuse_create_in *fci;
933 struct fuse_entry_out *feo;
934 struct fuse_open_out *foo;
935 struct puffs_node *pn;
936 const char *name;
937 size_t namelen;
938 size_t len;
939 int error;
940
941 /*
942 * Create an object require -WX permission in the parent directory
943 */
944 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
945 return EACCES;
946
947 /*
948 * If create is unimplemented: Check that it does not
949 * already exists, and if not, do mknod and open
950 */
951 ps = puffs_getspecific(pu);
952 if (ps->ps_flags & PS_NO_CREAT) {
953 error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn);
954 if (error == 0)
955 return EEXIST;
956
957 error = perfuse_node_mknod(pu, opc, pni, pcn, vap);
958 if (error != 0)
959 return error;
960
961 error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn);
962 if (error != 0)
963 return error;
964
965 opc = (puffs_cookie_t)pn;
966
967 error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred);
968 if (error != 0)
969 return error;
970
971 return 0;
972 }
973
974 name = basename_r((char *)PCNPATH(pcn));
975 namelen = strlen(name) + 1;
976 len = sizeof(*fci) + namelen;
977
978 /*
979 * flags should use O_WRONLY instead of O_RDWR, but it
980 * breaks when the caller tries to read from file.
981 *
982 * mode must contain file type (ie: S_IFREG), use VTTOIF(vap->va_type)
983 */
984 pm = ps->ps_new_msg(pu, opc, FUSE_CREATE, len, pcn->pcn_cred);
985 fci = GET_INPAYLOAD(ps, pm, fuse_create_in);
986 fci->flags = O_CREAT | O_TRUNC | O_RDWR;
987 fci->mode = vap->va_mode | VTTOIF(vap->va_type);
988 fci->umask = 0; /* Seems unused by libfuse */
989 (void)strlcpy((char*)(void *)(fci + 1), name, namelen);
990
991 len = sizeof(*feo) + sizeof(*foo);
992 if ((error = XCHG_MSG(ps, pu, pm, len)) != 0)
993 goto out;
994
995 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
996 foo = (struct fuse_open_out *)(void *)(feo + 1);
997 if (feo->nodeid == PERFUSE_UNKNOWN_INO)
998 DERRX(EX_SOFTWARE, "%s: no ino", __func__);
999
1000 /*
1001 * Save the file handle and inode in node private data
1002 * so that we can reuse it later
1003 */
1004 pn = perfuse_new_pn(pu, opc);
1005 perfuse_new_fh((puffs_cookie_t)pn, foo->fh, FWRITE);
1006 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
1007
1008 #ifdef PERFUSE_DEBUG
1009 if (perfuse_diagflags & PDF_FH)
1010 DPRINTF("%s: opc = %p, file = \"%s\", "
1011 "ino = %"PRId64", rfh = 0x%"PRIx64"\n",
1012 __func__, (void *)pn, (char *)PCNPATH(pcn),
1013 feo->nodeid, foo->fh);
1014 #endif
1015
1016 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
1017 puffs_newinfo_setcookie(pni, pn);
1018
1019 /*
1020 * The parent directory needs a sync
1021 */
1022 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
1023 out:
1024 ps->ps_destroy_msg(pm);
1025
1026 /*
1027 * create is unimplmented, remember it for later,
1028 * and start over using mknod and open instead.
1029 */
1030 if (error == ENOSYS) {
1031 ps->ps_flags |= PS_NO_CREAT;
1032 return perfuse_node_create(pu, opc, pni, pcn, vap);
1033 }
1034
1035 return error;
1036 }
1037
1038
1039 int
1040 perfuse_node_mknod(pu, opc, pni, pcn, vap)
1041 struct puffs_usermount *pu;
1042 puffs_cookie_t opc;
1043 struct puffs_newinfo *pni;
1044 const struct puffs_cn *pcn;
1045 const struct vattr *vap;
1046 {
1047 struct perfuse_state *ps;
1048 perfuse_msg_t *pm;
1049 struct fuse_mknod_in *fmi;
1050 const char* path;
1051 size_t len;
1052
1053 /*
1054 * Only superuser can mknod objects other than
1055 * directories, files, socks, fifo and links.
1056 *
1057 * Create an object require -WX permission in the parent directory
1058 */
1059 switch (vap->va_type) {
1060 case VDIR: /* FALLTHROUGH */
1061 case VREG: /* FALLTHROUGH */
1062 case VFIFO: /* FALLTHROUGH */
1063 case VSOCK: /* FALLTHROUGH */
1064 case VLNK:
1065 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1066 return EACCES;
1067 break;
1068 default: /* VNON, VBLK, VCHR, VBAD */
1069 if (!puffs_cred_isjuggernaut(pcn->pcn_cred))
1070 return EACCES;
1071 break;
1072 }
1073
1074
1075 ps = puffs_getspecific(pu);
1076 path = basename_r((char *)PCNPATH(pcn));
1077 len = sizeof(*fmi) + strlen(path) + 1;
1078
1079 /*
1080 * mode must contain file type (ie: S_IFREG), use VTTOIF(vap->va_type)
1081 */
1082 pm = ps->ps_new_msg(pu, opc, FUSE_MKNOD, len, pcn->pcn_cred);
1083 fmi = GET_INPAYLOAD(ps, pm, fuse_mknod_in);
1084 fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
1085 fmi->rdev = (uint32_t)vap->va_rdev;
1086 fmi->umask = 0; /* Seems unused bu libfuse */
1087 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
1088
1089 return node_mk_common(pu, opc, pni, pcn, pm);
1090 }
1091
1092
1093 int
1094 perfuse_node_open(pu, opc, mode, pcr)
1095 struct puffs_usermount *pu;
1096 puffs_cookie_t opc;
1097 int mode;
1098 const struct puffs_cred *pcr;
1099 {
1100 struct perfuse_state *ps;
1101 struct perfuse_node_data *pnd;
1102 perfuse_msg_t *pm;
1103 mode_t pmode;
1104 mode_t fmode;
1105 int op;
1106 struct fuse_open_in *foi;
1107 struct fuse_open_out *foo;
1108 struct puffs_node *pn;
1109 int error;
1110
1111 ps = puffs_getspecific(pu);
1112 pnd = PERFUSE_NODE_DATA(opc);
1113
1114 pn = (struct puffs_node *)opc;
1115 if (puffs_pn_getvap(pn)->va_type == VDIR) {
1116 op = FUSE_OPENDIR;
1117 pmode = PUFFS_VREAD|PUFFS_VEXEC;
1118 } else {
1119 op = FUSE_OPEN;
1120 if (mode & FWRITE)
1121 pmode = PUFFS_VWRITE|PUFFS_VREAD;
1122 else
1123 pmode = PUFFS_VREAD;
1124 }
1125
1126 /*
1127 * Opening a directory require R-X on the directory
1128 * Opening a file requires R-- for reading, -W- for writing
1129 * In both cases, --X is required on the parent.
1130 */
1131 if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC))
1132 return EACCES;
1133
1134 if (no_access(opc, pcr, pmode))
1135 return EACCES;
1136
1137 /*
1138 * libfuse docs say O_CREAT should not be set.
1139 */
1140 mode &= ~O_CREAT;
1141
1142 /*
1143 * Do not open twice, and do not reopen for reading
1144 * if we already have write handle.
1145 */
1146 if ((mode & FREAD) && (pnd->pnd_flags & PND_RFH))
1147 return 0;
1148 if ((mode & FWRITE) && (pnd->pnd_flags & PND_WFH))
1149 return 0;
1150
1151 /*
1152 * Convert PUFFS mode to FUSE mode: convert FREAD/FWRITE
1153 * to O_RDONLY/O_WRONLY while perserving the other options.
1154 */
1155 fmode = mode & ~(FREAD|FWRITE);
1156 fmode |= (mode & FWRITE) ? O_RDWR : O_RDONLY;
1157
1158 pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr);
1159 foi = GET_INPAYLOAD(ps, pm, fuse_open_in);
1160 foi->flags = fmode;
1161 foi->unused = 0;
1162
1163 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0)
1164 goto out;
1165
1166 foo = GET_OUTPAYLOAD(ps, pm, fuse_open_out);
1167
1168 /*
1169 * Save the file handle in node private data
1170 * so that we can reuse it later
1171 */
1172 perfuse_new_fh((puffs_cookie_t)pn, foo->fh, mode);
1173
1174 #ifdef PERFUSE_DEBUG
1175 if (perfuse_diagflags & PDF_FH)
1176 DPRINTF("%s: opc = %p, file = \"%s\", "
1177 "ino = %"PRId64", %s%sfh = 0x%"PRIx64"\n",
1178 __func__, (void *)opc,
1179 (char *)PNPATH((struct puffs_node *)opc),
1180 pnd->pnd_ino, mode & FREAD ? "r" : "",
1181 mode & FWRITE ? "w" : "", foo->fh);
1182 #endif
1183 out:
1184 ps->ps_destroy_msg(pm);
1185
1186 return error;
1187 }
1188
1189 /* ARGSUSED0 */
1190 int
1191 perfuse_node_close(pu, opc, flags, pcr)
1192 struct puffs_usermount *pu;
1193 puffs_cookie_t opc;
1194 int flags;
1195 const struct puffs_cred *pcr;
1196 {
1197 struct puffs_node *pn;
1198 struct perfuse_node_data *pnd;
1199
1200 pn = (struct puffs_node *)opc;
1201 pnd = PERFUSE_NODE_DATA(opc);
1202
1203 if (!(pnd->pnd_flags & PND_OPEN))
1204 return EBADF;
1205
1206 /*
1207 * The NetBSD kernel will send sync and setattr(mtime, ctime)
1208 * afer a close on a regular file. Some FUSE filesystem will
1209 * assume theses operations are performed on open files. We
1210 * therefore postpone the close operation at reclaim time.
1211 */
1212 if (puffs_pn_getvap(pn)->va_type != VREG)
1213 return node_close_common(pu, opc, flags);
1214
1215 return 0;
1216 }
1217
1218 int
1219 perfuse_node_access(pu, opc, mode, pcr)
1220 struct puffs_usermount *pu;
1221 puffs_cookie_t opc;
1222 int mode;
1223 const struct puffs_cred *pcr;
1224 {
1225 perfuse_msg_t *pm;
1226 struct perfuse_state *ps;
1227 struct fuse_access_in *fai;
1228 int error;
1229
1230 if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
1231 return ENOENT;
1232
1233 /*
1234 * If we previously detected the filesystem does not
1235 * implement access(), short-circuit the call and skip
1236 * to libpffs access() emulation.
1237 */
1238 ps = puffs_getspecific(pu);
1239 if (ps->ps_flags & PS_NO_ACCESS) {
1240 error = ENOSYS;
1241 } else {
1242 pm = ps->ps_new_msg(pu, opc, FUSE_ACCESS, sizeof(*fai), pcr);
1243 fai = GET_INPAYLOAD(ps, pm, fuse_access_in);
1244 fai->mask = mode;
1245
1246 error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN);
1247 ps->ps_destroy_msg(pm);
1248 }
1249
1250 if (error == ENOSYS) {
1251 struct fuse_getattr_in *fgi;
1252 struct fuse_attr_out *fao;
1253
1254 ps->ps_flags |= PS_NO_ACCESS;
1255
1256 pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR,
1257 sizeof(*fgi), NULL);
1258 fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
1259 fgi->getattr_flags = 0;
1260 fgi->dummy = 0;
1261 fgi->fh = perfuse_get_fh(opc, FREAD);
1262
1263 #ifdef PERFUSE_DEBUG
1264 if (perfuse_diagflags & PDF_FH)
1265 DPRINTF("%s: opc = %p, ino = %"PRId64", "
1266 "fh = 0x%"PRIx64"\n", __func__, (void *)opc,
1267 PERFUSE_NODE_DATA(opc)->pnd_ino, fgi->fh);
1268 #endif
1269 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0) {
1270 ps->ps_destroy_msg(pm);
1271 goto out;
1272 }
1273
1274 fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
1275
1276 error = puffs_access(VREG, fao->attr.mode, fao->attr.uid,
1277 fao->attr.gid, (mode_t)mode, pcr);
1278
1279 ps->ps_destroy_msg(pm);
1280 }
1281
1282 out:
1283 return error;
1284 }
1285
1286 int
1287 perfuse_node_getattr(pu, opc, vap, pcr)
1288 struct puffs_usermount *pu;
1289 puffs_cookie_t opc;
1290 struct vattr *vap;
1291 const struct puffs_cred *pcr;
1292 {
1293 perfuse_msg_t *pm;
1294 struct perfuse_state *ps;
1295 struct fuse_getattr_in *fgi;
1296 struct fuse_attr_out *fao;
1297 int error;
1298
1299 if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
1300 return ENOENT;
1301
1302 /*
1303 * getattr requires --X on the parent directory
1304 */
1305 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1306 pcr, PUFFS_VEXEC))
1307 return EACCES;
1308
1309 ps = puffs_getspecific(pu);
1310
1311 /*
1312 * FUSE_GETATTR_FH must be set in fgi->flags
1313 * if we use for fgi->fh, but we do not.
1314 */
1315 pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR, sizeof(*fgi), pcr);
1316 fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
1317 fgi->getattr_flags = 0;
1318 fgi->dummy = 0;
1319 fgi->fh = 0;
1320
1321 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0)
1322 goto out;
1323
1324 fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
1325
1326 /*
1327 * The message from filesystem has a cache timeout
1328 * XXX this is ignored yet, is that right?
1329 *
1330 * We also set birthtime, flags, filerev,vaflags to 0.
1331 * This seems the best bet, since the information is
1332 * not available from filesystem.
1333 */
1334 fuse_attr_to_vap(ps, vap, &fao->attr);
1335
1336 out:
1337 ps->ps_destroy_msg(pm);
1338
1339 return error;
1340 }
1341
1342 int
1343 perfuse_node_setattr(pu, opc, vap, pcr)
1344 struct puffs_usermount *pu;
1345 puffs_cookie_t opc;
1346 const struct vattr *vap;
1347 const struct puffs_cred *pcr;
1348 {
1349 perfuse_msg_t *pm;
1350 uint64_t fh;
1351 struct perfuse_state *ps;
1352 struct perfuse_node_data *pnd;
1353 struct fuse_setattr_in *fsi;
1354 int error;
1355 struct vattr *old_va;
1356
1357 ps = puffs_getspecific(pu);
1358 pnd = PERFUSE_NODE_DATA(opc);
1359
1360 /*
1361 * The only operation we can do once the file is removed
1362 * is to resize it, and we can do it only if it is open.
1363 */
1364 if (pnd->pnd_flags & PND_REMOVED) {
1365 if (!(pnd->pnd_flags & PND_OPEN))
1366 return ENOENT;
1367
1368 if (vap->va_size == (u_quad_t)PUFFS_VNOVAL)
1369 return 0;
1370 }
1371
1372 /*
1373 * setattr requires --X on the parent directory
1374 */
1375 if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC))
1376 return EACCES;
1377
1378 old_va = puffs_pn_getvap((struct puffs_node *)opc);
1379
1380 /*
1381 * Check for permission to change size
1382 */
1383 if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) &&
1384 no_access(opc, pcr, PUFFS_VWRITE))
1385 return EACCES;
1386
1387 /*
1388 * Check for permission to change dates
1389 */
1390 if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) ||
1391 (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) &&
1392 (puffs_access_times(old_va->va_uid, old_va->va_gid,
1393 old_va->va_mode, 0, pcr) != 0))
1394 return EACCES;
1395
1396 /*
1397 * Check for permission to change owner and group
1398 */
1399 if (((vap->va_uid != (uid_t)PUFFS_VNOVAL) ||
1400 (vap->va_gid != (gid_t)PUFFS_VNOVAL)) &&
1401 (puffs_access_chown(old_va->va_uid, old_va->va_gid,
1402 vap->va_uid, vap->va_gid, pcr)) != 0)
1403 return EACCES;
1404
1405 /*
1406 * Check for permission to change permissions
1407 */
1408 if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
1409 (puffs_access_chmod(old_va->va_uid, old_va->va_gid,
1410 old_va->va_type, vap->va_mode, pcr)) != 0)
1411 return EACCES;
1412
1413 /*
1414 * It seems troublesome to resize a file while
1415 * a write is just beeing done. Wait for
1416 * it to finish.
1417 */
1418 if (vap->va_size != (u_quad_t)PUFFS_VNOVAL)
1419 while (pnd->pnd_flags & PND_INWRITE)
1420 requeue_request(pu, opc, PCQ_AFTERWRITE);
1421
1422
1423 pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr);
1424 fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
1425 fsi->valid = 0;
1426
1427 if (pnd->pnd_flags & PND_WFH) {
1428 fh = perfuse_get_fh(opc, FWRITE);
1429 fsi->fh = fh;
1430 fsi->valid |= FUSE_FATTR_FH;
1431 }
1432
1433 if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) {
1434 fsi->size = vap->va_size;
1435 fsi->valid |= FUSE_FATTR_SIZE;
1436 }
1437
1438 if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) {
1439 fsi->atime = vap->va_atime.tv_sec;;
1440 fsi->atimensec = (uint32_t)vap->va_atime.tv_nsec;;
1441 fsi->valid |= (FUSE_FATTR_ATIME|FUSE_FATTR_ATIME_NOW);
1442 }
1443
1444 if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) {
1445 fsi->mtime = vap->va_mtime.tv_sec;;
1446 fsi->mtimensec = (uint32_t)vap->va_mtime.tv_nsec;;
1447 fsi->valid |= (FUSE_FATTR_MTIME|FUSE_FATTR_MTIME_NOW);
1448 }
1449
1450 if (vap->va_mode != (mode_t)PUFFS_VNOVAL) {
1451 fsi->mode = vap->va_mode;
1452 fsi->valid |= FUSE_FATTR_MODE;
1453 }
1454
1455 if (vap->va_uid != (uid_t)PUFFS_VNOVAL) {
1456 fsi->uid = vap->va_uid;
1457 fsi->valid |= FUSE_FATTR_UID;
1458 }
1459
1460 if (vap->va_gid != (gid_t)PUFFS_VNOVAL) {
1461 fsi->gid = vap->va_gid;
1462 fsi->valid |= FUSE_FATTR_GID;
1463 }
1464
1465 if (pnd->pnd_lock_owner != 0) {
1466 fsi->lock_owner = pnd->pnd_lock_owner;
1467 fsi->valid |= FUSE_FATTR_LOCKOWNER;
1468 }
1469
1470 /*
1471 * If node was removed, ignore anything but resize
1472 * This works around glusterfs'
1473 * "SETATTR (null) (fuse_loc_fill() failed), ret = -2"
1474 */
1475 if (pnd->pnd_flags & PND_REMOVED)
1476 fsi->valid &=
1477 (FUSE_FATTR_SIZE | FUSE_FATTR_FH | FUSE_FATTR_LOCKOWNER);
1478
1479 /*
1480 * A fuse_attr_out is returned, but we ignore it.
1481 */
1482 error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out));
1483
1484 ps->ps_destroy_msg(pm);
1485
1486 return error;
1487 }
1488
1489 int
1490 perfuse_node_poll(pu, opc, events)
1491 struct puffs_usermount *pu;
1492 puffs_cookie_t opc;
1493 int *events;
1494 {
1495 struct perfuse_state *ps;
1496 perfuse_msg_t *pm;
1497 struct fuse_poll_in *fpi;
1498 struct fuse_poll_out *fpo;
1499 int error;
1500
1501 ps = puffs_getspecific(pu);
1502 /*
1503 * kh is set if FUSE_POLL_SCHEDULE_NOTIFY is set.
1504 */
1505 pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL);
1506 fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in);
1507 fpi->fh = perfuse_get_fh(opc, FREAD);
1508 fpi->kh = 0;
1509 fpi->flags = 0;
1510
1511 #ifdef PERFUSE_DEBUG
1512 if (perfuse_diagflags & PDF_FH)
1513 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
1514 __func__, (void *)opc,
1515 PERFUSE_NODE_DATA(opc)->pnd_ino, fpi->fh);
1516 #endif
1517 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fpo))) != 0)
1518 goto out;
1519
1520 fpo = GET_OUTPAYLOAD(ps, pm, fuse_poll_out);
1521 *events = fpo->revents;
1522 out:
1523 ps->ps_destroy_msg(pm);
1524
1525 return error;
1526 }
1527
1528 /* ARGSUSED0 */
1529 int
1530 perfuse_node_mmap(pu, opc, flags, pcr)
1531 struct puffs_usermount *pu;
1532 puffs_cookie_t opc;
1533 int flags;
1534 const struct puffs_cred *pcr;
1535 {
1536 /*
1537 * Not implemented anymore in libfuse
1538 */
1539 return ENOSYS;
1540 }
1541
1542 /* ARGSUSED2 */
1543 int
1544 perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi)
1545 struct puffs_usermount *pu;
1546 puffs_cookie_t opc;
1547 const struct puffs_cred *pcr;
1548 int flags;
1549 off_t offlo;
1550 off_t offhi;
1551 {
1552 int op;
1553 perfuse_msg_t *pm;
1554 struct perfuse_state *ps;
1555 struct perfuse_node_data *pnd;
1556 struct fuse_fsync_in *ffi;
1557 uint64_t fh;
1558 int open_self;
1559 int error;
1560
1561 pm = NULL;
1562 open_self = 0;
1563 ps = puffs_getspecific(pu);
1564
1565 if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR)
1566 op = FUSE_FSYNCDIR;
1567 else /* VREG but also other types such as VLNK */
1568 op = FUSE_FSYNC;
1569
1570 /*
1571 * Do not sync if there are no change to sync
1572 * XXX remove that test on files if we implement mmap
1573 */
1574 pnd = PERFUSE_NODE_DATA(opc);
1575 #ifdef PERFUSE_DEBUG
1576 if (perfuse_diagflags & PDF_SYNC)
1577 DPRINTF("%s: TEST opc = %p, file = \"%s\" is %sdirty\n",
1578 __func__, (void*)opc,
1579 (char *)PNPATH((struct puffs_node *)opc),
1580 pnd->pnd_flags & PND_DIRTY ? "" : "not ");
1581 #endif
1582 if (!(pnd->pnd_flags & PND_DIRTY))
1583 return 0;
1584
1585 /*
1586 * It seems NetBSD can call fsync without open first
1587 * glusterfs complain in such a situation:
1588 * "FSYNC() ERR => -1 (Invalid argument)"
1589 */
1590 if (!(pnd->pnd_flags & PND_WFH)) {
1591 if ((error = perfuse_node_open(pu, opc, FWRITE, pcr)) != 0)
1592 goto out;
1593 open_self = 1;
1594 }
1595
1596 fh = perfuse_get_fh(opc, FWRITE);
1597
1598 /*
1599 * If fsync_flags is set, meta data should not be flushed.
1600 */
1601 pm = ps->ps_new_msg(pu, opc, op, sizeof(*ffi), NULL);
1602 ffi = GET_INPAYLOAD(ps, pm, fuse_fsync_in);
1603 ffi->fh = fh;
1604 ffi->fsync_flags = (flags & FFILESYNC) ? 0 : 1;
1605
1606 #ifdef PERFUSE_DEBUG
1607 if (perfuse_diagflags & PDF_FH)
1608 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
1609 __func__, (void *)opc,
1610 PERFUSE_NODE_DATA(opc)->pnd_ino, ffi->fh);
1611 #endif
1612
1613 if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
1614 goto out;
1615
1616 /*
1617 * No reply beyond fuse_out_header: nothing to do on success
1618 * just clear the dirty flag
1619 */
1620 pnd->pnd_flags &= ~PND_DIRTY;
1621
1622 #ifdef PERFUSE_DEBUG
1623 if (perfuse_diagflags & PDF_SYNC)
1624 DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
1625 __func__, (void*)opc,
1626 (char *)PNPATH((struct puffs_node *)opc));
1627 #endif
1628
1629 out:
1630 /*
1631 * ENOSYS is not returned to kernel,
1632 */
1633 if (error == ENOSYS)
1634 error = 0;
1635
1636 if (pm != NULL)
1637 ps->ps_destroy_msg(pm);
1638
1639 if (open_self)
1640 (void)node_close_common(pu, opc, FWRITE);
1641
1642 return error;
1643 }
1644
1645 /* ARGSUSED0 */
1646 int
1647 perfuse_node_seek(pu, opc, oldoff, newoff, pcr)
1648 struct puffs_usermount *pu;
1649 puffs_cookie_t opc;
1650 off_t oldoff;
1651 off_t newoff;
1652 const struct puffs_cred *pcr;
1653 {
1654 /*
1655 * XXX what should I do with oldoff?
1656 * XXX where is the newoffset returned?
1657 * XXX the held seek pointer seems just unused
1658 */
1659 PERFUSE_NODE_DATA(opc)->pnd_offset = newoff;
1660
1661 return 0;
1662 }
1663
1664 int
1665 perfuse_node_remove(pu, opc, targ, pcn)
1666 struct puffs_usermount *pu;
1667 puffs_cookie_t opc;
1668 puffs_cookie_t targ;
1669 const struct puffs_cn *pcn;
1670 {
1671 struct perfuse_state *ps;
1672 struct puffs_node *pn;
1673 struct perfuse_node_data *pnd;
1674 perfuse_msg_t *pm;
1675 char *path;
1676 const char *name;
1677 size_t len;
1678 int error;
1679
1680 pnd = PERFUSE_NODE_DATA(opc);
1681
1682 /*
1683 * remove requires -WX on the parent directory
1684 * no right required on the object.
1685 */
1686 if (no_access((puffs_cookie_t)pnd->pnd_parent,
1687 pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1688 return EACCES;
1689
1690 if (targ == NULL)
1691 DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__);
1692
1693 ps = puffs_getspecific(pu);
1694 pnd = PERFUSE_NODE_DATA(opc);
1695 pn = (struct puffs_node *)targ;
1696 name = basename_r((char *)PNPATH(pn));
1697 len = strlen(name) + 1;
1698
1699 pm = ps->ps_new_msg(pu, opc, FUSE_UNLINK, len, pcn->pcn_cred);
1700 path = _GET_INPAYLOAD(ps, pm, char *);
1701 (void)strlcpy(path, name, len);
1702
1703 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1704 goto out;
1705
1706 if (puffs_inval_namecache_dir(pu, opc) != 0)
1707 DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
1708
1709 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
1710
1711 PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED;
1712
1713 /*
1714 * Reclaim should take care of decreasing pnd_childcount
1715 */
1716
1717 /*
1718 * The parent directory needs a sync
1719 */
1720 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
1721 out:
1722 ps->ps_destroy_msg(pm);
1723
1724 return error;
1725 }
1726
1727 int
1728 perfuse_node_link(pu, opc, targ, pcn)
1729 struct puffs_usermount *pu;
1730 puffs_cookie_t opc;
1731 puffs_cookie_t targ;
1732 const struct puffs_cn *pcn;
1733 {
1734 struct perfuse_state *ps;
1735 perfuse_msg_t *pm;
1736 const char *name;
1737 size_t len;
1738 struct puffs_node *pn;
1739 struct fuse_link_in *fli;
1740 int error;
1741
1742 /*
1743 * Create an object require -WX permission in the parent directory
1744 */
1745 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1746 return EACCES;
1747
1748
1749 ps = puffs_getspecific(pu);
1750 pn = (struct puffs_node *)targ;
1751 name = basename_r((char *)PCNPATH(pcn));
1752 len = sizeof(*fli) + strlen(name) + 1;
1753
1754 pm = ps->ps_new_msg(pu, opc, FUSE_LINK, len, pcn->pcn_cred);
1755 fli = GET_INPAYLOAD(ps, pm, fuse_link_in);
1756 fli->oldnodeid = PERFUSE_NODE_DATA(pn)->pnd_ino;
1757 (void)strlcpy((char *)(void *)(fli + 1), name, len - sizeof(*fli));
1758
1759 error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
1760
1761 ps->ps_destroy_msg(pm);
1762
1763 return error;
1764 }
1765
1766 /* targ is unused since the name is in pcn_targ */
1767 /* ARGSUSED5 */
1768 int
1769 perfuse_node_rename(pu, opc, src, pcn_src, targ_dir, targ, pcn_targ)
1770 struct puffs_usermount *pu;
1771 puffs_cookie_t opc;
1772 puffs_cookie_t src;
1773 const struct puffs_cn *pcn_src;
1774 puffs_cookie_t targ_dir;
1775 puffs_cookie_t targ;
1776 const struct puffs_cn *pcn_targ;
1777 {
1778 struct perfuse_state *ps;
1779 perfuse_msg_t *pm;
1780 struct fuse_rename_in *fri;
1781 const char *newname;
1782 const char *oldname;
1783 char *np;
1784 int error;
1785 size_t len;
1786 size_t newname_len;
1787 size_t oldname_len;
1788
1789 /*
1790 * move requires -WX on source and destination directory
1791 */
1792 if (no_access(opc, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC) ||
1793 no_access(targ_dir, pcn_targ->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1794 return EACCES;
1795
1796 ps = puffs_getspecific(pu);
1797 newname = basename_r((char *)PCNPATH(pcn_targ));
1798 newname_len = strlen(newname) + 1;
1799 oldname = basename_r((char *)PCNPATH(pcn_src));
1800 oldname_len = strlen(oldname) + 1;
1801
1802 len = sizeof(*fri) + oldname_len + newname_len;
1803 pm = ps->ps_new_msg(pu, opc, FUSE_RENAME, len, pcn_src->pcn_cred);
1804 fri = GET_INPAYLOAD(ps, pm, fuse_rename_in);
1805 fri->newdir = PERFUSE_NODE_DATA(targ_dir)->pnd_ino;
1806 np = (char *)(void *)(fri + 1);
1807 (void)strlcpy(np, oldname, oldname_len);
1808 np += oldname_len;
1809 (void)strlcpy(np, newname, newname_len);
1810
1811 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1812 goto out;
1813
1814 /*
1815 * Update source and destination directories child count
1816 * Update moved object parent directory
1817 * Set dirty flag for source and parent
1818 */
1819 PERFUSE_NODE_DATA(opc)->pnd_childcount--;
1820 PERFUSE_NODE_DATA(targ_dir)->pnd_childcount++;
1821
1822 PERFUSE_NODE_DATA(src)->pnd_parent = targ_dir;
1823
1824 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
1825 PERFUSE_NODE_DATA(targ_dir)->pnd_flags |= PND_DIRTY;
1826 out:
1827 ps->ps_destroy_msg(pm);
1828
1829 return error;
1830 }
1831
1832 int
1833 perfuse_node_mkdir(pu, opc, pni, pcn, vap)
1834 struct puffs_usermount *pu;
1835 puffs_cookie_t opc;
1836 struct puffs_newinfo *pni;
1837 const struct puffs_cn *pcn;
1838 const struct vattr *vap;
1839 {
1840 struct perfuse_state *ps;
1841 perfuse_msg_t *pm;
1842 struct fuse_mkdir_in *fmi;
1843 const char *path;
1844 size_t len;
1845
1846 /*
1847 * Create an object require -WX permission in the parent directory
1848 */
1849 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1850 return EACCES;
1851
1852 ps = puffs_getspecific(pu);
1853 path = basename_r((char *)PCNPATH(pcn));
1854 len = sizeof(*fmi) + strlen(path) + 1;
1855
1856 pm = ps->ps_new_msg(pu, opc, FUSE_MKDIR, len, pcn->pcn_cred);
1857 fmi = GET_INPAYLOAD(ps, pm, fuse_mkdir_in);
1858 fmi->mode = vap->va_mode;
1859 fmi->umask = 0; /* Seems unused by libfuse? */
1860 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
1861
1862 return node_mk_common(pu, opc, pni, pcn, pm);
1863 }
1864
1865
1866 int
1867 perfuse_node_rmdir(pu, opc, targ, pcn)
1868 struct puffs_usermount *pu;
1869 puffs_cookie_t opc;
1870 puffs_cookie_t targ;
1871 const struct puffs_cn *pcn;
1872 {
1873 struct perfuse_state *ps;
1874 struct perfuse_node_data *pnd;
1875 perfuse_msg_t *pm;
1876 struct puffs_node *pn;
1877 char *path;
1878 const char *name;
1879 size_t len;
1880 int error;
1881
1882 pnd = PERFUSE_NODE_DATA(opc);
1883
1884 /*
1885 * remove requires -WX on the parent directory
1886 * no right required on the object.
1887 */
1888 if (no_access((puffs_cookie_t)pnd->pnd_parent,
1889 pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1890 return EACCES;
1891
1892 ps = puffs_getspecific(pu);
1893 pn = (struct puffs_node *)targ;
1894 name = basename_r((char *)PNPATH(pn));
1895 len = strlen(name) + 1;
1896
1897 pm = ps->ps_new_msg(pu, opc, FUSE_RMDIR, len, pcn->pcn_cred);
1898 path = _GET_INPAYLOAD(ps, pm, char *);
1899 (void)strlcpy(path, name, len);
1900
1901 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1902 goto out;
1903
1904 if (puffs_inval_namecache_dir(pu, opc) != 0)
1905 DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
1906
1907 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
1908
1909 PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED;
1910
1911 /*
1912 * The parent directory needs a sync
1913 */
1914 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
1915 out:
1916 ps->ps_destroy_msg(pm);
1917
1918 return error;
1919 }
1920
1921 /* vap is unused */
1922 /* ARGSUSED4 */
1923 int
1924 perfuse_node_symlink(pu, opc, pni, pcn_src, vap, link_target)
1925 struct puffs_usermount *pu;
1926 puffs_cookie_t opc;
1927 struct puffs_newinfo *pni;
1928 const struct puffs_cn *pcn_src;
1929 const struct vattr *vap;
1930 const char *link_target;
1931 {
1932 struct perfuse_state *ps;
1933 perfuse_msg_t *pm;
1934 char *np;
1935 const char *path;
1936 size_t path_len;
1937 size_t linkname_len;
1938 size_t len;
1939
1940 /*
1941 * Create an object require -WX permission in the parent directory
1942 */
1943 if (no_access(opc, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1944 return EACCES;
1945
1946 ps = puffs_getspecific(pu);
1947 path = basename_r((char *)PCNPATH(pcn_src));
1948 path_len = strlen(path) + 1;
1949 linkname_len = strlen(link_target) + 1;
1950 len = path_len + linkname_len;
1951
1952 pm = ps->ps_new_msg(pu, opc, FUSE_SYMLINK, len, pcn_src->pcn_cred);
1953 np = _GET_INPAYLOAD(ps, pm, char *);
1954 (void)strlcpy(np, path, path_len);
1955 np += path_len;
1956 (void)strlcpy(np, link_target, linkname_len);
1957
1958 return node_mk_common(pu, opc, pni, pcn_src, pm);
1959 }
1960
1961 int
1962 perfuse_node_readdir(pu, opc, dent, readoff,
1963 reslen, pcr, eofflag, cookies, ncookies)
1964 struct puffs_usermount *pu;
1965 puffs_cookie_t opc;
1966 struct dirent *dent;
1967 off_t *readoff;
1968 size_t *reslen;
1969 const struct puffs_cred *pcr;
1970 int *eofflag;
1971 off_t *cookies;
1972 size_t *ncookies;
1973 {
1974 perfuse_msg_t *pm;
1975 uint64_t fh;
1976 struct perfuse_state *ps;
1977 struct perfuse_node_data *pnd;
1978 struct fuse_read_in *fri;
1979 struct fuse_out_header *foh;
1980 struct fuse_dirent *fd;
1981 size_t foh_len;
1982 int error;
1983 int open_self;
1984 uint64_t fd_offset;
1985
1986 pm = NULL;
1987 error = 0;
1988 open_self = 0;
1989 ps = puffs_getspecific(pu);
1990
1991 /*
1992 * readdir state is kept at node level, and several readdir
1993 * requests can be issued at the same time on the same node.
1994 * We need to queue requests so that only one is in readdir
1995 * code at the same time.
1996 */
1997 pnd = PERFUSE_NODE_DATA(opc);
1998 while (pnd->pnd_flags & PND_INREADDIR)
1999 requeue_request(pu, opc, PCQ_READDIR);
2000 pnd->pnd_flags |= PND_INREADDIR;
2001
2002 #ifdef PERFUSE_DEBUG
2003 if (perfuse_diagflags & PDF_READDIR)
2004 DPRINTF("%s: READDIR opc = %p enter critical section\n",
2005 __func__, (void *)opc);
2006 #endif
2007 /*
2008 * Do we already have the data bufered?
2009 */
2010 if (pnd->pnd_dirent != NULL)
2011 goto out;
2012 pnd->pnd_dirent_len = 0;
2013
2014 /*
2015 * It seems NetBSD can call readdir without open first
2016 * libfuse will crash if it is done that way, hence open first.
2017 */
2018 if (!(pnd->pnd_flags & PND_OPEN)) {
2019 if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
2020 goto out;
2021 open_self = 1;
2022 }
2023
2024 fh = perfuse_get_fh(opc, FREAD);
2025
2026 #ifdef PERFUSE_DEBUG
2027 if (perfuse_diagflags & PDF_FH)
2028 DPRINTF("%s: opc = %p, ino = %"PRId64", rfh = 0x%"PRIx64"\n",
2029 __func__, (void *)opc,
2030 PERFUSE_NODE_DATA(opc)->pnd_ino, fh);
2031 #endif
2032
2033 pnd->pnd_all_fd = NULL;
2034 pnd->pnd_all_fd_len = 0;
2035 fd_offset = 0;
2036
2037 do {
2038 size_t fd_len;
2039 char *afdp;
2040
2041 pm = ps->ps_new_msg(pu, opc, FUSE_READDIR, sizeof(*fri), pcr);
2042
2043 /*
2044 * read_flags, lock_owner and flags are unused in libfuse
2045 *
2046 * XXX if fri->size is too big (bigger than PAGE_SIZE?), * we get strange bugs. ktrace shows 16 bytes or garbage
2047 * at the end of sent frames, but perfused does not receive
2048 * that data. The data length is hoverver the same, which
2049 * cause perfused to use the last 16 bytes of the frame
2050 * as the frame header of the next frame.
2051 *
2052 * This may be a kernel bug.
2053 */
2054 fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
2055 fri->fh = fh;
2056 fri->offset = fd_offset;
2057 fri->size = PAGE_SIZE - sizeof(struct fuse_out_header);
2058 fri->read_flags = 0;
2059 fri->lock_owner = 0;
2060 fri->flags = 0;
2061
2062 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
2063 goto out;
2064
2065 /*
2066 * There are many puffs_framebufs calls later,
2067 * therefore foh will not be valid for a long time.
2068 * Just get the length and forget it.
2069 */
2070 foh = GET_OUTHDR(ps, pm);
2071 foh_len = foh->len;
2072
2073 /*
2074 * It seems that the only way to discover the end
2075 * of the buffer is to get an empty read
2076 */
2077 if (foh_len == sizeof(*foh))
2078 break;
2079
2080 /*
2081 * Corrupted message.
2082 */
2083 if (foh_len < sizeof(*foh) + sizeof(*fd)) {
2084 DWARNX("readdir reply too short");
2085 error = EIO;
2086 goto out;
2087 }
2088
2089
2090 fd = GET_OUTPAYLOAD(ps, pm, fuse_dirent);
2091 fd_len = foh_len - sizeof(*foh);
2092
2093 pnd->pnd_all_fd = realloc(pnd->pnd_all_fd,
2094 pnd->pnd_all_fd_len + fd_len);
2095 if (pnd->pnd_all_fd == NULL)
2096 DERR(EX_OSERR, "malloc failed");
2097
2098 afdp = (char *)(void *)pnd->pnd_all_fd + pnd->pnd_all_fd_len;
2099 (void)memcpy(afdp, fd, fd_len);
2100
2101 pnd->pnd_all_fd_len += fd_len;
2102 fd_offset += fd_len;
2103
2104 ps->ps_destroy_msg(pm);
2105 pm = NULL;
2106 } while (1 /* CONSTCOND */);
2107
2108 if (fuse_to_dirent(pu, opc, pnd->pnd_all_fd, pnd->pnd_all_fd_len) == -1)
2109 error = EIO;
2110
2111 out:
2112 if (pnd->pnd_all_fd != NULL) {
2113 free(pnd->pnd_all_fd);
2114 pnd->pnd_all_fd = NULL;
2115 pnd->pnd_all_fd_len = 0;
2116 }
2117
2118 if (pm != NULL)
2119 ps->ps_destroy_msg(pm);
2120
2121 /*
2122 * If we opened the directory ourselves, close now
2123 * errors are ignored.
2124 */
2125 if (open_self)
2126 (void)perfuse_node_close(pu, opc, FWRITE, pcr);
2127
2128 if (error == 0)
2129 error = readdir_buffered(ps, opc, dent, readoff,
2130 reslen, pcr, eofflag, cookies, ncookies);
2131
2132 /*
2133 * Schedule queued readdir requests
2134 */
2135 pnd->pnd_flags &= ~PND_INREADDIR;
2136 (void)dequeue_requests(ps, opc, PCQ_READDIR, DEQUEUE_ALL);
2137
2138 #ifdef PERFUSE_DEBUG
2139 if (perfuse_diagflags & PDF_READDIR)
2140 DPRINTF("%s: READDIR opc = %p exit critical section\n",
2141 __func__, (void *)opc);
2142 #endif
2143
2144 return error;
2145 }
2146
2147 int
2148 perfuse_node_readlink(pu, opc, pcr, linkname, linklen)
2149 struct puffs_usermount *pu;
2150 puffs_cookie_t opc;
2151 const struct puffs_cred *pcr;
2152 char *linkname;
2153 size_t *linklen;
2154 {
2155 struct perfuse_state *ps;
2156 perfuse_msg_t *pm;
2157 int error;
2158 size_t len;
2159 struct fuse_out_header *foh;
2160
2161 /*
2162 * --X required on parent, R-- required on link
2163 */
2164 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
2165 pcr, PUFFS_VEXEC) ||
2166 no_access(opc, pcr, PUFFS_VREAD))
2167 return EACCES;
2168
2169 ps = puffs_getspecific(pu);
2170
2171 pm = ps->ps_new_msg(pu, opc, FUSE_READLINK, 0, pcr);
2172
2173 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
2174 goto out;
2175
2176 foh = GET_OUTHDR(ps, pm);
2177 len = foh->len - sizeof(*foh) + 1;
2178 if (len > *linklen)
2179 DERRX(EX_PROTOCOL, "path len = %zd too long", len);
2180
2181 *linklen = len;
2182 (void)strlcpy(linkname, _GET_OUTPAYLOAD(ps, pm, char *), len);
2183 out:
2184 ps->ps_destroy_msg(pm);
2185
2186 return error;
2187 }
2188
2189 int
2190 perfuse_node_reclaim(pu, opc)
2191 struct puffs_usermount *pu;
2192 puffs_cookie_t opc;
2193 {
2194 struct perfuse_state *ps;
2195 perfuse_msg_t *pm;
2196 struct perfuse_node_data *pnd;
2197 struct fuse_forget_in *ffi;
2198 struct puffs_node *pn;
2199 struct puffs_node *pn_root;
2200
2201 ps = puffs_getspecific(pu);
2202 pnd = PERFUSE_NODE_DATA(opc);
2203
2204 /*
2205 * Never forget the root.
2206 */
2207 if (pnd->pnd_ino == FUSE_ROOT_ID)
2208 return 0;
2209
2210 pnd->pnd_flags |= PND_RECLAIMED;
2211
2212 #ifdef PERFUSE_DEBUG
2213 if (perfuse_diagflags & PDF_RECLAIM)
2214 DPRINTF("%s (nodeid %"PRId64") reclaimed\n",
2215 (char *)PNPATH((struct puffs_node *)opc), pnd->pnd_ino);
2216 #endif
2217
2218 pn_root = puffs_getroot(pu);
2219 pn = (struct puffs_node *)opc;
2220 while (pn != pn_root) {
2221 struct puffs_node *parent_pn;
2222
2223 pnd = PERFUSE_NODE_DATA(pn);
2224
2225 #ifdef PERFUSE_DEBUG
2226 if (perfuse_diagflags & PDF_RECLAIM)
2227 DPRINTF("%s (nodeid %"PRId64") is %sreclaimed, "
2228 "has childcount %d %s%s%s, pending ops:%s%s%s\n",
2229 (char *)PNPATH(pn), pnd->pnd_ino,
2230 pnd->pnd_flags & PND_RECLAIMED ? "" : "not ",
2231 pnd->pnd_childcount,
2232 pnd->pnd_flags & PND_OPEN ? "open " : "not open",
2233 pnd->pnd_flags & PND_RFH ? "r" : "",
2234 pnd->pnd_flags & PND_WFH ? "w" : "",
2235 pnd->pnd_flags & PND_BUSY ? "" : " none",
2236 pnd->pnd_flags & PND_INREADDIR ? " readdir" : "",
2237 pnd->pnd_flags & PND_INWRITE ? " write" : "");
2238 #endif
2239
2240 if (!(pnd->pnd_flags & PND_RECLAIMED) ||
2241 (pnd->pnd_childcount != 0))
2242 return 0;
2243
2244 /*
2245 * Make sure all operation are finished
2246 * There can be an ongoing write, or queued operations
2247 */
2248 while (pnd->pnd_flags & PND_INWRITE) {
2249 requeue_request(pu, opc, PCQ_AFTERWRITE);
2250
2251 /*
2252 * reclaim may have been cancelled in the meantime
2253 * if the file as been look'ed up again.
2254 */
2255 if (!(pnd->pnd_flags & PND_RECLAIMED))
2256 return 0;
2257 }
2258
2259 #ifdef PERFUSE_DEBUG
2260 if ((pnd->pnd_flags & (PND_INREADDIR|PND_INWRITE)) ||
2261 !TAILQ_EMPTY(&pnd->pnd_pcq))
2262 DERRX(EX_SOFTWARE, "%s: opc = %p: ongoing operations",
2263 __func__, (void *)opc);
2264 #endif
2265
2266 /*
2267 * Close open files
2268 */
2269 if (pnd->pnd_flags & PND_WFH)
2270 (void)node_close_common(pu, opc, FWRITE);
2271
2272 if (pnd->pnd_flags & PND_RFH)
2273 (void)node_close_common(pu, opc, FREAD);
2274
2275 /*
2276 * And send the FORGET message
2277 */
2278 pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, FUSE_FORGET,
2279 sizeof(*ffi), NULL);
2280 ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in);
2281 ffi->nlookup = pnd->pnd_nlookup;
2282
2283 /*
2284 * No reply is expected, pm is freed in XCHG_MSG
2285 */
2286 (void)XCHG_MSG_NOREPLY(ps, pu, pm, UNSPEC_REPLY_LEN);
2287
2288 parent_pn = pnd->pnd_parent;
2289
2290 perfuse_destroy_pn(pn);
2291 puffs_pn_put(pn);
2292
2293 pn = parent_pn;
2294 }
2295
2296 return 0;
2297 }
2298
2299 /* ARGSUSED0 */
2300 int
2301 perfuse_node_inactive(pu, opc)
2302 struct puffs_usermount *pu;
2303 puffs_cookie_t opc;
2304 {
2305 return 0;
2306 }
2307
2308
2309 /* ARGSUSED0 */
2310 int
2311 perfuse_node_print(pu, opc)
2312 struct puffs_usermount *pu;
2313 puffs_cookie_t opc;
2314 {
2315 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
2316 return 0;
2317 }
2318
2319 /* ARGSUSED0 */
2320 int
2321 perfuse_node_pathconf(pu, opc, name, retval)
2322 struct puffs_usermount *pu;
2323 puffs_cookie_t opc;
2324 int name;
2325 int *retval;
2326 {
2327 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
2328 return 0;
2329 }
2330
2331 /* id is unused */
2332 /* ARGSUSED2 */
2333 int
2334 perfuse_node_advlock(pu, opc, id, op, fl, flags)
2335 struct puffs_usermount *pu;
2336 puffs_cookie_t opc;
2337 void *id;
2338 int op;
2339 struct flock *fl;
2340 int flags;
2341 {
2342 struct perfuse_state *ps;
2343 int fop;
2344 perfuse_msg_t *pm;
2345 struct fuse_lk_in *fli;
2346 struct fuse_lk_out *flo;
2347 int error;
2348
2349 ps = puffs_getspecific(pu);
2350
2351 if (op == F_GETLK)
2352 fop = FUSE_GETLK;
2353 else
2354 fop = (flags & F_WAIT) ? FUSE_SETLKW : FUSE_SETLK;
2355
2356 pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL);
2357 fli = GET_INPAYLOAD(ps, pm, fuse_lk_in);
2358 fli->fh = perfuse_get_fh(opc, FWRITE);
2359 fli->owner = fl->l_pid;
2360 fli->lk.start = fl->l_start;
2361 fli->lk.end = fl->l_start + fl->l_len;
2362 fli->lk.type = fl->l_type;
2363 fli->lk.pid = fl->l_pid;
2364 fli->lk_flags = (flags & F_FLOCK) ? FUSE_LK_FLOCK : 0;
2365
2366 #ifdef PERFUSE_DEBUG
2367 if (perfuse_diagflags & PDF_FH)
2368 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
2369 __func__, (void *)opc,
2370 PERFUSE_NODE_DATA(opc)->pnd_ino, fli->fh);
2371 #endif
2372
2373 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*flo))) != 0)
2374 goto out;
2375
2376 flo = GET_OUTPAYLOAD(ps, pm, fuse_lk_out);
2377 fl->l_start = flo->lk.start;
2378 fl->l_len = flo->lk.end - flo->lk.start;
2379 fl->l_pid = flo->lk.pid;
2380 fl->l_type = flo->lk.type;
2381 fl->l_whence = SEEK_SET; /* libfuse hardcodes it */
2382
2383 /*
2384 * Save or clear the lock
2385 */
2386 switch (op) {
2387 case F_SETLK:
2388 PERFUSE_NODE_DATA(opc)->pnd_lock_owner = flo->lk.pid;
2389 break;
2390 case F_UNLCK:
2391 PERFUSE_NODE_DATA(opc)->pnd_lock_owner = 0;
2392 break;
2393 default:
2394 break;
2395 }
2396
2397 out:
2398 ps->ps_destroy_msg(pm);
2399
2400 return error;
2401 }
2402
2403 int
2404 perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag)
2405 struct puffs_usermount *pu;
2406 puffs_cookie_t opc;
2407 uint8_t *buf;
2408 off_t offset;
2409 size_t *resid;
2410 const struct puffs_cred *pcr;
2411 int ioflag;
2412 {
2413 struct perfuse_state *ps;
2414 struct perfuse_node_data *pnd;
2415 perfuse_msg_t *pm;
2416 struct fuse_read_in *fri;
2417 struct fuse_out_header *foh;
2418 size_t readen;
2419 size_t requested;
2420 int error;
2421
2422 ps = puffs_getspecific(pu);
2423 pnd = PERFUSE_NODE_DATA(opc);
2424 pm = NULL;
2425
2426 if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR)
2427 return EBADF;
2428
2429 requested = *resid;
2430 if ((ps->ps_readahead + requested) > ps->ps_max_readahead) {
2431 if (perfuse_diagflags & PDF_REQUEUE)
2432 DPRINTF("readahead = %zd\n", ps->ps_readahead);
2433 requeue_request(pu, opc, PCQ_READ);
2434 }
2435 ps->ps_readahead += requested;
2436
2437 do {
2438 /*
2439 * flags may be set to FUSE_READ_LOCKOWNER
2440 * if lock_owner is provided.
2441 *
2442 * XXX See comment about fri->size in perfuse_node_readdir
2443 * We encounter the same bug here.
2444 */
2445 pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr);
2446 fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
2447 fri->fh = perfuse_get_fh(opc, FREAD);
2448 fri->offset = offset;
2449 fri->size = (uint32_t)MIN(*resid, PAGE_SIZE - sizeof(*foh));
2450 fri->read_flags = 0; /* XXX Unused by libfuse? */
2451 fri->lock_owner = pnd->pnd_lock_owner;
2452 fri->flags = 0;
2453 fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0;
2454
2455 #ifdef PERFUSE_DEBUG
2456 if (perfuse_diagflags & PDF_FH)
2457 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
2458 __func__, (void *)opc, pnd->pnd_ino, fri->fh);
2459 #endif
2460 error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
2461
2462 if (error != 0)
2463 goto out;
2464
2465 foh = GET_OUTHDR(ps, pm);
2466 readen = foh->len - sizeof(*foh);
2467
2468 (void)memcpy(buf, _GET_OUTPAYLOAD(ps, pm, char *), readen);
2469
2470 buf += readen;
2471 offset += readen;
2472 *resid -= readen;
2473
2474 ps->ps_destroy_msg(pm);
2475 pm = NULL;
2476 } while ((*resid != 0) && (readen != 0));
2477
2478 if (ioflag & (IO_SYNC|IO_DSYNC))
2479 ps->ps_syncreads++;
2480 else
2481 ps->ps_asyncreads++;
2482
2483 out:
2484 if (pm != NULL)
2485 ps->ps_destroy_msg(pm);
2486
2487 ps->ps_readahead -= requested;
2488
2489 (void)dequeue_requests(ps, opc, PCQ_READ, 1);
2490
2491 return error;
2492 }
2493
2494 int
2495 perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
2496 struct puffs_usermount *pu;
2497 puffs_cookie_t opc;
2498 uint8_t *buf;
2499 off_t offset;
2500 size_t *resid;
2501 const struct puffs_cred *pcr;
2502 int ioflag;
2503 {
2504 struct perfuse_state *ps;
2505 struct perfuse_node_data *pnd;
2506 perfuse_msg_t *pm;
2507 struct fuse_write_in *fwi;
2508 struct fuse_write_out *fwo;
2509 size_t data_len;
2510 size_t payload_len;
2511 size_t written;
2512 size_t requested;
2513 int error;
2514
2515 ps = puffs_getspecific(pu);
2516 pnd = PERFUSE_NODE_DATA(opc);
2517 pm = NULL;
2518 written = 0;
2519
2520 if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR)
2521 return EBADF;
2522
2523 while (pnd->pnd_flags & PND_INWRITE)
2524 requeue_request(pu, opc, PCQ_WRITE);
2525 pnd->pnd_flags |= PND_INWRITE;
2526
2527
2528 requested = *resid;
2529 if ((ps->ps_write + requested) > ps->ps_max_write) {
2530 if (perfuse_diagflags & PDF_REQUEUE)
2531 DPRINTF("write = %zd\n", ps->ps_write);
2532 requeue_request(pu, opc, PCQ_WRITE);
2533 }
2534 ps->ps_write += requested;
2535
2536 do {
2537 /*
2538 * It seems libfuse does not expects big chunks, so
2539 * send it page per page. The writepage feature is
2540 * probably there to minmize data movement.
2541 * XXX use ps->ps_maxwrite?
2542 */
2543 data_len = MIN(*resid, PAGE_SIZE);
2544 payload_len = data_len + sizeof(*fwi);
2545
2546 /*
2547 * flags may be set to FUSE_WRITE_CACHE (XXX usage?)
2548 * or FUSE_WRITE_LOCKOWNER, if lock_owner is provided.
2549 * write_flags is set to 1 for writepage.
2550 */
2551 pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr);
2552 fwi = GET_INPAYLOAD(ps, pm, fuse_write_in);
2553 fwi->fh = perfuse_get_fh(opc, FWRITE);
2554 fwi->offset = offset;
2555 fwi->size = (uint32_t)data_len;
2556 fwi->write_flags = (fwi->size % PAGE_SIZE) ? 0 : 1;
2557 fwi->lock_owner = pnd->pnd_lock_owner;
2558 fwi->flags = 0;
2559 fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0;
2560 fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE;
2561 (void)memcpy((fwi + 1), buf + written, data_len);
2562
2563 #ifdef PERFUSE_DEBUG
2564 if (perfuse_diagflags & PDF_FH)
2565 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
2566 __func__, (void *)opc, pnd->pnd_ino, fwi->fh);
2567 #endif
2568 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fwo))) != 0)
2569 goto out;
2570
2571 fwo = GET_OUTPAYLOAD(ps, pm, fuse_write_out);
2572 written = fwo->size;
2573 *resid -= written;
2574 offset += written;
2575 buf += written;
2576
2577 ps->ps_destroy_msg(pm);
2578 pm = NULL;
2579 } while (*resid != 0);
2580
2581 /*
2582 * puffs_ops(3) says
2583 * "everything must be written or an error will be generated"
2584 */
2585 if (*resid != 0)
2586 error = EFBIG;
2587
2588 if (ioflag & (IO_SYNC|IO_DSYNC))
2589 ps->ps_syncwrites++;
2590 else
2591 ps->ps_asyncwrites++;
2592
2593 /*
2594 * Remember to sync the file
2595 */
2596 pnd->pnd_flags |= PND_DIRTY;
2597
2598 #ifdef PERFUSE_DEBUG
2599 if (perfuse_diagflags & PDF_SYNC)
2600 DPRINTF("%s: DIRTY opc = %p, file = \"%s\"\n",
2601 __func__, (void*)opc,
2602 (char *)PNPATH((struct puffs_node *)opc));
2603 #endif
2604 out:
2605 if (pm != NULL)
2606 ps->ps_destroy_msg(pm);
2607
2608 ps->ps_write -= requested;
2609
2610
2611 /*
2612 * If there are no more queued write, we can resume
2613 * an operation awaiting write completion.
2614 */
2615 pnd->pnd_flags &= ~PND_INWRITE;
2616 if (dequeue_requests(ps, opc, PCQ_WRITE, 1) == 0)
2617 (void)dequeue_requests(ps, opc, PCQ_AFTERWRITE, DEQUEUE_ALL);
2618
2619 return error;
2620 }
2621
2622 /* ARGSUSED0 */
2623 void
2624 perfuse_cache_write(pu, opc, size, runs)
2625 struct puffs_usermount *pu;
2626 puffs_cookie_t opc;
2627 size_t size;
2628 struct puffs_cacherun *runs;
2629 {
2630 return;
2631 }
2632
2633