Example #1
0
/*
 * A ZFS file system iterator call-back function which returns the
 * zfs_handle_t for a ZFS file system on the specified mount point.
 */
static int
match_mountpoint(zfs_handle_t *zhp, void *data)
{
	int			res;
	zfs_mount_data_t	*cbp;
	char			mp[ZFS_MAXPROPLEN];

	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
		zfs_close(zhp);
		return (0);
	}

	/* First check if the dataset is mounted. */
	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
	    0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
		zfs_close(zhp);
		return (0);
	}

	/* Now check mount point. */
	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
	    0, B_FALSE) != 0) {
		zfs_close(zhp);
		return (0);
	}

	cbp = (zfs_mount_data_t *)data;

	if (strcmp(mp, "legacy") == 0) {
		/* If legacy, must look in mnttab for mountpoint. */
		FILE		*fp;
		struct mnttab	entry;
		const char	*nm;

		nm = zfs_get_name(zhp);
		if ((fp = fopen(MNTTAB, "r")) == NULL) {
			zfs_close(zhp);
			return (0);
		}

		while (getmntent(fp, &entry) == 0) {
			if (strcmp(nm, entry.mnt_special) == 0) {
				if (strcmp(entry.mnt_mountp, cbp->match_name)
				    == 0) {
					(void) fclose(fp);
					cbp->match_handle = zhp;
					return (1);
				}
				break;
			}
		}
		(void) fclose(fp);

	} else if (strcmp(mp, cbp->match_name) == 0) {
		cbp->match_handle = zhp;
		return (1);
	}

	/* Iterate over any nested datasets. */
	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
	zfs_close(zhp);
	return (res);
}
Example #2
0
/*
 * Called for each dataset.  If the object is of an appropriate type,
 * add it to the avl tree and recurse over any children as necessary.
 */
static int
zfs_callback(zfs_handle_t *zhp, void *data)
{
	callback_data_t *cb = data;
	boolean_t dontclose = B_FALSE;
	boolean_t include_snaps = zfs_include_snapshots(zhp, cb);
	boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);

	if ((zfs_get_type(zhp) & cb->cb_types) ||
	    ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
		uu_avl_index_t idx;
		zfs_node_t *node = safe_malloc(sizeof (zfs_node_t));

		node->zn_handle = zhp;
		uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
		if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
		    &idx) == NULL) {
			if (cb->cb_proplist) {
				if ((*cb->cb_proplist) &&
				    !(*cb->cb_proplist)->pl_all)
					zfs_prune_proplist(zhp,
					    cb->cb_props_table);

				if (zfs_expand_proplist(zhp, cb->cb_proplist,
				    (cb->cb_flags & ZFS_ITER_RECVD_PROPS),
				    (cb->cb_flags & ZFS_ITER_LITERAL_PROPS))
				    != 0) {
					free(node);
					return (-1);
				}
			}
			uu_avl_insert(cb->cb_avl, node, idx);
			dontclose = B_TRUE;
		} else {
			free(node);
		}
	}

	/*
	 * Recurse if necessary.
	 */
	if (cb->cb_flags & ZFS_ITER_RECURSE &&
	    ((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
	    cb->cb_depth < cb->cb_depth_limit)) {
		cb->cb_depth++;
		if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
			(void) zfs_iter_filesystems(zhp, zfs_callback, data);
		if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
		    ZFS_TYPE_BOOKMARK)) == 0) && include_snaps)
			(void) zfs_iter_snapshots(zhp,
			    (cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
			    data);
		if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
		    ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks)
			(void) zfs_iter_bookmarks(zhp, zfs_callback, data);
		cb->cb_depth--;
	}

	if (!dontclose)
		zfs_close(zhp);

	return (0);
}
Example #3
0
int
zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
{
	mount_cbdata_t cb = { 0 };
	libzfs_handle_t *hdl = zhp->zpool_hdl;
	zfs_handle_t *zfsp;
	int i, ret = -1;
	int *good;

	/*
	 * Gather all non-snap datasets within the pool.
	 */
	if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
		return (-1);
	cb.cb_alloc = 4;

	if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
		goto out;

	cb.cb_datasets[0] = zfsp;
	cb.cb_used = 1;

	if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0)
		goto out;

	/*
	 * Sort the datasets by mountpoint.
	 */
	qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp);

	/*
	 * And mount all the datasets, keeping track of which ones
	 * succeeded or failed. By using zfs_alloc(), the good pointer
	 * will always be non-NULL.
	 */
	good = zfs_alloc(zhp->zpool_hdl, cb.cb_used * sizeof (int));
	ret = 0;
	for (i = 0; i < cb.cb_used; i++) {
		if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0)
			ret = -1;
		else
			good[i] = 1;
	}

	/*
	 * Then share all the ones that need to be shared. This needs
	 * to be a separate pass in order to avoid excessive reloading
	 * of the configuration. Good should never be NULL since
	 * zfs_alloc is supposed to exit if memory isn't available.
	 */
	for (i = 0; i < cb.cb_used; i++) {
		if (good[i] && zfs_share(cb.cb_datasets[i]) != 0)
			ret = -1;
	}

	free(good);

