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