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.17 (Berkeley) 05/10/95 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 = 0; 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 * Get argument 73 */ 74 if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args))) 75 goto bad; 76 77 lowerrootvp = mp->mnt_vnodecovered; 78 VREF(lowerrootvp); 79 80 /* 81 * Find upper node. 82 */ 83 NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, 84 UIO_USERSPACE, args.target, p); 85 86 if (error = namei(ndp)) 87 goto bad; 88 89 upperrootvp = ndp->ni_vp; 90 vrele(ndp->ni_dvp); 91 ndp->ni_dvp = NULL; 92 93 if (upperrootvp->v_type != VDIR) { 94 error = EINVAL; 95 goto bad; 96 } 97 98 um = (struct union_mount *) malloc(sizeof(struct union_mount), 99 M_UFSMNT, M_WAITOK); /* XXX */ 100 101 /* 102 * Keep a held reference to the target vnodes. 103 * They are vrele'd in union_unmount. 104 * 105 * Depending on the _BELOW flag, the filesystems are 106 * viewed in a different order. In effect, this is the 107 * same as providing a mount under option to the mount syscall. 108 */ 109 110 um->um_op = args.mntflags & UNMNT_OPMASK; 111 switch (um->um_op) { 112 case UNMNT_ABOVE: 113 um->um_lowervp = lowerrootvp; 114 um->um_uppervp = upperrootvp; 115 break; 116 117 case UNMNT_BELOW: 118 um->um_lowervp = upperrootvp; 119 um->um_uppervp = lowerrootvp; 120 break; 121 122 case UNMNT_REPLACE: 123 vrele(lowerrootvp); 124 lowerrootvp = NULLVP; 125 um->um_uppervp = upperrootvp; 126 um->um_lowervp = lowerrootvp; 127 break; 128 129 default: 130 error = EINVAL; 131 goto bad; 132 } 133 134 /* 135 * Unless the mount is readonly, ensure that the top layer 136 * supports whiteout operations 137 */ 138 if ((mp->mnt_flag & MNT_RDONLY) == 0) { 139 error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP); 140 if (error) 141 goto bad; 142 } 143 144 um->um_cred = p->p_ucred; 145 crhold(um->um_cred); 146 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask; 147 148 /* 149 * Depending on what you think the MNT_LOCAL flag might mean, 150 * you may want the && to be || on the conditional below. 151 * At the moment it has been defined that the filesystem is 152 * only local if it is all local, ie the MNT_LOCAL flag implies 153 * that the entire namespace is local. If you think the MNT_LOCAL 154 * flag implies that some of the files might be stored locally 155 * then you will want to change the conditional. 156 */ 157 if (um->um_op == UNMNT_ABOVE) { 158 if (((um->um_lowervp == NULLVP) || 159 (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && 160 (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 161 mp->mnt_flag |= MNT_LOCAL; 162 } 163 164 /* 165 * Copy in the upper layer's RDONLY flag. This is for the benefit 166 * of lookup() which explicitly checks the flag, rather than asking 167 * the filesystem for it's own opinion. This means, that an update 168 * mount of the underlying filesystem to go from rdonly to rdwr 169 * will leave the unioned view as read-only. 170 */ 171 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); 172 173 mp->mnt_data = (qaddr_t) um; 174 vfs_getnewfsid(mp); 175 176 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); 177 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); 178 179 switch (um->um_op) { 180 case UNMNT_ABOVE: 181 cp = "<above>:"; 182 break; 183 case UNMNT_BELOW: 184 cp = "<below>:"; 185 break; 186 case UNMNT_REPLACE: 187 cp = ""; 188 break; 189 } 190 len = strlen(cp); 191 bcopy(cp, mp->mnt_stat.f_mntfromname, len); 192 193 cp = mp->mnt_stat.f_mntfromname + len; 194 len = MNAMELEN - len; 195 196 (void) copyinstr(args.target, cp, len - 1, &size); 197 bzero(cp + size, len - size); 198 199 #ifdef UNION_DIAGNOSTIC 200 printf("union_mount: from %s, on %s\n", 201 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 202 #endif 203 return (0); 204 205 bad: 206 if (um) 207 free(um, M_UFSMNT); 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 freeing; 245 int flags = 0; 246 247 #ifdef UNION_DIAGNOSTIC 248 printf("union_unmount(mp = %x)\n", mp); 249 #endif 250 251 if (mntflags & MNT_FORCE) 252 flags |= FORCECLOSE; 253 254 if (error = union_root(mp, &um_rootvp)) 255 return (error); 256 257 /* 258 * Keep flushing vnodes from the mount list. 259 * This is needed because of the un_pvp held 260 * reference to the parent vnode. 261 * If more vnodes have been freed on a given pass, 262 * the try again. The loop will iterate at most 263 * (d) times, where (d) is the maximum tree depth 264 * in the filesystem. 265 */ 266 for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) { 267 struct vnode *vp; 268 int n; 269 270 /* count #vnodes held on mount list */ 271 for (n = 0, vp = mp->mnt_vnodelist.lh_first; 272 vp != NULLVP; 273 vp = vp->v_mntvnodes.le_next) 274 n++; 275 276 /* if this is unchanged then stop */ 277 if (n == freeing) 278 break; 279 280 /* otherwise try once more time */ 281 freeing = n; 282 } 283 284 /* At this point the root vnode should have a single reference */ 285 if (um_rootvp->v_usecount > 1) { 286 vput(um_rootvp); 287 return (EBUSY); 288 } 289 290 #ifdef UNION_DIAGNOSTIC 291 vprint("union root", um_rootvp); 292 #endif 293 /* 294 * Discard references to upper and lower target vnodes. 295 */ 296 if (um->um_lowervp) 297 vrele(um->um_lowervp); 298 vrele(um->um_uppervp); 299 crfree(um->um_cred); 300 /* 301 * Release reference on underlying root vnode 302 */ 303 vput(um_rootvp); 304 /* 305 * And blow it away for future re-use 306 */ 307 VOP_REVOKE(um_rootvp, 0); 308 /* 309 * Finally, throw away the union_mount structure 310 */ 311 free(mp->mnt_data, M_UFSMNT); /* XXX */ 312 mp->mnt_data = 0; 313 return (0); 314 } 315 316 int 317 union_root(mp, vpp) 318 struct mount *mp; 319 struct vnode **vpp; 320 { 321 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 322 int error; 323 int loselock; 324 325 /* 326 * Return locked reference to root. 327 */ 328 VREF(um->um_uppervp); 329 if ((um->um_op == UNMNT_BELOW) && 330 VOP_ISLOCKED(um->um_uppervp)) { 331 loselock = 1; 332 } else { 333 VOP_LOCK(um->um_uppervp); 334 loselock = 0; 335 } 336 if (um->um_lowervp) 337 VREF(um->um_lowervp); 338 error = union_allocvp(vpp, mp, 339 (struct vnode *) 0, 340 (struct vnode *) 0, 341 (struct componentname *) 0, 342 um->um_uppervp, 343 um->um_lowervp, 344 1); 345 346 if (error) { 347 if (!loselock) 348 VOP_UNLOCK(um->um_uppervp); 349 vrele(um->um_uppervp); 350 if (um->um_lowervp) 351 vrele(um->um_lowervp); 352 } else { 353 if (loselock) 354 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 355 } 356 357 return (error); 358 } 359 360 int 361 union_statfs(mp, sbp, p) 362 struct mount *mp; 363 struct statfs *sbp; 364 struct proc *p; 365 { 366 int error; 367 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 368 struct statfs mstat; 369 int lbsize; 370 371 #ifdef UNION_DIAGNOSTIC 372 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 373 um->um_lowervp, 374 um->um_uppervp); 375 #endif 376 377 bzero(&mstat, sizeof(mstat)); 378 379 if (um->um_lowervp) { 380 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 381 if (error) 382 return (error); 383 } 384 385 /* now copy across the "interesting" information and fake the rest */ 386 #if 0 387 sbp->f_type = mstat.f_type; 388 sbp->f_flags = mstat.f_flags; 389 sbp->f_bsize = mstat.f_bsize; 390 sbp->f_iosize = mstat.f_iosize; 391 #endif 392 lbsize = mstat.f_bsize; 393 sbp->f_blocks = mstat.f_blocks; 394 sbp->f_bfree = mstat.f_bfree; 395 sbp->f_bavail = mstat.f_bavail; 396 sbp->f_files = mstat.f_files; 397 sbp->f_ffree = mstat.f_ffree; 398 399 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 400 if (error) 401 return (error); 402 403 sbp->f_flags = mstat.f_flags; 404 sbp->f_bsize = mstat.f_bsize; 405 sbp->f_iosize = mstat.f_iosize; 406 407 /* 408 * if the lower and upper blocksizes differ, then frig the 409 * block counts so that the sizes reported by df make some 410 * kind of sense. none of this makes sense though. 411 */ 412 413 if (mstat.f_bsize != lbsize) 414 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; 415 416 /* 417 * The "total" fields count total resources in all layers, 418 * the "free" fields count only those resources which are 419 * free in the upper layer (since only the upper layer 420 * is writeable). 421 */ 422 sbp->f_blocks += mstat.f_blocks; 423 sbp->f_bfree = mstat.f_bfree; 424 sbp->f_bavail = mstat.f_bavail; 425 sbp->f_files += mstat.f_files; 426 sbp->f_ffree = mstat.f_ffree; 427 428 if (sbp != &mp->mnt_stat) { 429 sbp->f_type = mp->mnt_vfc->vfc_typenum; 430 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 431 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 432 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 433 } 434 return (0); 435 } 436 437 /* 438 * XXX - Assumes no data cached at union layer. 439 */ 440 #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \ 441 struct proc *)))nullop) 442 443 #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \ 444 struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp) 445 int union_init __P((struct vfsconf *)); 446 #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \ 447 struct proc *)))eopnotsupp) 448 #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \ 449 size_t, struct proc *)))eopnotsupp) 450 #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \ 451 eopnotsupp) 452 #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp) 453 454 struct vfsops union_vfsops = { 455 union_mount, 456 union_start, 457 union_unmount, 458 union_root, 459 union_quotactl, 460 union_statfs, 461 union_sync, 462 union_vget, 463 union_fhtovp, 464 union_vptofh, 465 union_init, 466 union_sysctl, 467 }; 468