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 54MODULE(MODULE_CLASS_VFS, unionfs, "layerfs"); 55 56MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure"); 57 58struct vfsops unionfs_vfsops; 59 60VFS_PROTOS(unionfs); 61 62static struct sysctllog *unionfs_sysctl_log; 63 64/* 65 * Mount unionfs layer. 66 */ 67int 68unionfs_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("unionfs_mount(mp = %p)\n", (void *)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 cred = kauth_cred_get(); 109 110 if (mp->mnt_flag & MNT_ROOTFS) { 111 printf("union_mount: cannot union mount root filesystem\n"); 112 return (EOPNOTSUPP); 113 } 114 115 if (mp->mnt_flag & MNT_GETARGS) { 116 ump = MOUNTTOUNIONFSMOUNT(mp); 117 if (ump == NULL) 118 return EIO; 119 args->target = NULL; 120 args->mntflags = ump->um_op; 121 *data_len = sizeof *args; 122 return 0; 123 } 124 125 /* 126 * Update is a no operation. 127 */ 128 if (mp->mnt_flag & MNT_UPDATE) { 129 printf("union_mount: cannot update union mount\n"); 130 return (EOPNOTSUPP); 131 } 132 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("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 168 UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 169 UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 170 171 /* 172 * Find upper node 173 */ 174 error = pathbuf_copyin(args->target, &pb); 175 if (error) { 176 return error; 177 } 178 NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, pb); 179 if ((error = namei(ndp))) { 180 pathbuf_destroy(pb); 181 return error; 182 } 183 184 /* get root vnodes */ 185 lowerrootvp = mp->mnt_vnodecovered; 186 upperrootvp = ndp->ni_vp; 187 188 vrele(ndp->ni_dvp); 189 ndp->ni_dvp = NULLVP; 190 pathbuf_destroy(pb); 191 192 /* create unionfs_mount */ 193 ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), 194 M_UNIONFSMNT, M_WAITOK | M_ZERO); 195 196 /* 197 * Save reference 198 */ 199 if (below) { 200 VOP_UNLOCK(upperrootvp); 201 vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY); 202 ump->um_lowervp = upperrootvp; 203 ump->um_uppervp = lowerrootvp; 204 } else { 205 ump->um_lowervp = lowerrootvp; 206 ump->um_uppervp = upperrootvp; 207 } 208 ump->um_rootvp = NULLVP; 209 ump->um_uid = uid; 210 ump->um_gid = gid; 211 ump->um_udir = udir; 212 ump->um_ufile = ufile; 213 ump->um_copymode = copymode; 214 ump->um_whitemode = whitemode; 215 216 if ((lowerrootvp->v_mount->mnt_iflag & IMNT_MPSAFE) && 217 (upperrootvp->v_mount->mnt_flag & IMNT_MPSAFE)) 218 mp->mnt_iflag |= IMNT_MPSAFE; 219 mp->mnt_data = ump; 220 221 /* 222 * Copy upper layer's RDONLY flag. 223 */ 224 mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 225 226 /* 227 * Check whiteout 228 */ 229 if ((mp->mnt_flag & MNT_RDONLY) == 0) { 230 memset(&fakecn, 0, sizeof(fakecn)); 231 fakecn.cn_nameiop = LOOKUP; 232 error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP); 233 if (error) { 234 if (below) { 235 VOP_UNLOCK(ump->um_uppervp); 236 vrele(upperrootvp); 237 } else 238 vput(ump->um_uppervp); 239 free(ump, M_UNIONFSMNT); 240 mp->mnt_data = NULL; 241 return (error); 242 } 243 } 244 245 /* 246 * Unlock the node 247 */ 248 VOP_UNLOCK(ump->um_uppervp); 249 250 ump->um_op = args->mntflags & UNMNT_OPMASK; 251 252 /* 253 * Get the unionfs root vnode. 254 */ 255 error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 256 NULLVP, &(ump->um_rootvp), NULL); 257 vrele(upperrootvp); 258 if (error) { 259 free(ump, M_UNIONFSMNT); 260 mp->mnt_data = NULL; 261 return (error); 262 } 263 264 /* 265 * Check mnt_flag 266 */ 267 if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) && 268 (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 269 mp->mnt_flag |= MNT_LOCAL; 270 271 /* 272 * Get new fsid 273 */ 274 vfs_getnewfsid(mp); 275 276 error = set_statvfs_info(path, UIO_USERSPACE, NULL, UIO_USERSPACE, 277 mp->mnt_op->vfs_name, mp, curlwp); 278 if (error) { 279 unionfs_noderem(ump->um_rootvp); 280 free(ump, M_UNIONFSMNT); 281 mp->mnt_data = NULL; 282 return (error); 283 } 284 285 switch (ump->um_op) { 286 case UNMNT_ABOVE: 287 cp = "<above>:"; 288 break; 289 case UNMNT_BELOW: 290 cp = "<below>:"; 291 break; 292 default: 293 panic("union_mount: bad um_op"); 294 break; 295 } 296 len = strlen(cp); 297 memcpy(mp->mnt_stat.f_mntfromname, cp, len); 298 xp = mp->mnt_stat.f_mntfromname + len; 299 len = MNAMELEN - len; 300 (void) copyinstr(args->target, xp, len - 1, &size); 301 memset(xp + size, 0, len - size); 302 303 UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 304 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 305 306 return (0); 307} 308 309/* 310 * Free reference to unionfs layer 311 */ 312int 313unionfs_unmount(struct mount *mp, int mntflags) 314{ 315 struct unionfs_mount *ump; 316 int error; 317 int freeing; 318 int flags; 319 320 UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); 321 322 ump = MOUNTTOUNIONFSMOUNT(mp); 323 flags = 0; 324 325 if (mntflags & MNT_FORCE) 326 flags |= FORCECLOSE; 327 328 /* vflush (no need to call vrele) */ 329 for (freeing = 0; (error = vflush(mp, NULL, flags)) != 0;) { 330 struct vnode *vp; 331 int n; 332 333 /* count #vnodes held on mount list */ 334 n = 0; 335 TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) 336 n++; 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 free(ump, M_UNIONFSMNT); 350 mp->mnt_data = NULL; 351 352 return (0); 353} 354 355int 356unionfs_root(struct mount *mp, 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("unionfs_root: rootvp=%p locked=%x\n", 365 vp, VOP_ISLOCKED(vp)); 366 367 vref(vp); 368 vn_lock(vp, LK_EXCLUSIVE); 369 370 *vpp = vp; 371 372 return (0); 373} 374 375int 376unionfs_quotactl(struct mount *mp, prop_dictionary_t dict) 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, dict)); 386} 387 388int 389unionfs_statvfs(struct mount *mp, struct statvfs *sbp) 390{ 391 struct unionfs_mount *ump; 392 int error; 393 uint64_t lbsize; 394 struct statvfs *sbuf = malloc(sizeof(*sbuf), M_TEMP, M_WAITOK | M_ZERO); 395 396 ump = MOUNTTOUNIONFSMOUNT(mp); 397 398 UNIONFSDEBUG("unionfs_statvfs(mp = %p, lvp = %p, uvp = %p)\n", 399 (void *)mp, (void *)ump->um_lowervp, (void *)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 free(sbuf, M_TEMP); 434 return (error); 435} 436 437int 438unionfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 439{ 440 /* nothing to do */ 441 return (0); 442} 443 444int 445unionfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 446{ 447 return (EOPNOTSUPP); 448} 449 450int 451unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp) 452{ 453 return (EOPNOTSUPP); 454} 455 456int 457unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 458 int namespace, const char *attrname) 459{ 460 struct unionfs_mount *ump; 461 struct unionfs_node *unp; 462 463 ump = MOUNTTOUNIONFSMOUNT(mp); 464 unp = VTOUNIONFS(filename_vp); 465 466 if (unp->un_uppervp != NULLVP) { 467 return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd, 468 unp->un_uppervp, namespace, attrname)); 469 } else { 470 return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd, 471 unp->un_lowervp, namespace, attrname)); 472 } 473} 474 475/* 476 * Initialize 477 */ 478void 479unionfs_init(void) 480{ 481 UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ 482} 483 484static int 485unionfs_renamelock_enter(struct mount *mp) 486{ 487 struct unionfs_mount *um = MOUNTTOUNIONFSMOUNT(mp); 488 489 /* Lock just the upper fs, where the action happens. */ 490 return VFS_RENAMELOCK_ENTER(um->um_uppervp->v_mount); 491} 492 493static void 494unionfs_renamelock_exit(struct mount *mp) 495{ 496 struct unionfs_mount *um = MOUNTTOUNIONFSMOUNT(mp); 497 498 VFS_RENAMELOCK_EXIT(um->um_uppervp->v_mount); 499} 500 501int 502unionfs_start(struct mount *mp, int flags) 503{ 504 505 return (0); 506} 507 508void 509unionfs_done(void) 510{ 511 512 /* Make sure to unset the readdir hook. */ 513 vn_union_readdir_hook = NULL; 514} 515 516extern const struct vnodeopv_desc unionfs_vnodeop_opv_desc; 517 518const struct vnodeopv_desc * const unionfs_vnodeopv_descs[] = { 519 &unionfs_vnodeop_opv_desc, 520 NULL, 521}; 522 523struct vfsops unionfs_vfsops = { 524 MOUNT_UNION, 525 sizeof (struct unionfs_args), 526 unionfs_mount, 527 unionfs_start, 528 unionfs_unmount, 529 unionfs_root, 530 (void *)eopnotsupp, /* vfs_quotactl */ 531 unionfs_statvfs, 532 unionfs_sync, 533 unionfs_vget, 534 (void *)eopnotsupp, /* vfs_fhtovp */ 535 (void *)eopnotsupp, /* vfs_vptofh */ 536 unionfs_init, 537 NULL, /* vfs_reinit */ 538 unionfs_done, 539 NULL, /* vfs_mountroot */ 540 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp, 541 vfs_stdextattrctl, 542 (void *)eopnotsupp, /* vfs_suspendctl */ 543 unionfs_renamelock_enter, 544 unionfs_renamelock_exit, 545 (void *)eopnotsupp, 546 unionfs_vnodeopv_descs, 547 0, /* vfs_refcount */ 548 { NULL, NULL }, 549}; 550 551static int 552unionfs_modcmd(modcmd_t cmd, void *arg) 553{ 554 int error; 555 556 switch (cmd) { 557 case MODULE_CMD_INIT: 558 error = vfs_attach(&unionfs_vfsops); 559 if (error != 0) 560 break; 561 sysctl_createv(&unionfs_sysctl_log, 0, NULL, NULL, 562 CTLFLAG_PERMANENT, 563 CTLTYPE_NODE, "vfs", NULL, 564 NULL, 0, NULL, 0, 565 CTL_VFS, CTL_EOL); 566 sysctl_createv(&unionfs_sysctl_log, 0, NULL, NULL, 567 CTLFLAG_PERMANENT, 568 CTLTYPE_NODE, "union", 569 SYSCTL_DESCR("Union file system"), 570 NULL, 0, NULL, 0, 571 CTL_VFS, 15, CTL_EOL); 572 /* 573 * XXX the "15" above could be dynamic, thereby eliminating 574 * one more instance of the "number to vfs" mapping problem, 575 * but "15" is the order as taken from sys/mount.h 576 */ 577 break; 578 case MODULE_CMD_FINI: 579 error = vfs_detach(&unionfs_vfsops); 580 if (error != 0) 581 break; 582 sysctl_teardown(&unionfs_sysctl_log); 583 break; 584 default: 585 error = ENOTTY; 586 break; 587 } 588 589 return (error); 590} 591