rumpfs.c revision 1.22 1 /* $NetBSD: rumpfs.c,v 1.22 2009/08/04 12:40:42 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by Google Summer of Code.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: rumpfs.c,v 1.22 2009/08/04 12:40:42 pooka Exp $");
32
33 #include <sys/param.h>
34 #include <sys/mount.h>
35 #include <sys/vnode.h>
36 #include <sys/errno.h>
37 #include <sys/kauth.h>
38 #include <sys/lock.h>
39 #include <sys/lockf.h>
40 #include <sys/stat.h>
41 #include <sys/namei.h>
42 #include <sys/queue.h>
43 #include <sys/filedesc.h>
44 #include <sys/syscallargs.h>
45 #include <sys/atomic.h>
46
47 #include <miscfs/fifofs/fifo.h>
48 #include <miscfs/specfs/specdev.h>
49 #include <miscfs/genfs/genfs.h>
50
51 #include <rump/rumpuser.h>
52
53 #include "rump_private.h"
54 #include "rump_vfs_private.h"
55
56 static int rump_vop_lookup(void *);
57 static int rump_vop_getattr(void *);
58 static int rump_vop_mkdir(void *);
59 static int rump_vop_mknod(void *);
60 static int rump_vop_inactive(void *);
61 static int rump_vop_reclaim(void *);
62 static int rump_vop_success(void *);
63 static int rump_vop_spec(void *);
64
65 int (**fifo_vnodeop_p)(void *);
66 const struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
67 { &vop_default_desc, vn_default_error },
68 { NULL, NULL }
69 };
70 const struct vnodeopv_desc fifo_vnodeop_opv_desc =
71 { &fifo_vnodeop_p, fifo_vnodeop_entries };
72
73 int (**rump_vnodeop_p)(void *);
74 const struct vnodeopv_entry_desc rump_vnodeop_entries[] = {
75 { &vop_default_desc, vn_default_error },
76 { &vop_lookup_desc, rump_vop_lookup },
77 { &vop_getattr_desc, rump_vop_getattr },
78 { &vop_mkdir_desc, rump_vop_mkdir },
79 { &vop_mknod_desc, rump_vop_mknod },
80 { &vop_access_desc, rump_vop_success },
81 { &vop_putpages_desc, genfs_null_putpages },
82 { &vop_fsync_desc, rump_vop_success },
83 { &vop_lock_desc, genfs_lock },
84 { &vop_unlock_desc, genfs_unlock },
85 { &vop_inactive_desc, rump_vop_inactive },
86 { &vop_reclaim_desc, rump_vop_reclaim },
87 { NULL, NULL }
88 };
89 const struct vnodeopv_desc rump_vnodeop_opv_desc =
90 { &rump_vnodeop_p, rump_vnodeop_entries };
91
92 int (**rump_specop_p)(void *);
93 const struct vnodeopv_entry_desc rump_specop_entries[] = {
94 { &vop_default_desc, rump_vop_spec },
95 { NULL, NULL }
96 };
97 const struct vnodeopv_desc rump_specop_opv_desc =
98 { &rump_specop_p, rump_specop_entries };
99
100 const struct vnodeopv_desc * const rump_opv_descs[] = {
101 &rump_vnodeop_opv_desc,
102 &rump_specop_opv_desc,
103 NULL
104 };
105
106 struct rumpfs_dent {
107 char *rd_name;
108 struct rumpfs_node *rd_node;
109
110 LIST_ENTRY(rumpfs_dent) rd_entries;
111 };
112
113 struct rumpfs_node {
114 struct vattr rn_va;
115 struct vnode *rn_vp;
116
117 /* only for VDIR */
118 LIST_HEAD(, rumpfs_dent) rn_dir;
119 };
120
121 static struct rumpfs_node *makeprivate(enum vtype, dev_t, off_t);
122
123 /*
124 * Extra Terrestrial stuff. We map a given key (pathname) to a file on
125 * the host FS. ET phones home only from the root node of rumpfs.
126 *
127 * When an etfs node is removed, a vnode potentially behind it is not
128 * immediately recycled.
129 */
130
131 struct etfs {
132 char et_key[MAXPATHLEN];
133 LIST_ENTRY(etfs) et_entries;
134
135 struct rumpfs_node *et_rn;
136 };
137 static kmutex_t etfs_lock;
138 static LIST_HEAD(, etfs) etfs_list = LIST_HEAD_INITIALIZER(etfs_list);
139
140 static enum vtype
141 ettype_to_vtype(enum rump_etfs_type et)
142 {
143 enum vtype vt;
144
145 switch (et) {
146 case RUMP_ETFS_REG:
147 vt = VREG;
148 break;
149 case RUMP_ETFS_BLK:
150 vt = VBLK;
151 break;
152 case RUMP_ETFS_CHR:
153 vt = VCHR;
154 break;
155 default:
156 panic("invalid et type: %d", et);
157 }
158
159 return vt;
160 }
161
162 static bool
163 etfs_find(const char *key, struct rumpfs_node **rnp)
164 {
165 struct etfs *et;
166 bool rv = false;
167
168 KASSERT(mutex_owned(&etfs_lock));
169
170 LIST_FOREACH(et, &etfs_list, et_entries) {
171 if (strcmp(key, et->et_key) == 0) {
172 *rnp = et->et_rn;
173 rv = true;
174 break;
175 }
176 }
177
178 return rv;
179 }
180
181 int
182 rump_etfs_register(const char *key, const char *hostpath,
183 enum rump_etfs_type ftype)
184 {
185 struct etfs *et;
186 struct rumpfs_node *rn_dummy;
187 uint64_t fsize;
188 dev_t rdev;
189 devminor_t dmin;
190 int hft, error;
191
192 /* not supported for now, need r/w VOPs ... */
193 if (ftype == RUMP_ETFS_REG)
194 return EOPNOTSUPP;
195
196 if (rumpuser_getfileinfo(hostpath, &fsize, &hft, &error))
197 return error;
198
199 error = rumpblk_register(hostpath, &dmin);
200 if (error != 0) {
201 return error;
202 }
203 rdev = makedev(RUMPBLK, dmin);
204
205 et = kmem_alloc(sizeof(*et), KM_SLEEP);
206 strcpy(et->et_key, key);
207 et->et_rn = makeprivate(ettype_to_vtype(ftype), rdev, fsize);
208
209 mutex_enter(&etfs_lock);
210 if (etfs_find(key, &rn_dummy)) {
211 mutex_exit(&etfs_lock);
212 kmem_free(et, sizeof(*et));
213 /* XXX: rumpblk_deregister(hostpath); */
214 return EEXIST;
215 }
216 LIST_INSERT_HEAD(&etfs_list, et, et_entries);
217 mutex_exit(&etfs_lock);
218
219 return 0;
220 }
221
222 int
223 rump_etfs_remove(const char *key)
224 {
225 struct etfs *et;
226
227 mutex_enter(&etfs_lock);
228 LIST_FOREACH(et, &etfs_list, et_entries) {
229 if (strcmp(et->et_key, key) == 0) {
230 LIST_REMOVE(et, et_entries);
231 kmem_free(et, sizeof(*et));
232 break;
233 }
234 }
235 mutex_exit(&etfs_lock);
236
237 if (!et)
238 return ENOENT;
239 return 0;
240 }
241
242 /*
243 * rumpfs
244 */
245
246 static struct mount rump_mnt;
247 static int lastino = 1;
248 static kmutex_t reclock;
249
250 static struct rumpfs_node *
251 makeprivate(enum vtype vt, dev_t rdev, off_t size)
252 {
253 struct rumpfs_node *rn;
254 struct vattr *va;
255 struct timespec ts;
256
257 rn = kmem_alloc(sizeof(*rn), KM_SLEEP);
258 LIST_INIT(&rn->rn_dir);
259 nanotime(&ts);
260
261 va = &rn->rn_va;
262 va->va_type = vt;
263 va->va_mode = 0755;
264 if (vt == VDIR)
265 va->va_nlink = 2;
266 else
267 va->va_nlink = 1;
268 va->va_uid = 0;
269 va->va_gid = 0;
270 va->va_fsid =
271 va->va_fileid = atomic_inc_uint_nv(&lastino);
272 va->va_size = size;
273 va->va_blocksize = 512;
274 va->va_atime = ts;
275 va->va_mtime = ts;
276 va->va_ctime = ts;
277 va->va_birthtime = ts;
278 va->va_gen = 0;
279 va->va_flags = 0;
280 va->va_rdev = rdev;
281 va->va_bytes = 512;
282 va->va_filerev = 0;
283 va->va_vaflags = 0;
284
285 return rn;
286 }
287
288 static int
289 makevnode(struct rumpfs_node *rn, struct vnode **vpp)
290 {
291 struct vnode *vp;
292 int (**vpops)(void *);
293 struct vattr *va = &rn->rn_va;
294 int rv;
295
296 KASSERT(mutex_owned(&reclock));
297
298 if (va->va_type == VCHR || va->va_type == VBLK) {
299 vpops = rump_specop_p;
300 } else {
301 vpops = rump_vnodeop_p;
302 }
303 if (vpops != rump_specop_p && va->va_type != VDIR)
304 return EOPNOTSUPP;
305
306 rv = getnewvnode(VT_RUMP, &rump_mnt, vpops, &vp);
307 if (rv)
308 return rv;
309
310 vp->v_size = vp->v_writesize = va->va_size;
311 vp->v_type = va->va_type;
312
313 if (vpops == rump_specop_p) {
314 spec_node_init(vp, va->va_rdev);
315 }
316 vp->v_data = rn;
317
318 vn_lock(vp, LK_RETRY | LK_EXCLUSIVE);
319 rn->rn_vp = vp;
320 *vpp = vp;
321
322 return 0;
323 }
324
325 /*
326 * Simple lookup for faking lookup of device entry for rump file systems
327 * and for locating/creating directories. Yes, this will panic if you
328 * call it with the wrong arguments.
329 *
330 * uhm, this is twisted. C F C C, hope of C C F C looming
331 */
332 static int
333 rump_vop_lookup(void *v)
334 {
335 struct vop_lookup_args /* {
336 struct vnode *a_dvp;
337 struct vnode **a_vpp;
338 struct componentname *a_cnp;
339 }; */ *ap = v;
340 struct componentname *cnp = ap->a_cnp;
341 struct vnode *dvp = ap->a_dvp;
342 struct vnode **vpp = ap->a_vpp;
343 struct vnode *vp;
344 struct rumpfs_node *rnd = dvp->v_data, *rn;
345 struct rumpfs_dent *rd = NULL;
346 int rv;
347
348 /* we handle only some "non-special" cases */
349 if (!(((cnp->cn_flags & ISLASTCN) == 0)
350 || (cnp->cn_nameiop == LOOKUP || cnp->cn_nameiop == CREATE)))
351 return EOPNOTSUPP;
352 if (!((cnp->cn_flags & ISDOTDOT) == 0))
353 return EOPNOTSUPP;
354 if (!(cnp->cn_namelen != 0 && cnp->cn_pnbuf[0] != '.'))
355 return EOPNOTSUPP;
356
357 /* check if we are returning a faked block device */
358 if (dvp == rootvnode && cnp->cn_nameiop == LOOKUP) {
359 mutex_enter(&etfs_lock);
360 if (etfs_find(cnp->cn_pnbuf, &rn)) {
361 mutex_exit(&etfs_lock);
362 cnp->cn_consume = strlen(cnp->cn_nameptr
363 + cnp->cn_namelen);
364 cnp->cn_flags &= ~REQUIREDIR;
365 goto getvnode;
366 }
367 mutex_exit(&etfs_lock);
368 }
369
370 if (!rd) {
371 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
372 if (strncmp(rd->rd_name, cnp->cn_nameptr,
373 cnp->cn_namelen) == 0)
374 break;
375 }
376 }
377
378 if (!rd && ((cnp->cn_flags & ISLASTCN) == 0||cnp->cn_nameiop != CREATE))
379 return ENOENT;
380
381 if (!rd && (cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
382 cnp->cn_flags |= SAVENAME;
383 return EJUSTRETURN;
384 }
385 rn = rd->rd_node;
386 rd = NULL;
387
388 getvnode:
389 KASSERT(rn);
390 mutex_enter(&reclock);
391 if ((vp = rn->rn_vp)) {
392 mutex_enter(&vp->v_interlock);
393 mutex_exit(&reclock);
394 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
395 goto getvnode;
396 *vpp = vp;
397 } else {
398 rv = makevnode(rn, vpp);
399 rn->rn_vp = *vpp;
400 mutex_exit(&reclock);
401 if (rv)
402 return rv;
403 }
404
405 return 0;
406 }
407
408 static int
409 rump_vop_getattr(void *v)
410 {
411 struct vop_getattr_args /* {
412 struct vnode *a_vp;
413 struct vattr *a_vap;
414 kauth_cred_t a_cred;
415 } */ *ap = v;
416 struct rumpfs_node *rn = ap->a_vp->v_data;
417
418 memcpy(ap->a_vap, &rn->rn_va, sizeof(struct vattr));
419 return 0;
420 }
421
422 static int
423 rump_vop_mkdir(void *v)
424 {
425 struct vop_mkdir_args /* {
426 struct vnode *a_dvp;
427 struct vnode **a_vpp;
428 struct componentname *a_cnp;
429 struct vattr *a_vap;
430 }; */ *ap = v;
431 struct vnode *dvp = ap->a_dvp;
432 struct vnode **vpp = ap->a_vpp;
433 struct componentname *cnp = ap->a_cnp;
434 struct rumpfs_node *rnd = dvp->v_data, *rn;
435 struct rumpfs_dent *rdent;
436 int rv = 0;
437
438 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
439 mutex_enter(&reclock);
440 rv = makevnode(rn, vpp);
441 mutex_exit(&reclock);
442 if (rv)
443 goto out;
444
445 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
446 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
447 rdent->rd_node = (*vpp)->v_data;
448 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
449
450 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
451
452 out:
453 vput(dvp);
454 return rv;
455 }
456
457 static int
458 rump_vop_mknod(void *v)
459 {
460 struct vop_mknod_args /* {
461 struct vnode *a_dvp;
462 struct vnode **a_vpp;
463 struct componentname *a_cnp;
464 struct vattr *a_vap;
465 }; */ *ap = v;
466 struct vnode *dvp = ap->a_dvp;
467 struct vnode **vpp = ap->a_vpp;
468 struct componentname *cnp = ap->a_cnp;
469 struct vattr *va = ap->a_vap;
470 struct rumpfs_node *rnd = dvp->v_data, *rn;
471 struct rumpfs_dent *rdent;
472 int rv;
473
474 rn = makeprivate(va->va_type, va->va_rdev, DEV_BSIZE);
475 mutex_enter(&reclock);
476 rv = makevnode(rn, vpp);
477 mutex_exit(&reclock);
478 if (rv)
479 goto out;
480
481 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
482 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
483 rdent->rd_node = (*vpp)->v_data;
484 rdent->rd_node->rn_va.va_rdev = va->va_rdev;
485 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
486
487 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
488
489 out:
490 vput(dvp);
491 return rv;
492 }
493
494 static int
495 rump_vop_success(void *v)
496 {
497
498 return 0;
499 }
500
501 static int
502 rump_vop_inactive(void *v)
503 {
504 struct vop_inactive_args *ap = v;
505
506 VOP_UNLOCK(ap->a_vp, 0);
507 return 0;
508 }
509
510 static int
511 rump_vop_reclaim(void *v)
512 {
513 struct vop_reclaim_args /* {
514 struct vnode *a_vp;
515 } */ *ap = v;
516 struct vnode *vp = ap->a_vp;
517 struct rumpfs_node *rn = vp->v_data;
518
519 mutex_enter(&reclock);
520 rn->rn_vp = NULL;
521 mutex_exit(&reclock);
522 vp->v_data = NULL;
523
524 return 0;
525 }
526
527 static int
528 rump_vop_spec(void *v)
529 {
530 struct vop_generic_args *ap = v;
531 int (**opvec)(void *);
532
533 switch (ap->a_desc->vdesc_offset) {
534 case VOP_ACCESS_DESCOFFSET:
535 case VOP_GETATTR_DESCOFFSET:
536 case VOP_LOCK_DESCOFFSET:
537 case VOP_UNLOCK_DESCOFFSET:
538 opvec = rump_vnodeop_p;
539 break;
540 default:
541 opvec = spec_vnodeop_p;
542 break;
543 }
544
545 return VOCALL(opvec, ap->a_desc->vdesc_offset, v);
546 }
547
548 void
549 rumpfs_init(void)
550 {
551 struct rumpfs_node *rn;
552 int rv;
553
554 mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE);
555 mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE);
556
557 /* XXX: init properly instead of this crap */
558 rump_mnt.mnt_refcnt = 1;
559 rump_mnt.mnt_flag = MNT_ROOTFS;
560 rw_init(&rump_mnt.mnt_unmounting);
561 TAILQ_INIT(&rump_mnt.mnt_vnodelist);
562
563 vfs_opv_init(rump_opv_descs);
564 rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
565 mutex_enter(&reclock);
566 rv = makevnode(rn, &rootvnode);
567 mutex_exit(&reclock);
568 if (rv)
569 panic("could not create root vnode: %d", rv);
570 rootvnode->v_vflag |= VV_ROOT;
571 VOP_UNLOCK(rootvnode, 0);
572 }
573