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: releng/6.0/sys/fs/unionfs/union_vfsops.c 145586 2005-04-27 09:07:13Z jeff $
   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 vattr va;
   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         if (mp->mnt_flag & MNT_ROOTFS)
   92                 return (EOPNOTSUPP);
   93         /*
   94          * Update is a no-op
   95          */
   96         if (mp->mnt_flag & MNT_UPDATE)
   97                 /*
   98                  * Need to provide:
   99                  * 1. a way to convert between rdonly and rdwr mounts.
  100                  * 2. support for nfs exports.
  101                  */
  102                 return (EOPNOTSUPP);
  103 
  104         /*
  105          * Get arguments.
  106          */
  107         error = vfs_getopt(opts, "target", (void **)&target, &len);
  108         if (error || target[len - 1] != '\0')
  109                 return (EINVAL);
  110 
  111         op = 0;
  112         if (vfs_getopt(opts, "below", NULL, NULL) == 0)
  113                 op = UNMNT_BELOW;
  114         if (vfs_getopt(opts, "replace", NULL, NULL) == 0) {
  115                 /* These options are mutually exclusive. */
  116                 if (op)
  117                         return (EINVAL);
  118                 op = UNMNT_REPLACE;
  119         }
  120         /*
  121          * UNMNT_ABOVE is the default.
  122          */
  123         if (op == 0)
  124                 op = UNMNT_ABOVE;
  125 
  126         /*
  127          * Obtain lower vnode.  Vnode is stored in mp->mnt_vnodecovered.
  128          * We need to reference it but not lock it.
  129          */
  130         lowerrootvp = mp->mnt_vnodecovered;
  131         VREF(lowerrootvp);
  132         /*
  133          * Obtain upper vnode by calling namei() on the path.  The
  134          * upperrootvp will be turned referenced and locked.
  135          */
  136         NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, td);
  137         error = namei(ndp);
  138         if (error)
  139                 goto bad;
  140         NDFREE(ndp, NDF_ONLY_PNBUF);
  141         upperrootvp = ndp->ni_vp;
  142 
  143         UDEBUG(("mount_root UPPERVP %p locked = %d\n", upperrootvp,
  144             VOP_ISLOCKED(upperrootvp, NULL)));
  145 
  146         /*
  147          * Check multi union mount to avoid `lock myself again' panic.
  148          * Also require that it be a directory.
  149          */
  150         if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) {
  151 #ifdef DIAGNOSTIC
  152                 printf("union_mount: multi union mount?\n");
  153 #endif
  154                 error = EDEADLK;
  155                 goto bad;
  156         }
  157 
  158         if (upperrootvp->v_type != VDIR) {
  159                 error = EINVAL;
  160                 goto bad;
  161         }
  162 
  163         /*
  164          * Allocate our union_mount structure and populate the fields.
  165          * The vnode references are stored in the union_mount as held,
  166          * unlocked references.  Depending on the _BELOW flag, the
  167          * filesystems are viewed in a different order.  In effect this
  168          * is the same as providing a mount-under option to the mount
  169          * syscall.
  170          */
  171 
  172         um = (struct union_mount *) malloc(sizeof(struct union_mount),
  173                                 M_UNIONFSMNT, M_WAITOK | M_ZERO);
  174 
  175         um->um_op = op;
  176 
  177         error = VOP_GETATTR(upperrootvp, &va, td->td_ucred, td);
  178         if (error)
  179                 goto bad;
  180 
  181         um->um_upperdev = va.va_fsid;
  182 
  183         switch (um->um_op) {
  184         case UNMNT_ABOVE:
  185                 um->um_lowervp = lowerrootvp;
  186                 um->um_uppervp = upperrootvp;
  187                 upperrootvp = NULL;
  188                 lowerrootvp = NULL;
  189                 break;
  190 
  191         case UNMNT_BELOW:
  192                 VOP_UNLOCK(upperrootvp, 0, td);
  193                 vn_lock(lowerrootvp, LK_RETRY|LK_EXCLUSIVE, td);
  194                 um->um_lowervp = upperrootvp;
  195                 um->um_uppervp = lowerrootvp;
  196                 upperrootvp = NULL;
  197                 lowerrootvp = NULL;
  198                 break;
  199 
  200         case UNMNT_REPLACE:
  201                 vrele(lowerrootvp);
  202                 lowerrootvp = NULL;
  203                 um->um_uppervp = upperrootvp;
  204                 um->um_lowervp = lowerrootvp;
  205                 upperrootvp = NULL;
  206                 break;
  207 
  208         default:
  209                 error = EINVAL;
  210                 goto bad;
  211         }
  212 
  213         /*
  214          * Unless the mount is readonly, ensure that the top layer
  215          * supports whiteout operations.
  216          */
  217         if ((mp->mnt_flag & MNT_RDONLY) == 0) {
  218                 /*
  219                  * XXX Fake up a struct componentname with only cn_nameiop
  220                  * and cn_thread valid; union_whiteout() needs to use the
  221                  * thread pointer to lock the vnode.
  222                  */
  223                 bzero(&fakecn, sizeof(fakecn));
  224                 fakecn.cn_nameiop = LOOKUP;
  225                 fakecn.cn_thread = td;
  226                 error = VOP_WHITEOUT(um->um_uppervp, &fakecn, LOOKUP);
  227                 if (error)
  228                         goto bad;
  229         }
  230         VOP_UNLOCK(um->um_uppervp, 0, td);
  231 
  232         um->um_cred = crhold(td->td_ucred);
  233         FILEDESC_LOCK_FAST(td->td_proc->p_fd);
  234         um->um_cmode = UN_DIRMODE &~ td->td_proc->p_fd->fd_cmask;
  235         FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
  236 
  237         /*
  238          * Depending on what you think the MNT_LOCAL flag might mean,
  239          * you may want the && to be || on the conditional below.
  240          * At the moment it has been defined that the filesystem is
  241          * only local if it is all local, ie the MNT_LOCAL flag implies
  242          * that the entire namespace is local.  If you think the MNT_LOCAL
  243          * flag implies that some of the files might be stored locally
  244          * then you will want to change the conditional.
  245          */
  246         if (um->um_op == UNMNT_ABOVE) {
  247                 if (((um->um_lowervp == NULLVP) ||
  248                      (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
  249                     (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
  250                         mp->mnt_flag |= MNT_LOCAL;
  251         }
  252 
  253         /*
  254          * Copy in the upper layer's RDONLY flag.  This is for the benefit
  255          * of lookup() which explicitly checks the flag, rather than asking
  256          * the filesystem for its own opinion.  This means, that an update
  257          * mount of the underlying filesystem to go from rdonly to rdwr
  258          * will leave the unioned view as read-only.
  259          */
  260         mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
  261 
  262         mp->mnt_data = (qaddr_t) um;
  263         vfs_getnewfsid(mp);
  264 
  265         switch (um->um_op) {
  266         case UNMNT_ABOVE:
  267                 cp = "<above>:";
  268                 break;
  269         case UNMNT_BELOW:
  270                 cp = "<below>:";
  271                 break;
  272         case UNMNT_REPLACE:
  273                 cp = "";
  274                 break;
  275         }
  276         len = strlen(cp);
  277         bcopy(cp, mp->mnt_stat.f_mntfromname, len);
  278 
  279         cp = mp->mnt_stat.f_mntfromname + len;
  280         len = MNAMELEN - len;
  281 
  282         (void) copystr(target, cp, len - 1, &size);
  283         bzero(cp + size, len - size);
  284 
  285         UDEBUG(("union_mount: from %s, on %s\n",
  286                 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname));
  287         return (0);
  288 
  289 bad:
  290         if (um) {
  291                 if (um->um_uppervp)
  292                         vput(um->um_uppervp);
  293                 if (um->um_lowervp)
  294                         vrele(um->um_lowervp);
  295                 /* XXX other fields */
  296                 free(um, M_UNIONFSMNT);
  297         }
  298         if (upperrootvp)
  299                 vput(upperrootvp);
  300         if (lowerrootvp)
  301                 vrele(lowerrootvp);
  302         return (error);
  303 }
  304 
  305 /*
  306  * Free reference to union layer.
  307  */
  308 static int
  309 union_unmount(mp, mntflags, td)
  310         struct mount *mp;
  311         int mntflags;
  312         struct thread *td;
  313 {
  314         struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
  315         int error;
  316         int freeing;
  317         int flags = 0;
  318 
  319         UDEBUG(("union_unmount(mp = %p)\n", (void *)mp));
  320 
  321         if (mntflags & MNT_FORCE)
  322                 flags |= FORCECLOSE;
  323 
  324         /*
  325          * Keep flushing vnodes from the mount list.
  326          * This is needed because of the un_pvp held
  327          * reference to the parent vnode.
  328          * If more vnodes have been freed on a given pass,
  329          * the try again.  The loop will iterate at most
  330          * (d) times, where (d) is the maximum tree depth
  331          * in the filesystem.
  332          */
  333         for (freeing = 0; (error = vflush(mp, 0, flags, td)) != 0;) {
  334                 int n;
  335 
  336                 /* count #vnodes held on mount list */
  337                 n = mp->mnt_nvnodelistsize;
  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         /*
  348          * If the most recent vflush failed, the filesystem is still busy.
  349          */
  350         if (error)
  351                 return (error);
  352 
  353         /*
  354          * Discard references to upper and lower target vnodes.
  355          */
  356         if (um->um_lowervp)
  357                 vrele(um->um_lowervp);
  358         vrele(um->um_uppervp);
  359         crfree(um->um_cred);
  360         /*
  361          * Finally, throw away the union_mount structure.
  362          */
  363         free(mp->mnt_data, M_UNIONFSMNT);       /* XXX */
  364         mp->mnt_data = 0;
  365         return (0);
  366 }
  367 
  368 static int
  369 union_root(mp, flags, vpp, td)
  370         struct mount *mp;
  371         int flags;
  372         struct vnode **vpp;
  373         struct thread *td;
  374 {
  375         struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
  376         int error;
  377 
  378         /*
  379          * Supply an unlocked reference to um_uppervp and to um_lowervp.  It
  380          * is possible for um_uppervp to be locked without the associated
  381          * root union_node being locked.  We let union_allocvp() deal with
  382          * it.
  383          */
  384         UDEBUG(("union_root UPPERVP %p locked = %d\n", um->um_uppervp,
  385             VOP_ISLOCKED(um->um_uppervp, NULL)));
  386 
  387         VREF(um->um_uppervp);
  388         if (um->um_lowervp)
  389                 VREF(um->um_lowervp);
  390 
  391         error = union_allocvp(vpp, mp, NULLVP, NULLVP, NULL, 
  392                     um->um_uppervp, um->um_lowervp, 1);
  393         UDEBUG(("error %d\n", error));
  394         UDEBUG(("union_root2 UPPERVP %p locked = %d\n", um->um_uppervp,
  395             VOP_ISLOCKED(um->um_uppervp, NULL)));
  396 
  397         return (error);
  398 }
  399 
  400 static int
  401 union_statfs(mp, sbp, td)
  402         struct mount *mp;
  403         struct statfs *sbp;
  404         struct thread *td;
  405 {
  406         int error;
  407         struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
  408         struct statfs mstat;
  409         int lbsize;
  410 
  411         UDEBUG(("union_statfs(mp = %p, lvp = %p, uvp = %p)\n",
  412             (void *)mp, (void *)um->um_lowervp, (void *)um->um_uppervp));
  413 
  414         bzero(&mstat, sizeof(mstat));
  415 
  416         if (um->um_lowervp) {
  417                 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, td);
  418                 if (error)
  419                         return (error);
  420         }
  421 
  422         /*
  423          * Now copy across the "interesting" information and fake the rest.
  424          */
  425 #if 0
  426         sbp->f_type = mstat.f_type;
  427         sbp->f_flags = mstat.f_flags;
  428         sbp->f_bsize = mstat.f_bsize;
  429         sbp->f_iosize = mstat.f_iosize;
  430 #endif
  431         lbsize = mstat.f_bsize;
  432         sbp->f_blocks = mstat.f_blocks;
  433         sbp->f_bfree = mstat.f_bfree;
  434         sbp->f_bavail = mstat.f_bavail;
  435         sbp->f_files = mstat.f_files;
  436         sbp->f_ffree = mstat.f_ffree;
  437 
  438         error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, td);
  439         if (error)
  440                 return (error);
  441 
  442         sbp->f_flags = mstat.f_flags;
  443         sbp->f_bsize = mstat.f_bsize;
  444         sbp->f_iosize = mstat.f_iosize;
  445 
  446         /*
  447          * If the lower and upper blocksizes differ, then frig the
  448          * block counts so that the sizes reported by df make some
  449          * kind of sense.  None of this makes sense though.
  450          */
  451 
  452         if (mstat.f_bsize != lbsize)
  453                 sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize;
  454 
  455         /*
  456          * The "total" fields count total resources in all layers,
  457          * the "free" fields count only those resources which are
  458          * free in the upper layer (since only the upper layer
  459          * is writeable).
  460          */
  461         sbp->f_blocks += mstat.f_blocks;
  462         sbp->f_bfree = mstat.f_bfree;
  463         sbp->f_bavail = mstat.f_bavail;
  464         sbp->f_files += mstat.f_files;
  465         sbp->f_ffree = mstat.f_ffree;
  466         return (0);
  467 }
  468 
  469 static struct vfsops union_vfsops = {
  470         .vfs_init =             union_init,
  471         .vfs_mount =            union_mount,
  472         .vfs_root =             union_root,
  473         .vfs_statfs =           union_statfs,
  474         .vfs_unmount =          union_unmount,
  475 };
  476 
  477 VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK);

Cache object: 1ab32a3b467549c380e6813f8fe2bbe6


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