static int change_one(zfs_handle_t *zhp, void *data) { prop_changelist_t *clp = data; char property[ZFS_MAXPROPLEN]; char where[64]; prop_changenode_t *cn; zprop_source_t sourcetype = ZPROP_SRC_NONE; zprop_source_t share_sourcetype = ZPROP_SRC_NONE; /* * We only want to unmount/unshare those filesystems that may inherit * from the target filesystem. If we find any filesystem with a * locally set mountpoint, we ignore any children since changing the * property will not affect them. If this is a rename, we iterate * over all children regardless, since we need them unmounted in * order to do the rename. Also, if this is a volume and we're doing * a rename, then always add it to the changelist. */ if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) && zfs_prop_get(zhp, clp->cl_prop, property, sizeof (property), &sourcetype, where, sizeof (where), B_FALSE) != 0) { zfs_close(zhp); return (0); } /* * If we are "watching" sharenfs or sharesmb * then check out the companion property which is tracked * in cl_shareprop */ if (clp->cl_shareprop != ZPROP_INVAL && zfs_prop_get(zhp, clp->cl_shareprop, property, sizeof (property), &share_sourcetype, where, sizeof (where), B_FALSE) != 0) { zfs_close(zhp); return (0); } if (clp->cl_alldependents || clp->cl_allchildren || sourcetype == ZPROP_SRC_DEFAULT || sourcetype == ZPROP_SRC_INHERITED || (clp->cl_shareprop != ZPROP_INVAL && (share_sourcetype == ZPROP_SRC_DEFAULT || share_sourcetype == ZPROP_SRC_INHERITED))) { if ((cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t))) == NULL) { zfs_close(zhp); return (-1); } cn->cn_handle = zhp; cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || zfs_is_mounted(zhp, NULL); cn->cn_shared = zfs_is_shared(zhp); cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); cn->cn_needpost = B_TRUE; /* Indicate if any child is exported to a local zone. */ if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) clp->cl_haszonedchild = B_TRUE; uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); if (clp->cl_sorted) { uu_list_index_t idx; (void) uu_list_find(clp->cl_list, cn, NULL, &idx); uu_list_insert(clp->cl_list, cn, idx); } else { /* * Add this child to beginning of the list. Children * below this one in the hierarchy will get added above * this one in the list. This produces a list in * reverse dataset name order. * This is necessary when the original mountpoint * is legacy or none. */ ASSERT(!clp->cl_alldependents); verify(uu_list_insert_before(clp->cl_list, uu_list_first(clp->cl_list), cn) == 0); } if (!clp->cl_alldependents) return (zfs_iter_children(zhp, change_one, data)); } else { zfs_close(zhp); } return (0); }
/* * Given a ZFS handle and a property, construct a complete list of datasets * that need to be modified as part of this process. For anything but the * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. * Otherwise, we iterate over all children and look for any datasets that * inherit the property. For each such dataset, we add it to the list and * mark whether it was shared beforehand. */ prop_changelist_t * changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags, int mnt_flags) { prop_changelist_t *clp; prop_changenode_t *cn; zfs_handle_t *temp; char property[ZFS_MAXPROPLEN]; uu_compare_fn_t *compare = NULL; boolean_t legacy = B_FALSE; if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) return (NULL); /* * For mountpoint-related tasks, we want to sort everything by * mountpoint, so that we mount and unmount them in the appropriate * order, regardless of their position in the hierarchy. */ if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS || prop == ZFS_PROP_SHARESMB) { if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property, sizeof (property), NULL, NULL, 0, B_FALSE) == 0 && (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0)) { legacy = B_TRUE; } if (!legacy) { compare = compare_mountpoints; clp->cl_sorted = B_TRUE; } } clp->cl_pool = uu_list_pool_create("changelist_pool", sizeof (prop_changenode_t), offsetof(prop_changenode_t, cn_listnode), compare, 0); if (clp->cl_pool == NULL) { assert(uu_error() == UU_ERROR_NO_MEMORY); (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); changelist_free(clp); return (NULL); } clp->cl_list = uu_list_create(clp->cl_pool, NULL, clp->cl_sorted ? UU_LIST_SORTED : 0); clp->cl_gflags = gather_flags; clp->cl_mflags = mnt_flags; if (clp->cl_list == NULL) { assert(uu_error() == UU_ERROR_NO_MEMORY); (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); changelist_free(clp); return (NULL); } /* * If this is a rename or the 'zoned' property, we pretend we're * changing the mountpoint and flag it so we can catch all children in * change_one(). * * Flag cl_alldependents to catch all children plus the dependents * (clones) that are not in the hierarchy. */ if (prop == ZFS_PROP_NAME) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_alldependents = B_TRUE; } else if (prop == ZFS_PROP_ZONED) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_allchildren = B_TRUE; } else if (prop == ZFS_PROP_CANMOUNT) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else if (prop == ZFS_PROP_VOLSIZE) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else { clp->cl_prop = prop; } clp->cl_realprop = prop; if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && clp->cl_prop != ZFS_PROP_SHARENFS && clp->cl_prop != ZFS_PROP_SHARESMB) return (clp); /* * If watching SHARENFS or SHARESMB then * also watch its companion property. */ if (clp->cl_prop == ZFS_PROP_SHARENFS) clp->cl_shareprop = ZFS_PROP_SHARESMB; else if (clp->cl_prop == ZFS_PROP_SHARESMB) clp->cl_shareprop = ZFS_PROP_SHARENFS; if (clp->cl_alldependents) { if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { changelist_free(clp); return (NULL); } } else if (zfs_iter_children(zhp, change_one, clp) != 0) { changelist_free(clp); return (NULL); } /* * We have to re-open ourselves because we auto-close all the handles * and can't tell the difference. */ if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), ZFS_TYPE_DATASET)) == NULL) { changelist_free(clp); return (NULL); } /* * Always add ourself to the list. We add ourselves to the end so that * we're the last to be unmounted. */ if ((cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t))) == NULL) { zfs_close(temp); changelist_free(clp); return (NULL); } cn->cn_handle = temp; cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || zfs_is_mounted(temp, NULL); cn->cn_shared = zfs_is_shared(temp); cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); cn->cn_needpost = B_TRUE; uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); if (clp->cl_sorted) { uu_list_index_t idx; (void) uu_list_find(clp->cl_list, cn, NULL, &idx); uu_list_insert(clp->cl_list, cn, idx); } else { /* * Add the target dataset to the end of the list. * The list is not really unsorted. The list will be * in reverse dataset name order. This is necessary * when the original mountpoint is legacy or none. */ verify(uu_list_insert_after(clp->cl_list, uu_list_last(clp->cl_list), cn) == 0); } /* * If the mountpoint property was previously 'legacy', or 'none', * record it as the behavior of changelist_postfix() will be different. */ if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) { /* * do not automatically mount ex-legacy datasets if * we specifically set canmount to noauto */ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) != ZFS_CANMOUNT_NOAUTO) clp->cl_waslegacy = B_TRUE; } return (clp); }
int zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit, zfs_iter_f callback, void *data) { callback_data_t cb = {0}; int ret = 0; zfs_node_t *node; uu_avl_walk_t *walk; avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); if (avl_pool == NULL) nomem(); cb.cb_sortcol = sortcol; cb.cb_flags = flags; cb.cb_proplist = proplist; cb.cb_types = types; cb.cb_depth_limit = limit; /* * If cb_proplist is provided then in the zfs_handles created we * retain only those properties listed in cb_proplist and sortcol. * The rest are pruned. So, the caller should make sure that no other * properties other than those listed in cb_proplist/sortcol are * accessed. * * If cb_proplist is NULL then we retain all the properties. We * always retain the zoned property, which some other properties * need (userquota & friends), and the createtxg property, which * we need to sort snapshots. */ if (cb.cb_proplist && *cb.cb_proplist) { zprop_list_t *p = *cb.cb_proplist; while (p) { if (p->pl_prop >= ZFS_PROP_TYPE && p->pl_prop < ZFS_NUM_PROPS) { cb.cb_props_table[p->pl_prop] = B_TRUE; } p = p->pl_next; } while (sortcol) { if (sortcol->sc_prop >= ZFS_PROP_TYPE && sortcol->sc_prop < ZFS_NUM_PROPS) { cb.cb_props_table[sortcol->sc_prop] = B_TRUE; } sortcol = sortcol->sc_next; } cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE; cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE; } else { (void) memset(cb.cb_props_table, B_TRUE, sizeof (cb.cb_props_table)); } if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) nomem(); if (argc == 0) { /* * If given no arguments, iterate over all datasets. */ cb.cb_flags |= ZFS_ITER_RECURSE; ret = zfs_iter_root(g_zfs, zfs_callback, &cb); } else { int i; zfs_handle_t *zhp; zfs_type_t argtype; /* * If we're recursive, then we always allow filesystems as * arguments. If we also are interested in snapshots, then we * can take volumes as well. */ argtype = types; if (flags & ZFS_ITER_RECURSE) { argtype |= ZFS_TYPE_FILESYSTEM; if (types & ZFS_TYPE_SNAPSHOT) argtype |= ZFS_TYPE_VOLUME; } for (i = 0; i < argc; i++) { if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) { zhp = zfs_path_to_zhandle(g_zfs, argv[i], argtype); } else { zhp = zfs_open(g_zfs, argv[i], argtype); } if (zhp != NULL) ret |= zfs_callback(zhp, &cb); else ret = 1; } } /* * At this point we've got our AVL tree full of zfs handles, so iterate * over each one and execute the real user callback. */ for (node = uu_avl_first(cb.cb_avl); node != NULL; node = uu_avl_next(cb.cb_avl, node)) ret |= callback(node->zn_handle, data); /* * Finally, clean up the AVL tree. */ if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) nomem(); while ((node = uu_avl_walk_next(walk)) != NULL) { uu_avl_remove(cb.cb_avl, node); zfs_close(node->zn_handle); free(node); } uu_avl_walk_end(walk); uu_avl_destroy(cb.cb_avl); uu_avl_pool_destroy(avl_pool); return (ret); }