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 #include <sys/module.h> 51 52 #include <fs/unionfs/unionfs.h> 53 54 MODULE(MODULE_CLASS_VFS, unionfs, NULL); 55 56 MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure"); 57 58 struct vfsops unionfs_vfsops; 59 60 VFS_PROTOS(unionfs); 61 62 /* 63 * Mount unionfs layer. 64 */ 65 int 66 unionfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 67 { 68 int error; 69 struct vnode *lowerrootvp; 70 struct vnode *upperrootvp; 71 struct unionfs_mount *ump; 72 int below; 73 uid_t uid; 74 gid_t gid; 75 u_short udir; 76 u_short ufile; 77 unionfs_copymode copymode; 78 unionfs_whitemode whitemode; 79 struct componentname fakecn; 80 struct nameidata nd, *ndp; 81 struct vattr va; 82 struct union_args *args = data; 83 kauth_cred_t cred; 84 size_t size; 85 size_t len; 86 const char *cp; 87 char *xp; 88 89 if (*data_len < sizeof *args) 90 return EINVAL; 91 92 UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp); 93 94 error = 0; 95 below = 0; 96 uid = 0; 97 gid = 0; 98 udir = 0; 99 ufile = 0; 100 copymode = UNIONFS_TRANSPARENT; /* default */ 101 whitemode = UNIONFS_WHITE_ALWAYS; 102 ndp = &nd; 103 cred = kauth_cred_get(); 104 105 if (mp->mnt_flag & MNT_ROOTFS) { 106 printf("union_mount: cannot union mount root filesystem\n"); 107 return (EOPNOTSUPP); 108 } 109 110 if (mp->mnt_flag & MNT_GETARGS) { 111 ump = MOUNTTOUNIONFSMOUNT(mp); 112 if (ump == NULL) 113 return EIO; 114 args->target = NULL; 115 args->mntflags = ump->um_op; 116 *data_len = sizeof *args; 117 return 0; 118 } 119 120 /* 121 * Update is a no operation. 122 */ 123 if (mp->mnt_flag & MNT_UPDATE) { 124 printf("union_mount: cannot update union mount\n"); 125 return (EOPNOTSUPP); 126 } 127 128 vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); 129 error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred); 130 if (!error) { 131 if (udir == 0) 132 udir = va.va_mode; 133 if (ufile == 0) 134 ufile = va.va_mode; 135 uid = va.va_uid; 136 gid = va.va_gid; 137 } 138 VOP_UNLOCK(mp->mnt_vnodecovered, 0); 139 if (error) 140 return (error); 141 142 switch (args->mntflags & UNMNT_OPMASK) { 143 case UNMNT_ABOVE: 144 below = 0; 145 break; 146 147 case UNMNT_BELOW: 148 below = 1; 149 break; 150 151 case UNMNT_REPLACE: 152 default: 153 return EINVAL; 154 } 155 156 /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */ 157 if (copymode == UNIONFS_TRADITIONAL) { 158 uid = kauth_cred_getuid(cred); 159 gid = kauth_cred_getgid(cred); 160 } 161 162 UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 163 UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 164 UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 165 166 /* 167 * Find upper node 168 */ 169 NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, args->target); 170 if ((error = namei(ndp))) 171 return (error); 172 173 /* get root vnodes */ 174 lowerrootvp = mp->mnt_vnodecovered; 175 upperrootvp = ndp->ni_vp; 176 177 vrele(ndp->ni_dvp); 178 ndp->ni_dvp = NULLVP; 179 180 /* create unionfs_mount */ 181 ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), 182 M_UNIONFSMNT, M_WAITOK | M_ZERO); 183 184 /* 185 * Save reference 186 */ 187 if (below) { 188 VOP_UNLOCK(upperrootvp, 0); 189 vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY); 190 ump->um_lowervp = upperrootvp; 191 ump->um_uppervp = lowerrootvp; 192 } else { 193 ump->um_lowervp = lowerrootvp; 194 ump->um_uppervp = upperrootvp; 195 } 196 ump->um_rootvp = NULLVP; 197 ump->um_uid = uid; 198 ump->um_gid = gid; 199 ump->um_udir = udir; 200 ump->um_ufile = ufile; 201 ump->um_copymode = copymode; 202 ump->um_whitemode = whitemode; 203 204 if ((lowerrootvp->v_mount->mnt_iflag & IMNT_MPSAFE) && 205 (upperrootvp->v_mount->mnt_flag & IMNT_MPSAFE)) 206 mp->mnt_iflag |= IMNT_MPSAFE; 207 mp->mnt_data = ump; 208 209 /* 210 * Copy upper layer's RDONLY flag. 211 */ 212 mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 213 214 /* 215 * Check whiteout 216 */ 217 if ((mp->mnt_flag & MNT_RDONLY) == 0) { 218 memset(&fakecn, 0, sizeof(fakecn)); 219 fakecn.cn_nameiop = LOOKUP; 220 error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP); 221 if (error) { 222 if (below) { 223 VOP_UNLOCK(ump->um_uppervp, 0); 224 vrele(upperrootvp); 225 } else 226 vput(ump->um_uppervp); 227 free(ump, M_UNIONFSMNT); 228 mp->mnt_data = NULL; 229 return (error); 230 } 231 } 232 233 /* 234 * Unlock the node 235 */ 236 VOP_UNLOCK(ump->um_uppervp, 0); 237 238 ump->um_op = args->mntflags & UNMNT_OPMASK; 239 240 /* 241 * Get the unionfs root vnode. 242 */ 243 error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 244 NULLVP, &(ump->um_rootvp), NULL); 245 vrele(upperrootvp); 246 if (error) { 247 free(ump, M_UNIONFSMNT); 248 mp->mnt_data = NULL; 249 return (error); 250 } 251 252 /* 253 * Check mnt_flag 254 */ 255 if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) && 256 (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 257 mp->mnt_flag |= MNT_LOCAL; 258 259 /* 260 * Get new fsid 261 */ 262 vfs_getnewfsid(mp); 263 264 error = set_statvfs_info(path, UIO_USERSPACE, NULL, UIO_USERSPACE, 265 mp->mnt_op->vfs_name, mp, curlwp); 266 if (error) { 267 unionfs_noderem(ump->um_rootvp); 268 free(ump, M_UNIONFSMNT); 269 mp->mnt_data = NULL; 270 return (error); 271 } 272 273 switch (ump->um_op) { 274 case UNMNT_ABOVE: 275 cp = "<above>:"; 276 break; 277 case UNMNT_BELOW: 278 cp = "<below>:"; 279 break; 280 default: 281 panic("union_mount: bad um_op"); 282 break; 283 } 284 len = strlen(cp); 285 memcpy(mp->mnt_stat.f_mntfromname, cp, len); 286 xp = mp->mnt_stat.f_mntfromname + len; 287 len = MNAMELEN - len; 288 (void) copyinstr(args->target, xp, len - 1, &size); 289 memset(xp + size, 0, len - size); 290 291 UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 292 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 293 294 return (0); 295 } 296 297 /* 298 * Free reference to unionfs layer 299 */ 300 int 301 unionfs_unmount(struct mount *mp, int mntflags) 302 { 303 struct unionfs_mount *ump; 304 int error; 305 int freeing; 306 int flags; 307 308 UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); 309 310 ump = MOUNTTOUNIONFSMOUNT(mp); 311 flags = 0; 312 313 if (mntflags & MNT_FORCE) 314 flags |= FORCECLOSE; 315 316 /* vflush (no need to call vrele) */ 317 for (freeing = 0; (error = vflush(mp, NULL, flags)) != 0;) { 318 struct vnode *vp; 319 int n; 320 321 /* count #vnodes held on mount list */ 322 n = 0; 323 TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) 324 n++; 325 326 /* if this is unchanged then stop */ 327 if (n == freeing) 328 break; 329 330 /* otherwise try once more time */ 331 freeing = n; 332 } 333 334 if (error) 335 return (error); 336 337 free(ump, M_UNIONFSMNT); 338 mp->mnt_data = NULL; 339 340 return (0); 341 } 342 343 int 344 unionfs_root(struct mount *mp, struct vnode **vpp) 345 { 346 struct unionfs_mount *ump; 347 struct vnode *vp; 348 349 ump = MOUNTTOUNIONFSMOUNT(mp); 350 vp = ump->um_rootvp; 351 352 UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n", 353 vp, VOP_ISLOCKED(vp)); 354 355 vref(vp); 356 vn_lock(vp, LK_EXCLUSIVE); 357 358 *vpp = vp; 359 360 return (0); 361 } 362 363 int 364 unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg) 365 { 366 struct unionfs_mount *ump; 367 368 ump = MOUNTTOUNIONFSMOUNT(mp); 369 370 /* 371 * Writing is always performed to upper vnode. 372 */ 373 return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg)); 374 } 375 376 int 377 unionfs_statvfs(struct mount *mp, struct statvfs *sbp) 378 { 379 struct unionfs_mount *ump; 380 int error; 381 uint64_t lbsize; 382 struct statvfs *sbuf = malloc(sizeof(*sbuf), M_TEMP, M_WAITOK | M_ZERO); 383 384 ump = MOUNTTOUNIONFSMOUNT(mp); 385 386 UNIONFSDEBUG("unionfs_statvfs(mp = %p, lvp = %p, uvp = %p)\n", 387 (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp); 388 389 error = VFS_STATVFS(ump->um_lowervp->v_mount, sbuf); 390 if (error) 391 goto done; 392 393 /* now copy across the "interesting" information and fake the rest */ 394 sbp->f_blocks = sbuf->f_blocks; 395 sbp->f_files = sbuf->f_files; 396 397 lbsize = sbuf->f_bsize; 398 399 error = VFS_STATVFS(ump->um_uppervp->v_mount, sbuf); 400 if (error) 401 goto done; 402 403 /* 404 * The FS type etc is copy from upper vfs. 405 * (write able vfs have priority) 406 */ 407 sbp->f_flag = sbuf->f_flag; 408 sbp->f_bsize = sbuf->f_bsize; 409 sbp->f_iosize = sbuf->f_iosize; 410 411 if (sbuf->f_bsize != lbsize) 412 sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / sbuf->f_bsize; 413 414 sbp->f_blocks += sbuf->f_blocks; 415 sbp->f_bfree = sbuf->f_bfree; 416 sbp->f_bavail = sbuf->f_bavail; 417 sbp->f_files += sbuf->f_files; 418 sbp->f_ffree = sbuf->f_ffree; 419 420 done: 421 free(sbuf, M_TEMP); 422 return (error); 423 } 424 425 int 426 unionfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 427 { 428 /* nothing to do */ 429 return (0); 430 } 431 432 int 433 unionfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 434 { 435 return (EOPNOTSUPP); 436 } 437 438 int 439 unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp) 440 { 441 return (EOPNOTSUPP); 442 } 443 444 int 445 unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 446 int namespace, const char *attrname) 447 { 448 struct unionfs_mount *ump; 449 struct unionfs_node *unp; 450 451 ump = MOUNTTOUNIONFSMOUNT(mp); 452 unp = VTOUNIONFS(filename_vp); 453 454 if (unp->un_uppervp != NULLVP) { 455 return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd, 456 unp->un_uppervp, namespace, attrname)); 457 } else { 458 return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd, 459 unp->un_lowervp, namespace, attrname)); 460 } 461 } 462 463 /* 464 * Initialize 465 */ 466 void 467 unionfs_init(void) 468 { 469 UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ 470 } 471 472 static int 473 unionfs_renamelock_enter(struct mount *mp) 474 { 475 struct unionfs_mount *um = MOUNTTOUNIONFSMOUNT(mp); 476 477 /* Lock just the upper fs, where the action happens. */ 478 return VFS_RENAMELOCK_ENTER(um->um_uppervp->v_mount); 479 } 480 481 static void 482 unionfs_renamelock_exit(struct mount *mp) 483 { 484 struct unionfs_mount *um = MOUNTTOUNIONFSMOUNT(mp); 485 486 VFS_RENAMELOCK_EXIT(um->um_uppervp->v_mount); 487 } 488 489 int 490 unionfs_start(struct mount *mp, int flags) 491 { 492 493 return (0); 494 } 495 496 void 497 unionfs_done(void) 498 { 499 500 /* Make sure to unset the readdir hook. */ 501 vn_union_readdir_hook = NULL; 502 } 503 504 SYSCTL_SETUP(sysctl_vfs_unionfs_setup, "sysctl vfs.union subtree setup") 505 { 506 507 sysctl_createv(clog, 0, NULL, NULL, 508 CTLFLAG_PERMANENT, 509 CTLTYPE_NODE, "vfs", NULL, 510 NULL, 0, NULL, 0, 511 CTL_VFS, CTL_EOL); 512 sysctl_createv(clog, 0, NULL, NULL, 513 CTLFLAG_PERMANENT, 514 CTLTYPE_NODE, "union", 515 SYSCTL_DESCR("Union file system"), 516 NULL, 0, NULL, 0, 517 CTL_VFS, 15, CTL_EOL); 518 /* 519 * XXX the "15" above could be dynamic, thereby eliminating 520 * one more instance of the "number to vfs" mapping problem, 521 * but "15" is the order as taken from sys/mount.h 522 */ 523 } 524 525 extern const struct vnodeopv_desc unionfs_vnodeop_opv_desc; 526 527 const struct vnodeopv_desc * const unionfs_vnodeopv_descs[] = { 528 &unionfs_vnodeop_opv_desc, 529 NULL, 530 }; 531 532 struct vfsops unionfs_vfsops = { 533 MOUNT_UNION, 534 sizeof (struct unionfs_args), 535 unionfs_mount, 536 unionfs_start, 537 unionfs_unmount, 538 unionfs_root, 539 (void *)eopnotsupp, /* vfs_quotactl */ 540 unionfs_statvfs, 541 unionfs_sync, 542 unionfs_vget, 543 (void *)eopnotsupp, /* vfs_fhtovp */ 544 (void *)eopnotsupp, /* vfs_vptofh */ 545 unionfs_init, 546 NULL, /* vfs_reinit */ 547 unionfs_done, 548 NULL, /* vfs_mountroot */ 549 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp, 550 vfs_stdextattrctl, 551 (void *)eopnotsupp, /* vfs_suspendctl */ 552 unionfs_renamelock_enter, 553 unionfs_renamelock_exit, 554 (void *)eopnotsupp, 555 unionfs_vnodeopv_descs, 556 0, /* vfs_refcount */ 557 { NULL, NULL }, 558 }; 559 560 static int 561 unionfs_modcmd(modcmd_t cmd, void *arg) 562 { 563 564 switch (cmd) { 565 case MODULE_CMD_INIT: 566 return vfs_attach(&unionfs_vfsops); 567 case MODULE_CMD_FINI: 568 return vfs_detach(&unionfs_vfsops); 569 default: 570 return ENOTTY; 571 } 572 } 573