Example #1
0
/*
 * Async unmount callback function.
 * After the base umount() succeeds, we may want to take extra actions,
 * such as informing remote mount daemons that we've unmounted them.
 * See amfs_auto_umounted(), host_umounted(), nfs_umounted().
 */
static void
amfs_nfsl_umounted(am_node *mp)
{
  mntfs *mf = mp->am_mnt;

  /*
   * If a link, do nothing (same as type:=link)
   * If non-link, do nfs_fumount (same as type:=nfs).
   */
  if (mf->mf_flags & MFF_NFSLINK) {
    return;
  } else {
    nfs_umounted(mp);
    /*
     * MUST remove mount point directories, because if they remain
     * behind, the next nfsl access will think they are a link
     * type file system, and not NFS! (when it performs link target
     * existence test)
     */
    if (mf->mf_flags & MFF_MKMNT)
      rmdirs(mf->mf_mount);
    return;
  }
}
Example #2
0
/*
 * Unmount a mount tree
 */
static int
amfs_host_umount(am_node *am, mntfs *mf)
{
  mntlist *ml, *mprev;
  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
  int xerror = 0;

  /*
   * Read the mount list
   */
  mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);

#ifdef MOUNT_TABLE_ON_FILE
  /*
   * Unlock the mount list
   */
  unlock_mntlist();
#endif /* MOUNT_TABLE_ON_FILE */

  /*
   * Reverse list...
   */
  ml = mlist;
  mprev = NULL;
  while (ml) {
    mntlist *ml2 = ml->mnext;
    ml->mnext = mprev;
    mprev = ml;
    ml = ml2;
  }
  mlist = mprev;

  /*
   * Unmount all filesystems...
   */
  for (ml = mlist; ml && !xerror; ml = ml->mnext) {
    char *dir = ml->mnt->mnt_dir;
    if (directory_prefix(mf->mf_mount, dir)) {
      int error;
      dlog("amfs_host: unmounts %s", dir);
      /*
       * Unmount "dir"
       */
      error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
      /*
       * Keep track of errors
       */
      if (error) {
	/*
	 * If we have not already set xerror and error is not ENOENT,
	 * then set xerror equal to error and log it.
	 * 'xerror' is the return value for this function.
	 *
	 * We do not want to pass ENOENT as an error because if the
	 * directory does not exists our work is done anyway.
	 */
	if (!xerror && error != ENOENT)
	  xerror = error;
	if (error != EBUSY) {
	  errno = error;
	  plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
	}
      } else {
	(void) rmdirs(dir);
      }
    }
  }

  /*
   * Throw away mount list
   */
  discard_mntlist(mlist);

  /*
   * Try to remount, except when we are shutting down.
   */
  if (xerror && amd_state != Finishing) {
    xerror = amfs_host_mount(am, mf);
    if (!xerror) {
      /*
       * Don't log this - it's usually too verbose
       plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
       */
      xerror = EBUSY;
    }
  }
  return xerror;
}
Example #3
0
/*
 * Pick a file system to try mounting and
 * do that in the background if necessary
 *
For each location:
	discard previous mount location if required
	fetch next mount location
	if the filesystem failed to be mounted then
		this_error = error from filesystem
		goto failed
	if the filesystem is mounting or unmounting then
		goto retry;
	if the fileserver is down then
		this_error = EIO
		continue;
	if the filesystem is already mounted
		break
	fi

	this_error = initialize mount point

	if no error on this mount and mount is delayed then
		this_error = -1
	fi
	if this_error < 0 then
		retry = true
	fi
	if no error on this mount then
		if mount in background then
			run mount in background
			return -1
		else
			this_error = mount in foreground
		fi
	fi
	if an error occurred on this mount then
		update stats
		save error in mount point
	fi
endfor
 */
