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