The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/unionfs/union_vfsops.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1994, 1995 The Regents of the University of California.
    3  * Copyright (c) 1994, 1995 Jan-Simon Pendry.
    4  * All rights reserved.
    5  *
    6  * This code is derived from software donated to Berkeley by
    7  * Jan-Simon Pendry.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  * 4. Neither the name of the University nor the names of its contributors
   18  *    may be used to endorse or promote products derived from this software
   19  *    without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  *
   33  *      @(#)union_vfsops.c      8.20 (Berkeley) 5/20/95
   34  * $FreeBSD$
   35  */
   36 
   37 /*
   38  * Union Layer
   39  */
   40 
   41 #include <sys/param.h>
   42 #include <sys/systm.h>
   43 #include <sys/kernel.h>
   44 #include <sys/lock.h>
   45 #include <sys/mutex.h>
   46 #include <sys/proc.h>
   47 #include <sys/vnode.h>
   48 #include <sys/mount.h>
   49 #include <sys/namei.h>
   50 #include <sys/malloc.h>
   51 #include <sys/filedesc.h>
   52 #include <fs/unionfs/union.h>
   53 
   54 static MALLOC_DEFINE(M_UNIONFSMNT, "UNION mount", "UNION mount structure");
   55 
   56 extern vfs_init_t       union_init;
   57 static vfs_root_t       union_root;
   58 static vfs_mount_t      union_mount;
   59 static vfs_statfs_t     union_statfs;
   60 static vfs_unmount_t    union_unmount;
   61 
   62 /*
   63  * Mount union filesystem.
   64  */
   65 static int
   66 union_mount(mp, td)
   67         struct mount *mp;
   68         struct thread *td;
   69 {
   70         int error = 0;
   71         struct vfsoptlist *opts;
   72         struct vnode *lowerrootvp = NULLVP;
   73         struct vnode *upperrootvp = NULLVP;
   74         struct union_mount *um = 0;
   75         struct ucred *cred = 0;
   76         char *cp = 0, *target;
   77         int op;
   78         int len;
   79         size_t size;
   80         struct componentname fakecn;
   81         struct nameidata nd, *ndp = &nd;
   82 
   83         UDEBUG(("union_mount(mp = %p)\n", (void *)mp));
   84 
   85         opts = mp->mnt_optnew;
   86         /*
   87          * Disable clustered write, otherwise system becomes unstable.
   88          */
   89         mp->mnt_flag |= MNT_NOCLUSTERW;
   90 
   91         /*
   92          * Update is a no-op
   93          */
   94         if (mp->mnt_flag & MNT_UPDATE)
   95                 /*
   96                  * Need to provide:
   97                  * 1. a way to convert between rdonly and rdwr mounts.
   98                  * 2. support for nfs exports.
   99                  */
  100                 return (EOPNOTSUPP);
  101 
  102         /*
  103          * Get arguments.
  104          */
  105         error = vfs_getopt(opts, "target", (void **)&target, &len);
  106         if (error || target[len - 1] != '\0')
  107                 return (EINVAL);
  108 
  109         op = 0;
  110         if (vfs_getopt(opts, "below", NULL, NULL) == 0)
  111                 op = UNMNT_BELOW;
  112         if (vfs_getopt(opts, "replace", NULL, NULL) == 0) {
  113                 /* These options are mutually exclusive. */
  114                 if (op)
  115                         return (EINVAL);
  116                 op = UNMNT_REPLACE;
  117         }
  118         /*
  119          * UNMNT_ABOVE is the default.
  120          */
  121         if (op == 0)
  122                 op = UNMNT_ABOVE;
  123 
  124         /*
  125          * Obtain lower vnode.  Vnode is stored in mp->mnt_vnodecovered.
  126          * We need to reference it but not lock it.
  127          */
  128 
  129         lowerrootvp = mp->mnt_vnodecovered;
  130         VREF(lowerrootvp);
  131 
  132 #if 0
  133         /*
  134          * Unlock lower node to avoid deadlock.
  135          */
  136         if (lowerrootvp->v_op == union_vnodeop_p)
  137                 VOP_UNLOCK(lowerrootvp, 0, td);
  138 #endif
  139 
  140         /*
  141          * Obtain upper vnode by calling namei() on the path.  The
  142          * upperrootvp will be turned referenced but not locked.
  143          */
  144         NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, UIO_SYSSPACE, target, td);
  145 
  146         error = namei(ndp);
  147 
  148 #if 0
  149         if (lowerrootvp->v_op == union_vnodeop_p)
  150                 vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td);
  151 #endif
  152         if (error)
  153                 goto bad;
  154 
  155         NDFREE(ndp, NDF_ONLY_PNBUF);
  156         upperrootvp = ndp->ni_vp;
  157         vrele(ndp->ni_dvp);
  158         ndp->ni_dvp = NULL;
  159 
  160         UDEBUG(("mount_root UPPERVP %p locked = %d\n", upperrootvp,
  161             VOP_ISLOCKED(upperrootvp, NULL)));
  162 
  163         /*
  164          * Check multi union mount to avoid `lock myself again' panic.
  165          * Also require that it be a directory.
  166          */
  167         if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) {
  168 #ifdef DIAGNOSTIC
  169                 printf("union_mount: multi union mount?\n");
  170 #endif
  171                 error = EDEADLK;
  172                 goto bad;
  173         }
  174 
  175         if (upperrootvp->v_type != VDIR) {
  176                 error = EINVAL;
  177                 goto bad;
  178         }
  179 
  180         /*
  181          * Allocate our union_mount structure and populate the fields.
  182          * The vnode references are stored in the union_mount as held,
  183          * unlocked references.  Depending on the _BELOW flag, the
  184          * filesystems are viewed in a different order.  In effect this
  185          * is the same as providing a mount-under option to the mount
  186          * syscall.
  187          */
  188 
  189         um = (struct union_mount *) malloc(sizeof(struct union_mount),
  190                                 M_UNIONFSMNT, M_WAITOK | M_ZERO);
  191 
  192         um->um_op = op;
  193 
  194         switch (um->um_op) {
  195         case UNMNT_ABOVE:
  196                 um->um_lowervp = lowerrootvp;
  197                 um->um_uppervp = upperrootvp;
  198                 upperrootvp = NULL;
  199                 lowerrootvp = NULL;
  200                 break;
  201 
  202         case UNMNT_BELOW:
  203                 um->um_lowervp = upperrootvp;
  204                 um->um_uppervp = lowerrootvp;
  205                 upperrootvp = NULL;
  206                 lowerrootvp = NULL;
  207                 break;
  208 
  209         case UNMNT_REPLACE:
  210                 vrele(lowerrootvp);
  211                 lowerrootvp = NULL;
  212                 um->um_uppervp = upperrootvp;
  213                 um->um_lowervp = lowerrootvp;
  214                 upperrootvp = NULL;
  215                 break;
  216 
  217         default:
  218                 error = EINVAL;
  219                 goto bad;
  220         }
  221 
  222         /*
  223          * Unless the mount is readonly, ensure that the top layer
  224          * supports whiteout operations.
  225          */
  226         if ((mp->mnt_flag & MNT_RDONLY) == 0) {
  227                 /*
  228                  * XXX Fake up a struct componentname with only cn_nameiop
  229                  * and cn_thread valid; union_whiteout() needs to use the
  230                  * thread pointer to lock the vnode.
  231                  */
  232                 bzero(&fakecn, sizeof(fakecn));
  233                 fakecn.cn_nameiop = LOOKUP;
  234                 fakecn.cn_thread = td;
  235                 error = VOP_WHITEOUT(um->um_uppervp, &fakecn, LOOKUP);
  236                 if (error)
  237                         goto bad;
  238         }
  239 
  240         um->um_cred = crhold(td->td_ucred);
  241         FILEDESC_LOCK_FAST(td->td_proc->p_fd);
  242         um->um_cmode = UN_DIRMODE &~ td->td_proc->p_fd->fd_cmask;
  243         FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
  244 
  245         /*
  246          * Depending on what you think the MNT_LOCAL flag might mean,
  247          * you may want the && to be || on the conditional below.
  248          * At the moment it has been defined that the filesystem is
  249          * only local if it is all local, ie the MNT_LOCAL flag implies
  250          * that the entire namespace is local.  If you think the MNT_LOCAL
  251          * flag implies that some of the files might be stored locally
  252          * then you will want to change the conditional.
  253          */
  254         if (um->um_op == UNMNT_ABOVE) {
  255                 if (((um->um_lowervp == NULLVP) ||
  256                      (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
  257                     (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
  258                         mp->mnt_flag |= MNT_LOCAL;
  259         }
  260 
  261         /*
  262          * Copy in the upper layer's RDONLY flag.  This is for the benefit
  263          * of lookup() which explicitly checks the flag, rather than asking
  264          * the filesystem for its own opinion.  This means, that an update
  265          * mount of the underlying filesystem to go from rdonly to rdwr
  266          * will leave the unioned view as read-only.
  267          */
  268         mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
  269 
  270         mp->mnt_data = (qaddr_t) um;
  271         vfs_getnewfsid(mp);
  272 
  273         switch (um->um_op) {
  274         case UNMNT_ABOVE:
  275                 cp = "<above>:";
  276                 break;
  277         case UNMNT_BELOW:
  278                 cp = "<below>:";
  279                 break;
  280         case UNMNT_REPLACE:
  281                 cp = "";
  282                 break;
  283         }
  284         len = strlen(cp);
  285         bcopy(cp, mp->mnt_stat.f_mntfromname, len);
  286 
  287         cp = mp->mnt_stat.f_mntfromname + len;
  288         len = MNAMELEN - len;
  289 
  290         (void) copystr(target, cp, len - 1, &size);
  291         bzero(cp + size, len - size);
  292 
  293         (void)union_statfs(mp, &mp->mnt_stat, td);
  294 
  295         UDEBUG(("union_mount: from %s, on %s\n",
  296                 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname));
  297         return (0);
  298 
  299 bad:
  300         if (um) {
  301                 if (um->um_uppervp)
  302                         vrele(um->um_uppervp);
  303                 if (um->um_lowervp)
  304                         vrele(um->um_lowervp);
  305                 /* XXX other fields */
  306                 free(um, M_UNIONFSMNT);
  307         }
  308         if (cred)
  309                 crfree(cred);
  310         if (upperrootvp)
  311                 vrele(upperrootvp);
  312         if (lowerrootvp)
  313                 vrele(lowerrootvp);
  314         return (error);
  315 }
  316 
  317 /*
  318  * Free reference to union layer.
  319  */
  320 static int
  321 union_unmount(mp, mntflags, td)
  322         struct mount *mp;
  323         int mntflags;
  324         struct thread *td;
  325 {
  326         struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
  327         int error;
  328         int freeing;
  329         int flags = 0;
  330 
  331         UDEBUG(("union_unmount(mp = %p)\n", (void *)mp));
  332 
  333         if (mntflags & MNT_FORCE)
  334                 flags |= FORCECLOSE;
  335 
  336         /*
  337          * Keep flushing vnodes from the mount list.
  338          * This is needed because of the un_pvp held
  339          * reference to the parent vnode.
  340          * If more vnodes have been freed on a given pass,
  341          * the try again.  The loop will iterate at most
  342          * (d) times, where (d) is the maximum tree depth
  343          * in the filesystem.
  344          */
  345         for (freeing = 0; (error = vflush(mp, 0, flags, td)) != 0;) {
  346                 int n;
  347 
  348                 /* count #vnodes held on mount list */
  349                 n = mp->mnt_nvnodelistsize;
  350 
  351                 /* if this is unchanged then stop */
  352                 if (n == freeing)
  353                         break;
  354 
  355                 /* otherwise try once more time */
  356                 freeing = n;
  357         }
  358 
  359         /*
  360          * If the most recent vflush failed, the filesystem is still busy.
  361          */
  362         if (error)
  363                 return (error);
  364 
  365         /*
  366          * Discard references to upper and lower target vnodes.
  367          */
  368         if (um->um_lowervp)
  369                 vrele(um->um_lowervp);
  370         vrele(um->um_uppervp);
  371         crfree(um->um_cred);
  372         /*
  373          * Finally, throw away the union_mount structure.
  374          */
  375         free(mp->mnt_data, M_UNIONFSMNT);       /* XXX */
  376         mp->mnt_data = 0;
  377         return (0);
  378 }
  379 
  380 static int
  381 union_root(mp, vpp, td)
  382         struct mount *mp;
  383         struct vnode **vpp;
  384         struct thread *td;
  385 {
  386         struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
  387         int error;
  388 
  389         /*
  390          * Supply an unlocked reference to um_uppervp and to um_lowervp.  It
  391          * is possible for um_uppervp to be locked without the associated
  392          * root union_node being locked.  We let union_allocvp() deal with
  393          * it.
  394          */
  395         UDEBUG(("union_root UPPERVP %p locked = %d\n", um->um_uppervp,
  396             VOP_ISLOCKED(um->um_uppervp, NULL)));
  397 
  398         VREF(um->um_uppervp);
  399         if (um->um_lowervp)
  400                 VREF(um->um_lowervp);
  401 
  402         error = union_allocvp(vpp, mp, NULLVP, NULLVP, NULL, 
  403                     um->um_uppervp, um->um_lowervp, 1);
  404         UDEBUG(("error %d\n", error));
  405         UDEBUG(("union_root2 UPPERVP %p locked = %d\n", um->um_uppervp,
  406             VOP_ISLOCKED(um->um_uppervp, NULL)));
  407 
  408         return (error);
  409 }
  410 
  411 static int
  412 union_statfs(mp, sbp, td)
  413         struct mount *mp;
  414         struct statfs *sbp;
  415         struct thread *td;
  416 {
  417         int error;
  418         struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
  419         struct statfs mstat;
  420         int lbsize;
  421 
  422         UDEBUG(("union_statfs(mp = %p, lvp = %p, uvp = %p)\n",
  423             (void *)mp, (void *)um->um_lowervp, (void *)um->um_uppervp));
  424 
  425         bzero(&mstat, sizeof(mstat));
  426 
  427         if (um->um_lowervp) {
  428                 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, td);
  429                 if (error)
  430                         return (error);
  431         }
  432 
  433         /*
  434          * Now copy across the "interesting" information and fake the rest.
  435          */
  436 #if 0
  437         sbp->f_type = mstat.f_type;
  438         sbp->f_flags = mstat.f_flags;
  439         sbp->f_bsize = mstat.f_bsize;
  440         sbp->f_iosize = mstat.f_iosize;
  441 #endif
  442         lbsize = mstat.f_bsize;
  443         sbp->f_blocks = mstat.f_blocks;
  444         sbp->f_bfree = mstat.f_bfree;
  445         sbp->f_bavail = mstat.f_bavail;
  446         sbp->f_files = mstat.f_files;
  447         sbp->f_ffree = mstat.f_ffree;
  448 
  449         error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, td);
  450         if (error)
  451                 return (error);
  452 
  453         sbp->f_flags = mstat.f_flags;
  454         sbp->f_bsize = mstat.f_bsize;
  455         sbp->f_iosize = mstat.f_iosize;
  456 
  457         /*
  458          * If the lower and upper blocksizes differ, then frig the
  459          * block counts so that the sizes reported by df make some
  460          * kind of sense.  None of this makes sense though.
  461          */
  462 
  463         if (mstat.f_bsize != lbsize)
  464                 sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize;
  465 
  466         /*
  467          * The "total" fields count total resources in all layers,
  468          * the "free" fields count only those resources which are
  469          * free in the upper layer (since only the upper layer
  470          * is writeable).
  471          */
  472         sbp->f_blocks += mstat.f_blocks;
  473         sbp->f_bfree = mstat.f_bfree;
  474         sbp->f_bavail = mstat.f_bavail;
  475         sbp->f_files += mstat.f_files;
  476         sbp->f_ffree = mstat.f_ffree;
  477 
  478         if (sbp != &mp->mnt_stat) {
  479                 sbp->f_type = mp->mnt_vfc->vfc_typenum;
  480                 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
  481                 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
  482                 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
  483         }
  484         return (0);
  485 }
  486 
  487 static struct vfsops union_vfsops = {
  488         .vfs_init =             union_init,
  489         .vfs_mount =            union_mount,
  490         .vfs_root =             union_root,
  491         .vfs_statfs =           union_statfs,
  492         .vfs_unmount =          union_unmount,
  493 };
  494 
  495 VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK);

Cache object: c8eab7f77299a2ae39957b4ee49fe4aa


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.