1 /* 2 * Copyright (c) 1994, 1995 The Regents of the University of California. 3 * Copyright (c) 1994, 1995 Jan-Simon Pendry. 4 * All rights reserved. 5 * 6 * This code is derived from software donated to Berkeley by 7 * Jan-Simon Pendry. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 38 * $FreeBSD$ 39 */ 40 41 /* 42 * Union Layer 43 */ 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/kernel.h> 48 #include <sys/time.h> 49 #include <sys/types.h> 50 #include <sys/proc.h> 51 #include <sys/vnode.h> 52 #include <sys/mount.h> 53 #include <sys/namei.h> 54 #include <sys/malloc.h> 55 #include <sys/filedesc.h> 56 #include <sys/queue.h> 57 #include <miscfs/union/union.h> 58 59 extern int union_init __P((struct vfsconf *)); 60 61 extern int union_fhtovp __P((struct mount *mp, struct fid *fidp, 62 struct mbuf *nam, struct vnode **vpp, 63 int *exflagsp, struct ucred **credanonp)); 64 extern int union_mount __P((struct mount *mp, char *path, caddr_t data, 65 struct nameidata *ndp, struct proc *p)); 66 extern int union_quotactl __P((struct mount *mp, int cmd, uid_t uid, 67 caddr_t arg, struct proc *p)); 68 extern int union_root __P((struct mount *mp, struct vnode **vpp)); 69 extern int union_start __P((struct mount *mp, int flags, struct proc *p)); 70 extern int union_statfs __P((struct mount *mp, struct statfs *sbp, 71 struct proc *p)); 72 extern int union_sync __P((struct mount *mp, int waitfor, 73 struct ucred *cred, struct proc *p)); 74 extern int union_unmount __P((struct mount *mp, int mntflags, 75 struct proc *p)); 76 extern int union_vget __P((struct mount *mp, ino_t ino, 77 struct vnode **vpp)); 78 extern int union_vptofh __P((struct vnode *vp, struct fid *fhp)); 79 80 /* 81 * Mount union filesystem 82 */ 83 int 84 union_mount(mp, path, data, ndp, p) 85 struct mount *mp; 86 char *path; 87 caddr_t data; 88 struct nameidata *ndp; 89 struct proc *p; 90 { 91 int error = 0; 92 struct union_args args; 93 struct vnode *lowerrootvp = NULLVP; 94 struct vnode *upperrootvp = NULLVP; 95 struct union_mount *um = 0; 96 struct ucred *cred = 0; 97 struct ucred *scred; 98 struct vattr va; 99 char *cp = 0; 100 int len; 101 u_int size; 102 103 #ifdef UNION_DIAGNOSTIC 104 printf("union_mount(mp = %x)\n", mp); 105 #endif 106 107 /* 108 * Update is a no-op 109 */ 110 if (mp->mnt_flag & MNT_UPDATE) { 111 /* 112 * Need to provide. 113 * 1. a way to convert between rdonly and rdwr mounts. 114 * 2. support for nfs exports. 115 */ 116 error = EOPNOTSUPP; 117 goto bad; 118 } 119 120 /* 121 * Get argument 122 */ 123 error = copyin(data, (caddr_t)&args, sizeof(struct union_args)); 124 if (error) 125 goto bad; 126 127 lowerrootvp = mp->mnt_vnodecovered; 128 VREF(lowerrootvp); 129 130 /* 131 * Find upper node. 132 */ 133 NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, 134 UIO_USERSPACE, args.target, p); 135 136 error = namei(ndp); 137 if (error) 138 goto bad; 139 140 upperrootvp = ndp->ni_vp; 141 vrele(ndp->ni_dvp); 142 ndp->ni_dvp = NULL; 143 144 if (upperrootvp->v_type != VDIR) { 145 error = EINVAL; 146 goto bad; 147 } 148 149 um = (struct union_mount *) malloc(sizeof(struct union_mount), 150 M_UFSMNT, M_WAITOK); /* XXX */ 151 152 /* 153 * Keep a held reference to the target vnodes. 154 * They are vrele'd in union_unmount. 155 * 156 * Depending on the _BELOW flag, the filesystems are 157 * viewed in a different order. In effect, this is the 158 * same as providing a mount under option to the mount syscall. 159 */ 160 161 um->um_op = args.mntflags & UNMNT_OPMASK; 162 switch (um->um_op) { 163 case UNMNT_ABOVE: 164 um->um_lowervp = lowerrootvp; 165 um->um_uppervp = upperrootvp; 166 break; 167 168 case UNMNT_BELOW: 169 um->um_lowervp = upperrootvp; 170 um->um_uppervp = lowerrootvp; 171 break; 172 173 case UNMNT_REPLACE: 174 vrele(lowerrootvp); 175 lowerrootvp = NULLVP; 176 um->um_uppervp = upperrootvp; 177 um->um_lowervp = lowerrootvp; 178 break; 179 180 default: 181 error = EINVAL; 182 goto bad; 183 } 184 185 /* 186 * Unless the mount is readonly, ensure that the top layer 187 * supports whiteout operations 188 */ 189 if ((mp->mnt_flag & MNT_RDONLY) == 0) { 190 error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP); 191 if (error) 192 goto bad; 193 } 194 195 um->um_cred = p->p_ucred; 196 crhold(um->um_cred); 197 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask; 198 199 /* 200 * Depending on what you think the MNT_LOCAL flag might mean, 201 * you may want the && to be || on the conditional below. 202 * At the moment it has been defined that the filesystem is 203 * only local if it is all local, ie the MNT_LOCAL flag implies 204 * that the entire namespace is local. If you think the MNT_LOCAL 205 * flag implies that some of the files might be stored locally 206 * then you will want to change the conditional. 207 */ 208 if (um->um_op == UNMNT_ABOVE) { 209 if (((um->um_lowervp == NULLVP) || 210 (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && 211 (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 212 mp->mnt_flag |= MNT_LOCAL; 213 } 214 215 /* 216 * Copy in the upper layer's RDONLY flag. This is for the benefit 217 * of lookup() which explicitly checks the flag, rather than asking 218 * the filesystem for it's own opinion. This means, that an update 219 * mount of the underlying filesystem to go from rdonly to rdwr 220 * will leave the unioned view as read-only. 221 */ 222 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); 223 224 mp->mnt_data = (qaddr_t) um; 225 vfs_getnewfsid(mp); 226 227 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); 228 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); 229 230 switch (um->um_op) { 231 case UNMNT_ABOVE: 232 cp = "<above>:"; 233 break; 234 case UNMNT_BELOW: 235 cp = "<below>:"; 236 break; 237 case UNMNT_REPLACE: 238 cp = ""; 239 break; 240 } 241 len = strlen(cp); 242 bcopy(cp, mp->mnt_stat.f_mntfromname, len); 243 244 cp = mp->mnt_stat.f_mntfromname + len; 245 len = MNAMELEN - len; 246 247 (void) copyinstr(args.target, cp, len - 1, &size); 248 bzero(cp + size, len - size); 249 250 (void)union_statfs(mp, &mp->mnt_stat, p); 251 252 #ifdef UNION_DIAGNOSTIC 253 printf("union_mount: from %s, on %s\n", 254 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 255 #endif 256 return (0); 257 258 bad: 259 if (um) 260 free(um, M_UFSMNT); 261 if (cred) 262 crfree(cred); 263 if (upperrootvp) 264 vrele(upperrootvp); 265 if (lowerrootvp) 266 vrele(lowerrootvp); 267 return (error); 268 } 269 270 /* 271 * VFS start. Nothing needed here - the start routine 272 * on the underlying filesystem(s) will have been called 273 * when that filesystem was mounted. 274 */ 275 int 276 union_start(mp, flags, p) 277 struct mount *mp; 278 int flags; 279 struct proc *p; 280 { 281 282 return (0); 283 } 284 285 /* 286 * Free reference to union layer 287 */ 288 int 289 union_unmount(mp, mntflags, p) 290 struct mount *mp; 291 int mntflags; 292 struct proc *p; 293 { 294 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 295 struct vnode *um_rootvp; 296 int error; 297 int freeing; 298 int flags = 0; 299 300 #ifdef UNION_DIAGNOSTIC 301 printf("union_unmount(mp = %x)\n", mp); 302 #endif 303 304 if (mntflags & MNT_FORCE) 305 flags |= FORCECLOSE; 306 307 if (error = union_root(mp, &um_rootvp)) 308 return (error); 309 310 /* 311 * Keep flushing vnodes from the mount list. 312 * This is needed because of the un_pvp held 313 * reference to the parent vnode. 314 * If more vnodes have been freed on a given pass, 315 * the try again. The loop will iterate at most 316 * (d) times, where (d) is the maximum tree depth 317 * in the filesystem. 318 */ 319 for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) { 320 struct vnode *vp; 321 int n; 322 323 /* count #vnodes held on mount list */ 324 for (n = 0, vp = mp->mnt_vnodelist.lh_first; 325 vp != NULLVP; 326 vp = vp->v_mntvnodes.le_next) 327 n++; 328 329 /* if this is unchanged then stop */ 330 if (n == freeing) 331 break; 332 333 /* otherwise try once more time */ 334 freeing = n; 335 } 336 337 /* At this point the root vnode should have a single reference */ 338 if (um_rootvp->v_usecount > 1) { 339 vput(um_rootvp); 340 return (EBUSY); 341 } 342 343 #ifdef UNION_DIAGNOSTIC 344 vprint("union root", um_rootvp); 345 #endif 346 /* 347 * Discard references to upper and lower target vnodes. 348 */ 349 if (um->um_lowervp) 350 vrele(um->um_lowervp); 351 vrele(um->um_uppervp); 352 crfree(um->um_cred); 353 /* 354 * Release reference on underlying root vnode 355 */ 356 vput(um_rootvp); 357 /* 358 * And blow it away for future re-use 359 */ 360 vgone(um_rootvp); 361 /* 362 * Finally, throw away the union_mount structure 363 */ 364 free(mp->mnt_data, M_UFSMNT); /* XXX */ 365 mp->mnt_data = 0; 366 return (0); 367 } 368 369 int 370 union_root(mp, vpp) 371 struct mount *mp; 372 struct vnode **vpp; 373 { 374 struct proc *p = curproc; /* XXX */ 375 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 376 int error; 377 int loselock; 378 379 /* 380 * Return locked reference to root. 381 */ 382 VREF(um->um_uppervp); 383 if ((um->um_op == UNMNT_BELOW) && 384 VOP_ISLOCKED(um->um_uppervp)) { 385 loselock = 1; 386 } else { 387 vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p); 388 loselock = 0; 389 } 390 if (um->um_lowervp) 391 VREF(um->um_lowervp); 392 error = union_allocvp(vpp, mp, 393 (struct vnode *) 0, 394 (struct vnode *) 0, 395 (struct componentname *) 0, 396 um->um_uppervp, 397 um->um_lowervp, 398 1); 399 400 if (error) { 401 if (loselock) 402 vrele(um->um_uppervp); 403 else 404 vput(um->um_uppervp); 405 if (um->um_lowervp) 406 vrele(um->um_lowervp); 407 } else { 408 if (loselock) 409 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 410 } 411 412 return (error); 413 } 414 415 int 416 union_statfs(mp, sbp, p) 417 struct mount *mp; 418 struct statfs *sbp; 419 struct proc *p; 420 { 421 int error; 422 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 423 struct statfs mstat; 424 int lbsize; 425 426 #ifdef UNION_DIAGNOSTIC 427 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 428 um->um_lowervp, 429 um->um_uppervp); 430 #endif 431 432 bzero(&mstat, sizeof(mstat)); 433 434 if (um->um_lowervp) { 435 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 436 if (error) 437 return (error); 438 } 439 440 /* now copy across the "interesting" information and fake the rest */ 441 #if 0 442 sbp->f_type = mstat.f_type; 443 sbp->f_flags = mstat.f_flags; 444 sbp->f_bsize = mstat.f_bsize; 445 sbp->f_iosize = mstat.f_iosize; 446 #endif 447 lbsize = mstat.f_bsize; 448 sbp->f_blocks = mstat.f_blocks; 449 sbp->f_bfree = mstat.f_bfree; 450 sbp->f_bavail = mstat.f_bavail; 451 sbp->f_files = mstat.f_files; 452 sbp->f_ffree = mstat.f_ffree; 453 454 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 455 if (error) 456 return (error); 457 458 sbp->f_flags = mstat.f_flags; 459 sbp->f_bsize = mstat.f_bsize; 460 sbp->f_iosize = mstat.f_iosize; 461 462 /* 463 * if the lower and upper blocksizes differ, then frig the 464 * block counts so that the sizes reported by df make some 465 * kind of sense. none of this makes sense though. 466 */ 467 468 if (mstat.f_bsize != lbsize) 469 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; 470 471 /* 472 * The "total" fields count total resources in all layers, 473 * the "free" fields count only those resources which are 474 * free in the upper layer (since only the upper layer 475 * is writeable). 476 */ 477 sbp->f_blocks += mstat.f_blocks; 478 sbp->f_bfree = mstat.f_bfree; 479 sbp->f_bavail = mstat.f_bavail; 480 sbp->f_files += mstat.f_files; 481 sbp->f_ffree = mstat.f_ffree; 482 483 if (sbp != &mp->mnt_stat) { 484 sbp->f_type = mp->mnt_vfc->vfc_typenum; 485 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 486 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 487 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 488 } 489 return (0); 490 } 491 492 /* 493 * XXX - Assumes no data cached at union layer. 494 */ 495 #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \ 496 struct proc *)))nullop) 497 498 #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \ 499 struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp) 500 #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \ 501 struct proc *)))eopnotsupp) 502 #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \ 503 size_t, struct proc *)))eopnotsupp) 504 #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \ 505 eopnotsupp) 506 #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp) 507 508 struct vfsops union_vfsops = { 509 union_mount, 510 union_start, 511 union_unmount, 512 union_root, 513 union_quotactl, 514 union_statfs, 515 union_sync, 516 union_vget, 517 union_fhtovp, 518 union_vptofh, 519 union_init, 520 }; 521 522 VFS_SET(union_vfsops, union, MOUNT_UNION, VFCF_LOOPBACK); 523