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