1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5ea8dc4b6Seschrock * Common Development and Distribution License (the "License"). 6ea8dc4b6Seschrock * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21f3861e1aSahl 22fa9e4066Sahrens /* 232e5e9e19SSanjeev Bagewadi * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #include <assert.h> 28fa9e4066Sahrens #include <ctype.h> 29fa9e4066Sahrens #include <errno.h> 30fa9e4066Sahrens #include <libdevinfo.h> 31fa9e4066Sahrens #include <libintl.h> 32fa9e4066Sahrens #include <math.h> 33fa9e4066Sahrens #include <stdio.h> 34fa9e4066Sahrens #include <stdlib.h> 35fa9e4066Sahrens #include <strings.h> 36fa9e4066Sahrens #include <unistd.h> 373cb34c60Sahrens #include <stddef.h> 38fa9e4066Sahrens #include <zone.h> 3999653d4eSeschrock #include <fcntl.h> 40fa9e4066Sahrens #include <sys/mntent.h> 41b12a1c38Slling #include <sys/mount.h> 42ecd6cf80Smarks #include <sys/avl.h> 43ecd6cf80Smarks #include <priv.h> 44ecd6cf80Smarks #include <pwd.h> 45ecd6cf80Smarks #include <grp.h> 46ecd6cf80Smarks #include <stddef.h> 47ecd6cf80Smarks #include <ucred.h> 48*14843421SMatthew Ahrens #include <idmap.h> 49*14843421SMatthew Ahrens #include <aclutils.h> 50fa9e4066Sahrens 51fa9e4066Sahrens #include <sys/spa.h> 52e9dbad6fSeschrock #include <sys/zap.h> 53fa9e4066Sahrens #include <libzfs.h> 54fa9e4066Sahrens 55fa9e4066Sahrens #include "zfs_namecheck.h" 56fa9e4066Sahrens #include "zfs_prop.h" 57fa9e4066Sahrens #include "libzfs_impl.h" 58ecd6cf80Smarks #include "zfs_deleg.h" 59fa9e4066Sahrens 60cdf5b4caSmmusante static int zvol_create_link_common(libzfs_handle_t *, const char *, int); 61*14843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned, 62*14843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 63cdf5b4caSmmusante 64fa9e4066Sahrens /* 65fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 66fa9e4066Sahrens * readable form. 67fa9e4066Sahrens */ 68fa9e4066Sahrens const char * 69fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 70fa9e4066Sahrens { 71fa9e4066Sahrens switch (type) { 72fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 73fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 74fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 75fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 76fa9e4066Sahrens case ZFS_TYPE_VOLUME: 77fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 78fa9e4066Sahrens } 79fa9e4066Sahrens 80fa9e4066Sahrens return (NULL); 81fa9e4066Sahrens } 82fa9e4066Sahrens 83fa9e4066Sahrens /* 84fa9e4066Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 85fa9e4066Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 86fa9e4066Sahrens * We guess what the type would have been based on the path and the mask of 87fa9e4066Sahrens * acceptable types. 88fa9e4066Sahrens */ 89fa9e4066Sahrens static const char * 90fa9e4066Sahrens path_to_str(const char *path, int types) 91fa9e4066Sahrens { 92fa9e4066Sahrens /* 93fa9e4066Sahrens * When given a single type, always report the exact type. 94fa9e4066Sahrens */ 95fa9e4066Sahrens if (types == ZFS_TYPE_SNAPSHOT) 96fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 97fa9e4066Sahrens if (types == ZFS_TYPE_FILESYSTEM) 98fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 99fa9e4066Sahrens if (types == ZFS_TYPE_VOLUME) 100fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 101fa9e4066Sahrens 102fa9e4066Sahrens /* 103fa9e4066Sahrens * The user is requesting more than one type of dataset. If this is the 104fa9e4066Sahrens * case, consult the path itself. If we're looking for a snapshot, and 105fa9e4066Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 106fa9e4066Sahrens * snapshot attribute and try again. 107fa9e4066Sahrens */ 108fa9e4066Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 109fa9e4066Sahrens if (strchr(path, '@') != NULL) 110fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 111fa9e4066Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 112fa9e4066Sahrens } 113fa9e4066Sahrens 114fa9e4066Sahrens /* 115fa9e4066Sahrens * The user has requested either filesystems or volumes. 116fa9e4066Sahrens * We have no way of knowing a priori what type this would be, so always 117fa9e4066Sahrens * report it as "filesystem" or "volume", our two primitive types. 118fa9e4066Sahrens */ 119fa9e4066Sahrens if (types & ZFS_TYPE_FILESYSTEM) 120fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 121fa9e4066Sahrens 122fa9e4066Sahrens assert(types & ZFS_TYPE_VOLUME); 123fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 124fa9e4066Sahrens } 125fa9e4066Sahrens 126fa9e4066Sahrens /* 127fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 128*14843421SMatthew Ahrens * provide a more meaningful error message. We call zfs_error_aux() to 129*14843421SMatthew Ahrens * explain exactly why the name was not valid. 130fa9e4066Sahrens */ 131fa9e4066Sahrens static int 132f18faf3fSek zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 133f18faf3fSek boolean_t modifying) 134fa9e4066Sahrens { 135fa9e4066Sahrens namecheck_err_t why; 136fa9e4066Sahrens char what; 137fa9e4066Sahrens 138fa9e4066Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 13999653d4eSeschrock if (hdl != NULL) { 140fa9e4066Sahrens switch (why) { 141b81d61a6Slling case NAME_ERR_TOOLONG: 14299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14399653d4eSeschrock "name is too long")); 144b81d61a6Slling break; 145b81d61a6Slling 146fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 14799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14899653d4eSeschrock "leading slash in name")); 149fa9e4066Sahrens break; 150fa9e4066Sahrens 151fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 15299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15399653d4eSeschrock "empty component in name")); 154fa9e4066Sahrens break; 155fa9e4066Sahrens 156fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 15799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15899653d4eSeschrock "trailing slash in name")); 159fa9e4066Sahrens break; 160fa9e4066Sahrens 161fa9e4066Sahrens case NAME_ERR_INVALCHAR: 16299653d4eSeschrock zfs_error_aux(hdl, 163fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 16499653d4eSeschrock "'%c' in name"), what); 165fa9e4066Sahrens break; 166fa9e4066Sahrens 167fa9e4066Sahrens case NAME_ERR_MULTIPLE_AT: 16899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 16999653d4eSeschrock "multiple '@' delimiters in name")); 170fa9e4066Sahrens break; 1715ad82045Snd 1725ad82045Snd case NAME_ERR_NOLETTER: 1735ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1745ad82045Snd "pool doesn't begin with a letter")); 1755ad82045Snd break; 1765ad82045Snd 1775ad82045Snd case NAME_ERR_RESERVED: 1785ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1795ad82045Snd "name is reserved")); 1805ad82045Snd break; 1815ad82045Snd 1825ad82045Snd case NAME_ERR_DISKLIKE: 1835ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1845ad82045Snd "reserved disk name")); 1855ad82045Snd break; 186fa9e4066Sahrens } 187fa9e4066Sahrens } 188fa9e4066Sahrens 189fa9e4066Sahrens return (0); 190fa9e4066Sahrens } 191fa9e4066Sahrens 192fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 19399653d4eSeschrock if (hdl != NULL) 19499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19599653d4eSeschrock "snapshot delimiter '@' in filesystem name")); 196fa9e4066Sahrens return (0); 197fa9e4066Sahrens } 198fa9e4066Sahrens 1991d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 2001d452cf5Sahrens if (hdl != NULL) 2011d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 202d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 2031d452cf5Sahrens return (0); 2041d452cf5Sahrens } 2051d452cf5Sahrens 206f18faf3fSek if (modifying && strchr(path, '%') != NULL) { 207f18faf3fSek if (hdl != NULL) 208f18faf3fSek zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 209f18faf3fSek "invalid character %c in name"), '%'); 210f18faf3fSek return (0); 211f18faf3fSek } 212f18faf3fSek 21399653d4eSeschrock return (-1); 214fa9e4066Sahrens } 215fa9e4066Sahrens 216fa9e4066Sahrens int 217fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 218fa9e4066Sahrens { 219e7cbe64fSgw if (type == ZFS_TYPE_POOL) 220e7cbe64fSgw return (zpool_name_valid(NULL, B_FALSE, name)); 221f18faf3fSek return (zfs_validate_name(NULL, name, type, B_FALSE)); 222fa9e4066Sahrens } 223fa9e4066Sahrens 224e9dbad6fSeschrock /* 225e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 226e9dbad6fSeschrock * properties into a separate nvlist. 227e9dbad6fSeschrock */ 228fac3008cSeschrock static nvlist_t * 229fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 230e9dbad6fSeschrock { 231e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 232e9dbad6fSeschrock nvpair_t *elem; 233e9dbad6fSeschrock nvlist_t *propval; 234fac3008cSeschrock nvlist_t *nvl; 235e9dbad6fSeschrock 236fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 237fac3008cSeschrock (void) no_memory(hdl); 238fac3008cSeschrock return (NULL); 239fac3008cSeschrock } 240e9dbad6fSeschrock 241e9dbad6fSeschrock elem = NULL; 242fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 243e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 244e9dbad6fSeschrock continue; 245e9dbad6fSeschrock 246e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 247fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 248fac3008cSeschrock nvlist_free(nvl); 249fac3008cSeschrock (void) no_memory(hdl); 250fac3008cSeschrock return (NULL); 251fac3008cSeschrock } 252e9dbad6fSeschrock } 253e9dbad6fSeschrock 254fac3008cSeschrock return (nvl); 255e9dbad6fSeschrock } 256e9dbad6fSeschrock 25729ab75c9Srm static zpool_handle_t * 25829ab75c9Srm zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 25929ab75c9Srm { 26029ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 26129ab75c9Srm zpool_handle_t *zph; 26229ab75c9Srm 26329ab75c9Srm if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 26429ab75c9Srm if (hdl->libzfs_pool_handles != NULL) 26529ab75c9Srm zph->zpool_next = hdl->libzfs_pool_handles; 26629ab75c9Srm hdl->libzfs_pool_handles = zph; 26729ab75c9Srm } 26829ab75c9Srm return (zph); 26929ab75c9Srm } 27029ab75c9Srm 27129ab75c9Srm static zpool_handle_t * 27229ab75c9Srm zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 27329ab75c9Srm { 27429ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 27529ab75c9Srm zpool_handle_t *zph = hdl->libzfs_pool_handles; 27629ab75c9Srm 27729ab75c9Srm while ((zph != NULL) && 27829ab75c9Srm (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 27929ab75c9Srm zph = zph->zpool_next; 28029ab75c9Srm return (zph); 28129ab75c9Srm } 28229ab75c9Srm 28329ab75c9Srm /* 28429ab75c9Srm * Returns a handle to the pool that contains the provided dataset. 28529ab75c9Srm * If a handle to that pool already exists then that handle is returned. 28629ab75c9Srm * Otherwise, a new handle is created and added to the list of handles. 28729ab75c9Srm */ 28829ab75c9Srm static zpool_handle_t * 28929ab75c9Srm zpool_handle(zfs_handle_t *zhp) 29029ab75c9Srm { 29129ab75c9Srm char *pool_name; 29229ab75c9Srm int len; 29329ab75c9Srm zpool_handle_t *zph; 29429ab75c9Srm 29529ab75c9Srm len = strcspn(zhp->zfs_name, "/@") + 1; 29629ab75c9Srm pool_name = zfs_alloc(zhp->zfs_hdl, len); 29729ab75c9Srm (void) strlcpy(pool_name, zhp->zfs_name, len); 29829ab75c9Srm 29929ab75c9Srm zph = zpool_find_handle(zhp, pool_name, len); 30029ab75c9Srm if (zph == NULL) 30129ab75c9Srm zph = zpool_add_handle(zhp, pool_name); 30229ab75c9Srm 30329ab75c9Srm free(pool_name); 30429ab75c9Srm return (zph); 30529ab75c9Srm } 30629ab75c9Srm 30729ab75c9Srm void 30829ab75c9Srm zpool_free_handles(libzfs_handle_t *hdl) 30929ab75c9Srm { 31029ab75c9Srm zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 31129ab75c9Srm 31229ab75c9Srm while (zph != NULL) { 31329ab75c9Srm next = zph->zpool_next; 31429ab75c9Srm zpool_close(zph); 31529ab75c9Srm zph = next; 31629ab75c9Srm } 31729ab75c9Srm hdl->libzfs_pool_handles = NULL; 31829ab75c9Srm } 31929ab75c9Srm 320fa9e4066Sahrens /* 321fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 322fa9e4066Sahrens */ 323fa9e4066Sahrens static int 324ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 325fa9e4066Sahrens { 326e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 327fa9e4066Sahrens 328ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 329fa9e4066Sahrens 330ebedde84SEric Taylor while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3317f7322feSeschrock if (errno == ENOMEM) { 332ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 33399653d4eSeschrock return (-1); 334e9dbad6fSeschrock } 3357f7322feSeschrock } else { 3367f7322feSeschrock return (-1); 3377f7322feSeschrock } 3387f7322feSeschrock } 339ebedde84SEric Taylor return (0); 340ebedde84SEric Taylor } 341fa9e4066Sahrens 342ebedde84SEric Taylor static int 343ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 344ebedde84SEric Taylor { 345ebedde84SEric Taylor nvlist_t *allprops, *userprops; 346fa9e4066Sahrens 347ebedde84SEric Taylor zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 348ebedde84SEric Taylor 349ebedde84SEric Taylor if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 35099653d4eSeschrock return (-1); 35199653d4eSeschrock } 352fa9e4066Sahrens 353*14843421SMatthew Ahrens /* 354*14843421SMatthew Ahrens * XXX Why do we store the user props separately, in addition to 355*14843421SMatthew Ahrens * storing them in zfs_props? 356*14843421SMatthew Ahrens */ 357fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 358fac3008cSeschrock nvlist_free(allprops); 359e9dbad6fSeschrock return (-1); 360fac3008cSeschrock } 361fac3008cSeschrock 362fac3008cSeschrock nvlist_free(zhp->zfs_props); 363fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 364fac3008cSeschrock 365fac3008cSeschrock zhp->zfs_props = allprops; 366fac3008cSeschrock zhp->zfs_user_props = userprops; 36799653d4eSeschrock 368fa9e4066Sahrens return (0); 369fa9e4066Sahrens } 370fa9e4066Sahrens 371ebedde84SEric Taylor static int 372ebedde84SEric Taylor get_stats(zfs_handle_t *zhp) 373ebedde84SEric Taylor { 374ebedde84SEric Taylor int rc = 0; 375ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 376ebedde84SEric Taylor 377ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 378ebedde84SEric Taylor return (-1); 379ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) != 0) 380ebedde84SEric Taylor rc = -1; 381ebedde84SEric Taylor else if (put_stats_zhdl(zhp, &zc) != 0) 382ebedde84SEric Taylor rc = -1; 383ebedde84SEric Taylor zcmd_free_nvlists(&zc); 384ebedde84SEric Taylor return (rc); 385ebedde84SEric Taylor } 386ebedde84SEric Taylor 387fa9e4066Sahrens /* 388fa9e4066Sahrens * Refresh the properties currently stored in the handle. 389fa9e4066Sahrens */ 390fa9e4066Sahrens void 391fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 392fa9e4066Sahrens { 393fa9e4066Sahrens (void) get_stats(zhp); 394fa9e4066Sahrens } 395fa9e4066Sahrens 396fa9e4066Sahrens /* 397fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 398fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 399fa9e4066Sahrens */ 400ebedde84SEric Taylor static int 401ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 402fa9e4066Sahrens { 403ecd6cf80Smarks char *logstr; 404ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 405fa9e4066Sahrens 406ecd6cf80Smarks /* 407ecd6cf80Smarks * Preserve history log string. 408ecd6cf80Smarks * any changes performed here will be 409ecd6cf80Smarks * logged as an internal event. 410ecd6cf80Smarks */ 411ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 412ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 413fa9e4066Sahrens 414ebedde84SEric Taylor top: 415ebedde84SEric Taylor if (put_stats_zhdl(zhp, zc) != 0) { 416ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 417ebedde84SEric Taylor return (-1); 418fa9e4066Sahrens } 419fa9e4066Sahrens 420ebedde84SEric Taylor 42131fd60d3Sahrens if (zhp->zfs_dmustats.dds_inconsistent) { 422ebedde84SEric Taylor zfs_cmd_t zc2 = { 0 }; 42331fd60d3Sahrens 42431fd60d3Sahrens /* 42531fd60d3Sahrens * If it is dds_inconsistent, then we've caught it in 42631fd60d3Sahrens * the middle of a 'zfs receive' or 'zfs destroy', and 42731fd60d3Sahrens * it is inconsistent from the ZPL's point of view, so 42831fd60d3Sahrens * can't be mounted. However, it could also be that we 42931fd60d3Sahrens * have crashed in the middle of one of those 43031fd60d3Sahrens * operations, in which case we need to get rid of the 43131fd60d3Sahrens * inconsistent state. We do that by either rolling 43231fd60d3Sahrens * back to the previous snapshot (which will fail if 43331fd60d3Sahrens * there is none), or destroying the filesystem. Note 43431fd60d3Sahrens * that if we are still in the middle of an active 43531fd60d3Sahrens * 'receive' or 'destroy', then the rollback and destroy 43631fd60d3Sahrens * will fail with EBUSY and we will drive on as usual. 43731fd60d3Sahrens */ 43831fd60d3Sahrens 439ebedde84SEric Taylor (void) strlcpy(zc2.zc_name, zhp->zfs_name, 440ebedde84SEric Taylor sizeof (zc2.zc_name)); 44131fd60d3Sahrens 442a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) { 44399653d4eSeschrock (void) zvol_remove_link(hdl, zhp->zfs_name); 444ebedde84SEric Taylor zc2.zc_objset_type = DMU_OST_ZVOL; 44531fd60d3Sahrens } else { 446ebedde84SEric Taylor zc2.zc_objset_type = DMU_OST_ZFS; 44731fd60d3Sahrens } 44831fd60d3Sahrens 44931fd60d3Sahrens /* 450da6c28aaSamw * If we can successfully destroy it, pretend that it 45131fd60d3Sahrens * never existed. 45231fd60d3Sahrens */ 453ebedde84SEric Taylor if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc2) == 0) { 454ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 45531fd60d3Sahrens errno = ENOENT; 456ebedde84SEric Taylor return (-1); 45731fd60d3Sahrens } 458ebedde84SEric Taylor /* If we can successfully roll it back, reset the stats */ 459ebedde84SEric Taylor if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc2) == 0) { 460ebedde84SEric Taylor if (get_stats_ioctl(zhp, zc) != 0) { 461ebedde84SEric Taylor zhp->zfs_hdl->libzfs_log_str = logstr; 462ebedde84SEric Taylor return (-1); 463ebedde84SEric Taylor } 4643cb34c60Sahrens goto top; 465ebedde84SEric Taylor } 46631fd60d3Sahrens } 46731fd60d3Sahrens 468fa9e4066Sahrens /* 469fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 470fa9e4066Sahrens * the high-level type. 471fa9e4066Sahrens */ 472a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 473a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 474a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 475a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 476a2eea2e1Sahrens else 477a2eea2e1Sahrens abort(); 478a2eea2e1Sahrens 479fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 480fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 481fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 482fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 483fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 484fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 485fa9e4066Sahrens else 48699653d4eSeschrock abort(); /* we should never see any other types */ 487fa9e4066Sahrens 488ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 48929ab75c9Srm zhp->zpool_hdl = zpool_handle(zhp); 490ebedde84SEric Taylor return (0); 491ebedde84SEric Taylor } 492ebedde84SEric Taylor 493ebedde84SEric Taylor zfs_handle_t * 494ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 495ebedde84SEric Taylor { 496ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 497ebedde84SEric Taylor 498ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 499ebedde84SEric Taylor 500ebedde84SEric Taylor if (zhp == NULL) 501ebedde84SEric Taylor return (NULL); 502ebedde84SEric Taylor 503ebedde84SEric Taylor zhp->zfs_hdl = hdl; 504ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 505ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 506ebedde84SEric Taylor free(zhp); 507ebedde84SEric Taylor return (NULL); 508ebedde84SEric Taylor } 509ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 510ebedde84SEric Taylor zcmd_free_nvlists(&zc); 511ebedde84SEric Taylor free(zhp); 512ebedde84SEric Taylor return (NULL); 513ebedde84SEric Taylor } 514ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 515ebedde84SEric Taylor free(zhp); 516ebedde84SEric Taylor zhp = NULL; 517ebedde84SEric Taylor } 518ebedde84SEric Taylor zcmd_free_nvlists(&zc); 519ebedde84SEric Taylor return (zhp); 520ebedde84SEric Taylor } 521ebedde84SEric Taylor 522ebedde84SEric Taylor static zfs_handle_t * 523ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 524ebedde84SEric Taylor { 525ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 526ebedde84SEric Taylor 527ebedde84SEric Taylor if (zhp == NULL) 528ebedde84SEric Taylor return (NULL); 529ebedde84SEric Taylor 530ebedde84SEric Taylor zhp->zfs_hdl = hdl; 531ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 532ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 533ebedde84SEric Taylor free(zhp); 534ebedde84SEric Taylor return (NULL); 535ebedde84SEric Taylor } 536fa9e4066Sahrens return (zhp); 537fa9e4066Sahrens } 538fa9e4066Sahrens 539fa9e4066Sahrens /* 540fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 541fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 542fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 543fa9e4066Sahrens */ 544fa9e4066Sahrens zfs_handle_t * 54599653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 546fa9e4066Sahrens { 547fa9e4066Sahrens zfs_handle_t *zhp; 54899653d4eSeschrock char errbuf[1024]; 54999653d4eSeschrock 55099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 55199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 552fa9e4066Sahrens 553fa9e4066Sahrens /* 55499653d4eSeschrock * Validate the name before we even try to open it. 555fa9e4066Sahrens */ 556f18faf3fSek if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 55799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 55899653d4eSeschrock "invalid dataset name")); 55999653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 560fa9e4066Sahrens return (NULL); 561fa9e4066Sahrens } 562fa9e4066Sahrens 563fa9e4066Sahrens /* 564fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 565fa9e4066Sahrens */ 566fa9e4066Sahrens errno = 0; 56799653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 568ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 569fa9e4066Sahrens return (NULL); 570fa9e4066Sahrens } 571fa9e4066Sahrens 572fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 57399653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 57494de1d4cSeschrock zfs_close(zhp); 575fa9e4066Sahrens return (NULL); 576fa9e4066Sahrens } 577fa9e4066Sahrens 578fa9e4066Sahrens return (zhp); 579fa9e4066Sahrens } 580fa9e4066Sahrens 581fa9e4066Sahrens /* 582fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 583fa9e4066Sahrens */ 584fa9e4066Sahrens void 585fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 586fa9e4066Sahrens { 587fa9e4066Sahrens if (zhp->zfs_mntopts) 588fa9e4066Sahrens free(zhp->zfs_mntopts); 589e9dbad6fSeschrock nvlist_free(zhp->zfs_props); 590e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 591fa9e4066Sahrens free(zhp); 592fa9e4066Sahrens } 593fa9e4066Sahrens 594ebedde84SEric Taylor typedef struct mnttab_node { 595ebedde84SEric Taylor struct mnttab mtn_mt; 596ebedde84SEric Taylor avl_node_t mtn_node; 597ebedde84SEric Taylor } mnttab_node_t; 598ebedde84SEric Taylor 599ebedde84SEric Taylor static int 600ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 601ebedde84SEric Taylor { 602ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 603ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 604ebedde84SEric Taylor int rv; 605ebedde84SEric Taylor 606ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 607ebedde84SEric Taylor 608ebedde84SEric Taylor if (rv == 0) 609ebedde84SEric Taylor return (0); 610ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 611ebedde84SEric Taylor } 612ebedde84SEric Taylor 613ebedde84SEric Taylor void 614ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 615ebedde84SEric Taylor { 616ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 617ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 618ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 619b2634b9cSEric Taylor } 620b2634b9cSEric Taylor 621b2634b9cSEric Taylor void 622b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 623b2634b9cSEric Taylor { 624b2634b9cSEric Taylor struct mnttab entry; 625ebedde84SEric Taylor 626ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 627ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 628ebedde84SEric Taylor mnttab_node_t *mtn; 629ebedde84SEric Taylor 630ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 631ebedde84SEric Taylor continue; 632ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 633ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 634ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 635ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 636ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 637ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 638ebedde84SEric Taylor } 639ebedde84SEric Taylor } 640ebedde84SEric Taylor 641ebedde84SEric Taylor void 642ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 643ebedde84SEric Taylor { 644ebedde84SEric Taylor void *cookie = NULL; 645ebedde84SEric Taylor mnttab_node_t *mtn; 646ebedde84SEric Taylor 647ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 648ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 649ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 650ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 651ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 652ebedde84SEric Taylor free(mtn); 653ebedde84SEric Taylor } 654ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 655ebedde84SEric Taylor } 656ebedde84SEric Taylor 657b2634b9cSEric Taylor void 658b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 659b2634b9cSEric Taylor { 660b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 661b2634b9cSEric Taylor } 662b2634b9cSEric Taylor 663ebedde84SEric Taylor int 664ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 665ebedde84SEric Taylor struct mnttab *entry) 666ebedde84SEric Taylor { 667ebedde84SEric Taylor mnttab_node_t find; 668ebedde84SEric Taylor mnttab_node_t *mtn; 669ebedde84SEric Taylor 670b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 671b2634b9cSEric Taylor struct mnttab srch = { 0 }; 672b2634b9cSEric Taylor 673b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 674b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 675b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 676b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 677b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 678b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 679b2634b9cSEric Taylor return (0); 680b2634b9cSEric Taylor else 681b2634b9cSEric Taylor return (ENOENT); 682b2634b9cSEric Taylor } 683b2634b9cSEric Taylor 684ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 685b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 686ebedde84SEric Taylor 687ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 688ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 689ebedde84SEric Taylor if (mtn) { 690ebedde84SEric Taylor *entry = mtn->mtn_mt; 691ebedde84SEric Taylor return (0); 692ebedde84SEric Taylor } 693ebedde84SEric Taylor return (ENOENT); 694ebedde84SEric Taylor } 695ebedde84SEric Taylor 696ebedde84SEric Taylor void 697ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 698ebedde84SEric Taylor const char *mountp, const char *mntopts) 699ebedde84SEric Taylor { 700ebedde84SEric Taylor mnttab_node_t *mtn; 701ebedde84SEric Taylor 702ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 703ebedde84SEric Taylor return; 704ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 705ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 706ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 707ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 708ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 709ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 710ebedde84SEric Taylor } 711ebedde84SEric Taylor 712ebedde84SEric Taylor void 713ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 714ebedde84SEric Taylor { 715ebedde84SEric Taylor mnttab_node_t find; 716ebedde84SEric Taylor mnttab_node_t *ret; 717ebedde84SEric Taylor 718ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 719ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 720ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 721ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 722ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 723ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 724ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 725ebedde84SEric Taylor free(ret); 726ebedde84SEric Taylor } 727ebedde84SEric Taylor } 728ebedde84SEric Taylor 7297b97dc1aSrm int 7307b97dc1aSrm zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7317b97dc1aSrm { 73229ab75c9Srm zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7337b97dc1aSrm 7347b97dc1aSrm if (zpool_handle == NULL) 7357b97dc1aSrm return (-1); 7367b97dc1aSrm 7377b97dc1aSrm *spa_version = zpool_get_prop_int(zpool_handle, 7387b97dc1aSrm ZPOOL_PROP_VERSION, NULL); 7397b97dc1aSrm return (0); 7407b97dc1aSrm } 7417b97dc1aSrm 7427b97dc1aSrm /* 7437b97dc1aSrm * The choice of reservation property depends on the SPA version. 7447b97dc1aSrm */ 7457b97dc1aSrm static int 7467b97dc1aSrm zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7477b97dc1aSrm { 7487b97dc1aSrm int spa_version; 7497b97dc1aSrm 7507b97dc1aSrm if (zfs_spa_version(zhp, &spa_version) < 0) 7517b97dc1aSrm return (-1); 7527b97dc1aSrm 7537b97dc1aSrm if (spa_version >= SPA_VERSION_REFRESERVATION) 7547b97dc1aSrm *resv_prop = ZFS_PROP_REFRESERVATION; 7557b97dc1aSrm else 7567b97dc1aSrm *resv_prop = ZFS_PROP_RESERVATION; 7577b97dc1aSrm 7587b97dc1aSrm return (0); 7597b97dc1aSrm } 7607b97dc1aSrm 761e9dbad6fSeschrock /* 762e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 763e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 764e9dbad6fSeschrock * strings. 765e9dbad6fSeschrock */ 7660a48a24eStimh nvlist_t * 7670a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 768990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 769e9dbad6fSeschrock { 770e9dbad6fSeschrock nvpair_t *elem; 771e9dbad6fSeschrock uint64_t intval; 772e9dbad6fSeschrock char *strval; 773990b4856Slling zfs_prop_t prop; 774e9dbad6fSeschrock nvlist_t *ret; 775da6c28aaSamw int chosen_normal = -1; 776da6c28aaSamw int chosen_utf = -1; 777e9dbad6fSeschrock 778990b4856Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 779990b4856Slling (void) no_memory(hdl); 780990b4856Slling return (NULL); 781e9dbad6fSeschrock } 782e9dbad6fSeschrock 783*14843421SMatthew Ahrens /* 784*14843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 785*14843421SMatthew Ahrens */ 786*14843421SMatthew Ahrens 787e9dbad6fSeschrock elem = NULL; 788e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 789990b4856Slling const char *propname = nvpair_name(elem); 790e9dbad6fSeschrock 791*14843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 792*14843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 793990b4856Slling /* 794*14843421SMatthew Ahrens * This is a user property: make sure it's a 795990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 796990b4856Slling */ 797990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 798990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 799990b4856Slling "'%s' must be a string"), propname); 800990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 801990b4856Slling goto error; 802990b4856Slling } 803990b4856Slling 804990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 805990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 806990b4856Slling "property name '%s' is too long"), 807990b4856Slling propname); 808990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 809990b4856Slling goto error; 810fa9e4066Sahrens } 811fa9e4066Sahrens 812e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 813e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 814e9dbad6fSeschrock (void) no_memory(hdl); 815e9dbad6fSeschrock goto error; 816e9dbad6fSeschrock } 817e9dbad6fSeschrock continue; 818e9dbad6fSeschrock } 819e9dbad6fSeschrock 820*14843421SMatthew Ahrens /* 821*14843421SMatthew Ahrens * Currently, only user properties can be modified on 822*14843421SMatthew Ahrens * snapshots. 823*14843421SMatthew Ahrens */ 824bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 825bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 826bb0ade09Sahrens "this property can not be modified for snapshots")); 827bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 828bb0ade09Sahrens goto error; 829bb0ade09Sahrens } 830bb0ade09Sahrens 831*14843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 832*14843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 833*14843421SMatthew Ahrens char newpropname[128]; 834*14843421SMatthew Ahrens char domain[128]; 835*14843421SMatthew Ahrens uint64_t rid; 836*14843421SMatthew Ahrens uint64_t valary[3]; 837*14843421SMatthew Ahrens 838*14843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 839*14843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 840*14843421SMatthew Ahrens zfs_error_aux(hdl, 841*14843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 842*14843421SMatthew Ahrens "'%s' has an invalid user/group name"), 843*14843421SMatthew Ahrens propname); 844*14843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 845*14843421SMatthew Ahrens goto error; 846*14843421SMatthew Ahrens } 847*14843421SMatthew Ahrens 848*14843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 849*14843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 850*14843421SMatthew Ahrens zfs_error_aux(hdl, 851*14843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 852*14843421SMatthew Ahrens propname); 853*14843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 854*14843421SMatthew Ahrens errbuf); 855*14843421SMatthew Ahrens goto error; 856*14843421SMatthew Ahrens } 857*14843421SMatthew Ahrens 858*14843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 859*14843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 860*14843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 861*14843421SMatthew Ahrens intval = 0; 862*14843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 863*14843421SMatthew Ahrens strval, &intval) != 0) { 864*14843421SMatthew Ahrens (void) zfs_error(hdl, 865*14843421SMatthew Ahrens EZFS_BADPROP, errbuf); 866*14843421SMatthew Ahrens goto error; 867*14843421SMatthew Ahrens } 868*14843421SMatthew Ahrens } else if (nvpair_type(elem) == 869*14843421SMatthew Ahrens DATA_TYPE_UINT64) { 870*14843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 871*14843421SMatthew Ahrens if (intval == 0) { 872*14843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 873*14843421SMatthew Ahrens "use 'none' to disable " 874*14843421SMatthew Ahrens "userquota/groupquota")); 875*14843421SMatthew Ahrens goto error; 876*14843421SMatthew Ahrens } 877*14843421SMatthew Ahrens } else { 878*14843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 879*14843421SMatthew Ahrens "'%s' must be a number"), propname); 880*14843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 881*14843421SMatthew Ahrens goto error; 882*14843421SMatthew Ahrens } 883*14843421SMatthew Ahrens 884*14843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 885*14843421SMatthew Ahrens "%s%s", zfs_userquota_prop_prefixes[uqtype], 886*14843421SMatthew Ahrens domain); 887*14843421SMatthew Ahrens valary[0] = uqtype; 888*14843421SMatthew Ahrens valary[1] = rid; 889*14843421SMatthew Ahrens valary[2] = intval; 890*14843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 891*14843421SMatthew Ahrens valary, 3) != 0) { 892*14843421SMatthew Ahrens (void) no_memory(hdl); 893*14843421SMatthew Ahrens goto error; 894*14843421SMatthew Ahrens } 895*14843421SMatthew Ahrens continue; 896*14843421SMatthew Ahrens } 897*14843421SMatthew Ahrens 898*14843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 899*14843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 900*14843421SMatthew Ahrens "invalid property '%s'"), propname); 901*14843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 902*14843421SMatthew Ahrens goto error; 903*14843421SMatthew Ahrens } 904*14843421SMatthew Ahrens 905e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 906e9dbad6fSeschrock zfs_error_aux(hdl, 907e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 908e9dbad6fSeschrock "apply to datasets of this type"), propname); 909e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 910e9dbad6fSeschrock goto error; 911e9dbad6fSeschrock } 912e9dbad6fSeschrock 913e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 914da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 915e9dbad6fSeschrock zfs_error_aux(hdl, 916e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 917e9dbad6fSeschrock propname); 918e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 919e9dbad6fSeschrock goto error; 920e9dbad6fSeschrock } 921e9dbad6fSeschrock 922990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 923990b4856Slling &strval, &intval, errbuf) != 0) 924e9dbad6fSeschrock goto error; 925fa9e4066Sahrens 926e9dbad6fSeschrock /* 927e9dbad6fSeschrock * Perform some additional checks for specific properties. 928e9dbad6fSeschrock */ 929e9dbad6fSeschrock switch (prop) { 930e7437265Sahrens case ZFS_PROP_VERSION: 931e7437265Sahrens { 932e7437265Sahrens int version; 933e7437265Sahrens 934e7437265Sahrens if (zhp == NULL) 935e7437265Sahrens break; 936e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 937e7437265Sahrens if (intval < version) { 938e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 939e7437265Sahrens "Can not downgrade; already at version %u"), 940e7437265Sahrens version); 941e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 942e7437265Sahrens goto error; 943e7437265Sahrens } 944e7437265Sahrens break; 945e7437265Sahrens } 946e7437265Sahrens 947e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 948e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 949e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 950e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 951e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 95299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 953e9dbad6fSeschrock "'%s' must be power of 2 from %u " 954e9dbad6fSeschrock "to %uk"), propname, 955e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 956e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 957e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 958e9dbad6fSeschrock goto error; 959fa9e4066Sahrens } 960fa9e4066Sahrens break; 961fa9e4066Sahrens 962f3861e1aSahl case ZFS_PROP_SHAREISCSI: 963f3861e1aSahl if (strcmp(strval, "off") != 0 && 964f3861e1aSahl strcmp(strval, "on") != 0 && 965f3861e1aSahl strcmp(strval, "type=disk") != 0) { 966f3861e1aSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 967f3861e1aSahl "'%s' must be 'on', 'off', or 'type=disk'"), 968f3861e1aSahl propname); 969f3861e1aSahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 970f3861e1aSahl goto error; 971f3861e1aSahl } 972f3861e1aSahl 973f3861e1aSahl break; 974f3861e1aSahl 975e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 97689eef05eSrm { 97789eef05eSrm namecheck_err_t why; 97889eef05eSrm 979e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 980e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 981e9dbad6fSeschrock break; 982fa9e4066Sahrens 98389eef05eSrm if (mountpoint_namecheck(strval, &why)) { 98489eef05eSrm switch (why) { 98589eef05eSrm case NAME_ERR_LEADING_SLASH: 98689eef05eSrm zfs_error_aux(hdl, 98789eef05eSrm dgettext(TEXT_DOMAIN, 98889eef05eSrm "'%s' must be an absolute path, " 98989eef05eSrm "'none', or 'legacy'"), propname); 99089eef05eSrm break; 99189eef05eSrm case NAME_ERR_TOOLONG: 99289eef05eSrm zfs_error_aux(hdl, 99389eef05eSrm dgettext(TEXT_DOMAIN, 99489eef05eSrm "component of '%s' is too long"), 99589eef05eSrm propname); 99689eef05eSrm break; 99789eef05eSrm } 998e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 999e9dbad6fSeschrock goto error; 1000fa9e4066Sahrens } 100189eef05eSrm } 100289eef05eSrm 1003f3861e1aSahl /*FALLTHRU*/ 1004fa9e4066Sahrens 1005da6c28aaSamw case ZFS_PROP_SHARESMB: 1006f3861e1aSahl case ZFS_PROP_SHARENFS: 1007f3861e1aSahl /* 1008da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1009da6c28aaSamw * properties, check if it can be set in a 1010da6c28aaSamw * global/non-global zone based on 1011f3861e1aSahl * the zoned property value: 1012f3861e1aSahl * 1013f3861e1aSahl * global zone non-global zone 1014f3861e1aSahl * -------------------------------------------------- 1015f3861e1aSahl * zoned=on mountpoint (no) mountpoint (yes) 1016f3861e1aSahl * sharenfs (no) sharenfs (no) 1017da6c28aaSamw * sharesmb (no) sharesmb (no) 1018f3861e1aSahl * 1019f3861e1aSahl * zoned=off mountpoint (yes) N/A 1020f3861e1aSahl * sharenfs (yes) 1021da6c28aaSamw * sharesmb (yes) 1022f3861e1aSahl */ 1023e9dbad6fSeschrock if (zoned) { 1024e9dbad6fSeschrock if (getzoneid() == GLOBAL_ZONEID) { 1025e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1026e9dbad6fSeschrock "'%s' cannot be set on " 1027e9dbad6fSeschrock "dataset in a non-global zone"), 1028e9dbad6fSeschrock propname); 1029e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1030e9dbad6fSeschrock errbuf); 1031e9dbad6fSeschrock goto error; 1032da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1033da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 1034e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1035e9dbad6fSeschrock "'%s' cannot be set in " 1036e9dbad6fSeschrock "a non-global zone"), propname); 1037e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1038e9dbad6fSeschrock errbuf); 1039e9dbad6fSeschrock goto error; 1040fa9e4066Sahrens } 1041e9dbad6fSeschrock } else if (getzoneid() != GLOBAL_ZONEID) { 1042e9dbad6fSeschrock /* 1043e9dbad6fSeschrock * If zoned property is 'off', this must be in 1044*14843421SMatthew Ahrens * a global zone. If not, something is wrong. 1045e9dbad6fSeschrock */ 1046e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1047e9dbad6fSeschrock "'%s' cannot be set while dataset " 1048e9dbad6fSeschrock "'zoned' property is set"), propname); 1049e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1050e9dbad6fSeschrock goto error; 1051fa9e4066Sahrens } 1052f3861e1aSahl 105367331909Sdougm /* 105467331909Sdougm * At this point, it is legitimate to set the 105567331909Sdougm * property. Now we want to make sure that the 105667331909Sdougm * property value is valid if it is sharenfs. 105767331909Sdougm */ 1058da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1059da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 1060fac3008cSeschrock strcmp(strval, "on") != 0 && 1061fac3008cSeschrock strcmp(strval, "off") != 0) { 1062da6c28aaSamw zfs_share_proto_t proto; 1063da6c28aaSamw 1064da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1065da6c28aaSamw proto = PROTO_SMB; 1066da6c28aaSamw else 1067da6c28aaSamw proto = PROTO_NFS; 106867331909Sdougm 106967331909Sdougm /* 1070da6c28aaSamw * Must be an valid sharing protocol 1071da6c28aaSamw * option string so init the libshare 1072da6c28aaSamw * in order to enable the parser and 1073da6c28aaSamw * then parse the options. We use the 1074da6c28aaSamw * control API since we don't care about 1075da6c28aaSamw * the current configuration and don't 107667331909Sdougm * want the overhead of loading it 107767331909Sdougm * until we actually do something. 107867331909Sdougm */ 107967331909Sdougm 1080fac3008cSeschrock if (zfs_init_libshare(hdl, 1081fac3008cSeschrock SA_INIT_CONTROL_API) != SA_OK) { 1082fac3008cSeschrock /* 1083fac3008cSeschrock * An error occurred so we can't do 1084fac3008cSeschrock * anything 1085fac3008cSeschrock */ 1086fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1087fac3008cSeschrock "'%s' cannot be set: problem " 1088fac3008cSeschrock "in share initialization"), 1089fac3008cSeschrock propname); 1090fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1091fac3008cSeschrock errbuf); 1092fac3008cSeschrock goto error; 1093fac3008cSeschrock } 109467331909Sdougm 1095da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 1096fac3008cSeschrock /* 1097fac3008cSeschrock * There was an error in parsing so 1098fac3008cSeschrock * deal with it by issuing an error 1099fac3008cSeschrock * message and leaving after 1100fac3008cSeschrock * uninitializing the the libshare 1101fac3008cSeschrock * interface. 1102fac3008cSeschrock */ 1103fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1104fac3008cSeschrock "'%s' cannot be set to invalid " 1105fac3008cSeschrock "options"), propname); 1106fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1107fac3008cSeschrock errbuf); 1108fac3008cSeschrock zfs_uninit_libshare(hdl); 1109fac3008cSeschrock goto error; 1110fac3008cSeschrock } 111167331909Sdougm zfs_uninit_libshare(hdl); 111267331909Sdougm } 111367331909Sdougm 1114da6c28aaSamw break; 1115da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1116da6c28aaSamw chosen_utf = (int)intval; 1117da6c28aaSamw break; 1118da6c28aaSamw case ZFS_PROP_NORMALIZE: 1119da6c28aaSamw chosen_normal = (int)intval; 1120f3861e1aSahl break; 1121e9dbad6fSeschrock } 1122fa9e4066Sahrens 1123e9dbad6fSeschrock /* 1124e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1125e9dbad6fSeschrock * checks to enforce. 1126e9dbad6fSeschrock */ 1127e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1128e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1129e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1130e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1131e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1132e9dbad6fSeschrock char buf[64]; 1133e9dbad6fSeschrock 1134e9dbad6fSeschrock switch (prop) { 1135e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1136a9799022Sck case ZFS_PROP_REFRESERVATION: 1137e9dbad6fSeschrock if (intval > volsize) { 1138e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1139e9dbad6fSeschrock "'%s' is greater than current " 1140e9dbad6fSeschrock "volume size"), propname); 1141e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1142e9dbad6fSeschrock errbuf); 1143e9dbad6fSeschrock goto error; 1144e9dbad6fSeschrock } 1145e9dbad6fSeschrock break; 1146e9dbad6fSeschrock 1147e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1148e9dbad6fSeschrock if (intval % blocksize != 0) { 1149e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1150e9dbad6fSeschrock sizeof (buf)); 1151e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1152e9dbad6fSeschrock "'%s' must be a multiple of " 1153e9dbad6fSeschrock "volume block size (%s)"), 1154e9dbad6fSeschrock propname, buf); 1155e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1156e9dbad6fSeschrock errbuf); 1157e9dbad6fSeschrock goto error; 1158e9dbad6fSeschrock } 1159e9dbad6fSeschrock 1160e9dbad6fSeschrock if (intval == 0) { 1161e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1162e9dbad6fSeschrock "'%s' cannot be zero"), 1163e9dbad6fSeschrock propname); 1164e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1165e9dbad6fSeschrock errbuf); 1166e9dbad6fSeschrock goto error; 1167e9dbad6fSeschrock } 1168f3861e1aSahl break; 1169fa9e4066Sahrens } 1170e9dbad6fSeschrock } 1171e9dbad6fSeschrock } 1172fa9e4066Sahrens 1173da6c28aaSamw /* 1174da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1175da6c28aaSamw * enforce rejection of non-UTF8 names. 1176da6c28aaSamw * 1177da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1178da6c28aaSamw * was explicitly not chosen, it is an error. 1179da6c28aaSamw */ 1180de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1181da6c28aaSamw if (nvlist_add_uint64(ret, 1182da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1183da6c28aaSamw (void) no_memory(hdl); 1184da6c28aaSamw goto error; 1185da6c28aaSamw } 1186de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1187da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1188da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1189da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1190da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1191da6c28aaSamw goto error; 1192da6c28aaSamw } 1193da6c28aaSamw 1194e9dbad6fSeschrock /* 1195e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1196e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1197e9dbad6fSeschrock */ 1198e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 1199e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1200e9dbad6fSeschrock &intval) == 0) { 1201e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 1202e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1203a9b821a0Sck uint64_t old_reservation; 1204e9dbad6fSeschrock uint64_t new_reservation; 1205a9b821a0Sck zfs_prop_t resv_prop; 1206a9b821a0Sck 12077b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1208a9b821a0Sck goto error; 1209a9b821a0Sck old_reservation = zfs_prop_get_int(zhp, resv_prop); 1210e9dbad6fSeschrock 1211e9dbad6fSeschrock if (old_volsize == old_reservation && 1212a9b821a0Sck nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 1213e9dbad6fSeschrock &new_reservation) != 0) { 1214e9dbad6fSeschrock if (nvlist_add_uint64(ret, 1215a9b821a0Sck zfs_prop_to_name(resv_prop), intval) != 0) { 1216e9dbad6fSeschrock (void) no_memory(hdl); 1217e9dbad6fSeschrock goto error; 1218e9dbad6fSeschrock } 1219fa9e4066Sahrens } 1220fa9e4066Sahrens } 1221e9dbad6fSeschrock return (ret); 1222fa9e4066Sahrens 1223e9dbad6fSeschrock error: 1224e9dbad6fSeschrock nvlist_free(ret); 1225e9dbad6fSeschrock return (NULL); 1226fa9e4066Sahrens } 1227fa9e4066Sahrens 1228fa9e4066Sahrens /* 1229fa9e4066Sahrens * Given a property name and value, set the property for the given dataset. 1230fa9e4066Sahrens */ 1231fa9e4066Sahrens int 1232e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1233fa9e4066Sahrens { 1234fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1235e9dbad6fSeschrock int ret = -1; 1236e9dbad6fSeschrock prop_changelist_t *cl = NULL; 123799653d4eSeschrock char errbuf[1024]; 123899653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1239e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1240e9dbad6fSeschrock zfs_prop_t prop; 12410068372bSMark J Musante boolean_t do_prefix; 12420068372bSMark J Musante uint64_t idx; 124399653d4eSeschrock 124499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1245e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 124699653d4eSeschrock zhp->zfs_name); 124799653d4eSeschrock 1248e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1249e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1250e9dbad6fSeschrock (void) no_memory(hdl); 1251e9dbad6fSeschrock goto error; 1252fa9e4066Sahrens } 1253fa9e4066Sahrens 12540a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1255e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1256e9dbad6fSeschrock goto error; 1257990b4856Slling 1258e9dbad6fSeschrock nvlist_free(nvl); 1259e9dbad6fSeschrock nvl = realprops; 1260e9dbad6fSeschrock 1261e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1262e9dbad6fSeschrock 12630069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1264e9dbad6fSeschrock goto error; 1265fa9e4066Sahrens 1266fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 126799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 126899653d4eSeschrock "child dataset with inherited mountpoint is used " 126999653d4eSeschrock "in a non-global zone")); 127099653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1271fa9e4066Sahrens goto error; 1272fa9e4066Sahrens } 1273fa9e4066Sahrens 12740068372bSMark J Musante /* 12750068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 12760068372bSMark J Musante * then we want to prevent unmounting & remounting it. 12770068372bSMark J Musante */ 12780068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 12790068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 12800068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1281a227b7f4Shs 1282a227b7f4Shs if (do_prefix && (ret = changelist_prefix(cl)) != 0) 12830068372bSMark J Musante goto error; 1284fa9e4066Sahrens 1285fa9e4066Sahrens /* 1286fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1287fa9e4066Sahrens */ 1288fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1289fa9e4066Sahrens 1290990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1291e9dbad6fSeschrock goto error; 1292e9dbad6fSeschrock 1293ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1294743a77edSAlan Wright 1295fa9e4066Sahrens if (ret != 0) { 1296fa9e4066Sahrens switch (errno) { 1297fa9e4066Sahrens 1298fa9e4066Sahrens case ENOSPC: 1299fa9e4066Sahrens /* 1300fa9e4066Sahrens * For quotas and reservations, ENOSPC indicates 1301fa9e4066Sahrens * something different; setting a quota or reservation 1302fa9e4066Sahrens * doesn't use any disk space. 1303fa9e4066Sahrens */ 1304fa9e4066Sahrens switch (prop) { 1305fa9e4066Sahrens case ZFS_PROP_QUOTA: 1306a9799022Sck case ZFS_PROP_REFQUOTA: 130799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130899653d4eSeschrock "size is less than current used or " 130999653d4eSeschrock "reserved space")); 131099653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1311fa9e4066Sahrens break; 1312fa9e4066Sahrens 1313fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1314a9799022Sck case ZFS_PROP_REFRESERVATION: 131599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 131699653d4eSeschrock "size is greater than available space")); 131799653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1318fa9e4066Sahrens break; 1319fa9e4066Sahrens 1320fa9e4066Sahrens default: 132199653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1322fa9e4066Sahrens break; 1323fa9e4066Sahrens } 1324fa9e4066Sahrens break; 1325fa9e4066Sahrens 1326fa9e4066Sahrens case EBUSY: 132799653d4eSeschrock if (prop == ZFS_PROP_VOLBLOCKSIZE) 132899653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); 132999653d4eSeschrock else 1330e9dbad6fSeschrock (void) zfs_standard_error(hdl, EBUSY, errbuf); 1331fa9e4066Sahrens break; 1332fa9e4066Sahrens 13332a79c5feSlling case EROFS: 133499653d4eSeschrock (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 13352a79c5feSlling break; 13362a79c5feSlling 1337c9431fa1Sahl case ENOTSUP: 1338c9431fa1Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13399e6eda55Smarks "pool and or dataset must be upgraded to set this " 134040feaa91Sahrens "property or value")); 1341c9431fa1Sahl (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1342c9431fa1Sahl break; 1343c9431fa1Sahl 134415e6edf1Sgw case ERANGE: 134515e6edf1Sgw if (prop == ZFS_PROP_COMPRESSION) { 134615e6edf1Sgw (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 134715e6edf1Sgw "property setting is not allowed on " 134815e6edf1Sgw "bootable datasets")); 134915e6edf1Sgw (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 135015e6edf1Sgw } else { 135115e6edf1Sgw (void) zfs_standard_error(hdl, errno, errbuf); 135215e6edf1Sgw } 135315e6edf1Sgw break; 135415e6edf1Sgw 1355fa9e4066Sahrens case EOVERFLOW: 1356fa9e4066Sahrens /* 1357fa9e4066Sahrens * This platform can't address a volume this big. 1358fa9e4066Sahrens */ 1359fa9e4066Sahrens #ifdef _ILP32 1360fa9e4066Sahrens if (prop == ZFS_PROP_VOLSIZE) { 136199653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1362fa9e4066Sahrens break; 1363fa9e4066Sahrens } 1364fa9e4066Sahrens #endif 136599653d4eSeschrock /* FALLTHROUGH */ 1366fa9e4066Sahrens default: 136799653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1368fa9e4066Sahrens } 1369fa9e4066Sahrens } else { 1370a227b7f4Shs if (do_prefix) 1371a227b7f4Shs ret = changelist_postfix(cl); 1372a227b7f4Shs 1373fa9e4066Sahrens /* 1374fa9e4066Sahrens * Refresh the statistics so the new property value 1375fa9e4066Sahrens * is reflected. 1376fa9e4066Sahrens */ 1377a227b7f4Shs if (ret == 0) 1378e9dbad6fSeschrock (void) get_stats(zhp); 1379fa9e4066Sahrens } 1380fa9e4066Sahrens 1381fa9e4066Sahrens error: 1382e9dbad6fSeschrock nvlist_free(nvl); 1383e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1384e9dbad6fSeschrock if (cl) 1385e9dbad6fSeschrock changelist_free(cl); 1386fa9e4066Sahrens return (ret); 1387fa9e4066Sahrens } 1388fa9e4066Sahrens 1389fa9e4066Sahrens /* 1390fa9e4066Sahrens * Given a property, inherit the value from the parent dataset. 1391fa9e4066Sahrens */ 1392fa9e4066Sahrens int 1393e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) 1394fa9e4066Sahrens { 1395fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1396fa9e4066Sahrens int ret; 1397fa9e4066Sahrens prop_changelist_t *cl; 139899653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 139999653d4eSeschrock char errbuf[1024]; 1400e9dbad6fSeschrock zfs_prop_t prop; 140199653d4eSeschrock 140299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 140399653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1404fa9e4066Sahrens 1405990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1406e9dbad6fSeschrock /* 1407e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1408e9dbad6fSeschrock * small, so just do it here. 1409e9dbad6fSeschrock */ 1410e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1411e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1412e9dbad6fSeschrock "invalid property")); 1413e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1414e9dbad6fSeschrock } 1415e9dbad6fSeschrock 1416e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1417e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1418e9dbad6fSeschrock 1419e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1420e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1421e9dbad6fSeschrock 1422e9dbad6fSeschrock return (0); 1423e9dbad6fSeschrock } 1424e9dbad6fSeschrock 1425fa9e4066Sahrens /* 1426fa9e4066Sahrens * Verify that this property is inheritable. 1427fa9e4066Sahrens */ 142899653d4eSeschrock if (zfs_prop_readonly(prop)) 142999653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1430fa9e4066Sahrens 143199653d4eSeschrock if (!zfs_prop_inheritable(prop)) 143299653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1433fa9e4066Sahrens 1434fa9e4066Sahrens /* 1435fa9e4066Sahrens * Check to see if the value applies to this type 1436fa9e4066Sahrens */ 143799653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 143899653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1439fa9e4066Sahrens 1440bf7c2d40Srm /* 1441bf7c2d40Srm * Normalize the name, to get rid of shorthand abbrevations. 1442bf7c2d40Srm */ 1443bf7c2d40Srm propname = zfs_prop_to_name(prop); 1444fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1445e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1446fa9e4066Sahrens 1447fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1448fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 144999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 145099653d4eSeschrock "dataset is used in a non-global zone")); 145199653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1452fa9e4066Sahrens } 1453fa9e4066Sahrens 1454fa9e4066Sahrens /* 1455fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1456fa9e4066Sahrens */ 14570069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1458fa9e4066Sahrens return (-1); 1459fa9e4066Sahrens 1460fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 146199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 146299653d4eSeschrock "child dataset with inherited mountpoint is used " 146399653d4eSeschrock "in a non-global zone")); 146499653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1465fa9e4066Sahrens goto error; 1466fa9e4066Sahrens } 1467fa9e4066Sahrens 1468fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1469fa9e4066Sahrens goto error; 1470fa9e4066Sahrens 1471e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 147299653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1473fa9e4066Sahrens } else { 1474fa9e4066Sahrens 1475efc555ebSnd if ((ret = changelist_postfix(cl)) != 0) 1476fa9e4066Sahrens goto error; 1477fa9e4066Sahrens 1478fa9e4066Sahrens /* 1479fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1480fa9e4066Sahrens */ 1481fa9e4066Sahrens (void) get_stats(zhp); 1482fa9e4066Sahrens } 1483fa9e4066Sahrens 1484fa9e4066Sahrens error: 1485fa9e4066Sahrens changelist_free(cl); 1486fa9e4066Sahrens return (ret); 1487fa9e4066Sahrens } 1488fa9e4066Sahrens 14897f7322feSeschrock /* 14907f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 14917f7322feSeschrock * extract them appropriately. 14927f7322feSeschrock */ 14937f7322feSeschrock static uint64_t 14947f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 14957f7322feSeschrock { 14967f7322feSeschrock nvlist_t *nv; 14977f7322feSeschrock uint64_t value; 14987f7322feSeschrock 1499a2eea2e1Sahrens *source = NULL; 15007f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15017f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1502990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1503990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15047f7322feSeschrock } else { 15052e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15062e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15077f7322feSeschrock value = zfs_prop_default_numeric(prop); 15087f7322feSeschrock *source = ""; 15097f7322feSeschrock } 15107f7322feSeschrock 15117f7322feSeschrock return (value); 15127f7322feSeschrock } 15137f7322feSeschrock 15147f7322feSeschrock static char * 15157f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15167f7322feSeschrock { 15177f7322feSeschrock nvlist_t *nv; 15187f7322feSeschrock char *value; 15197f7322feSeschrock 1520a2eea2e1Sahrens *source = NULL; 15217f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15227f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1523990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1524990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15257f7322feSeschrock } else { 15262e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15272e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15287f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15297f7322feSeschrock value = ""; 15307f7322feSeschrock *source = ""; 15317f7322feSeschrock } 15327f7322feSeschrock 15337f7322feSeschrock return (value); 15347f7322feSeschrock } 15357f7322feSeschrock 1536fa9e4066Sahrens /* 1537fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1538fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1539fa9e4066Sahrens * 1540fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1541fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1542fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1543fa9e4066Sahrens * the source "temporary". 1544fa9e4066Sahrens */ 154599653d4eSeschrock static int 1546990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 154799653d4eSeschrock char **source, uint64_t *val) 1548fa9e4066Sahrens { 1549bd00f61bSrm zfs_cmd_t zc = { 0 }; 155096510749Stimh nvlist_t *zplprops = NULL; 1551fa9e4066Sahrens struct mnttab mnt; 15523ccfa83cSahrens char *mntopt_on = NULL; 15533ccfa83cSahrens char *mntopt_off = NULL; 1554fa9e4066Sahrens 1555fa9e4066Sahrens *source = NULL; 1556fa9e4066Sahrens 15573ccfa83cSahrens switch (prop) { 15583ccfa83cSahrens case ZFS_PROP_ATIME: 15593ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 15603ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 15613ccfa83cSahrens break; 15623ccfa83cSahrens 15633ccfa83cSahrens case ZFS_PROP_DEVICES: 15643ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 15653ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 15663ccfa83cSahrens break; 15673ccfa83cSahrens 15683ccfa83cSahrens case ZFS_PROP_EXEC: 15693ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 15703ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 15713ccfa83cSahrens break; 15723ccfa83cSahrens 15733ccfa83cSahrens case ZFS_PROP_READONLY: 15743ccfa83cSahrens mntopt_on = MNTOPT_RO; 15753ccfa83cSahrens mntopt_off = MNTOPT_RW; 15763ccfa83cSahrens break; 15773ccfa83cSahrens 15783ccfa83cSahrens case ZFS_PROP_SETUID: 15793ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 15803ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 15813ccfa83cSahrens break; 15823ccfa83cSahrens 15833ccfa83cSahrens case ZFS_PROP_XATTR: 15843ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 15853ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 15863ccfa83cSahrens break; 1587da6c28aaSamw 1588da6c28aaSamw case ZFS_PROP_NBMAND: 1589da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1590da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1591da6c28aaSamw break; 15923ccfa83cSahrens } 15933ccfa83cSahrens 15943bb79becSeschrock /* 15953bb79becSeschrock * Because looking up the mount options is potentially expensive 15963bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 15973bb79becSeschrock * we're looking up a property which requires its presence. 15983bb79becSeschrock */ 15993bb79becSeschrock if (!zhp->zfs_mntcheck && 16003ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1601ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1602ebedde84SEric Taylor struct mnttab entry; 16033bb79becSeschrock 1604ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1605ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 16063ccfa83cSahrens entry.mnt_mntopts); 16073ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 16083ccfa83cSahrens return (-1); 16093ccfa83cSahrens } 16103bb79becSeschrock 16113bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 16123bb79becSeschrock } 16133bb79becSeschrock 1614fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1615fa9e4066Sahrens mnt.mnt_mntopts = ""; 1616fa9e4066Sahrens else 1617fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1618fa9e4066Sahrens 1619fa9e4066Sahrens switch (prop) { 1620fa9e4066Sahrens case ZFS_PROP_ATIME: 1621fa9e4066Sahrens case ZFS_PROP_DEVICES: 1622fa9e4066Sahrens case ZFS_PROP_EXEC: 16233ccfa83cSahrens case ZFS_PROP_READONLY: 16243ccfa83cSahrens case ZFS_PROP_SETUID: 16253ccfa83cSahrens case ZFS_PROP_XATTR: 1626da6c28aaSamw case ZFS_PROP_NBMAND: 162799653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1628fa9e4066Sahrens 16293ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 163099653d4eSeschrock *val = B_TRUE; 1631fa9e4066Sahrens if (src) 1632990b4856Slling *src = ZPROP_SRC_TEMPORARY; 16333ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 163499653d4eSeschrock *val = B_FALSE; 1635fa9e4066Sahrens if (src) 1636990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1637fa9e4066Sahrens } 163899653d4eSeschrock break; 1639fa9e4066Sahrens 16403ccfa83cSahrens case ZFS_PROP_CANMOUNT: 164199653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1642a227b7f4Shs if (*val != ZFS_CANMOUNT_ON) 1643fda77a98Srm *source = zhp->zfs_name; 1644fda77a98Srm else 1645fda77a98Srm *source = ""; /* default */ 164699653d4eSeschrock break; 1647fa9e4066Sahrens 1648fa9e4066Sahrens case ZFS_PROP_QUOTA: 1649a9799022Sck case ZFS_PROP_REFQUOTA: 1650fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1651a9799022Sck case ZFS_PROP_REFRESERVATION: 1652a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 1653a2eea2e1Sahrens if (*val == 0) 1654fa9e4066Sahrens *source = ""; /* default */ 1655fa9e4066Sahrens else 1656fa9e4066Sahrens *source = zhp->zfs_name; 165799653d4eSeschrock break; 1658fa9e4066Sahrens 1659fa9e4066Sahrens case ZFS_PROP_MOUNTED: 166099653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 166199653d4eSeschrock break; 1662fa9e4066Sahrens 166339c23413Seschrock case ZFS_PROP_NUMCLONES: 166439c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 166539c23413Seschrock break; 166639c23413Seschrock 1667bd00f61bSrm case ZFS_PROP_VERSION: 1668de8267e0Stimh case ZFS_PROP_NORMALIZE: 1669de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1670de8267e0Stimh case ZFS_PROP_CASE: 1671de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1672de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 16733d7934e1Srm return (-1); 1674bd00f61bSrm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1675de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1676de8267e0Stimh zcmd_free_nvlists(&zc); 1677bd00f61bSrm zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1678de8267e0Stimh "unable to get %s property"), 1679de8267e0Stimh zfs_prop_to_name(prop)); 1680bd00f61bSrm return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, 1681bd00f61bSrm dgettext(TEXT_DOMAIN, "internal error"))); 1682bd00f61bSrm } 1683de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1684de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1685de8267e0Stimh val) != 0) { 1686de8267e0Stimh zcmd_free_nvlists(&zc); 1687de8267e0Stimh zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1688de8267e0Stimh "unable to get %s property"), 1689de8267e0Stimh zfs_prop_to_name(prop)); 1690de8267e0Stimh return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM, 1691de8267e0Stimh dgettext(TEXT_DOMAIN, "internal error"))); 1692de8267e0Stimh } 169396510749Stimh if (zplprops) 169496510749Stimh nvlist_free(zplprops); 1695de8267e0Stimh zcmd_free_nvlists(&zc); 1696bd00f61bSrm break; 1697bd00f61bSrm 1698fa9e4066Sahrens default: 1699e7437265Sahrens switch (zfs_prop_get_type(prop)) { 170091ebeef5Sahrens case PROP_TYPE_NUMBER: 170191ebeef5Sahrens case PROP_TYPE_INDEX: 1702e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 170374e7dc98SMatthew Ahrens /* 1704*14843421SMatthew Ahrens * If we tried to use a default value for a 170574e7dc98SMatthew Ahrens * readonly property, it means that it was not 170674e7dc98SMatthew Ahrens * present; return an error. 170774e7dc98SMatthew Ahrens */ 170874e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 170974e7dc98SMatthew Ahrens *source && (*source)[0] == '\0') { 171074e7dc98SMatthew Ahrens return (-1); 171174e7dc98SMatthew Ahrens } 1712e7437265Sahrens break; 1713e7437265Sahrens 171491ebeef5Sahrens case PROP_TYPE_STRING: 1715e7437265Sahrens default: 1716e7437265Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1717e7437265Sahrens "cannot get non-numeric property")); 1718e7437265Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 1719e7437265Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 1720e7437265Sahrens } 1721fa9e4066Sahrens } 1722fa9e4066Sahrens 1723fa9e4066Sahrens return (0); 1724fa9e4066Sahrens } 1725fa9e4066Sahrens 1726fa9e4066Sahrens /* 1727fa9e4066Sahrens * Calculate the source type, given the raw source string. 1728fa9e4066Sahrens */ 1729fa9e4066Sahrens static void 1730990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1731fa9e4066Sahrens char *statbuf, size_t statlen) 1732fa9e4066Sahrens { 1733990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1734fa9e4066Sahrens return; 1735fa9e4066Sahrens 1736fa9e4066Sahrens if (source == NULL) { 1737990b4856Slling *srctype = ZPROP_SRC_NONE; 1738fa9e4066Sahrens } else if (source[0] == '\0') { 1739990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 1740fa9e4066Sahrens } else { 1741fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1742990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1743fa9e4066Sahrens } else { 1744fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1745990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1746fa9e4066Sahrens } 1747fa9e4066Sahrens } 1748fa9e4066Sahrens 1749fa9e4066Sahrens } 1750fa9e4066Sahrens 1751fa9e4066Sahrens /* 1752fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1753fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1754fa9e4066Sahrens * human-readable form. 1755fa9e4066Sahrens * 1756fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1757fa9e4066Sahrens */ 1758fa9e4066Sahrens int 1759fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1760990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1761fa9e4066Sahrens { 1762fa9e4066Sahrens char *source = NULL; 1763fa9e4066Sahrens uint64_t val; 1764fa9e4066Sahrens char *str; 1765e9dbad6fSeschrock const char *strval; 1766fa9e4066Sahrens 1767fa9e4066Sahrens /* 1768fa9e4066Sahrens * Check to see if this property applies to our object 1769fa9e4066Sahrens */ 1770fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1771fa9e4066Sahrens return (-1); 1772fa9e4066Sahrens 1773fa9e4066Sahrens if (src) 1774990b4856Slling *src = ZPROP_SRC_NONE; 1775fa9e4066Sahrens 1776fa9e4066Sahrens switch (prop) { 1777fa9e4066Sahrens case ZFS_PROP_CREATION: 1778fa9e4066Sahrens /* 1779fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1780fa9e4066Sahrens * this into a string unless 'literal' is specified. 1781fa9e4066Sahrens */ 1782fa9e4066Sahrens { 1783a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1784a2eea2e1Sahrens time_t time = (time_t)val; 1785fa9e4066Sahrens struct tm t; 1786fa9e4066Sahrens 1787fa9e4066Sahrens if (literal || 1788fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1789fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1790fa9e4066Sahrens &t) == 0) 1791a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1792fa9e4066Sahrens } 1793fa9e4066Sahrens break; 1794fa9e4066Sahrens 1795fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1796fa9e4066Sahrens /* 1797fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1798fa9e4066Sahrens * 1799fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1800fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1801fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1802fa9e4066Sahrens * 1803fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1804fa9e4066Sahrens * root to any values we return. 1805fa9e4066Sahrens */ 180629ab75c9Srm 18077f7322feSeschrock str = getprop_string(zhp, prop, &source); 1808fa9e4066Sahrens 1809b21718f0Sgw if (str[0] == '/') { 181029ab75c9Srm char buf[MAXPATHLEN]; 181129ab75c9Srm char *root = buf; 18127f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 1813fa9e4066Sahrens 1814fa9e4066Sahrens if (relpath[0] == '/') 1815fa9e4066Sahrens relpath++; 1816b21718f0Sgw 181729ab75c9Srm if ((zpool_get_prop(zhp->zpool_hdl, 181829ab75c9Srm ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 181929ab75c9Srm (strcmp(root, "-") == 0)) 182029ab75c9Srm root[0] = '\0'; 1821b21718f0Sgw /* 1822b21718f0Sgw * Special case an alternate root of '/'. This will 1823b21718f0Sgw * avoid having multiple leading slashes in the 1824b21718f0Sgw * mountpoint path. 1825b21718f0Sgw */ 1826b21718f0Sgw if (strcmp(root, "/") == 0) 1827b21718f0Sgw root++; 1828b21718f0Sgw 1829b21718f0Sgw /* 1830b21718f0Sgw * If the mountpoint is '/' then skip over this 1831b21718f0Sgw * if we are obtaining either an alternate root or 1832b21718f0Sgw * an inherited mountpoint. 1833b21718f0Sgw */ 1834b21718f0Sgw if (str[1] == '\0' && (root[0] != '\0' || 1835b21718f0Sgw relpath[0] != '\0')) 18367f7322feSeschrock str++; 1837fa9e4066Sahrens 1838fa9e4066Sahrens if (relpath[0] == '\0') 1839fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 18407f7322feSeschrock root, str); 1841fa9e4066Sahrens else 1842fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 18437f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1844fa9e4066Sahrens relpath); 1845fa9e4066Sahrens } else { 1846fa9e4066Sahrens /* 'legacy' or 'none' */ 18477f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1848fa9e4066Sahrens } 1849fa9e4066Sahrens 1850fa9e4066Sahrens break; 1851fa9e4066Sahrens 1852fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1853a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1854fa9e4066Sahrens proplen); 1855fa9e4066Sahrens /* 1856fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1857fa9e4066Sahrens * it doesn't apply to this dataset. 1858fa9e4066Sahrens */ 1859fa9e4066Sahrens if (propbuf[0] == '\0') 1860fa9e4066Sahrens return (-1); 1861fa9e4066Sahrens break; 1862fa9e4066Sahrens 1863fa9e4066Sahrens case ZFS_PROP_QUOTA: 1864a9799022Sck case ZFS_PROP_REFQUOTA: 1865fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1866a9799022Sck case ZFS_PROP_REFRESERVATION: 1867a9799022Sck 186899653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 186999653d4eSeschrock return (-1); 1870fa9e4066Sahrens 1871fa9e4066Sahrens /* 1872fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 1873fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 1874fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 1875fa9e4066Sahrens * that its set locally. 1876fa9e4066Sahrens */ 1877fa9e4066Sahrens if (val == 0) { 1878fa9e4066Sahrens if (literal) 1879fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 1880fa9e4066Sahrens else 1881fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 1882fa9e4066Sahrens } else { 1883fa9e4066Sahrens if (literal) 18845ad82045Snd (void) snprintf(propbuf, proplen, "%llu", 1885b1b8ab34Slling (u_longlong_t)val); 1886fa9e4066Sahrens else 1887fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 1888fa9e4066Sahrens } 1889fa9e4066Sahrens break; 1890fa9e4066Sahrens 1891fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 189299653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 189399653d4eSeschrock return (-1); 18945ad82045Snd (void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t) 18955ad82045Snd val / 100, (longlong_t)val % 100); 1896fa9e4066Sahrens break; 1897fa9e4066Sahrens 1898fa9e4066Sahrens case ZFS_PROP_TYPE: 1899fa9e4066Sahrens switch (zhp->zfs_type) { 1900fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 1901fa9e4066Sahrens str = "filesystem"; 1902fa9e4066Sahrens break; 1903fa9e4066Sahrens case ZFS_TYPE_VOLUME: 1904fa9e4066Sahrens str = "volume"; 1905fa9e4066Sahrens break; 1906fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 1907fa9e4066Sahrens str = "snapshot"; 1908fa9e4066Sahrens break; 1909fa9e4066Sahrens default: 191099653d4eSeschrock abort(); 1911fa9e4066Sahrens } 1912fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 1913fa9e4066Sahrens break; 1914fa9e4066Sahrens 1915fa9e4066Sahrens case ZFS_PROP_MOUNTED: 1916fa9e4066Sahrens /* 1917fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 1918fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 1919fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 1920fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 1921fa9e4066Sahrens */ 192299653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 192399653d4eSeschrock src, &source, &val) != 0) 192499653d4eSeschrock return (-1); 192599653d4eSeschrock if (val) 1926fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 1927fa9e4066Sahrens else 1928fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 1929fa9e4066Sahrens break; 1930fa9e4066Sahrens 1931fa9e4066Sahrens case ZFS_PROP_NAME: 1932fa9e4066Sahrens /* 1933fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 1934fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 1935fa9e4066Sahrens * consumers. 1936fa9e4066Sahrens */ 1937fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 1938fa9e4066Sahrens break; 1939fa9e4066Sahrens 1940fa9e4066Sahrens default: 1941e7437265Sahrens switch (zfs_prop_get_type(prop)) { 194291ebeef5Sahrens case PROP_TYPE_NUMBER: 1943e7437265Sahrens if (get_numeric_property(zhp, prop, src, 1944e7437265Sahrens &source, &val) != 0) 1945e7437265Sahrens return (-1); 1946e7437265Sahrens if (literal) 1947e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 1948e7437265Sahrens (u_longlong_t)val); 1949e7437265Sahrens else 1950e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 1951e7437265Sahrens break; 1952e7437265Sahrens 195391ebeef5Sahrens case PROP_TYPE_STRING: 1954e7437265Sahrens (void) strlcpy(propbuf, 1955e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 1956e7437265Sahrens break; 1957e7437265Sahrens 195891ebeef5Sahrens case PROP_TYPE_INDEX: 1959fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 1960fe192f76Sahrens &source, &val) != 0) 1961fe192f76Sahrens return (-1); 1962fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 1963e7437265Sahrens return (-1); 1964e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 1965e7437265Sahrens break; 1966e7437265Sahrens 1967e7437265Sahrens default: 1968e7437265Sahrens abort(); 1969e7437265Sahrens } 1970fa9e4066Sahrens } 1971fa9e4066Sahrens 1972fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 1973fa9e4066Sahrens 1974fa9e4066Sahrens return (0); 1975fa9e4066Sahrens } 1976fa9e4066Sahrens 1977fa9e4066Sahrens /* 1978fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 1979fa9e4066Sahrens * the given property is the appropriate type; should only be used with 1980fa9e4066Sahrens * hard-coded property types. 1981fa9e4066Sahrens */ 1982fa9e4066Sahrens uint64_t 1983fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 1984fa9e4066Sahrens { 1985fa9e4066Sahrens char *source; 198699653d4eSeschrock uint64_t val; 198799653d4eSeschrock 19883cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 1989fa9e4066Sahrens 199099653d4eSeschrock return (val); 1991fa9e4066Sahrens } 1992fa9e4066Sahrens 19937b97dc1aSrm int 19947b97dc1aSrm zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 19957b97dc1aSrm { 19967b97dc1aSrm char buf[64]; 19977b97dc1aSrm 1998*14843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 19997b97dc1aSrm return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 20007b97dc1aSrm } 20017b97dc1aSrm 2002fa9e4066Sahrens /* 2003fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2004fa9e4066Sahrens */ 2005fa9e4066Sahrens int 2006fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2007990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2008fa9e4066Sahrens { 2009fa9e4066Sahrens char *source; 2010fa9e4066Sahrens 2011fa9e4066Sahrens /* 2012fa9e4066Sahrens * Check to see if this property applies to our object 2013fa9e4066Sahrens */ 2014e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2015ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 201699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 201799653d4eSeschrock zfs_prop_to_name(prop))); 2018e45ce728Sahrens } 2019fa9e4066Sahrens 2020fa9e4066Sahrens if (src) 2021990b4856Slling *src = ZPROP_SRC_NONE; 2022fa9e4066Sahrens 202399653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 202499653d4eSeschrock return (-1); 2025fa9e4066Sahrens 2026fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2027fa9e4066Sahrens 2028fa9e4066Sahrens return (0); 2029fa9e4066Sahrens } 2030fa9e4066Sahrens 2031*14843421SMatthew Ahrens static int 2032*14843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 2033*14843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 2034*14843421SMatthew Ahrens { 2035*14843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 2036*14843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 2037*14843421SMatthew Ahrens idmap_stat status; 2038*14843421SMatthew Ahrens int err = EINVAL; 2039*14843421SMatthew Ahrens 2040*14843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 2041*14843421SMatthew Ahrens goto out; 2042*14843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 2043*14843421SMatthew Ahrens goto out; 2044*14843421SMatthew Ahrens 2045*14843421SMatthew Ahrens if (isuser) { 2046*14843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 2047*14843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2048*14843421SMatthew Ahrens } else { 2049*14843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 2050*14843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2051*14843421SMatthew Ahrens } 2052*14843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 2053*14843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 2054*14843421SMatthew Ahrens status == IDMAP_SUCCESS) 2055*14843421SMatthew Ahrens err = 0; 2056*14843421SMatthew Ahrens else 2057*14843421SMatthew Ahrens err = EINVAL; 2058*14843421SMatthew Ahrens out: 2059*14843421SMatthew Ahrens if (get_hdl) 2060*14843421SMatthew Ahrens idmap_get_destroy(get_hdl); 2061*14843421SMatthew Ahrens if (idmap_hdl) 2062*14843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 2063*14843421SMatthew Ahrens return (err); 2064*14843421SMatthew Ahrens } 2065*14843421SMatthew Ahrens 2066*14843421SMatthew Ahrens /* 2067*14843421SMatthew Ahrens * convert the propname into parameters needed by kernel 2068*14843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 2069*14843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 2070*14843421SMatthew Ahrens */ 2071*14843421SMatthew Ahrens static int 2072*14843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 2073*14843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 2074*14843421SMatthew Ahrens { 2075*14843421SMatthew Ahrens zfs_userquota_prop_t type; 2076*14843421SMatthew Ahrens char *cp, *end; 2077*14843421SMatthew Ahrens boolean_t isuser; 2078*14843421SMatthew Ahrens 2079*14843421SMatthew Ahrens domain[0] = '\0'; 2080*14843421SMatthew Ahrens 2081*14843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 2082*14843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 2083*14843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 2084*14843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 2085*14843421SMatthew Ahrens break; 2086*14843421SMatthew Ahrens } 2087*14843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 2088*14843421SMatthew Ahrens return (EINVAL); 2089*14843421SMatthew Ahrens *typep = type; 2090*14843421SMatthew Ahrens 2091*14843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 2092*14843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 2093*14843421SMatthew Ahrens 2094*14843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 2095*14843421SMatthew Ahrens 2096*14843421SMatthew Ahrens if (strchr(cp, '@')) { 2097*14843421SMatthew Ahrens /* 2098*14843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 2099*14843421SMatthew Ahrens * turned into S-1-domainID-RID. There should be a 2100*14843421SMatthew Ahrens * better way to do this, but for now just translate it 2101*14843421SMatthew Ahrens * to the (possibly ephemeral) uid and then back to the 2102*14843421SMatthew Ahrens * SID. This is like getsidname(noresolve=TRUE). 2103*14843421SMatthew Ahrens */ 2104*14843421SMatthew Ahrens uid_t id; 2105*14843421SMatthew Ahrens idmap_rid_t rid; 2106*14843421SMatthew Ahrens char *mapdomain; 2107*14843421SMatthew Ahrens 2108*14843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 2109*14843421SMatthew Ahrens return (ENOENT); 2110*14843421SMatthew Ahrens if (sid_to_id(cp, isuser, &id) != 0) 2111*14843421SMatthew Ahrens return (ENOENT); 2112*14843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 2113*14843421SMatthew Ahrens &mapdomain, &rid) != 0) 2114*14843421SMatthew Ahrens return (ENOENT); 2115*14843421SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 2116*14843421SMatthew Ahrens *ridp = rid; 2117*14843421SMatthew Ahrens } else if (strncmp(cp, "S-1-", 4) == 0) { 2118*14843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 2119*14843421SMatthew Ahrens (void) strcpy(domain, cp); 2120*14843421SMatthew Ahrens cp = strrchr(domain, '-'); 2121*14843421SMatthew Ahrens *cp = '\0'; 2122*14843421SMatthew Ahrens cp++; 2123*14843421SMatthew Ahrens 2124*14843421SMatthew Ahrens errno = 0; 2125*14843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 2126*14843421SMatthew Ahrens if (errno == 0 || *end != '\0') 2127*14843421SMatthew Ahrens return (EINVAL); 2128*14843421SMatthew Ahrens } else if (!isdigit(*cp)) { 2129*14843421SMatthew Ahrens /* 2130*14843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 2131*14843421SMatthew Ahrens * turned into a uid/gid 2132*14843421SMatthew Ahrens */ 2133*14843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 2134*14843421SMatthew Ahrens return (ENOENT); 2135*14843421SMatthew Ahrens if (isuser) { 2136*14843421SMatthew Ahrens struct passwd *pw; 2137*14843421SMatthew Ahrens pw = getpwnam(cp); 2138*14843421SMatthew Ahrens if (pw == NULL) 2139*14843421SMatthew Ahrens return (ENOENT); 2140*14843421SMatthew Ahrens *ridp = pw->pw_uid; 2141*14843421SMatthew Ahrens } else { 2142*14843421SMatthew Ahrens struct group *gr; 2143*14843421SMatthew Ahrens gr = getgrnam(cp); 2144*14843421SMatthew Ahrens if (gr == NULL) 2145*14843421SMatthew Ahrens return (ENOENT); 2146*14843421SMatthew Ahrens *ridp = gr->gr_gid; 2147*14843421SMatthew Ahrens } 2148*14843421SMatthew Ahrens } else { 2149*14843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 2150*14843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 2151*14843421SMatthew Ahrens idmap_rid_t rid; 2152*14843421SMatthew Ahrens char *mapdomain; 2153*14843421SMatthew Ahrens 2154*14843421SMatthew Ahrens if (*end != '\0') 2155*14843421SMatthew Ahrens return (EINVAL); 2156*14843421SMatthew Ahrens if (id > MAXUID) { 2157*14843421SMatthew Ahrens /* It's an ephemeral ID. */ 2158*14843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 2159*14843421SMatthew Ahrens &mapdomain, &rid) != 0) 2160*14843421SMatthew Ahrens return (ENOENT); 2161*14843421SMatthew Ahrens (void) strcpy(domain, mapdomain); 2162*14843421SMatthew Ahrens *ridp = rid; 2163*14843421SMatthew Ahrens } else { 2164*14843421SMatthew Ahrens *ridp = id; 2165*14843421SMatthew Ahrens } 2166*14843421SMatthew Ahrens } 2167*14843421SMatthew Ahrens 2168*14843421SMatthew Ahrens return (0); 2169*14843421SMatthew Ahrens } 2170*14843421SMatthew Ahrens 2171*14843421SMatthew Ahrens int 2172*14843421SMatthew Ahrens zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2173*14843421SMatthew Ahrens char *propbuf, int proplen, boolean_t literal) 2174*14843421SMatthew Ahrens { 2175*14843421SMatthew Ahrens int err; 2176*14843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 2177*14843421SMatthew Ahrens zfs_userquota_prop_t type; 2178*14843421SMatthew Ahrens 2179*14843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2180*14843421SMatthew Ahrens 2181*14843421SMatthew Ahrens err = userquota_propname_decode(propname, 2182*14843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2183*14843421SMatthew Ahrens &type, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2184*14843421SMatthew Ahrens zc.zc_objset_type = type; 2185*14843421SMatthew Ahrens if (err) 2186*14843421SMatthew Ahrens return (err); 2187*14843421SMatthew Ahrens 2188*14843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 2189*14843421SMatthew Ahrens if (err) 2190*14843421SMatthew Ahrens return (err); 2191*14843421SMatthew Ahrens 2192*14843421SMatthew Ahrens if (literal) { 2193*14843421SMatthew Ahrens (void) snprintf(propbuf, proplen, "%llu", 2194*14843421SMatthew Ahrens (u_longlong_t)zc.zc_cookie); 2195*14843421SMatthew Ahrens } else if (zc.zc_cookie == 0 && 2196*14843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 2197*14843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 2198*14843421SMatthew Ahrens } else { 2199*14843421SMatthew Ahrens zfs_nicenum(zc.zc_cookie, propbuf, proplen); 2200*14843421SMatthew Ahrens } 2201*14843421SMatthew Ahrens return (0); 2202*14843421SMatthew Ahrens } 2203*14843421SMatthew Ahrens 2204fa9e4066Sahrens /* 2205fa9e4066Sahrens * Returns the name of the given zfs handle. 2206fa9e4066Sahrens */ 2207fa9e4066Sahrens const char * 2208fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2209fa9e4066Sahrens { 2210fa9e4066Sahrens return (zhp->zfs_name); 2211fa9e4066Sahrens } 2212fa9e4066Sahrens 2213fa9e4066Sahrens /* 2214fa9e4066Sahrens * Returns the type of the given zfs handle. 2215fa9e4066Sahrens */ 2216fa9e4066Sahrens zfs_type_t 2217fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2218fa9e4066Sahrens { 2219fa9e4066Sahrens return (zhp->zfs_type); 2220fa9e4066Sahrens } 2221fa9e4066Sahrens 2222ebedde84SEric Taylor static int 2223ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2224ebedde84SEric Taylor { 2225ebedde84SEric Taylor int rc; 2226ebedde84SEric Taylor uint64_t orig_cookie; 2227ebedde84SEric Taylor 2228ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2229ebedde84SEric Taylor top: 2230ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2231ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2232ebedde84SEric Taylor 2233ebedde84SEric Taylor if (rc == -1) { 2234ebedde84SEric Taylor switch (errno) { 2235ebedde84SEric Taylor case ENOMEM: 2236ebedde84SEric Taylor /* expand nvlist memory and try again */ 2237ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2238ebedde84SEric Taylor zcmd_free_nvlists(zc); 2239ebedde84SEric Taylor return (-1); 2240ebedde84SEric Taylor } 2241ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2242ebedde84SEric Taylor goto top; 2243ebedde84SEric Taylor /* 2244ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2245ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2246ebedde84SEric Taylor * has been removed since we obtained the handle. 2247ebedde84SEric Taylor */ 2248ebedde84SEric Taylor case ESRCH: 2249ebedde84SEric Taylor case ENOENT: 2250ebedde84SEric Taylor rc = 1; 2251ebedde84SEric Taylor break; 2252ebedde84SEric Taylor default: 2253ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2254ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2255ebedde84SEric Taylor "cannot iterate filesystems")); 2256ebedde84SEric Taylor break; 2257ebedde84SEric Taylor } 2258ebedde84SEric Taylor } 2259ebedde84SEric Taylor return (rc); 2260ebedde84SEric Taylor } 2261ebedde84SEric Taylor 2262fa9e4066Sahrens /* 22637f7322feSeschrock * Iterate over all child filesystems 2264fa9e4066Sahrens */ 2265fa9e4066Sahrens int 22667f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2267fa9e4066Sahrens { 2268fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2269fa9e4066Sahrens zfs_handle_t *nzhp; 2270fa9e4066Sahrens int ret; 2271fa9e4066Sahrens 22723cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 22733cb34c60Sahrens return (0); 22743cb34c60Sahrens 2275ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2276ebedde84SEric Taylor return (-1); 2277ebedde84SEric Taylor 2278ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2279ebedde84SEric Taylor &zc)) == 0) { 2280fa9e4066Sahrens /* 2281fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2282fa9e4066Sahrens * that the pool has since been removed. 2283fa9e4066Sahrens */ 2284ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2285ebedde84SEric Taylor &zc)) == NULL) { 2286fa9e4066Sahrens continue; 2287ebedde84SEric Taylor } 2288fa9e4066Sahrens 2289ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2290ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2291fa9e4066Sahrens return (ret); 2292ebedde84SEric Taylor } 2293fa9e4066Sahrens } 2294ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2295ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 22967f7322feSeschrock } 22977f7322feSeschrock 22987f7322feSeschrock /* 22997f7322feSeschrock * Iterate over all snapshots 23007f7322feSeschrock */ 23017f7322feSeschrock int 23027f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 23037f7322feSeschrock { 23047f7322feSeschrock zfs_cmd_t zc = { 0 }; 23057f7322feSeschrock zfs_handle_t *nzhp; 23067f7322feSeschrock int ret; 2307fa9e4066Sahrens 23083cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 23093cb34c60Sahrens return (0); 23103cb34c60Sahrens 2311ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2312ebedde84SEric Taylor return (-1); 2313ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2314ebedde84SEric Taylor &zc)) == 0) { 2315fa9e4066Sahrens 2316ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2317ebedde84SEric Taylor &zc)) == NULL) { 2318fa9e4066Sahrens continue; 2319ebedde84SEric Taylor } 2320fa9e4066Sahrens 2321ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2322ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2323fa9e4066Sahrens return (ret); 2324ebedde84SEric Taylor } 2325fa9e4066Sahrens } 2326ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2327ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2328fa9e4066Sahrens } 2329fa9e4066Sahrens 23307f7322feSeschrock /* 23317f7322feSeschrock * Iterate over all children, snapshots and filesystems 23327f7322feSeschrock */ 23337f7322feSeschrock int 23347f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 23357f7322feSeschrock { 23367f7322feSeschrock int ret; 23377f7322feSeschrock 23387f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 23397f7322feSeschrock return (ret); 23407f7322feSeschrock 23417f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 23427f7322feSeschrock } 23437f7322feSeschrock 2344fa9e4066Sahrens /* 2345fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2346fa9e4066Sahrens * Can return NULL if this is a pool. 2347fa9e4066Sahrens */ 2348fa9e4066Sahrens static int 2349fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2350fa9e4066Sahrens { 2351fa9e4066Sahrens char *loc; 2352fa9e4066Sahrens 2353fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2354fa9e4066Sahrens return (-1); 2355fa9e4066Sahrens 2356fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2357fa9e4066Sahrens buf[loc - path] = '\0'; 2358fa9e4066Sahrens 2359fa9e4066Sahrens return (0); 2360fa9e4066Sahrens } 2361fa9e4066Sahrens 2362fa9e4066Sahrens /* 23637f1f55eaSvb * If accept_ancestor is false, then check to make sure that the given path has 23647f1f55eaSvb * a parent, and that it exists. If accept_ancestor is true, then find the 23657f1f55eaSvb * closest existing ancestor for the given path. In prefixlen return the 23667f1f55eaSvb * length of already existing prefix of the given path. We also fetch the 23677f1f55eaSvb * 'zoned' property, which is used to validate property settings when creating 23687f1f55eaSvb * new datasets. 2369fa9e4066Sahrens */ 2370fa9e4066Sahrens static int 23717f1f55eaSvb check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 23727f1f55eaSvb boolean_t accept_ancestor, int *prefixlen) 2373fa9e4066Sahrens { 2374fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2375fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2376fa9e4066Sahrens char *slash; 23777f7322feSeschrock zfs_handle_t *zhp; 237899653d4eSeschrock char errbuf[1024]; 237999653d4eSeschrock 2380deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2381deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2382fa9e4066Sahrens 2383fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2384fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 238599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 238699653d4eSeschrock "missing dataset name")); 238799653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2388fa9e4066Sahrens } 2389fa9e4066Sahrens 2390fa9e4066Sahrens /* check to see if the pool exists */ 2391fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2392fa9e4066Sahrens slash = parent + strlen(parent); 2393fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2394fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 239599653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2396fa9e4066Sahrens errno == ENOENT) { 239799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 239899653d4eSeschrock "no such pool '%s'"), zc.zc_name); 239999653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2400fa9e4066Sahrens } 2401fa9e4066Sahrens 2402fa9e4066Sahrens /* check to see if the parent dataset exists */ 24037f1f55eaSvb while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 24047f1f55eaSvb if (errno == ENOENT && accept_ancestor) { 24057f1f55eaSvb /* 24067f1f55eaSvb * Go deeper to find an ancestor, give up on top level. 24077f1f55eaSvb */ 24087f1f55eaSvb if (parent_name(parent, parent, sizeof (parent)) != 0) { 24097f1f55eaSvb zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 24107f1f55eaSvb "no such pool '%s'"), zc.zc_name); 24117f1f55eaSvb return (zfs_error(hdl, EZFS_NOENT, errbuf)); 24127f1f55eaSvb } 24137f1f55eaSvb } else if (errno == ENOENT) { 241499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 241599653d4eSeschrock "parent does not exist")); 241699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 24177f1f55eaSvb } else 241899653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2419fa9e4066Sahrens } 2420fa9e4066Sahrens 2421e9dbad6fSeschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2422fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2423e9dbad6fSeschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 242499653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 24257f7322feSeschrock zfs_close(zhp); 2426fa9e4066Sahrens return (-1); 2427fa9e4066Sahrens } 2428fa9e4066Sahrens 2429fa9e4066Sahrens /* make sure parent is a filesystem */ 24307f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 243199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 243299653d4eSeschrock "parent is not a filesystem")); 243399653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 24347f7322feSeschrock zfs_close(zhp); 2435fa9e4066Sahrens return (-1); 2436fa9e4066Sahrens } 2437fa9e4066Sahrens 24387f7322feSeschrock zfs_close(zhp); 24397f1f55eaSvb if (prefixlen != NULL) 24407f1f55eaSvb *prefixlen = strlen(parent); 24417f1f55eaSvb return (0); 24427f1f55eaSvb } 24437f1f55eaSvb 24447f1f55eaSvb /* 24457f1f55eaSvb * Finds whether the dataset of the given type(s) exists. 24467f1f55eaSvb */ 24477f1f55eaSvb boolean_t 24487f1f55eaSvb zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 24497f1f55eaSvb { 24507f1f55eaSvb zfs_handle_t *zhp; 24517f1f55eaSvb 2452f18faf3fSek if (!zfs_validate_name(hdl, path, types, B_FALSE)) 24537f1f55eaSvb return (B_FALSE); 24547f1f55eaSvb 24557f1f55eaSvb /* 24567f1f55eaSvb * Try to get stats for the dataset, which will tell us if it exists. 24577f1f55eaSvb */ 24587f1f55eaSvb if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 24597f1f55eaSvb int ds_type = zhp->zfs_type; 24607f1f55eaSvb 24617f1f55eaSvb zfs_close(zhp); 24627f1f55eaSvb if (types & ds_type) 24637f1f55eaSvb return (B_TRUE); 24647f1f55eaSvb } 24657f1f55eaSvb return (B_FALSE); 24667f1f55eaSvb } 24677f1f55eaSvb 24683cb34c60Sahrens /* 24693cb34c60Sahrens * Given a path to 'target', create all the ancestors between 24703cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 24713cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 24723cb34c60Sahrens */ 24733cb34c60Sahrens int 24743cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 24753cb34c60Sahrens { 24763cb34c60Sahrens zfs_handle_t *h; 24773cb34c60Sahrens char *cp; 24783cb34c60Sahrens const char *opname; 24793cb34c60Sahrens 24803cb34c60Sahrens /* make sure prefix exists */ 24813cb34c60Sahrens cp = target + prefixlen; 24823cb34c60Sahrens if (*cp != '/') { 24833cb34c60Sahrens assert(strchr(cp, '/') == NULL); 24843cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 24853cb34c60Sahrens } else { 24863cb34c60Sahrens *cp = '\0'; 24873cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 24883cb34c60Sahrens *cp = '/'; 24893cb34c60Sahrens } 24903cb34c60Sahrens if (h == NULL) 24913cb34c60Sahrens return (-1); 24923cb34c60Sahrens zfs_close(h); 24933cb34c60Sahrens 24943cb34c60Sahrens /* 24953cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 24963cb34c60Sahrens * up to the prefixlen-long one. 24973cb34c60Sahrens */ 24983cb34c60Sahrens for (cp = target + prefixlen + 1; 24993cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 25003cb34c60Sahrens char *logstr; 25013cb34c60Sahrens 25023cb34c60Sahrens *cp = '\0'; 25033cb34c60Sahrens 25043cb34c60Sahrens h = make_dataset_handle(hdl, target); 25053cb34c60Sahrens if (h) { 25063cb34c60Sahrens /* it already exists, nothing to do here */ 25073cb34c60Sahrens zfs_close(h); 25083cb34c60Sahrens continue; 25093cb34c60Sahrens } 25103cb34c60Sahrens 25113cb34c60Sahrens logstr = hdl->libzfs_log_str; 25123cb34c60Sahrens hdl->libzfs_log_str = NULL; 25133cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 25143cb34c60Sahrens NULL) != 0) { 25153cb34c60Sahrens hdl->libzfs_log_str = logstr; 25163cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 25173cb34c60Sahrens goto ancestorerr; 25183cb34c60Sahrens } 25193cb34c60Sahrens 25203cb34c60Sahrens hdl->libzfs_log_str = logstr; 25213cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 25223cb34c60Sahrens if (h == NULL) { 25233cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 25243cb34c60Sahrens goto ancestorerr; 25253cb34c60Sahrens } 25263cb34c60Sahrens 25273cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 25283cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 25293cb34c60Sahrens goto ancestorerr; 25303cb34c60Sahrens } 25313cb34c60Sahrens 25323cb34c60Sahrens if (zfs_share(h) != 0) { 25333cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 25343cb34c60Sahrens goto ancestorerr; 25353cb34c60Sahrens } 25363cb34c60Sahrens 25373cb34c60Sahrens zfs_close(h); 25383cb34c60Sahrens } 25393cb34c60Sahrens 25403cb34c60Sahrens return (0); 25413cb34c60Sahrens 25423cb34c60Sahrens ancestorerr: 25433cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25443cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 25453cb34c60Sahrens return (-1); 25463cb34c60Sahrens } 25473cb34c60Sahrens 25487f1f55eaSvb /* 25497f1f55eaSvb * Creates non-existing ancestors of the given path. 25507f1f55eaSvb */ 25517f1f55eaSvb int 25527f1f55eaSvb zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 25537f1f55eaSvb { 25547f1f55eaSvb int prefix; 25557f1f55eaSvb uint64_t zoned; 25567f1f55eaSvb char *path_copy; 25577f1f55eaSvb int rc; 25587f1f55eaSvb 25597f1f55eaSvb if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 25607f1f55eaSvb return (-1); 25617f1f55eaSvb 25627f1f55eaSvb if ((path_copy = strdup(path)) != NULL) { 25637f1f55eaSvb rc = create_parents(hdl, path_copy, prefix); 25647f1f55eaSvb free(path_copy); 25657f1f55eaSvb } 25667f1f55eaSvb if (path_copy == NULL || rc != 0) 25677f1f55eaSvb return (-1); 25687f1f55eaSvb 2569fa9e4066Sahrens return (0); 2570fa9e4066Sahrens } 2571fa9e4066Sahrens 2572fa9e4066Sahrens /* 2573e9dbad6fSeschrock * Create a new filesystem or volume. 2574fa9e4066Sahrens */ 2575fa9e4066Sahrens int 257699653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2577e9dbad6fSeschrock nvlist_t *props) 2578fa9e4066Sahrens { 2579fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2580fa9e4066Sahrens int ret; 2581fa9e4066Sahrens uint64_t size = 0; 2582fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 258399653d4eSeschrock char errbuf[1024]; 2584e9dbad6fSeschrock uint64_t zoned; 258599653d4eSeschrock 258699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 258799653d4eSeschrock "cannot create '%s'"), path); 2588fa9e4066Sahrens 2589fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2590f18faf3fSek if (!zfs_validate_name(hdl, path, type, B_TRUE)) 259199653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2592fa9e4066Sahrens 2593fa9e4066Sahrens /* validate parents exist */ 25947f1f55eaSvb if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2595fa9e4066Sahrens return (-1); 2596fa9e4066Sahrens 2597fa9e4066Sahrens /* 2598fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2599fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2600fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2601fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2602fa9e4066Sahrens * first try to see if the dataset exists. 2603fa9e4066Sahrens */ 2604fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2605990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 260699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 260799653d4eSeschrock "dataset already exists")); 260899653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2609fa9e4066Sahrens } 2610fa9e4066Sahrens 2611fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2612fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2613fa9e4066Sahrens else 2614fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2615fa9e4066Sahrens 26160a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2617b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2618e9dbad6fSeschrock return (-1); 2619e9dbad6fSeschrock 2620fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 26215c5460e9Seschrock /* 26225c5460e9Seschrock * If we are creating a volume, the size and block size must 26235c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 26245c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 26255c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 26265c5460e9Seschrock * zero. 26275c5460e9Seschrock */ 2628e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2629e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2630e9dbad6fSeschrock nvlist_free(props); 263199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2632e9dbad6fSeschrock "missing volume size")); 2633e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2634e9dbad6fSeschrock } 2635e9dbad6fSeschrock 2636e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2637e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2638e9dbad6fSeschrock &blocksize)) != 0) { 2639e9dbad6fSeschrock if (ret == ENOENT) { 2640e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2641e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2642e9dbad6fSeschrock } else { 2643e9dbad6fSeschrock nvlist_free(props); 2644e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2645e9dbad6fSeschrock "missing volume block size")); 2646e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2647e9dbad6fSeschrock } 2648fa9e4066Sahrens } 2649fa9e4066Sahrens 2650e9dbad6fSeschrock if (size == 0) { 2651e9dbad6fSeschrock nvlist_free(props); 265299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2653e9dbad6fSeschrock "volume size cannot be zero")); 2654e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 26555c5460e9Seschrock } 26565c5460e9Seschrock 26575c5460e9Seschrock if (size % blocksize != 0) { 2658e9dbad6fSeschrock nvlist_free(props); 265999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2660e9dbad6fSeschrock "volume size must be a multiple of volume block " 2661e9dbad6fSeschrock "size")); 2662e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 26635c5460e9Seschrock } 2664fa9e4066Sahrens } 2665fa9e4066Sahrens 2666990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2667e9dbad6fSeschrock return (-1); 2668e9dbad6fSeschrock nvlist_free(props); 2669e9dbad6fSeschrock 2670fa9e4066Sahrens /* create the dataset */ 2671ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2672fa9e4066Sahrens 2673b1b8ab34Slling if (ret == 0 && type == ZFS_TYPE_VOLUME) { 267499653d4eSeschrock ret = zvol_create_link(hdl, path); 2675b1b8ab34Slling if (ret) { 2676b1b8ab34Slling (void) zfs_standard_error(hdl, errno, 2677b1b8ab34Slling dgettext(TEXT_DOMAIN, 2678b1b8ab34Slling "Volume successfully created, but device links " 2679b1b8ab34Slling "were not created")); 2680b1b8ab34Slling zcmd_free_nvlists(&zc); 2681b1b8ab34Slling return (-1); 2682b1b8ab34Slling } 2683b1b8ab34Slling } 2684fa9e4066Sahrens 2685e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2686e9dbad6fSeschrock 2687fa9e4066Sahrens /* check for failure */ 2688fa9e4066Sahrens if (ret != 0) { 2689fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2690fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2691fa9e4066Sahrens 2692fa9e4066Sahrens switch (errno) { 2693fa9e4066Sahrens case ENOENT: 269499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 269599653d4eSeschrock "no such parent '%s'"), parent); 269699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2697fa9e4066Sahrens 2698fa9e4066Sahrens case EINVAL: 269999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2700d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 270199653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2702fa9e4066Sahrens 2703fa9e4066Sahrens case EDOM: 270499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2705e9dbad6fSeschrock "volume block size must be power of 2 from " 2706e9dbad6fSeschrock "%u to %uk"), 2707fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2708fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 270999653d4eSeschrock 2710e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 271199653d4eSeschrock 271240feaa91Sahrens case ENOTSUP: 271340feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 271440feaa91Sahrens "pool must be upgraded to set this " 271540feaa91Sahrens "property or value")); 271640feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2717fa9e4066Sahrens #ifdef _ILP32 2718fa9e4066Sahrens case EOVERFLOW: 2719fa9e4066Sahrens /* 2720fa9e4066Sahrens * This platform can't address a volume this big. 2721fa9e4066Sahrens */ 272299653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 272399653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 272499653d4eSeschrock errbuf)); 2725fa9e4066Sahrens #endif 272699653d4eSeschrock /* FALLTHROUGH */ 2727fa9e4066Sahrens default: 272899653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2729fa9e4066Sahrens } 2730fa9e4066Sahrens } 2731fa9e4066Sahrens 2732fa9e4066Sahrens return (0); 2733fa9e4066Sahrens } 2734fa9e4066Sahrens 2735fa9e4066Sahrens /* 2736fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2737fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2738fa9e4066Sahrens */ 2739fa9e4066Sahrens int 2740fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp) 2741fa9e4066Sahrens { 2742fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2743fa9e4066Sahrens 2744fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2745fa9e4066Sahrens 2746e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2747f3861e1aSahl /* 2748ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2749ecd6cf80Smarks * abort the request. This would only happen for a 2750ecd6cf80Smarks * non-privileged user. 2751f3861e1aSahl */ 2752ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2753ecd6cf80Smarks return (-1); 2754ecd6cf80Smarks } 2755f3861e1aSahl 275699653d4eSeschrock if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 2757fa9e4066Sahrens return (-1); 2758fa9e4066Sahrens 2759fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2760fa9e4066Sahrens } else { 2761fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2762fa9e4066Sahrens } 2763fa9e4066Sahrens 2764ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2765ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 276699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 276799653d4eSeschrock zhp->zfs_name)); 27681d452cf5Sahrens } 2769fa9e4066Sahrens 2770fa9e4066Sahrens remove_mountpoint(zhp); 2771fa9e4066Sahrens 2772fa9e4066Sahrens return (0); 2773fa9e4066Sahrens } 2774fa9e4066Sahrens 27751d452cf5Sahrens struct destroydata { 27761d452cf5Sahrens char *snapname; 27771d452cf5Sahrens boolean_t gotone; 27783ccfa83cSahrens boolean_t closezhp; 27791d452cf5Sahrens }; 27801d452cf5Sahrens 27811d452cf5Sahrens static int 27821d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg) 27831d452cf5Sahrens { 27841d452cf5Sahrens struct destroydata *dd = arg; 27851d452cf5Sahrens zfs_handle_t *szhp; 27861d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 27873ccfa83cSahrens boolean_t closezhp = dd->closezhp; 27883ccfa83cSahrens int rv; 27891d452cf5Sahrens 2790e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2791e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2792e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 27931d452cf5Sahrens 27941d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 27951d452cf5Sahrens if (szhp) { 27961d452cf5Sahrens dd->gotone = B_TRUE; 27971d452cf5Sahrens zfs_close(szhp); 27981d452cf5Sahrens } 27991d452cf5Sahrens 28001d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 28011d452cf5Sahrens (void) zvol_remove_link(zhp->zfs_hdl, name); 28021d452cf5Sahrens /* 28031d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 28041d452cf5Sahrens * return an error, because then we wouldn't visit all 28051d452cf5Sahrens * the volumes. 28061d452cf5Sahrens */ 28071d452cf5Sahrens } 28081d452cf5Sahrens 28093ccfa83cSahrens dd->closezhp = B_TRUE; 28103ccfa83cSahrens rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg); 28113ccfa83cSahrens if (closezhp) 28123ccfa83cSahrens zfs_close(zhp); 28133ccfa83cSahrens return (rv); 28141d452cf5Sahrens } 28151d452cf5Sahrens 28161d452cf5Sahrens /* 28171d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 28181d452cf5Sahrens */ 28191d452cf5Sahrens int 28201d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname) 28211d452cf5Sahrens { 28221d452cf5Sahrens zfs_cmd_t zc = { 0 }; 28231d452cf5Sahrens int ret; 28241d452cf5Sahrens struct destroydata dd = { 0 }; 28251d452cf5Sahrens 28261d452cf5Sahrens dd.snapname = snapname; 28271d452cf5Sahrens (void) zfs_remove_link_cb(zhp, &dd); 28281d452cf5Sahrens 28291d452cf5Sahrens if (!dd.gotone) { 2830ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 28311d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 28321d452cf5Sahrens zhp->zfs_name, snapname)); 28331d452cf5Sahrens } 28341d452cf5Sahrens 28351d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2836e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 28371d452cf5Sahrens 2838ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 28391d452cf5Sahrens if (ret != 0) { 28401d452cf5Sahrens char errbuf[1024]; 28411d452cf5Sahrens 28421d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 28431d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 28441d452cf5Sahrens 28451d452cf5Sahrens switch (errno) { 28461d452cf5Sahrens case EEXIST: 28471d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 28481d452cf5Sahrens "snapshot is cloned")); 28491d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 28501d452cf5Sahrens 28511d452cf5Sahrens default: 28521d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 28531d452cf5Sahrens errbuf)); 28541d452cf5Sahrens } 28551d452cf5Sahrens } 28561d452cf5Sahrens 28571d452cf5Sahrens return (0); 28581d452cf5Sahrens } 28591d452cf5Sahrens 2860fa9e4066Sahrens /* 2861fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 2862fa9e4066Sahrens */ 2863fa9e4066Sahrens int 2864e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 2865fa9e4066Sahrens { 2866fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2867fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2868fa9e4066Sahrens int ret; 286999653d4eSeschrock char errbuf[1024]; 287099653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2871e9dbad6fSeschrock zfs_type_t type; 2872e9dbad6fSeschrock uint64_t zoned; 2873fa9e4066Sahrens 2874fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 2875fa9e4066Sahrens 287699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 287799653d4eSeschrock "cannot create '%s'"), target); 287899653d4eSeschrock 2879fa9e4066Sahrens /* validate the target name */ 2880f18faf3fSek if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 288199653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2882fa9e4066Sahrens 2883fa9e4066Sahrens /* validate parents exist */ 28847f1f55eaSvb if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 2885fa9e4066Sahrens return (-1); 2886fa9e4066Sahrens 2887fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 2888fa9e4066Sahrens 2889fa9e4066Sahrens /* do the clone */ 2890e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2891fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 28925f8e1617Snn type = ZFS_TYPE_VOLUME; 2893e9dbad6fSeschrock } else { 2894fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 28955f8e1617Snn type = ZFS_TYPE_FILESYSTEM; 2896e9dbad6fSeschrock } 2897e9dbad6fSeschrock 2898e9dbad6fSeschrock if (props) { 28990a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 29000a48a24eStimh zhp, errbuf)) == NULL) 2901e9dbad6fSeschrock return (-1); 2902e9dbad6fSeschrock 2903990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 2904e9dbad6fSeschrock nvlist_free(props); 2905e9dbad6fSeschrock return (-1); 2906e9dbad6fSeschrock } 2907e9dbad6fSeschrock 2908e9dbad6fSeschrock nvlist_free(props); 2909e9dbad6fSeschrock } 2910fa9e4066Sahrens 2911fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 2912e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 2913ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 2914fa9e4066Sahrens 2915e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2916e9dbad6fSeschrock 2917fa9e4066Sahrens if (ret != 0) { 2918fa9e4066Sahrens switch (errno) { 2919fa9e4066Sahrens 2920fa9e4066Sahrens case ENOENT: 2921fa9e4066Sahrens /* 2922fa9e4066Sahrens * The parent doesn't exist. We should have caught this 2923fa9e4066Sahrens * above, but there may a race condition that has since 2924fa9e4066Sahrens * destroyed the parent. 2925fa9e4066Sahrens * 2926fa9e4066Sahrens * At this point, we don't know whether it's the source 2927fa9e4066Sahrens * that doesn't exist anymore, or whether the target 2928fa9e4066Sahrens * dataset doesn't exist. 2929fa9e4066Sahrens */ 293099653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 293199653d4eSeschrock "no such parent '%s'"), parent); 293299653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 2933fa9e4066Sahrens 293499653d4eSeschrock case EXDEV: 293599653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 293699653d4eSeschrock "source and target pools differ")); 293799653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 293899653d4eSeschrock errbuf)); 2939fa9e4066Sahrens 294099653d4eSeschrock default: 294199653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 294299653d4eSeschrock errbuf)); 294399653d4eSeschrock } 2944e9dbad6fSeschrock } else if (ZFS_IS_VOLUME(zhp)) { 294599653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, target); 294699653d4eSeschrock } 2947fa9e4066Sahrens 294899653d4eSeschrock return (ret); 294999653d4eSeschrock } 295099653d4eSeschrock 295199653d4eSeschrock typedef struct promote_data { 295299653d4eSeschrock char cb_mountpoint[MAXPATHLEN]; 295399653d4eSeschrock const char *cb_target; 295499653d4eSeschrock const char *cb_errbuf; 295599653d4eSeschrock uint64_t cb_pivot_txg; 295699653d4eSeschrock } promote_data_t; 295799653d4eSeschrock 295899653d4eSeschrock static int 295999653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data) 296099653d4eSeschrock { 296199653d4eSeschrock promote_data_t *pd = data; 296299653d4eSeschrock zfs_handle_t *szhp; 296399653d4eSeschrock char snapname[MAXPATHLEN]; 29643ccfa83cSahrens int rv = 0; 296599653d4eSeschrock 296699653d4eSeschrock /* We don't care about snapshots after the pivot point */ 29673ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) { 29683ccfa83cSahrens zfs_close(zhp); 296999653d4eSeschrock return (0); 29703ccfa83cSahrens } 297199653d4eSeschrock 29720b69c2f0Sahrens /* Remove the device link if it's a zvol. */ 2973e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 29740b69c2f0Sahrens (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); 297599653d4eSeschrock 297699653d4eSeschrock /* Check for conflicting names */ 2977e9dbad6fSeschrock (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); 2978e9dbad6fSeschrock (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); 297999653d4eSeschrock szhp = make_dataset_handle(zhp->zfs_hdl, snapname); 298099653d4eSeschrock if (szhp != NULL) { 298199653d4eSeschrock zfs_close(szhp); 298299653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 298399653d4eSeschrock "snapshot name '%s' from origin \n" 298499653d4eSeschrock "conflicts with '%s' from target"), 298599653d4eSeschrock zhp->zfs_name, snapname); 29863ccfa83cSahrens rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf); 298799653d4eSeschrock } 29883ccfa83cSahrens zfs_close(zhp); 29893ccfa83cSahrens return (rv); 299099653d4eSeschrock } 299199653d4eSeschrock 29920b69c2f0Sahrens static int 29930b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data) 29940b69c2f0Sahrens { 29950b69c2f0Sahrens promote_data_t *pd = data; 29960b69c2f0Sahrens 29970b69c2f0Sahrens /* We don't care about snapshots after the pivot point */ 29983ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) { 29993ccfa83cSahrens /* Create the device link if it's a zvol. */ 30003ccfa83cSahrens if (ZFS_IS_VOLUME(zhp)) 30013ccfa83cSahrens (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); 30023ccfa83cSahrens } 30030b69c2f0Sahrens 30043ccfa83cSahrens zfs_close(zhp); 30050b69c2f0Sahrens return (0); 30060b69c2f0Sahrens } 30070b69c2f0Sahrens 300899653d4eSeschrock /* 300999653d4eSeschrock * Promotes the given clone fs to be the clone parent. 301099653d4eSeschrock */ 301199653d4eSeschrock int 301299653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 301399653d4eSeschrock { 301499653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 301599653d4eSeschrock zfs_cmd_t zc = { 0 }; 301699653d4eSeschrock char parent[MAXPATHLEN]; 301799653d4eSeschrock char *cp; 301899653d4eSeschrock int ret; 301999653d4eSeschrock zfs_handle_t *pzhp; 302099653d4eSeschrock promote_data_t pd; 302199653d4eSeschrock char errbuf[1024]; 302299653d4eSeschrock 302399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 302499653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 302599653d4eSeschrock 302699653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 302799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 302899653d4eSeschrock "snapshots can not be promoted")); 302999653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 303099653d4eSeschrock } 303199653d4eSeschrock 30323cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 303399653d4eSeschrock if (parent[0] == '\0') { 303499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 303599653d4eSeschrock "not a cloned filesystem")); 303699653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 303799653d4eSeschrock } 303899653d4eSeschrock cp = strchr(parent, '@'); 303999653d4eSeschrock *cp = '\0'; 304099653d4eSeschrock 304199653d4eSeschrock /* Walk the snapshots we will be moving */ 30423cb34c60Sahrens pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); 304399653d4eSeschrock if (pzhp == NULL) 304499653d4eSeschrock return (-1); 304599653d4eSeschrock pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); 304699653d4eSeschrock zfs_close(pzhp); 304799653d4eSeschrock pd.cb_target = zhp->zfs_name; 304899653d4eSeschrock pd.cb_errbuf = errbuf; 3049990b4856Slling pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET); 305099653d4eSeschrock if (pzhp == NULL) 305199653d4eSeschrock return (-1); 305299653d4eSeschrock (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, 305399653d4eSeschrock sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); 305499653d4eSeschrock ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); 30550b69c2f0Sahrens if (ret != 0) { 30560b69c2f0Sahrens zfs_close(pzhp); 305799653d4eSeschrock return (-1); 30580b69c2f0Sahrens } 305999653d4eSeschrock 306099653d4eSeschrock /* issue the ioctl */ 30613cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3062e9dbad6fSeschrock sizeof (zc.zc_value)); 306399653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3064ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 306599653d4eSeschrock 306699653d4eSeschrock if (ret != 0) { 30670b69c2f0Sahrens int save_errno = errno; 30680b69c2f0Sahrens 30690b69c2f0Sahrens (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd); 30700b69c2f0Sahrens zfs_close(pzhp); 307199653d4eSeschrock 30720b69c2f0Sahrens switch (save_errno) { 307399653d4eSeschrock case EEXIST: 3074fa9e4066Sahrens /* 307599653d4eSeschrock * There is a conflicting snapshot name. We 307699653d4eSeschrock * should have caught this above, but they could 307799653d4eSeschrock * have renamed something in the mean time. 3078fa9e4066Sahrens */ 307999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 308099653d4eSeschrock "conflicting snapshot name from parent '%s'"), 308199653d4eSeschrock parent); 308299653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3083fa9e4066Sahrens 3084fa9e4066Sahrens default: 30850b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3086fa9e4066Sahrens } 30870b69c2f0Sahrens } else { 30880b69c2f0Sahrens (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd); 3089fa9e4066Sahrens } 3090fa9e4066Sahrens 30910b69c2f0Sahrens zfs_close(pzhp); 3092fa9e4066Sahrens return (ret); 3093fa9e4066Sahrens } 3094fa9e4066Sahrens 3095cdf5b4caSmmusante struct createdata { 3096cdf5b4caSmmusante const char *cd_snapname; 3097cdf5b4caSmmusante int cd_ifexists; 3098cdf5b4caSmmusante }; 3099cdf5b4caSmmusante 31001d452cf5Sahrens static int 31011d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg) 31021d452cf5Sahrens { 3103cdf5b4caSmmusante struct createdata *cd = arg; 3104e9dbad6fSeschrock int ret; 31051d452cf5Sahrens 31061d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 31071d452cf5Sahrens char name[MAXPATHLEN]; 31081d452cf5Sahrens 3109e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 3110e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 3111cdf5b4caSmmusante (void) strlcat(name, cd->cd_snapname, sizeof (name)); 3112cdf5b4caSmmusante (void) zvol_create_link_common(zhp->zfs_hdl, name, 3113cdf5b4caSmmusante cd->cd_ifexists); 31141d452cf5Sahrens /* 31151d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 31161d452cf5Sahrens * return an error, because then we wouldn't visit all 31171d452cf5Sahrens * the volumes. 31181d452cf5Sahrens */ 31191d452cf5Sahrens } 3120e9dbad6fSeschrock 3121cdf5b4caSmmusante ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd); 3122e9dbad6fSeschrock 3123e9dbad6fSeschrock zfs_close(zhp); 3124e9dbad6fSeschrock 3125e9dbad6fSeschrock return (ret); 31261d452cf5Sahrens } 31271d452cf5Sahrens 3128fa9e4066Sahrens /* 312972bdce51Sahl * Takes a snapshot of the given dataset. 3130fa9e4066Sahrens */ 3131fa9e4066Sahrens int 3132bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3133bb0ade09Sahrens nvlist_t *props) 3134fa9e4066Sahrens { 3135fa9e4066Sahrens const char *delim; 3136bb0ade09Sahrens char parent[ZFS_MAXNAMELEN]; 3137fa9e4066Sahrens zfs_handle_t *zhp; 3138fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3139fa9e4066Sahrens int ret; 314099653d4eSeschrock char errbuf[1024]; 3141fa9e4066Sahrens 314299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 314399653d4eSeschrock "cannot snapshot '%s'"), path); 314499653d4eSeschrock 314599653d4eSeschrock /* validate the target name */ 3146f18faf3fSek if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 314799653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3148fa9e4066Sahrens 3149bb0ade09Sahrens if (props) { 3150bb0ade09Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3151bb0ade09Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 3152bb0ade09Sahrens return (-1); 3153bb0ade09Sahrens 3154bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3155bb0ade09Sahrens nvlist_free(props); 3156bb0ade09Sahrens return (-1); 3157bb0ade09Sahrens } 3158bb0ade09Sahrens 3159bb0ade09Sahrens nvlist_free(props); 3160bb0ade09Sahrens } 3161bb0ade09Sahrens 3162fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 31631d452cf5Sahrens delim = strchr(path, '@'); 3164fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3165fa9e4066Sahrens parent[delim - path] = '\0'; 3166fa9e4066Sahrens 316799653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3168fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3169bb0ade09Sahrens zcmd_free_nvlists(&zc); 3170fa9e4066Sahrens return (-1); 3171fa9e4066Sahrens } 3172fa9e4066Sahrens 31731d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3174e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3175ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3176ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3177ecd6cf80Smarks else 3178ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 31791d452cf5Sahrens zc.zc_cookie = recursive; 3180ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3181fa9e4066Sahrens 3182bb0ade09Sahrens zcmd_free_nvlists(&zc); 3183bb0ade09Sahrens 31841d452cf5Sahrens /* 31851d452cf5Sahrens * if it was recursive, the one that actually failed will be in 31861d452cf5Sahrens * zc.zc_name. 31871d452cf5Sahrens */ 3188ecd6cf80Smarks if (ret != 0) 3189ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3190ecd6cf80Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 3191ecd6cf80Smarks 31921d452cf5Sahrens if (ret == 0 && recursive) { 3193cdf5b4caSmmusante struct createdata cd; 3194cdf5b4caSmmusante 3195cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3196cdf5b4caSmmusante cd.cd_ifexists = B_FALSE; 3197cdf5b4caSmmusante (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd); 31981d452cf5Sahrens } 3199fa9e4066Sahrens if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { 320099653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, path); 32011d452cf5Sahrens if (ret != 0) { 3202ecd6cf80Smarks (void) zfs_standard_error(hdl, errno, 3203ecd6cf80Smarks dgettext(TEXT_DOMAIN, 3204ecd6cf80Smarks "Volume successfully snapshotted, but device links " 3205ecd6cf80Smarks "were not created")); 3206ecd6cf80Smarks zfs_close(zhp); 3207ecd6cf80Smarks return (-1); 32081d452cf5Sahrens } 3209fa9e4066Sahrens } 3210fa9e4066Sahrens 321199653d4eSeschrock if (ret != 0) 321299653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3213fa9e4066Sahrens 3214fa9e4066Sahrens zfs_close(zhp); 3215fa9e4066Sahrens 3216fa9e4066Sahrens return (ret); 3217fa9e4066Sahrens } 3218fa9e4066Sahrens 3219fa9e4066Sahrens /* 3220b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3221b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3222b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3223b12a1c38Slling * group. 3224fa9e4066Sahrens */ 3225b12a1c38Slling typedef struct rollback_data { 3226b12a1c38Slling const char *cb_target; /* the snapshot */ 3227b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3228c391e322Sahrens boolean_t cb_error; 322999653d4eSeschrock boolean_t cb_dependent; 3230c391e322Sahrens boolean_t cb_force; 3231b12a1c38Slling } rollback_data_t; 3232b12a1c38Slling 3233b12a1c38Slling static int 3234b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3235b12a1c38Slling { 3236b12a1c38Slling rollback_data_t *cbp = data; 3237b12a1c38Slling 3238b12a1c38Slling if (!cbp->cb_dependent) { 3239b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3240b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3241b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3242b12a1c38Slling cbp->cb_create) { 3243ecd6cf80Smarks char *logstr; 3244b12a1c38Slling 324599653d4eSeschrock cbp->cb_dependent = B_TRUE; 32464ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32474ccbb6e7Sahrens rollback_destroy, cbp); 324899653d4eSeschrock cbp->cb_dependent = B_FALSE; 3249b12a1c38Slling 3250ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3251ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 32524ccbb6e7Sahrens cbp->cb_error |= zfs_destroy(zhp); 3253ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3254b12a1c38Slling } 3255b12a1c38Slling } else { 3256c391e322Sahrens /* We must destroy this clone; first unmount it */ 3257c391e322Sahrens prop_changelist_t *clp; 3258c391e322Sahrens 32590069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3260c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3261c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3262c391e322Sahrens cbp->cb_error = B_TRUE; 3263c391e322Sahrens zfs_close(zhp); 3264c391e322Sahrens return (0); 3265c391e322Sahrens } 3266c391e322Sahrens if (zfs_destroy(zhp) != 0) 3267c391e322Sahrens cbp->cb_error = B_TRUE; 3268c391e322Sahrens else 3269c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3270ba7b046eSahrens (void) changelist_postfix(clp); 3271c391e322Sahrens changelist_free(clp); 3272b12a1c38Slling } 3273b12a1c38Slling 3274b12a1c38Slling zfs_close(zhp); 3275b12a1c38Slling return (0); 3276b12a1c38Slling } 3277b12a1c38Slling 3278b12a1c38Slling /* 3279b12a1c38Slling * Given a dataset, rollback to a specific snapshot, discarding any 3280b12a1c38Slling * data changes since then and making it the active dataset. 3281b12a1c38Slling * 3282b12a1c38Slling * Any snapshots more recent than the target are destroyed, along with 3283b12a1c38Slling * their dependents. 3284b12a1c38Slling */ 3285b12a1c38Slling int 3286c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3287b12a1c38Slling { 3288b12a1c38Slling rollback_data_t cb = { 0 }; 32894ccbb6e7Sahrens int err; 32904ccbb6e7Sahrens zfs_cmd_t zc = { 0 }; 32917b97dc1aSrm boolean_t restore_resv = 0; 32927b97dc1aSrm uint64_t old_volsize, new_volsize; 32937b97dc1aSrm zfs_prop_t resv_prop; 3294b12a1c38Slling 32954ccbb6e7Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 32964ccbb6e7Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3297b12a1c38Slling 3298b12a1c38Slling /* 3299b12a1c38Slling * Destroy all recent snapshots and its dependends. 3300b12a1c38Slling */ 3301c391e322Sahrens cb.cb_force = force; 3302b12a1c38Slling cb.cb_target = snap->zfs_name; 3303b12a1c38Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3304b12a1c38Slling (void) zfs_iter_children(zhp, rollback_destroy, &cb); 3305b12a1c38Slling 3306c391e322Sahrens if (cb.cb_error) 3307c391e322Sahrens return (-1); 3308b12a1c38Slling 3309b12a1c38Slling /* 3310b12a1c38Slling * Now that we have verified that the snapshot is the latest, 3311b12a1c38Slling * rollback to the given snapshot. 3312b12a1c38Slling */ 3313b12a1c38Slling 33147b97dc1aSrm if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33157b97dc1aSrm if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 33167b97dc1aSrm return (-1); 33177b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33187b97dc1aSrm return (-1); 33197b97dc1aSrm old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33207b97dc1aSrm restore_resv = 33217b97dc1aSrm (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33227b97dc1aSrm } 33234ccbb6e7Sahrens 33244ccbb6e7Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 33254ccbb6e7Sahrens 33264ccbb6e7Sahrens if (ZFS_IS_VOLUME(zhp)) 33274ccbb6e7Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 33284ccbb6e7Sahrens else 33294ccbb6e7Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3330b12a1c38Slling 3331b12a1c38Slling /* 33324ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 33334ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 33344ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 33354ccbb6e7Sahrens * an unlikely race condition where the user has taken a 33364ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 33377b97dc1aSrm * 3338b12a1c38Slling */ 33394ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33404ccbb6e7Sahrens (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33414ccbb6e7Sahrens dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33424ccbb6e7Sahrens zhp->zfs_name); 3343b9415e83Srm return (err); 3344b9415e83Srm } 33457b97dc1aSrm 33467b97dc1aSrm /* 33477b97dc1aSrm * For volumes, if the pre-rollback volsize matched the pre- 33487b97dc1aSrm * rollback reservation and the volsize has changed then set 33497b97dc1aSrm * the reservation property to the post-rollback volsize. 33507b97dc1aSrm * Make a new handle since the rollback closed the dataset. 33517b97dc1aSrm */ 3352b9415e83Srm if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3353b9415e83Srm (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3354b9415e83Srm if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) { 3355b9415e83Srm zfs_close(zhp); 33567b97dc1aSrm return (err); 3357b9415e83Srm } 33587b97dc1aSrm if (restore_resv) { 33597b97dc1aSrm new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33607b97dc1aSrm if (old_volsize != new_volsize) 3361b9415e83Srm err = zfs_prop_set_int(zhp, resv_prop, 3362b9415e83Srm new_volsize); 33637b97dc1aSrm } 33647b97dc1aSrm zfs_close(zhp); 33654ccbb6e7Sahrens } 33664ccbb6e7Sahrens return (err); 3367b12a1c38Slling } 3368b12a1c38Slling 3369fa9e4066Sahrens /* 3370fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3371fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3372fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3373fa9e4066Sahrens * libzfs_graph.c. 3374fa9e4066Sahrens */ 3375fa9e4066Sahrens int 33763bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33773bb79becSeschrock zfs_iter_f func, void *data) 3378fa9e4066Sahrens { 3379fa9e4066Sahrens char **dependents; 3380fa9e4066Sahrens size_t count; 3381fa9e4066Sahrens int i; 3382fa9e4066Sahrens zfs_handle_t *child; 3383fa9e4066Sahrens int ret = 0; 3384fa9e4066Sahrens 33853bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 33863bb79becSeschrock &dependents, &count) != 0) 33873bb79becSeschrock return (-1); 33883bb79becSeschrock 3389fa9e4066Sahrens for (i = 0; i < count; i++) { 339099653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 339199653d4eSeschrock dependents[i])) == NULL) 3392fa9e4066Sahrens continue; 3393fa9e4066Sahrens 3394fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3395fa9e4066Sahrens break; 3396fa9e4066Sahrens } 3397fa9e4066Sahrens 3398fa9e4066Sahrens for (i = 0; i < count; i++) 3399fa9e4066Sahrens free(dependents[i]); 3400fa9e4066Sahrens free(dependents); 3401fa9e4066Sahrens 3402fa9e4066Sahrens return (ret); 3403fa9e4066Sahrens } 3404fa9e4066Sahrens 3405fa9e4066Sahrens /* 3406fa9e4066Sahrens * Renames the given dataset. 3407fa9e4066Sahrens */ 3408fa9e4066Sahrens int 34097f1f55eaSvb zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3410fa9e4066Sahrens { 3411fa9e4066Sahrens int ret; 3412fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3413fa9e4066Sahrens char *delim; 3414cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3415cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3416cdf5b4caSmmusante char *parentname = NULL; 3417fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 341899653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 341999653d4eSeschrock char errbuf[1024]; 3420fa9e4066Sahrens 3421fa9e4066Sahrens /* if we have the same exact name, just return success */ 3422fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3423fa9e4066Sahrens return (0); 3424fa9e4066Sahrens 342599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 342699653d4eSeschrock "cannot rename to '%s'"), target); 342799653d4eSeschrock 3428fa9e4066Sahrens /* 3429fa9e4066Sahrens * Make sure the target name is valid 3430fa9e4066Sahrens */ 3431fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 343298579b20Snd if ((strchr(target, '@') == NULL) || 343398579b20Snd *target == '@') { 343498579b20Snd /* 343598579b20Snd * Snapshot target name is abbreviated, 343698579b20Snd * reconstruct full dataset name 343798579b20Snd */ 343898579b20Snd (void) strlcpy(parent, zhp->zfs_name, 343998579b20Snd sizeof (parent)); 344098579b20Snd delim = strchr(parent, '@'); 344198579b20Snd if (strchr(target, '@') == NULL) 344298579b20Snd *(++delim) = '\0'; 344398579b20Snd else 344498579b20Snd *delim = '\0'; 344598579b20Snd (void) strlcat(parent, target, sizeof (parent)); 344698579b20Snd target = parent; 344798579b20Snd } else { 344898579b20Snd /* 344998579b20Snd * Make sure we're renaming within the same dataset. 345098579b20Snd */ 345198579b20Snd delim = strchr(target, '@'); 345298579b20Snd if (strncmp(zhp->zfs_name, target, delim - target) 345398579b20Snd != 0 || zhp->zfs_name[delim - target] != '@') { 345498579b20Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 345598579b20Snd "snapshots must be part of same " 345698579b20Snd "dataset")); 345798579b20Snd return (zfs_error(hdl, EZFS_CROSSTARGET, 3458b1b8ab34Slling errbuf)); 345998579b20Snd } 3460fa9e4066Sahrens } 3461f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 346298579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3463fa9e4066Sahrens } else { 3464cdf5b4caSmmusante if (recursive) { 3465cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3466cdf5b4caSmmusante "recursive rename must be a snapshot")); 3467cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3468cdf5b4caSmmusante } 3469cdf5b4caSmmusante 3470f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 347198579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3472e9dbad6fSeschrock uint64_t unused; 3473e9dbad6fSeschrock 3474fa9e4066Sahrens /* validate parents */ 34757f1f55eaSvb if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3476fa9e4066Sahrens return (-1); 3477fa9e4066Sahrens 3478fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3479fa9e4066Sahrens 3480fa9e4066Sahrens /* make sure we're in the same pool */ 3481fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3482fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3483fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 348499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 348599653d4eSeschrock "datasets must be within same pool")); 348699653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3487fa9e4066Sahrens } 3488f2fdf992Snd 3489f2fdf992Snd /* new name cannot be a child of the current dataset name */ 3490f2fdf992Snd if (strncmp(parent, zhp->zfs_name, 3491b1b8ab34Slling strlen(zhp->zfs_name)) == 0) { 3492f2fdf992Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3493f2fdf992Snd "New dataset name cannot be a descendent of " 3494f2fdf992Snd "current dataset name")); 3495f2fdf992Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3496f2fdf992Snd } 3497fa9e4066Sahrens } 3498fa9e4066Sahrens 349999653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 350099653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 350199653d4eSeschrock 3502fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3503fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 350499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 350599653d4eSeschrock "dataset is used in a non-global zone")); 350699653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3507fa9e4066Sahrens } 3508fa9e4066Sahrens 3509cdf5b4caSmmusante if (recursive) { 3510cdf5b4caSmmusante struct destroydata dd; 3511fa9e4066Sahrens 3512f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3513f0c5ee21Smmusante if (parentname == NULL) { 3514f0c5ee21Smmusante ret = -1; 3515f0c5ee21Smmusante goto error; 3516f0c5ee21Smmusante } 3517cdf5b4caSmmusante delim = strchr(parentname, '@'); 3518cdf5b4caSmmusante *delim = '\0'; 3519990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3520cdf5b4caSmmusante if (zhrp == NULL) { 3521f0c5ee21Smmusante ret = -1; 3522f0c5ee21Smmusante goto error; 3523cdf5b4caSmmusante } 3524fa9e4066Sahrens 3525cdf5b4caSmmusante dd.snapname = delim + 1; 3526cdf5b4caSmmusante dd.gotone = B_FALSE; 3527f0c5ee21Smmusante dd.closezhp = B_TRUE; 3528cdf5b4caSmmusante 3529cdf5b4caSmmusante /* We remove any zvol links prior to renaming them */ 3530cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd); 3531cdf5b4caSmmusante if (ret) { 3532cdf5b4caSmmusante goto error; 3533cdf5b4caSmmusante } 3534cdf5b4caSmmusante } else { 35350069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 3536cdf5b4caSmmusante return (-1); 3537cdf5b4caSmmusante 3538cdf5b4caSmmusante if (changelist_haszonedchild(cl)) { 3539cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3540cdf5b4caSmmusante "child dataset with inherited mountpoint is used " 3541cdf5b4caSmmusante "in a non-global zone")); 3542cdf5b4caSmmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3543cdf5b4caSmmusante goto error; 3544cdf5b4caSmmusante } 3545cdf5b4caSmmusante 3546cdf5b4caSmmusante if ((ret = changelist_prefix(cl)) != 0) 3547cdf5b4caSmmusante goto error; 3548cdf5b4caSmmusante } 3549fa9e4066Sahrens 3550e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3551fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3552fa9e4066Sahrens else 3553fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3554fa9e4066Sahrens 355598579b20Snd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3556e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 355798579b20Snd 3558cdf5b4caSmmusante zc.zc_cookie = recursive; 3559cdf5b4caSmmusante 3560ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3561cdf5b4caSmmusante /* 3562cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3563cdf5b4caSmmusante * be in zc.zc_name 3564cdf5b4caSmmusante */ 3565cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35663cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3567cdf5b4caSmmusante 3568cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3569cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3570cdf5b4caSmmusante "a child dataset already has a snapshot " 3571cdf5b4caSmmusante "with the new name")); 3572a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3573cdf5b4caSmmusante } else { 3574cdf5b4caSmmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3575cdf5b4caSmmusante } 3576fa9e4066Sahrens 3577fa9e4066Sahrens /* 3578fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3579fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3580fa9e4066Sahrens */ 3581cdf5b4caSmmusante if (recursive) { 3582cdf5b4caSmmusante struct createdata cd; 3583cdf5b4caSmmusante 3584cdf5b4caSmmusante /* only create links for datasets that had existed */ 3585cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3586cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3587cdf5b4caSmmusante (void) zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3588cdf5b4caSmmusante &cd); 3589cdf5b4caSmmusante } else { 3590cdf5b4caSmmusante (void) changelist_postfix(cl); 3591cdf5b4caSmmusante } 3592fa9e4066Sahrens } else { 3593cdf5b4caSmmusante if (recursive) { 3594cdf5b4caSmmusante struct createdata cd; 3595cdf5b4caSmmusante 3596cdf5b4caSmmusante /* only create links for datasets that had existed */ 3597cdf5b4caSmmusante cd.cd_snapname = strchr(target, '@') + 1; 3598cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3599cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3600cdf5b4caSmmusante &cd); 3601cdf5b4caSmmusante } else { 3602cdf5b4caSmmusante changelist_rename(cl, zfs_get_name(zhp), target); 3603cdf5b4caSmmusante ret = changelist_postfix(cl); 3604cdf5b4caSmmusante } 3605fa9e4066Sahrens } 3606fa9e4066Sahrens 3607fa9e4066Sahrens error: 3608cdf5b4caSmmusante if (parentname) { 3609cdf5b4caSmmusante free(parentname); 3610cdf5b4caSmmusante } 3611cdf5b4caSmmusante if (zhrp) { 3612cdf5b4caSmmusante zfs_close(zhrp); 3613cdf5b4caSmmusante } 3614cdf5b4caSmmusante if (cl) { 3615cdf5b4caSmmusante changelist_free(cl); 3616cdf5b4caSmmusante } 3617fa9e4066Sahrens return (ret); 3618fa9e4066Sahrens } 3619fa9e4066Sahrens 3620fa9e4066Sahrens /* 3621fa9e4066Sahrens * Given a zvol dataset, issue the ioctl to create the appropriate minor node, 3622fa9e4066Sahrens * poke devfsadm to create the /dev link, and then wait for the link to appear. 3623fa9e4066Sahrens */ 3624fa9e4066Sahrens int 362599653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset) 3626cdf5b4caSmmusante { 3627cdf5b4caSmmusante return (zvol_create_link_common(hdl, dataset, B_FALSE)); 3628cdf5b4caSmmusante } 3629cdf5b4caSmmusante 3630cdf5b4caSmmusante static int 3631cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists) 3632fa9e4066Sahrens { 3633fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 363499653d4eSeschrock di_devlink_handle_t dhdl; 3635ecd6cf80Smarks priv_set_t *priv_effective; 3636ecd6cf80Smarks int privileged; 3637fa9e4066Sahrens 3638fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3639fa9e4066Sahrens 3640fa9e4066Sahrens /* 3641fa9e4066Sahrens * Issue the appropriate ioctl. 3642fa9e4066Sahrens */ 364399653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { 3644fa9e4066Sahrens switch (errno) { 3645fa9e4066Sahrens case EEXIST: 3646fa9e4066Sahrens /* 3647fa9e4066Sahrens * Silently ignore the case where the link already 3648fa9e4066Sahrens * exists. This allows 'zfs volinit' to be run multiple 3649fa9e4066Sahrens * times without errors. 3650fa9e4066Sahrens */ 3651fa9e4066Sahrens return (0); 3652fa9e4066Sahrens 3653cdf5b4caSmmusante case ENOENT: 3654cdf5b4caSmmusante /* 3655cdf5b4caSmmusante * Dataset does not exist in the kernel. If we 3656cdf5b4caSmmusante * don't care (see zfs_rename), then ignore the 3657cdf5b4caSmmusante * error quietly. 3658cdf5b4caSmmusante */ 3659cdf5b4caSmmusante if (ifexists) { 3660cdf5b4caSmmusante return (0); 3661cdf5b4caSmmusante } 3662cdf5b4caSmmusante 3663cdf5b4caSmmusante /* FALLTHROUGH */ 3664cdf5b4caSmmusante 3665fa9e4066Sahrens default: 3666ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 366799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 366899653d4eSeschrock "for '%s'"), dataset)); 3669fa9e4066Sahrens } 3670fa9e4066Sahrens } 3671fa9e4066Sahrens 3672fa9e4066Sahrens /* 3673ecd6cf80Smarks * If privileged call devfsadm and wait for the links to 3674ecd6cf80Smarks * magically appear. 3675ecd6cf80Smarks * Otherwise, print out an informational message. 3676fa9e4066Sahrens */ 3677ecd6cf80Smarks 3678ecd6cf80Smarks priv_effective = priv_allocset(); 3679ecd6cf80Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 3680ecd6cf80Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 3681ecd6cf80Smarks priv_freeset(priv_effective); 3682ecd6cf80Smarks 3683ecd6cf80Smarks if (privileged) { 3684ecd6cf80Smarks if ((dhdl = di_devlink_init(ZFS_DRIVER, 3685ecd6cf80Smarks DI_MAKE_LINK)) == NULL) { 3686ecd6cf80Smarks zfs_error_aux(hdl, strerror(errno)); 3687d1e7ea07SEric Taylor (void) zfs_error_fmt(hdl, errno, 3688ecd6cf80Smarks dgettext(TEXT_DOMAIN, "cannot create device links " 3689ecd6cf80Smarks "for '%s'"), dataset); 3690ecd6cf80Smarks (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); 3691ecd6cf80Smarks return (-1); 3692ecd6cf80Smarks } else { 3693ecd6cf80Smarks (void) di_devlink_fini(&dhdl); 3694ecd6cf80Smarks } 3695fa9e4066Sahrens } else { 3696ecd6cf80Smarks char pathname[MAXPATHLEN]; 3697ecd6cf80Smarks struct stat64 statbuf; 3698ecd6cf80Smarks int i; 3699ecd6cf80Smarks 3700ecd6cf80Smarks #define MAX_WAIT 10 3701ecd6cf80Smarks 3702ecd6cf80Smarks /* 3703ecd6cf80Smarks * This is the poor mans way of waiting for the link 3704ecd6cf80Smarks * to show up. If after 10 seconds we still don't 3705ecd6cf80Smarks * have it, then print out a message. 3706ecd6cf80Smarks */ 3707ecd6cf80Smarks (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s", 3708ecd6cf80Smarks dataset); 3709ecd6cf80Smarks 3710ecd6cf80Smarks for (i = 0; i != MAX_WAIT; i++) { 3711ecd6cf80Smarks if (stat64(pathname, &statbuf) == 0) 3712ecd6cf80Smarks break; 3713ecd6cf80Smarks (void) sleep(1); 3714ecd6cf80Smarks } 3715ecd6cf80Smarks if (i == MAX_WAIT) 3716ecd6cf80Smarks (void) printf(gettext("%s may not be immediately " 3717ecd6cf80Smarks "available\n"), pathname); 3718fa9e4066Sahrens } 3719fa9e4066Sahrens 3720fa9e4066Sahrens return (0); 3721fa9e4066Sahrens } 3722fa9e4066Sahrens 3723fa9e4066Sahrens /* 3724fa9e4066Sahrens * Remove a minor node for the given zvol and the associated /dev links. 3725fa9e4066Sahrens */ 3726fa9e4066Sahrens int 372799653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) 3728fa9e4066Sahrens { 3729fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3730fa9e4066Sahrens 3731fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3732fa9e4066Sahrens 373399653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { 3734fa9e4066Sahrens switch (errno) { 3735fa9e4066Sahrens case ENXIO: 3736fa9e4066Sahrens /* 3737fa9e4066Sahrens * Silently ignore the case where the link no longer 3738fa9e4066Sahrens * exists, so that 'zfs volfini' can be run multiple 3739fa9e4066Sahrens * times without errors. 3740fa9e4066Sahrens */ 3741fa9e4066Sahrens return (0); 3742fa9e4066Sahrens 3743fa9e4066Sahrens default: 3744ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 374599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot remove device " 374699653d4eSeschrock "links for '%s'"), dataset)); 3747fa9e4066Sahrens } 3748fa9e4066Sahrens } 3749fa9e4066Sahrens 3750fa9e4066Sahrens return (0); 3751fa9e4066Sahrens } 3752e9dbad6fSeschrock 3753e9dbad6fSeschrock nvlist_t * 3754e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3755e9dbad6fSeschrock { 3756e9dbad6fSeschrock return (zhp->zfs_user_props); 3757e9dbad6fSeschrock } 3758e9dbad6fSeschrock 3759b1b8ab34Slling /* 3760b1b8ab34Slling * This function is used by 'zfs list' to determine the exact set of columns to 3761b1b8ab34Slling * display, and their maximum widths. This does two main things: 3762b1b8ab34Slling * 3763b1b8ab34Slling * - If this is a list of all properties, then expand the list to include 3764b1b8ab34Slling * all native properties, and set a flag so that for each dataset we look 3765b1b8ab34Slling * for new unique user properties and add them to the list. 3766b1b8ab34Slling * 3767b1b8ab34Slling * - For non fixed-width properties, keep track of the maximum width seen 3768b1b8ab34Slling * so that we can size the column appropriately. 3769b1b8ab34Slling */ 3770b1b8ab34Slling int 3771990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) 3772b1b8ab34Slling { 3773b1b8ab34Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 3774990b4856Slling zprop_list_t *entry; 3775990b4856Slling zprop_list_t **last, **start; 3776b1b8ab34Slling nvlist_t *userprops, *propval; 3777b1b8ab34Slling nvpair_t *elem; 3778b1b8ab34Slling char *strval; 3779b1b8ab34Slling char buf[ZFS_MAXPROPLEN]; 3780b1b8ab34Slling 3781990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3782b1b8ab34Slling return (-1); 3783e9dbad6fSeschrock 3784e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3785e9dbad6fSeschrock 3786e9dbad6fSeschrock entry = *plp; 3787e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3788e9dbad6fSeschrock /* 3789e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3790e9dbad6fSeschrock * start by incrementing our list pointer to the first 3791e9dbad6fSeschrock * non-native property. 3792e9dbad6fSeschrock */ 3793e9dbad6fSeschrock start = plp; 3794e9dbad6fSeschrock while (*start != NULL) { 3795990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3796e9dbad6fSeschrock break; 3797e9dbad6fSeschrock start = &(*start)->pl_next; 3798e9dbad6fSeschrock } 3799e9dbad6fSeschrock 3800e9dbad6fSeschrock elem = NULL; 3801e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3802e9dbad6fSeschrock /* 3803e9dbad6fSeschrock * See if we've already found this property in our list. 3804e9dbad6fSeschrock */ 3805e9dbad6fSeschrock for (last = start; *last != NULL; 3806e9dbad6fSeschrock last = &(*last)->pl_next) { 3807e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3808e9dbad6fSeschrock nvpair_name(elem)) == 0) 3809e9dbad6fSeschrock break; 3810e9dbad6fSeschrock } 3811e9dbad6fSeschrock 3812e9dbad6fSeschrock if (*last == NULL) { 3813e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3814990b4856Slling sizeof (zprop_list_t))) == NULL || 3815e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3816e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3817e9dbad6fSeschrock free(entry); 3818e9dbad6fSeschrock return (-1); 3819e9dbad6fSeschrock } 3820e9dbad6fSeschrock 3821990b4856Slling entry->pl_prop = ZPROP_INVAL; 3822e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3823e9dbad6fSeschrock entry->pl_all = B_TRUE; 3824e9dbad6fSeschrock *last = entry; 3825e9dbad6fSeschrock } 3826e9dbad6fSeschrock } 3827e9dbad6fSeschrock } 3828e9dbad6fSeschrock 3829e9dbad6fSeschrock /* 3830e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3831e9dbad6fSeschrock */ 3832e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3833e9dbad6fSeschrock if (entry->pl_fixed) 3834e9dbad6fSeschrock continue; 3835e9dbad6fSeschrock 3836990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3837e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3838e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3839e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3840e9dbad6fSeschrock entry->pl_width = strlen(buf); 3841e9dbad6fSeschrock } 3842e9dbad6fSeschrock } else if (nvlist_lookup_nvlist(userprops, 3843e9dbad6fSeschrock entry->pl_user_prop, &propval) == 0) { 3844e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3845990b4856Slling ZPROP_VALUE, &strval) == 0); 3846e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3847e9dbad6fSeschrock entry->pl_width = strlen(strval); 3848e9dbad6fSeschrock } 3849e9dbad6fSeschrock } 3850e9dbad6fSeschrock 3851e9dbad6fSeschrock return (0); 3852e9dbad6fSeschrock } 3853ecd6cf80Smarks 3854ecd6cf80Smarks int 3855ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 3856ecd6cf80Smarks { 3857ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3858ecd6cf80Smarks nvlist_t *nvp; 3859ecd6cf80Smarks gid_t gid; 3860ecd6cf80Smarks uid_t uid; 3861ecd6cf80Smarks const gid_t *groups; 3862ecd6cf80Smarks int group_cnt; 3863ecd6cf80Smarks int error; 3864ecd6cf80Smarks 3865ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 3866ecd6cf80Smarks return (no_memory(hdl)); 3867ecd6cf80Smarks 3868ecd6cf80Smarks uid = ucred_geteuid(cred); 3869ecd6cf80Smarks gid = ucred_getegid(cred); 3870ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 3871ecd6cf80Smarks 3872ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 3873ecd6cf80Smarks return (1); 3874ecd6cf80Smarks 3875ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 3876ecd6cf80Smarks nvlist_free(nvp); 3877ecd6cf80Smarks return (1); 3878ecd6cf80Smarks } 3879ecd6cf80Smarks 3880ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 3881ecd6cf80Smarks nvlist_free(nvp); 3882ecd6cf80Smarks return (1); 3883ecd6cf80Smarks } 3884ecd6cf80Smarks 3885ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 3886ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 3887ecd6cf80Smarks nvlist_free(nvp); 3888ecd6cf80Smarks return (1); 3889ecd6cf80Smarks } 3890ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3891ecd6cf80Smarks 3892990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 3893ecd6cf80Smarks return (-1); 3894ecd6cf80Smarks 3895ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 3896ecd6cf80Smarks nvlist_free(nvp); 3897ecd6cf80Smarks return (error); 3898ecd6cf80Smarks } 3899ecd6cf80Smarks 3900ecd6cf80Smarks int 3901ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3902743a77edSAlan Wright char *resource, void *export, void *sharetab, 3903743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3904ecd6cf80Smarks { 3905ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3906ecd6cf80Smarks int error; 3907ecd6cf80Smarks 3908ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3909ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3910743a77edSAlan Wright if (resource) 3911743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3912ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3913ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3914da6c28aaSamw zc.zc_share.z_sharetype = operation; 3915ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3916ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3917ecd6cf80Smarks return (error); 3918ecd6cf80Smarks } 39192e5e9e19SSanjeev Bagewadi 39202e5e9e19SSanjeev Bagewadi void 39212e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 39222e5e9e19SSanjeev Bagewadi { 39232e5e9e19SSanjeev Bagewadi nvpair_t *curr; 39242e5e9e19SSanjeev Bagewadi 39252e5e9e19SSanjeev Bagewadi /* 39262e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 39272e5e9e19SSanjeev Bagewadi * properties. 39282e5e9e19SSanjeev Bagewadi */ 39292e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 39302e5e9e19SSanjeev Bagewadi 39312e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 39322e5e9e19SSanjeev Bagewadi 39332e5e9e19SSanjeev Bagewadi while (curr) { 39342e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 39352e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 39362e5e9e19SSanjeev Bagewadi 3937*14843421SMatthew Ahrens /* 3938*14843421SMatthew Ahrens * We leave user:props in the nvlist, so there will be 3939*14843421SMatthew Ahrens * some ZPROP_INVAL. To be extra safe, don't prune 3940*14843421SMatthew Ahrens * those. 3941*14843421SMatthew Ahrens */ 3942*14843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 39432e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 39442e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 39452e5e9e19SSanjeev Bagewadi curr = next; 39462e5e9e19SSanjeev Bagewadi } 39472e5e9e19SSanjeev Bagewadi } 3948743a77edSAlan Wright 3949743a77edSAlan Wright static int 3950743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3951743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3952743a77edSAlan Wright { 3953743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3954743a77edSAlan Wright nvlist_t *nvlist = NULL; 3955743a77edSAlan Wright int error; 3956743a77edSAlan Wright 3957743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3958743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3959743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3960743a77edSAlan Wright 3961743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3962743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3963743a77edSAlan Wright (void) no_memory(hdl); 3964743a77edSAlan Wright return (NULL); 3965743a77edSAlan Wright } 3966743a77edSAlan Wright } 3967743a77edSAlan Wright 3968743a77edSAlan Wright switch (cmd) { 3969743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3970743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3971743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3972743a77edSAlan Wright break; 3973743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3974743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3975743a77edSAlan Wright resource1) != 0) { 3976743a77edSAlan Wright (void) no_memory(hdl); 3977743a77edSAlan Wright return (-1); 3978743a77edSAlan Wright } 3979743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 3980743a77edSAlan Wright resource2) != 0) { 3981743a77edSAlan Wright (void) no_memory(hdl); 3982743a77edSAlan Wright return (-1); 3983743a77edSAlan Wright } 3984743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 3985743a77edSAlan Wright nvlist_free(nvlist); 3986743a77edSAlan Wright return (-1); 3987743a77edSAlan Wright } 3988743a77edSAlan Wright break; 3989743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3990743a77edSAlan Wright break; 3991743a77edSAlan Wright default: 3992743a77edSAlan Wright return (-1); 3993743a77edSAlan Wright } 3994743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 3995743a77edSAlan Wright if (nvlist) 3996743a77edSAlan Wright nvlist_free(nvlist); 3997743a77edSAlan Wright return (error); 3998743a77edSAlan Wright } 3999743a77edSAlan Wright 4000743a77edSAlan Wright int 4001743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 4002743a77edSAlan Wright char *path, char *resource) 4003743a77edSAlan Wright { 4004743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 4005743a77edSAlan Wright resource, NULL)); 4006743a77edSAlan Wright } 4007743a77edSAlan Wright 4008743a77edSAlan Wright int 4009743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 4010743a77edSAlan Wright char *path, char *resource) 4011743a77edSAlan Wright { 4012743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 4013743a77edSAlan Wright resource, NULL)); 4014743a77edSAlan Wright } 4015743a77edSAlan Wright 4016743a77edSAlan Wright int 4017743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 4018743a77edSAlan Wright { 4019743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 4020743a77edSAlan Wright NULL, NULL)); 4021743a77edSAlan Wright } 4022743a77edSAlan Wright 4023743a77edSAlan Wright int 4024743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 4025743a77edSAlan Wright char *oldname, char *newname) 4026743a77edSAlan Wright { 4027743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 4028743a77edSAlan Wright oldname, newname)); 4029743a77edSAlan Wright } 4030*14843421SMatthew Ahrens 4031*14843421SMatthew Ahrens int 4032*14843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 4033*14843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 4034*14843421SMatthew Ahrens { 4035*14843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 4036*14843421SMatthew Ahrens int error; 4037*14843421SMatthew Ahrens zfs_useracct_t buf[100]; 4038*14843421SMatthew Ahrens 4039*14843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4040*14843421SMatthew Ahrens 4041*14843421SMatthew Ahrens zc.zc_objset_type = type; 4042*14843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 4043*14843421SMatthew Ahrens 4044*14843421SMatthew Ahrens /* CONSTCOND */ 4045*14843421SMatthew Ahrens while (1) { 4046*14843421SMatthew Ahrens zfs_useracct_t *zua = buf; 4047*14843421SMatthew Ahrens 4048*14843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 4049*14843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 4050*14843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 4051*14843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 4052*14843421SMatthew Ahrens break; 4053*14843421SMatthew Ahrens 4054*14843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 4055*14843421SMatthew Ahrens func(arg, zua->zu_domain, zua->zu_rid, zua->zu_space); 4056*14843421SMatthew Ahrens zua++; 4057*14843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 4058*14843421SMatthew Ahrens } 4059*14843421SMatthew Ahrens } 4060*14843421SMatthew Ahrens 4061*14843421SMatthew Ahrens return (error); 4062*14843421SMatthew Ahrens } 4063