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> 4814843421SMatthew Ahrens #include <idmap.h> 4914843421SMatthew Ahrens #include <aclutils.h> 50*3b12c289SMatthew Ahrens #include <directory.h> 51fa9e4066Sahrens 52fa9e4066Sahrens #include <sys/spa.h> 53e9dbad6fSeschrock #include <sys/zap.h> 54fa9e4066Sahrens #include <libzfs.h> 55fa9e4066Sahrens 56fa9e4066Sahrens #include "zfs_namecheck.h" 57fa9e4066Sahrens #include "zfs_prop.h" 58fa9e4066Sahrens #include "libzfs_impl.h" 59ecd6cf80Smarks #include "zfs_deleg.h" 60fa9e4066Sahrens 61cdf5b4caSmmusante static int zvol_create_link_common(libzfs_handle_t *, const char *, int); 6214843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned, 6314843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 64cdf5b4caSmmusante 65fa9e4066Sahrens /* 66fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 67fa9e4066Sahrens * readable form. 68fa9e4066Sahrens */ 69fa9e4066Sahrens const char * 70fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 71fa9e4066Sahrens { 72fa9e4066Sahrens switch (type) { 73fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 74fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 75fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 76fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 77fa9e4066Sahrens case ZFS_TYPE_VOLUME: 78fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 79fa9e4066Sahrens } 80fa9e4066Sahrens 81fa9e4066Sahrens return (NULL); 82fa9e4066Sahrens } 83fa9e4066Sahrens 84fa9e4066Sahrens /* 85fa9e4066Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 86fa9e4066Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 87fa9e4066Sahrens * We guess what the type would have been based on the path and the mask of 88fa9e4066Sahrens * acceptable types. 89fa9e4066Sahrens */ 90fa9e4066Sahrens static const char * 91fa9e4066Sahrens path_to_str(const char *path, int types) 92fa9e4066Sahrens { 93fa9e4066Sahrens /* 94fa9e4066Sahrens * When given a single type, always report the exact type. 95fa9e4066Sahrens */ 96fa9e4066Sahrens if (types == ZFS_TYPE_SNAPSHOT) 97fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 98fa9e4066Sahrens if (types == ZFS_TYPE_FILESYSTEM) 99fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 100fa9e4066Sahrens if (types == ZFS_TYPE_VOLUME) 101fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 102fa9e4066Sahrens 103fa9e4066Sahrens /* 104fa9e4066Sahrens * The user is requesting more than one type of dataset. If this is the 105fa9e4066Sahrens * case, consult the path itself. If we're looking for a snapshot, and 106fa9e4066Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 107fa9e4066Sahrens * snapshot attribute and try again. 108fa9e4066Sahrens */ 109fa9e4066Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 110fa9e4066Sahrens if (strchr(path, '@') != NULL) 111fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 112fa9e4066Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 113fa9e4066Sahrens } 114fa9e4066Sahrens 115fa9e4066Sahrens /* 116fa9e4066Sahrens * The user has requested either filesystems or volumes. 117fa9e4066Sahrens * We have no way of knowing a priori what type this would be, so always 118fa9e4066Sahrens * report it as "filesystem" or "volume", our two primitive types. 119fa9e4066Sahrens */ 120fa9e4066Sahrens if (types & ZFS_TYPE_FILESYSTEM) 121fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 122fa9e4066Sahrens 123fa9e4066Sahrens assert(types & ZFS_TYPE_VOLUME); 124fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 125fa9e4066Sahrens } 126fa9e4066Sahrens 127fa9e4066Sahrens /* 128fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 12914843421SMatthew Ahrens * provide a more meaningful error message. We call zfs_error_aux() to 13014843421SMatthew Ahrens * explain exactly why the name was not valid. 131fa9e4066Sahrens */ 132fa9e4066Sahrens static int 133f18faf3fSek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 134f18faf3fSek110237 boolean_t modifying) 135fa9e4066Sahrens { 136fa9e4066Sahrens namecheck_err_t why; 137fa9e4066Sahrens char what; 138fa9e4066Sahrens 139fa9e4066Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 14099653d4eSeschrock if (hdl != NULL) { 141fa9e4066Sahrens switch (why) { 142b81d61a6Slling case NAME_ERR_TOOLONG: 14399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14499653d4eSeschrock "name is too long")); 145b81d61a6Slling break; 146b81d61a6Slling 147fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 14899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14999653d4eSeschrock "leading slash in name")); 150fa9e4066Sahrens break; 151fa9e4066Sahrens 152fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 15399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15499653d4eSeschrock "empty component in name")); 155fa9e4066Sahrens break; 156fa9e4066Sahrens 157fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 15899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15999653d4eSeschrock "trailing slash in name")); 160fa9e4066Sahrens break; 161fa9e4066Sahrens 162fa9e4066Sahrens case NAME_ERR_INVALCHAR: 16399653d4eSeschrock zfs_error_aux(hdl, 164fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 16599653d4eSeschrock "'%c' in name"), what); 166fa9e4066Sahrens break; 167fa9e4066Sahrens 168fa9e4066Sahrens case NAME_ERR_MULTIPLE_AT: 16999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 17099653d4eSeschrock "multiple '@' delimiters in name")); 171fa9e4066Sahrens break; 1725ad82045Snd150628 1735ad82045Snd150628 case NAME_ERR_NOLETTER: 1745ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1755ad82045Snd150628 "pool doesn't begin with a letter")); 1765ad82045Snd150628 break; 1775ad82045Snd150628 1785ad82045Snd150628 case NAME_ERR_RESERVED: 1795ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1805ad82045Snd150628 "name is reserved")); 1815ad82045Snd150628 break; 1825ad82045Snd150628 1835ad82045Snd150628 case NAME_ERR_DISKLIKE: 1845ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1855ad82045Snd150628 "reserved disk name")); 1865ad82045Snd150628 break; 187fa9e4066Sahrens } 188fa9e4066Sahrens } 189fa9e4066Sahrens 190fa9e4066Sahrens return (0); 191fa9e4066Sahrens } 192fa9e4066Sahrens 193fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 19499653d4eSeschrock if (hdl != NULL) 19599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19699653d4eSeschrock "snapshot delimiter '@' in filesystem name")); 197fa9e4066Sahrens return (0); 198fa9e4066Sahrens } 199fa9e4066Sahrens 2001d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 2011d452cf5Sahrens if (hdl != NULL) 2021d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 203d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 2041d452cf5Sahrens return (0); 2051d452cf5Sahrens } 2061d452cf5Sahrens 207f18faf3fSek110237 if (modifying && strchr(path, '%') != NULL) { 208f18faf3fSek110237 if (hdl != NULL) 209f18faf3fSek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 210f18faf3fSek110237 "invalid character %c in name"), '%'); 211f18faf3fSek110237 return (0); 212f18faf3fSek110237 } 213f18faf3fSek110237 21499653d4eSeschrock return (-1); 215fa9e4066Sahrens } 216fa9e4066Sahrens 217fa9e4066Sahrens int 218fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 219fa9e4066Sahrens { 220e7cbe64fSgw25295 if (type == ZFS_TYPE_POOL) 221e7cbe64fSgw25295 return (zpool_name_valid(NULL, B_FALSE, name)); 222f18faf3fSek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 223fa9e4066Sahrens } 224fa9e4066Sahrens 225fa9e4066Sahrens /* 226e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 227e9dbad6fSeschrock * properties into a separate nvlist. 228e9dbad6fSeschrock */ 229fac3008cSeschrock static nvlist_t * 230fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 231e9dbad6fSeschrock { 232e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 233e9dbad6fSeschrock nvpair_t *elem; 234e9dbad6fSeschrock nvlist_t *propval; 235fac3008cSeschrock nvlist_t *nvl; 236e9dbad6fSeschrock 237fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 238fac3008cSeschrock (void) no_memory(hdl); 239fac3008cSeschrock return (NULL); 240fac3008cSeschrock } 241e9dbad6fSeschrock 242e9dbad6fSeschrock elem = NULL; 243fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 244e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 245e9dbad6fSeschrock continue; 246e9dbad6fSeschrock 247e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 248fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 249fac3008cSeschrock nvlist_free(nvl); 250fac3008cSeschrock (void) no_memory(hdl); 251fac3008cSeschrock return (NULL); 252fac3008cSeschrock } 253e9dbad6fSeschrock } 254e9dbad6fSeschrock 255fac3008cSeschrock return (nvl); 256e9dbad6fSeschrock } 257e9dbad6fSeschrock 25829ab75c9Srm160521 static zpool_handle_t * 25929ab75c9Srm160521 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 26029ab75c9Srm160521 { 26129ab75c9Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 26229ab75c9Srm160521 zpool_handle_t *zph; 26329ab75c9Srm160521 26429ab75c9Srm160521 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 26529ab75c9Srm160521 if (hdl->libzfs_pool_handles != NULL) 26629ab75c9Srm160521 zph->zpool_next = hdl->libzfs_pool_handles; 26729ab75c9Srm160521 hdl->libzfs_pool_handles = zph; 26829ab75c9Srm160521 } 26929ab75c9Srm160521 return (zph); 27029ab75c9Srm160521 } 27129ab75c9Srm160521 27229ab75c9Srm160521 static zpool_handle_t * 27329ab75c9Srm160521 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 27429ab75c9Srm160521 { 27529ab75c9Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 27629ab75c9Srm160521 zpool_handle_t *zph = hdl->libzfs_pool_handles; 27729ab75c9Srm160521 27829ab75c9Srm160521 while ((zph != NULL) && 27929ab75c9Srm160521 (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 28029ab75c9Srm160521 zph = zph->zpool_next; 28129ab75c9Srm160521 return (zph); 28229ab75c9Srm160521 } 28329ab75c9Srm160521 28429ab75c9Srm160521 /* 28529ab75c9Srm160521 * Returns a handle to the pool that contains the provided dataset. 28629ab75c9Srm160521 * If a handle to that pool already exists then that handle is returned. 28729ab75c9Srm160521 * Otherwise, a new handle is created and added to the list of handles. 28829ab75c9Srm160521 */ 28929ab75c9Srm160521 static zpool_handle_t * 29029ab75c9Srm160521 zpool_handle(zfs_handle_t *zhp) 29129ab75c9Srm160521 { 29229ab75c9Srm160521 char *pool_name; 29329ab75c9Srm160521 int len; 29429ab75c9Srm160521 zpool_handle_t *zph; 29529ab75c9Srm160521 29629ab75c9Srm160521 len = strcspn(zhp->zfs_name, "/@") + 1; 29729ab75c9Srm160521 pool_name = zfs_alloc(zhp->zfs_hdl, len); 29829ab75c9Srm160521 (void) strlcpy(pool_name, zhp->zfs_name, len); 29929ab75c9Srm160521 30029ab75c9Srm160521 zph = zpool_find_handle(zhp, pool_name, len); 30129ab75c9Srm160521 if (zph == NULL) 30229ab75c9Srm160521 zph = zpool_add_handle(zhp, pool_name); 30329ab75c9Srm160521 30429ab75c9Srm160521 free(pool_name); 30529ab75c9Srm160521 return (zph); 30629ab75c9Srm160521 } 30729ab75c9Srm160521 30829ab75c9Srm160521 void 30929ab75c9Srm160521 zpool_free_handles(libzfs_handle_t *hdl) 31029ab75c9Srm160521 { 31129ab75c9Srm160521 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 31229ab75c9Srm160521 31329ab75c9Srm160521 while (zph != NULL) { 31429ab75c9Srm160521 next = zph->zpool_next; 31529ab75c9Srm160521 zpool_close(zph); 31629ab75c9Srm160521 zph = next; 31729ab75c9Srm160521 } 31829ab75c9Srm160521 hdl->libzfs_pool_handles = NULL; 31929ab75c9Srm160521 } 32029ab75c9Srm160521 321e9dbad6fSeschrock /* 322fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 323fa9e4066Sahrens */ 324fa9e4066Sahrens static int 325ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 326fa9e4066Sahrens { 327e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 328fa9e4066Sahrens 329ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 330fa9e4066Sahrens 331ebedde84SEric Taylor while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3327f7322feSeschrock if (errno == ENOMEM) { 333ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 33499653d4eSeschrock return (-1); 335e9dbad6fSeschrock } 3367f7322feSeschrock } else { 337fa9e4066Sahrens return (-1); 3387f7322feSeschrock } 3397f7322feSeschrock } 340ebedde84SEric Taylor return (0); 341fac3008cSeschrock } 342fac3008cSeschrock 343ebedde84SEric Taylor static int 344ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 345ebedde84SEric Taylor { 346ebedde84SEric Taylor nvlist_t *allprops, *userprops; 347ebedde84SEric Taylor 348ebedde84SEric Taylor zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 349ebedde84SEric Taylor 350ebedde84SEric Taylor if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 351ebedde84SEric Taylor return (-1); 352ebedde84SEric Taylor } 353fac3008cSeschrock 35414843421SMatthew Ahrens /* 35514843421SMatthew Ahrens * XXX Why do we store the user props separately, in addition to 35614843421SMatthew Ahrens * storing them in zfs_props? 35714843421SMatthew Ahrens */ 358fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 359fac3008cSeschrock nvlist_free(allprops); 360fac3008cSeschrock return (-1); 361fac3008cSeschrock } 362fac3008cSeschrock 36399653d4eSeschrock nvlist_free(zhp->zfs_props); 364fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 36599653d4eSeschrock 366fac3008cSeschrock zhp->zfs_props = allprops; 367fac3008cSeschrock zhp->zfs_user_props = userprops; 36899653d4eSeschrock 369fa9e4066Sahrens return (0); 370fa9e4066Sahrens } 371fa9e4066Sahrens 372ebedde84SEric Taylor static int 373ebedde84SEric Taylor get_stats(zfs_handle_t *zhp) 374ebedde84SEric Taylor { 375ebedde84SEric Taylor int rc = 0; 376ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 377ebedde84SEric Taylor 378ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 379ebedde84SEric Taylor return (-1); 380ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) != 0) 381ebedde84SEric Taylor rc = -1; 382ebedde84SEric Taylor else if (put_stats_zhdl(zhp, &zc) != 0) 383ebedde84SEric Taylor rc = -1; 384ebedde84SEric Taylor zcmd_free_nvlists(&zc); 385ebedde84SEric Taylor return (rc); 386ebedde84SEric Taylor } 387ebedde84SEric Taylor 388fa9e4066Sahrens /* 389fa9e4066Sahrens * Refresh the properties currently stored in the handle. 390fa9e4066Sahrens */ 391fa9e4066Sahrens void 392fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 393fa9e4066Sahrens { 394fa9e4066Sahrens (void) get_stats(zhp); 395fa9e4066Sahrens } 396fa9e4066Sahrens 397fa9e4066Sahrens /* 398fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 399fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 400fa9e4066Sahrens */ 401ebedde84SEric Taylor static int 402ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 403fa9e4066Sahrens { 404ecd6cf80Smarks char *logstr; 405ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 406fa9e4066Sahrens 407ecd6cf80Smarks /* 408ecd6cf80Smarks * Preserve history log string. 409ecd6cf80Smarks * any changes performed here will be 410ecd6cf80Smarks * logged as an internal event. 411ecd6cf80Smarks */ 412ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 413ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 414fa9e4066Sahrens 415ebedde84SEric Taylor top: 416ebedde84SEric Taylor if (put_stats_zhdl(zhp, zc) != 0) { 417ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 418ebedde84SEric Taylor return (-1); 419fa9e4066Sahrens } 420fa9e4066Sahrens 421ebedde84SEric Taylor 42231fd60d3Sahrens if (zhp->zfs_dmustats.dds_inconsistent) { 423ebedde84SEric Taylor zfs_cmd_t zc2 = { 0 }; 42431fd60d3Sahrens 42531fd60d3Sahrens /* 42631fd60d3Sahrens * If it is dds_inconsistent, then we've caught it in 42731fd60d3Sahrens * the middle of a 'zfs receive' or 'zfs destroy', and 42831fd60d3Sahrens * it is inconsistent from the ZPL's point of view, so 42931fd60d3Sahrens * can't be mounted. However, it could also be that we 43031fd60d3Sahrens * have crashed in the middle of one of those 43131fd60d3Sahrens * operations, in which case we need to get rid of the 43231fd60d3Sahrens * inconsistent state. We do that by either rolling 43331fd60d3Sahrens * back to the previous snapshot (which will fail if 43431fd60d3Sahrens * there is none), or destroying the filesystem. Note 43531fd60d3Sahrens * that if we are still in the middle of an active 43631fd60d3Sahrens * 'receive' or 'destroy', then the rollback and destroy 43731fd60d3Sahrens * will fail with EBUSY and we will drive on as usual. 43831fd60d3Sahrens */ 43931fd60d3Sahrens 440ebedde84SEric Taylor (void) strlcpy(zc2.zc_name, zhp->zfs_name, 441ebedde84SEric Taylor sizeof (zc2.zc_name)); 44231fd60d3Sahrens 443a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) { 44499653d4eSeschrock (void) zvol_remove_link(hdl, zhp->zfs_name); 445ebedde84SEric Taylor zc2.zc_objset_type = DMU_OST_ZVOL; 44631fd60d3Sahrens } else { 447ebedde84SEric Taylor zc2.zc_objset_type = DMU_OST_ZFS; 44831fd60d3Sahrens } 44931fd60d3Sahrens 45031fd60d3Sahrens /* 451da6c28aaSamw * If we can successfully destroy it, pretend that it 45231fd60d3Sahrens * never existed. 45331fd60d3Sahrens */ 454ebedde84SEric Taylor if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc2) == 0) { 455ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 45631fd60d3Sahrens errno = ENOENT; 457ebedde84SEric Taylor return (-1); 45831fd60d3Sahrens } 459ebedde84SEric Taylor /* If we can successfully roll it back, reset the stats */ 460ebedde84SEric Taylor if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc2) == 0) { 461ebedde84SEric Taylor if (get_stats_ioctl(zhp, zc) != 0) { 462ebedde84SEric Taylor zhp->zfs_hdl->libzfs_log_str = logstr; 463ebedde84SEric Taylor return (-1); 464ebedde84SEric Taylor } 4653cb34c60Sahrens goto top; 46631fd60d3Sahrens } 467ebedde84SEric Taylor } 46831fd60d3Sahrens 469fa9e4066Sahrens /* 470fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 471fa9e4066Sahrens * the high-level type. 472fa9e4066Sahrens */ 473a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 474a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 475a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 476a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 477a2eea2e1Sahrens else 478a2eea2e1Sahrens abort(); 479a2eea2e1Sahrens 480fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 481fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 482fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 483fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 484fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 485fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 486fa9e4066Sahrens else 48799653d4eSeschrock abort(); /* we should never see any other types */ 488fa9e4066Sahrens 489ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 49029ab75c9Srm160521 zhp->zpool_hdl = zpool_handle(zhp); 491ebedde84SEric Taylor return (0); 492ebedde84SEric Taylor } 493ebedde84SEric Taylor 494ebedde84SEric Taylor zfs_handle_t * 495ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 496ebedde84SEric Taylor { 497ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 498ebedde84SEric Taylor 499ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 500ebedde84SEric Taylor 501ebedde84SEric Taylor if (zhp == NULL) 502ebedde84SEric Taylor return (NULL); 503ebedde84SEric Taylor 504ebedde84SEric Taylor zhp->zfs_hdl = hdl; 505ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 506ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 507ebedde84SEric Taylor free(zhp); 508ebedde84SEric Taylor return (NULL); 509ebedde84SEric Taylor } 510ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 511ebedde84SEric Taylor zcmd_free_nvlists(&zc); 512ebedde84SEric Taylor free(zhp); 513ebedde84SEric Taylor return (NULL); 514ebedde84SEric Taylor } 515ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 516ebedde84SEric Taylor free(zhp); 517ebedde84SEric Taylor zhp = NULL; 518ebedde84SEric Taylor } 519ebedde84SEric Taylor zcmd_free_nvlists(&zc); 520ebedde84SEric Taylor return (zhp); 521ebedde84SEric Taylor } 522ebedde84SEric Taylor 523ebedde84SEric Taylor static zfs_handle_t * 524ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 525ebedde84SEric Taylor { 526ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 527ebedde84SEric Taylor 528ebedde84SEric Taylor if (zhp == NULL) 529ebedde84SEric Taylor return (NULL); 530ebedde84SEric Taylor 531ebedde84SEric Taylor zhp->zfs_hdl = hdl; 532ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 533ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 534ebedde84SEric Taylor free(zhp); 535ebedde84SEric Taylor return (NULL); 536ebedde84SEric Taylor } 537fa9e4066Sahrens return (zhp); 538fa9e4066Sahrens } 539fa9e4066Sahrens 540fa9e4066Sahrens /* 541fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 542fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 543fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 544fa9e4066Sahrens */ 545fa9e4066Sahrens zfs_handle_t * 54699653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 547fa9e4066Sahrens { 548fa9e4066Sahrens zfs_handle_t *zhp; 54999653d4eSeschrock char errbuf[1024]; 55099653d4eSeschrock 55199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 55299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 553fa9e4066Sahrens 554fa9e4066Sahrens /* 55599653d4eSeschrock * Validate the name before we even try to open it. 556fa9e4066Sahrens */ 557f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 55899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 55999653d4eSeschrock "invalid dataset name")); 56099653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 561fa9e4066Sahrens return (NULL); 562fa9e4066Sahrens } 563fa9e4066Sahrens 564fa9e4066Sahrens /* 565fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 566fa9e4066Sahrens */ 567fa9e4066Sahrens errno = 0; 56899653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 569ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 570fa9e4066Sahrens return (NULL); 571fa9e4066Sahrens } 572fa9e4066Sahrens 573fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 57499653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 57594de1d4cSeschrock zfs_close(zhp); 576fa9e4066Sahrens return (NULL); 577fa9e4066Sahrens } 578fa9e4066Sahrens 579fa9e4066Sahrens return (zhp); 580fa9e4066Sahrens } 581fa9e4066Sahrens 582fa9e4066Sahrens /* 583fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 584fa9e4066Sahrens */ 585fa9e4066Sahrens void 586fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 587fa9e4066Sahrens { 588fa9e4066Sahrens if (zhp->zfs_mntopts) 589fa9e4066Sahrens free(zhp->zfs_mntopts); 59099653d4eSeschrock nvlist_free(zhp->zfs_props); 591e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 592fa9e4066Sahrens free(zhp); 593fa9e4066Sahrens } 594fa9e4066Sahrens 595ebedde84SEric Taylor typedef struct mnttab_node { 596ebedde84SEric Taylor struct mnttab mtn_mt; 597ebedde84SEric Taylor avl_node_t mtn_node; 598ebedde84SEric Taylor } mnttab_node_t; 599ebedde84SEric Taylor 600ebedde84SEric Taylor static int 601ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 602ebedde84SEric Taylor { 603ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 604ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 605ebedde84SEric Taylor int rv; 606ebedde84SEric Taylor 607ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 608ebedde84SEric Taylor 609ebedde84SEric Taylor if (rv == 0) 610ebedde84SEric Taylor return (0); 611ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 612ebedde84SEric Taylor } 613ebedde84SEric Taylor 614ebedde84SEric Taylor void 615ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 616ebedde84SEric Taylor { 617ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 618ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 619ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 620b2634b9cSEric Taylor } 621b2634b9cSEric Taylor 622b2634b9cSEric Taylor void 623b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 624b2634b9cSEric Taylor { 625b2634b9cSEric Taylor struct mnttab entry; 626ebedde84SEric Taylor 627ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 628ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 629ebedde84SEric Taylor mnttab_node_t *mtn; 630ebedde84SEric Taylor 631ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 632ebedde84SEric Taylor continue; 633ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 634ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 635ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 636ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 637ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 638ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 639ebedde84SEric Taylor } 640ebedde84SEric Taylor } 641ebedde84SEric Taylor 642ebedde84SEric Taylor void 643ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 644ebedde84SEric Taylor { 645ebedde84SEric Taylor void *cookie = NULL; 646ebedde84SEric Taylor mnttab_node_t *mtn; 647ebedde84SEric Taylor 648ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 649ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 650ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 651ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 652ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 653ebedde84SEric Taylor free(mtn); 654ebedde84SEric Taylor } 655ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 656ebedde84SEric Taylor } 657ebedde84SEric Taylor 658b2634b9cSEric Taylor void 659b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 660b2634b9cSEric Taylor { 661b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 662b2634b9cSEric Taylor } 663b2634b9cSEric Taylor 664ebedde84SEric Taylor int 665ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 666ebedde84SEric Taylor struct mnttab *entry) 667ebedde84SEric Taylor { 668ebedde84SEric Taylor mnttab_node_t find; 669ebedde84SEric Taylor mnttab_node_t *mtn; 670ebedde84SEric Taylor 671b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 672b2634b9cSEric Taylor struct mnttab srch = { 0 }; 673b2634b9cSEric Taylor 674b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 675b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 676b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 677b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 678b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 679b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 680b2634b9cSEric Taylor return (0); 681b2634b9cSEric Taylor else 682b2634b9cSEric Taylor return (ENOENT); 683b2634b9cSEric Taylor } 684b2634b9cSEric Taylor 685ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 686b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 687ebedde84SEric Taylor 688ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 689ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 690ebedde84SEric Taylor if (mtn) { 691ebedde84SEric Taylor *entry = mtn->mtn_mt; 692ebedde84SEric Taylor return (0); 693ebedde84SEric Taylor } 694ebedde84SEric Taylor return (ENOENT); 695ebedde84SEric Taylor } 696ebedde84SEric Taylor 697ebedde84SEric Taylor void 698ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 699ebedde84SEric Taylor const char *mountp, const char *mntopts) 700ebedde84SEric Taylor { 701ebedde84SEric Taylor mnttab_node_t *mtn; 702ebedde84SEric Taylor 703ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 704ebedde84SEric Taylor return; 705ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 706ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 707ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 708ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 709ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 710ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 711ebedde84SEric Taylor } 712ebedde84SEric Taylor 713ebedde84SEric Taylor void 714ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 715ebedde84SEric Taylor { 716ebedde84SEric Taylor mnttab_node_t find; 717ebedde84SEric Taylor mnttab_node_t *ret; 718ebedde84SEric Taylor 719ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 720ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 721ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 722ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 723ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 724ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 725ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 726ebedde84SEric Taylor free(ret); 727ebedde84SEric Taylor } 728ebedde84SEric Taylor } 729ebedde84SEric Taylor 7307b97dc1aSrm160521 int 7317b97dc1aSrm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7327b97dc1aSrm160521 { 73329ab75c9Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7347b97dc1aSrm160521 7357b97dc1aSrm160521 if (zpool_handle == NULL) 7367b97dc1aSrm160521 return (-1); 7377b97dc1aSrm160521 7387b97dc1aSrm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7397b97dc1aSrm160521 ZPOOL_PROP_VERSION, NULL); 7407b97dc1aSrm160521 return (0); 7417b97dc1aSrm160521 } 7427b97dc1aSrm160521 7437b97dc1aSrm160521 /* 7447b97dc1aSrm160521 * The choice of reservation property depends on the SPA version. 7457b97dc1aSrm160521 */ 7467b97dc1aSrm160521 static int 7477b97dc1aSrm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7487b97dc1aSrm160521 { 7497b97dc1aSrm160521 int spa_version; 7507b97dc1aSrm160521 7517b97dc1aSrm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7527b97dc1aSrm160521 return (-1); 7537b97dc1aSrm160521 7547b97dc1aSrm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7557b97dc1aSrm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7567b97dc1aSrm160521 else 7577b97dc1aSrm160521 *resv_prop = ZFS_PROP_RESERVATION; 7587b97dc1aSrm160521 7597b97dc1aSrm160521 return (0); 7607b97dc1aSrm160521 } 7617b97dc1aSrm160521 762b1b8ab34Slling /* 763e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 764e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 765e9dbad6fSeschrock * strings. 766fa9e4066Sahrens */ 7670a48a24eStimh nvlist_t * 7680a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 769990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 770fa9e4066Sahrens { 771e9dbad6fSeschrock nvpair_t *elem; 772e9dbad6fSeschrock uint64_t intval; 773e9dbad6fSeschrock char *strval; 774990b4856Slling zfs_prop_t prop; 775e9dbad6fSeschrock nvlist_t *ret; 776da6c28aaSamw int chosen_normal = -1; 777da6c28aaSamw int chosen_utf = -1; 778990b4856Slling 779e9dbad6fSeschrock if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 780e9dbad6fSeschrock (void) no_memory(hdl); 781e9dbad6fSeschrock return (NULL); 782e9dbad6fSeschrock } 783fa9e4066Sahrens 78414843421SMatthew Ahrens /* 78514843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 78614843421SMatthew Ahrens */ 78714843421SMatthew Ahrens 788e9dbad6fSeschrock elem = NULL; 789e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 790990b4856Slling const char *propname = nvpair_name(elem); 79199653d4eSeschrock 79214843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 79314843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 794fa9e4066Sahrens /* 79514843421SMatthew Ahrens * This is a user property: make sure it's a 796990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 797990b4856Slling */ 798990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 799990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 800990b4856Slling "'%s' must be a string"), propname); 801990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 802990b4856Slling goto error; 803990b4856Slling } 804990b4856Slling 805990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 806e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 807e9dbad6fSeschrock "property name '%s' is too long"), 808e9dbad6fSeschrock propname); 809990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 810e9dbad6fSeschrock goto error; 811e9dbad6fSeschrock } 812e9dbad6fSeschrock 813e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 814e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 815e9dbad6fSeschrock (void) no_memory(hdl); 816e9dbad6fSeschrock goto error; 817e9dbad6fSeschrock } 818e9dbad6fSeschrock continue; 819e9dbad6fSeschrock } 820fa9e4066Sahrens 82114843421SMatthew Ahrens /* 82214843421SMatthew Ahrens * Currently, only user properties can be modified on 82314843421SMatthew Ahrens * snapshots. 82414843421SMatthew Ahrens */ 825bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 826bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 827bb0ade09Sahrens "this property can not be modified for snapshots")); 828bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 829bb0ade09Sahrens goto error; 830bb0ade09Sahrens } 831bb0ade09Sahrens 83214843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 83314843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 83414843421SMatthew Ahrens char newpropname[128]; 83514843421SMatthew Ahrens char domain[128]; 83614843421SMatthew Ahrens uint64_t rid; 83714843421SMatthew Ahrens uint64_t valary[3]; 83814843421SMatthew Ahrens 83914843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 84014843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 84114843421SMatthew Ahrens zfs_error_aux(hdl, 84214843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 84314843421SMatthew Ahrens "'%s' has an invalid user/group name"), 84414843421SMatthew Ahrens propname); 84514843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 84614843421SMatthew Ahrens goto error; 84714843421SMatthew Ahrens } 84814843421SMatthew Ahrens 84914843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 85014843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 85114843421SMatthew Ahrens zfs_error_aux(hdl, 85214843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 85314843421SMatthew Ahrens propname); 85414843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 85514843421SMatthew Ahrens errbuf); 85614843421SMatthew Ahrens goto error; 85714843421SMatthew Ahrens } 85814843421SMatthew Ahrens 85914843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 86014843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 86114843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 86214843421SMatthew Ahrens intval = 0; 86314843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 86414843421SMatthew Ahrens strval, &intval) != 0) { 86514843421SMatthew Ahrens (void) zfs_error(hdl, 86614843421SMatthew Ahrens EZFS_BADPROP, errbuf); 86714843421SMatthew Ahrens goto error; 86814843421SMatthew Ahrens } 86914843421SMatthew Ahrens } else if (nvpair_type(elem) == 87014843421SMatthew Ahrens DATA_TYPE_UINT64) { 87114843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 87214843421SMatthew Ahrens if (intval == 0) { 87314843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 87414843421SMatthew Ahrens "use 'none' to disable " 87514843421SMatthew Ahrens "userquota/groupquota")); 87614843421SMatthew Ahrens goto error; 87714843421SMatthew Ahrens } 87814843421SMatthew Ahrens } else { 87914843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 88014843421SMatthew Ahrens "'%s' must be a number"), propname); 88114843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 88214843421SMatthew Ahrens goto error; 88314843421SMatthew Ahrens } 88414843421SMatthew Ahrens 88514843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 88614843421SMatthew Ahrens "%s%s", zfs_userquota_prop_prefixes[uqtype], 88714843421SMatthew Ahrens domain); 88814843421SMatthew Ahrens valary[0] = uqtype; 88914843421SMatthew Ahrens valary[1] = rid; 89014843421SMatthew Ahrens valary[2] = intval; 89114843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 89214843421SMatthew Ahrens valary, 3) != 0) { 89314843421SMatthew Ahrens (void) no_memory(hdl); 89414843421SMatthew Ahrens goto error; 89514843421SMatthew Ahrens } 89614843421SMatthew Ahrens continue; 89714843421SMatthew Ahrens } 89814843421SMatthew Ahrens 89914843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 90014843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 90114843421SMatthew Ahrens "invalid property '%s'"), propname); 90214843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 90314843421SMatthew Ahrens goto error; 90414843421SMatthew Ahrens } 90514843421SMatthew Ahrens 906e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 907e9dbad6fSeschrock zfs_error_aux(hdl, 908e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 909e9dbad6fSeschrock "apply to datasets of this type"), propname); 910e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 911e9dbad6fSeschrock goto error; 912e9dbad6fSeschrock } 913e9dbad6fSeschrock 914e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 915da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 916e9dbad6fSeschrock zfs_error_aux(hdl, 917e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 918e9dbad6fSeschrock propname); 919e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 920e9dbad6fSeschrock goto error; 921e9dbad6fSeschrock } 922e9dbad6fSeschrock 923990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 924990b4856Slling &strval, &intval, errbuf) != 0) 925e9dbad6fSeschrock goto error; 926e9dbad6fSeschrock 927e9dbad6fSeschrock /* 928e9dbad6fSeschrock * Perform some additional checks for specific properties. 929e9dbad6fSeschrock */ 930e9dbad6fSeschrock switch (prop) { 931e7437265Sahrens case ZFS_PROP_VERSION: 932e7437265Sahrens { 933e7437265Sahrens int version; 934e7437265Sahrens 935e7437265Sahrens if (zhp == NULL) 936e7437265Sahrens break; 937e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 938e7437265Sahrens if (intval < version) { 939e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 940e7437265Sahrens "Can not downgrade; already at version %u"), 941e7437265Sahrens version); 942e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 943e7437265Sahrens goto error; 944e7437265Sahrens } 945e7437265Sahrens break; 946e7437265Sahrens } 947e7437265Sahrens 948e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 949e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 950e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 951e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 952e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 953e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 954e9dbad6fSeschrock "'%s' must be power of 2 from %u " 955e9dbad6fSeschrock "to %uk"), propname, 956e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 957e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 958e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 959e9dbad6fSeschrock goto error; 960e9dbad6fSeschrock } 961e9dbad6fSeschrock break; 962e9dbad6fSeschrock 963f3861e1aSahl case ZFS_PROP_SHAREISCSI: 964f3861e1aSahl if (strcmp(strval, "off") != 0 && 965f3861e1aSahl strcmp(strval, "on") != 0 && 966f3861e1aSahl strcmp(strval, "type=disk") != 0) { 967f3861e1aSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 968f3861e1aSahl "'%s' must be 'on', 'off', or 'type=disk'"), 969f3861e1aSahl propname); 970f3861e1aSahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 971f3861e1aSahl goto error; 972f3861e1aSahl } 973f3861e1aSahl 974f3861e1aSahl break; 975f3861e1aSahl 976e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 97789eef05eSrm160521 { 97889eef05eSrm160521 namecheck_err_t why; 97989eef05eSrm160521 980e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 981e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 982e9dbad6fSeschrock break; 983e9dbad6fSeschrock 98489eef05eSrm160521 if (mountpoint_namecheck(strval, &why)) { 98589eef05eSrm160521 switch (why) { 98689eef05eSrm160521 case NAME_ERR_LEADING_SLASH: 98789eef05eSrm160521 zfs_error_aux(hdl, 98889eef05eSrm160521 dgettext(TEXT_DOMAIN, 989e9dbad6fSeschrock "'%s' must be an absolute path, " 990e9dbad6fSeschrock "'none', or 'legacy'"), propname); 99189eef05eSrm160521 break; 99289eef05eSrm160521 case NAME_ERR_TOOLONG: 99389eef05eSrm160521 zfs_error_aux(hdl, 99489eef05eSrm160521 dgettext(TEXT_DOMAIN, 99589eef05eSrm160521 "component of '%s' is too long"), 99689eef05eSrm160521 propname); 99789eef05eSrm160521 break; 99889eef05eSrm160521 } 999e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1000e9dbad6fSeschrock goto error; 1001e9dbad6fSeschrock } 100289eef05eSrm160521 } 100389eef05eSrm160521 1004f3861e1aSahl /*FALLTHRU*/ 1005e9dbad6fSeschrock 1006da6c28aaSamw case ZFS_PROP_SHARESMB: 1007f3861e1aSahl case ZFS_PROP_SHARENFS: 1008e9dbad6fSeschrock /* 1009da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1010da6c28aaSamw * properties, check if it can be set in a 1011da6c28aaSamw * global/non-global zone based on 1012f3861e1aSahl * the zoned property value: 1013fa9e4066Sahrens * 1014fa9e4066Sahrens * global zone non-global zone 1015f3861e1aSahl * -------------------------------------------------- 1016fa9e4066Sahrens * zoned=on mountpoint (no) mountpoint (yes) 1017fa9e4066Sahrens * sharenfs (no) sharenfs (no) 1018da6c28aaSamw * sharesmb (no) sharesmb (no) 1019fa9e4066Sahrens * 1020fa9e4066Sahrens * zoned=off mountpoint (yes) N/A 1021fa9e4066Sahrens * sharenfs (yes) 1022da6c28aaSamw * sharesmb (yes) 1023fa9e4066Sahrens */ 1024e9dbad6fSeschrock if (zoned) { 1025fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID) { 102699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1027e9dbad6fSeschrock "'%s' cannot be set on " 1028e9dbad6fSeschrock "dataset in a non-global zone"), 1029e9dbad6fSeschrock propname); 1030e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1031e9dbad6fSeschrock errbuf); 1032e9dbad6fSeschrock goto error; 1033da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1034da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 103599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1036e9dbad6fSeschrock "'%s' cannot be set in " 1037e9dbad6fSeschrock "a non-global zone"), propname); 1038e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1039e9dbad6fSeschrock errbuf); 1040e9dbad6fSeschrock goto error; 1041fa9e4066Sahrens } 1042fa9e4066Sahrens } else if (getzoneid() != GLOBAL_ZONEID) { 1043fa9e4066Sahrens /* 1044fa9e4066Sahrens * If zoned property is 'off', this must be in 104514843421SMatthew Ahrens * a global zone. If not, something is wrong. 1046fa9e4066Sahrens */ 104799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1048e9dbad6fSeschrock "'%s' cannot be set while dataset " 1049e9dbad6fSeschrock "'zoned' property is set"), propname); 1050e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1051e9dbad6fSeschrock goto error; 1052fa9e4066Sahrens } 1053f3861e1aSahl 105467331909Sdougm /* 105567331909Sdougm * At this point, it is legitimate to set the 105667331909Sdougm * property. Now we want to make sure that the 105767331909Sdougm * property value is valid if it is sharenfs. 105867331909Sdougm */ 1059da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1060da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 106167331909Sdougm strcmp(strval, "on") != 0 && 106267331909Sdougm strcmp(strval, "off") != 0) { 1063da6c28aaSamw zfs_share_proto_t proto; 1064da6c28aaSamw 1065da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1066da6c28aaSamw proto = PROTO_SMB; 1067da6c28aaSamw else 1068da6c28aaSamw proto = PROTO_NFS; 106967331909Sdougm 107067331909Sdougm /* 1071da6c28aaSamw * Must be an valid sharing protocol 1072da6c28aaSamw * option string so init the libshare 1073da6c28aaSamw * in order to enable the parser and 1074da6c28aaSamw * then parse the options. We use the 1075da6c28aaSamw * control API since we don't care about 1076da6c28aaSamw * the current configuration and don't 107767331909Sdougm * want the overhead of loading it 107867331909Sdougm * until we actually do something. 107967331909Sdougm */ 108067331909Sdougm 108167331909Sdougm if (zfs_init_libshare(hdl, 108267331909Sdougm SA_INIT_CONTROL_API) != SA_OK) { 1083fac3008cSeschrock /* 1084fac3008cSeschrock * An error occurred so we can't do 1085fac3008cSeschrock * anything 1086fac3008cSeschrock */ 108767331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 108867331909Sdougm "'%s' cannot be set: problem " 108967331909Sdougm "in share initialization"), 109067331909Sdougm propname); 1091fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1092fac3008cSeschrock errbuf); 109367331909Sdougm goto error; 109467331909Sdougm } 109567331909Sdougm 1096da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 109767331909Sdougm /* 109867331909Sdougm * There was an error in parsing so 109967331909Sdougm * deal with it by issuing an error 110067331909Sdougm * message and leaving after 110167331909Sdougm * uninitializing the the libshare 110267331909Sdougm * interface. 110367331909Sdougm */ 110467331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 110567331909Sdougm "'%s' cannot be set to invalid " 110667331909Sdougm "options"), propname); 1107fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1108fac3008cSeschrock errbuf); 110967331909Sdougm zfs_uninit_libshare(hdl); 111067331909Sdougm goto error; 111167331909Sdougm } 111267331909Sdougm zfs_uninit_libshare(hdl); 111367331909Sdougm } 111467331909Sdougm 1115f3861e1aSahl break; 1116da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1117da6c28aaSamw chosen_utf = (int)intval; 1118da6c28aaSamw break; 1119da6c28aaSamw case ZFS_PROP_NORMALIZE: 1120da6c28aaSamw chosen_normal = (int)intval; 1121da6c28aaSamw break; 1122fa9e4066Sahrens } 1123fa9e4066Sahrens 1124e9dbad6fSeschrock /* 1125e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1126e9dbad6fSeschrock * checks to enforce. 1127e9dbad6fSeschrock */ 1128e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1129e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1130e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1131e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1132e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1133e9dbad6fSeschrock char buf[64]; 1134e9dbad6fSeschrock 1135e9dbad6fSeschrock switch (prop) { 1136e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1137a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1138e9dbad6fSeschrock if (intval > volsize) { 1139e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1140e9dbad6fSeschrock "'%s' is greater than current " 1141e9dbad6fSeschrock "volume size"), propname); 1142e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1143e9dbad6fSeschrock errbuf); 1144e9dbad6fSeschrock goto error; 1145e9dbad6fSeschrock } 1146e9dbad6fSeschrock break; 1147e9dbad6fSeschrock 1148e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1149e9dbad6fSeschrock if (intval % blocksize != 0) { 1150e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1151e9dbad6fSeschrock sizeof (buf)); 1152e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1153e9dbad6fSeschrock "'%s' must be a multiple of " 1154e9dbad6fSeschrock "volume block size (%s)"), 1155e9dbad6fSeschrock propname, buf); 1156e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1157e9dbad6fSeschrock errbuf); 1158e9dbad6fSeschrock goto error; 1159e9dbad6fSeschrock } 1160e9dbad6fSeschrock 1161e9dbad6fSeschrock if (intval == 0) { 1162e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1163e9dbad6fSeschrock "'%s' cannot be zero"), 1164e9dbad6fSeschrock propname); 1165e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1166e9dbad6fSeschrock errbuf); 1167e9dbad6fSeschrock goto error; 1168e9dbad6fSeschrock } 1169f3861e1aSahl break; 1170e9dbad6fSeschrock } 1171e9dbad6fSeschrock } 1172e9dbad6fSeschrock } 1173e9dbad6fSeschrock 1174e9dbad6fSeschrock /* 1175da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1176da6c28aaSamw * enforce rejection of non-UTF8 names. 1177da6c28aaSamw * 1178da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1179da6c28aaSamw * was explicitly not chosen, it is an error. 1180da6c28aaSamw */ 1181de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1182da6c28aaSamw if (nvlist_add_uint64(ret, 1183da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1184da6c28aaSamw (void) no_memory(hdl); 1185da6c28aaSamw goto error; 1186da6c28aaSamw } 1187de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1188da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1189da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1190da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1191da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1192da6c28aaSamw goto error; 1193da6c28aaSamw } 1194da6c28aaSamw 1195da6c28aaSamw /* 1196e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1197e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1198e9dbad6fSeschrock */ 1199e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 1200e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1201e9dbad6fSeschrock &intval) == 0) { 1202e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 1203e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1204a9b821a0Sck153898 uint64_t old_reservation; 1205e9dbad6fSeschrock uint64_t new_reservation; 1206a9b821a0Sck153898 zfs_prop_t resv_prop; 1207a9b821a0Sck153898 12087b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1209a9b821a0Sck153898 goto error; 1210a9b821a0Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 1211e9dbad6fSeschrock 1212e9dbad6fSeschrock if (old_volsize == old_reservation && 1213a9b821a0Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 1214e9dbad6fSeschrock &new_reservation) != 0) { 1215e9dbad6fSeschrock if (nvlist_add_uint64(ret, 1216a9b821a0Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 1217e9dbad6fSeschrock (void) no_memory(hdl); 1218e9dbad6fSeschrock goto error; 1219e9dbad6fSeschrock } 1220e9dbad6fSeschrock } 1221e9dbad6fSeschrock } 1222e9dbad6fSeschrock return (ret); 1223e9dbad6fSeschrock 1224e9dbad6fSeschrock error: 1225e9dbad6fSeschrock nvlist_free(ret); 1226e9dbad6fSeschrock return (NULL); 1227e9dbad6fSeschrock } 1228e9dbad6fSeschrock 1229e9dbad6fSeschrock /* 1230e9dbad6fSeschrock * Given a property name and value, set the property for the given dataset. 1231e9dbad6fSeschrock */ 1232e9dbad6fSeschrock int 1233e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1234e9dbad6fSeschrock { 1235e9dbad6fSeschrock zfs_cmd_t zc = { 0 }; 1236e9dbad6fSeschrock int ret = -1; 1237e9dbad6fSeschrock prop_changelist_t *cl = NULL; 1238e9dbad6fSeschrock char errbuf[1024]; 1239e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1240e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1241e9dbad6fSeschrock zfs_prop_t prop; 12420068372bSMark J Musante boolean_t do_prefix; 12430068372bSMark J Musante uint64_t idx; 1244e9dbad6fSeschrock 1245e9dbad6fSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1246e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1247e9dbad6fSeschrock zhp->zfs_name); 1248e9dbad6fSeschrock 1249e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1250e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1251e9dbad6fSeschrock (void) no_memory(hdl); 1252e9dbad6fSeschrock goto error; 1253e9dbad6fSeschrock } 1254e9dbad6fSeschrock 12550a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1256e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1257e9dbad6fSeschrock goto error; 1258990b4856Slling 1259e9dbad6fSeschrock nvlist_free(nvl); 1260e9dbad6fSeschrock nvl = realprops; 1261e9dbad6fSeschrock 1262e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1263e9dbad6fSeschrock 12640069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1265e9dbad6fSeschrock goto error; 1266fa9e4066Sahrens 1267fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 126899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1269fa9e4066Sahrens "child dataset with inherited mountpoint is used " 127099653d4eSeschrock "in a non-global zone")); 127199653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1272fa9e4066Sahrens goto error; 1273fa9e4066Sahrens } 1274fa9e4066Sahrens 12750068372bSMark J Musante /* 12760068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 12770068372bSMark J Musante * then we want to prevent unmounting & remounting it. 12780068372bSMark J Musante */ 12790068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 12800068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 12810068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1282a227b7f4Shs24103 1283a227b7f4Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1284fa9e4066Sahrens goto error; 1285fa9e4066Sahrens 1286fa9e4066Sahrens /* 1287fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1288fa9e4066Sahrens */ 1289fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1290fa9e4066Sahrens 1291990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1292e9dbad6fSeschrock goto error; 1293e9dbad6fSeschrock 1294ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1295743a77edSAlan Wright 1296fa9e4066Sahrens if (ret != 0) { 1297fa9e4066Sahrens switch (errno) { 1298fa9e4066Sahrens 1299fa9e4066Sahrens case ENOSPC: 1300fa9e4066Sahrens /* 1301fa9e4066Sahrens * For quotas and reservations, ENOSPC indicates 1302fa9e4066Sahrens * something different; setting a quota or reservation 1303fa9e4066Sahrens * doesn't use any disk space. 1304fa9e4066Sahrens */ 1305fa9e4066Sahrens switch (prop) { 1306fa9e4066Sahrens case ZFS_PROP_QUOTA: 1307a9799022Sck153898 case ZFS_PROP_REFQUOTA: 130899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130999653d4eSeschrock "size is less than current used or " 131099653d4eSeschrock "reserved space")); 131199653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1312fa9e4066Sahrens break; 1313fa9e4066Sahrens 1314fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1315a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 131699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 131799653d4eSeschrock "size is greater than available space")); 131899653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1319fa9e4066Sahrens break; 1320fa9e4066Sahrens 1321fa9e4066Sahrens default: 132299653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1323fa9e4066Sahrens break; 1324fa9e4066Sahrens } 1325fa9e4066Sahrens break; 1326fa9e4066Sahrens 1327fa9e4066Sahrens case EBUSY: 132899653d4eSeschrock if (prop == ZFS_PROP_VOLBLOCKSIZE) 132999653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); 133099653d4eSeschrock else 1331e9dbad6fSeschrock (void) zfs_standard_error(hdl, EBUSY, errbuf); 1332fa9e4066Sahrens break; 1333fa9e4066Sahrens 13342a79c5feSlling case EROFS: 133599653d4eSeschrock (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 13362a79c5feSlling break; 13372a79c5feSlling 1338c9431fa1Sahl case ENOTSUP: 1339c9431fa1Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13409e6eda55Smarks "pool and or dataset must be upgraded to set this " 134140feaa91Sahrens "property or value")); 1342c9431fa1Sahl (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1343c9431fa1Sahl break; 1344c9431fa1Sahl 134515e6edf1Sgw25295 case ERANGE: 134615e6edf1Sgw25295 if (prop == ZFS_PROP_COMPRESSION) { 134715e6edf1Sgw25295 (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 134815e6edf1Sgw25295 "property setting is not allowed on " 134915e6edf1Sgw25295 "bootable datasets")); 135015e6edf1Sgw25295 (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 135115e6edf1Sgw25295 } else { 135215e6edf1Sgw25295 (void) zfs_standard_error(hdl, errno, errbuf); 135315e6edf1Sgw25295 } 135415e6edf1Sgw25295 break; 135515e6edf1Sgw25295 1356fa9e4066Sahrens case EOVERFLOW: 1357fa9e4066Sahrens /* 1358fa9e4066Sahrens * This platform can't address a volume this big. 1359fa9e4066Sahrens */ 1360fa9e4066Sahrens #ifdef _ILP32 1361fa9e4066Sahrens if (prop == ZFS_PROP_VOLSIZE) { 136299653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1363fa9e4066Sahrens break; 1364fa9e4066Sahrens } 1365fa9e4066Sahrens #endif 136699653d4eSeschrock /* FALLTHROUGH */ 1367fa9e4066Sahrens default: 136899653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1369fa9e4066Sahrens } 1370fa9e4066Sahrens } else { 1371a227b7f4Shs24103 if (do_prefix) 1372a227b7f4Shs24103 ret = changelist_postfix(cl); 1373a227b7f4Shs24103 1374fa9e4066Sahrens /* 1375fa9e4066Sahrens * Refresh the statistics so the new property value 1376fa9e4066Sahrens * is reflected. 1377fa9e4066Sahrens */ 1378a227b7f4Shs24103 if (ret == 0) 1379fa9e4066Sahrens (void) get_stats(zhp); 1380fa9e4066Sahrens } 1381fa9e4066Sahrens 1382fa9e4066Sahrens error: 1383e9dbad6fSeschrock nvlist_free(nvl); 1384e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1385e9dbad6fSeschrock if (cl) 1386fa9e4066Sahrens changelist_free(cl); 1387fa9e4066Sahrens return (ret); 1388fa9e4066Sahrens } 1389fa9e4066Sahrens 1390fa9e4066Sahrens /* 1391fa9e4066Sahrens * Given a property, inherit the value from the parent dataset. 1392fa9e4066Sahrens */ 1393fa9e4066Sahrens int 1394e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) 1395fa9e4066Sahrens { 1396fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1397fa9e4066Sahrens int ret; 1398fa9e4066Sahrens prop_changelist_t *cl; 139999653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 140099653d4eSeschrock char errbuf[1024]; 1401e9dbad6fSeschrock zfs_prop_t prop; 140299653d4eSeschrock 140399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 140499653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1405fa9e4066Sahrens 1406990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1407e9dbad6fSeschrock /* 1408e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1409e9dbad6fSeschrock * small, so just do it here. 1410e9dbad6fSeschrock */ 1411e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1412e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1413e9dbad6fSeschrock "invalid property")); 1414e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1415e9dbad6fSeschrock } 1416e9dbad6fSeschrock 1417e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1418e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1419e9dbad6fSeschrock 1420e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1421e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1422e9dbad6fSeschrock 1423e9dbad6fSeschrock return (0); 1424e9dbad6fSeschrock } 1425e9dbad6fSeschrock 1426fa9e4066Sahrens /* 1427fa9e4066Sahrens * Verify that this property is inheritable. 1428fa9e4066Sahrens */ 142999653d4eSeschrock if (zfs_prop_readonly(prop)) 143099653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1431fa9e4066Sahrens 143299653d4eSeschrock if (!zfs_prop_inheritable(prop)) 143399653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1434fa9e4066Sahrens 1435fa9e4066Sahrens /* 1436fa9e4066Sahrens * Check to see if the value applies to this type 1437fa9e4066Sahrens */ 143899653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 143999653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1440fa9e4066Sahrens 1441bf7c2d40Srm160521 /* 1442bf7c2d40Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 1443bf7c2d40Srm160521 */ 1444bf7c2d40Srm160521 propname = zfs_prop_to_name(prop); 1445fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1446e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1447fa9e4066Sahrens 1448fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1449fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 145099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 145199653d4eSeschrock "dataset is used in a non-global zone")); 145299653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1453fa9e4066Sahrens } 1454fa9e4066Sahrens 1455fa9e4066Sahrens /* 1456fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1457fa9e4066Sahrens */ 14580069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1459fa9e4066Sahrens return (-1); 1460fa9e4066Sahrens 1461fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 146299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 146399653d4eSeschrock "child dataset with inherited mountpoint is used " 146499653d4eSeschrock "in a non-global zone")); 146599653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1466fa9e4066Sahrens goto error; 1467fa9e4066Sahrens } 1468fa9e4066Sahrens 1469fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1470fa9e4066Sahrens goto error; 1471fa9e4066Sahrens 1472e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 147399653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1474fa9e4066Sahrens } else { 1475fa9e4066Sahrens 1476efc555ebSnd150628 if ((ret = changelist_postfix(cl)) != 0) 1477fa9e4066Sahrens goto error; 1478fa9e4066Sahrens 1479fa9e4066Sahrens /* 1480fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1481fa9e4066Sahrens */ 1482fa9e4066Sahrens (void) get_stats(zhp); 1483fa9e4066Sahrens } 1484fa9e4066Sahrens 1485fa9e4066Sahrens error: 1486fa9e4066Sahrens changelist_free(cl); 1487fa9e4066Sahrens return (ret); 1488fa9e4066Sahrens } 1489fa9e4066Sahrens 1490fa9e4066Sahrens /* 14917f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 14927f7322feSeschrock * extract them appropriately. 14937f7322feSeschrock */ 14947f7322feSeschrock static uint64_t 14957f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 14967f7322feSeschrock { 14977f7322feSeschrock nvlist_t *nv; 14987f7322feSeschrock uint64_t value; 14997f7322feSeschrock 1500a2eea2e1Sahrens *source = NULL; 15017f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15027f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1503990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1504990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15057f7322feSeschrock } else { 15062e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15072e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15087f7322feSeschrock value = zfs_prop_default_numeric(prop); 15097f7322feSeschrock *source = ""; 15107f7322feSeschrock } 15117f7322feSeschrock 15127f7322feSeschrock return (value); 15137f7322feSeschrock } 15147f7322feSeschrock 15157f7322feSeschrock static char * 15167f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15177f7322feSeschrock { 15187f7322feSeschrock nvlist_t *nv; 15197f7322feSeschrock char *value; 15207f7322feSeschrock 1521a2eea2e1Sahrens *source = NULL; 15227f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15237f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1524990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1525990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15267f7322feSeschrock } else { 15272e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15282e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15297f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15307f7322feSeschrock value = ""; 15317f7322feSeschrock *source = ""; 15327f7322feSeschrock } 15337f7322feSeschrock 15347f7322feSeschrock return (value); 15357f7322feSeschrock } 15367f7322feSeschrock 15377f7322feSeschrock /* 1538fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1539fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1540fa9e4066Sahrens * 1541fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1542fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1543fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1544fa9e4066Sahrens * the source "temporary". 1545fa9e4066Sahrens */ 154699653d4eSeschrock static int 1547990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 154899653d4eSeschrock char **source, uint64_t *val) 1549fa9e4066Sahrens { 1550bd00f61bSrm160521 zfs_cmd_t zc = { 0 }; 155196510749Stimh nvlist_t *zplprops = NULL; 1552fa9e4066Sahrens struct mnttab mnt; 15533ccfa83cSahrens char *mntopt_on = NULL; 15543ccfa83cSahrens char *mntopt_off = NULL; 1555fa9e4066Sahrens 1556fa9e4066Sahrens *source = NULL; 1557fa9e4066Sahrens 15583ccfa83cSahrens switch (prop) { 15593ccfa83cSahrens case ZFS_PROP_ATIME: 15603ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 15613ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 15623ccfa83cSahrens break; 15633ccfa83cSahrens 15643ccfa83cSahrens case ZFS_PROP_DEVICES: 15653ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 15663ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 15673ccfa83cSahrens break; 15683ccfa83cSahrens 15693ccfa83cSahrens case ZFS_PROP_EXEC: 15703ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 15713ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 15723ccfa83cSahrens break; 15733ccfa83cSahrens 15743ccfa83cSahrens case ZFS_PROP_READONLY: 15753ccfa83cSahrens mntopt_on = MNTOPT_RO; 15763ccfa83cSahrens mntopt_off = MNTOPT_RW; 15773ccfa83cSahrens break; 15783ccfa83cSahrens 15793ccfa83cSahrens case ZFS_PROP_SETUID: 15803ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 15813ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 15823ccfa83cSahrens break; 15833ccfa83cSahrens 15843ccfa83cSahrens case ZFS_PROP_XATTR: 15853ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 15863ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 15873ccfa83cSahrens break; 1588da6c28aaSamw 1589da6c28aaSamw case ZFS_PROP_NBMAND: 1590da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1591da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1592da6c28aaSamw break; 15933ccfa83cSahrens } 15943ccfa83cSahrens 15953bb79becSeschrock /* 15963bb79becSeschrock * Because looking up the mount options is potentially expensive 15973bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 15983bb79becSeschrock * we're looking up a property which requires its presence. 15993bb79becSeschrock */ 16003bb79becSeschrock if (!zhp->zfs_mntcheck && 16013ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1602ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1603ebedde84SEric Taylor struct mnttab entry; 16043bb79becSeschrock 1605ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1606ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 16073ccfa83cSahrens entry.mnt_mntopts); 16083ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 16093bb79becSeschrock return (-1); 16103ccfa83cSahrens } 16113bb79becSeschrock 16123bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 16133bb79becSeschrock } 16143bb79becSeschrock 1615fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1616fa9e4066Sahrens mnt.mnt_mntopts = ""; 1617fa9e4066Sahrens else 1618fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1619fa9e4066Sahrens 1620fa9e4066Sahrens switch (prop) { 1621fa9e4066Sahrens case ZFS_PROP_ATIME: 1622fa9e4066Sahrens case ZFS_PROP_DEVICES: 1623fa9e4066Sahrens case ZFS_PROP_EXEC: 16243ccfa83cSahrens case ZFS_PROP_READONLY: 16253ccfa83cSahrens case ZFS_PROP_SETUID: 16263ccfa83cSahrens case ZFS_PROP_XATTR: 1627da6c28aaSamw case ZFS_PROP_NBMAND: 162899653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1629fa9e4066Sahrens 16303ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 163199653d4eSeschrock *val = B_TRUE; 1632fa9e4066Sahrens if (src) 1633990b4856Slling *src = ZPROP_SRC_TEMPORARY; 16343ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 163599653d4eSeschrock *val = B_FALSE; 1636fa9e4066Sahrens if (src) 1637990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1638fa9e4066Sahrens } 163999653d4eSeschrock break; 1640fa9e4066Sahrens 16413ccfa83cSahrens case ZFS_PROP_CANMOUNT: 164299653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1643a227b7f4Shs24103 if (*val != ZFS_CANMOUNT_ON) 1644fda77a98Srm160521 *source = zhp->zfs_name; 1645fda77a98Srm160521 else 1646fda77a98Srm160521 *source = ""; /* default */ 164799653d4eSeschrock break; 1648fa9e4066Sahrens 1649fa9e4066Sahrens case ZFS_PROP_QUOTA: 1650a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1651fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1652a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1653a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 1654a2eea2e1Sahrens if (*val == 0) 1655fa9e4066Sahrens *source = ""; /* default */ 1656fa9e4066Sahrens else 1657fa9e4066Sahrens *source = zhp->zfs_name; 165899653d4eSeschrock break; 1659fa9e4066Sahrens 1660fa9e4066Sahrens case ZFS_PROP_MOUNTED: 166199653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 166299653d4eSeschrock break; 1663fa9e4066Sahrens 166439c23413Seschrock case ZFS_PROP_NUMCLONES: 166539c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 166639c23413Seschrock break; 166739c23413Seschrock 1668bd00f61bSrm160521 case ZFS_PROP_VERSION: 1669de8267e0Stimh case ZFS_PROP_NORMALIZE: 1670de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1671de8267e0Stimh case ZFS_PROP_CASE: 1672de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1673de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 16743d7934e1Srm160521 return (-1); 1675bd00f61bSrm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1676de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1677de8267e0Stimh zcmd_free_nvlists(&zc); 1678bd00f61bSrm160521 zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1679de8267e0Stimh "unable to get %s property"), 1680de8267e0Stimh zfs_prop_to_name(prop)); 1681bd00f61bSrm160521 return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, 1682bd00f61bSrm160521 dgettext(TEXT_DOMAIN, "internal error"))); 1683bd00f61bSrm160521 } 1684de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1685de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1686de8267e0Stimh val) != 0) { 1687de8267e0Stimh zcmd_free_nvlists(&zc); 1688de8267e0Stimh zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1689de8267e0Stimh "unable to get %s property"), 1690de8267e0Stimh zfs_prop_to_name(prop)); 1691de8267e0Stimh return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM, 1692de8267e0Stimh dgettext(TEXT_DOMAIN, "internal error"))); 1693de8267e0Stimh } 169496510749Stimh if (zplprops) 169596510749Stimh nvlist_free(zplprops); 1696de8267e0Stimh zcmd_free_nvlists(&zc); 1697bd00f61bSrm160521 break; 1698bd00f61bSrm160521 1699fa9e4066Sahrens default: 1700e7437265Sahrens switch (zfs_prop_get_type(prop)) { 170191ebeef5Sahrens case PROP_TYPE_NUMBER: 170291ebeef5Sahrens case PROP_TYPE_INDEX: 1703e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 170474e7dc98SMatthew Ahrens /* 170514843421SMatthew Ahrens * If we tried to use a default value for a 170674e7dc98SMatthew Ahrens * readonly property, it means that it was not 170774e7dc98SMatthew Ahrens * present; return an error. 170874e7dc98SMatthew Ahrens */ 170974e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 171074e7dc98SMatthew Ahrens *source && (*source)[0] == '\0') { 171174e7dc98SMatthew Ahrens return (-1); 171274e7dc98SMatthew Ahrens } 1713e7437265Sahrens break; 1714e7437265Sahrens 171591ebeef5Sahrens case PROP_TYPE_STRING: 1716e7437265Sahrens default: 171799653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 171899653d4eSeschrock "cannot get non-numeric property")); 171999653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 172099653d4eSeschrock dgettext(TEXT_DOMAIN, "internal error"))); 1721fa9e4066Sahrens } 1722e7437265Sahrens } 1723fa9e4066Sahrens 1724fa9e4066Sahrens return (0); 1725fa9e4066Sahrens } 1726fa9e4066Sahrens 1727fa9e4066Sahrens /* 1728fa9e4066Sahrens * Calculate the source type, given the raw source string. 1729fa9e4066Sahrens */ 1730fa9e4066Sahrens static void 1731990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1732fa9e4066Sahrens char *statbuf, size_t statlen) 1733fa9e4066Sahrens { 1734990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1735fa9e4066Sahrens return; 1736fa9e4066Sahrens 1737fa9e4066Sahrens if (source == NULL) { 1738990b4856Slling *srctype = ZPROP_SRC_NONE; 1739fa9e4066Sahrens } else if (source[0] == '\0') { 1740990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 1741fa9e4066Sahrens } else { 1742fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1743990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1744fa9e4066Sahrens } else { 1745fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1746990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1747fa9e4066Sahrens } 1748fa9e4066Sahrens } 1749fa9e4066Sahrens 1750fa9e4066Sahrens } 1751fa9e4066Sahrens 1752fa9e4066Sahrens /* 1753fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1754fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1755fa9e4066Sahrens * human-readable form. 1756fa9e4066Sahrens * 1757fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1758fa9e4066Sahrens */ 1759fa9e4066Sahrens int 1760fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1761990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1762fa9e4066Sahrens { 1763fa9e4066Sahrens char *source = NULL; 1764fa9e4066Sahrens uint64_t val; 1765fa9e4066Sahrens char *str; 1766e9dbad6fSeschrock const char *strval; 1767fa9e4066Sahrens 1768fa9e4066Sahrens /* 1769fa9e4066Sahrens * Check to see if this property applies to our object 1770fa9e4066Sahrens */ 1771fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1772fa9e4066Sahrens return (-1); 1773fa9e4066Sahrens 1774fa9e4066Sahrens if (src) 1775990b4856Slling *src = ZPROP_SRC_NONE; 1776fa9e4066Sahrens 1777fa9e4066Sahrens switch (prop) { 1778fa9e4066Sahrens case ZFS_PROP_CREATION: 1779fa9e4066Sahrens /* 1780fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1781fa9e4066Sahrens * this into a string unless 'literal' is specified. 1782fa9e4066Sahrens */ 1783fa9e4066Sahrens { 1784a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1785a2eea2e1Sahrens time_t time = (time_t)val; 1786fa9e4066Sahrens struct tm t; 1787fa9e4066Sahrens 1788fa9e4066Sahrens if (literal || 1789fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1790fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1791fa9e4066Sahrens &t) == 0) 1792a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1793fa9e4066Sahrens } 1794fa9e4066Sahrens break; 1795fa9e4066Sahrens 1796fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1797fa9e4066Sahrens /* 1798fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1799fa9e4066Sahrens * 1800fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1801fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1802fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1803fa9e4066Sahrens * 1804fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1805fa9e4066Sahrens * root to any values we return. 1806fa9e4066Sahrens */ 180729ab75c9Srm160521 18087f7322feSeschrock str = getprop_string(zhp, prop, &source); 1809fa9e4066Sahrens 1810b21718f0Sgw25295 if (str[0] == '/') { 181129ab75c9Srm160521 char buf[MAXPATHLEN]; 181229ab75c9Srm160521 char *root = buf; 18137f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 1814fa9e4066Sahrens 1815fa9e4066Sahrens if (relpath[0] == '/') 1816fa9e4066Sahrens relpath++; 1817b21718f0Sgw25295 181829ab75c9Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 181929ab75c9Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 182029ab75c9Srm160521 (strcmp(root, "-") == 0)) 182129ab75c9Srm160521 root[0] = '\0'; 1822b21718f0Sgw25295 /* 1823b21718f0Sgw25295 * Special case an alternate root of '/'. This will 1824b21718f0Sgw25295 * avoid having multiple leading slashes in the 1825b21718f0Sgw25295 * mountpoint path. 1826b21718f0Sgw25295 */ 1827b21718f0Sgw25295 if (strcmp(root, "/") == 0) 1828b21718f0Sgw25295 root++; 1829b21718f0Sgw25295 1830b21718f0Sgw25295 /* 1831b21718f0Sgw25295 * If the mountpoint is '/' then skip over this 1832b21718f0Sgw25295 * if we are obtaining either an alternate root or 1833b21718f0Sgw25295 * an inherited mountpoint. 1834b21718f0Sgw25295 */ 1835b21718f0Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 1836b21718f0Sgw25295 relpath[0] != '\0')) 18377f7322feSeschrock str++; 1838fa9e4066Sahrens 1839fa9e4066Sahrens if (relpath[0] == '\0') 1840fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 18417f7322feSeschrock root, str); 1842fa9e4066Sahrens else 1843fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 18447f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1845fa9e4066Sahrens relpath); 1846fa9e4066Sahrens } else { 1847fa9e4066Sahrens /* 'legacy' or 'none' */ 18487f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1849fa9e4066Sahrens } 1850fa9e4066Sahrens 1851fa9e4066Sahrens break; 1852fa9e4066Sahrens 1853fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1854a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1855fa9e4066Sahrens proplen); 1856fa9e4066Sahrens /* 1857fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1858fa9e4066Sahrens * it doesn't apply to this dataset. 1859fa9e4066Sahrens */ 1860fa9e4066Sahrens if (propbuf[0] == '\0') 1861fa9e4066Sahrens return (-1); 1862fa9e4066Sahrens break; 1863fa9e4066Sahrens 1864fa9e4066Sahrens case ZFS_PROP_QUOTA: 1865a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1866fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1867a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1868a9799022Sck153898 186999653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 187099653d4eSeschrock return (-1); 1871fa9e4066Sahrens 1872fa9e4066Sahrens /* 1873fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 1874fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 1875fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 1876fa9e4066Sahrens * that its set locally. 1877fa9e4066Sahrens */ 1878fa9e4066Sahrens if (val == 0) { 1879fa9e4066Sahrens if (literal) 1880fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 1881fa9e4066Sahrens else 1882fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 1883fa9e4066Sahrens } else { 1884fa9e4066Sahrens if (literal) 18855ad82045Snd150628 (void) snprintf(propbuf, proplen, "%llu", 18865ad82045Snd150628 (u_longlong_t)val); 1887fa9e4066Sahrens else 1888fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 1889fa9e4066Sahrens } 1890fa9e4066Sahrens break; 1891fa9e4066Sahrens 1892fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 189399653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 189499653d4eSeschrock return (-1); 18955ad82045Snd150628 (void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t) 18965ad82045Snd150628 val / 100, (longlong_t)val % 100); 1897fa9e4066Sahrens break; 1898fa9e4066Sahrens 1899fa9e4066Sahrens case ZFS_PROP_TYPE: 1900fa9e4066Sahrens switch (zhp->zfs_type) { 1901fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 1902fa9e4066Sahrens str = "filesystem"; 1903fa9e4066Sahrens break; 1904fa9e4066Sahrens case ZFS_TYPE_VOLUME: 1905fa9e4066Sahrens str = "volume"; 1906fa9e4066Sahrens break; 1907fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 1908fa9e4066Sahrens str = "snapshot"; 1909fa9e4066Sahrens break; 1910fa9e4066Sahrens default: 191199653d4eSeschrock abort(); 1912fa9e4066Sahrens } 1913fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 1914fa9e4066Sahrens break; 1915fa9e4066Sahrens 1916fa9e4066Sahrens case ZFS_PROP_MOUNTED: 1917fa9e4066Sahrens /* 1918fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 1919fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 1920fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 1921fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 1922fa9e4066Sahrens */ 192399653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 192499653d4eSeschrock src, &source, &val) != 0) 192599653d4eSeschrock return (-1); 192699653d4eSeschrock if (val) 1927fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 1928fa9e4066Sahrens else 1929fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 1930fa9e4066Sahrens break; 1931fa9e4066Sahrens 1932fa9e4066Sahrens case ZFS_PROP_NAME: 1933fa9e4066Sahrens /* 1934fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 1935fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 1936fa9e4066Sahrens * consumers. 1937fa9e4066Sahrens */ 1938fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 1939fa9e4066Sahrens break; 1940fa9e4066Sahrens 1941fa9e4066Sahrens default: 1942e7437265Sahrens switch (zfs_prop_get_type(prop)) { 194391ebeef5Sahrens case PROP_TYPE_NUMBER: 1944e7437265Sahrens if (get_numeric_property(zhp, prop, src, 1945e7437265Sahrens &source, &val) != 0) 1946e7437265Sahrens return (-1); 1947e7437265Sahrens if (literal) 1948e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 1949e7437265Sahrens (u_longlong_t)val); 1950e7437265Sahrens else 1951e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 1952e7437265Sahrens break; 1953e7437265Sahrens 195491ebeef5Sahrens case PROP_TYPE_STRING: 1955e7437265Sahrens (void) strlcpy(propbuf, 1956e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 1957e7437265Sahrens break; 1958e7437265Sahrens 195991ebeef5Sahrens case PROP_TYPE_INDEX: 1960fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 1961fe192f76Sahrens &source, &val) != 0) 1962fe192f76Sahrens return (-1); 1963fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 1964e7437265Sahrens return (-1); 1965e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 1966e7437265Sahrens break; 1967e7437265Sahrens 1968e7437265Sahrens default: 196999653d4eSeschrock abort(); 1970fa9e4066Sahrens } 1971e7437265Sahrens } 1972fa9e4066Sahrens 1973fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 1974fa9e4066Sahrens 1975fa9e4066Sahrens return (0); 1976fa9e4066Sahrens } 1977fa9e4066Sahrens 1978fa9e4066Sahrens /* 1979fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 1980fa9e4066Sahrens * the given property is the appropriate type; should only be used with 1981fa9e4066Sahrens * hard-coded property types. 1982fa9e4066Sahrens */ 1983fa9e4066Sahrens uint64_t 1984fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 1985fa9e4066Sahrens { 1986fa9e4066Sahrens char *source; 198799653d4eSeschrock uint64_t val; 1988fa9e4066Sahrens 19893cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 199099653d4eSeschrock 199199653d4eSeschrock return (val); 1992fa9e4066Sahrens } 1993fa9e4066Sahrens 19947b97dc1aSrm160521 int 19957b97dc1aSrm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 19967b97dc1aSrm160521 { 19977b97dc1aSrm160521 char buf[64]; 19987b97dc1aSrm160521 199914843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 20007b97dc1aSrm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 20017b97dc1aSrm160521 } 20027b97dc1aSrm160521 2003fa9e4066Sahrens /* 2004fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2005fa9e4066Sahrens */ 2006fa9e4066Sahrens int 2007fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2008990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2009fa9e4066Sahrens { 2010fa9e4066Sahrens char *source; 2011fa9e4066Sahrens 2012fa9e4066Sahrens /* 2013fa9e4066Sahrens * Check to see if this property applies to our object 2014fa9e4066Sahrens */ 2015e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2016ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 201799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 201899653d4eSeschrock zfs_prop_to_name(prop))); 2019e45ce728Sahrens } 2020fa9e4066Sahrens 2021fa9e4066Sahrens if (src) 2022990b4856Slling *src = ZPROP_SRC_NONE; 2023fa9e4066Sahrens 202499653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 202599653d4eSeschrock return (-1); 2026fa9e4066Sahrens 2027fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2028fa9e4066Sahrens 2029fa9e4066Sahrens return (0); 2030fa9e4066Sahrens } 2031fa9e4066Sahrens 203214843421SMatthew Ahrens static int 203314843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 203414843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 203514843421SMatthew Ahrens { 203614843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 203714843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 203814843421SMatthew Ahrens idmap_stat status; 203914843421SMatthew Ahrens int err = EINVAL; 204014843421SMatthew Ahrens 204114843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 204214843421SMatthew Ahrens goto out; 204314843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 204414843421SMatthew Ahrens goto out; 204514843421SMatthew Ahrens 204614843421SMatthew Ahrens if (isuser) { 204714843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 204814843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 204914843421SMatthew Ahrens } else { 205014843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 205114843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 205214843421SMatthew Ahrens } 205314843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 205414843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 205514843421SMatthew Ahrens status == IDMAP_SUCCESS) 205614843421SMatthew Ahrens err = 0; 205714843421SMatthew Ahrens else 205814843421SMatthew Ahrens err = EINVAL; 205914843421SMatthew Ahrens out: 206014843421SMatthew Ahrens if (get_hdl) 206114843421SMatthew Ahrens idmap_get_destroy(get_hdl); 206214843421SMatthew Ahrens if (idmap_hdl) 206314843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 206414843421SMatthew Ahrens return (err); 206514843421SMatthew Ahrens } 206614843421SMatthew Ahrens 206714843421SMatthew Ahrens /* 206814843421SMatthew Ahrens * convert the propname into parameters needed by kernel 206914843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 207014843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 207114843421SMatthew Ahrens */ 207214843421SMatthew Ahrens static int 207314843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 207414843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 207514843421SMatthew Ahrens { 207614843421SMatthew Ahrens zfs_userquota_prop_t type; 207714843421SMatthew Ahrens char *cp, *end; 2078*3b12c289SMatthew Ahrens char *numericsid = NULL; 207914843421SMatthew Ahrens boolean_t isuser; 208014843421SMatthew Ahrens 208114843421SMatthew Ahrens domain[0] = '\0'; 208214843421SMatthew Ahrens 208314843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 208414843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 208514843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 208614843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 208714843421SMatthew Ahrens break; 208814843421SMatthew Ahrens } 208914843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 209014843421SMatthew Ahrens return (EINVAL); 209114843421SMatthew Ahrens *typep = type; 209214843421SMatthew Ahrens 209314843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 209414843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 209514843421SMatthew Ahrens 209614843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 209714843421SMatthew Ahrens 209814843421SMatthew Ahrens if (strchr(cp, '@')) { 209914843421SMatthew Ahrens /* 210014843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 2101*3b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 210214843421SMatthew Ahrens */ 2103*3b12c289SMatthew Ahrens directory_error_t e; 210414843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 210514843421SMatthew Ahrens return (ENOENT); 2106*3b12c289SMatthew Ahrens if (isuser) { 2107*3b12c289SMatthew Ahrens e = directory_sid_from_user_name(NULL, 2108*3b12c289SMatthew Ahrens cp, &numericsid); 2109*3b12c289SMatthew Ahrens } else { 2110*3b12c289SMatthew Ahrens e = directory_sid_from_group_name(NULL, 2111*3b12c289SMatthew Ahrens cp, &numericsid); 2112*3b12c289SMatthew Ahrens } 2113*3b12c289SMatthew Ahrens if (e != NULL) { 2114*3b12c289SMatthew Ahrens directory_error_free(e); 211514843421SMatthew Ahrens return (ENOENT); 2116*3b12c289SMatthew Ahrens } 2117*3b12c289SMatthew Ahrens if (numericsid == NULL) 211814843421SMatthew Ahrens return (ENOENT); 2119*3b12c289SMatthew Ahrens cp = numericsid; 2120*3b12c289SMatthew Ahrens /* will be further decoded below */ 2121*3b12c289SMatthew Ahrens } 2122*3b12c289SMatthew Ahrens 2123*3b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 212414843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 2125*3b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 212614843421SMatthew Ahrens cp = strrchr(domain, '-'); 212714843421SMatthew Ahrens *cp = '\0'; 212814843421SMatthew Ahrens cp++; 212914843421SMatthew Ahrens 213014843421SMatthew Ahrens errno = 0; 213114843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 2132*3b12c289SMatthew Ahrens if (numericsid) { 2133*3b12c289SMatthew Ahrens free(numericsid); 2134*3b12c289SMatthew Ahrens numericsid = NULL; 2135*3b12c289SMatthew Ahrens } 2136777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 213714843421SMatthew Ahrens return (EINVAL); 213814843421SMatthew Ahrens } else if (!isdigit(*cp)) { 213914843421SMatthew Ahrens /* 214014843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 214114843421SMatthew Ahrens * turned into a uid/gid 214214843421SMatthew Ahrens */ 214314843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 214414843421SMatthew Ahrens return (ENOENT); 214514843421SMatthew Ahrens if (isuser) { 214614843421SMatthew Ahrens struct passwd *pw; 214714843421SMatthew Ahrens pw = getpwnam(cp); 214814843421SMatthew Ahrens if (pw == NULL) 214914843421SMatthew Ahrens return (ENOENT); 215014843421SMatthew Ahrens *ridp = pw->pw_uid; 215114843421SMatthew Ahrens } else { 215214843421SMatthew Ahrens struct group *gr; 215314843421SMatthew Ahrens gr = getgrnam(cp); 215414843421SMatthew Ahrens if (gr == NULL) 215514843421SMatthew Ahrens return (ENOENT); 215614843421SMatthew Ahrens *ridp = gr->gr_gid; 215714843421SMatthew Ahrens } 215814843421SMatthew Ahrens } else { 215914843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 216014843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 216114843421SMatthew Ahrens idmap_rid_t rid; 216214843421SMatthew Ahrens char *mapdomain; 216314843421SMatthew Ahrens 216414843421SMatthew Ahrens if (*end != '\0') 216514843421SMatthew Ahrens return (EINVAL); 216614843421SMatthew Ahrens if (id > MAXUID) { 216714843421SMatthew Ahrens /* It's an ephemeral ID. */ 216814843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 216914843421SMatthew Ahrens &mapdomain, &rid) != 0) 217014843421SMatthew Ahrens return (ENOENT); 2171*3b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 217214843421SMatthew Ahrens *ridp = rid; 217314843421SMatthew Ahrens } else { 217414843421SMatthew Ahrens *ridp = id; 217514843421SMatthew Ahrens } 217614843421SMatthew Ahrens } 217714843421SMatthew Ahrens 2178*3b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 217914843421SMatthew Ahrens return (0); 218014843421SMatthew Ahrens } 218114843421SMatthew Ahrens 2182edea4b55SLin Ling static int 2183edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2184edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 218514843421SMatthew Ahrens { 218614843421SMatthew Ahrens int err; 218714843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 218814843421SMatthew Ahrens 218914843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 219014843421SMatthew Ahrens 219114843421SMatthew Ahrens err = userquota_propname_decode(propname, 219214843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2193edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2194edea4b55SLin Ling zc.zc_objset_type = *typep; 219514843421SMatthew Ahrens if (err) 219614843421SMatthew Ahrens return (err); 219714843421SMatthew Ahrens 219814843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 219914843421SMatthew Ahrens if (err) 220014843421SMatthew Ahrens return (err); 220114843421SMatthew Ahrens 2202edea4b55SLin Ling *propvalue = zc.zc_cookie; 2203edea4b55SLin Ling return (0); 2204edea4b55SLin Ling } 2205edea4b55SLin Ling 2206edea4b55SLin Ling int 2207edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2208edea4b55SLin Ling uint64_t *propvalue) 2209edea4b55SLin Ling { 2210edea4b55SLin Ling zfs_userquota_prop_t type; 2211edea4b55SLin Ling 2212edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2213edea4b55SLin Ling &type)); 2214edea4b55SLin Ling } 2215edea4b55SLin Ling 2216edea4b55SLin Ling int 2217edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2218edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 2219edea4b55SLin Ling { 2220edea4b55SLin Ling int err; 2221edea4b55SLin Ling uint64_t propvalue; 2222edea4b55SLin Ling zfs_userquota_prop_t type; 2223edea4b55SLin Ling 2224edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2225edea4b55SLin Ling &type); 2226edea4b55SLin Ling 2227edea4b55SLin Ling if (err) 2228edea4b55SLin Ling return (err); 2229edea4b55SLin Ling 223014843421SMatthew Ahrens if (literal) { 2231edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 2232edea4b55SLin Ling } else if (propvalue == 0 && 223314843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 223414843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 223514843421SMatthew Ahrens } else { 2236edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 223714843421SMatthew Ahrens } 223814843421SMatthew Ahrens return (0); 223914843421SMatthew Ahrens } 224014843421SMatthew Ahrens 2241fa9e4066Sahrens /* 2242fa9e4066Sahrens * Returns the name of the given zfs handle. 2243fa9e4066Sahrens */ 2244fa9e4066Sahrens const char * 2245fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2246fa9e4066Sahrens { 2247fa9e4066Sahrens return (zhp->zfs_name); 2248fa9e4066Sahrens } 2249fa9e4066Sahrens 2250fa9e4066Sahrens /* 2251fa9e4066Sahrens * Returns the type of the given zfs handle. 2252fa9e4066Sahrens */ 2253fa9e4066Sahrens zfs_type_t 2254fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2255fa9e4066Sahrens { 2256fa9e4066Sahrens return (zhp->zfs_type); 2257fa9e4066Sahrens } 2258fa9e4066Sahrens 2259ebedde84SEric Taylor static int 2260ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2261ebedde84SEric Taylor { 2262ebedde84SEric Taylor int rc; 2263ebedde84SEric Taylor uint64_t orig_cookie; 2264ebedde84SEric Taylor 2265ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2266ebedde84SEric Taylor top: 2267ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2268ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2269ebedde84SEric Taylor 2270ebedde84SEric Taylor if (rc == -1) { 2271ebedde84SEric Taylor switch (errno) { 2272ebedde84SEric Taylor case ENOMEM: 2273ebedde84SEric Taylor /* expand nvlist memory and try again */ 2274ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2275ebedde84SEric Taylor zcmd_free_nvlists(zc); 2276ebedde84SEric Taylor return (-1); 2277ebedde84SEric Taylor } 2278ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2279ebedde84SEric Taylor goto top; 2280ebedde84SEric Taylor /* 2281ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2282ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2283ebedde84SEric Taylor * has been removed since we obtained the handle. 2284ebedde84SEric Taylor */ 2285ebedde84SEric Taylor case ESRCH: 2286ebedde84SEric Taylor case ENOENT: 2287ebedde84SEric Taylor rc = 1; 2288ebedde84SEric Taylor break; 2289ebedde84SEric Taylor default: 2290ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2291ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2292ebedde84SEric Taylor "cannot iterate filesystems")); 2293ebedde84SEric Taylor break; 2294ebedde84SEric Taylor } 2295ebedde84SEric Taylor } 2296ebedde84SEric Taylor return (rc); 2297ebedde84SEric Taylor } 2298ebedde84SEric Taylor 2299fa9e4066Sahrens /* 23007f7322feSeschrock * Iterate over all child filesystems 2301fa9e4066Sahrens */ 2302fa9e4066Sahrens int 23037f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2304fa9e4066Sahrens { 2305fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2306fa9e4066Sahrens zfs_handle_t *nzhp; 2307fa9e4066Sahrens int ret; 2308fa9e4066Sahrens 23093cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 23103cb34c60Sahrens return (0); 23113cb34c60Sahrens 2312ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2313ebedde84SEric Taylor return (-1); 2314ebedde84SEric Taylor 2315ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2316ebedde84SEric Taylor &zc)) == 0) { 2317fa9e4066Sahrens /* 2318fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2319fa9e4066Sahrens * that the pool has since been removed. 2320fa9e4066Sahrens */ 2321ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2322ebedde84SEric Taylor &zc)) == NULL) { 2323fa9e4066Sahrens continue; 2324fa9e4066Sahrens } 2325fa9e4066Sahrens 2326ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2327ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2328ebedde84SEric Taylor return (ret); 2329ebedde84SEric Taylor } 2330ebedde84SEric Taylor } 2331ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2332ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 23337f7322feSeschrock } 23347f7322feSeschrock 23357f7322feSeschrock /* 23367f7322feSeschrock * Iterate over all snapshots 23377f7322feSeschrock */ 23387f7322feSeschrock int 23397f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 23407f7322feSeschrock { 23417f7322feSeschrock zfs_cmd_t zc = { 0 }; 23427f7322feSeschrock zfs_handle_t *nzhp; 23437f7322feSeschrock int ret; 2344fa9e4066Sahrens 23453cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 23463cb34c60Sahrens return (0); 23473cb34c60Sahrens 2348ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2349ebedde84SEric Taylor return (-1); 2350ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2351ebedde84SEric Taylor &zc)) == 0) { 2352fa9e4066Sahrens 2353ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2354ebedde84SEric Taylor &zc)) == NULL) { 2355fa9e4066Sahrens continue; 2356fa9e4066Sahrens } 2357fa9e4066Sahrens 2358ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2359ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2360ebedde84SEric Taylor return (ret); 2361ebedde84SEric Taylor } 2362ebedde84SEric Taylor } 2363ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2364ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2365fa9e4066Sahrens } 2366fa9e4066Sahrens 2367fa9e4066Sahrens /* 23687f7322feSeschrock * Iterate over all children, snapshots and filesystems 23697f7322feSeschrock */ 23707f7322feSeschrock int 23717f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 23727f7322feSeschrock { 23737f7322feSeschrock int ret; 23747f7322feSeschrock 23757f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 23767f7322feSeschrock return (ret); 23777f7322feSeschrock 23787f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 23797f7322feSeschrock } 23807f7322feSeschrock 23817f7322feSeschrock /* 2382fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2383fa9e4066Sahrens * Can return NULL if this is a pool. 2384fa9e4066Sahrens */ 2385fa9e4066Sahrens static int 2386fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2387fa9e4066Sahrens { 2388fa9e4066Sahrens char *loc; 2389fa9e4066Sahrens 2390fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2391fa9e4066Sahrens return (-1); 2392fa9e4066Sahrens 2393fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2394fa9e4066Sahrens buf[loc - path] = '\0'; 2395fa9e4066Sahrens 2396fa9e4066Sahrens return (0); 2397fa9e4066Sahrens } 2398fa9e4066Sahrens 2399fa9e4066Sahrens /* 24007f1f55eaSvb160487 * If accept_ancestor is false, then check to make sure that the given path has 24017f1f55eaSvb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 24027f1f55eaSvb160487 * closest existing ancestor for the given path. In prefixlen return the 24037f1f55eaSvb160487 * length of already existing prefix of the given path. We also fetch the 24047f1f55eaSvb160487 * 'zoned' property, which is used to validate property settings when creating 24057f1f55eaSvb160487 * new datasets. 2406fa9e4066Sahrens */ 2407fa9e4066Sahrens static int 24087f1f55eaSvb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 24097f1f55eaSvb160487 boolean_t accept_ancestor, int *prefixlen) 2410fa9e4066Sahrens { 2411fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2412fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2413fa9e4066Sahrens char *slash; 24147f7322feSeschrock zfs_handle_t *zhp; 241599653d4eSeschrock char errbuf[1024]; 241699653d4eSeschrock 2417deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2418deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2419fa9e4066Sahrens 2420fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2421fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 242299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 242399653d4eSeschrock "missing dataset name")); 242499653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2425fa9e4066Sahrens } 2426fa9e4066Sahrens 2427fa9e4066Sahrens /* check to see if the pool exists */ 2428fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2429fa9e4066Sahrens slash = parent + strlen(parent); 2430fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2431fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 243299653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2433fa9e4066Sahrens errno == ENOENT) { 243499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 243599653d4eSeschrock "no such pool '%s'"), zc.zc_name); 243699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2437fa9e4066Sahrens } 2438fa9e4066Sahrens 2439fa9e4066Sahrens /* check to see if the parent dataset exists */ 24407f1f55eaSvb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 24417f1f55eaSvb160487 if (errno == ENOENT && accept_ancestor) { 24427f1f55eaSvb160487 /* 24437f1f55eaSvb160487 * Go deeper to find an ancestor, give up on top level. 24447f1f55eaSvb160487 */ 24457f1f55eaSvb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 24467f1f55eaSvb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 24477f1f55eaSvb160487 "no such pool '%s'"), zc.zc_name); 24487f1f55eaSvb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 24497f1f55eaSvb160487 } 24507f1f55eaSvb160487 } else if (errno == ENOENT) { 245199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 245299653d4eSeschrock "parent does not exist")); 245399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 24547f1f55eaSvb160487 } else 245599653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2456fa9e4066Sahrens } 2457fa9e4066Sahrens 2458e9dbad6fSeschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2459fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2460e9dbad6fSeschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 246199653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 24627f7322feSeschrock zfs_close(zhp); 2463fa9e4066Sahrens return (-1); 2464fa9e4066Sahrens } 2465fa9e4066Sahrens 2466fa9e4066Sahrens /* make sure parent is a filesystem */ 24677f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 246899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 246999653d4eSeschrock "parent is not a filesystem")); 247099653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 24717f7322feSeschrock zfs_close(zhp); 2472fa9e4066Sahrens return (-1); 2473fa9e4066Sahrens } 2474fa9e4066Sahrens 24757f7322feSeschrock zfs_close(zhp); 24767f1f55eaSvb160487 if (prefixlen != NULL) 24777f1f55eaSvb160487 *prefixlen = strlen(parent); 24787f1f55eaSvb160487 return (0); 24797f1f55eaSvb160487 } 24807f1f55eaSvb160487 24817f1f55eaSvb160487 /* 24827f1f55eaSvb160487 * Finds whether the dataset of the given type(s) exists. 24837f1f55eaSvb160487 */ 24847f1f55eaSvb160487 boolean_t 24857f1f55eaSvb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 24867f1f55eaSvb160487 { 24877f1f55eaSvb160487 zfs_handle_t *zhp; 24887f1f55eaSvb160487 2489f18faf3fSek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 24907f1f55eaSvb160487 return (B_FALSE); 24917f1f55eaSvb160487 24927f1f55eaSvb160487 /* 24937f1f55eaSvb160487 * Try to get stats for the dataset, which will tell us if it exists. 24947f1f55eaSvb160487 */ 24957f1f55eaSvb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 24967f1f55eaSvb160487 int ds_type = zhp->zfs_type; 24977f1f55eaSvb160487 24987f1f55eaSvb160487 zfs_close(zhp); 24997f1f55eaSvb160487 if (types & ds_type) 25007f1f55eaSvb160487 return (B_TRUE); 25017f1f55eaSvb160487 } 25027f1f55eaSvb160487 return (B_FALSE); 25037f1f55eaSvb160487 } 25047f1f55eaSvb160487 25057f1f55eaSvb160487 /* 25063cb34c60Sahrens * Given a path to 'target', create all the ancestors between 25073cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 25083cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 25093cb34c60Sahrens */ 25103cb34c60Sahrens int 25113cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 25123cb34c60Sahrens { 25133cb34c60Sahrens zfs_handle_t *h; 25143cb34c60Sahrens char *cp; 25153cb34c60Sahrens const char *opname; 25163cb34c60Sahrens 25173cb34c60Sahrens /* make sure prefix exists */ 25183cb34c60Sahrens cp = target + prefixlen; 25193cb34c60Sahrens if (*cp != '/') { 25203cb34c60Sahrens assert(strchr(cp, '/') == NULL); 25213cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 25223cb34c60Sahrens } else { 25233cb34c60Sahrens *cp = '\0'; 25243cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 25253cb34c60Sahrens *cp = '/'; 25263cb34c60Sahrens } 25273cb34c60Sahrens if (h == NULL) 25283cb34c60Sahrens return (-1); 25293cb34c60Sahrens zfs_close(h); 25303cb34c60Sahrens 25313cb34c60Sahrens /* 25323cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 25333cb34c60Sahrens * up to the prefixlen-long one. 25343cb34c60Sahrens */ 25353cb34c60Sahrens for (cp = target + prefixlen + 1; 25363cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 25373cb34c60Sahrens char *logstr; 25383cb34c60Sahrens 25393cb34c60Sahrens *cp = '\0'; 25403cb34c60Sahrens 25413cb34c60Sahrens h = make_dataset_handle(hdl, target); 25423cb34c60Sahrens if (h) { 25433cb34c60Sahrens /* it already exists, nothing to do here */ 25443cb34c60Sahrens zfs_close(h); 25453cb34c60Sahrens continue; 25463cb34c60Sahrens } 25473cb34c60Sahrens 25483cb34c60Sahrens logstr = hdl->libzfs_log_str; 25493cb34c60Sahrens hdl->libzfs_log_str = NULL; 25503cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 25513cb34c60Sahrens NULL) != 0) { 25523cb34c60Sahrens hdl->libzfs_log_str = logstr; 25533cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 25543cb34c60Sahrens goto ancestorerr; 25553cb34c60Sahrens } 25563cb34c60Sahrens 25573cb34c60Sahrens hdl->libzfs_log_str = logstr; 25583cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 25593cb34c60Sahrens if (h == NULL) { 25603cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 25613cb34c60Sahrens goto ancestorerr; 25623cb34c60Sahrens } 25633cb34c60Sahrens 25643cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 25653cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 25663cb34c60Sahrens goto ancestorerr; 25673cb34c60Sahrens } 25683cb34c60Sahrens 25693cb34c60Sahrens if (zfs_share(h) != 0) { 25703cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 25713cb34c60Sahrens goto ancestorerr; 25723cb34c60Sahrens } 25733cb34c60Sahrens 25743cb34c60Sahrens zfs_close(h); 25753cb34c60Sahrens } 25763cb34c60Sahrens 25773cb34c60Sahrens return (0); 25783cb34c60Sahrens 25793cb34c60Sahrens ancestorerr: 25803cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25813cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 25823cb34c60Sahrens return (-1); 25833cb34c60Sahrens } 25843cb34c60Sahrens 25853cb34c60Sahrens /* 25867f1f55eaSvb160487 * Creates non-existing ancestors of the given path. 25877f1f55eaSvb160487 */ 25887f1f55eaSvb160487 int 25897f1f55eaSvb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 25907f1f55eaSvb160487 { 25917f1f55eaSvb160487 int prefix; 25927f1f55eaSvb160487 uint64_t zoned; 25937f1f55eaSvb160487 char *path_copy; 25947f1f55eaSvb160487 int rc; 25957f1f55eaSvb160487 25967f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 25977f1f55eaSvb160487 return (-1); 25987f1f55eaSvb160487 25997f1f55eaSvb160487 if ((path_copy = strdup(path)) != NULL) { 26007f1f55eaSvb160487 rc = create_parents(hdl, path_copy, prefix); 26017f1f55eaSvb160487 free(path_copy); 26027f1f55eaSvb160487 } 26037f1f55eaSvb160487 if (path_copy == NULL || rc != 0) 26047f1f55eaSvb160487 return (-1); 26057f1f55eaSvb160487 2606fa9e4066Sahrens return (0); 2607fa9e4066Sahrens } 2608fa9e4066Sahrens 2609fa9e4066Sahrens /* 2610e9dbad6fSeschrock * Create a new filesystem or volume. 2611fa9e4066Sahrens */ 2612fa9e4066Sahrens int 261399653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2614e9dbad6fSeschrock nvlist_t *props) 2615fa9e4066Sahrens { 2616fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2617fa9e4066Sahrens int ret; 2618fa9e4066Sahrens uint64_t size = 0; 2619fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 262099653d4eSeschrock char errbuf[1024]; 2621e9dbad6fSeschrock uint64_t zoned; 262299653d4eSeschrock 262399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 262499653d4eSeschrock "cannot create '%s'"), path); 2625fa9e4066Sahrens 2626fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2627f18faf3fSek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 262899653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2629fa9e4066Sahrens 2630fa9e4066Sahrens /* validate parents exist */ 26317f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2632fa9e4066Sahrens return (-1); 2633fa9e4066Sahrens 2634fa9e4066Sahrens /* 2635fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2636fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2637fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2638fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2639fa9e4066Sahrens * first try to see if the dataset exists. 2640fa9e4066Sahrens */ 2641fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2642990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 264399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 264499653d4eSeschrock "dataset already exists")); 264599653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2646fa9e4066Sahrens } 2647fa9e4066Sahrens 2648fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2649fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2650fa9e4066Sahrens else 2651fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2652fa9e4066Sahrens 26530a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2654b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2655e9dbad6fSeschrock return (-1); 2656e9dbad6fSeschrock 2657fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 26585c5460e9Seschrock /* 26595c5460e9Seschrock * If we are creating a volume, the size and block size must 26605c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 26615c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 26625c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 26635c5460e9Seschrock * zero. 26645c5460e9Seschrock */ 2665e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2666e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2667e9dbad6fSeschrock nvlist_free(props); 266899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2669e9dbad6fSeschrock "missing volume size")); 2670e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2671fa9e4066Sahrens } 2672fa9e4066Sahrens 2673e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2674e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2675e9dbad6fSeschrock &blocksize)) != 0) { 2676e9dbad6fSeschrock if (ret == ENOENT) { 2677e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2678e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2679e9dbad6fSeschrock } else { 2680e9dbad6fSeschrock nvlist_free(props); 268199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2682e9dbad6fSeschrock "missing volume block size")); 2683e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2684e9dbad6fSeschrock } 2685e9dbad6fSeschrock } 2686e9dbad6fSeschrock 2687e9dbad6fSeschrock if (size == 0) { 2688e9dbad6fSeschrock nvlist_free(props); 2689e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2690e9dbad6fSeschrock "volume size cannot be zero")); 2691e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 26925c5460e9Seschrock } 26935c5460e9Seschrock 26945c5460e9Seschrock if (size % blocksize != 0) { 2695e9dbad6fSeschrock nvlist_free(props); 269699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2697e9dbad6fSeschrock "volume size must be a multiple of volume block " 2698e9dbad6fSeschrock "size")); 2699e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2700e9dbad6fSeschrock } 27015c5460e9Seschrock } 27025c5460e9Seschrock 2703990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2704e9dbad6fSeschrock return (-1); 2705e9dbad6fSeschrock nvlist_free(props); 2706fa9e4066Sahrens 2707fa9e4066Sahrens /* create the dataset */ 2708ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2709fa9e4066Sahrens 2710b1b8ab34Slling if (ret == 0 && type == ZFS_TYPE_VOLUME) { 271199653d4eSeschrock ret = zvol_create_link(hdl, path); 2712b1b8ab34Slling if (ret) { 2713b1b8ab34Slling (void) zfs_standard_error(hdl, errno, 2714b1b8ab34Slling dgettext(TEXT_DOMAIN, 2715b1b8ab34Slling "Volume successfully created, but device links " 2716b1b8ab34Slling "were not created")); 2717b1b8ab34Slling zcmd_free_nvlists(&zc); 2718b1b8ab34Slling return (-1); 2719b1b8ab34Slling } 2720b1b8ab34Slling } 2721fa9e4066Sahrens 2722e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2723e9dbad6fSeschrock 2724fa9e4066Sahrens /* check for failure */ 2725fa9e4066Sahrens if (ret != 0) { 2726fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2727fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2728fa9e4066Sahrens 2729fa9e4066Sahrens switch (errno) { 2730fa9e4066Sahrens case ENOENT: 273199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 273299653d4eSeschrock "no such parent '%s'"), parent); 273399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2734fa9e4066Sahrens 2735fa9e4066Sahrens case EINVAL: 273699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2737d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 273899653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2739fa9e4066Sahrens 2740fa9e4066Sahrens case EDOM: 274199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2742e9dbad6fSeschrock "volume block size must be power of 2 from " 2743e9dbad6fSeschrock "%u to %uk"), 2744fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2745fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 274699653d4eSeschrock 2747e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 274899653d4eSeschrock 274940feaa91Sahrens case ENOTSUP: 275040feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 275140feaa91Sahrens "pool must be upgraded to set this " 275240feaa91Sahrens "property or value")); 275340feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2754fa9e4066Sahrens #ifdef _ILP32 2755fa9e4066Sahrens case EOVERFLOW: 2756fa9e4066Sahrens /* 2757fa9e4066Sahrens * This platform can't address a volume this big. 2758fa9e4066Sahrens */ 275999653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 276099653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 276199653d4eSeschrock errbuf)); 2762fa9e4066Sahrens #endif 276399653d4eSeschrock /* FALLTHROUGH */ 2764fa9e4066Sahrens default: 276599653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2766fa9e4066Sahrens } 2767fa9e4066Sahrens } 2768fa9e4066Sahrens 2769fa9e4066Sahrens return (0); 2770fa9e4066Sahrens } 2771fa9e4066Sahrens 2772fa9e4066Sahrens /* 2773fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2774fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2775fa9e4066Sahrens */ 2776fa9e4066Sahrens int 2777fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp) 2778fa9e4066Sahrens { 2779fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2780fa9e4066Sahrens 2781fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2782fa9e4066Sahrens 2783e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2784f3861e1aSahl /* 2785ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2786ecd6cf80Smarks * abort the request. This would only happen for a 2787ecd6cf80Smarks * non-privileged user. 2788f3861e1aSahl */ 2789ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2790ecd6cf80Smarks return (-1); 2791ecd6cf80Smarks } 2792f3861e1aSahl 279399653d4eSeschrock if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 2794fa9e4066Sahrens return (-1); 2795fa9e4066Sahrens 2796fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2797fa9e4066Sahrens } else { 2798fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2799fa9e4066Sahrens } 2800fa9e4066Sahrens 2801ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2802ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 280399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 280499653d4eSeschrock zhp->zfs_name)); 28051d452cf5Sahrens } 2806fa9e4066Sahrens 2807fa9e4066Sahrens remove_mountpoint(zhp); 2808fa9e4066Sahrens 2809fa9e4066Sahrens return (0); 2810fa9e4066Sahrens } 2811fa9e4066Sahrens 28121d452cf5Sahrens struct destroydata { 28131d452cf5Sahrens char *snapname; 28141d452cf5Sahrens boolean_t gotone; 28153ccfa83cSahrens boolean_t closezhp; 28161d452cf5Sahrens }; 28171d452cf5Sahrens 28181d452cf5Sahrens static int 28191d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg) 28201d452cf5Sahrens { 28211d452cf5Sahrens struct destroydata *dd = arg; 28221d452cf5Sahrens zfs_handle_t *szhp; 28231d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 28243ccfa83cSahrens boolean_t closezhp = dd->closezhp; 28253ccfa83cSahrens int rv; 28261d452cf5Sahrens 2827e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2828e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2829e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 28301d452cf5Sahrens 28311d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 28321d452cf5Sahrens if (szhp) { 28331d452cf5Sahrens dd->gotone = B_TRUE; 28341d452cf5Sahrens zfs_close(szhp); 28351d452cf5Sahrens } 28361d452cf5Sahrens 28371d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 28381d452cf5Sahrens (void) zvol_remove_link(zhp->zfs_hdl, name); 28391d452cf5Sahrens /* 28401d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 28411d452cf5Sahrens * return an error, because then we wouldn't visit all 28421d452cf5Sahrens * the volumes. 28431d452cf5Sahrens */ 28441d452cf5Sahrens } 28451d452cf5Sahrens 28463ccfa83cSahrens dd->closezhp = B_TRUE; 28473ccfa83cSahrens rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg); 28483ccfa83cSahrens if (closezhp) 28493ccfa83cSahrens zfs_close(zhp); 28503ccfa83cSahrens return (rv); 28511d452cf5Sahrens } 28521d452cf5Sahrens 28531d452cf5Sahrens /* 28541d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 28551d452cf5Sahrens */ 28561d452cf5Sahrens int 28571d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname) 28581d452cf5Sahrens { 28591d452cf5Sahrens zfs_cmd_t zc = { 0 }; 28601d452cf5Sahrens int ret; 28611d452cf5Sahrens struct destroydata dd = { 0 }; 28621d452cf5Sahrens 28631d452cf5Sahrens dd.snapname = snapname; 28641d452cf5Sahrens (void) zfs_remove_link_cb(zhp, &dd); 28651d452cf5Sahrens 28661d452cf5Sahrens if (!dd.gotone) { 2867ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 28681d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 28691d452cf5Sahrens zhp->zfs_name, snapname)); 28701d452cf5Sahrens } 28711d452cf5Sahrens 28721d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2873e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 28741d452cf5Sahrens 2875ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 28761d452cf5Sahrens if (ret != 0) { 28771d452cf5Sahrens char errbuf[1024]; 28781d452cf5Sahrens 28791d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 28801d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 28811d452cf5Sahrens 28821d452cf5Sahrens switch (errno) { 28831d452cf5Sahrens case EEXIST: 28841d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 28851d452cf5Sahrens "snapshot is cloned")); 28861d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 28871d452cf5Sahrens 28881d452cf5Sahrens default: 28891d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 28901d452cf5Sahrens errbuf)); 28911d452cf5Sahrens } 28921d452cf5Sahrens } 28931d452cf5Sahrens 28941d452cf5Sahrens return (0); 28951d452cf5Sahrens } 28961d452cf5Sahrens 2897fa9e4066Sahrens /* 2898fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 2899fa9e4066Sahrens */ 2900fa9e4066Sahrens int 2901e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 2902fa9e4066Sahrens { 2903fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2904fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2905fa9e4066Sahrens int ret; 290699653d4eSeschrock char errbuf[1024]; 290799653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2908e9dbad6fSeschrock zfs_type_t type; 2909e9dbad6fSeschrock uint64_t zoned; 2910fa9e4066Sahrens 2911fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 2912fa9e4066Sahrens 291399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 291499653d4eSeschrock "cannot create '%s'"), target); 291599653d4eSeschrock 2916fa9e4066Sahrens /* validate the target name */ 2917f18faf3fSek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 291899653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2919fa9e4066Sahrens 2920fa9e4066Sahrens /* validate parents exist */ 29217f1f55eaSvb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 2922fa9e4066Sahrens return (-1); 2923fa9e4066Sahrens 2924fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 2925fa9e4066Sahrens 2926fa9e4066Sahrens /* do the clone */ 2927e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2928fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 29295f8e1617Snn35248 type = ZFS_TYPE_VOLUME; 2930e9dbad6fSeschrock } else { 2931fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 29325f8e1617Snn35248 type = ZFS_TYPE_FILESYSTEM; 2933e9dbad6fSeschrock } 2934e9dbad6fSeschrock 2935e9dbad6fSeschrock if (props) { 29360a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 29370a48a24eStimh zhp, errbuf)) == NULL) 2938e9dbad6fSeschrock return (-1); 2939e9dbad6fSeschrock 2940990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 2941e9dbad6fSeschrock nvlist_free(props); 2942e9dbad6fSeschrock return (-1); 2943e9dbad6fSeschrock } 2944e9dbad6fSeschrock 2945e9dbad6fSeschrock nvlist_free(props); 2946e9dbad6fSeschrock } 2947fa9e4066Sahrens 2948fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 2949e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 2950ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 2951fa9e4066Sahrens 2952e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2953e9dbad6fSeschrock 2954fa9e4066Sahrens if (ret != 0) { 2955fa9e4066Sahrens switch (errno) { 2956fa9e4066Sahrens 2957fa9e4066Sahrens case ENOENT: 2958fa9e4066Sahrens /* 2959fa9e4066Sahrens * The parent doesn't exist. We should have caught this 2960fa9e4066Sahrens * above, but there may a race condition that has since 2961fa9e4066Sahrens * destroyed the parent. 2962fa9e4066Sahrens * 2963fa9e4066Sahrens * At this point, we don't know whether it's the source 2964fa9e4066Sahrens * that doesn't exist anymore, or whether the target 2965fa9e4066Sahrens * dataset doesn't exist. 2966fa9e4066Sahrens */ 296799653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 296899653d4eSeschrock "no such parent '%s'"), parent); 296999653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 2970fa9e4066Sahrens 297199653d4eSeschrock case EXDEV: 297299653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 297399653d4eSeschrock "source and target pools differ")); 297499653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 297599653d4eSeschrock errbuf)); 297699653d4eSeschrock 297799653d4eSeschrock default: 297899653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 297999653d4eSeschrock errbuf)); 298099653d4eSeschrock } 2981e9dbad6fSeschrock } else if (ZFS_IS_VOLUME(zhp)) { 298299653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, target); 298399653d4eSeschrock } 298499653d4eSeschrock 298599653d4eSeschrock return (ret); 298699653d4eSeschrock } 298799653d4eSeschrock 298899653d4eSeschrock typedef struct promote_data { 298999653d4eSeschrock char cb_mountpoint[MAXPATHLEN]; 299099653d4eSeschrock const char *cb_target; 299199653d4eSeschrock const char *cb_errbuf; 299299653d4eSeschrock uint64_t cb_pivot_txg; 299399653d4eSeschrock } promote_data_t; 299499653d4eSeschrock 299599653d4eSeschrock static int 299699653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data) 299799653d4eSeschrock { 299899653d4eSeschrock promote_data_t *pd = data; 299999653d4eSeschrock zfs_handle_t *szhp; 300099653d4eSeschrock char snapname[MAXPATHLEN]; 30013ccfa83cSahrens int rv = 0; 300299653d4eSeschrock 300399653d4eSeschrock /* We don't care about snapshots after the pivot point */ 30043ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) { 30053ccfa83cSahrens zfs_close(zhp); 300699653d4eSeschrock return (0); 30073ccfa83cSahrens } 300899653d4eSeschrock 30090b69c2f0Sahrens /* Remove the device link if it's a zvol. */ 3010e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 30110b69c2f0Sahrens (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); 301299653d4eSeschrock 301399653d4eSeschrock /* Check for conflicting names */ 3014e9dbad6fSeschrock (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); 3015e9dbad6fSeschrock (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); 301699653d4eSeschrock szhp = make_dataset_handle(zhp->zfs_hdl, snapname); 301799653d4eSeschrock if (szhp != NULL) { 301899653d4eSeschrock zfs_close(szhp); 301999653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 302099653d4eSeschrock "snapshot name '%s' from origin \n" 302199653d4eSeschrock "conflicts with '%s' from target"), 302299653d4eSeschrock zhp->zfs_name, snapname); 30233ccfa83cSahrens rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf); 302499653d4eSeschrock } 30253ccfa83cSahrens zfs_close(zhp); 30263ccfa83cSahrens return (rv); 302799653d4eSeschrock } 302899653d4eSeschrock 30290b69c2f0Sahrens static int 30300b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data) 30310b69c2f0Sahrens { 30320b69c2f0Sahrens promote_data_t *pd = data; 30330b69c2f0Sahrens 30340b69c2f0Sahrens /* We don't care about snapshots after the pivot point */ 30353ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) { 30360b69c2f0Sahrens /* Create the device link if it's a zvol. */ 3037e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 30380b69c2f0Sahrens (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); 30393ccfa83cSahrens } 30400b69c2f0Sahrens 30413ccfa83cSahrens zfs_close(zhp); 30420b69c2f0Sahrens return (0); 30430b69c2f0Sahrens } 30440b69c2f0Sahrens 304599653d4eSeschrock /* 304699653d4eSeschrock * Promotes the given clone fs to be the clone parent. 304799653d4eSeschrock */ 304899653d4eSeschrock int 304999653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 305099653d4eSeschrock { 305199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 305299653d4eSeschrock zfs_cmd_t zc = { 0 }; 305399653d4eSeschrock char parent[MAXPATHLEN]; 305499653d4eSeschrock char *cp; 305599653d4eSeschrock int ret; 305699653d4eSeschrock zfs_handle_t *pzhp; 305799653d4eSeschrock promote_data_t pd; 305899653d4eSeschrock char errbuf[1024]; 305999653d4eSeschrock 306099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 306199653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 306299653d4eSeschrock 306399653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 306499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 306599653d4eSeschrock "snapshots can not be promoted")); 306699653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 306799653d4eSeschrock } 306899653d4eSeschrock 30693cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 307099653d4eSeschrock if (parent[0] == '\0') { 307199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 307299653d4eSeschrock "not a cloned filesystem")); 307399653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 307499653d4eSeschrock } 307599653d4eSeschrock cp = strchr(parent, '@'); 307699653d4eSeschrock *cp = '\0'; 307799653d4eSeschrock 307899653d4eSeschrock /* Walk the snapshots we will be moving */ 30793cb34c60Sahrens pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); 308099653d4eSeschrock if (pzhp == NULL) 308199653d4eSeschrock return (-1); 308299653d4eSeschrock pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); 308399653d4eSeschrock zfs_close(pzhp); 308499653d4eSeschrock pd.cb_target = zhp->zfs_name; 308599653d4eSeschrock pd.cb_errbuf = errbuf; 3086990b4856Slling pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET); 308799653d4eSeschrock if (pzhp == NULL) 308899653d4eSeschrock return (-1); 308999653d4eSeschrock (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, 309099653d4eSeschrock sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); 309199653d4eSeschrock ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); 30920b69c2f0Sahrens if (ret != 0) { 30930b69c2f0Sahrens zfs_close(pzhp); 309499653d4eSeschrock return (-1); 30950b69c2f0Sahrens } 309699653d4eSeschrock 309799653d4eSeschrock /* issue the ioctl */ 30983cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3099e9dbad6fSeschrock sizeof (zc.zc_value)); 310099653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3101ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 310299653d4eSeschrock 310399653d4eSeschrock if (ret != 0) { 31040b69c2f0Sahrens int save_errno = errno; 3105fa9e4066Sahrens 31060b69c2f0Sahrens (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd); 31070b69c2f0Sahrens zfs_close(pzhp); 31080b69c2f0Sahrens 31090b69c2f0Sahrens switch (save_errno) { 3110fa9e4066Sahrens case EEXIST: 3111fa9e4066Sahrens /* 311299653d4eSeschrock * There is a conflicting snapshot name. We 311399653d4eSeschrock * should have caught this above, but they could 311499653d4eSeschrock * have renamed something in the mean time. 3115fa9e4066Sahrens */ 311699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 311799653d4eSeschrock "conflicting snapshot name from parent '%s'"), 311899653d4eSeschrock parent); 311999653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3120fa9e4066Sahrens 3121fa9e4066Sahrens default: 31220b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3123fa9e4066Sahrens } 31240b69c2f0Sahrens } else { 31250b69c2f0Sahrens (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd); 3126fa9e4066Sahrens } 3127fa9e4066Sahrens 31280b69c2f0Sahrens zfs_close(pzhp); 3129fa9e4066Sahrens return (ret); 3130fa9e4066Sahrens } 3131fa9e4066Sahrens 3132cdf5b4caSmmusante struct createdata { 3133cdf5b4caSmmusante const char *cd_snapname; 3134cdf5b4caSmmusante int cd_ifexists; 3135cdf5b4caSmmusante }; 3136cdf5b4caSmmusante 31371d452cf5Sahrens static int 31381d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg) 31391d452cf5Sahrens { 3140cdf5b4caSmmusante struct createdata *cd = arg; 3141e9dbad6fSeschrock int ret; 31421d452cf5Sahrens 31431d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 31441d452cf5Sahrens char name[MAXPATHLEN]; 31451d452cf5Sahrens 3146e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 3147e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 3148cdf5b4caSmmusante (void) strlcat(name, cd->cd_snapname, sizeof (name)); 3149cdf5b4caSmmusante (void) zvol_create_link_common(zhp->zfs_hdl, name, 3150cdf5b4caSmmusante cd->cd_ifexists); 31511d452cf5Sahrens /* 31521d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 31531d452cf5Sahrens * return an error, because then we wouldn't visit all 31541d452cf5Sahrens * the volumes. 31551d452cf5Sahrens */ 31561d452cf5Sahrens } 3157e9dbad6fSeschrock 3158cdf5b4caSmmusante ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd); 3159e9dbad6fSeschrock 3160e9dbad6fSeschrock zfs_close(zhp); 3161e9dbad6fSeschrock 3162e9dbad6fSeschrock return (ret); 31631d452cf5Sahrens } 31641d452cf5Sahrens 3165fa9e4066Sahrens /* 316672bdce51Sahl * Takes a snapshot of the given dataset. 3167fa9e4066Sahrens */ 3168fa9e4066Sahrens int 3169bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3170bb0ade09Sahrens nvlist_t *props) 3171fa9e4066Sahrens { 3172fa9e4066Sahrens const char *delim; 3173bb0ade09Sahrens char parent[ZFS_MAXNAMELEN]; 3174fa9e4066Sahrens zfs_handle_t *zhp; 3175fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3176fa9e4066Sahrens int ret; 317799653d4eSeschrock char errbuf[1024]; 3178fa9e4066Sahrens 317999653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 318099653d4eSeschrock "cannot snapshot '%s'"), path); 318199653d4eSeschrock 318299653d4eSeschrock /* validate the target name */ 3183f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 318499653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3185fa9e4066Sahrens 3186bb0ade09Sahrens if (props) { 3187bb0ade09Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3188bb0ade09Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 3189bb0ade09Sahrens return (-1); 3190bb0ade09Sahrens 3191bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3192bb0ade09Sahrens nvlist_free(props); 3193bb0ade09Sahrens return (-1); 3194bb0ade09Sahrens } 3195bb0ade09Sahrens 3196bb0ade09Sahrens nvlist_free(props); 3197bb0ade09Sahrens } 3198bb0ade09Sahrens 3199fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 32001d452cf5Sahrens delim = strchr(path, '@'); 3201fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3202fa9e4066Sahrens parent[delim - path] = '\0'; 3203fa9e4066Sahrens 320499653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3205fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3206bb0ade09Sahrens zcmd_free_nvlists(&zc); 3207fa9e4066Sahrens return (-1); 3208fa9e4066Sahrens } 3209fa9e4066Sahrens 32101d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3211e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3212ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3213ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3214ecd6cf80Smarks else 3215ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 32161d452cf5Sahrens zc.zc_cookie = recursive; 3217ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3218fa9e4066Sahrens 3219bb0ade09Sahrens zcmd_free_nvlists(&zc); 3220bb0ade09Sahrens 32211d452cf5Sahrens /* 32221d452cf5Sahrens * if it was recursive, the one that actually failed will be in 32231d452cf5Sahrens * zc.zc_name. 32241d452cf5Sahrens */ 3225ecd6cf80Smarks if (ret != 0) 32261d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3227e9dbad6fSeschrock "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 3228ecd6cf80Smarks 32291d452cf5Sahrens if (ret == 0 && recursive) { 3230cdf5b4caSmmusante struct createdata cd; 3231cdf5b4caSmmusante 3232cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3233cdf5b4caSmmusante cd.cd_ifexists = B_FALSE; 3234cdf5b4caSmmusante (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd); 32351d452cf5Sahrens } 3236fa9e4066Sahrens if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { 323799653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, path); 32381d452cf5Sahrens if (ret != 0) { 3239ecd6cf80Smarks (void) zfs_standard_error(hdl, errno, 3240ecd6cf80Smarks dgettext(TEXT_DOMAIN, 3241ecd6cf80Smarks "Volume successfully snapshotted, but device links " 3242ecd6cf80Smarks "were not created")); 3243ecd6cf80Smarks zfs_close(zhp); 3244ecd6cf80Smarks return (-1); 3245fa9e4066Sahrens } 32461d452cf5Sahrens } 3247fa9e4066Sahrens 324899653d4eSeschrock if (ret != 0) 324999653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3250fa9e4066Sahrens 3251fa9e4066Sahrens zfs_close(zhp); 3252fa9e4066Sahrens 3253fa9e4066Sahrens return (ret); 3254fa9e4066Sahrens } 3255fa9e4066Sahrens 3256fa9e4066Sahrens /* 3257b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3258b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3259b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3260b12a1c38Slling * group. 3261fa9e4066Sahrens */ 3262b12a1c38Slling typedef struct rollback_data { 3263b12a1c38Slling const char *cb_target; /* the snapshot */ 3264b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3265c391e322Sahrens boolean_t cb_error; 326699653d4eSeschrock boolean_t cb_dependent; 3267c391e322Sahrens boolean_t cb_force; 3268b12a1c38Slling } rollback_data_t; 3269b12a1c38Slling 3270b12a1c38Slling static int 3271b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3272b12a1c38Slling { 3273b12a1c38Slling rollback_data_t *cbp = data; 3274b12a1c38Slling 3275b12a1c38Slling if (!cbp->cb_dependent) { 3276b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3277b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3278b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3279b12a1c38Slling cbp->cb_create) { 3280ecd6cf80Smarks char *logstr; 3281b12a1c38Slling 328299653d4eSeschrock cbp->cb_dependent = B_TRUE; 32834ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32844ccbb6e7Sahrens rollback_destroy, cbp); 328599653d4eSeschrock cbp->cb_dependent = B_FALSE; 3286b12a1c38Slling 3287ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3288ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 32894ccbb6e7Sahrens cbp->cb_error |= zfs_destroy(zhp); 3290ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3291b12a1c38Slling } 3292b12a1c38Slling } else { 3293c391e322Sahrens /* We must destroy this clone; first unmount it */ 3294c391e322Sahrens prop_changelist_t *clp; 3295c391e322Sahrens 32960069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3297c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3298c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3299c391e322Sahrens cbp->cb_error = B_TRUE; 3300c391e322Sahrens zfs_close(zhp); 3301c391e322Sahrens return (0); 3302c391e322Sahrens } 3303c391e322Sahrens if (zfs_destroy(zhp) != 0) 3304c391e322Sahrens cbp->cb_error = B_TRUE; 3305c391e322Sahrens else 3306c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3307ba7b046eSahrens (void) changelist_postfix(clp); 3308c391e322Sahrens changelist_free(clp); 3309b12a1c38Slling } 3310b12a1c38Slling 3311b12a1c38Slling zfs_close(zhp); 3312b12a1c38Slling return (0); 3313b12a1c38Slling } 3314b12a1c38Slling 3315b12a1c38Slling /* 33164ccbb6e7Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33174ccbb6e7Sahrens * data changes since then and making it the active dataset. 33184ccbb6e7Sahrens * 33194ccbb6e7Sahrens * Any snapshots more recent than the target are destroyed, along with 33204ccbb6e7Sahrens * their dependents. 3321b12a1c38Slling */ 33224ccbb6e7Sahrens int 3323c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3324fa9e4066Sahrens { 33254ccbb6e7Sahrens rollback_data_t cb = { 0 }; 33264ccbb6e7Sahrens int err; 3327fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 33287b97dc1aSrm160521 boolean_t restore_resv = 0; 33297b97dc1aSrm160521 uint64_t old_volsize, new_volsize; 33307b97dc1aSrm160521 zfs_prop_t resv_prop; 3331fa9e4066Sahrens 3332fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3333fa9e4066Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3334fa9e4066Sahrens 33354ccbb6e7Sahrens /* 33364ccbb6e7Sahrens * Destroy all recent snapshots and its dependends. 33374ccbb6e7Sahrens */ 3338c391e322Sahrens cb.cb_force = force; 33394ccbb6e7Sahrens cb.cb_target = snap->zfs_name; 33404ccbb6e7Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33414ccbb6e7Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33424ccbb6e7Sahrens 3343c391e322Sahrens if (cb.cb_error) 3344c391e322Sahrens return (-1); 33454ccbb6e7Sahrens 33464ccbb6e7Sahrens /* 33474ccbb6e7Sahrens * Now that we have verified that the snapshot is the latest, 33484ccbb6e7Sahrens * rollback to the given snapshot. 33494ccbb6e7Sahrens */ 33504ccbb6e7Sahrens 33517b97dc1aSrm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33527b97dc1aSrm160521 if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 3353fa9e4066Sahrens return (-1); 33547b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33557b97dc1aSrm160521 return (-1); 33567b97dc1aSrm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33577b97dc1aSrm160521 restore_resv = 33587b97dc1aSrm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33597b97dc1aSrm160521 } 3360fa9e4066Sahrens 3361fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3362fa9e4066Sahrens 3363e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3364fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3365fa9e4066Sahrens else 3366fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3367fa9e4066Sahrens 3368fa9e4066Sahrens /* 33694ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 33704ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 33714ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 33724ccbb6e7Sahrens * an unlikely race condition where the user has taken a 33734ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 33747b97dc1aSrm160521 * 3375fa9e4066Sahrens */ 33764ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3377ece3d9b3Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 337899653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 337999653d4eSeschrock zhp->zfs_name); 3380b9415e83Srm160521 return (err); 3381b9415e83Srm160521 } 3382fa9e4066Sahrens 33837b97dc1aSrm160521 /* 33847b97dc1aSrm160521 * For volumes, if the pre-rollback volsize matched the pre- 33857b97dc1aSrm160521 * rollback reservation and the volsize has changed then set 33867b97dc1aSrm160521 * the reservation property to the post-rollback volsize. 33877b97dc1aSrm160521 * Make a new handle since the rollback closed the dataset. 33887b97dc1aSrm160521 */ 3389b9415e83Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3390b9415e83Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3391b9415e83Srm160521 if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) { 3392b9415e83Srm160521 zfs_close(zhp); 33937b97dc1aSrm160521 return (err); 3394b9415e83Srm160521 } 33957b97dc1aSrm160521 if (restore_resv) { 33967b97dc1aSrm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33977b97dc1aSrm160521 if (old_volsize != new_volsize) 3398b9415e83Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 3399b9415e83Srm160521 new_volsize); 34007b97dc1aSrm160521 } 34017b97dc1aSrm160521 zfs_close(zhp); 34027b97dc1aSrm160521 } 34034ccbb6e7Sahrens return (err); 3404b12a1c38Slling } 3405b12a1c38Slling 3406b12a1c38Slling /* 3407fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3408fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3409fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3410fa9e4066Sahrens * libzfs_graph.c. 3411fa9e4066Sahrens */ 3412fa9e4066Sahrens int 34133bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 34143bb79becSeschrock zfs_iter_f func, void *data) 3415fa9e4066Sahrens { 3416fa9e4066Sahrens char **dependents; 3417fa9e4066Sahrens size_t count; 3418fa9e4066Sahrens int i; 3419fa9e4066Sahrens zfs_handle_t *child; 3420fa9e4066Sahrens int ret = 0; 3421fa9e4066Sahrens 34223bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34233bb79becSeschrock &dependents, &count) != 0) 34243bb79becSeschrock return (-1); 34253bb79becSeschrock 3426fa9e4066Sahrens for (i = 0; i < count; i++) { 342799653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 342899653d4eSeschrock dependents[i])) == NULL) 3429fa9e4066Sahrens continue; 3430fa9e4066Sahrens 3431fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3432fa9e4066Sahrens break; 3433fa9e4066Sahrens } 3434fa9e4066Sahrens 3435fa9e4066Sahrens for (i = 0; i < count; i++) 3436fa9e4066Sahrens free(dependents[i]); 3437fa9e4066Sahrens free(dependents); 3438fa9e4066Sahrens 3439fa9e4066Sahrens return (ret); 3440fa9e4066Sahrens } 3441fa9e4066Sahrens 3442fa9e4066Sahrens /* 3443fa9e4066Sahrens * Renames the given dataset. 3444fa9e4066Sahrens */ 3445fa9e4066Sahrens int 34467f1f55eaSvb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3447fa9e4066Sahrens { 3448fa9e4066Sahrens int ret; 3449fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3450fa9e4066Sahrens char *delim; 3451cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3452cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3453cdf5b4caSmmusante char *parentname = NULL; 3454fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 345599653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 345699653d4eSeschrock char errbuf[1024]; 3457fa9e4066Sahrens 3458fa9e4066Sahrens /* if we have the same exact name, just return success */ 3459fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3460fa9e4066Sahrens return (0); 3461fa9e4066Sahrens 346299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 346399653d4eSeschrock "cannot rename to '%s'"), target); 346499653d4eSeschrock 3465fa9e4066Sahrens /* 3466fa9e4066Sahrens * Make sure the target name is valid 3467fa9e4066Sahrens */ 3468fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 346998579b20Snd150628 if ((strchr(target, '@') == NULL) || 347098579b20Snd150628 *target == '@') { 347198579b20Snd150628 /* 347298579b20Snd150628 * Snapshot target name is abbreviated, 347398579b20Snd150628 * reconstruct full dataset name 347498579b20Snd150628 */ 347598579b20Snd150628 (void) strlcpy(parent, zhp->zfs_name, 347698579b20Snd150628 sizeof (parent)); 347798579b20Snd150628 delim = strchr(parent, '@'); 347898579b20Snd150628 if (strchr(target, '@') == NULL) 347998579b20Snd150628 *(++delim) = '\0'; 348098579b20Snd150628 else 348198579b20Snd150628 *delim = '\0'; 348298579b20Snd150628 (void) strlcat(parent, target, sizeof (parent)); 348398579b20Snd150628 target = parent; 348498579b20Snd150628 } else { 3485fa9e4066Sahrens /* 3486fa9e4066Sahrens * Make sure we're renaming within the same dataset. 3487fa9e4066Sahrens */ 348898579b20Snd150628 delim = strchr(target, '@'); 348998579b20Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 349098579b20Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 349199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 349298579b20Snd150628 "snapshots must be part of same " 349398579b20Snd150628 "dataset")); 349498579b20Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 349598579b20Snd150628 errbuf)); 3496fa9e4066Sahrens } 349798579b20Snd150628 } 3498f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 349998579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3500fa9e4066Sahrens } else { 3501cdf5b4caSmmusante if (recursive) { 3502cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3503cdf5b4caSmmusante "recursive rename must be a snapshot")); 3504cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3505cdf5b4caSmmusante } 3506cdf5b4caSmmusante 3507f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 350898579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3509e9dbad6fSeschrock uint64_t unused; 3510e9dbad6fSeschrock 3511fa9e4066Sahrens /* validate parents */ 35127f1f55eaSvb160487 if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3513fa9e4066Sahrens return (-1); 3514fa9e4066Sahrens 3515fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3516fa9e4066Sahrens 3517fa9e4066Sahrens /* make sure we're in the same pool */ 3518fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3519fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3520fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 352199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 352299653d4eSeschrock "datasets must be within same pool")); 352399653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3524fa9e4066Sahrens } 3525f2fdf992Snd150628 3526f2fdf992Snd150628 /* new name cannot be a child of the current dataset name */ 3527f2fdf992Snd150628 if (strncmp(parent, zhp->zfs_name, 3528f2fdf992Snd150628 strlen(zhp->zfs_name)) == 0) { 3529f2fdf992Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3530f2fdf992Snd150628 "New dataset name cannot be a descendent of " 3531f2fdf992Snd150628 "current dataset name")); 3532f2fdf992Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3533f2fdf992Snd150628 } 3534fa9e4066Sahrens } 3535fa9e4066Sahrens 353699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 353799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 353899653d4eSeschrock 3539fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3540fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 354199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 354299653d4eSeschrock "dataset is used in a non-global zone")); 354399653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3544fa9e4066Sahrens } 3545fa9e4066Sahrens 3546cdf5b4caSmmusante if (recursive) { 3547cdf5b4caSmmusante struct destroydata dd; 3548cdf5b4caSmmusante 3549f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3550f0c5ee21Smmusante if (parentname == NULL) { 3551f0c5ee21Smmusante ret = -1; 3552f0c5ee21Smmusante goto error; 3553f0c5ee21Smmusante } 3554cdf5b4caSmmusante delim = strchr(parentname, '@'); 3555cdf5b4caSmmusante *delim = '\0'; 3556990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3557cdf5b4caSmmusante if (zhrp == NULL) { 3558f0c5ee21Smmusante ret = -1; 3559f0c5ee21Smmusante goto error; 3560cdf5b4caSmmusante } 3561cdf5b4caSmmusante 3562cdf5b4caSmmusante dd.snapname = delim + 1; 3563cdf5b4caSmmusante dd.gotone = B_FALSE; 3564f0c5ee21Smmusante dd.closezhp = B_TRUE; 3565cdf5b4caSmmusante 3566cdf5b4caSmmusante /* We remove any zvol links prior to renaming them */ 3567cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd); 3568cdf5b4caSmmusante if (ret) { 3569cdf5b4caSmmusante goto error; 3570cdf5b4caSmmusante } 3571cdf5b4caSmmusante } else { 35720069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 357399653d4eSeschrock return (-1); 3574fa9e4066Sahrens 3575fa9e4066Sahrens if (changelist_haszonedchild(cl)) { 357699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 357799653d4eSeschrock "child dataset with inherited mountpoint is used " 357899653d4eSeschrock "in a non-global zone")); 3579e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3580fa9e4066Sahrens goto error; 3581fa9e4066Sahrens } 3582fa9e4066Sahrens 3583fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 3584fa9e4066Sahrens goto error; 3585cdf5b4caSmmusante } 3586fa9e4066Sahrens 3587e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3588fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3589fa9e4066Sahrens else 3590fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3591fa9e4066Sahrens 359298579b20Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3593e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 359498579b20Snd150628 3595cdf5b4caSmmusante zc.zc_cookie = recursive; 3596cdf5b4caSmmusante 3597ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3598cdf5b4caSmmusante /* 3599cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3600cdf5b4caSmmusante * be in zc.zc_name 3601cdf5b4caSmmusante */ 3602cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 36033cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3604cdf5b4caSmmusante 3605cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3606cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3607cdf5b4caSmmusante "a child dataset already has a snapshot " 3608cdf5b4caSmmusante "with the new name")); 3609a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3610cdf5b4caSmmusante } else { 361199653d4eSeschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3612cdf5b4caSmmusante } 3613fa9e4066Sahrens 3614fa9e4066Sahrens /* 3615fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3616fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3617fa9e4066Sahrens */ 3618cdf5b4caSmmusante if (recursive) { 3619cdf5b4caSmmusante struct createdata cd; 3620cdf5b4caSmmusante 3621cdf5b4caSmmusante /* only create links for datasets that had existed */ 3622cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3623cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3624cdf5b4caSmmusante (void) zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3625cdf5b4caSmmusante &cd); 3626cdf5b4caSmmusante } else { 3627fa9e4066Sahrens (void) changelist_postfix(cl); 3628cdf5b4caSmmusante } 3629cdf5b4caSmmusante } else { 3630cdf5b4caSmmusante if (recursive) { 3631cdf5b4caSmmusante struct createdata cd; 3632cdf5b4caSmmusante 3633cdf5b4caSmmusante /* only create links for datasets that had existed */ 3634cdf5b4caSmmusante cd.cd_snapname = strchr(target, '@') + 1; 3635cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3636cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3637cdf5b4caSmmusante &cd); 3638fa9e4066Sahrens } else { 3639fa9e4066Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 3640fa9e4066Sahrens ret = changelist_postfix(cl); 3641fa9e4066Sahrens } 3642cdf5b4caSmmusante } 3643fa9e4066Sahrens 3644fa9e4066Sahrens error: 3645cdf5b4caSmmusante if (parentname) { 3646cdf5b4caSmmusante free(parentname); 3647cdf5b4caSmmusante } 3648cdf5b4caSmmusante if (zhrp) { 3649cdf5b4caSmmusante zfs_close(zhrp); 3650cdf5b4caSmmusante } 3651cdf5b4caSmmusante if (cl) { 3652fa9e4066Sahrens changelist_free(cl); 3653cdf5b4caSmmusante } 3654fa9e4066Sahrens return (ret); 3655fa9e4066Sahrens } 3656fa9e4066Sahrens 3657fa9e4066Sahrens /* 3658fa9e4066Sahrens * Given a zvol dataset, issue the ioctl to create the appropriate minor node, 3659fa9e4066Sahrens * poke devfsadm to create the /dev link, and then wait for the link to appear. 3660fa9e4066Sahrens */ 3661fa9e4066Sahrens int 366299653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset) 3663fa9e4066Sahrens { 3664cdf5b4caSmmusante return (zvol_create_link_common(hdl, dataset, B_FALSE)); 3665cdf5b4caSmmusante } 3666cdf5b4caSmmusante 3667cdf5b4caSmmusante static int 3668cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists) 3669cdf5b4caSmmusante { 3670fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 367199653d4eSeschrock di_devlink_handle_t dhdl; 3672ecd6cf80Smarks priv_set_t *priv_effective; 3673ecd6cf80Smarks int privileged; 3674fa9e4066Sahrens 3675fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3676fa9e4066Sahrens 3677fa9e4066Sahrens /* 3678fa9e4066Sahrens * Issue the appropriate ioctl. 3679fa9e4066Sahrens */ 368099653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { 3681fa9e4066Sahrens switch (errno) { 3682fa9e4066Sahrens case EEXIST: 3683fa9e4066Sahrens /* 3684fa9e4066Sahrens * Silently ignore the case where the link already 3685fa9e4066Sahrens * exists. This allows 'zfs volinit' to be run multiple 3686fa9e4066Sahrens * times without errors. 3687fa9e4066Sahrens */ 3688fa9e4066Sahrens return (0); 3689fa9e4066Sahrens 3690cdf5b4caSmmusante case ENOENT: 3691cdf5b4caSmmusante /* 3692cdf5b4caSmmusante * Dataset does not exist in the kernel. If we 3693cdf5b4caSmmusante * don't care (see zfs_rename), then ignore the 3694cdf5b4caSmmusante * error quietly. 3695cdf5b4caSmmusante */ 3696cdf5b4caSmmusante if (ifexists) { 3697cdf5b4caSmmusante return (0); 3698cdf5b4caSmmusante } 3699cdf5b4caSmmusante 3700cdf5b4caSmmusante /* FALLTHROUGH */ 3701cdf5b4caSmmusante 3702fa9e4066Sahrens default: 3703ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 370499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 370599653d4eSeschrock "for '%s'"), dataset)); 3706fa9e4066Sahrens } 3707fa9e4066Sahrens } 3708fa9e4066Sahrens 3709fa9e4066Sahrens /* 3710ecd6cf80Smarks * If privileged call devfsadm and wait for the links to 3711ecd6cf80Smarks * magically appear. 3712ecd6cf80Smarks * Otherwise, print out an informational message. 3713fa9e4066Sahrens */ 3714ecd6cf80Smarks 3715ecd6cf80Smarks priv_effective = priv_allocset(); 3716ecd6cf80Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 3717ecd6cf80Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 3718ecd6cf80Smarks priv_freeset(priv_effective); 3719ecd6cf80Smarks 3720ecd6cf80Smarks if (privileged) { 3721ecd6cf80Smarks if ((dhdl = di_devlink_init(ZFS_DRIVER, 3722ecd6cf80Smarks DI_MAKE_LINK)) == NULL) { 372399653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 3724d1e7ea07SEric Taylor (void) zfs_error_fmt(hdl, errno, 372599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 372699653d4eSeschrock "for '%s'"), dataset); 372799653d4eSeschrock (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); 3728fa9e4066Sahrens return (-1); 3729fa9e4066Sahrens } else { 373099653d4eSeschrock (void) di_devlink_fini(&dhdl); 3731fa9e4066Sahrens } 3732ecd6cf80Smarks } else { 3733ecd6cf80Smarks char pathname[MAXPATHLEN]; 3734ecd6cf80Smarks struct stat64 statbuf; 3735ecd6cf80Smarks int i; 3736ecd6cf80Smarks 3737ecd6cf80Smarks #define MAX_WAIT 10 3738ecd6cf80Smarks 3739ecd6cf80Smarks /* 3740ecd6cf80Smarks * This is the poor mans way of waiting for the link 3741ecd6cf80Smarks * to show up. If after 10 seconds we still don't 3742ecd6cf80Smarks * have it, then print out a message. 3743ecd6cf80Smarks */ 3744ecd6cf80Smarks (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s", 3745ecd6cf80Smarks dataset); 3746ecd6cf80Smarks 3747ecd6cf80Smarks for (i = 0; i != MAX_WAIT; i++) { 3748ecd6cf80Smarks if (stat64(pathname, &statbuf) == 0) 3749ecd6cf80Smarks break; 3750ecd6cf80Smarks (void) sleep(1); 3751ecd6cf80Smarks } 3752ecd6cf80Smarks if (i == MAX_WAIT) 3753ecd6cf80Smarks (void) printf(gettext("%s may not be immediately " 3754ecd6cf80Smarks "available\n"), pathname); 3755ecd6cf80Smarks } 3756fa9e4066Sahrens 3757fa9e4066Sahrens return (0); 3758fa9e4066Sahrens } 3759fa9e4066Sahrens 3760fa9e4066Sahrens /* 3761fa9e4066Sahrens * Remove a minor node for the given zvol and the associated /dev links. 3762fa9e4066Sahrens */ 3763fa9e4066Sahrens int 376499653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) 3765fa9e4066Sahrens { 3766fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3767fa9e4066Sahrens 3768fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3769fa9e4066Sahrens 377099653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { 3771fa9e4066Sahrens switch (errno) { 3772fa9e4066Sahrens case ENXIO: 3773fa9e4066Sahrens /* 3774fa9e4066Sahrens * Silently ignore the case where the link no longer 3775fa9e4066Sahrens * exists, so that 'zfs volfini' can be run multiple 3776fa9e4066Sahrens * times without errors. 3777fa9e4066Sahrens */ 3778fa9e4066Sahrens return (0); 3779fa9e4066Sahrens 3780fa9e4066Sahrens default: 3781ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 378299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot remove device " 378399653d4eSeschrock "links for '%s'"), dataset)); 3784fa9e4066Sahrens } 3785fa9e4066Sahrens } 3786fa9e4066Sahrens 3787fa9e4066Sahrens return (0); 3788fa9e4066Sahrens } 3789e9dbad6fSeschrock 3790e9dbad6fSeschrock nvlist_t * 3791e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3792e9dbad6fSeschrock { 3793e9dbad6fSeschrock return (zhp->zfs_user_props); 3794e9dbad6fSeschrock } 3795e9dbad6fSeschrock 3796e9dbad6fSeschrock /* 3797e9dbad6fSeschrock * This function is used by 'zfs list' to determine the exact set of columns to 3798e9dbad6fSeschrock * display, and their maximum widths. This does two main things: 3799e9dbad6fSeschrock * 3800e9dbad6fSeschrock * - If this is a list of all properties, then expand the list to include 3801e9dbad6fSeschrock * all native properties, and set a flag so that for each dataset we look 3802e9dbad6fSeschrock * for new unique user properties and add them to the list. 3803e9dbad6fSeschrock * 3804e9dbad6fSeschrock * - For non fixed-width properties, keep track of the maximum width seen 3805e9dbad6fSeschrock * so that we can size the column appropriately. 3806e9dbad6fSeschrock */ 3807e9dbad6fSeschrock int 3808990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) 3809e9dbad6fSeschrock { 3810e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3811990b4856Slling zprop_list_t *entry; 3812990b4856Slling zprop_list_t **last, **start; 3813e9dbad6fSeschrock nvlist_t *userprops, *propval; 3814e9dbad6fSeschrock nvpair_t *elem; 3815e9dbad6fSeschrock char *strval; 3816e9dbad6fSeschrock char buf[ZFS_MAXPROPLEN]; 3817e9dbad6fSeschrock 3818990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3819e9dbad6fSeschrock return (-1); 3820e9dbad6fSeschrock 3821e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3822e9dbad6fSeschrock 3823e9dbad6fSeschrock entry = *plp; 3824e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3825e9dbad6fSeschrock /* 3826e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3827e9dbad6fSeschrock * start by incrementing our list pointer to the first 3828e9dbad6fSeschrock * non-native property. 3829e9dbad6fSeschrock */ 3830e9dbad6fSeschrock start = plp; 3831e9dbad6fSeschrock while (*start != NULL) { 3832990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3833e9dbad6fSeschrock break; 3834e9dbad6fSeschrock start = &(*start)->pl_next; 3835e9dbad6fSeschrock } 3836e9dbad6fSeschrock 3837e9dbad6fSeschrock elem = NULL; 3838e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3839e9dbad6fSeschrock /* 3840e9dbad6fSeschrock * See if we've already found this property in our list. 3841e9dbad6fSeschrock */ 3842e9dbad6fSeschrock for (last = start; *last != NULL; 3843e9dbad6fSeschrock last = &(*last)->pl_next) { 3844e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3845e9dbad6fSeschrock nvpair_name(elem)) == 0) 3846e9dbad6fSeschrock break; 3847e9dbad6fSeschrock } 3848e9dbad6fSeschrock 3849e9dbad6fSeschrock if (*last == NULL) { 3850e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3851990b4856Slling sizeof (zprop_list_t))) == NULL || 3852e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3853e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3854e9dbad6fSeschrock free(entry); 3855e9dbad6fSeschrock return (-1); 3856e9dbad6fSeschrock } 3857e9dbad6fSeschrock 3858990b4856Slling entry->pl_prop = ZPROP_INVAL; 3859e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3860e9dbad6fSeschrock entry->pl_all = B_TRUE; 3861e9dbad6fSeschrock *last = entry; 3862e9dbad6fSeschrock } 3863e9dbad6fSeschrock } 3864e9dbad6fSeschrock } 3865e9dbad6fSeschrock 3866e9dbad6fSeschrock /* 3867e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3868e9dbad6fSeschrock */ 3869e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3870e9dbad6fSeschrock if (entry->pl_fixed) 3871e9dbad6fSeschrock continue; 3872e9dbad6fSeschrock 3873990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3874e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3875e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3876e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3877e9dbad6fSeschrock entry->pl_width = strlen(buf); 3878e9dbad6fSeschrock } 3879e9dbad6fSeschrock } else if (nvlist_lookup_nvlist(userprops, 3880e9dbad6fSeschrock entry->pl_user_prop, &propval) == 0) { 3881e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3882990b4856Slling ZPROP_VALUE, &strval) == 0); 3883e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3884e9dbad6fSeschrock entry->pl_width = strlen(strval); 3885e9dbad6fSeschrock } 3886e9dbad6fSeschrock } 3887e9dbad6fSeschrock 3888e9dbad6fSeschrock return (0); 3889e9dbad6fSeschrock } 3890ecd6cf80Smarks 3891ecd6cf80Smarks int 3892ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 3893ecd6cf80Smarks { 3894ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3895ecd6cf80Smarks nvlist_t *nvp; 3896ecd6cf80Smarks gid_t gid; 3897ecd6cf80Smarks uid_t uid; 3898ecd6cf80Smarks const gid_t *groups; 3899ecd6cf80Smarks int group_cnt; 3900ecd6cf80Smarks int error; 3901ecd6cf80Smarks 3902ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 3903ecd6cf80Smarks return (no_memory(hdl)); 3904ecd6cf80Smarks 3905ecd6cf80Smarks uid = ucred_geteuid(cred); 3906ecd6cf80Smarks gid = ucred_getegid(cred); 3907ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 3908ecd6cf80Smarks 3909ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 3910ecd6cf80Smarks return (1); 3911ecd6cf80Smarks 3912ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 3913ecd6cf80Smarks nvlist_free(nvp); 3914ecd6cf80Smarks return (1); 3915ecd6cf80Smarks } 3916ecd6cf80Smarks 3917ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 3918ecd6cf80Smarks nvlist_free(nvp); 3919ecd6cf80Smarks return (1); 3920ecd6cf80Smarks } 3921ecd6cf80Smarks 3922ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 3923ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 3924ecd6cf80Smarks nvlist_free(nvp); 3925ecd6cf80Smarks return (1); 3926ecd6cf80Smarks } 3927ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3928ecd6cf80Smarks 3929990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 3930ecd6cf80Smarks return (-1); 3931ecd6cf80Smarks 3932ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 3933ecd6cf80Smarks nvlist_free(nvp); 3934ecd6cf80Smarks return (error); 3935ecd6cf80Smarks } 3936ecd6cf80Smarks 3937ecd6cf80Smarks int 3938ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3939743a77edSAlan Wright char *resource, void *export, void *sharetab, 3940743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3941ecd6cf80Smarks { 3942ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3943ecd6cf80Smarks int error; 3944ecd6cf80Smarks 3945ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3946ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3947743a77edSAlan Wright if (resource) 3948743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3949ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3950ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3951da6c28aaSamw zc.zc_share.z_sharetype = operation; 3952ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3953ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3954ecd6cf80Smarks return (error); 3955ecd6cf80Smarks } 39562e5e9e19SSanjeev Bagewadi 39572e5e9e19SSanjeev Bagewadi void 39582e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 39592e5e9e19SSanjeev Bagewadi { 39602e5e9e19SSanjeev Bagewadi nvpair_t *curr; 39612e5e9e19SSanjeev Bagewadi 39622e5e9e19SSanjeev Bagewadi /* 39632e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 39642e5e9e19SSanjeev Bagewadi * properties. 39652e5e9e19SSanjeev Bagewadi */ 39662e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 39672e5e9e19SSanjeev Bagewadi 39682e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 39692e5e9e19SSanjeev Bagewadi 39702e5e9e19SSanjeev Bagewadi while (curr) { 39712e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 39722e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 39732e5e9e19SSanjeev Bagewadi 397414843421SMatthew Ahrens /* 397514843421SMatthew Ahrens * We leave user:props in the nvlist, so there will be 397614843421SMatthew Ahrens * some ZPROP_INVAL. To be extra safe, don't prune 397714843421SMatthew Ahrens * those. 397814843421SMatthew Ahrens */ 397914843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 39802e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 39812e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 39822e5e9e19SSanjeev Bagewadi curr = next; 39832e5e9e19SSanjeev Bagewadi } 39842e5e9e19SSanjeev Bagewadi } 3985743a77edSAlan Wright 3986743a77edSAlan Wright static int 3987743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3988743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3989743a77edSAlan Wright { 3990743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3991743a77edSAlan Wright nvlist_t *nvlist = NULL; 3992743a77edSAlan Wright int error; 3993743a77edSAlan Wright 3994743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3995743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3996743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3997743a77edSAlan Wright 3998743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3999743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 4000743a77edSAlan Wright (void) no_memory(hdl); 4001743a77edSAlan Wright return (NULL); 4002743a77edSAlan Wright } 4003743a77edSAlan Wright } 4004743a77edSAlan Wright 4005743a77edSAlan Wright switch (cmd) { 4006743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 4007743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 4008743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 4009743a77edSAlan Wright break; 4010743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 4011743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 4012743a77edSAlan Wright resource1) != 0) { 4013743a77edSAlan Wright (void) no_memory(hdl); 4014743a77edSAlan Wright return (-1); 4015743a77edSAlan Wright } 4016743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 4017743a77edSAlan Wright resource2) != 0) { 4018743a77edSAlan Wright (void) no_memory(hdl); 4019743a77edSAlan Wright return (-1); 4020743a77edSAlan Wright } 4021743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 4022743a77edSAlan Wright nvlist_free(nvlist); 4023743a77edSAlan Wright return (-1); 4024743a77edSAlan Wright } 4025743a77edSAlan Wright break; 4026743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 4027743a77edSAlan Wright break; 4028743a77edSAlan Wright default: 4029743a77edSAlan Wright return (-1); 4030743a77edSAlan Wright } 4031743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 4032743a77edSAlan Wright if (nvlist) 4033743a77edSAlan Wright nvlist_free(nvlist); 4034743a77edSAlan Wright return (error); 4035743a77edSAlan Wright } 4036743a77edSAlan Wright 4037743a77edSAlan Wright int 4038743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 4039743a77edSAlan Wright char *path, char *resource) 4040743a77edSAlan Wright { 4041743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 4042743a77edSAlan Wright resource, NULL)); 4043743a77edSAlan Wright } 4044743a77edSAlan Wright 4045743a77edSAlan Wright int 4046743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 4047743a77edSAlan Wright char *path, char *resource) 4048743a77edSAlan Wright { 4049743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 4050743a77edSAlan Wright resource, NULL)); 4051743a77edSAlan Wright } 4052743a77edSAlan Wright 4053743a77edSAlan Wright int 4054743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 4055743a77edSAlan Wright { 4056743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 4057743a77edSAlan Wright NULL, NULL)); 4058743a77edSAlan Wright } 4059743a77edSAlan Wright 4060743a77edSAlan Wright int 4061743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 4062743a77edSAlan Wright char *oldname, char *newname) 4063743a77edSAlan Wright { 4064743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 4065743a77edSAlan Wright oldname, newname)); 4066743a77edSAlan Wright } 406714843421SMatthew Ahrens 406814843421SMatthew Ahrens int 406914843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 407014843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 407114843421SMatthew Ahrens { 407214843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 407314843421SMatthew Ahrens int error; 407414843421SMatthew Ahrens zfs_useracct_t buf[100]; 407514843421SMatthew Ahrens 407614843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 407714843421SMatthew Ahrens 407814843421SMatthew Ahrens zc.zc_objset_type = type; 407914843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 408014843421SMatthew Ahrens 408114843421SMatthew Ahrens /* CONSTCOND */ 408214843421SMatthew Ahrens while (1) { 408314843421SMatthew Ahrens zfs_useracct_t *zua = buf; 408414843421SMatthew Ahrens 408514843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 408614843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 408714843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 408814843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 408914843421SMatthew Ahrens break; 409014843421SMatthew Ahrens 409114843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 40920aea4b19SMatthew Ahrens error = func(arg, zua->zu_domain, zua->zu_rid, 40930aea4b19SMatthew Ahrens zua->zu_space); 40940aea4b19SMatthew Ahrens if (error != 0) 40950aea4b19SMatthew Ahrens return (error); 409614843421SMatthew Ahrens zua++; 409714843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 409814843421SMatthew Ahrens } 409914843421SMatthew Ahrens } 410014843421SMatthew Ahrens 410114843421SMatthew Ahrens return (error); 410214843421SMatthew Ahrens } 4103