1 /* $NetBSD: union_vfsops.c,v 1.60 2009/06/29 05:08:18 dholland Exp $ */
4 * Copyright (c) 1994 The Regents of the University of California.
7 * This code is derived from software donated to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95
38 * Copyright (c) 1994 Jan-Simon Pendry.
39 * All rights reserved.
41 * This code is derived from software donated to Berkeley by
44 * Redistribution and use in source and binary forms, with or without
45 * modification, are permitted provided that the following conditions
47 * 1. Redistributions of source code must retain the above copyright
48 * notice, this list of conditions and the following disclaimer.
49 * 2. Redistributions in binary form must reproduce the above copyright
50 * notice, this list of conditions and the following disclaimer in the
51 * documentation and/or other materials provided with the distribution.
52 * 3. All advertising materials mentioning features or use of this software
53 * must display the following acknowledgement:
54 * This product includes software developed by the University of
55 * California, Berkeley and its contributors.
56 * 4. Neither the name of the University nor the names of its contributors
57 * may be used to endorse or promote products derived from this software
58 * without specific prior written permission.
60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.60 2009/06/29 05:08:18 dholland Exp $");
82 #include <sys/param.h>
83 #include <sys/systm.h>
84 #include <sys/sysctl.h>
87 #include <sys/vnode.h>
88 #include <sys/mount.h>
89 #include <sys/namei.h>
90 #include <sys/malloc.h>
91 #include <sys/filedesc.h>
92 #include <sys/queue.h>
94 #include <sys/kauth.h>
95 #include <sys/module.h>
97 #include <fs/union/union.h>
99 MODULE(MODULE_CLASS_VFS
, union, "layerfs");
103 static struct sysctllog
*union_sysctl_log
;
106 * Mount union filesystem
109 union_mount(struct mount
*mp
, const char *path
, void *data
, size_t *data_len
)
111 struct lwp
*l
= curlwp
;
113 struct union_args
*args
= data
;
114 struct vnode
*lowerrootvp
= NULLVP
;
115 struct vnode
*upperrootvp
= NULLVP
;
116 struct union_mount
*um
= 0;
122 if (*data_len
< sizeof *args
)
125 #ifdef UNION_DIAGNOSTIC
126 printf("union_mount(mp = %p)\n", mp
);
129 if (mp
->mnt_flag
& MNT_GETARGS
) {
130 um
= MOUNTTOUNIONMOUNT(mp
);
134 args
->mntflags
= um
->um_op
;
135 *data_len
= sizeof *args
;
141 if (mp
->mnt_flag
& MNT_UPDATE
) {
144 * 1. a way to convert between rdonly and rdwr mounts.
145 * 2. support for nfs exports.
151 printf("WARNING: the union file system is experimental\n"
152 "WARNING: it can cause crashes and file system corruption\n");
154 lowerrootvp
= mp
->mnt_vnodecovered
;
160 error
= namei_simple_user(args
->target
,
161 NSM_FOLLOW_NOEMULROOT
, &upperrootvp
);
165 if (upperrootvp
->v_type
!= VDIR
) {
170 um
= (struct union_mount
*) malloc(sizeof(struct union_mount
),
171 M_UFSMNT
, M_WAITOK
); /* XXX */
174 * Keep a held reference to the target vnodes.
175 * They are vrele'd in union_unmount.
177 * Depending on the _BELOW flag, the filesystems are
178 * viewed in a different order. In effect, this is the
179 * same as providing a mount under option to the mount syscall.
182 um
->um_op
= args
->mntflags
& UNMNT_OPMASK
;
185 um
->um_lowervp
= lowerrootvp
;
186 um
->um_uppervp
= upperrootvp
;
190 um
->um_lowervp
= upperrootvp
;
191 um
->um_uppervp
= lowerrootvp
;
196 lowerrootvp
= NULLVP
;
197 um
->um_uppervp
= upperrootvp
;
198 um
->um_lowervp
= lowerrootvp
;
207 * Unless the mount is readonly, ensure that the top layer
208 * supports whiteout operations
210 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0) {
211 error
= VOP_WHITEOUT(um
->um_uppervp
, (struct componentname
*) 0, LOOKUP
);
216 um
->um_cred
= l
->l_cred
;
217 kauth_cred_hold(um
->um_cred
);
218 um
->um_cmode
= UN_DIRMODE
&~ l
->l_proc
->p_cwdi
->cwdi_cmask
;
221 * Depending on what you think the MNT_LOCAL flag might mean,
222 * you may want the && to be || on the conditional below.
223 * At the moment it has been defined that the filesystem is
224 * only local if it is all local, ie the MNT_LOCAL flag implies
225 * that the entire namespace is local. If you think the MNT_LOCAL
226 * flag implies that some of the files might be stored locally
227 * then you will want to change the conditional.
229 if (um
->um_op
== UNMNT_ABOVE
) {
230 if (((um
->um_lowervp
== NULLVP
) ||
231 (um
->um_lowervp
->v_mount
->mnt_flag
& MNT_LOCAL
)) &&
232 (um
->um_uppervp
->v_mount
->mnt_flag
& MNT_LOCAL
))
233 mp
->mnt_flag
|= MNT_LOCAL
;
237 * Copy in the upper layer's RDONLY flag. This is for the benefit
238 * of lookup() which explicitly checks the flag, rather than asking
239 * the filesystem for it's own opinion. This means, that an update
240 * mount of the underlying filesystem to go from rdonly to rdwr
241 * will leave the unioned view as read-only.
243 mp
->mnt_flag
|= (um
->um_uppervp
->v_mount
->mnt_flag
& MNT_RDONLY
);
248 error
= set_statvfs_info( path
, UIO_USERSPACE
, NULL
, UIO_USERSPACE
,
249 mp
->mnt_op
->vfs_name
, mp
, l
);
266 panic("union_mount: bad um_op");
271 memcpy(mp
->mnt_stat
.f_mntfromname
, cp
, len
);
273 xp
= mp
->mnt_stat
.f_mntfromname
+ len
;
274 len
= MNAMELEN
- len
;
276 (void) copyinstr(args
->target
, xp
, len
- 1, &size
);
277 memset(xp
+ size
, 0, len
- size
);
279 #ifdef UNION_DIAGNOSTIC
280 printf("union_mount: from %s, on %s\n",
281 mp
->mnt_stat
.f_mntfromname
, mp
->mnt_stat
.f_mntonname
);
284 /* Setup the readdir hook if it's not set already */
285 if (!vn_union_readdir_hook
)
286 vn_union_readdir_hook
= union_readdirhook
;
301 * VFS start. Nothing needed here - the start routine
302 * on the underlying filesystem(s) will have been called
303 * when that filesystem was mounted.
307 union_start(struct mount
*mp
, int flags
)
314 * Free reference to union layer
317 union_unmount(struct mount
*mp
, int mntflags
)
319 struct union_mount
*um
= MOUNTTOUNIONMOUNT(mp
);
323 #ifdef UNION_DIAGNOSTIC
324 printf("union_unmount(mp = %p)\n", mp
);
328 * Keep flushing vnodes from the mount list.
329 * This is needed because of the un_pvp held
330 * reference to the parent vnode.
331 * If more vnodes have been freed on a given pass,
332 * the try again. The loop will iterate at most
333 * (d) times, where (d) is the maximum tree depth
336 for (freeing
= 0; (error
= vflush(mp
, NULL
, 0)) != 0;) {
340 /* count #vnodes held on mount list */
342 TAILQ_FOREACH(vp
, &mp
->mnt_vnodelist
, v_mntvnodes
)
345 /* if this is unchanged then stop */
349 /* otherwise try once more time */
354 * Ok, now that we've tried doing it gently, get out the hammer.
357 if (mntflags
& MNT_FORCE
)
358 error
= vflush(mp
, NULL
, FORCECLOSE
);
364 * Discard references to upper and lower target vnodes.
367 vrele(um
->um_lowervp
);
368 vrele(um
->um_uppervp
);
369 kauth_cred_free(um
->um_cred
);
371 * Finally, throw away the union_mount structure
373 free(mp
->mnt_data
, M_UFSMNT
); /* XXX */
379 union_root(struct mount
*mp
, struct vnode
**vpp
)
381 struct union_mount
*um
= MOUNTTOUNIONMOUNT(mp
);
385 * Return locked reference to root.
387 vref(um
->um_uppervp
);
388 vn_lock(um
->um_uppervp
, LK_EXCLUSIVE
| LK_RETRY
);
390 vref(um
->um_lowervp
);
391 error
= union_allocvp(vpp
, mp
, NULL
, NULL
, NULL
,
392 um
->um_uppervp
, um
->um_lowervp
, 1);
395 vput(um
->um_uppervp
);
397 vrele(um
->um_lowervp
);
404 union_statvfs(struct mount
*mp
, struct statvfs
*sbp
)
407 struct union_mount
*um
= MOUNTTOUNIONMOUNT(mp
);
408 struct statvfs
*sbuf
= malloc(sizeof(*sbuf
), M_TEMP
, M_WAITOK
| M_ZERO
);
409 unsigned long lbsize
;
411 #ifdef UNION_DIAGNOSTIC
412 printf("union_statvfs(mp = %p, lvp = %p, uvp = %p)\n", mp
,
413 um
->um_lowervp
, um
->um_uppervp
);
416 if (um
->um_lowervp
) {
417 error
= VFS_STATVFS(um
->um_lowervp
->v_mount
, sbuf
);
422 /* now copy across the "interesting" information and fake the rest */
423 lbsize
= sbuf
->f_bsize
;
424 sbp
->f_blocks
= sbuf
->f_blocks
- sbuf
->f_bfree
;
425 sbp
->f_files
= sbuf
->f_files
- sbuf
->f_ffree
;
427 error
= VFS_STATVFS(um
->um_uppervp
->v_mount
, sbuf
);
431 sbp
->f_flag
= sbuf
->f_flag
;
432 sbp
->f_bsize
= sbuf
->f_bsize
;
433 sbp
->f_frsize
= sbuf
->f_frsize
;
434 sbp
->f_iosize
= sbuf
->f_iosize
;
437 * The "total" fields count total resources in all layers,
438 * the "free" fields count only those resources which are
439 * free in the upper layer (since only the upper layer
443 if (sbuf
->f_bsize
!= lbsize
)
444 sbp
->f_blocks
= sbp
->f_blocks
* lbsize
/ sbuf
->f_bsize
;
445 sbp
->f_blocks
+= sbuf
->f_blocks
;
446 sbp
->f_bfree
= sbuf
->f_bfree
;
447 sbp
->f_bavail
= sbuf
->f_bavail
;
448 sbp
->f_bresvd
= sbuf
->f_bresvd
;
449 sbp
->f_files
+= sbuf
->f_files
;
450 sbp
->f_ffree
= sbuf
->f_ffree
;
451 sbp
->f_favail
= sbuf
->f_favail
;
452 sbp
->f_fresvd
= sbuf
->f_fresvd
;
454 copy_statvfs_info(sbp
, mp
);
462 union_sync(struct mount
*mp
, int waitfor
,
467 * XXX - Assumes no data cached at union layer.
474 union_vget(struct mount
*mp
, ino_t ino
,
482 union_renamelock_enter(struct mount
*mp
)
484 struct union_mount
*um
= MOUNTTOUNIONMOUNT(mp
);
486 /* Lock just the upper fs, where the action happens. */
487 return VFS_RENAMELOCK_ENTER(um
->um_uppervp
->v_mount
);
491 union_renamelock_exit(struct mount
*mp
)
493 struct union_mount
*um
= MOUNTTOUNIONMOUNT(mp
);
495 VFS_RENAMELOCK_EXIT(um
->um_uppervp
->v_mount
);
498 extern const struct vnodeopv_desc union_vnodeop_opv_desc
;
500 const struct vnodeopv_desc
* const union_vnodeopv_descs
[] = {
501 &union_vnodeop_opv_desc
,
505 struct vfsops union_vfsops
= {
507 sizeof (struct union_args
),
512 (void *)eopnotsupp
, /* vfs_quotactl */
516 (void *)eopnotsupp
, /* vfs_fhtovp */
517 (void *)eopnotsupp
, /* vfs_vptofh */
519 NULL
, /* vfs_reinit */
521 NULL
, /* vfs_mountroot */
522 (int (*)(struct mount
*, struct vnode
*, struct timespec
*)) eopnotsupp
,
524 (void *)eopnotsupp
, /* vfs_suspendctl */
525 union_renamelock_enter
,
526 union_renamelock_exit
,
528 union_vnodeopv_descs
,
529 0, /* vfs_refcount */
534 union_modcmd(modcmd_t cmd
, void *arg
)
539 case MODULE_CMD_INIT
:
540 error
= vfs_attach(&union_vfsops
);
543 sysctl_createv(&union_sysctl_log
, 0, NULL
, NULL
,
545 CTLTYPE_NODE
, "vfs", NULL
,
548 sysctl_createv(&union_sysctl_log
, 0, NULL
, NULL
,
550 CTLTYPE_NODE
, "union",
551 SYSCTL_DESCR("Union file system"),
553 CTL_VFS
, 15, CTL_EOL
);
555 * XXX the "15" above could be dynamic, thereby eliminating
556 * one more instance of the "number to vfs" mapping problem,
557 * but "15" is the order as taken from sys/mount.h
560 case MODULE_CMD_FINI
:
561 error
= vfs_detach(&union_vfsops
);
564 sysctl_teardown(&union_sysctl_log
);