static int
amfs_bgmount(struct continuation *cp)
{
  am_node *mp = cp->mp;
  am_loc *loc;
  mntfs *mf;
  int this_error = -1;		/* Per-mount error */
  int hard_error = -1;		/* Cumulative per-node error */

  if (mp->am_al)
    free_loc(mp->am_al);

  /*
   * Try to mount each location.
   * At the end:
   * hard_error == 0 indicates something was mounted.
   * hard_error > 0 indicates everything failed with a hard error
   * hard_error < 0 indicates nothing could be mounted now
   */
  for (mp->am_al = *cp->al; *cp->al; cp->al++, mp->am_al = *cp->al) {
    am_ops *p;

    loc = dup_loc(mp->am_al);
    mf = loc->al_mnt;
    p = mf->mf_ops;

    if (hard_error < 0)
      hard_error = this_error;
    this_error = 0;

    if (mf->mf_error > 0) {
      this_error = mf->mf_error;
      goto failed;
    }

    if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
      /*
       * Still mounting - retry later
       */
      dlog("mount of \"%s\" already pending", mf->mf_info);
      goto retry;
    }

    if (FSRV_ISDOWN(mf->mf_server)) {
      /*
       * Would just mount from the same place
       * as a hung mount - so give up
       */
      dlog("%s is already hung - giving up", mf->mf_server->fs_host);
      this_error = EIO;
      goto failed;
    }

    XFREE(mp->am_link);
    mp->am_link = NULL;

    if (loc->al_fo && loc->al_fo->opt_sublink && loc->al_fo->opt_sublink[0])
      mp->am_link = xstrdup(loc->al_fo->opt_sublink);

    /*
     * Will usually need to play around with the mount nodes
     * file attribute structure.  This must be done here.
     * Try and get things initialized, even if the fileserver
     * is not known to be up.  In the common case this will
     * progress things faster.
     */

    /*
     * Fill in attribute fields.
     */
    if (mf->mf_fsflags & FS_DIRECTORY)
      mk_fattr(&mp->am_fattr, NFDIR);
    else
      mk_fattr(&mp->am_fattr, NFLNK);

    if (mf->mf_flags & MFF_MOUNTED) {
      dlog("duplicate mount of \"%s\" ...", mf->mf_info);
      /*
       * Skip initial processing of the mountpoint if already mounted.
       * This could happen if we have multiple sublinks into the same f/s,
       * or if we are restarting an already-mounted filesystem.
       */
      goto already_mounted;
    }

    if (mf->mf_fo && mf->mf_fo->fs_mtab) {
      plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
	   mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
	   mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
    }

    if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
      this_error = p->fs_init(mf);

    if (this_error > 0)
      goto failed;
    if (this_error < 0)
      goto retry;

    if (loc->al_fo && loc->al_fo->opt_delay) {
      /*
       * If there is a delay timer on the location
       * then don't try to mount if the timer
       * has not expired.
       */
      int i = atoi(loc->al_fo->opt_delay);
      time_t now = clocktime(NULL);
      if (i > 0 && now < (cp->start + i)) {
	dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
	goto retry;
      }
    }

    /*
     * If the directory is not yet made and it needs to be made, then make it!
     */
    if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
      plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
      this_error = mkdirs(mf->mf_mount, 0555);
      if (this_error) {
	plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
	goto failed;
      }
      mf->mf_flags |= MFF_MKMNT;
    }

#ifdef HAVE_FS_AUTOFS
    if (mf->mf_flags & MFF_IS_AUTOFS)
      if ((this_error = autofs_get_fh(mp)))
	goto failed;
