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