puffs_vfsops.c revision 1.81.8.3.2.1 1 /* $NetBSD: puffs_vfsops.c,v 1.81.8.3.2.1 2014/04/28 16:05:35 sborrill Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Google Summer of Code program and the Ulla Tuominen Foundation.
8 * The Google SoC project was mentored by Bill Studenmund.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: puffs_vfsops.c,v 1.81.8.3.2.1 2014/04/28 16:05:35 sborrill Exp $");
34
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/malloc.h>
38 #include <sys/extattr.h>
39 #include <sys/queue.h>
40 #include <sys/vnode.h>
41 #include <sys/dirent.h>
42 #include <sys/kauth.h>
43 #include <sys/fstrans.h>
44 #include <sys/proc.h>
45 #include <sys/module.h>
46 #include <sys/kthread.h>
47
48 #include <uvm/uvm.h>
49
50 #include <dev/putter/putter_sys.h>
51
52 #include <miscfs/genfs/genfs.h>
53
54 #include <fs/puffs/puffs_msgif.h>
55 #include <fs/puffs/puffs_sys.h>
56
57 #include <lib/libkern/libkern.h>
58
59 #include <nfs/nfsproto.h> /* for fh sizes */
60
61 MODULE(MODULE_CLASS_VFS, puffs, "putter");
62
63 VFS_PROTOS(puffs_vfsop);
64
65 #ifndef PUFFS_PNODEBUCKETS
66 #define PUFFS_PNODEBUCKETS 256
67 #endif
68 #ifndef PUFFS_MAXPNODEBUCKETS
69 #define PUFFS_MAXPNODEBUCKETS 8192
70 #endif
71 int puffs_pnodebuckets_default = PUFFS_PNODEBUCKETS;
72 int puffs_maxpnodebuckets = PUFFS_MAXPNODEBUCKETS;
73
74 #define BUCKETALLOC(a) (sizeof(struct puffs_pnode_hashlist *) * (a))
75
76 static struct putter_ops puffs_putter = {
77 .pop_getout = puffs_msgif_getout,
78 .pop_releaseout = puffs_msgif_releaseout,
79 .pop_waitcount = puffs_msgif_waitcount,
80 .pop_dispatch = puffs_msgif_dispatch,
81 .pop_close = puffs_msgif_close,
82 };
83
84 int
85 puffs_vfsop_mount(struct mount *mp, const char *path, void *data,
86 size_t *data_len)
87 {
88 struct puffs_mount *pmp = NULL;
89 struct puffs_kargs *args;
90 char fstype[_VFS_NAMELEN];
91 char *p;
92 int error = 0, i;
93 pid_t mntpid = curlwp->l_proc->p_pid;
94
95 if (data == NULL)
96 return EINVAL;
97 if (*data_len < sizeof *args)
98 return EINVAL;
99
100 if (mp->mnt_flag & MNT_GETARGS) {
101 pmp = MPTOPUFFSMP(mp);
102 *(struct puffs_kargs *)data = pmp->pmp_args;
103 *data_len = sizeof *args;
104 return 0;
105 }
106
107 /* update is not supported currently */
108 if (mp->mnt_flag & MNT_UPDATE)
109 return EOPNOTSUPP;
110
111 error = fstrans_mount(mp);
112 if (error)
113 return error;
114 args = (struct puffs_kargs *)data;
115
116 /* devel phase */
117 if (args->pa_vers != (PUFFSVERSION | PUFFSDEVELVERS)) {
118 printf("puffs_mount: development version mismatch: "
119 "kernel %d, lib %d\n",
120 PUFFSVERSION, args->pa_vers & ~PUFFSDEVELVERS);
121 error = EINVAL;
122 goto out;
123 }
124
125 if ((args->pa_flags & ~PUFFS_KFLAG_MASK) != 0) {
126 printf("puffs_mount: invalid KFLAGs 0x%x\n", args->pa_flags);
127 error = EINVAL;
128 goto out;
129 }
130 if ((args->pa_fhflags & ~PUFFS_FHFLAG_MASK) != 0) {
131 printf("puffs_mount: invalid FHFLAGs 0x%x\n", args->pa_fhflags);
132 error = EINVAL;
133 goto out;
134 }
135
136 /* use dummy value for passthrough */
137 if (args->pa_fhflags & PUFFS_FHFLAG_PASSTHROUGH)
138 args->pa_fhsize = sizeof(struct fid);
139
140 /* sanitize file handle length */
141 if (PUFFS_TOFHSIZE(args->pa_fhsize) > FHANDLE_SIZE_MAX) {
142 printf("puffs_mount: handle size %zu too large\n",
143 args->pa_fhsize);
144 error = EINVAL;
145 goto out;
146 }
147 /* sanity check file handle max sizes */
148 if (args->pa_fhsize && args->pa_fhflags & PUFFS_FHFLAG_PROTOMASK) {
149 size_t kfhsize = PUFFS_TOFHSIZE(args->pa_fhsize);
150
151 if (args->pa_fhflags & PUFFS_FHFLAG_NFSV2) {
152 if (NFSX_FHTOOBIG_P(kfhsize, 0)) {
153 printf("puffs_mount: fhsize larger than "
154 "NFSv2 max %d\n",
155 PUFFS_FROMFHSIZE(NFSX_V2FH));
156 error = EINVAL;
157 goto out;
158 }
159 }
160
161 if (args->pa_fhflags & PUFFS_FHFLAG_NFSV3) {
162 if (NFSX_FHTOOBIG_P(kfhsize, 1)) {
163 printf("puffs_mount: fhsize larger than "
164 "NFSv3 max %d\n",
165 PUFFS_FROMFHSIZE(NFSX_V3FHMAX));
166 error = EINVAL;
167 goto out;
168 }
169 }
170 }
171
172 /* don't allow non-printing characters (like my sweet umlauts.. snif) */
173 args->pa_typename[sizeof(args->pa_typename)-1] = '\0';
174 for (p = args->pa_typename; *p; p++)
175 if (*p < ' ' || *p > '~')
176 *p = '.';
177
178 args->pa_mntfromname[sizeof(args->pa_mntfromname)-1] = '\0';
179 for (p = args->pa_mntfromname; *p; p++)
180 if (*p < ' ' || *p > '~')
181 *p = '.';
182
183 /* build real name */
184 (void)strlcpy(fstype, PUFFS_TYPEPREFIX, sizeof(fstype));
185 (void)strlcat(fstype, args->pa_typename, sizeof(fstype));
186
187 /* inform user server if it got the max request size it wanted */
188 if (args->pa_maxmsglen == 0 || args->pa_maxmsglen > PUFFS_MSG_MAXSIZE)
189 args->pa_maxmsglen = PUFFS_MSG_MAXSIZE;
190 else if (args->pa_maxmsglen < 2*PUFFS_MSGSTRUCT_MAX)
191 args->pa_maxmsglen = 2*PUFFS_MSGSTRUCT_MAX;
192
193 (void)strlcpy(args->pa_typename, fstype, sizeof(args->pa_typename));
194
195 if (args->pa_nhashbuckets == 0)
196 args->pa_nhashbuckets = puffs_pnodebuckets_default;
197 if (args->pa_nhashbuckets < 1)
198 args->pa_nhashbuckets = 1;
199 if (args->pa_nhashbuckets > PUFFS_MAXPNODEBUCKETS) {
200 args->pa_nhashbuckets = puffs_maxpnodebuckets;
201 printf("puffs_mount: using %d hash buckets. "
202 "adjust puffs_maxpnodebuckets for more\n",
203 puffs_maxpnodebuckets);
204 }
205
206 error = set_statvfs_info(path, UIO_USERSPACE, args->pa_mntfromname,
207 UIO_SYSSPACE, fstype, mp, curlwp);
208 if (error)
209 goto out;
210 mp->mnt_stat.f_iosize = DEV_BSIZE;
211
212 /*
213 * We can't handle the VFS_STATVFS() mount_domount() does
214 * after VFS_MOUNT() because we'd deadlock, so handle it
215 * here already.
216 */
217 copy_statvfs_info(&args->pa_svfsb, mp);
218 (void)memcpy(&mp->mnt_stat, &args->pa_svfsb, sizeof(mp->mnt_stat));
219
220 KASSERT(curlwp != uvm.pagedaemon_lwp);
221 pmp = kmem_zalloc(sizeof(struct puffs_mount), KM_SLEEP);
222
223 mp->mnt_fs_bshift = DEV_BSHIFT;
224 mp->mnt_dev_bshift = DEV_BSHIFT;
225 mp->mnt_flag &= ~MNT_LOCAL; /* we don't really know, so ... */
226 mp->mnt_data = pmp;
227 mp->mnt_iflag |= IMNT_HAS_TRANS;
228
229 pmp->pmp_status = PUFFSTAT_MOUNTING;
230 pmp->pmp_mp = mp;
231 pmp->pmp_msg_maxsize = args->pa_maxmsglen;
232 pmp->pmp_args = *args;
233
234 pmp->pmp_npnodehash = args->pa_nhashbuckets;
235 pmp->pmp_pnodehash = kmem_alloc(BUCKETALLOC(pmp->pmp_npnodehash), KM_SLEEP);
236 for (i = 0; i < pmp->pmp_npnodehash; i++)
237 LIST_INIT(&pmp->pmp_pnodehash[i]);
238 LIST_INIT(&pmp->pmp_newcookie);
239
240 /*
241 * Inform the fileops processing code that we have a mountpoint.
242 * If it doesn't know about anyone with our pid/fd having the
243 * device open, punt
244 */
245 if ((pmp->pmp_pi
246 = putter_attach(mntpid, args->pa_fd, pmp, &puffs_putter)) == NULL) {
247 error = ENOENT;
248 goto out;
249 }
250
251 /* XXX: check parameters */
252 pmp->pmp_root_cookie = args->pa_root_cookie;
253 pmp->pmp_root_vtype = args->pa_root_vtype;
254 pmp->pmp_root_vsize = args->pa_root_vsize;
255 pmp->pmp_root_rdev = args->pa_root_rdev;
256
257 mutex_init(&pmp->pmp_lock, MUTEX_DEFAULT, IPL_NONE);
258 mutex_init(&pmp->pmp_sopmtx, MUTEX_DEFAULT, IPL_NONE);
259 cv_init(&pmp->pmp_msg_waiter_cv, "puffsget");
260 cv_init(&pmp->pmp_refcount_cv, "puffsref");
261 cv_init(&pmp->pmp_unmounting_cv, "puffsum");
262 cv_init(&pmp->pmp_sopcv, "puffsop");
263 TAILQ_INIT(&pmp->pmp_msg_touser);
264 TAILQ_INIT(&pmp->pmp_msg_replywait);
265 TAILQ_INIT(&pmp->pmp_sopreqs);
266
267 if ((error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
268 puffs_sop_thread, pmp, NULL, "puffsop")) != 0)
269 goto out;
270 pmp->pmp_sopthrcount = 1;
271
272 DPRINTF(("puffs_mount: mount point at %p, puffs specific at %p\n",
273 mp, MPTOPUFFSMP(mp)));
274
275 vfs_getnewfsid(mp);
276
277 out:
278 if (error)
279 fstrans_unmount(mp);
280 if (error && pmp && pmp->pmp_pi)
281 putter_detach(pmp->pmp_pi);
282 if (error && pmp && pmp->pmp_pnodehash)
283 kmem_free(pmp->pmp_pnodehash, BUCKETALLOC(pmp->pmp_npnodehash));
284 if (error && pmp)
285 kmem_free(pmp, sizeof(struct puffs_mount));
286 return error;
287 }
288
289 int
290 puffs_vfsop_start(struct mount *mp, int flags)
291 {
292 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
293
294 KASSERT(pmp->pmp_status == PUFFSTAT_MOUNTING);
295 pmp->pmp_status = PUFFSTAT_RUNNING;
296
297 return 0;
298 }
299
300 int
301 puffs_vfsop_unmount(struct mount *mp, int mntflags)
302 {
303 PUFFS_MSG_VARS(vfs, unmount);
304 struct puffs_mount *pmp;
305 int error, force;
306
307 error = 0;
308 force = mntflags & MNT_FORCE;
309 pmp = MPTOPUFFSMP(mp);
310
311 DPRINTF(("puffs_unmount: detach filesystem from vfs, current "
312 "status 0x%x\n", pmp->pmp_status));
313
314 /*
315 * flush all the vnodes. VOP_RECLAIM() takes care that the
316 * root vnode does not get flushed until unmount. The
317 * userspace root node cookie is stored in the mount
318 * structure, so we can always re-instantiate a root vnode,
319 * should userspace unmount decide it doesn't want to
320 * cooperate.
321 */
322 error = vflush(mp, NULLVP, force ? FORCECLOSE : 0);
323 if (error)
324 goto out;
325
326 /*
327 * If we are not DYING, we should ask userspace's opinion
328 * about the situation
329 */
330 mutex_enter(&pmp->pmp_lock);
331 if (pmp->pmp_status != PUFFSTAT_DYING) {
332 pmp->pmp_unmounting = 1;
333 mutex_exit(&pmp->pmp_lock);
334
335 PUFFS_MSG_ALLOC(vfs, unmount);
336 puffs_msg_setinfo(park_unmount,
337 PUFFSOP_VFS, PUFFS_VFS_UNMOUNT, NULL);
338 unmount_msg->pvfsr_flags = mntflags;
339
340 PUFFS_MSG_ENQUEUEWAIT(pmp, park_unmount, error);
341 PUFFS_MSG_RELEASE(unmount);
342
343 error = checkerr(pmp, error, __func__);
344 DPRINTF(("puffs_unmount: error %d force %d\n", error, force));
345
346 mutex_enter(&pmp->pmp_lock);
347 pmp->pmp_unmounting = 0;
348 cv_broadcast(&pmp->pmp_unmounting_cv);
349 }
350
351 /*
352 * if userspace cooperated or we really need to die,
353 * screw what userland thinks and just die.
354 */
355 if (error == 0 || force) {
356 struct puffs_sopreq *psopr;
357
358 /* tell waiters & other resources to go unwait themselves */
359 puffs_userdead(pmp);
360 putter_detach(pmp->pmp_pi);
361
362 /*
363 * Wait until there are no more users for the mount resource.
364 * Notice that this is hooked against transport_close
365 * and return from touser. In an ideal world, it would
366 * be hooked against final return from all operations.
367 * But currently it works well enough, since nobody
368 * does weird blocking voodoo after return from touser().
369 */
370 while (pmp->pmp_refcount != 0)
371 cv_wait(&pmp->pmp_refcount_cv, &pmp->pmp_lock);
372 mutex_exit(&pmp->pmp_lock);
373
374 /*
375 * Release kernel thread now that there is nothing
376 * it would be wanting to lock.
377 */
378 KASSERT(curlwp != uvm.pagedaemon_lwp);
379 psopr = kmem_alloc(sizeof(*psopr), KM_SLEEP);
380 psopr->psopr_sopreq = PUFFS_SOPREQ_EXIT;
381 mutex_enter(&pmp->pmp_sopmtx);
382 TAILQ_INSERT_TAIL(&pmp->pmp_sopreqs, psopr, psopr_entries);
383 cv_signal(&pmp->pmp_sopcv);
384 while (pmp->pmp_sopthrcount > 0)
385 cv_wait(&pmp->pmp_sopcv, &pmp->pmp_sopmtx);
386 mutex_exit(&pmp->pmp_sopmtx);
387
388 /* free resources now that we hopefully have no waiters left */
389 cv_destroy(&pmp->pmp_unmounting_cv);
390 cv_destroy(&pmp->pmp_refcount_cv);
391 cv_destroy(&pmp->pmp_msg_waiter_cv);
392 cv_destroy(&pmp->pmp_sopcv);
393 mutex_destroy(&pmp->pmp_lock);
394 mutex_destroy(&pmp->pmp_sopmtx);
395
396 fstrans_unmount(mp);
397 kmem_free(pmp->pmp_pnodehash, BUCKETALLOC(pmp->pmp_npnodehash));
398 kmem_free(pmp, sizeof(struct puffs_mount));
399 error = 0;
400 } else {
401 mutex_exit(&pmp->pmp_lock);
402 }
403
404 out:
405 DPRINTF(("puffs_unmount: return %d\n", error));
406 return error;
407 }
408
409 /*
410 * This doesn't need to travel to userspace
411 */
412 int
413 puffs_vfsop_root(struct mount *mp, struct vnode **vpp)
414 {
415 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
416 int rv;
417
418 rv = puffs_cookie2vnode(pmp, pmp->pmp_root_cookie, 1, 1, vpp);
419 KASSERT(rv != PUFFS_NOSUCHCOOKIE);
420 return rv;
421 }
422
423 int
424 puffs_vfsop_statvfs(struct mount *mp, struct statvfs *sbp)
425 {
426 PUFFS_MSG_VARS(vfs, statvfs);
427 struct puffs_mount *pmp;
428 int error = 0;
429
430 pmp = MPTOPUFFSMP(mp);
431
432 /*
433 * If we are mounting, it means that the userspace counterpart
434 * is calling mount(2), but mount(2) also calls statvfs. So
435 * requesting statvfs from userspace would mean a deadlock.
436 * Compensate.
437 */
438 if (pmp->pmp_status == PUFFSTAT_MOUNTING)
439 return EINPROGRESS;
440
441 PUFFS_MSG_ALLOC(vfs, statvfs);
442 puffs_msg_setinfo(park_statvfs, PUFFSOP_VFS, PUFFS_VFS_STATVFS, NULL);
443
444 PUFFS_MSG_ENQUEUEWAIT(pmp, park_statvfs, error);
445 error = checkerr(pmp, error, __func__);
446 statvfs_msg->pvfsr_sb.f_iosize = DEV_BSIZE;
447
448 /*
449 * Try to produce a sensible result even in the event
450 * of userspace error.
451 *
452 * XXX: cache the copy in non-error case
453 */
454 if (!error) {
455 copy_statvfs_info(&statvfs_msg->pvfsr_sb, mp);
456 (void)memcpy(sbp, &statvfs_msg->pvfsr_sb,
457 sizeof(struct statvfs));
458 } else {
459 copy_statvfs_info(sbp, mp);
460 }
461
462 PUFFS_MSG_RELEASE(statvfs);
463 return error;
464 }
465
466 static int
467 pageflush(struct mount *mp, kauth_cred_t cred, int waitfor, int suspending)
468 {
469 struct puffs_node *pn;
470 struct vnode *vp, *mvp;
471 int error, rv, fsyncwait;
472
473 KASSERT(((waitfor == MNT_WAIT) && suspending) == 0);
474 KASSERT((suspending == 0)
475 || (fstrans_is_owner(mp)
476 && fstrans_getstate(mp) == FSTRANS_SUSPENDING));
477
478 error = 0;
479 fsyncwait = (waitfor == MNT_WAIT) ? FSYNC_WAIT : 0;
480
481 /* Allocate a marker vnode. */
482 if ((mvp = vnalloc(mp)) == NULL)
483 return ENOMEM;
484
485 /*
486 * Sync all cached data from regular vnodes (which are not
487 * currently locked, see below). After this we call VFS_SYNC
488 * for the fs server, which should handle data and metadata for
489 * all the nodes it knows to exist.
490 */
491 mutex_enter(&mntvnode_lock);
492 loop:
493 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) {
494 vmark(mvp, vp);
495 if (vp->v_mount != mp || vismarker(vp))
496 continue;
497
498 mutex_enter(&vp->v_interlock);
499 pn = VPTOPP(vp);
500 if (vp->v_type != VREG || UVM_OBJ_IS_CLEAN(&vp->v_uobj)) {
501 mutex_exit(&vp->v_interlock);
502 continue;
503 }
504
505 mutex_exit(&mntvnode_lock);
506
507 /*
508 * Here we try to get a reference to the vnode and to
509 * lock it. This is mostly cargo-culted, but I will
510 * offer an explanation to why I believe this might
511 * actually do the right thing.
512 *
513 * If the vnode is a goner, we quite obviously don't need
514 * to sync it.
515 *
516 * If the vnode was busy, we don't need to sync it because
517 * this is never called with MNT_WAIT except from
518 * dounmount(), when we are wait-flushing all the dirty
519 * vnodes through other routes in any case. So there,
520 * sync() doesn't actually sync. Happy now?
521 *
522 * NOTE: if we're suspending, vget() does NOT lock.
523 * See puffs_lock() for details.
524 */
525 rv = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
526 if (rv) {
527 mutex_enter(&mntvnode_lock);
528 if (rv == ENOENT) {
529 (void)vunmark(mvp);
530 goto loop;
531 }
532 continue;
533 }
534
535 /*
536 * Thread information to puffs_strategy() through the
537 * pnode flags: we want to issue the putpages operations
538 * as FAF if we're suspending, since it's very probable
539 * that our execution context is that of the userspace
540 * daemon. We can do this because:
541 * + we send the "going to suspend" prior to this part
542 * + if any of the writes fails in userspace, it's the
543 * file system server's problem to decide if this was a
544 * failed snapshot when it gets the "snapshot complete"
545 * notification.
546 * + if any of the writes fail in the kernel already, we
547 * immediately fail *and* notify the user server of
548 * failure.
549 *
550 * We also do FAFs if we're called from the syncer. This
551 * is just general optimization for trickle sync: no need
552 * to really guarantee that the stuff ended on backing
553 * storage.
554 * TODO: Maybe also hint the user server of this twist?
555 */
556 if (suspending || waitfor == MNT_LAZY) {
557 mutex_enter(&vp->v_interlock);
558 pn->pn_stat |= PNODE_SUSPEND;
559 mutex_exit(&vp->v_interlock);
560 }
561 rv = VOP_FSYNC(vp, cred, fsyncwait, 0, 0);
562 if (suspending || waitfor == MNT_LAZY) {
563 mutex_enter(&vp->v_interlock);
564 pn->pn_stat &= ~PNODE_SUSPEND;
565 mutex_exit(&vp->v_interlock);
566 }
567 if (rv)
568 error = rv;
569 vput(vp);
570 mutex_enter(&mntvnode_lock);
571 }
572 mutex_exit(&mntvnode_lock);
573 vnfree(mvp);
574
575 return error;
576 }
577
578 int
579 puffs_vfsop_sync(struct mount *mp, int waitfor, struct kauth_cred *cred)
580 {
581 PUFFS_MSG_VARS(vfs, sync);
582 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
583 int error, rv;
584
585 error = pageflush(mp, cred, waitfor, 0);
586
587 /* sync fs */
588 PUFFS_MSG_ALLOC(vfs, sync);
589 sync_msg->pvfsr_waitfor = waitfor;
590 puffs_credcvt(&sync_msg->pvfsr_cred, cred);
591 puffs_msg_setinfo(park_sync, PUFFSOP_VFS, PUFFS_VFS_SYNC, NULL);
592
593 PUFFS_MSG_ENQUEUEWAIT(pmp, park_sync, rv);
594 rv = checkerr(pmp, rv, __func__);
595 if (rv)
596 error = rv;
597
598 PUFFS_MSG_RELEASE(sync);
599 return error;
600 }
601
602 int
603 puffs_vfsop_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
604 {
605 PUFFS_MSG_VARS(vfs, fhtonode);
606 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
607 struct vnode *vp;
608 void *fhdata;
609 size_t argsize, fhlen;
610 int error;
611
612 if (pmp->pmp_args.pa_fhsize == 0)
613 return EOPNOTSUPP;
614
615 if (pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_PASSTHROUGH) {
616 fhlen = fhp->fid_len;
617 fhdata = fhp;
618 } else {
619 fhlen = PUFFS_FROMFHSIZE(fhp->fid_len);
620 fhdata = fhp->fid_data;
621
622 if (pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_DYNAMIC) {
623 if (pmp->pmp_args.pa_fhsize < fhlen)
624 return EINVAL;
625 } else {
626 if (pmp->pmp_args.pa_fhsize != fhlen)
627 return EINVAL;
628 }
629 }
630
631 argsize = sizeof(struct puffs_vfsmsg_fhtonode) + fhlen;
632 puffs_msgmem_alloc(argsize, &park_fhtonode, (void *)&fhtonode_msg, 1);
633 fhtonode_msg->pvfsr_dsize = fhlen;
634 memcpy(fhtonode_msg->pvfsr_data, fhdata, fhlen);
635 puffs_msg_setinfo(park_fhtonode, PUFFSOP_VFS, PUFFS_VFS_FHTOVP, NULL);
636
637 PUFFS_MSG_ENQUEUEWAIT(pmp, park_fhtonode, error);
638 error = checkerr(pmp, error, __func__);
639 if (error)
640 goto out;
641
642 error = puffs_cookie2vnode(pmp, fhtonode_msg->pvfsr_fhcookie, 1,1,&vp);
643 DPRINTF(("puffs_fhtovp: got cookie %p, existing vnode %p\n",
644 fhtonode_msg->pvfsr_fhcookie, vp));
645 if (error == PUFFS_NOSUCHCOOKIE) {
646 error = puffs_getvnode(mp, fhtonode_msg->pvfsr_fhcookie,
647 fhtonode_msg->pvfsr_vtype, fhtonode_msg->pvfsr_size,
648 fhtonode_msg->pvfsr_rdev, &vp);
649 if (error)
650 goto out;
651 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
652 } else if (error) {
653 goto out;
654 }
655
656 *vpp = vp;
657 out:
658 puffs_msgmem_release(park_fhtonode);
659 return error;
660 }
661
662 int
663 puffs_vfsop_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
664 {
665 PUFFS_MSG_VARS(vfs, nodetofh);
666 struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
667 size_t argsize, fhlen;
668 int error;
669
670 if (pmp->pmp_args.pa_fhsize == 0)
671 return EOPNOTSUPP;
672
673 /* if file handles are static len, we can test len immediately */
674 if (((pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_DYNAMIC) == 0)
675 && ((pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_PASSTHROUGH) == 0)
676 && (PUFFS_FROMFHSIZE(*fh_size) < pmp->pmp_args.pa_fhsize)) {
677 *fh_size = PUFFS_TOFHSIZE(pmp->pmp_args.pa_fhsize);
678 return E2BIG;
679 }
680
681 if (pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_PASSTHROUGH)
682 fhlen = *fh_size;
683 else
684 fhlen = PUFFS_FROMFHSIZE(*fh_size);
685
686 argsize = sizeof(struct puffs_vfsmsg_nodetofh) + fhlen;
687 puffs_msgmem_alloc(argsize, &park_nodetofh, (void *)&nodetofh_msg, 1);
688 nodetofh_msg->pvfsr_fhcookie = VPTOPNC(vp);
689 nodetofh_msg->pvfsr_dsize = fhlen;
690 puffs_msg_setinfo(park_nodetofh, PUFFSOP_VFS, PUFFS_VFS_VPTOFH, NULL);
691
692 PUFFS_MSG_ENQUEUEWAIT(pmp, park_nodetofh, error);
693 error = checkerr(pmp, error, __func__);
694
695 if (pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_PASSTHROUGH)
696 fhlen = nodetofh_msg->pvfsr_dsize;
697 else if (pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_DYNAMIC)
698 fhlen = PUFFS_TOFHSIZE(nodetofh_msg->pvfsr_dsize);
699 else
700 fhlen = PUFFS_TOFHSIZE(pmp->pmp_args.pa_fhsize);
701
702 if (error) {
703 if (error == E2BIG)
704 *fh_size = fhlen;
705 goto out;
706 }
707
708 if (fhlen > FHANDLE_SIZE_MAX) {
709 puffs_senderr(pmp, PUFFS_ERR_VPTOFH, E2BIG,
710 "file handle too big", VPTOPNC(vp));
711 error = EPROTO;
712 goto out;
713 }
714
715 if (*fh_size < fhlen) {
716 *fh_size = fhlen;
717 error = E2BIG;
718 goto out;
719 }
720 *fh_size = fhlen;
721
722 if (fhp) {
723 if (pmp->pmp_args.pa_fhflags & PUFFS_FHFLAG_PASSTHROUGH) {
724 memcpy(fhp, nodetofh_msg->pvfsr_data, fhlen);
725 } else {
726 fhp->fid_len = *fh_size;
727 memcpy(fhp->fid_data, nodetofh_msg->pvfsr_data,
728 nodetofh_msg->pvfsr_dsize);
729 }
730 }
731
732 out:
733 puffs_msgmem_release(park_nodetofh);
734 return error;
735 }
736
737 void
738 puffs_vfsop_init()
739 {
740
741 /* some checks depend on this */
742 KASSERT(VNOVAL == VSIZENOTSET);
743
744 pool_init(&puffs_pnpool, sizeof(struct puffs_node), 0, 0, 0,
745 "puffpnpl", &pool_allocator_nointr, IPL_NONE);
746 puffs_msgif_init();
747 }
748
749 void
750 puffs_vfsop_done()
751 {
752
753 puffs_msgif_destroy();
754 pool_destroy(&puffs_pnpool);
755 }
756
757 int
758 puffs_vfsop_snapshot(struct mount *mp, struct vnode *vp, struct timespec *ts)
759 {
760
761 return EOPNOTSUPP;
762 }
763
764 int
765 puffs_vfsop_suspendctl(struct mount *mp, int cmd)
766 {
767 PUFFS_MSG_VARS(vfs, suspend);
768 struct puffs_mount *pmp;
769 int error;
770
771 pmp = MPTOPUFFSMP(mp);
772 switch (cmd) {
773 case SUSPEND_SUSPEND:
774 DPRINTF(("puffs_suspendctl: suspending\n"));
775 if ((error = fstrans_setstate(mp, FSTRANS_SUSPENDING)) != 0)
776 break;
777 PUFFS_MSG_ALLOC(vfs, suspend);
778 puffs_msg_setfaf(park_suspend);
779 suspend_msg->pvfsr_status = PUFFS_SUSPEND_START;
780 puffs_msg_setinfo(park_suspend, PUFFSOP_VFS,
781 PUFFS_VFS_SUSPEND, NULL);
782
783 puffs_msg_enqueue(pmp, park_suspend);
784 PUFFS_MSG_RELEASE(suspend);
785
786 error = pageflush(mp, FSCRED, 0, 1);
787 if (error == 0)
788 error = fstrans_setstate(mp, FSTRANS_SUSPENDED);
789
790 if (error != 0) {
791 PUFFS_MSG_ALLOC(vfs, suspend);
792 puffs_msg_setfaf(park_suspend);
793 suspend_msg->pvfsr_status = PUFFS_SUSPEND_ERROR;
794 puffs_msg_setinfo(park_suspend, PUFFSOP_VFS,
795 PUFFS_VFS_SUSPEND, NULL);
796
797 puffs_msg_enqueue(pmp, park_suspend);
798 PUFFS_MSG_RELEASE(suspend);
799 (void) fstrans_setstate(mp, FSTRANS_NORMAL);
800 break;
801 }
802
803 PUFFS_MSG_ALLOC(vfs, suspend);
804 puffs_msg_setfaf(park_suspend);
805 suspend_msg->pvfsr_status = PUFFS_SUSPEND_SUSPENDED;
806 puffs_msg_setinfo(park_suspend, PUFFSOP_VFS,
807 PUFFS_VFS_SUSPEND, NULL);
808
809 puffs_msg_enqueue(pmp, park_suspend);
810 PUFFS_MSG_RELEASE(suspend);
811
812 break;
813
814 case SUSPEND_RESUME:
815 DPRINTF(("puffs_suspendctl: resume\n"));
816 error = 0;
817 (void) fstrans_setstate(mp, FSTRANS_NORMAL);
818 PUFFS_MSG_ALLOC(vfs, suspend);
819 puffs_msg_setfaf(park_suspend);
820 suspend_msg->pvfsr_status = PUFFS_SUSPEND_RESUME;
821 puffs_msg_setinfo(park_suspend, PUFFSOP_VFS,
822 PUFFS_VFS_SUSPEND, NULL);
823
824 puffs_msg_enqueue(pmp, park_suspend);
825 PUFFS_MSG_RELEASE(suspend);
826 break;
827
828 default:
829 error = EINVAL;
830 break;
831 }
832
833 DPRINTF(("puffs_suspendctl: return %d\n", error));
834 return error;
835 }
836
837 int
838 puffs_vfsop_extattrctl(struct mount *mp, int cmd, struct vnode *vp,
839 int attrnamespace, const char *attrname)
840 {
841 PUFFS_MSG_VARS(vfs, extattrctl);
842 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
843 struct puffs_node *pnp;
844 puffs_cookie_t pnc;
845 int error, flags;
846
847 if (vp) {
848 /* doesn't make sense for puffs servers */
849 if (vp->v_mount != mp)
850 return EXDEV;
851 pnp = vp->v_data;
852 pnc = pnp->pn_cookie;
853 flags = PUFFS_EXTATTRCTL_HASNODE;
854 } else {
855 pnp = pnc = NULL;
856 flags = 0;
857 }
858
859 PUFFS_MSG_ALLOC(vfs, extattrctl);
860 extattrctl_msg->pvfsr_cmd = cmd;
861 extattrctl_msg->pvfsr_attrnamespace = attrnamespace;
862 extattrctl_msg->pvfsr_flags = flags;
863 if (attrname) {
864 strlcpy(extattrctl_msg->pvfsr_attrname, attrname,
865 sizeof(extattrctl_msg->pvfsr_attrname));
866 extattrctl_msg->pvfsr_flags |= PUFFS_EXTATTRCTL_HASATTRNAME;
867 }
868 puffs_msg_setinfo(park_extattrctl,
869 PUFFSOP_VFS, PUFFS_VFS_EXTATTRCTL, pnc);
870
871 puffs_msg_enqueue(pmp, park_extattrctl);
872 if (vp) {
873 mutex_enter(&pnp->pn_mtx);
874 puffs_referencenode(pnp);
875 mutex_exit(&pnp->pn_mtx);
876 VOP_UNLOCK(vp, 0);
877 }
878 error = puffs_msg_wait2(pmp, park_extattrctl, pnp, NULL);
879 PUFFS_MSG_RELEASE(extattrctl);
880 if (vp) {
881 puffs_releasenode(pnp);
882 }
883
884 return checkerr(pmp, error, __func__);
885 }
886
887 const struct vnodeopv_desc * const puffs_vnodeopv_descs[] = {
888 &puffs_vnodeop_opv_desc,
889 &puffs_specop_opv_desc,
890 &puffs_fifoop_opv_desc,
891 &puffs_msgop_opv_desc,
892 NULL,
893 };
894
895 struct vfsops puffs_vfsops = {
896 MOUNT_PUFFS,
897 sizeof (struct puffs_kargs),
898 puffs_vfsop_mount, /* mount */
899 puffs_vfsop_start, /* start */
900 puffs_vfsop_unmount, /* unmount */
901 puffs_vfsop_root, /* root */
902 (void *)eopnotsupp, /* quotactl */
903 puffs_vfsop_statvfs, /* statvfs */
904 puffs_vfsop_sync, /* sync */
905 (void *)eopnotsupp, /* vget */
906 puffs_vfsop_fhtovp, /* fhtovp */
907 puffs_vfsop_vptofh, /* vptofh */
908 puffs_vfsop_init, /* init */
909 NULL, /* reinit */
910 puffs_vfsop_done, /* done */
911 NULL, /* mountroot */
912 puffs_vfsop_snapshot, /* snapshot */
913 puffs_vfsop_extattrctl, /* extattrctl */
914 puffs_vfsop_suspendctl, /* suspendctl */
915 genfs_renamelock_enter,
916 genfs_renamelock_exit,
917 (void *)eopnotsupp,
918 puffs_vnodeopv_descs, /* vnodeops */
919 0, /* refcount */
920 { NULL, NULL }
921 };
922
923 static int
924 puffs_modcmd(modcmd_t cmd, void *arg)
925 {
926
927 switch (cmd) {
928 case MODULE_CMD_INIT:
929 return vfs_attach(&puffs_vfsops);
930 case MODULE_CMD_FINI:
931 return vfs_detach(&puffs_vfsops);
932 default:
933 return ENOTTY;
934 }
935 }
936