#endif /* HAVE_FS_AUTOFS */

  already_mounted:
    mf->mf_flags |= MFF_MOUNTING;
    if (mf->mf_fsflags & FS_MBACKGROUND) {
      dlog("backgrounding mount of \"%s\"", mf->mf_mount);
      if (cp->callout) {
	untimeout(cp->callout);
	cp->callout = 0;
      }

      /* actually run the task, backgrounding as necessary */
      run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
      return -1;
    } else {
      dlog("foreground mount of \"%s\" ...", mf->mf_mount);
      this_error = mount_node((opaque_t) mp);
    }

    mf->mf_flags &= ~MFF_MOUNTING;
    if (this_error > 0)
      goto failed;
    if (this_error == 0) {
      am_mounted(mp);
      break;					/* Success */
    }

  retry:
    if (!cp->retry)
      continue;
    dlog("will retry ...\n");

    /*
     * Arrange that amfs_bgmount is called
     * after anything else happens.
     */
    dlog("Arranging to retry mount of %s", mp->am_path);
    sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
    if (cp->callout)
      untimeout(cp->callout);
    cp->callout = timeout(RETRY_INTERVAL, wakeup,
			  (opaque_t) get_mntfs_wchan(mf));

    mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;

    /*
     * Not done yet - so don't return anything
     */
    return -1;

  failed:
    if (!FSRV_ISDOWN(mf->mf_server)) {
      /* mark the mount as failed unless the server is down */
      amd_stats.d_merr++;
      mf->mf_error = this_error;
      mf->mf_flags |= MFF_ERROR;
#ifdef HAVE_FS_AUTOFS
      if (mp->am_autofs_fh)
	autofs_release_fh(mp);
#endif /* HAVE_FS_AUTOFS */
      if (mf->mf_flags & MFF_MKMNT) {
	rmdirs(mf->mf_mount);
	mf->mf_flags &= ~MFF_MKMNT;
      }
    }
    /*
     * Wakeup anything waiting for this mount
     */
    wakeup(get_mntfs_wchan(mf));
    free_loc(loc);
    /* continue */
  }

  /*
   * If we get here, then either the mount succeeded or
   * there is no more mount information available.
   */
  if (this_error) {
    if (mp->am_al)
      free_loc(mp->am_al);
    mp->am_al = loc = new_loc();
    mf = loc->al_mnt;

#ifdef HAVE_FS_AUTOFS
    if (mp->am_flags & AMF_AUTOFS)
      autofs_mount_failed(mp);
    else
#endif /* HAVE_FS_AUTOFS */
      nfs_quick_reply(mp, this_error);

    if (hard_error <= 0)
      hard_error = this_error;
    if (hard_error < 0)
      hard_error = ETIMEDOUT;

    /*
     * Set a small(ish) timeout on an error node if
     * the error was not a time out.
     */
    switch (hard_error) {
    case ETIMEDOUT:
    case EWOULDBLOCK:
    case EIO:
      mp->am_timeo = 17;
      break;
    default:
      mp->am_timeo = 5;
      break;
    }
    new_ttl(mp);
  } else {
    mf = loc->al_mnt;
    /*
     * Wakeup anything waiting for this mount
     */
    wakeup(get_mntfs_wchan(mf));
    hard_error = 0;
  }

  /*
   * Make sure that the error value in the mntfs has a
   * reasonable value.
   */
  if (mf->mf_error < 0) {
    mf->mf_error = hard_error;
    if (hard_error)
      mf->mf_flags |= MFF_ERROR;
  }

  /*
   * In any case we don't need the continuation any more
   */
  free_continuation(cp);

  return hard_error;
}
Example #4
0
void
am_unmounted(am_node *mp)
{
  mntfs *mf = mp->am_mnt;

  if (!foreground)		/* firewall - should never happen */
    return;

  /*
   * Do unmounted callback
   */
  if (mf->mf_ops->umounted)
    mf->mf_ops->umounted(mf);

  /*
   * This is ugly, but essentially unavoidable.
   * Sublinks must be treated separately as type==link
   * when the base type is different.
   */
  if (mp->am_link && mp->am_mnt->mf_ops != &amfs_link_ops)
    amfs_link_ops.umount_fs(mp, mp->am_mnt);

#ifdef HAVE_FS_AUTOFS
  if (mp->am_flags & AMF_AUTOFS)
    autofs_umount_succeeded(mp);
#endif /* HAVE_FS_AUTOFS */

  /*
   * Clean up any directories that were made
   *
   * If we remove the mount point of a pending mount, any queued access
   * to it will fail. So don't do it in that case.
   */
  if (mf->mf_flags & MFF_MKMNT &&
      !(mp->am_flags & AMF_REMOUNT)) {
    plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_real_mount);
    rmdirs(mf->mf_real_mount);
  }

  /*
   * If this is a pseudo-directory then adjust the link count
   * in the parent
   */
  if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
    --mp->am_parent->am_fattr.na_nlink;

  /*
   * Update mtime of parent node
   */
  if (mp->am_parent && mp->am_parent->am_mnt)
    mp->am_parent->am_fattr.na_mtime.nt_seconds = clocktime();

  if (mp->am_flags & AMF_REMOUNT) {
    char *fname = strdup(mp->am_name);
    am_node *mp_parent = mp->am_parent;
    int error = 0;

    free_map(mp);
    plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
    mp = amfs_auto_lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
    if (mp && error < 0)
      mp = amfs_auto_mount_child(mp, &error);
    if (error > 0) {
      errno = error;
      plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
    }
    XFREE(fname);
  } else
    free_map(mp);
}
Example #5
0
/*
 * Unmount an NFS hierarchy.
 * Note that this is called in the foreground
 * and so may hang under extremely rare conditions.
 */
