Beispiel #1
0
/*
 * If we rename a filesystem, child filesystem handles are no longer valid
 * since we identify each dataset by its name in the ZFS namespace.  As a
 * result, we have to go through and fix up all the names appropriately.  We
 * could do this automatically if libzfs kept track of all open handles, but
 * this is a lot less work.
 */
void
changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
{
	prop_changenode_t *cn;
	char newname[ZFS_MAXNAMELEN];

	for (cn = uu_list_first(clp->cl_list); cn != NULL;
	    cn = uu_list_next(clp->cl_list, cn)) {
		/*
		 * Do not rename a clone that's not in the source hierarchy.
		 */
		if (!isa_child_of(cn->cn_handle->zfs_name, src))
			continue;

		/*
		 * Destroy the previous mountpoint if needed.
		 */
		remove_mountpoint(cn->cn_handle);

		(void) strlcpy(newname, dst, sizeof (newname));
		(void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));

		(void) strlcpy(cn->cn_handle->zfs_name, newname,
		    sizeof (cn->cn_handle->zfs_name));
	}
}
Beispiel #2
0
void mount_umount_filesystems(void)
{
	struct mount_point *mp, *mp_list;
	bool changed;

	mp_list = get_mountpoints();
	changed = false;
	do {
		/* We keep umounting filesystems as long as we can. Some
		 * filesystems maybe mounted on top of others, so the 'bottom'
		 * ones will only umount when there's nothing left on top of
		 * them. After one iteration without being able to umount
		 * anything, we give up. */
		mp = mp_list;
		changed = false;
		while (mp != NULL) {
			int err;
			struct mount_point *next = mp->next;

			log_message("Umounting %s\n", mp->path);

			errno = 0;
			err = umount(mp->path);
			if (err == 0) {
				log_message("Umounted %s\n", mp->path);
				changed = true;
				remove_mountpoint(&mp_list, mp);
			} else {
				log_message("Could not umount: %s: %m\n",
					    mp->path);
			}
			mp = next;
		}
	} while (changed);

	while (mp_list != NULL) {
		remove_mountpoint(&mp_list, mp_list);
	}
}
Beispiel #3
0
/*
 * Destroy the given pool.  It is up to the caller to ensure that there are no
 * datasets left in the pool.
 */
int
zpool_destroy(zpool_handle_t *zhp)
{
	zfs_cmd_t zc = { 0 };
	zfs_handle_t *zfp = NULL;
	libzfs_handle_t *hdl = zhp->zpool_hdl;
	char msg[1024];

	if (zhp->zpool_state == POOL_STATE_ACTIVE &&
	    (zfp = zfs_open(zhp->zpool_hdl, zhp->zpool_name,
	    ZFS_TYPE_FILESYSTEM)) == NULL)
		return (-1);

	if (zpool_remove_zvol_links(zhp) != NULL)
		return (-1);

	(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));

	if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
		(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
		    "cannot destroy '%s'"), zhp->zpool_name);

		if (errno == EROFS) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "one or more devices is read only"));
			(void) zfs_error(hdl, EZFS_BADDEV, msg);
		} else {
			(void) zpool_standard_error(hdl, errno, msg);
		}

		if (zfp)
			zfs_close(zfp);
		return (-1);
	}

	if (zfp) {
		remove_mountpoint(zfp);
		zfs_close(zfp);
	}

	return (0);
}
/*
 * Unshare and unmount all datasets within the given pool.  We don't want to
 * rely on traversing the DSL to discover the filesystems within the pool,
 * because this may be expensive (if not all of them are mounted), and can fail
 * arbitrarily (on I/O error, for example).  Instead, we walk /etc/mtab and
 * gather all the filesystems that are currently mounted.
 */
