Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * Copyright (c) 1992, 1993, 1995 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * This code is derived from software donated to Berkeley by | |
6 | * Jan-Simon Pendry. | |
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. | |
dc71b7ab | 16 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
30 | * SUCH DAMAGE. | |
31 | * | |
32 | * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 | |
33 | * | |
34 | * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 | |
35 | * $FreeBSD: src/sys/miscfs/nullfs/null_vfsops.c,v 1.35.2.3 2001/07/26 20:37:11 iedowse Exp $ | |
67863d04 | 36 | * $DragonFly: src/sys/vfs/nullfs/null_vfsops.c,v 1.31 2008/09/17 21:44:25 dillon Exp $ |
984263bc MD |
37 | */ |
38 | ||
39 | /* | |
40 | * Null Layer | |
41 | * (See null_vnops.c for a description of what this does.) | |
42 | */ | |
43 | ||
44 | #include <sys/param.h> | |
45 | #include <sys/systm.h> | |
46 | #include <sys/kernel.h> | |
47 | #include <sys/proc.h> | |
48 | #include <sys/malloc.h> | |
49 | #include <sys/vnode.h> | |
50 | #include <sys/mount.h> | |
75779c3c | 51 | #include <sys/namecache.h> |
fad57d0e | 52 | #include <sys/nlookup.h> |
67863d04 | 53 | #include <sys/mountctl.h> |
1bb7a917 | 54 | #include <sys/sysctl.h> |
1f2de5d4 | 55 | #include "null.h" |
984263bc | 56 | |
66a1ddf5 | 57 | extern struct vop_ops null_vnode_vops; |
0961aa92 | 58 | |
984263bc MD |
59 | static MALLOC_DEFINE(M_NULLFSMNT, "NULLFS mount", "NULLFS mount structure"); |
60 | ||
1bb7a917 MD |
61 | SYSCTL_NODE(_vfs, OID_AUTO, nullfs, CTLFLAG_RD, 0, "NULLFS filesystem"); |
62 | ||
63 | static int nullfs_debug; | |
64 | TUNABLE_INT("vfs.nullfs.debug", &nullfs_debug); | |
65 | SYSCTL_INT(_vfs_nullfs, OID_AUTO, debug, CTLFLAG_RW, | |
66 | &nullfs_debug, 0, "nullfs debugging"); | |
67 | ||
984263bc | 68 | static int nullfs_root(struct mount *mp, struct vnode **vpp); |
984263bc | 69 | static int nullfs_statfs(struct mount *mp, struct statfs *sbp, |
acde96db | 70 | struct ucred *cred); |
984263bc MD |
71 | |
72 | /* | |
73 | * Mount null layer | |
74 | */ | |
75 | static int | |
acde96db | 76 | nullfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) |
984263bc MD |
77 | { |
78 | int error = 0; | |
79 | struct null_args args; | |
8cb8b61a | 80 | struct vnode *rootvp; |
984263bc | 81 | struct null_mount *xmp; |
973c11b9 | 82 | size_t size; |
fad57d0e | 83 | struct nlookupdata nd; |
67863d04 | 84 | fhandle_t fh; |
984263bc MD |
85 | |
86 | NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); | |
87 | ||
88 | /* | |
8b02b69a | 89 | * Get argument |
984263bc | 90 | */ |
8b02b69a SK |
91 | error = copyin(data, (caddr_t)&args, sizeof(struct null_args)); |
92 | if (error) | |
93 | return (error); | |
984263bc MD |
94 | |
95 | /* | |
8b02b69a SK |
96 | * XXX: Should we process mount export info ? |
97 | * If not, returning zero here is enough as the actual ro/rw update is | |
98 | * being done in sys_mount(). | |
984263bc | 99 | */ |
8b02b69a SK |
100 | if (mp->mnt_flag & MNT_UPDATE) { |
101 | xmp = MOUNTTONULLMOUNT(mp); | |
102 | error = vfs_export(mp, &xmp->export, &args.export); | |
984263bc | 103 | return (error); |
8b02b69a | 104 | } |
984263bc | 105 | |
984263bc MD |
106 | /* |
107 | * Find lower node | |
108 | */ | |
8cb8b61a | 109 | rootvp = NULL; |
fad57d0e | 110 | error = nlookup_init(&nd, args.target, UIO_USERSPACE, NLC_FOLLOW); |
28623bf9 MD |
111 | if (error) |
112 | goto fail1; | |
113 | error = nlookup(&nd); | |
114 | if (error) | |
115 | goto fail2; | |
116 | error = cache_vget(&nd.nl_nch, nd.nl_cred, LK_EXCLUSIVE, &rootvp); | |
117 | if (error) | |
118 | goto fail2; | |
984263bc | 119 | |
77652cad | 120 | xmp = (struct null_mount *) kmalloc(sizeof(struct null_mount), |
67863d04 | 121 | M_NULLFSMNT, M_WAITOK | M_ZERO); |
984263bc MD |
122 | |
123 | /* | |
124 | * Save reference to underlying FS | |
28623bf9 | 125 | * |
8cb8b61a | 126 | * As lite stacking enters the scene, the old way of doing this |
28623bf9 MD |
127 | * -- via the vnode -- is not good enough anymore. Use the |
128 | * underlying filesystem's namecache handle as our mount point | |
129 | * root, adjusting the mount to point to us. | |
130 | * | |
131 | * NCF_ISMOUNTPT is normally set on the mount point, but we also | |
132 | * want to set it on the base directory being mounted to prevent | |
133 | * that directory from being destroyed out from under the nullfs | |
134 | * mount. | |
3ea4f8fe MD |
135 | * |
136 | * The forwarding mount pointer (xmp->nullm_vfs) must be set to | |
137 | * the actual target filesystem. If the target filesystem was | |
138 | * resolved via a nullfs mount nd.nl_nch.mount will be pointing | |
139 | * to the nullfs mount structure instead of the target filesystem, | |
140 | * which would otherwise cause the mount VOPS and VFSOPS to recurse | |
141 | * endlessly. If we are mounting via a nullfs mount we inherit | |
142 | * its read-only state, if set. | |
8cb8b61a | 143 | */ |
28623bf9 | 144 | xmp->nullm_vfs = nd.nl_nch.mount; |
3ea4f8fe MD |
145 | if (xmp->nullm_vfs != rootvp->v_mount) { |
146 | if (xmp->nullm_vfs->mnt_flag & MNT_RDONLY) | |
147 | mp->mnt_flag |= MNT_RDONLY; | |
246693ac AHJ |
148 | if (xmp->nullm_vfs->mnt_flag & MNT_NOEXEC) |
149 | mp->mnt_flag |= MNT_NOEXEC; | |
3ea4f8fe MD |
150 | xmp->nullm_vfs = rootvp->v_mount; |
151 | } | |
152 | ||
153 | /* | |
154 | * ncmountpt is the parent glue. When mounting a nullfs via a nullfs | |
155 | * we retain the parent nullfs to create a unique chain tuple. | |
156 | */ | |
28623bf9 MD |
157 | mp->mnt_ncmountpt = nd.nl_nch; |
158 | cache_changemount(&mp->mnt_ncmountpt, mp); | |
159 | mp->mnt_ncmountpt.ncp->nc_flag |= NCF_ISMOUNTPT; | |
160 | cache_unlock(&mp->mnt_ncmountpt); | |
161 | cache_zero(&nd.nl_nch); | |
8cb8b61a | 162 | nlookup_done(&nd); |
984263bc | 163 | |
66a1ddf5 | 164 | vfs_add_vnodeops(mp, &null_vnode_vops, &mp->mnt_vn_norm_ops); |
0961aa92 | 165 | |
28623bf9 | 166 | vn_unlock(rootvp); /* leave reference intact */ |
984263bc MD |
167 | |
168 | /* | |
169 | * Keep a held reference to the root vnode. | |
170 | * It is vrele'd in nullfs_unmount. | |
171 | */ | |
8cb8b61a MD |
172 | xmp->nullm_rootvp = rootvp; |
173 | /* | |
174 | * XXX What's the proper safety condition for querying | |
175 | * the underlying mount? Is this flag tuning necessary | |
176 | * at all? | |
177 | */ | |
178 | if (xmp->nullm_vfs->mnt_flag & MNT_LOCAL) | |
984263bc MD |
179 | mp->mnt_flag |= MNT_LOCAL; |
180 | mp->mnt_data = (qaddr_t) xmp; | |
67863d04 | 181 | |
1bb7a917 MD |
182 | /* |
183 | * Mount path | |
184 | */ | |
185 | copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); | |
186 | ||
67863d04 MD |
187 | /* |
188 | * Try to create a unique but non-random fsid for the nullfs to | |
1bb7a917 MD |
189 | * allow it to be exported via NFS. Use a combination of the |
190 | * FH for the nullfs root in the underlying filesystem and a | |
191 | * CRC of the null mount path. | |
fb578eac | 192 | * |
ae4bce3c MD |
193 | * Use val[1] so as not to interfere with the generally unique |
194 | * val[0]. | |
67863d04 MD |
195 | */ |
196 | bzero(&fh, sizeof(fh)); | |
197 | fh.fh_fsid = rootvp->v_mount->mnt_stat.f_fsid; | |
1bb7a917 | 198 | |
67863d04 | 199 | if (VFS_VPTOFH(rootvp, &fh.fh_fid) == 0) { |
1bb7a917 MD |
200 | if (nullfs_debug) |
201 | kprintf("(A)"); | |
67863d04 MD |
202 | vfs_setfsid(mp, &fh.fh_fsid); |
203 | } else { | |
1bb7a917 MD |
204 | if (nullfs_debug) |
205 | kprintf("(B)"); | |
67863d04 MD |
206 | vfs_getnewfsid(mp); |
207 | } | |
1bb7a917 MD |
208 | fh.fh_fsid.val[1] ^= crc32(mp->mnt_stat.f_mntfromname, size); |
209 | ||
210 | if (nullfs_debug) { | |
211 | kprintf("NULLFS FSID %08x.%08x -> %08x.%08x (%*.*s)\n", | |
212 | rootvp->v_mount->mnt_stat.f_fsid.val[0], | |
213 | rootvp->v_mount->mnt_stat.f_fsid.val[1], | |
214 | fh.fh_fsid.val[0], | |
215 | fh.fh_fsid.val[1], | |
216 | (int)size, (int)size, mp->mnt_stat.f_mntfromname); | |
217 | } | |
984263bc | 218 | |
984263bc | 219 | bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); |
1bb7a917 | 220 | nullfs_statfs(mp, &mp->mnt_stat, cred); |
984263bc | 221 | NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", |
75ffff0d | 222 | mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntfromname); |
28623bf9 | 223 | |
dedb7ef4 FT |
224 | bzero(mp->mnt_stat.f_mntonname, MNAMELEN); |
225 | if (path != NULL) { | |
226 | (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, | |
227 | &size); | |
228 | } | |
229 | ||
28623bf9 | 230 | /* |
572a7ed9 MD |
231 | * Set NCALIASED so unmount won't complain about namecache refs |
232 | * still existing. | |
233 | * | |
234 | * All NULLFS operations are MPSAFE, though it will be short-lived | |
235 | * if the underlying filesystem is not. | |
00369c4a MD |
236 | * |
237 | * NOTE: There is no syncer thread for nullfs (flagged in vfsops) | |
28623bf9 | 238 | */ |
572a7ed9 | 239 | mp->mnt_kern_flag |= MNTK_NCALIASED | MNTK_ALL_MPSAFE; |
cf6a53ca | 240 | |
984263bc | 241 | return (0); |
28623bf9 MD |
242 | fail2: |
243 | nlookup_done(&nd); | |
244 | fail1: | |
245 | return (error); | |
984263bc MD |
246 | } |
247 | ||
984263bc MD |
248 | /* |
249 | * Free reference to null layer | |
250 | */ | |
251 | static int | |
acde96db | 252 | nullfs_unmount(struct mount *mp, int mntflags) |
984263bc | 253 | { |
b81dacf0 | 254 | struct null_mount *xmp; |
984263bc MD |
255 | |
256 | NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); | |
257 | ||
984263bc | 258 | /* |
a8ea7783 | 259 | * Throw away the null_mount structure |
984263bc | 260 | */ |
8e77d370 | 261 | xmp = (void *)mp->mnt_data; |
984263bc | 262 | mp->mnt_data = 0; |
b81dacf0 MD |
263 | if (xmp->nullm_rootvp) { |
264 | vrele(xmp->nullm_rootvp); | |
265 | xmp->nullm_rootvp = NULL; | |
266 | } | |
267 | kfree(xmp, M_NULLFSMNT); | |
984263bc MD |
268 | return 0; |
269 | } | |
270 | ||
271 | static int | |
dadab5e9 | 272 | nullfs_root(struct mount *mp, struct vnode **vpp) |
984263bc | 273 | { |
984263bc | 274 | struct vnode *vp; |
44b1cf3d | 275 | int error; |
984263bc | 276 | |
8cb8b61a MD |
277 | NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, |
278 | (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp); | |
984263bc MD |
279 | |
280 | /* | |
281 | * Return locked reference to root. | |
282 | */ | |
283 | vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; | |
44b1cf3d MD |
284 | error = vget(vp, LK_EXCLUSIVE | LK_RETRY); |
285 | if (error == 0) | |
286 | *vpp = vp; | |
287 | return (error); | |
984263bc MD |
288 | } |
289 | ||
290 | static int | |
f4a3189a | 291 | nullfs_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg, |
acde96db | 292 | struct ucred *cred) |
984263bc | 293 | { |
acde96db | 294 | return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, cred); |
984263bc MD |
295 | } |
296 | ||
297 | static int | |
acde96db | 298 | nullfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) |
984263bc MD |
299 | { |
300 | int error; | |
301 | struct statfs mstat; | |
302 | ||
8cb8b61a MD |
303 | NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p)\n", (void *)mp, |
304 | (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp); | |
984263bc MD |
305 | |
306 | bzero(&mstat, sizeof(mstat)); | |
307 | ||
acde96db | 308 | error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, cred); |
984263bc MD |
309 | if (error) |
310 | return (error); | |
311 | ||
312 | /* now copy across the "interesting" information and fake the rest */ | |
313 | sbp->f_type = mstat.f_type; | |
314 | sbp->f_flags = mstat.f_flags; | |
315 | sbp->f_bsize = mstat.f_bsize; | |
316 | sbp->f_iosize = mstat.f_iosize; | |
317 | sbp->f_blocks = mstat.f_blocks; | |
318 | sbp->f_bfree = mstat.f_bfree; | |
319 | sbp->f_bavail = mstat.f_bavail; | |
320 | sbp->f_files = mstat.f_files; | |
321 | sbp->f_ffree = mstat.f_ffree; | |
322 | if (sbp != &mp->mnt_stat) { | |
323 | bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); | |
984263bc | 324 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); |
dedb7ef4 | 325 | bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); |
984263bc MD |
326 | } |
327 | return (0); | |
328 | } | |
329 | ||
67863d04 MD |
330 | /* |
331 | * Implement NFS export tracking | |
332 | */ | |
984263bc | 333 | static int |
f4a3189a CP |
334 | nullfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, |
335 | struct ucred **credanonp) | |
984263bc | 336 | { |
67863d04 MD |
337 | struct null_mount *xmp = (void *)mp->mnt_data; |
338 | struct netcred *np; | |
339 | int error; | |
984263bc | 340 | |
67863d04 MD |
341 | np = vfs_export_lookup(mp, &xmp->export, nam); |
342 | if (np) { | |
343 | *extflagsp = np->netc_exflags; | |
344 | *credanonp = &np->netc_anon; | |
345 | error = 0; | |
346 | } else { | |
347 | error = EACCES; | |
348 | } | |
349 | return(error); | |
350 | #if 0 | |
984263bc MD |
351 | return VFS_CHECKEXP(MOUNTTONULLMOUNT(mp)->nullm_vfs, nam, |
352 | extflagsp, credanonp); | |
67863d04 MD |
353 | #endif |
354 | } | |
355 | ||
356 | int | |
357 | nullfs_export(struct mount *mp, int op, const struct export_args *export) | |
358 | { | |
359 | struct null_mount *xmp = (void *)mp->mnt_data; | |
360 | int error; | |
361 | ||
362 | switch(op) { | |
363 | case MOUNTCTL_SET_EXPORT: | |
364 | error = vfs_export(mp, &xmp->export, export); | |
365 | break; | |
366 | default: | |
367 | error = EOPNOTSUPP; | |
368 | break; | |
369 | } | |
370 | return(error); | |
371 | } | |
372 | ||
373 | /* | |
374 | * Pass through file handle conversion functions. | |
375 | */ | |
376 | static int | |
377 | nullfs_vptofh(struct vnode *vp, struct fid *fhp) | |
378 | { | |
379 | return VFS_VPTOFH(vp, fhp); | |
380 | } | |
381 | ||
382 | /* | |
383 | * Pass through file handle conversion functions. | |
384 | * | |
385 | * NOTE: currently only HAMMER uses rootvp. HAMMER uses rootvp only | |
386 | * to enforce PFS isolation. | |
387 | */ | |
388 | static int | |
389 | nullfs_fhtovp(struct mount *mp, struct vnode *rootvp, | |
390 | struct fid *fhp, struct vnode **vpp) | |
391 | { | |
392 | struct null_mount *xmp = MOUNTTONULLMOUNT(mp); | |
393 | ||
394 | return VFS_FHTOVP(xmp->nullm_vfs, xmp->nullm_rootvp, fhp, vpp); | |
984263bc MD |
395 | } |
396 | ||
984263bc | 397 | static int |
0f6997f9 MD |
398 | nullfs_extattrctl(struct mount *mp, int cmd, struct vnode *vp, |
399 | int attrnamespace, const char *attrname, struct ucred *cred) | |
984263bc | 400 | { |
0f6997f9 MD |
401 | return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, |
402 | vp, attrnamespace, attrname, cred); | |
984263bc MD |
403 | } |
404 | ||
75779c3c AH |
405 | static void |
406 | nullfs_ncpgen_set(struct mount *mp, struct namecache *ncp) | |
407 | { | |
408 | struct null_mount *xmp = MOUNTTONULLMOUNT(mp); | |
409 | ||
410 | VFS_NCPGEN_SET(xmp->nullm_vfs, ncp); | |
411 | } | |
412 | ||
413 | ||
414 | static int | |
415 | nullfs_ncpgen_test(struct mount *mp, struct namecache *ncp) | |
416 | { | |
417 | struct null_mount *xmp = MOUNTTONULLMOUNT(mp); | |
418 | ||
419 | return VFS_NCPGEN_TEST(xmp->nullm_vfs, ncp); | |
420 | } | |
421 | ||
d0e99d5d MD |
422 | static int |
423 | nullfs_modifying(struct mount *mp) | |
424 | { | |
425 | struct null_mount *xmp = MOUNTTONULLMOUNT(mp); | |
426 | int error; | |
427 | ||
428 | if (mp->mnt_flag & MNT_RDONLY) | |
429 | error = EROFS; | |
430 | else if (xmp->nullm_vfs) | |
431 | error = VFS_MODIFYING(xmp->nullm_vfs); | |
432 | else | |
433 | error = 0; | |
434 | return error; | |
435 | } | |
984263bc MD |
436 | |
437 | static struct vfsops null_vfsops = { | |
00369c4a | 438 | .vfs_flags = VFSOPSF_NOSYNCERTHR, |
43c45e8f HP |
439 | .vfs_mount = nullfs_mount, |
440 | .vfs_unmount = nullfs_unmount, | |
441 | .vfs_root = nullfs_root, | |
442 | .vfs_quotactl = nullfs_quotactl, | |
443 | .vfs_statfs = nullfs_statfs, | |
67863d04 MD |
444 | .vfs_extattrctl = nullfs_extattrctl, |
445 | .vfs_fhtovp = nullfs_fhtovp, | |
446 | .vfs_vptofh = nullfs_vptofh, | |
75779c3c AH |
447 | .vfs_ncpgen_set = nullfs_ncpgen_set, |
448 | .vfs_ncpgen_test = nullfs_ncpgen_test, | |
d0e99d5d MD |
449 | .vfs_checkexp = nullfs_checkexp, |
450 | .vfs_modifying = nullfs_modifying | |
984263bc MD |
451 | }; |
452 | ||
87f62b1c | 453 | VFS_SET(null_vfsops, null, VFCF_LOOPBACK | VFCF_MPSAFE); |
e5e63c20 | 454 | MODULE_VERSION(null, 1); |