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