out:
	for (i = 0; i < cb.cb_used; i++)
		zfs_close(cb.cb_datasets[i]);
	free(cb.cb_datasets);

	return (ret);
}
Example #4
0
/*
 * Function:	be_get_list_callback
 * Description:	Callback function used by zfs_iter to look through all
 *		the pools on the system looking for BEs. If a BE name was
 *		specified only that BE's information will be collected and
 *		returned.
 * Parameters:
 *		zlp - handle to the first zfs dataset. (provided by the
 *		      zfs_iter_* call)
 *		data - pointer to the callback data and where we'll pass
 *		       the BE information back.
 * Returns:
 *		0 - Success
 *		be_errno_t - Failure
 * Scope:
 *		Private
 */
static int
be_get_list_callback(zpool_handle_t *zlp, void *data)
{
	list_callback_data_t *cb = (list_callback_data_t *)data;
	char be_ds[MAXPATHLEN];
	char *open_ds = NULL;
	char *rpool = NULL;
	zfs_handle_t *zhp = NULL;
	int ret = 0;

	cb->zpool_name = rpool =  (char *)zpool_get_name(zlp);

	/*
	 * Generate string for the BE container dataset
	 */
	be_make_container_ds(rpool, be_container_ds,
	    sizeof (be_container_ds));

	/*
	 * If a BE name was specified we use it's root dataset in place of
	 * the container dataset. This is because we only want to collect
	 * the information for the specified BE.
	 */
	if (cb->be_name != NULL) {
		if (!be_valid_be_name(cb->be_name))
			return (BE_ERR_INVAL);
		/*
		 * Generate string for the BE root dataset
		 */
		be_make_root_ds(rpool, cb->be_name, be_ds, sizeof (be_ds));
		open_ds = be_ds;
	} else {
		open_ds = be_container_ds;
	}

	/*
	 * Check if the dataset exists
	 */
	if (!zfs_dataset_exists(g_zfs, open_ds,
	    ZFS_TYPE_FILESYSTEM)) {
		/*
		 * The specified dataset does not exist in this pool or
		 * there are no valid BE's in this pool. Try the next zpool.
		 */
		zpool_close(zlp);
		return (0);
	}

	if ((zhp = zfs_open(g_zfs, open_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
		be_print_err(gettext("be_get_list_callback: failed to open "
		    "the BE dataset %s: %s\n"), open_ds,
		    libzfs_error_description(g_zfs));
		ret = zfs_err_to_be_err(g_zfs);
		zpool_close(zlp);
		return (ret);
	}

	/*
	 * If a BE name was specified we iterate through the datasets
	 * and snapshots for this BE only. Otherwise we will iterate
	 * through the next level of datasets to find all the BE's
	 * within the pool
	 */
	if (cb->be_name != NULL) {
		if (cb->be_nodes_head == NULL) {
			if ((cb->be_nodes_head = be_list_alloc(&ret,
			    sizeof (be_node_list_t))) == NULL) {
				ZFS_CLOSE(zhp);
				zpool_close(zlp);
				return (ret);
			}
			cb->be_nodes = cb->be_nodes_head;
		}

		if ((ret = be_get_node_data(zhp, cb->be_nodes, cb->be_name,
		    rpool, cb->current_be, be_ds)) != BE_SUCCESS) {
			ZFS_CLOSE(zhp);
			zpool_close(zlp);
			return (ret);
		}
		ret = zfs_iter_snapshots(zhp, be_add_children_callback, cb);
	}

	if (ret == 0)
		ret = zfs_iter_filesystems(zhp, be_add_children_callback, cb);
	ZFS_CLOSE(zhp);

	zpool_close(zlp);
	return (ret);
}
Example #5
0
/*
 * Function:	be_promote_ds_callback
 * Description:	This function is used to promote the datasets for the BE
 *		being activated as well as the datasets for the zones BE
 *		being activated.
 *
 * Parameters:
 *              zhp - the zfs handle for zone BE being activated.
 *		data - not used.
 * Return:
 *		0 - Success
 *		be_errno_t - Failure
 *
 * Scope:
 *		Private
 */
static int
/* LINTED */
be_promote_ds_callback(zfs_handle_t *zhp, void *data)
{
	char	origin[MAXPATHLEN];
	char	*sub_dataset = NULL;
	int	ret = 0;

	if (zhp != NULL) {
		sub_dataset = strdup(zfs_get_name(zhp));
		if (sub_dataset == NULL) {
			ret = BE_ERR_NOMEM;
			goto done;
		}
	} else {
		be_print_err(gettext("be_promote_ds_callback: "
		    "Invalid zfs handle passed into function\n"));
		ret = BE_ERR_INVAL;
		goto done;
	}

	/*
	 * This loop makes sure that we promote the dataset to the
	 * top of the tree so that it is no longer a decendent of any
	 * dataset. The ZFS close and then open is used to make sure that
	 * the promotion is updated before we move on.
	 */
	while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
	    sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
		if (zfs_promote(zhp) != 0) {
			if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
				be_print_err(gettext("be_promote_ds_callback: "
				    "promote of %s failed: %s\n"),
				    zfs_get_name(zhp),
				    libzfs_error_description(g_zfs));
				ret = zfs_err_to_be_err(g_zfs);
				goto done;
			} else {
				/*
				 * If the call to zfs_promote returns the
				 * error EZFS_EXISTS we've hit a snapshot name
				 * collision. This means we're probably
				 * attemping to promote a zone dataset above a
				 * parent dataset that belongs to another zone
				 * which this zone was cloned from.
				 *
				 * TODO: If this is a zone dataset at some
				 * point we should skip this if the zone
				 * paths for the dataset and the snapshot
				 * don't match.
				 */
				be_print_err(gettext("be_promote_ds_callback: "
				    "promote of %s failed due to snapshot "
				    "name collision: %s\n"), zfs_get_name(zhp),
				    libzfs_error_description(g_zfs));
				ret = zfs_err_to_be_err(g_zfs);
				goto done;
			}
		}
		ZFS_CLOSE(zhp);
		if ((zhp = zfs_open(g_zfs, sub_dataset,
		    ZFS_TYPE_FILESYSTEM)) == NULL) {
			be_print_err(gettext("be_promote_ds_callback: "
			    "Failed to open dataset (%s): %s\n"), sub_dataset,
			    libzfs_error_description(g_zfs));
			ret = zfs_err_to_be_err(g_zfs);
			goto done;
		}
	}

	/* Iterate down this dataset's children and promote them */
	ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);

