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