int
zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
{
	int used, alloc;
	struct mnttab entry;
	size_t namelen;
	char **mountpoints = NULL;
	zfs_handle_t **datasets = NULL;
	libzfs_handle_t *hdl = zhp->zpool_hdl;
	int i;
	int ret = -1;
	int flags = (force ? MS_FORCE : 0);

	namelen = strlen(zhp->zpool_name);

	rewind(hdl->libzfs_mnttab);
	used = alloc = 0;
	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
		/*
		 * Ignore non-ZFS entries.
		 */
		if (entry.mnt_fstype == NULL ||
		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
			continue;

		/*
		 * Ignore filesystems not within this pool.
		 */
		if (entry.mnt_mountp == NULL ||
		    strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
		    (entry.mnt_special[namelen] != '/' &&
		    entry.mnt_special[namelen] != '\0'))
			continue;

		/*
		 * At this point we've found a filesystem within our pool.  Add
		 * it to our growing list.
		 */
		if (used == alloc) {
			if (alloc == 0) {
				if ((mountpoints = zfs_alloc(hdl,
				    8 * sizeof (void *))) == NULL)
					goto out;

				if ((datasets = zfs_alloc(hdl,
				    8 * sizeof (void *))) == NULL)
					goto out;

				alloc = 8;
			} else {
				void *ptr;

				if ((ptr = zfs_realloc(hdl, mountpoints,
				    alloc * sizeof (void *),
				    alloc * 2 * sizeof (void *))) == NULL)
					goto out;
				mountpoints = ptr;

				if ((ptr = zfs_realloc(hdl, datasets,
				    alloc * sizeof (void *),
				    alloc * 2 * sizeof (void *))) == NULL)
					goto out;
				datasets = ptr;

				alloc *= 2;
			}
		}

		if ((mountpoints[used] = zfs_strdup(hdl,
		    entry.mnt_mountp)) == NULL)
			goto out;

		/*
		 * This is allowed to fail, in case there is some I/O error.  It
		 * is only used to determine if we need to remove the underlying
		 * mountpoint, so failure is not fatal.
		 */
		datasets[used] = make_dataset_handle(hdl, entry.mnt_special);

		used++;
	}

	/*
	 * At this point, we have the entire list of filesystems, so sort it by
	 * mountpoint.
	 */
	qsort(mountpoints, used, sizeof (char *), mountpoint_compare);

	/*
	 * Walk through and first unshare everything.
	 */
	for (i = 0; i < used; i++) {
		zfs_share_proto_t *curr_proto;
		for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
		    curr_proto++) {
			if (is_shared(hdl, mountpoints[i], *curr_proto) &&
			    unshare_one(hdl, mountpoints[i],
			    mountpoints[i], *curr_proto) != 0)
				goto out;
		}
	}

	/*
	 * Now unmount everything, removing the underlying directories as
	 * appropriate.
	 */
	for (i = 0; i < used; i++) {
		if (unmount_one(hdl, mountpoints[i], flags) != 0)
			goto out;
	}

	for (i = 0; i < used; i++) {
		if (datasets[i])
			remove_mountpoint(datasets[i]);
	}

	ret = 0;
out:
	for (i = 0; i < used; i++) {
		if (datasets[i])
			zfs_close(datasets[i]);
		free(mountpoints[i]);
	}
	free(datasets);
	free(mountpoints);

	return (ret);
}
Beispiel #5
0
/*
 * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
 * reshare the filesystems as necessary.  In changelist_gather() we recorded
 * whether the filesystem was previously shared or mounted.  The action we take
 * depends on the previous state, and whether the value was previously 'legacy'.
 * For non-legacy properties, we only remount/reshare the filesystem if it was
 * previously mounted/shared.  Otherwise, we always remount/reshare the
 * filesystem.
 */