done:
	free(sub_dataset);
	ZFS_CLOSE(zhp);
	return (ret);
}
Example #6
0
/*
 * Function:	be_add_children_callback
 * Description:	Callback function used by zfs_iter to look through all
 *		the datasets and snapshots for each BE and add them to
 *		the lists of information to be passed back.
 * Parameters:
 *		zhp - handle to the first zfs dataset. (provided by the
 *		      zfs_iter_* call)
 *		data - pointer to the callback data and where we'll pass
 *		       the BE information back.
 * Returns:
 *		0 - Success
 *		be_errno_t - Failure
 * Scope:
 *		Private
 */
static int
be_add_children_callback(zfs_handle_t *zhp, void *data)
{
	list_callback_data_t	*cb = (list_callback_data_t *)data;
	char			*str = NULL, *ds_path = NULL;
	int			ret = 0;

	ds_path = str = strdup(zfs_get_name(zhp));

	/*
	 * get past the end of the container dataset plus the trailing "/"
	 */
	str = str + (strlen(be_container_ds) + 1);
	if (cb->be_defaults.be_deflt_rpool_container) {
		/* just skip if invalid */
		if (!be_valid_be_name(str))
			return (BE_SUCCESS);
	}

	if (cb->be_nodes_head == NULL &&
	    (ret = be_allocate_callback_nodes(cb)) != BE_SUCCESS) {
		ZFS_CLOSE(zhp);
		return (ret);
	}

	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && !zone_be) {
		be_snapshot_list_t *snapshot;

		if ((snapshot = be_list_alloc(&ret,
		    sizeof (be_snapshot_list_t))) == NULL ||
		    ret != BE_SUCCESS) {
			ZFS_CLOSE(zhp);
			return (ret);
		}

		if ((ret = be_get_ss_data(zhp, str, snapshot,
		    cb->be_nodes)) != BE_SUCCESS) {
			free(snapshot);
			ZFS_CLOSE(zhp);
			return (ret);
		}

		snapshot->be_next_snapshot = NULL;
		*cb->be_snapshots_tail = snapshot;
		cb->be_snapshots_tail = &snapshot->be_next_snapshot;
	} else if (strchr(str, '/') == NULL) {
		if (cb->be_nodes->be_node_name != NULL) {
			if ((cb->be_nodes->be_next_node =
			    be_list_alloc(&ret, sizeof (be_node_list_t))) ==
			    NULL || ret != BE_SUCCESS) {
				ZFS_CLOSE(zhp);
				return (ret);
			}
			cb->be_nodes = cb->be_nodes->be_next_node;
			cb->be_nodes->be_next_node = NULL;
		}

		/*
		 * If this is a zone root dataset then we only need
		 * the name of the zone BE at this point. We grab that
		 * and return.
		 */
		if (zone_be) {
			ret = be_get_zone_node_data(cb->be_nodes, str);
			ZFS_CLOSE(zhp);
			return (ret);
		}

		if ((ret = be_get_node_data(zhp, cb->be_nodes, str,
		    cb->zpool_name, cb->current_be, ds_path)) != BE_SUCCESS) {
			ZFS_CLOSE(zhp);
			return (ret);
		}
	} else if (strchr(str, '/') != NULL && !zone_be) {
		be_dataset_list_t *dataset;

		if ((dataset = be_list_alloc(&ret,
		    sizeof (be_dataset_list_t))) == NULL ||
		    ret != BE_SUCCESS) {
			ZFS_CLOSE(zhp);
			return (ret);
		}

		if ((ret = be_get_ds_data(zhp, str,
		    dataset, cb->be_nodes)) != BE_SUCCESS) {
			free(dataset);
			ZFS_CLOSE(zhp);
			return (ret);
		}

		dataset->be_next_dataset = NULL;
		*cb->be_datasets_tail = dataset;
		cb->be_datasets_tail = &dataset->be_next_dataset;
	}
	if (cb->flags & BE_LIST_SNAPSHOTS)
		ret = zfs_iter_children(zhp, be_add_children_callback, cb);
	else
		ret = zfs_iter_filesystems(zhp, be_add_children_callback, cb);
	if (ret != 0) {
		be_print_err(gettext("be_add_children_callback: "
		    "encountered error: %s\n"),
		    libzfs_error_description(g_zfs));
		ret = zfs_err_to_be_err(g_zfs);
	}
	ZFS_CLOSE(zhp);
	return (ret);
}
Example #7
0
int
zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
{
	get_all_cb_t cb = { 0 };
	libzfs_handle_t *hdl = zhp->zpool_hdl;
	zfs_handle_t *zfsp;
	int i, ret = -1;
	int *good;

	/*
	 * Gather all non-snap datasets within the pool.
	 */
	if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
		goto out;

	libzfs_add_handle(&cb, zfsp);

    /*
     * If the top level dataset is encrypted load its keys.
     */
    if (zfs_prop_get_int(zfsp, ZFS_PROP_KEYSTATUS) ==
        ZFS_CRYPT_KEY_UNAVAILABLE) {
        (void) zfs_key_load(zfsp, B_FALSE, B_FALSE, B_TRUE);
    }

	if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0)
		goto out;
	/*
	 * Sort the datasets by mountpoint.
	 */
	qsort(cb.cb_handles, cb.cb_used, sizeof (void *),
	    libzfs_dataset_cmp);

	/*
	 * And mount all the datasets, keeping track of which ones
	 * succeeded or failed.
	 */
	if ((good = zfs_alloc(zhp->zpool_hdl,
	    cb.cb_used * sizeof (int))) == NULL)
		goto out;

	ret = 0;
	for (i = 0; i < cb.cb_used; i++) {
		if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0)
			ret = -1;
		else
			good[i] = 1;
	}

	/*
	 * Then share all the ones that need to be shared. This needs
	 * to be a separate pass in order to avoid excessive reloading
	 * of the configuration. Good should never be NULL since
	 * zfs_alloc is supposed to exit if memory isn't available.
	 */
	for (i = 0; i < cb.cb_used; i++) {
		if (good[i] && zfs_share(cb.cb_handles[i]) != 0)
			ret = -1;
	}

	free(good);

out:
	for (i = 0; i < cb.cb_used; i++)
		zfs_close(cb.cb_handles[i]);
	free(cb.cb_handles);

	return (ret);
}