static int
nfsx_fumount(mntfs *mf)
{
	struct nfsx *nx = (struct nfsx *) mf->mf_private;
	nfsx_mnt *n;
	int glob_error = 0;

	/*
	 * Iterate in reverse through the mntfs's and unmount each filesystem
	 * which is mounted.
	 */
	for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
		mntfs *m = n->n_mnt;
		/*
		 * If this node has not been messed with
		 * and there has been no error so far
		 * then try and unmount.
		 * If an error had occured then zero
		 * the error code so that the remount
		 * only tries to unmount those nodes
		 * which had been successfully unmounted.
		 */
		if (n->n_error == 0) {
#ifdef DEBUG
			dlog("calling underlying fumount on %s", m->mf_mount);
#endif
			n->n_error = (*m->mf_ops->fumount_fs)(m);
			if (n->n_error) {
				glob_error = n->n_error;
				n->n_error = 0;
			} else {
				/*
				 * Make sure remount gets this node
				 */
				n->n_error = -1;
			}
		}
	}

	/*
	 * If any unmounts failed then remount the
	 * whole lot...
	 */
	if (glob_error) {
		glob_error = nfsx_remount(mf, TRUE);
		if (glob_error) {
			errno = glob_error; /* XXX */
			plog(XLOG_USER, "nfsx: remount of %s failed: %m", mf->mf_mount);
		}
		glob_error = EBUSY;
	} else {
		/*
		 * Remove all the mount points
		 */
		for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
			mntfs *m = n->n_mnt;
			if (n->n_error < 0) {
				if (m->mf_ops->fs_flags & FS_MKMNT) {
					(void) rmdirs(m->mf_mount);
					m->mf_flags &= ~MFF_MKMNT;
				}
			}
			free_mntfs(m);
			n->n_mnt = 0;
			n->n_error = -1;
		}
	}

	return glob_error;
}
Example #6
0
File: autil.c Project: 0mp/freebsd
void
am_unmounted(am_node *mp)
{
  mntfs *mf = mp->am_al->al_mnt;

  if (!foreground) {		/* firewall - should never happen */
    /*
     * This is a coding error.  Make sure we hear about it!
     */
    plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)",
	mp->am_name);
    notify_child(mp, AMQ_UMNT_OK, 0, 0);	/* XXX - be safe? */
    return;
  }

  /*
   * Do unmounted callback
   */
  if (mf->mf_ops->umounted)
    mf->mf_ops->umounted(mf);

  /*
   * This is ugly, but essentially unavoidable.
   * Sublinks must be treated separately as type==link
   * when the base type is different.
   */
  if (mp->am_link && mf->mf_ops != &amfs_link_ops)
    amfs_link_ops.umount_fs(mp, mf);

#ifdef HAVE_FS_AUTOFS
  if (mf->mf_flags & MFF_IS_AUTOFS)
    autofs_release_fh(mp);
  if (mp->am_flags & AMF_AUTOFS)
    autofs_umount_succeeded(mp);
#endif /* HAVE_FS_AUTOFS */

  /*
   * Clean up any directories that were made
   *
   * If we remove the mount point of a pending mount, any queued access
   * to it will fail. So don't do it in that case.
   * Also don't do it if the refcount is > 1.
   */
  if (mf->mf_flags & MFF_MKMNT &&
      mf->mf_refc == 1 &&
      !(mp->am_flags & AMF_REMOUNT)) {
    plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
    rmdirs(mf->mf_mount);
    mf->mf_flags &= ~MFF_MKMNT;
  }

  /*
   * If this is a pseudo-directory then adjust the link count
   * in the parent
   */
  if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
    --mp->am_parent->am_fattr.na_nlink;

  /*
   * Update mtime of parent node
   */
  if (mp->am_parent && mp->am_parent->am_al->al_mnt)
    clocktime(&mp->am_parent->am_fattr.na_mtime);

  if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
    char *fname = xstrdup(mp->am_name);
    am_node *mp_parent = mp->am_parent;
    mntfs *mf_parent = mp_parent->am_al->al_mnt;
    am_node fake_mp;
    int error = 0;

    /*
     * We need to use notify_child() after free_map(), so save enough
     * to do that in fake_mp.
     */
    fake_mp.am_fd[1] = mp->am_fd[1];
    mp->am_fd[1] = -1;

    free_map(mp);
    plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
    mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
    if (mp && error < 0)
      (void)mf_parent->mf_ops->mount_child(mp, &error);
    if (error > 0) {
      errno = error;
      plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
      notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0);
    } else {
      notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0);
    }
    XFREE(fname);
  } else {
    /*
     * We have a race here.
     * If this node has a pending mount and amd is going down (unmounting
     * everything in the process), then we could potentially free it here
     * while a struct continuation still has a reference to it. So when
     * amfs_cont is called, it blows up.
     * We avoid the race by refusing to free any nodes that have
     * pending mounts (defined as having a non-NULL am_alarray).
     */
    notify_child(mp, AMQ_UMNT_OK, 0, 0);	/* do this regardless */
    if (!mp->am_alarray)
      free_map(mp);
  }
}