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.9 (Berkeley) 04/29/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 /* 339 * Return locked reference to root. 340 */ 341 VREF(um->um_uppervp); 342 if ((um->um_op == UNMNT_BELOW) && 343 VOP_ISLOCKED(um->um_uppervp)) { 344 loselock = 1; 345 } else { 346 VOP_LOCK(um->um_uppervp); 347 loselock = 0; 348 } 349 if (um->um_lowervp) 350 VREF(um->um_lowervp); 351 error = union_allocvp(vpp, mp, 352 (struct vnode *) 0, 353 (struct vnode *) 0, 354 (struct componentname *) 0, 355 um->um_uppervp, 356 um->um_lowervp); 357 358 if (error) { 359 if (!loselock) 360 VOP_UNLOCK(um->um_uppervp); 361 vrele(um->um_uppervp); 362 if (um->um_lowervp) 363 vrele(um->um_lowervp); 364 } else { 365 if (loselock) 366 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 367 } 368 369 return (error); 370 } 371 372 int 373 union_quotactl(mp, cmd, uid, arg, p) 374 struct mount *mp; 375 int cmd; 376 uid_t uid; 377 caddr_t arg; 378 struct proc *p; 379 { 380 381 return (EOPNOTSUPP); 382 } 383 384 int 385 union_statfs(mp, sbp, p) 386 struct mount *mp; 387 struct statfs *sbp; 388 struct proc *p; 389 { 390 int error; 391 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 392 struct statfs mstat; 393 int lbsize; 394 395 #ifdef UNION_DIAGNOSTIC 396 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 397 um->um_lowervp, 398 um->um_uppervp); 399 #endif 400 401 bzero(&mstat, sizeof(mstat)); 402 403 if (um->um_lowervp) { 404 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 405 if (error) 406 return (error); 407 } 408 409 /* now copy across the "interesting" information and fake the rest */ 410 #if 0 411 sbp->f_type = mstat.f_type; 412 sbp->f_flags = mstat.f_flags; 413 sbp->f_bsize = mstat.f_bsize; 414 sbp->f_iosize = mstat.f_iosize; 415 #endif 416 lbsize = mstat.f_bsize; 417 sbp->f_blocks = mstat.f_blocks; 418 sbp->f_bfree = mstat.f_bfree; 419 sbp->f_bavail = mstat.f_bavail; 420 sbp->f_files = mstat.f_files; 421 sbp->f_ffree = mstat.f_ffree; 422 423 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 424 if (error) 425 return (error); 426 427 sbp->f_type = MOUNT_UNION; 428 sbp->f_flags = mstat.f_flags; 429 sbp->f_bsize = mstat.f_bsize; 430 sbp->f_iosize = mstat.f_iosize; 431 432 /* 433 * if the lower and upper blocksizes differ, then frig the 434 * block counts so that the sizes reported by df make some 435 * kind of sense. none of this makes sense though. 436 */ 437 438 if (mstat.f_bsize != lbsize) { 439 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; 440 sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize; 441 sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize; 442 } 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 if (sbp != &mp->mnt_stat) { 450 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 451 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 452 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 453 } 454 return (0); 455 } 456 457 int 458 union_sync(mp, waitfor, cred, p) 459 struct mount *mp; 460 int waitfor; 461 struct ucred *cred; 462 struct proc *p; 463 { 464 465 /* 466 * XXX - Assumes no data cached at union layer. 467 */ 468 return (0); 469 } 470 471 int 472 union_vget(mp, ino, vpp) 473 struct mount *mp; 474 ino_t ino; 475 struct vnode **vpp; 476 { 477 478 return (EOPNOTSUPP); 479 } 480 481 int 482 union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) 483 struct mount *mp; 484 struct fid *fidp; 485 struct mbuf *nam; 486 struct vnode **vpp; 487 int *exflagsp; 488 struct ucred **credanonp; 489 { 490 491 return (EOPNOTSUPP); 492 } 493 494 int 495 union_vptofh(vp, fhp) 496 struct vnode *vp; 497 struct fid *fhp; 498 { 499 500 return (EOPNOTSUPP); 501 } 502 503 int union_init __P((void)); 504 505 struct vfsops union_vfsops = { 506 union_mount, 507 union_start, 508 union_unmount, 509 union_root, 510 union_quotactl, 511 union_statfs, 512 union_sync, 513 union_vget, 514 union_fhtovp, 515 union_vptofh, 516 union_init, 517 }; 518