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.8 (Berkeley) 04/24/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 flags = 0; 274 extern int doforce; 275 276 #ifdef UNION_DIAGNOSTIC 277 printf("union_unmount(mp = %x)\n", mp); 278 #endif 279 280 /* only the mounter, or superuser can unmount */ 281 if ((p->p_cred->p_ruid != um->um_cred->cr_uid) && 282 (error = suser(p->p_ucred, &p->p_acflag))) 283 return (error); 284 285 if (mntflags & MNT_FORCE) { 286 /* union can never be rootfs so don't check for it */ 287 if (!doforce) 288 return (EINVAL); 289 flags |= FORCECLOSE; 290 } 291 292 if (error = union_root(mp, &um_rootvp)) 293 return (error); 294 if (um_rootvp->v_usecount > 1) { 295 vput(um_rootvp); 296 return (EBUSY); 297 } 298 if (error = vflush(mp, um_rootvp, flags)) { 299 vput(um_rootvp); 300 return (error); 301 } 302 303 #ifdef UNION_DIAGNOSTIC 304 vprint("alias root of lower", um_rootvp); 305 #endif 306 /* 307 * Discard references to upper and lower target vnodes. 308 */ 309 if (um->um_lowervp) 310 vrele(um->um_lowervp); 311 vrele(um->um_uppervp); 312 crfree(um->um_cred); 313 /* 314 * Release reference on underlying root vnode 315 */ 316 vput(um_rootvp); 317 /* 318 * And blow it away for future re-use 319 */ 320 vgone(um_rootvp); 321 /* 322 * Finally, throw away the union_mount structure 323 */ 324 free(mp->mnt_data, M_UFSMNT); /* XXX */ 325 mp->mnt_data = 0; 326 return (0); 327 } 328 329 int 330 union_root(mp, vpp) 331 struct mount *mp; 332 struct vnode **vpp; 333 { 334 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 335 int error; 336 int loselock; 337 338 #ifdef UNION_DIAGNOSTIC 339 printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp, 340 um->um_lowervp, 341 um->um_uppervp); 342 #endif 343 344 /* 345 * Return locked reference to root. 346 */ 347 VREF(um->um_uppervp); 348 if ((um->um_op == UNMNT_BELOW) && 349 VOP_ISLOCKED(um->um_uppervp)) { 350 loselock = 1; 351 } else { 352 VOP_LOCK(um->um_uppervp); 353 loselock = 0; 354 } 355 if (um->um_lowervp) 356 VREF(um->um_lowervp); 357 error = union_allocvp(vpp, mp, 358 (struct vnode *) 0, 359 (struct vnode *) 0, 360 (struct componentname *) 0, 361 um->um_uppervp, 362 um->um_lowervp); 363 364 if (error) { 365 if (!loselock) 366 VOP_UNLOCK(um->um_uppervp); 367 vrele(um->um_uppervp); 368 if (um->um_lowervp) 369 vrele(um->um_lowervp); 370 } else { 371 if (loselock) 372 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 373 } 374 375 return (error); 376 } 377 378 int 379 union_quotactl(mp, cmd, uid, arg, p) 380 struct mount *mp; 381 int cmd; 382 uid_t uid; 383 caddr_t arg; 384 struct proc *p; 385 { 386 387 return (EOPNOTSUPP); 388 } 389 390 int 391 union_statfs(mp, sbp, p) 392 struct mount *mp; 393 struct statfs *sbp; 394 struct proc *p; 395 { 396 int error; 397 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 398 struct statfs mstat; 399 int lbsize; 400 401 #ifdef UNION_DIAGNOSTIC 402 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 403 um->um_lowervp, 404 um->um_uppervp); 405 #endif 406 407 bzero(&mstat, sizeof(mstat)); 408 409 if (um->um_lowervp) { 410 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 411 if (error) 412 return (error); 413 } 414 415 /* now copy across the "interesting" information and fake the rest */ 416 #if 0 417 sbp->f_type = mstat.f_type; 418 sbp->f_flags = mstat.f_flags; 419 sbp->f_bsize = mstat.f_bsize; 420 sbp->f_iosize = mstat.f_iosize; 421 #endif 422 lbsize = mstat.f_bsize; 423 sbp->f_blocks = mstat.f_blocks; 424 sbp->f_bfree = mstat.f_bfree; 425 sbp->f_bavail = mstat.f_bavail; 426 sbp->f_files = mstat.f_files; 427 sbp->f_ffree = mstat.f_ffree; 428 429 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 430 if (error) 431 return (error); 432 433 sbp->f_type = MOUNT_UNION; 434 sbp->f_flags = mstat.f_flags; 435 sbp->f_bsize = mstat.f_bsize; 436 sbp->f_iosize = mstat.f_iosize; 437 438 /* 439 * if the lower and upper blocksizes differ, then frig the 440 * block counts so that the sizes reported by df make some 441 * kind of sense. none of this makes sense though. 442 */ 443 444 if (mstat.f_bsize != lbsize) { 445 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; 446 sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize; 447 sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize; 448 } 449 sbp->f_blocks += mstat.f_blocks; 450 sbp->f_bfree += mstat.f_bfree; 451 sbp->f_bavail += mstat.f_bavail; 452 sbp->f_files += mstat.f_files; 453 sbp->f_ffree += mstat.f_ffree; 454 455 if (sbp != &mp->mnt_stat) { 456 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 457 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 458 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 459 } 460 return (0); 461 } 462 463 int 464 union_sync(mp, waitfor, cred, p) 465 struct mount *mp; 466 int waitfor; 467 struct ucred *cred; 468 struct proc *p; 469 { 470 471 /* 472 * XXX - Assumes no data cached at union layer. 473 */ 474 return (0); 475 } 476 477 int 478 union_vget(mp, ino, vpp) 479 struct mount *mp; 480 ino_t ino; 481 struct vnode **vpp; 482 { 483 484 return (EOPNOTSUPP); 485 } 486 487 int 488 union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) 489 struct mount *mp; 490 struct fid *fidp; 491 struct mbuf *nam; 492 struct vnode **vpp; 493 int *exflagsp; 494 struct ucred **credanonp; 495 { 496 497 return (EOPNOTSUPP); 498 } 499 500 int 501 union_vptofh(vp, fhp) 502 struct vnode *vp; 503 struct fid *fhp; 504 { 505 506 return (EOPNOTSUPP); 507 } 508 509 int union_init __P((void)); 510 511 struct vfsops union_vfsops = { 512 union_mount, 513 union_start, 514 union_unmount, 515 union_root, 516 union_quotactl, 517 union_statfs, 518 union_sync, 519 union_vget, 520 union_fhtovp, 521 union_vptofh, 522 union_init, 523 }; 524