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