1 /* 2 * Copyright (c) 1994 The Regents of the University of California. 3 * Copyright (c) 1994 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.7 (Berkeley) 3/5/94 38 * $Id: union_vfsops.c,v 1.8 1995/03/16 18:14:02 bde Exp $ 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 int union_root __P((struct mount *, struct vnode **)); 60 int union_statfs __P((struct mount *, struct statfs *, struct proc *)); 61 62 /* 63 * Mount union filesystem 64 */ 65 int 66 union_mount(mp, path, data, ndp, p) 67 struct mount *mp; 68 char *path; 69 caddr_t data; 70 struct nameidata *ndp; 71 struct proc *p; 72 { 73 int error = 0; 74 struct union_args args; 75 struct vnode *lowerrootvp = NULLVP; 76 struct vnode *upperrootvp = NULLVP; 77 struct union_mount *um; 78 struct ucred *cred = 0; 79 struct ucred *scred; 80 struct vattr va; 81 char *cp = 0; 82 int len; 83 u_int size; 84 85 #ifdef UNION_DIAGNOSTIC 86 printf("union_mount(mp = %x)\n", mp); 87 #endif 88 89 /* 90 * Update is a no-op 91 */ 92 if (mp->mnt_flag & MNT_UPDATE) { 93 /* 94 * Need to provide. 95 * 1. a way to convert between rdonly and rdwr mounts. 96 * 2. support for nfs exports. 97 */ 98 error = EOPNOTSUPP; 99 goto bad; 100 } 101 102 /* 103 * Take a copy of the process's credentials. This isn't 104 * quite right since the euid will always be zero and we 105 * want to get the "real" users credentials. So fix up 106 * the uid field after taking the copy. 107 */ 108 cred = crdup(p->p_ucred); 109 cred->cr_uid = p->p_cred->p_ruid; 110 111 /* 112 * Ensure the *real* user has write permission on the 113 * mounted-on directory. This allows the mount_union 114 * command to be made setuid root so allowing anyone 115 * to do union mounts onto any directory on which they 116 * have write permission and which they also own. 117 */ 118 error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); 119 if (error) 120 goto bad; 121 if ((va.va_uid != cred->cr_uid) && 122 (cred->cr_uid != 0)) { 123 error = EACCES; 124 goto bad; 125 } 126 error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p); 127 if (error) 128 goto bad; 129 130 /* 131 * Get argument 132 */ 133 error = copyin(data, (caddr_t)&args, sizeof(struct union_args)); 134 if (error) 135 goto bad; 136 137 lowerrootvp = mp->mnt_vnodecovered; 138 VREF(lowerrootvp); 139 140 /* 141 * Find upper node. Use the real process credentials, 142 * not the effective ones since this will have come 143 * through a setuid process (mount_union). All this 144 * messing around with permissions is entirely bogus 145 * and should be removed by allowing any user straight 146 * past the mount system call. 147 */ 148 scred = p->p_ucred; 149 p->p_ucred = cred; 150 NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, 151 UIO_USERSPACE, args.target, p); 152 p->p_ucred = scred; 153 154 error = namei(ndp); 155 if (error) 156 goto bad; 157 158 upperrootvp = ndp->ni_vp; 159 vrele(ndp->ni_dvp); 160 ndp->ni_dvp = NULL; 161 162 if (upperrootvp->v_type != VDIR) { 163 error = EINVAL; 164 goto bad; 165 } 166 167 um = (struct union_mount *) malloc(sizeof(struct union_mount), 168 M_UFSMNT, M_WAITOK); /* XXX */ 169 170 /* 171 * Keep a held reference to the target vnodes. 172 * They are vrele'd in union_unmount. 173 * 174 * Depending on the _BELOW flag, the filesystems are 175 * viewed in a different order. In effect, this is the 176 * same as providing a mount under option to the mount syscall. 177 */ 178 179 um->um_op = args.mntflags & UNMNT_OPMASK; 180 switch (um->um_op) { 181 case UNMNT_ABOVE: 182 um->um_lowervp = lowerrootvp; 183 um->um_uppervp = upperrootvp; 184 break; 185 186 case UNMNT_BELOW: 187 um->um_lowervp = upperrootvp; 188 um->um_uppervp = lowerrootvp; 189 break; 190 191 case UNMNT_REPLACE: 192 vrele(lowerrootvp); 193 lowerrootvp = NULLVP; 194 um->um_uppervp = upperrootvp; 195 um->um_lowervp = lowerrootvp; 196 break; 197 198 default: 199 error = EINVAL; 200 goto bad; 201 } 202 203 um->um_cred = cred; 204 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask; 205 206 /* 207 * Depending on what you think the MNT_LOCAL flag might mean, 208 * you may want the && to be || on the conditional below. 209 * At the moment it has been defined that the filesystem is 210 * only local if it is all local, ie the MNT_LOCAL flag implies 211 * that the entire namespace is local. If you think the MNT_LOCAL 212 * flag implies that some of the files might be stored locally 213 * then you will want to change the conditional. 214 */ 215 if (um->um_op == UNMNT_ABOVE) { 216 if (((um->um_lowervp == NULLVP) || 217 (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && 218 (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 219 mp->mnt_flag |= MNT_LOCAL; 220 } 221 222 /* 223 * Copy in the upper layer's RDONLY flag. This is for the benefit 224 * of lookup() which explicitly checks the flag, rather than asking 225 * the filesystem for it's own opinion. This means, that an update 226 * mount of the underlying filesystem to go from rdonly to rdwr 227 * will leave the unioned view as read-only. 228 */ 229 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); 230 231 /* 232 * This is a user mount. Privilege check for unmount 233 * will be done in union_unmount. 234 */ 235 mp->mnt_flag |= MNT_USER; 236 237 mp->mnt_data = (qaddr_t) um; 238 getnewfsid(mp, MOUNT_UNION); 239 240 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); 241 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); 242 243 switch (um->um_op) { 244 case UNMNT_ABOVE: 245 cp = "<above>"; 246 break; 247 case UNMNT_BELOW: 248 cp = "<below>"; 249 break; 250 case UNMNT_REPLACE: 251 cp = ""; 252 break; 253 } 254 len = strlen(cp); 255 bcopy(cp, mp->mnt_stat.f_mntfromname, len); 256 257 cp = mp->mnt_stat.f_mntfromname + len; 258 len = MNAMELEN - len; 259 260 (void) copyinstr(args.target, cp, len - 1, &size); 261 bzero(cp + size, len - size); 262 263 (void)union_statfs(mp, &mp->mnt_stat, p); 264 265 #ifdef UNION_DIAGNOSTIC 266 printf("union_mount: from %s, on %s\n", 267 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 268 #endif 269 return (0); 270 271 bad: 272 if (cred) 273 crfree(cred); 274 if (upperrootvp) 275 vrele(upperrootvp); 276 if (lowerrootvp) 277 vrele(lowerrootvp); 278 return (error); 279 } 280 281 /* 282 * VFS start. Nothing needed here - the start routine 283 * on the underlying filesystem(s) will have been called 284 * when that filesystem was mounted. 285 */ 286 int 287 union_start(mp, flags, p) 288 struct mount *mp; 289 int flags; 290 struct proc *p; 291 { 292 293 return (0); 294 } 295 296 /* 297 * Free reference to union layer 298 */ 299 int 300 union_unmount(mp, mntflags, p) 301 struct mount *mp; 302 int mntflags; 303 struct proc *p; 304 { 305 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 306 struct vnode *um_rootvp; 307 int error; 308 int flags = 0; 309 310 #ifdef UNION_DIAGNOSTIC 311 printf("union_unmount(mp = %x)\n", mp); 312 #endif 313 314 /* only the mounter, or superuser can unmount */ 315 if ((p->p_cred->p_ruid != um->um_cred->cr_uid) && 316 (error = suser(p->p_ucred, &p->p_acflag))) 317 return (error); 318 319 if (mntflags & MNT_FORCE) { 320 /* union can never be rootfs so don't check for it */ 321 if (!doforce) 322 return (EINVAL); 323 flags |= FORCECLOSE; 324 } 325 326 error = union_root(mp, &um_rootvp); 327 if (error) 328 return (error); 329 if (um_rootvp->v_usecount > 1) { 330 vput(um_rootvp); 331 return (EBUSY); 332 } 333 error = vflush(mp, um_rootvp, flags); 334 if (error) { 335 vput(um_rootvp); 336 return (error); 337 } 338 339 #ifdef UNION_DIAGNOSTIC 340 vprint("alias root of lower", um_rootvp); 341 #endif 342 /* 343 * Discard references to upper and lower target vnodes. 344 */ 345 if (um->um_lowervp) 346 vrele(um->um_lowervp); 347 vrele(um->um_uppervp); 348 crfree(um->um_cred); 349 /* 350 * Release reference on underlying root vnode 351 */ 352 vput(um_rootvp); 353 /* 354 * And blow it away for future re-use 355 */ 356 vgone(um_rootvp); 357 /* 358 * Finally, throw away the union_mount structure 359 */ 360 free(mp->mnt_data, M_UFSMNT); /* XXX */ 361 mp->mnt_data = 0; 362 return (0); 363 } 364 365 int 366 union_root(mp, vpp) 367 struct mount *mp; 368 struct vnode **vpp; 369 { 370 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 371 int error; 372 int loselock; 373 374 #ifdef UNION_DIAGNOSTIC 375 printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp, 376 um->um_lowervp, 377 um->um_uppervp); 378 #endif 379 380 /* 381 * Return locked reference to root. 382 */ 383 VREF(um->um_uppervp); 384 if ((um->um_op == UNMNT_BELOW) && 385 VOP_ISLOCKED(um->um_uppervp)) { 386 loselock = 1; 387 } else { 388 VOP_LOCK(um->um_uppervp); 389 loselock = 0; 390 } 391 if (um->um_lowervp) 392 VREF(um->um_lowervp); 393 error = union_allocvp(vpp, mp, 394 (struct vnode *) 0, 395 (struct vnode *) 0, 396 (struct componentname *) 0, 397 um->um_uppervp, 398 um->um_lowervp); 399 400 if (error) { 401 if (!loselock) 402 VOP_UNLOCK(um->um_uppervp); 403 vrele(um->um_uppervp); 404 if (um->um_lowervp) 405 vrele(um->um_lowervp); 406 } else { 407 (*vpp)->v_flag |= VROOT; 408 if (loselock) 409 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 410 } 411 412 return (error); 413 } 414 415 int 416 union_quotactl(mp, cmd, uid, arg, p) 417 struct mount *mp; 418 int cmd; 419 uid_t uid; 420 caddr_t arg; 421 struct proc *p; 422 { 423 424 return (EOPNOTSUPP); 425 } 426 427 int 428 union_statfs(mp, sbp, p) 429 struct mount *mp; 430 struct statfs *sbp; 431 struct proc *p; 432 { 433 int error; 434 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 435 struct statfs mstat; 436 int lbsize; 437 438 #ifdef UNION_DIAGNOSTIC 439 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 440 um->um_lowervp, 441 um->um_uppervp); 442 #endif 443 444 bzero(&mstat, sizeof(mstat)); 445 446 if (um->um_lowervp) { 447 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 448 if (error) 449 return (error); 450 } 451 452 /* now copy across the "interesting" information and fake the rest */ 453 #if 0 454 sbp->f_type = mstat.f_type; 455 sbp->f_flags = mstat.f_flags; 456 sbp->f_bsize = mstat.f_bsize; 457 sbp->f_iosize = mstat.f_iosize; 458 #endif 459 lbsize = mstat.f_bsize; 460 sbp->f_blocks = mstat.f_blocks; 461 sbp->f_bfree = mstat.f_bfree; 462 sbp->f_bavail = mstat.f_bavail; 463 sbp->f_files = mstat.f_files; 464 sbp->f_ffree = mstat.f_ffree; 465 466 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 467 if (error) 468 return (error); 469 470 sbp->f_type = MOUNT_UNION; 471 sbp->f_flags = mstat.f_flags; 472 sbp->f_bsize = mstat.f_bsize; 473 sbp->f_iosize = mstat.f_iosize; 474 475 /* 476 * if the lower and upper blocksizes differ, then frig the 477 * block counts so that the sizes reported by df make some 478 * kind of sense. none of this makes sense though. 479 */ 480 481 if (mstat.f_bsize != lbsize) { 482 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; 483 sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize; 484 sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize; 485 } 486 sbp->f_blocks += mstat.f_blocks; 487 sbp->f_bfree += mstat.f_bfree; 488 sbp->f_bavail += mstat.f_bavail; 489 sbp->f_files += mstat.f_files; 490 sbp->f_ffree += mstat.f_ffree; 491 492 if (sbp != &mp->mnt_stat) { 493 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 494 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 495 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 496 } 497 return (0); 498 } 499 500 int 501 union_sync(mp, waitfor, cred, p) 502 struct mount *mp; 503 int waitfor; 504 struct ucred *cred; 505 struct proc *p; 506 { 507 508 /* 509 * XXX - Assumes no data cached at union layer. 510 */ 511 return (0); 512 } 513 514 int 515 union_vget(mp, ino, vpp) 516 struct mount *mp; 517 ino_t ino; 518 struct vnode **vpp; 519 { 520 521 return (EOPNOTSUPP); 522 } 523 524 int 525 union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) 526 struct mount *mp; 527 struct fid *fidp; 528 struct mbuf *nam; 529 struct vnode **vpp; 530 int *exflagsp; 531 struct ucred **credanonp; 532 { 533 534 return (EOPNOTSUPP); 535 } 536 537 int 538 union_vptofh(vp, fhp) 539 struct vnode *vp; 540 struct fid *fhp; 541 { 542 543 return (EOPNOTSUPP); 544 } 545 546 int union_init __P((void)); 547 548 struct vfsops union_vfsops = { 549 union_mount, 550 union_start, 551 union_unmount, 552 union_root, 553 union_quotactl, 554 union_statfs, 555 union_sync, 556 union_vget, 557 union_fhtovp, 558 union_vptofh, 559 union_init, 560 }; 561 562 VFS_SET(union_vfsops, union, MOUNT_UNION, VFCF_LOOPBACK); 563