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

Cache object: 90c861743910366353a7097d5099d4c3


[ 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.