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