1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1992, 1993, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software donated to Berkeley by 8 * Jan-Simon Pendry. 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 * 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. 21 * 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 32 * SUCH DAMAGE. 33 * 34 * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 35 * 36 * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 37 * $FreeBSD$ 38 */ 39 40 /* 41 * Null Layer 42 * (See null_vnops.c for a description of what this does.) 43 */ 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/fcntl.h> 48 #include <sys/kernel.h> 49 #include <sys/lock.h> 50 #include <sys/malloc.h> 51 #include <sys/mount.h> 52 #include <sys/namei.h> 53 #include <sys/proc.h> 54 #include <sys/vnode.h> 55 #include <sys/jail.h> 56 57 #include <fs/nullfs/null.h> 58 59 static MALLOC_DEFINE(M_NULLFSMNT, "nullfs_mount", "NULLFS mount structure"); 60 61 static vfs_fhtovp_t nullfs_fhtovp; 62 static vfs_mount_t nullfs_mount; 63 static vfs_quotactl_t nullfs_quotactl; 64 static vfs_root_t nullfs_root; 65 static vfs_sync_t nullfs_sync; 66 static vfs_statfs_t nullfs_statfs; 67 static vfs_unmount_t nullfs_unmount; 68 static vfs_vget_t nullfs_vget; 69 static vfs_extattrctl_t nullfs_extattrctl; 70 71 /* 72 * Mount null layer 73 */ 74 static int 75 nullfs_mount(struct mount *mp) 76 { 77 int error = 0; 78 struct vnode *lowerrootvp, *vp; 79 struct vnode *nullm_rootvp; 80 struct null_mount *xmp; 81 char *target; 82 int isvnunlocked = 0, len; 83 struct nameidata nd, *ndp = &nd; 84 85 NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); 86 87 if (mp->mnt_flag & MNT_ROOTFS) 88 return (EOPNOTSUPP); 89 90 /* 91 * Update is a no-op 92 */ 93 if (mp->mnt_flag & MNT_UPDATE) { 94 /* 95 * Only support update mounts for NFS export. 96 */ 97 if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) 98 return (0); 99 else 100 return (EOPNOTSUPP); 101 } 102 103 /* 104 * Get argument 105 */ 106 error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 107 if (error || target[len - 1] != '\0') 108 return (EINVAL); 109 110 /* 111 * Unlock lower node to avoid possible deadlock. 112 */ 113 if ((mp->mnt_vnodecovered->v_op == &null_vnodeops) && 114 VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) { 115 VOP_UNLOCK(mp->mnt_vnodecovered, 0); 116 isvnunlocked = 1; 117 } 118 /* 119 * Find lower node 120 */ 121 NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, curthread); 122 error = namei(ndp); 123 124 /* 125 * Re-lock vnode. 126 * XXXKIB This is deadlock-prone as well. 127 */ 128 if (isvnunlocked) 129 vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); 130 131 if (error) 132 return (error); 133 NDFREE(ndp, NDF_ONLY_PNBUF); 134 135 /* 136 * Sanity check on lower vnode 137 */ 138 lowerrootvp = ndp->ni_vp; 139 140 /* 141 * Check multi null mount to avoid `lock against myself' panic. 142 */ 143 if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) { 144 NULLFSDEBUG("nullfs_mount: multi null mount?\n"); 145 vput(lowerrootvp); 146 return (EDEADLK); 147 } 148 149 xmp = (struct null_mount *) malloc(sizeof(struct null_mount), 150 M_NULLFSMNT, M_WAITOK | M_ZERO); 151 152 /* 153 * Save reference to underlying FS 154 */ 155 xmp->nullm_vfs = lowerrootvp->v_mount; 156 157 /* 158 * Save reference. Each mount also holds 159 * a reference on the root vnode. 160 */ 161 error = null_nodeget(mp, lowerrootvp, &vp); 162 /* 163 * Make sure the node alias worked 164 */ 165 if (error) { 166 free(xmp, M_NULLFSMNT); 167 return (error); 168 } 169 170 /* 171 * Keep a held reference to the root vnode. 172 * It is vrele'd in nullfs_unmount. 173 */ 174 nullm_rootvp = vp; 175 nullm_rootvp->v_vflag |= VV_ROOT; 176 xmp->nullm_rootvp = nullm_rootvp; 177 178 /* 179 * Unlock the node (either the lower or the alias) 180 */ 181 VOP_UNLOCK(vp, 0); 182 183 if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) { 184 MNT_ILOCK(mp); 185 mp->mnt_flag |= MNT_LOCAL; 186 MNT_IUNLOCK(mp); 187 } 188 189 xmp->nullm_flags |= NULLM_CACHE; 190 if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 || 191 (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0) 192 xmp->nullm_flags &= ~NULLM_CACHE; 193 194 MNT_ILOCK(mp); 195 if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 196 mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 197 (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED | 198 MNTK_EXTENDED_SHARED); 199 } 200 mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT; 201 mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 202 (MNTK_USES_BCACHE | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS); 203 MNT_IUNLOCK(mp); 204 mp->mnt_data = xmp; 205 vfs_getnewfsid(mp); 206 if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 207 MNT_ILOCK(xmp->nullm_vfs); 208 TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp, 209 mnt_upper_link); 210 MNT_IUNLOCK(xmp->nullm_vfs); 211 } 212 213 vfs_mountedfrom(mp, target); 214 215 NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", 216 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 217 return (0); 218 } 219 220 /* 221 * Free reference to null layer 222 */ 223 static int 224 nullfs_unmount(mp, mntflags) 225 struct mount *mp; 226 int mntflags; 227 { 228 struct null_mount *mntdata; 229 struct mount *ump; 230 int error, flags; 231 232 NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); 233 234 if (mntflags & MNT_FORCE) 235 flags = FORCECLOSE; 236 else 237 flags = 0; 238 239 /* There is 1 extra root vnode reference (nullm_rootvp). */ 240 error = vflush(mp, 1, flags, curthread); 241 if (error) 242 return (error); 243 244 /* 245 * Finally, throw away the null_mount structure 246 */ 247 mntdata = mp->mnt_data; 248 ump = mntdata->nullm_vfs; 249 if ((mntdata->nullm_flags & NULLM_CACHE) != 0) { 250 MNT_ILOCK(ump); 251 while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) { 252 ump->mnt_kern_flag |= MNTK_VGONE_WAITER; 253 msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0); 254 } 255 TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link); 256 MNT_IUNLOCK(ump); 257 } 258 mp->mnt_data = NULL; 259 free(mntdata, M_NULLFSMNT); 260 return (0); 261 } 262 263 static int 264 nullfs_root(mp, flags, vpp) 265 struct mount *mp; 266 int flags; 267 struct vnode **vpp; 268 { 269 struct vnode *vp; 270 271 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp, 272 (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, 273 (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); 274 275 /* 276 * Return locked reference to root. 277 */ 278 vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; 279 VREF(vp); 280 281 ASSERT_VOP_UNLOCKED(vp, "root vnode is locked"); 282 vn_lock(vp, flags | LK_RETRY); 283 *vpp = vp; 284 return 0; 285 } 286 287 static int 288 nullfs_quotactl(mp, cmd, uid, arg) 289 struct mount *mp; 290 int cmd; 291 uid_t uid; 292 void *arg; 293 { 294 return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg); 295 } 296 297 static int 298 nullfs_statfs(mp, sbp) 299 struct mount *mp; 300 struct statfs *sbp; 301 { 302 int error; 303 struct statfs *mstat; 304 305 NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, 306 (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, 307 (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); 308 309 mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); 310 311 error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat); 312 if (error) { 313 free(mstat, M_STATFS); 314 return (error); 315 } 316 317 /* now copy across the "interesting" information and fake the rest */ 318 sbp->f_type = mstat->f_type; 319 sbp->f_flags = (sbp->f_flags & (MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID | 320 MNT_UNION | MNT_NOSYMFOLLOW | MNT_AUTOMOUNTED)) | 321 (mstat->f_flags & ~(MNT_ROOTFS | MNT_AUTOMOUNTED)); 322 sbp->f_bsize = mstat->f_bsize; 323 sbp->f_iosize = mstat->f_iosize; 324 sbp->f_blocks = mstat->f_blocks; 325 sbp->f_bfree = mstat->f_bfree; 326 sbp->f_bavail = mstat->f_bavail; 327 sbp->f_files = mstat->f_files; 328 sbp->f_ffree = mstat->f_ffree; 329 330 free(mstat, M_STATFS); 331 return (0); 332 } 333 334 static int 335 nullfs_sync(mp, waitfor) 336 struct mount *mp; 337 int waitfor; 338 { 339 /* 340 * XXX - Assumes no data cached at null layer. 341 */ 342 return (0); 343 } 344 345 static int 346 nullfs_vget(mp, ino, flags, vpp) 347 struct mount *mp; 348 ino_t ino; 349 int flags; 350 struct vnode **vpp; 351 { 352 int error; 353 354 KASSERT((flags & LK_TYPE_MASK) != 0, 355 ("nullfs_vget: no lock requested")); 356 357 error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp); 358 if (error != 0) 359 return (error); 360 return (null_nodeget(mp, *vpp, vpp)); 361 } 362 363 static int 364 nullfs_fhtovp(mp, fidp, flags, vpp) 365 struct mount *mp; 366 struct fid *fidp; 367 int flags; 368 struct vnode **vpp; 369 { 370 int error; 371 372 error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags, 373 vpp); 374 if (error != 0) 375 return (error); 376 return (null_nodeget(mp, *vpp, vpp)); 377 } 378 379 static int 380 nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname) 381 struct mount *mp; 382 int cmd; 383 struct vnode *filename_vp; 384 int namespace; 385 const char *attrname; 386 { 387 388 return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, 389 filename_vp, namespace, attrname)); 390 } 391 392 static void 393 nullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp) 394 { 395 struct vnode *vp; 396 397 vp = null_hashget(mp, lowervp); 398 if (vp == NULL) 399 return; 400 VTONULL(vp)->null_flags |= NULLV_NOUNLOCK; 401 vgone(vp); 402 vput(vp); 403 } 404 405 static void 406 nullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp) 407 { 408 struct vnode *vp; 409 struct null_node *xp; 410 411 vp = null_hashget(mp, lowervp); 412 if (vp == NULL) 413 return; 414 xp = VTONULL(vp); 415 xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK; 416 vhold(vp); 417 vunref(vp); 418 419 if (vp->v_usecount == 0) { 420 /* 421 * If vunref() dropped the last use reference on the 422 * nullfs vnode, it must be reclaimed, and its lock 423 * was split from the lower vnode lock. Need to do 424 * extra unlock before allowing the final vdrop() to 425 * free the vnode. 426 */ 427 KASSERT((vp->v_iflag & VI_DOOMED) != 0, 428 ("not reclaimed nullfs vnode %p", vp)); 429 VOP_UNLOCK(vp, 0); 430 } else { 431 /* 432 * Otherwise, the nullfs vnode still shares the lock 433 * with the lower vnode, and must not be unlocked. 434 * Also clear the NULLV_NOUNLOCK, the flag is not 435 * relevant for future reclamations. 436 */ 437 ASSERT_VOP_ELOCKED(vp, "unlink_lowervp"); 438 KASSERT((vp->v_iflag & VI_DOOMED) == 0, 439 ("reclaimed nullfs vnode %p", vp)); 440 xp->null_flags &= ~NULLV_NOUNLOCK; 441 } 442 vdrop(vp); 443 } 444 445 static struct vfsops null_vfsops = { 446 .vfs_extattrctl = nullfs_extattrctl, 447 .vfs_fhtovp = nullfs_fhtovp, 448 .vfs_init = nullfs_init, 449 .vfs_mount = nullfs_mount, 450 .vfs_quotactl = nullfs_quotactl, 451 .vfs_root = nullfs_root, 452 .vfs_statfs = nullfs_statfs, 453 .vfs_sync = nullfs_sync, 454 .vfs_uninit = nullfs_uninit, 455 .vfs_unmount = nullfs_unmount, 456 .vfs_vget = nullfs_vget, 457 .vfs_reclaim_lowervp = nullfs_reclaim_lowervp, 458 .vfs_unlink_lowervp = nullfs_unlink_lowervp, 459 }; 460 461 VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL); 462