1 /* 2 * Copyright (c) 1994, 1995 The Regents of the University of California. 3 * Copyright (c) 1994, 1995 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.19 (Berkeley) 05/14/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 vgone(um_rootvp); 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 proc *p = curproc; /* XXX */ 322 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 323 int error; 324 int loselock; 325 326 /* 327 * Return locked reference to root. 328 */ 329 VREF(um->um_uppervp); 330 if ((um->um_op == UNMNT_BELOW) && 331 VOP_ISLOCKED(um->um_uppervp)) { 332 loselock = 1; 333 } else { 334 vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p); 335 loselock = 0; 336 } 337 if (um->um_lowervp) 338 VREF(um->um_lowervp); 339 error = union_allocvp(vpp, mp, 340 (struct vnode *) 0, 341 (struct vnode *) 0, 342 (struct componentname *) 0, 343 um->um_uppervp, 344 um->um_lowervp, 345 1); 346 347 if (error) { 348 if (!loselock) 349 VOP_UNLOCK(um->um_uppervp, 0, p); 350 vrele(um->um_uppervp); 351 if (um->um_lowervp) 352 vrele(um->um_lowervp); 353 } else { 354 if (loselock) 355 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 356 } 357 358 return (error); 359 } 360 361 int 362 union_statfs(mp, sbp, p) 363 struct mount *mp; 364 struct statfs *sbp; 365 struct proc *p; 366 { 367 int error; 368 struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 369 struct statfs mstat; 370 int lbsize; 371 372 #ifdef UNION_DIAGNOSTIC 373 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 374 um->um_lowervp, 375 um->um_uppervp); 376 #endif 377 378 bzero(&mstat, sizeof(mstat)); 379 380 if (um->um_lowervp) { 381 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 382 if (error) 383 return (error); 384 } 385 386 /* now copy across the "interesting" information and fake the rest */ 387 #if 0 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 #endif 393 lbsize = mstat.f_bsize; 394 sbp->f_blocks = mstat.f_blocks; 395 sbp->f_bfree = mstat.f_bfree; 396 sbp->f_bavail = mstat.f_bavail; 397 sbp->f_files = mstat.f_files; 398 sbp->f_ffree = mstat.f_ffree; 399 400 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 401 if (error) 402 return (error); 403 404 sbp->f_flags = mstat.f_flags; 405 sbp->f_bsize = mstat.f_bsize; 406 sbp->f_iosize = mstat.f_iosize; 407 408 /* 409 * if the lower and upper blocksizes differ, then frig the 410 * block counts so that the sizes reported by df make some 411 * kind of sense. none of this makes sense though. 412 */ 413 414 if (mstat.f_bsize != lbsize) 415 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; 416 417 /* 418 * The "total" fields count total resources in all layers, 419 * the "free" fields count only those resources which are 420 * free in the upper layer (since only the upper layer 421 * is writeable). 422 */ 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 if (sbp != &mp->mnt_stat) { 430 sbp->f_type = mp->mnt_vfc->vfc_typenum; 431 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 432 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 433 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 434 } 435 return (0); 436 } 437 438 /* 439 * XXX - Assumes no data cached at union layer. 440 */ 441 #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \ 442 struct proc *)))nullop) 443 444 #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \ 445 struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp) 446 int union_init __P((struct vfsconf *)); 447 #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \ 448 struct proc *)))eopnotsupp) 449 #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \ 450 size_t, struct proc *)))eopnotsupp) 451 #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \ 452 eopnotsupp) 453 #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp) 454 455 struct vfsops union_vfsops = { 456 union_mount, 457 union_start, 458 union_unmount, 459 union_root, 460 union_quotactl, 461 union_statfs, 462 union_sync, 463 union_vget, 464 union_fhtovp, 465 union_vptofh, 466 union_init, 467 union_sysctl, 468 }; 469