/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright 2015 Toomas Soome * Copyright 2015 Gary Mills * Copyright (c) 2016 Martin Matuska. All rights reserved. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Callback data used for zfs_iter calls. */ typedef struct list_callback_data { char *zpool_name; char *be_name; be_node_list_t *be_nodes_head; be_node_list_t *be_nodes; be_dataset_list_t **be_datasets_tail; be_snapshot_list_t **be_snapshots_tail; char current_be[MAXPATHLEN]; struct be_defaults be_defaults; uint64_t flags; } list_callback_data_t; /* * Private function prototypes */ static int be_add_children_callback(zfs_handle_t *zhp, void *data); static int be_get_list_callback(zpool_handle_t *, void *); static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *, const char *, char *, char *); static int be_get_zone_node_data(be_node_list_t *, char *); static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *, be_node_list_t *); static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *, be_node_list_t *); static int be_sort_list(be_node_list_t **, int (*)(const void *, const void *)); static int be_qsort_compare_BEs_name(const void *, const void *); static int be_qsort_compare_BEs_name_rev(const void *, const void *); static int be_qsort_compare_BEs_date(const void *, const void *); static int be_qsort_compare_BEs_date_rev(const void *, const void *); static int be_qsort_compare_BEs_space(const void *, const void *); static int be_qsort_compare_BEs_space_rev(const void *, const void *); static int be_qsort_compare_snapshots(const void *x, const void *y); static int be_qsort_compare_datasets(const void *x, const void *y); static void *be_list_alloc(int *, size_t); static int be_allocate_callback_nodes(list_callback_data_t *); /* * Private data. */ static char be_container_ds[MAXPATHLEN]; static boolean_t zone_be = B_FALSE; /* ******************************************************************** */ /* Public Functions */ /* ******************************************************************** */ /* * Function: be_list * Description: Calls _be_list which finds all the BEs on the system and * returns the datasets and snapshots belonging to each BE. * Also data, such as dataset and snapshot properties, * for each BE and their snapshots and datasets is * returned. The data returned is as described in the * be_dataset_list_t, be_snapshot_list_t and be_node_list_t * structures. * Parameters: * be_name - The name of the BE to look up. * If NULL a list of all BEs will be returned. * be_nodes - A reference pointer to the list of BEs. The list * structure will be allocated by _be_list and must * be freed by a call to be_free_list. If there are no * BEs found on the system this reference will be * set to NULL. * Return: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Public */ int be_list(char *be_name, be_node_list_t **be_nodes, uint64_t flags) { int ret = BE_SUCCESS; /* Initialize libzfs handle */ if (!be_zfs_init()) return (BE_ERR_INIT); /* Validate be_name if its not NULL */ if (be_name != NULL) { if (!be_valid_be_name(be_name)) { be_print_err(gettext("be_list: " "invalid BE name %s\n"), be_name); return (BE_ERR_INVAL); } } ret = _be_list(be_name, be_nodes, flags); be_zfs_fini(); return (ret); } /* * Function: be_sort * Description: Sort BE node list * Parameters: * pointer to address of list head * sort order type * Return: * BE_SUCCESS - Success * be_errno_t - Failure * Side effect: * node list sorted by name * Scope: * Public */ int be_sort(be_node_list_t **be_nodes, int order) { int (*compar)(const void *, const void *) = be_qsort_compare_BEs_date; if (be_nodes == NULL) return (BE_ERR_INVAL); switch (order) { case BE_SORT_UNSPECIFIED: case BE_SORT_DATE: compar = be_qsort_compare_BEs_date; break; case BE_SORT_DATE_REV: compar = be_qsort_compare_BEs_date_rev; break; case BE_SORT_NAME: compar = be_qsort_compare_BEs_name; break; case BE_SORT_NAME_REV: compar = be_qsort_compare_BEs_name_rev; break; case BE_SORT_SPACE: compar = be_qsort_compare_BEs_space; break; case BE_SORT_SPACE_REV: compar = be_qsort_compare_BEs_space_rev; break; default: be_print_err(gettext("be_sort: invalid sort order %d\n"), order); return (BE_ERR_INVAL); } return (be_sort_list(be_nodes, compar)); } /* ******************************************************************** */ /* Semi-Private Functions */ /* ******************************************************************** */ /* * Function: _be_list * Description: This does the actual work described in be_list. * Parameters: * be_name - The name of the BE to look up. * If NULL a list of all BEs will be returned. * be_nodes - A reference pointer to the list of BEs. The list * structure will be allocated here and must * be freed by a call to be_free_list. If there are no * BEs found on the system this reference will be * set to NULL. * Return: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Semi-private (library wide use only) */ int _be_list(char *be_name, be_node_list_t **be_nodes, uint64_t flags) { list_callback_data_t cb = { 0 }; be_transaction_data_t bt = { 0 }; int ret = BE_SUCCESS; int sret; zpool_handle_t *zphp; char *rpool = NULL; if (be_nodes == NULL) return (BE_ERR_INVAL); be_get_defaults(&cb.be_defaults); cb.flags = flags; if (be_find_current_be(&bt) != BE_SUCCESS) { /* * We were unable to find a currently booted BE which * probably means that we're not booted in a BE envoronment. * None of the BE's will be marked as the active BE. */ (void) strcpy(cb.current_be, "-"); } else { (void) strncpy(cb.current_be, bt.obe_name, sizeof (cb.current_be)); rpool = bt.obe_zpool; } /* * If be_name is NULL we'll look for all BE's on the system. * If not then we will only return data for the specified BE. */ if (be_name != NULL) cb.be_name = strdup(be_name); if (cb.be_defaults.be_deflt_rpool_container && rpool != NULL) { if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { be_print_err(gettext("be_list: failed to " "open rpool (%s): %s\n"), rpool, libzfs_error_description(g_zfs)); free(cb.be_name); return (zfs_err_to_be_err(g_zfs)); } ret = be_get_list_callback(zphp, &cb); } else { if ((zpool_iter(g_zfs, be_get_list_callback, &cb)) != 0) { if (cb.be_nodes_head != NULL) { be_free_list(cb.be_nodes_head); cb.be_nodes_head = NULL; cb.be_nodes = NULL; } ret = BE_ERR_BE_NOENT; } } if (cb.be_nodes_head == NULL) { if (be_name != NULL) be_print_err(gettext("be_list: BE (%s) does not " "exist\n"), be_name); else be_print_err(gettext("be_list: No BE's found\n")); ret = BE_ERR_BE_NOENT; } *be_nodes = cb.be_nodes_head; free(cb.be_name); sret = be_sort(be_nodes, BE_SORT_DATE); return ((ret == BE_SUCCESS) ? sret : ret); } /* * Function: be_free_list * Description: Frees up all the data allocated for the list of BEs, * datasets and snapshots returned by be_list. * Parameters: * be_node - be_nodes_t structure returned from call to be_list. * Returns: * none * Scope: * Semi-private (library wide use only) */ void be_free_list(be_node_list_t *be_nodes) { be_node_list_t *temp_node = NULL; be_node_list_t *list = be_nodes; while (list != NULL) { be_dataset_list_t *datasets = list->be_node_datasets; be_snapshot_list_t *snapshots = list->be_node_snapshots; while (datasets != NULL) { be_dataset_list_t *temp_ds = datasets; datasets = datasets->be_next_dataset; free(temp_ds->be_dataset_name); free(temp_ds->be_ds_mntpt); free(temp_ds->be_ds_plcy_type); free(temp_ds); } while (snapshots != NULL) { be_snapshot_list_t *temp_ss = snapshots; snapshots = snapshots->be_next_snapshot; free(temp_ss->be_snapshot_name); free(temp_ss->be_snapshot_type); free(temp_ss); } temp_node = list; list = list->be_next_node; free(temp_node->be_node_name); free(temp_node->be_root_ds); free(temp_node->be_rpool); free(temp_node->be_mntpt); free(temp_node->be_policy_type); free(temp_node->be_uuid_str); free(temp_node); } } /* * Function: be_get_zone_be_list * Description: Finds all the BEs for this zone on the system. * Parameters: * zone_be_name - The name of the BE to look up. * zone_be_container_ds - The dataset for the zone. * zbe_nodes - A reference pointer to the list of BEs. The list * structure will be allocated here and must * be freed by a call to be_free_list. If there are no * BEs found on the system this reference will be * set to NULL. * Return: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Semi-private (library wide use only) */ int /* LINTED */ be_get_zone_be_list(char *zone_be_name, char *zone_be_container_ds, be_node_list_t **zbe_nodes) { zfs_handle_t *zhp = NULL; list_callback_data_t cb = { 0 }; int ret = BE_SUCCESS; if (zbe_nodes == NULL) return (BE_ERR_INVAL); if (!zfs_dataset_exists(g_zfs, zone_be_container_ds, ZFS_TYPE_FILESYSTEM)) { return (BE_ERR_BE_NOENT); } zone_be = B_TRUE; if ((zhp = zfs_open(g_zfs, zone_be_container_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { be_print_err(gettext("be_get_zone_be_list: failed to open " "the zone BE dataset %s: %s\n"), zone_be_container_ds, libzfs_error_description(g_zfs)); ret = zfs_err_to_be_err(g_zfs); goto cleanup; } (void) strcpy(be_container_ds, zone_be_container_ds); if ((ret = be_allocate_callback_nodes(&cb)) != BE_SUCCESS) { ZFS_CLOSE(zhp); goto cleanup; } if (ret == 0) { be_get_defaults(&cb.be_defaults); ret = zfs_iter_filesystems(zhp, be_add_children_callback, &cb); } ZFS_CLOSE(zhp); *zbe_nodes = cb.be_nodes_head; cleanup: zone_be = B_FALSE; return (ret); } /* ******************************************************************** */ /* Private Functions */ /* ******************************************************************** */ /* * 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 ((ret = be_allocate_callback_nodes(cb)) != BE_SUCCESS) { ZFS_CLOSE(zhp); zpool_close(zlp); return (ret); } 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); } if (cb->flags & BE_LIST_SNAPSHOTS) ret = zfs_iter_snapshots(zhp, B_FALSE, 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_allocate_callback_nodes * Description: Function to create the be_nodes list in the callback data * structure, and set up tail pointers to the dataset and * snapshot lists. * Parameters: * data - pointer to the callback data. * Returns: * 0 - Success * be_errno_t - Failure * Scope: * Private */ static int be_allocate_callback_nodes(list_callback_data_t *cb) { int ret = BE_SUCCESS; if (cb->be_nodes_head != NULL) return (BE_SUCCESS); if ((cb->be_nodes_head = be_list_alloc(&ret, sizeof (be_node_list_t))) == NULL) return (ret); cb->be_nodes = cb->be_nodes_head; cb->be_snapshots_tail = &cb->be_nodes->be_node_snapshots; cb->be_datasets_tail = &cb->be_nodes->be_node_datasets; return (BE_SUCCESS); } /* * 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); } /* * Function: be_sort_list * Description: Sort BE node list * Parameters: * pointer to address of list head * compare function * Return: * BE_SUCCESS - Success * be_errno_t - Failure * Side effect: * node list sorted by name * Scope: * Private */ static int be_sort_list(be_node_list_t **pstart, int (*compar)(const void *, const void *)) { int ret = BE_SUCCESS; size_t ibe, nbe; be_node_list_t *p = NULL; be_node_list_t **ptrlist = NULL; be_node_list_t **ptrtmp; if (pstart == NULL) /* Nothing to sort */ return (BE_SUCCESS); /* build array of linked list BE struct pointers */ for (p = *pstart, nbe = 0; p != NULL; nbe++, p = p->be_next_node) { ptrtmp = realloc(ptrlist, sizeof (be_node_list_t *) * (nbe + 2)); if (ptrtmp == NULL) { /* out of memory */ be_print_err(gettext("be_sort_list: memory " "allocation failed\n")); ret = BE_ERR_NOMEM; goto free; } ptrlist = ptrtmp; ptrlist[nbe] = p; } if (nbe == 0) /* Nothing to sort */ return (BE_SUCCESS); /* in-place list quicksort using qsort(3C) */ if (nbe > 1) /* no sort if less than 2 BEs */ qsort(ptrlist, nbe, sizeof (be_node_list_t *), compar); ptrlist[nbe] = NULL; /* add linked list terminator */ *pstart = ptrlist[0]; /* set new linked list header */ /* for each BE in list */ for (ibe = 0; ibe < nbe; ibe++) { size_t k, ns; /* subordinate index, count */ /* rewrite list pointer chain, including terminator */ ptrlist[ibe]->be_next_node = ptrlist[ibe + 1]; /* sort subordinate snapshots */ if (ptrlist[ibe]->be_node_num_snapshots > 1) { const size_t nmax = ptrlist[ibe]->be_node_num_snapshots; be_snapshot_list_t ** const slist = malloc(sizeof (be_snapshot_list_t *) * (nmax + 1)); be_snapshot_list_t *p; if (slist == NULL) { ret = BE_ERR_NOMEM; continue; } /* build array of linked list snapshot struct ptrs */ for (ns = 0, p = ptrlist[ibe]->be_node_snapshots; ns < nmax && p != NULL; ns++, p = p->be_next_snapshot) { slist[ns] = p; } if (ns < 2) goto end_snapshot; slist[ns] = NULL; /* add terminator */ /* in-place list quicksort using qsort(3C) */ qsort(slist, ns, sizeof (be_snapshot_list_t *), be_qsort_compare_snapshots); /* rewrite list pointer chain, including terminator */ ptrlist[ibe]->be_node_snapshots = slist[0]; for (k = 0; k < ns; k++) slist[k]->be_next_snapshot = slist[k + 1]; end_snapshot: free(slist); } /* sort subordinate datasets */ if (ptrlist[ibe]->be_node_num_datasets > 1) { const size_t nmax = ptrlist[ibe]->be_node_num_datasets; be_dataset_list_t ** const slist = malloc(sizeof (be_dataset_list_t *) * (nmax + 1)); be_dataset_list_t *p; if (slist == NULL) { ret = BE_ERR_NOMEM; continue; } /* build array of linked list dataset struct ptrs */ for (ns = 0, p = ptrlist[ibe]->be_node_datasets; ns < nmax && p != NULL; ns++, p = p->be_next_dataset) { slist[ns] = p; } if (ns < 2) /* subordinate datasets < 2 - no sort */ goto end_dataset; slist[ns] = NULL; /* add terminator */ /* in-place list quicksort using qsort(3C) */ qsort(slist, ns, sizeof (be_dataset_list_t *), be_qsort_compare_datasets); /* rewrite list pointer chain, including terminator */ ptrlist[ibe]->be_node_datasets = slist[0]; for (k = 0; k < ns; k++) slist[k]->be_next_dataset = slist[k + 1]; end_dataset: free(slist); } } free: free(ptrlist); return (ret); } /* * Function: be_qsort_compare_BEs_date * Description: compare BE creation times for qsort(3C) * will sort BE list from oldest to most recent * Parameters: * x,y - BEs with names to compare * Returns: * positive if x>y, negative if y>x, 0 if equal * Scope: * Private */ static int be_qsort_compare_BEs_date(const void *x, const void *y) { be_node_list_t *p = *(be_node_list_t **)x; be_node_list_t *q = *(be_node_list_t **)y; assert(p != NULL); assert(q != NULL); if (p->be_node_creation > q->be_node_creation) return (1); if (p->be_node_creation < q->be_node_creation) return (-1); return (0); } /* * Function: be_qsort_compare_BEs_date_rev * Description: compare BE creation times for qsort(3C) * will sort BE list from recent to oldest * Parameters: * x,y - BEs with names to compare * Returns: * positive if y>x, negative if x>y, 0 if equal * Scope: * Private */ static int be_qsort_compare_BEs_date_rev(const void *x, const void *y) { return (be_qsort_compare_BEs_date(y, x)); } /* * Function: be_qsort_compare_BEs_name * Description: lexical compare of BE names for qsort(3C) * Parameters: * x,y - BEs with names to compare * Returns: * positive if x>y, negative if y>x, 0 if equal * Scope: * Private */ static int be_qsort_compare_BEs_name(const void *x, const void *y) { be_node_list_t *p = *(be_node_list_t **)x; be_node_list_t *q = *(be_node_list_t **)y; assert(p != NULL); assert(p->be_node_name != NULL); assert(q != NULL); assert(q->be_node_name != NULL); return (strcmp(p->be_node_name, q->be_node_name)); } /* * Function: be_qsort_compare_BEs_name_rev * Description: reverse lexical compare of BE names for qsort(3C) * Parameters: * x,y - BEs with names to compare * Returns: * positive if y>x, negative if x>y, 0 if equal * Scope: * Private */ static int be_qsort_compare_BEs_name_rev(const void *x, const void *y) { return (be_qsort_compare_BEs_name(y, x)); } /* * Function: be_qsort_compare_BEs_space * Description: compare BE sizes for qsort(3C) * will sort BE list in growing order * Parameters: * x,y - BEs with names to compare * Returns: * positive if x>y, negative if y>x, 0 if equal * Scope: * Private */ static int be_qsort_compare_BEs_space(const void *x, const void *y) { be_node_list_t *p = *(be_node_list_t **)x; be_node_list_t *q = *(be_node_list_t **)y; assert(p != NULL); assert(q != NULL); if (p->be_space_used > q->be_space_used) return (1); if (p->be_space_used < q->be_space_used) return (-1); return (0); } /* * Function: be_qsort_compare_BEs_space_rev * Description: compare BE sizes for qsort(3C) * will sort BE list in shrinking * Parameters: * x,y - BEs with names to compare * Returns: * positive if y>x, negative if x>y, 0 if equal * Scope: * Private */ static int be_qsort_compare_BEs_space_rev(const void *x, const void *y) { return (be_qsort_compare_BEs_space(y, x)); } /* * Function: be_qsort_compare_snapshots * Description: lexical compare of BE names for qsort(3C) * Parameters: * x,y - BE snapshots with names to compare * Returns: * positive if y>x, negative if x>y, 0 if equal * Scope: * Private */ static int be_qsort_compare_snapshots(const void *x, const void *y) { be_snapshot_list_t *p = *(be_snapshot_list_t **)x; be_snapshot_list_t *q = *(be_snapshot_list_t **)y; if (p == NULL || p->be_snapshot_name == NULL) return (1); if (q == NULL || q->be_snapshot_name == NULL) return (-1); return (strcmp(p->be_snapshot_name, q->be_snapshot_name)); } /* * Function: be_qsort_compare_datasets * Description: lexical compare of dataset names for qsort(3C) * Parameters: * x,y - BE snapshots with names to compare * Returns: * positive if y>x, negative if x>y, 0 if equal * Scope: * Private */ static int be_qsort_compare_datasets(const void *x, const void *y) { be_dataset_list_t *p = *(be_dataset_list_t **)x; be_dataset_list_t *q = *(be_dataset_list_t **)y; if (p == NULL || p->be_dataset_name == NULL) return (1); if (q == NULL || q->be_dataset_name == NULL) return (-1); return (strcmp(p->be_dataset_name, q->be_dataset_name)); } /* * Function: be_get_node_data * Description: Helper function used to collect all the information to fill * in the be_node_list structure to be returned by be_list. * Parameters: * zhp - Handle to the root dataset for the BE whose information * we're collecting. * be_node - a pointer to the node structure we're filling in. * be_name - The BE name of the node whose information we're * collecting. * current_be - the name of the currently active BE. * be_ds - The dataset name for the BE. * * Returns: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Private */ static int be_get_node_data(zfs_handle_t *zhp, be_node_list_t *be_node, char *be_name, const char *rpool, char *current_be, char *be_ds) { char prop_buf[MAXPATHLEN]; nvlist_t *userprops = NULL; nvlist_t *propval = NULL; nvlist_t *zone_propval = NULL; char *prop_str = NULL; char *zone_prop_str = NULL; char *grub_default_bootfs = NULL; zpool_handle_t *zphp = NULL; int err = 0; if (be_node == NULL || be_name == NULL || current_be == NULL || be_ds == NULL) { be_print_err(gettext("be_get_node_data: invalid arguments, " "can not be NULL\n")); return (BE_ERR_INVAL); } errno = 0; be_node->be_root_ds = strdup(be_ds); if ((err = errno) != 0 || be_node->be_root_ds == NULL) { be_print_err(gettext("be_get_node_data: failed to " "copy root dataset name\n")); return (errno_to_be_err(err)); } be_node->be_node_name = strdup(be_name); if ((err = errno) != 0 || be_node->be_node_name == NULL) { be_print_err(gettext("be_get_node_data: failed to " "copy BE name\n")); return (errno_to_be_err(err)); } if (strncmp(be_name, current_be, MAXPATHLEN) == 0) be_node->be_active = B_TRUE; else be_node->be_active = B_FALSE; be_node->be_rpool = strdup(rpool); if (be_node->be_rpool == NULL || (err = errno) != 0) { be_print_err(gettext("be_get_node_data: failed to " "copy root pool name\n")); return (errno_to_be_err(err)); } be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED); if (getzoneid() == GLOBAL_ZONEID) { char *nextboot; if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { be_print_err(gettext("be_get_node_data: failed to open " "pool (%s): %s\n"), rpool, libzfs_error_description(g_zfs)); return (zfs_err_to_be_err(g_zfs)); } /* Set nextboot info */ be_node->be_active_next = B_FALSE; if (lzbe_get_boot_device(rpool, &nextboot) == 0) { if (nextboot != NULL) { if (strcmp(nextboot, be_ds) == 0) be_node->be_active_next = B_TRUE; free(nextboot); } } (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN, NULL, B_FALSE); if (be_has_grub() && (be_default_grub_bootfs(rpool, &grub_default_bootfs) == BE_SUCCESS) && grub_default_bootfs != NULL) if (strcmp(grub_default_bootfs, be_ds) == 0) be_node->be_active_on_boot = B_TRUE; else be_node->be_active_on_boot = B_FALSE; else if (strcmp(prop_buf, be_ds) == 0) be_node->be_active_on_boot = B_TRUE; else be_node->be_active_on_boot = B_FALSE; be_node->be_global_active = B_TRUE; free(grub_default_bootfs); zpool_close(zphp); } else { if (be_zone_compare_uuids(be_node->be_root_ds)) be_node->be_global_active = B_TRUE; else be_node->be_global_active = B_FALSE; } /* * If the dataset is mounted use the mount point * returned from the zfs_is_mounted call. If the * dataset is not mounted then pull the mount * point information out of the zfs properties. */ be_node->be_mounted = zfs_is_mounted(zhp, &(be_node->be_mntpt)); if (!be_node->be_mounted) { if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0) be_node->be_mntpt = strdup(prop_buf); else return (zfs_err_to_be_err(g_zfs)); } be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); /* Get all user properties used for libbe */ if ((userprops = zfs_get_user_props(zhp)) == NULL) { be_node->be_policy_type = strdup(be_default_policy()); } else { if (getzoneid() != GLOBAL_ZONEID) { if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &zone_propval) != 0 || zone_propval == NULL) { be_node->be_active_on_boot = B_FALSE; } else { verify(nvlist_lookup_string(zone_propval, ZPROP_VALUE, &zone_prop_str) == 0); if (strcmp(zone_prop_str, "on") == 0) { be_node->be_active_on_boot = B_TRUE; } else { be_node->be_active_on_boot = B_FALSE; } } } if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY, &propval) != 0 || propval == NULL) { be_node->be_policy_type = strdup(be_default_policy()); } else { verify(nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0); if (prop_str == NULL || strcmp(prop_str, "-") == 0 || strcmp(prop_str, "") == 0) be_node->be_policy_type = strdup(be_default_policy()); else be_node->be_policy_type = strdup(prop_str); } if (getzoneid() != GLOBAL_ZONEID) { if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY, &propval) != 0 && nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0) { be_node->be_uuid_str = strdup(prop_str); } } else { if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propval) == 0 && nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0) { be_node->be_uuid_str = strdup(prop_str); } } } /* * Increment the dataset counter to include the root dataset * of the BE. */ be_node->be_node_num_datasets++; return (BE_SUCCESS); } /* * Function: be_get_ds_data * Description: Helper function used by be_add_children_callback to collect * the dataset related information that will be returned by * be_list. * Parameters: * zhp - Handle to the zfs dataset whose information we're * collecting. * name - The name of the dataset we're processing. * dataset - A pointer to the be_dataset_list structure * we're filling in. * node - The node structure that this dataset belongs to. * Return: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Private */ static int be_get_ds_data( zfs_handle_t *zfshp, char *name, be_dataset_list_t *dataset, be_node_list_t *node) { char prop_buf[ZFS_MAXPROPLEN]; nvlist_t *propval = NULL; nvlist_t *userprops = NULL; char *prop_str = NULL; int err = 0; if (zfshp == NULL || name == NULL || dataset == NULL || node == NULL) { be_print_err(gettext("be_get_ds_data: invalid arguments, " "can not be NULL\n")); return (BE_ERR_INVAL); } errno = 0; dataset->be_dataset_name = strdup(name); if ((err = errno) != 0) { be_print_err(gettext("be_get_ds_data: failed to copy " "dataset name\n")); return (errno_to_be_err(err)); } dataset->be_ds_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED); /* * If the dataset is mounted use the mount point * returned from the zfs_is_mounted call. If the * dataset is not mounted then pull the mount * point information out of the zfs properties. */ if (!(dataset->be_ds_mounted = zfs_is_mounted(zfshp, &(dataset->be_ds_mntpt)))) { if (zfs_prop_get(zfshp, ZFS_PROP_MOUNTPOINT, prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0) dataset->be_ds_mntpt = strdup(prop_buf); else return (zfs_err_to_be_err(g_zfs)); } dataset->be_ds_creation = (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION); /* * Get the user property used for the libbe * cleaup policy */ if ((userprops = zfs_get_user_props(zfshp)) == NULL) { dataset->be_ds_plcy_type = strdup(node->be_policy_type); } else { if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY, &propval) != 0 || propval == NULL) { dataset->be_ds_plcy_type = strdup(node->be_policy_type); } else { verify(nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0); if (prop_str == NULL || strcmp(prop_str, "-") == 0 || strcmp(prop_str, "") == 0) dataset->be_ds_plcy_type = strdup(node->be_policy_type); else dataset->be_ds_plcy_type = strdup(prop_str); } } node->be_node_num_datasets++; return (BE_SUCCESS); } /* * Function: be_get_ss_data * Description: Helper function used by be_add_children_callback to collect * the dataset related information that will be returned by * be_list. * Parameters: * zhp - Handle to the zfs snapshot whose information we're * collecting. * name - The name of the snapshot we're processing. * shapshot - A pointer to the be_snapshot_list structure * we're filling in. * node - The node structure that this snapshot belongs to. * Returns: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Private */ static int be_get_ss_data( zfs_handle_t *zfshp, char *name, be_snapshot_list_t *snapshot, be_node_list_t *node) { nvlist_t *propval = NULL; nvlist_t *userprops = NULL; char *prop_str = NULL; int err = 0; if (zfshp == NULL || name == NULL || snapshot == NULL || node == NULL) { be_print_err(gettext("be_get_ss_data: invalid arguments, " "can not be NULL\n")); return (BE_ERR_INVAL); } errno = 0; snapshot->be_snapshot_name = strdup(name); if ((err = errno) != 0) { be_print_err(gettext("be_get_ss_data: failed to copy name\n")); return (errno_to_be_err(err)); } snapshot->be_snapshot_creation = (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION); /* * Try to get this snapshot's cleanup policy from its * user properties first. If not there, use default * cleanup policy. */ if ((userprops = zfs_get_user_props(zfshp)) != NULL && nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY, &propval) == 0 && nvlist_lookup_string(propval, ZPROP_VALUE, &prop_str) == 0) { snapshot->be_snapshot_type = strdup(prop_str); } else { snapshot->be_snapshot_type = strdup(be_default_policy()); } snapshot->be_snapshot_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED); node->be_node_num_snapshots++; return (BE_SUCCESS); } /* * Function: be_list_alloc * Description: Helper function used to allocate memory for the various * sructures that make up a BE node. * Parameters: * err - Used to return any errors encountered. * BE_SUCCESS - Success * BE_ERR_NOMEM - Allocation failure * size - The size of memory to allocate. * Returns: * Success - A pointer to the allocated memory * Failure - NULL * Scope: * Private */ static void* be_list_alloc(int *err, size_t size) { void *bep = NULL; bep = calloc(1, size); if (bep == NULL) { be_print_err(gettext("be_list_alloc: memory " "allocation failed\n")); *err = BE_ERR_NOMEM; } *err = BE_SUCCESS; return (bep); } /* * Function: be_get_zone_node_data * Description: Helper function used to collect all the information to * fill in the be_node_list structure to be returned by * be_get_zone_list. * Parameters: * be_node - a pointer to the node structure we're filling in. * be_name - The BE name of the node whose information we're * Returns: * BE_SUCCESS - Success * be_errno_t - Failure * Scope: * Private * * NOTE: This function currently only collects the zone BE name but when * support for beadm/libbe in a zone is provided it will need to fill * in the rest of the information needed for a zone BE. */ static int be_get_zone_node_data(be_node_list_t *be_node, char *be_name) { if ((be_node->be_node_name = strdup(be_name)) != NULL) return (BE_SUCCESS); return (BE_ERR_NOMEM); }