/* * 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); }
/* * 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); }
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); }
/* * 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); }
/* * 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); }
/* * 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); }
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); }