int
changelist_postfix(prop_changelist_t *clp)
{
	prop_changenode_t *cn;
	char shareopts[ZFS_MAXPROPLEN];
	int errors = 0;
	libzfs_handle_t *hdl;

	/*
	 * If we're changing the mountpoint, attempt to destroy the underlying
	 * mountpoint.  All other datasets will have inherited from this dataset
	 * (in which case their mountpoints exist in the filesystem in the new
	 * location), or have explicit mountpoints set (in which case they won't
	 * be in the changelist).
	 */
	if ((cn = uu_list_last(clp->cl_list)) == NULL)
		return (0);

	if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
	    !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) {
		remove_mountpoint(cn->cn_handle);
	}

	/*
	 * It is possible that the changelist_prefix() used libshare
	 * to unshare some entries. Since libshare caches data, an
	 * attempt to reshare during postfix can fail unless libshare
	 * is uninitialized here so that it will reinitialize later.
	 */
	if (cn->cn_handle != NULL) {
		hdl = cn->cn_handle->zfs_hdl;
		assert(hdl != NULL);
		zfs_uninit_libshare(hdl);
	}

	/*
	 * We walk the datasets in reverse, because we want to mount any parent
	 * datasets before mounting the children.  We walk all datasets even if
	 * there are errors.
	 */
	for (cn = uu_list_last(clp->cl_list); cn != NULL;
	    cn = uu_list_prev(clp->cl_list, cn)) {

		boolean_t sharenfs;
		boolean_t sharesmb;
		boolean_t mounted;

		/*
		 * If we are in the global zone, but this dataset is exported
		 * to a local zone, do nothing.
		 */
		if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
			continue;

		/* Only do post-processing if it's required */
		if (!cn->cn_needpost)
			continue;
		cn->cn_needpost = B_FALSE;

		zfs_refresh_properties(cn->cn_handle);

		if (ZFS_IS_VOLUME(cn->cn_handle))
			continue;

		/*
		 * Remount if previously mounted or mountpoint was legacy,
		 * or sharenfs or sharesmb  property is set.
		 */
		sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
		    shareopts, sizeof (shareopts), NULL, NULL, 0,
		    B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));

		sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
		    shareopts, sizeof (shareopts), NULL, NULL, 0,
		    B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));

		mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
		    zfs_is_mounted(cn->cn_handle, NULL);

		if (!mounted && (cn->cn_mounted ||
		    ((sharenfs || sharesmb || clp->cl_waslegacy) &&
		    (zfs_prop_get_int(cn->cn_handle,
		    ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {

			if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
				errors++;
			else
				mounted = TRUE;
		}

		/*
		 * If the file system is mounted we always re-share even
		 * if the filesystem is currently shared, so that we can
		 * adopt any new options.
		 */
		if (sharenfs && mounted)
			errors += zfs_share_nfs(cn->cn_handle);
		else if (cn->cn_shared || clp->cl_waslegacy)
			errors += zfs_unshare_nfs(cn->cn_handle, NULL);
		if (sharesmb && mounted)
			errors += zfs_share_smb(cn->cn_handle);
		else if (cn->cn_shared || clp->cl_waslegacy)
			errors += zfs_unshare_smb(cn->cn_handle, NULL);
	}

	return (errors ? -1 : 0);
}
Beispiel #6
0
/*
 * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
 * reshare the filesystems as necessary.  In changelist_gather() we recorded
 * whether the filesystem was previously shared or mounted.  The action we take
 * depends on the previous state, and whether the value was previously 'legacy'.
 * For non-legacy properties, we only remount/reshare the filesystem if it was
 * previously mounted/shared.  Otherwise, we always remount/reshare the
 * filesystem.
 */
int
changelist_postfix(prop_changelist_t *clp)
{
	prop_changenode_t *cn;
	char shareopts[ZFS_MAXPROPLEN];
	int ret = 0;
	libzfs_handle_t *hdl;

	/*
	 * If we're changing the mountpoint, attempt to destroy the underlying
	 * mountpoint.  All other datasets will have inherited from this dataset
	 * (in which case their mountpoints exist in the filesystem in the new
	 * location), or have explicit mountpoints set (in which case they won't
	 * be in the changelist).
	 */
	if ((cn = uu_list_last(clp->cl_list)) == NULL)
		return (0);

	if (clp->cl_prop == ZFS_PROP_MOUNTPOINT)
		remove_mountpoint(cn->cn_handle);

	/*
	 * It is possible that the changelist_prefix() used libshare
	 * to unshare some entries. Since libshare caches data, an
	 * attempt to reshare during postfix can fail unless libshare
	 * is uninitialized here so that it will reinitialize later.
	 */
	if (cn->cn_handle != NULL) {
		hdl = cn->cn_handle->zfs_hdl;
		assert(hdl != NULL);
#ifndef __APPLE__
		zfs_uninit_libshare(hdl);
#endif
	}

	/*
	 * We walk the datasets in reverse, because we want to mount any parent
	 * datasets before mounting the children.
	 */
	for (cn = uu_list_last(clp->cl_list); cn != NULL;
	    cn = uu_list_prev(clp->cl_list, cn)) {

		boolean_t sharenfs;

#ifndef __APPLE__
		/*
		 * If we are in the global zone, but this dataset is exported
		 * to a local zone, do nothing.
		 */
		if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
			continue;
#endif /*!__APPLE__*/

		zfs_refresh_properties(cn->cn_handle);

		if (ZFS_IS_VOLUME(cn->cn_handle)) {
			/*
			 * If we're doing a rename, recreate the /dev/zvol
			 * links.
			 */
			if (clp->cl_realprop == ZFS_PROP_NAME &&
			    zvol_create_link(cn->cn_handle->zfs_hdl,
			    cn->cn_handle->zfs_name) != 0) {
				ret = -1;
			} else if (cn->cn_shared ||
			    clp->cl_prop == ZFS_PROP_SHAREISCSI) {
				if (zfs_prop_get(cn->cn_handle,
				    ZFS_PROP_SHAREISCSI, shareopts,
				    sizeof (shareopts), NULL, NULL, 0,
				    B_FALSE) == 0 &&
				    strcmp(shareopts, "off") == 0) {
					ret = zfs_unshare_iscsi(cn->cn_handle);
				} else {
					ret = zfs_share_iscsi(cn->cn_handle);
				}
			}

			continue;
		}

		/*
		 * Remount if previously mounted or mountpoint was legacy,
		 * or sharenfs property is set.
		 */
		sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
		    shareopts, sizeof (shareopts), NULL, NULL, 0,
		    B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));

		if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs) &&
		    !zfs_is_mounted(cn->cn_handle, NULL) &&
		    zfs_mount(cn->cn_handle, NULL, 0) != 0)
			ret = -1;

		/*
		 * We always re-share even if the filesystem is currently
		 * shared, so that we can adopt any new options.
		 */
		if (cn->cn_shared || clp->cl_waslegacy || sharenfs) {
			if (sharenfs)
				ret = zfs_share_nfs(cn->cn_handle);
			else
				ret = zfs_unshare_nfs(cn->cn_handle, NULL);
		}
	}

	return (ret);
}
Beispiel #7
0
/*
 * Unshare and unmount all datasets within the given pool.  We don't want to
 * rely on traversing the DSL to discover the filesystems within the pool,
 * because this may be expensive (if not all of them are mounted), and can fail
 * arbitrarily (on I/O error, for example).  Instead, we walk /etc/mtab and
 * gather all the filesystems that are currently mounted.
 */
