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