1 /*- 2 * Copyright (c) 1994, 1995 The Regents of the University of California. 3 * Copyright (c) 1994, 1995 Jan-Simon Pendry. 4 * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 5 * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org> 6 * All rights reserved. 7 * 8 * This code is derived from software donated to Berkeley by 9 * Jan-Simon Pendry. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 36 * $FreeBSD: src/sys/fs/unionfs/union_vfsops.c,v 1.89 2008/01/13 14:44:06 attilio Exp $ 37 */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/lock.h> 43 #include <sys/malloc.h> 44 #include <sys/mount.h> 45 #include <sys/namei.h> 46 #include <sys/proc.h> 47 #include <sys/vnode.h> 48 #include <sys/stat.h> 49 #include <sys/sysctl.h> 50 51 #include <fs/unionfs/unionfs.h> 52 53 MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure"); 54 55 struct vfsops unionfs_vfsops; 56 57 VFS_PROTOS(unionfs); 58 59 /* 60 * Mount unionfs layer. 61 */ 62 int 63 unionfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 64 { 65 int error; 66 struct vnode *lowerrootvp; 67 struct vnode *upperrootvp; 68 struct unionfs_mount *ump; 69 int below; 70 uid_t uid; 71 gid_t gid; 72 u_short udir; 73 u_short ufile; 74 unionfs_copymode copymode; 75 unionfs_whitemode whitemode; 76 struct componentname fakecn; 77 struct nameidata nd, *ndp; 78 struct vattr va; 79 struct union_args *args = data; 80 kauth_cred_t cred; 81 size_t size; 82 size_t len; 83 const char *cp; 84 char *xp; 85 86 if (*data_len < sizeof *args) 87 return EINVAL; 88 89 UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp); 90 91 error = 0; 92 below = 0; 93 uid = 0; 94 gid = 0; 95 udir = 0; 96 ufile = 0; 97 copymode = UNIONFS_TRANSPARENT; /* default */ 98 whitemode = UNIONFS_WHITE_ALWAYS; 99 ndp = &nd; 100 cred = kauth_cred_get(); 101 102 if (mp->mnt_flag & MNT_ROOTFS) { 103 printf("union_mount: cannot union mount root filesystem\n"); 104 return (EOPNOTSUPP); 105 } 106 107 if (mp->mnt_flag & MNT_GETARGS) { 108 ump = MOUNTTOUNIONFSMOUNT(mp); 109 if (ump == NULL) 110 return EIO; 111 args->target = NULL; 112 args->mntflags = ump->um_op; 113 *data_len = sizeof *args; 114 return 0; 115 } 116 117 /* 118 * Update is a no operation. 119 */ 120 if (mp->mnt_flag & MNT_UPDATE) { 121 printf("union_mount: cannot update union mount\n"); 122 return (EOPNOTSUPP); 123 } 124 125 vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); 126 error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred); 127 if (!error) { 128 if (udir == 0) 129 udir = va.va_mode; 130 if (ufile == 0) 131 ufile = va.va_mode; 132 uid = va.va_uid; 133 gid = va.va_gid; 134 } 135 VOP_UNLOCK(mp->mnt_vnodecovered, 0); 136 if (error) 137 return (error); 138 139 switch (args->mntflags & UNMNT_OPMASK) { 140 case UNMNT_ABOVE: 141 below = 0; 142 break; 143 144 case UNMNT_BELOW: 145 below = 1; 146 break; 147 148 case UNMNT_REPLACE: 149 default: 150 return EINVAL; 151 } 152 153 /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */ 154 if (copymode == UNIONFS_TRADITIONAL) { 155 uid = kauth_cred_getuid(cred); 156 gid = kauth_cred_getgid(cred); 157 } 158 159 UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 160 UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 161 UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 162 163 /* 164 * Find upper node 165 */ 166 NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, args->target); 167 if ((error = namei(ndp))) 168 return (error); 169 170 /* get root vnodes */ 171 lowerrootvp = mp->mnt_vnodecovered; 172 upperrootvp = ndp->ni_vp; 173 174 vrele(ndp->ni_dvp); 175 ndp->ni_dvp = NULLVP; 176 177 /* create unionfs_mount */ 178 ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), 179 M_UNIONFSMNT, M_WAITOK | M_ZERO); 180 181 /* 182 * Save reference 183 */ 184 if (below) { 185 VOP_UNLOCK(upperrootvp, 0); 186 vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY); 187 ump->um_lowervp = upperrootvp; 188 ump->um_uppervp = lowerrootvp; 189 } else { 190 ump->um_lowervp = lowerrootvp; 191 ump->um_uppervp = upperrootvp; 192 } 193 ump->um_rootvp = NULLVP; 194 ump->um_uid = uid; 195 ump->um_gid = gid; 196 ump->um_udir = udir; 197 ump->um_ufile = ufile; 198 ump->um_copymode = copymode; 199 ump->um_whitemode = whitemode; 200 201 if ((lowerrootvp->v_mount->mnt_iflag & IMNT_MPSAFE) && 202 (upperrootvp->v_mount->mnt_flag & IMNT_MPSAFE)) 203 mp->mnt_iflag |= IMNT_MPSAFE; 204 mp->mnt_data = ump; 205 206 /* 207 * Copy upper layer's RDONLY flag. 208 */ 209 mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 210 211 /* 212 * Check whiteout 213 */ 214 if ((mp->mnt_flag & MNT_RDONLY) == 0) { 215 memset(&fakecn, 0, sizeof(fakecn)); 216 fakecn.cn_nameiop = LOOKUP; 217 error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP); 218 if (error) { 219 if (below) { 220 VOP_UNLOCK(ump->um_uppervp, 0); 221 vrele(upperrootvp); 222 } else 223 vput(ump->um_uppervp); 224 free(ump, M_UNIONFSMNT); 225 mp->mnt_data = NULL; 226 return (error); 227 } 228 } 229 230 /* 231 * Unlock the node 232 */ 233 VOP_UNLOCK(ump->um_uppervp, 0); 234 235 ump->um_op = args->mntflags & UNMNT_OPMASK; 236 237 /* 238 * Get the unionfs root vnode. 239 */ 240 error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 241 NULLVP, &(ump->um_rootvp), NULL); 242 vrele(upperrootvp); 243 if (error) { 244 free(ump, M_UNIONFSMNT); 245 mp->mnt_data = NULL; 246 return (error); 247 } 248 249 /* 250 * Check mnt_flag 251 */ 252 if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) && 253 (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 254 mp->mnt_flag |= MNT_LOCAL; 255 256 /* 257 * Get new fsid 258 */ 259 vfs_getnewfsid(mp); 260 261 error = set_statvfs_info(path, UIO_USERSPACE, NULL, UIO_USERSPACE, 262 mp->mnt_op->vfs_name, mp, curlwp); 263 if (error) { 264 unionfs_noderem(ump->um_rootvp); 265 free(ump, M_UNIONFSMNT); 266 mp->mnt_data = NULL; 267 return (error); 268 } 269 270 switch (ump->um_op) { 271 case UNMNT_ABOVE: 272 cp = "<above>:"; 273 break; 274 case UNMNT_BELOW: 275 cp = "<below>:"; 276 break; 277 default: 278 panic("union_mount: bad um_op"); 279 break; 280 } 281 len = strlen(cp); 282 memcpy(mp->mnt_stat.f_mntfromname, cp, len); 283 xp = mp->mnt_stat.f_mntfromname + len; 284 len = MNAMELEN - len; 285 (void) copyinstr(args->target, xp, len - 1, &size); 286 memset(xp + size, 0, len - size); 287 288 UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 289 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 290 291 return (0); 292 } 293 294 /* 295 * Free reference to unionfs layer 296 */ 297 int 298 unionfs_unmount(struct mount *mp, int mntflags) 299 { 300 struct unionfs_mount *ump; 301 int error; 302 int freeing; 303 int flags; 304 305 UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); 306 307 ump = MOUNTTOUNIONFSMOUNT(mp); 308 flags = 0; 309 310 if (mntflags & MNT_FORCE) 311 flags |= FORCECLOSE; 312 313 /* vflush (no need to call vrele) */ 314 for (freeing = 0; (error = vflush(mp, NULL, flags)) != 0;) { 315 struct vnode *vp; 316 int n; 317 318 /* count #vnodes held on mount list */ 319 n = 0; 320 TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) 321 n++; 322 323 /* if this is unchanged then stop */ 324 if (n == freeing) 325 break; 326 327 /* otherwise try once more time */ 328 freeing = n; 329 } 330 331 if (error) 332 return (error); 333 334 free(ump, M_UNIONFSMNT); 335 mp->mnt_data = 0; 336 337 return (0); 338 } 339 340 int 341 unionfs_root(struct mount *mp, struct vnode **vpp) 342 { 343 struct unionfs_mount *ump; 344 struct vnode *vp; 345 346 ump = MOUNTTOUNIONFSMOUNT(mp); 347 vp = ump->um_rootvp; 348 349 UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n", 350 vp, VOP_ISLOCKED(vp)); 351 352 vref(vp); 353 vn_lock(vp, LK_EXCLUSIVE); 354 355 *vpp = vp; 356 357 return (0); 358 } 359 360 int 361 unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg) 362 { 363 struct unionfs_mount *ump; 364 365 ump = MOUNTTOUNIONFSMOUNT(mp); 366 367 /* 368 * Writing is always performed to upper vnode. 369 */ 370 return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg)); 371 } 372 373 int 374 unionfs_statvfs(struct mount *mp, struct statvfs *sbp) 375 { 376 struct unionfs_mount *ump; 377 int error; 378 uint64_t lbsize; 379 struct statvfs *sbuf = malloc(sizeof(*sbuf), M_TEMP, M_WAITOK | M_ZERO); 380 381 ump = MOUNTTOUNIONFSMOUNT(mp); 382 383 UNIONFSDEBUG("unionfs_statvfs(mp = %p, lvp = %p, uvp = %p)\n", 384 (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp); 385 386 error = VFS_STATVFS(ump->um_lowervp->v_mount, sbuf); 387 if (error) 388 goto done; 389 390 /* now copy across the "interesting" information and fake the rest */ 391 sbp->f_blocks = sbuf->f_blocks; 392 sbp->f_files = sbuf->f_files; 393 394 lbsize = sbuf->f_bsize; 395 396 error = VFS_STATVFS(ump->um_uppervp->v_mount, sbuf); 397 if (error) 398 goto done; 399 400 /* 401 * The FS type etc is copy from upper vfs. 402 * (write able vfs have priority) 403 */ 404 sbp->f_flag = sbuf->f_flag; 405 sbp->f_bsize = sbuf->f_bsize; 406 sbp->f_iosize = sbuf->f_iosize; 407 408 if (sbuf->f_bsize != lbsize) 409 sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / sbuf->f_bsize; 410 411 sbp->f_blocks += sbuf->f_blocks; 412 sbp->f_bfree = sbuf->f_bfree; 413 sbp->f_bavail = sbuf->f_bavail; 414 sbp->f_files += sbuf->f_files; 415 sbp->f_ffree = sbuf->f_ffree; 416 417 done: 418 free(sbuf, M_TEMP); 419 return (error); 420 } 421 422 int 423 unionfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 424 { 425 /* nothing to do */ 426 return (0); 427 } 428 429 int 430 unionfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 431 { 432 return (EOPNOTSUPP); 433 } 434 435 int 436 unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp) 437 { 438 return (EOPNOTSUPP); 439 } 440 441 int 442 unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 443 int namespace, const char *attrname) 444 { 445 struct unionfs_mount *ump; 446 struct unionfs_node *unp; 447 448 ump = MOUNTTOUNIONFSMOUNT(mp); 449 unp = VTOUNIONFS(filename_vp); 450 451 if (unp->un_uppervp != NULLVP) { 452 return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd, 453 unp->un_uppervp, namespace, attrname)); 454 } else { 455 return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd, 456 unp->un_lowervp, namespace, attrname)); 457 } 458 } 459 460 /* 461 * Initialize 462 */ 463 void 464 unionfs_init(void) 465 { 466 UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ 467 } 468 469 static int 470 unionfs_renamelock_enter(struct mount *mp) 471 { 472 struct unionfs_mount *um = MOUNTTOUNIONFSMOUNT(mp); 473 474 /* Lock just the upper fs, where the action happens. */ 475 return VFS_RENAMELOCK_ENTER(um->um_uppervp->v_mount); 476 } 477 478 static void 479 unionfs_renamelock_exit(struct mount *mp) 480 { 481 struct unionfs_mount *um = MOUNTTOUNIONFSMOUNT(mp); 482 483 VFS_RENAMELOCK_EXIT(um->um_uppervp->v_mount); 484 } 485 486 int 487 unionfs_start(struct mount *mp, int flags) 488 { 489 490 return (0); 491 } 492 493 void 494 unionfs_done(void) 495 { 496 497 /* Make sure to unset the readdir hook. */ 498 vn_union_readdir_hook = NULL; 499 } 500 501 SYSCTL_SETUP(sysctl_vfs_unionfs_setup, "sysctl vfs.union subtree setup") 502 { 503 504 sysctl_createv(clog, 0, NULL, NULL, 505 CTLFLAG_PERMANENT, 506 CTLTYPE_NODE, "vfs", NULL, 507 NULL, 0, NULL, 0, 508 CTL_VFS, CTL_EOL); 509 sysctl_createv(clog, 0, NULL, NULL, 510 CTLFLAG_PERMANENT, 511 CTLTYPE_NODE, "union", 512 SYSCTL_DESCR("Union file system"), 513 NULL, 0, NULL, 0, 514 CTL_VFS, 15, CTL_EOL); 515 /* 516 * XXX the "15" above could be dynamic, thereby eliminating 517 * one more instance of the "number to vfs" mapping problem, 518 * but "15" is the order as taken from sys/mount.h 519 */ 520 } 521 522 extern const struct vnodeopv_desc unionfs_vnodeop_opv_desc; 523 524 const struct vnodeopv_desc * const unionfs_vnodeopv_descs[] = { 525 &unionfs_vnodeop_opv_desc, 526 NULL, 527 }; 528 529 struct vfsops unionfs_vfsops = { 530 MOUNT_UNION, 531 sizeof (struct unionfs_args), 532 unionfs_mount, 533 unionfs_start, 534 unionfs_unmount, 535 unionfs_root, 536 (void *)eopnotsupp, /* vfs_quotactl */ 537 unionfs_statvfs, 538 unionfs_sync, 539 unionfs_vget, 540 (void *)eopnotsupp, /* vfs_fhtovp */ 541 (void *)eopnotsupp, /* vfs_vptofh */ 542 unionfs_init, 543 NULL, /* vfs_reinit */ 544 unionfs_done, 545 NULL, /* vfs_mountroot */ 546 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp, 547 vfs_stdextattrctl, 548 (void *)eopnotsupp, /* vfs_suspendctl */ 549 unionfs_renamelock_enter, 550 unionfs_renamelock_exit, 551 (void *)eopnotsupp, 552 unionfs_vnodeopv_descs, 553 0, /* vfs_refcount */ 554 { NULL, NULL }, 555 }; 556 VFS_ATTACH(unionfs_vfsops); 557