int
zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
{
	int used, alloc;
	struct mnttab entry;
	size_t namelen;
	char **mountpoints = NULL;
	zfs_handle_t **datasets = NULL;
	libzfs_handle_t *hdl = zhp->zpool_hdl;
	int i;
	int ret = -1;
	int flags = (force ? MS_FORCE : 0);

	namelen = strlen(zhp->zpool_name);

	/* Reopen MNTTAB to prevent reading stale data from open file */
	if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
		return (ENOENT);

	used = alloc = 0;
	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
		/*
		 * Ignore filesystems not within this pool.
		 */
		if (entry.mnt_fstype == NULL ||
		    strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
		    (entry.mnt_special[namelen] != '/' &&
#ifdef __APPLE__
		    /*
		     * On OS X, '@' is possible too since we're temporarily
		     * allowing manual snapshot mounting.
		     */
		    entry.mnt_special[namelen] != '@' &&
#endif /* __APPLE__ */
		    entry.mnt_special[namelen] != '\0'))
			continue;

		/*
		 * At this point we've found a filesystem within our pool.  Add
		 * it to our growing list.
		 */
		if (used == alloc) {
			if (alloc == 0) {
				if ((mountpoints = zfs_alloc(hdl,
				    8 * sizeof (void *))) == NULL)
					goto out;

				if ((datasets = zfs_alloc(hdl,
				    8 * sizeof (void *))) == NULL)
					goto out;

				alloc = 8;
			} else {
				void *ptr;

				if ((ptr = zfs_realloc(hdl, mountpoints,
				    alloc * sizeof (void *),
				    alloc * 2 * sizeof (void *))) == NULL)
					goto out;
				mountpoints = ptr;

				if ((ptr = zfs_realloc(hdl, datasets,
				    alloc * sizeof (void *),
				    alloc * 2 * sizeof (void *))) == NULL)
					goto out;
				datasets = ptr;

				alloc *= 2;
			}
		}

               if ((mountpoints[used] = zfs_strdup(hdl,
                    entry.mnt_mountp)) == NULL)
                        goto out;

		/*
		 * This is allowed to fail, in case there is some I/O error.  It
		 * is only used to determine if we need to remove the underlying
		 * mountpoint, so failure is not fatal.
		 */
		datasets[used] = make_dataset_handle(hdl, entry.mnt_special);

		used++;
	}

	/*
	 * At this point, we have the entire list of filesystems, so sort it by
	 * mountpoint.
	 */
	qsort(mountpoints, used, sizeof (char *), mountpoint_compare);

	/*
	 * Walk through and first unshare everything.
	 */
	for (i = 0; i < used; i++) {
		zfs_share_proto_t *curr_proto;
		for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
		    curr_proto++) {
			if (is_shared(hdl, mountpoints[i], *curr_proto) &&
			    unshare_one(hdl, mountpoints[i],
			    mountpoints[i], *curr_proto) != 0)
				goto out;
		}
	}

	/*
	 * Now unmount everything, removing the underlying directories as
	 * appropriate.
	 */
	for (i = 0; i < used; i++) {
		if (unmount_one(hdl, mountpoints[i], flags) != 0)
			goto out;
	}

	for (i = 0; i < used; i++) {
		if (datasets[i])
			remove_mountpoint(datasets[i]);
	}

    // Surely there exists a better way to iterate a POOL to find its ZVOLs?
    zfs_iter_root(hdl, zpool_disable_volumes, (void *) zpool_get_name(zhp));

	ret = 0;
out:
	for (i = 0; i < used; i++) {
		if (datasets[i])
			zfs_close(datasets[i]);
		free(mountpoints[i]);
	}
	free(datasets);
	free(mountpoints);

	return (ret);
}