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/ufs/ffs/ffs_suspend.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2012 The FreeBSD Foundation
    5  *
    6  * This software was developed by Edward Tomasz Napierala under sponsorship
    7  * from the FreeBSD Foundation.
    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  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  *
   30  * $FreeBSD$
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 #include <sys/systm.h>
   37 #include <sys/buf.h>
   38 #include <sys/conf.h>
   39 #include <sys/filedesc.h>
   40 #include <sys/ioccom.h>
   41 #include <sys/jail.h>
   42 #include <sys/mount.h>
   43 #include <sys/sx.h>
   44 #include <sys/vnode.h>
   45 
   46 #include <security/mac/mac_framework.h>
   47 
   48 #include <ufs/ufs/extattr.h>
   49 #include <ufs/ufs/quota.h>
   50 #include <ufs/ufs/ufsmount.h>
   51 #include <ufs/ufs/inode.h>
   52 
   53 #include <ufs/ffs/fs.h>
   54 #include <ufs/ffs/ffs_extern.h>
   55 
   56 static d_open_t ffs_susp_open;
   57 static d_write_t ffs_susp_rdwr;
   58 static d_ioctl_t ffs_susp_ioctl;
   59 
   60 static struct cdevsw ffs_susp_cdevsw = {
   61         .d_version =    D_VERSION,
   62         .d_open =       ffs_susp_open,
   63         .d_read =       ffs_susp_rdwr,
   64         .d_write =      ffs_susp_rdwr,
   65         .d_ioctl =      ffs_susp_ioctl,
   66         .d_name =       "ffs_susp",
   67 };
   68 
   69 static struct cdev *ffs_susp_dev;
   70 static struct sx ffs_susp_lock;
   71 
   72 static int
   73 ffs_susp_suspended(struct mount *mp)
   74 {
   75         struct ufsmount *ump;
   76 
   77         sx_assert(&ffs_susp_lock, SA_LOCKED);
   78 
   79         ump = VFSTOUFS(mp);
   80         if ((ump->um_flags & UM_WRITESUSPENDED) != 0)
   81                 return (1);
   82         return (0);
   83 }
   84 
   85 static int
   86 ffs_susp_open(struct cdev *dev __unused, int flags __unused,
   87     int fmt __unused, struct thread *td __unused)
   88 {
   89 
   90         return (0);
   91 }
   92 
   93 static int
   94 ffs_susp_rdwr(struct cdev *dev, struct uio *uio, int ioflag)
   95 {
   96         int error, i;
   97         struct vnode *devvp;
   98         struct mount *mp;
   99         struct ufsmount *ump;
  100         struct buf *bp;
  101         void *base;
  102         size_t len;
  103         ssize_t cnt;
  104         struct fs *fs;
  105 
  106         sx_slock(&ffs_susp_lock);
  107 
  108         error = devfs_get_cdevpriv((void **)&mp);
  109         if (error != 0) {
  110                 sx_sunlock(&ffs_susp_lock);
  111                 return (ENXIO);
  112         }
  113 
  114         ump = VFSTOUFS(mp);
  115         devvp = ump->um_devvp;
  116         fs = ump->um_fs;
  117 
  118         if (ffs_susp_suspended(mp) == 0) {
  119                 sx_sunlock(&ffs_susp_lock);
  120                 return (ENXIO);
  121         }
  122 
  123         KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE,
  124             ("neither UIO_READ or UIO_WRITE"));
  125         KASSERT(uio->uio_segflg == UIO_USERSPACE,
  126             ("uio->uio_segflg != UIO_USERSPACE"));
  127 
  128         cnt = uio->uio_resid;
  129 
  130         for (i = 0; i < uio->uio_iovcnt; i++) {
  131                 while (uio->uio_iov[i].iov_len) {
  132                         base = uio->uio_iov[i].iov_base;
  133                         len = uio->uio_iov[i].iov_len;
  134                         if (len > fs->fs_bsize)
  135                                 len = fs->fs_bsize;
  136                         if (fragoff(fs, uio->uio_offset) != 0 ||
  137                             fragoff(fs, len) != 0) {
  138                                 error = EINVAL;
  139                                 goto out;
  140                         }
  141                         error = bread(devvp, btodb(uio->uio_offset), len,
  142                             NOCRED, &bp);
  143                         if (error != 0)
  144                                 goto out;
  145                         if (uio->uio_rw == UIO_WRITE) {
  146                                 error = copyin(base, bp->b_data, len);
  147                                 if (error != 0) {
  148                                         bp->b_flags |= B_INVAL | B_NOCACHE;
  149                                         brelse(bp);
  150                                         goto out;
  151                                 }
  152                                 error = bwrite(bp);
  153                                 if (error != 0)
  154                                         goto out;
  155                         } else {
  156                                 error = copyout(bp->b_data, base, len);
  157                                 brelse(bp);
  158                                 if (error != 0)
  159                                         goto out;
  160                         }
  161                         uio->uio_iov[i].iov_base =
  162                             (char *)uio->uio_iov[i].iov_base + len;
  163                         uio->uio_iov[i].iov_len -= len;
  164                         uio->uio_resid -= len;
  165                         uio->uio_offset += len;
  166                 }
  167         }
  168 
  169 out:
  170         sx_sunlock(&ffs_susp_lock);
  171 
  172         if (uio->uio_resid < cnt)
  173                 return (0);
  174 
  175         return (error);
  176 }
  177 
  178 static int
  179 ffs_susp_suspend(struct mount *mp)
  180 {
  181         struct ufsmount *ump;
  182         int error;
  183 
  184         sx_assert(&ffs_susp_lock, SA_XLOCKED);
  185 
  186         if (!ffs_own_mount(mp))
  187                 return (EINVAL);
  188         if (ffs_susp_suspended(mp))
  189                 return (EBUSY);
  190 
  191         ump = VFSTOUFS(mp);
  192 
  193         /*
  194          * Make sure the calling thread is permitted to access the mounted
  195          * device.  The permissions can change after we unlock the vnode;
  196          * it's harmless.
  197          */
  198         vn_lock(ump->um_odevvp, LK_EXCLUSIVE | LK_RETRY);
  199         error = VOP_ACCESS(ump->um_odevvp, VREAD | VWRITE,
  200             curthread->td_ucred, curthread);
  201         VOP_UNLOCK(ump->um_odevvp);
  202         if (error != 0)
  203                 return (error);
  204 #ifdef MAC
  205         if (mac_mount_check_stat(curthread->td_ucred, mp) != 0)
  206                 return (EPERM);
  207 #endif
  208 
  209         if ((error = vfs_write_suspend(mp, VS_SKIP_UNMOUNT)) != 0)
  210                 return (error);
  211 
  212         UFS_LOCK(ump);
  213         ump->um_flags |= UM_WRITESUSPENDED;
  214         UFS_UNLOCK(ump);
  215 
  216         return (0);
  217 }
  218 
  219 static void
  220 ffs_susp_unsuspend(struct mount *mp)
  221 {
  222         struct ufsmount *ump;
  223 
  224         sx_assert(&ffs_susp_lock, SA_XLOCKED);
  225 
  226         /*
  227          * XXX: The status is kept per-process; the vfs_write_resume() routine
  228          *      asserts that the resuming thread is the same one that called
  229          *      vfs_write_suspend().  The cdevpriv data, however, is attached
  230          *      to the file descriptor, e.g. is inherited during fork.  Thus,
  231          *      it's possible that the resuming process will be different from
  232          *      the one that started the suspension.
  233          *
  234          *      Work around by fooling the check in vfs_write_resume().
  235          */
  236         mp->mnt_susp_owner = curthread;
  237 
  238         vfs_write_resume(mp, 0);
  239         ump = VFSTOUFS(mp);
  240         UFS_LOCK(ump);
  241         ump->um_flags &= ~UM_WRITESUSPENDED;
  242         UFS_UNLOCK(ump);
  243         vfs_unbusy(mp);
  244 }
  245 
  246 static void
  247 ffs_susp_dtor(void *data)
  248 {
  249         struct fs *fs;
  250         struct ufsmount *ump;
  251         struct mount *mp;
  252         int error;
  253 
  254         sx_xlock(&ffs_susp_lock);
  255 
  256         mp = (struct mount *)data;
  257         ump = VFSTOUFS(mp);
  258         fs = ump->um_fs;
  259 
  260         if (ffs_susp_suspended(mp) == 0) {
  261                 sx_xunlock(&ffs_susp_lock);
  262                 return;
  263         }
  264 
  265         KASSERT((mp->mnt_kern_flag & MNTK_SUSPEND) != 0,
  266             ("MNTK_SUSPEND not set"));
  267 
  268         error = ffs_reload(mp, FFSR_FORCE | FFSR_UNSUSPEND);
  269         if (error != 0)
  270                 panic("failed to unsuspend writes on %s", fs->fs_fsmnt);
  271 
  272         ffs_susp_unsuspend(mp);
  273         sx_xunlock(&ffs_susp_lock);
  274 }
  275 
  276 static int
  277 ffs_susp_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
  278     struct thread *td)
  279 {
  280         struct mount *mp;
  281         fsid_t *fsidp;
  282         int error;
  283 
  284         /*
  285          * No suspend inside the jail.  Allowing it would require making
  286          * sure that e.g. the devfs ruleset for that jail permits access
  287          * to the devvp.
  288          */
  289         if (jailed(td->td_ucred))
  290                 return (EPERM);
  291 
  292         sx_xlock(&ffs_susp_lock);
  293 
  294         switch (cmd) {
  295         case UFSSUSPEND:
  296                 fsidp = (fsid_t *)addr;
  297                 mp = vfs_getvfs(fsidp);
  298                 if (mp == NULL) {
  299                         error = ENOENT;
  300                         break;
  301                 }
  302                 error = vfs_busy(mp, 0);
  303                 vfs_rel(mp);
  304                 if (error != 0)
  305                         break;
  306 
  307                 /*
  308                  * Require single-thread curproc so that the check is not racey.
  309                  * XXXKIB: might consider to singlethread curproc instead.
  310                  */
  311                 error = curproc->p_numthreads > 1 ? EDEADLK :
  312                     descrip_check_write_mp(curproc->p_fd, mp);
  313                 if (error != 0) {
  314                         vfs_unbusy(mp);
  315                         break;
  316                 }
  317 
  318                 error = ffs_susp_suspend(mp);
  319                 if (error != 0) {
  320                         vfs_unbusy(mp);
  321                         break;
  322                 }
  323                 error = devfs_set_cdevpriv(mp, ffs_susp_dtor);
  324                 if (error != 0)
  325                         ffs_susp_unsuspend(mp);
  326                 break;
  327         case UFSRESUME:
  328                 error = devfs_get_cdevpriv((void **)&mp);
  329                 if (error != 0)
  330                         break;
  331                 /*
  332                  * This calls ffs_susp_dtor, which in turn unsuspends the fs.
  333                  * The dtor expects to be called without lock held, because
  334                  * sometimes it's called from here, and sometimes due to the
  335                  * file being closed or process exiting.
  336                  */
  337                 sx_xunlock(&ffs_susp_lock);
  338                 devfs_clear_cdevpriv();
  339                 return (0);
  340         default:
  341                 error = ENXIO;
  342                 break;
  343         }
  344 
  345         sx_xunlock(&ffs_susp_lock);
  346 
  347         return (error);
  348 }
  349 
  350 void
  351 ffs_susp_initialize(void)
  352 {
  353 
  354         sx_init(&ffs_susp_lock, "ffs_susp");
  355         ffs_susp_dev = make_dev(&ffs_susp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
  356             "ufssuspend");
  357 }
  358 
  359 void
  360 ffs_susp_uninitialize(void)
  361 {
  362 
  363         destroy_dev(ffs_susp_dev);
  364         sx_destroy(&ffs_susp_lock);
  365 }

Cache object: 61c1b85ace5940142653677016460804


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