예제 #1
0
int
umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags)
{
  int error;

eintr:
  error = umount(mntdir, MNT_NOFORCE);
  if (error < 0)
    error = errno;

  switch (error) {
  case EINVAL:
  case ENOTBLK:
    plog(XLOG_WARNING, "unmount: %s is not mounted", mntdir);
    error = 0;			/* Not really an error */
    break;

  case ENOENT:
    plog(XLOG_ERROR, "mount point %s: %m", mntdir);
    break;

  case EINTR:
    /* not sure why this happens, but it does.  ask kirk one day... */
    dlog("%s: unmount: %m", mntdir);
    goto eintr;

#ifdef MNT2_GEN_OPT_FORCE
  case EBUSY:
  case EIO:
  case ESTALE:
    /* caller determines if forced unmounts should be used */
    if (unmount_flags & AMU_UMOUNT_FORCE) {
      error = umount2_fs(mntdir, unmount_flags);
      if ((error = umount2_fs(mntdir, unmount_flags)) < 0)
	error = errno;
      else
	return error;
    }
    /* fallthrough */
#endif /* MNT2_GEN_OPT_FORCE */

  default:
    dlog("%s: unmount: %m", mntdir);
    break;
  }

  return error;
}
예제 #2
0
/*
 * Initialize a top-level mount.  In our case, if the user asked for
 * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts
 * on any previous toplvl mounts.  This is useful if a previous Amd died and
 * left behind toplvl mount points (this Amd will clean them up).
 *
 * WARNING: Don't use forced/lazy unmounts if you have another valid Amd
 * running, because this code WILL force those valid toplvl mount points to
 * be detached as well!
 */
static int
amfs_toplvl_init(mntfs *mf)
{
  int error = 0;

#if (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) && (defined(HAVE_UVMOUNT) || defined(HAVE_UMOUNT2))
  if (gopt.flags & CFM_FORCED_UNMOUNTS) {
    plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s",
	 mf->mf_mount);
    error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH);
    if (error)
      plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m");
    else
      dlog("amfs_toplvl_init: forced/lazy unmount succeeded");
  }
#endif /* (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) && (HAVE_UVMOUNT || HAVE_UMOUNT2) */
  return error;
}
예제 #3
0
int
umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags)
{
  mntlist *mlist, *mp, *mp_save = NULL;
  int error = 0;
#ifdef HAVE_LOOP_DEVICE
  char *opt, *xopts = NULL;
  char loopstr[] = "loop=";
  char *loopdev;
#endif /* HAVE_LOOP_DEVICE */
  unsigned int retries = 8;

  mp = mlist = read_mtab(mntdir, mnttabname);

  /*
   * Search the mount table looking for
   * the correct (ie last) matching entry
   */
  while (mp) {
    if (STREQ(mp->mnt->mnt_dir, mntdir))
      mp_save = mp;
    mp = mp->mnext;
  }

  if (!mp_save) {
    plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir);
    /* Assume it is already unmounted */
    error = 0;
    goto out;
  }

  plog(XLOG_ERROR, "Trying unmount %s, umount_flags 0x%x", mp_save->mnt->mnt_dir, unmount_flags);
  dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir);

#ifdef MOUNT_TABLE_ON_FILE
  /*
   * This unmount may hang leaving this process with an exclusive lock on
   * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount,
   * then lock mtab (again) and reread it and finally update it.
   */
  unlock_mntlist();
#endif /* MOUNT_TABLE_ON_FILE */

again:
#if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH)
  /*
   * If user asked to try forced unmounts, then do a quick check to see if
   * the mount point is hung badly.  If so, then try to detach it by
   * force; if the latter works, we're done.
   */
  if (unmount_flags & AMU_UMOUNT_DETACH) {
    /*
     * Note: we pass both DETACH and FORCE flags, because umount2_fs below
     * (on Linux), should try FORCE before DETACH (the latter always
     * succeeds).
     */
    error = umount2_fs(mp_save->mnt->mnt_dir,
		       unmount_flags & (AMU_UMOUNT_DETACH|AMU_UMOUNT_FORCE));
  } else
#endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */
    error = UNMOUNT_TRAP(mp_save->mnt);

  /* Linux kernel can be sluggish for some reason */
  if (error == EBUSY && retries--) {
    struct timespec tm = {0, 200000000};
    nanosleep(&tm, NULL);
    goto again;
  }

  if (error < 0) {
    plog(XLOG_WARNING, "unmount(%s) failed: %m", mp_save->mnt->mnt_dir);
    switch ((error = errno)) {
    case EINVAL:
    case ENOTBLK:
      plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir);
      error = 0;		/* Not really an error */
      break;

    case ENOENT:
      /*
       * This could happen if the kernel insists on following symlinks
       * when we try to unmount a direct mountpoint. We need to propagate
       * the error up so that the top layers know it failed and don't
       * try to rmdir() the mountpoint or other silly things.
       */
      plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir);
      break;

#if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE)
    case EBUSY:
      /*
       * Caller determines if forced unmounts should be used now (for
       * EBUSY).  If caller asked to force an unmount, *and* the above
       * "trivial" unmount attempt failed with EBUSY, then try to force
       * the unmount.
       */
      if (unmount_flags & AMU_UMOUNT_FORCE) {
	error = umount2_fs(mp_save->mnt->mnt_dir,
			   unmount_flags & AMU_UMOUNT_FORCE);
	if (error < 0) {
	  plog(XLOG_WARNING, "%s: unmount/force: %m",
	       mp_save->mnt->mnt_dir);
	  error = errno;
	}
      }
      break;
#endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) */

    default:
      dlog("%s: unmount: %m", mp_save->mnt->mnt_dir);
      break;
    }
  } else {
    dlog("unmount(%s) succeeded", mp_save->mnt->mnt_dir);
  }
  dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir);

  /*
   * If we are successful or there was an ENOENT, remove
   * the mount entry from the mtab file.
   */
  if (error && error != ENOENT)
    goto out;

#ifdef HAVE_LOOP_DEVICE
  /* look for loop=/dev/loopX in mnt_opts */
  xopts = xstrdup(mp_save->mnt->mnt_opts); /* b/c strtok is destructive */
  for (opt = strtok(xopts, ","); opt; opt = strtok(NULL, ","))
    if (NSTREQ(opt, loopstr, sizeof(loopstr) - 1)) {
      loopdev = opt + sizeof(loopstr) - 1;
      if (delete_loop_device(loopdev) < 0)
	plog(XLOG_WARNING, "unmount() failed to release loop device %s: %m", loopdev);
      else
	plog(XLOG_INFO, "unmount() released loop device %s OK", loopdev);
      break;
    }
  if (xopts)
    XFREE(xopts);
#endif /* HAVE_LOOP_DEVICE */

#ifdef MOUNT_TABLE_ON_FILE
  free_mntlist(mlist);
  mp = mlist = read_mtab(mntdir, mnttabname);

  /*
   * Search the mount table looking for
   * the correct (ie last) matching entry
   */
  mp_save = NULL;
  while (mp) {
    if (STREQ(mp->mnt->mnt_dir, mntdir))
      mp_save = mp;
    mp = mp->mnext;
  }

  if (mp_save) {
    mnt_free(mp_save->mnt);
    mp_save->mnt = NULL;
    rewrite_mtab(mlist, mnttabname);
  }
#endif /* MOUNT_TABLE_ON_FILE */

 out:
  free_mntlist(mlist);

  return error;
}
예제 #4
0
int
umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags)
{
  mntlist *mlist, *mp, *mp_save = NULL;
  int error = 0;

  mp = mlist = read_mtab(mntdir, mnttabname);

  /*
   * Search the mount table looking for
   * the correct (ie last) matching entry
   */
  while (mp) {
    if (STREQ(mp->mnt->mnt_dir, mntdir))
      mp_save = mp;
    mp = mp->mnext;
  }

  if (mp_save) {
    dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir);

#ifdef MOUNT_TABLE_ON_FILE
    /*
     * This unmount may hang leaving this process with an exclusive lock on
     * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount,
     * then lock mtab (again) and reread it and finally update it.
     */
    unlock_mntlist();
#endif /* MOUNT_TABLE_ON_FILE */

#ifdef NEED_AUTOFS_SPACE_HACK
    if (unmount_flags & AMU_UMOUNT_AUTOFS) {
      char *mnt_dir_save = mp_save->mnt->mnt_dir;
      mp_save->mnt->mnt_dir = autofs_strdup_space_hack(mnt_dir_save);
      error = UNMOUNT_TRAP(mp_save->mnt);
      XFREE(mp_save->mnt->mnt_dir);
      mp_save->mnt->mnt_dir = mnt_dir_save;
    } else
#endif /* NEED_AUTOFS_SPACE_HACK */
      error = UNMOUNT_TRAP(mp_save->mnt);
    if (error < 0) {
      switch ((error = errno)) {
      case EINVAL:
      case ENOTBLK:
	plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir);
	error = 0;		/* Not really an error */
	break;

      case ENOENT:
	/*
	 * This could happen if the kernel insists on following symlinks
	 * when we try to unmount a direct mountpoint. We need to propagate
	 * the error up so that the top layers know it failed and don't
	 * try to rmdir() the mountpoint or other silly things.
	 */
	plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir);
	break;

#if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UVMOUNT)
      case EBUSY:
      case EIO:
      case ESTALE:
	/* caller determines if forced unmounts should be used */
	if (unmount_flags & AMU_UMOUNT_FORCE) {
	  error = umount2_fs(mntdir, unmount_flags);
	  if (error < 0)
	    error = errno;
	  else
	    break;		/* all is OK */
	}
	/* fallthrough */
#endif /* MNT2_GEN_OPT_FORCE && HAVE_UVMOUNT */
      default:
	dlog("%s: unmount: %m", mp_save->mnt->mnt_dir);
	break;
      }
    }
    dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir);

    if (!error) {
#ifdef MOUNT_TABLE_ON_FILE
      free_mntlist(mlist);
      mp = mlist = read_mtab(mntdir, mnttabname);

      /*
       * Search the mount table looking for
       * the correct (ie last) matching entry
       */
      mp_save = NULL;
      while (mp) {
	if (STREQ(mp->mnt->mnt_dir, mntdir))
	  mp_save = mp;
	mp = mp->mnext;
      }

      if (mp_save) {
	mnt_free(mp_save->mnt);
	mp_save->mnt = NULL;
	rewrite_mtab(mlist, mnttabname);
      }
#endif /* MOUNT_TABLE_ON_FILE */
    }

  } else {

    plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir);
    /*
     * Assume it is already unmounted
     */
    error = 0;
  } /* end of "if (mp_save)" statement */

  free_mntlist(mlist);

  return error;
}