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 /* 23*818119b8SChris Kirby * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #include <ctype.h> 28fa9e4066Sahrens #include <errno.h> 29fa9e4066Sahrens #include <libintl.h> 30fa9e4066Sahrens #include <math.h> 31fa9e4066Sahrens #include <stdio.h> 32fa9e4066Sahrens #include <stdlib.h> 33fa9e4066Sahrens #include <strings.h> 34fa9e4066Sahrens #include <unistd.h> 353cb34c60Sahrens #include <stddef.h> 36fa9e4066Sahrens #include <zone.h> 3799653d4eSeschrock #include <fcntl.h> 38fa9e4066Sahrens #include <sys/mntent.h> 39b12a1c38Slling #include <sys/mount.h> 40ecd6cf80Smarks #include <priv.h> 41ecd6cf80Smarks #include <pwd.h> 42ecd6cf80Smarks #include <grp.h> 43ecd6cf80Smarks #include <stddef.h> 44ecd6cf80Smarks #include <ucred.h> 4514843421SMatthew Ahrens #include <idmap.h> 4614843421SMatthew Ahrens #include <aclutils.h> 473b12c289SMatthew Ahrens #include <directory.h> 48fa9e4066Sahrens 49fa9e4066Sahrens #include <sys/spa.h> 50e9dbad6fSeschrock #include <sys/zap.h> 51fa9e4066Sahrens #include <libzfs.h> 52fa9e4066Sahrens 53fa9e4066Sahrens #include "zfs_namecheck.h" 54fa9e4066Sahrens #include "zfs_prop.h" 55fa9e4066Sahrens #include "libzfs_impl.h" 56ecd6cf80Smarks #include "zfs_deleg.h" 57fa9e4066Sahrens 5814843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned, 5914843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 60cdf5b4caSmmusante 61fa9e4066Sahrens /* 62fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 63fa9e4066Sahrens * readable form. 64fa9e4066Sahrens */ 65fa9e4066Sahrens const char * 66fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 67fa9e4066Sahrens { 68fa9e4066Sahrens switch (type) { 69fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 70fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 71fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 72fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 73fa9e4066Sahrens case ZFS_TYPE_VOLUME: 74fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 75fa9e4066Sahrens } 76fa9e4066Sahrens 77fa9e4066Sahrens return (NULL); 78fa9e4066Sahrens } 79fa9e4066Sahrens 80fa9e4066Sahrens /* 81fa9e4066Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 82fa9e4066Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 83fa9e4066Sahrens * We guess what the type would have been based on the path and the mask of 84fa9e4066Sahrens * acceptable types. 85fa9e4066Sahrens */ 86fa9e4066Sahrens static const char * 87fa9e4066Sahrens path_to_str(const char *path, int types) 88fa9e4066Sahrens { 89fa9e4066Sahrens /* 90fa9e4066Sahrens * When given a single type, always report the exact type. 91fa9e4066Sahrens */ 92fa9e4066Sahrens if (types == ZFS_TYPE_SNAPSHOT) 93fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 94fa9e4066Sahrens if (types == ZFS_TYPE_FILESYSTEM) 95fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 96fa9e4066Sahrens if (types == ZFS_TYPE_VOLUME) 97fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 98fa9e4066Sahrens 99fa9e4066Sahrens /* 100fa9e4066Sahrens * The user is requesting more than one type of dataset. If this is the 101fa9e4066Sahrens * case, consult the path itself. If we're looking for a snapshot, and 102fa9e4066Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 103fa9e4066Sahrens * snapshot attribute and try again. 104fa9e4066Sahrens */ 105fa9e4066Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 106fa9e4066Sahrens if (strchr(path, '@') != NULL) 107fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 108fa9e4066Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 109fa9e4066Sahrens } 110fa9e4066Sahrens 111fa9e4066Sahrens /* 112fa9e4066Sahrens * The user has requested either filesystems or volumes. 113fa9e4066Sahrens * We have no way of knowing a priori what type this would be, so always 114fa9e4066Sahrens * report it as "filesystem" or "volume", our two primitive types. 115fa9e4066Sahrens */ 116fa9e4066Sahrens if (types & ZFS_TYPE_FILESYSTEM) 117fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 118fa9e4066Sahrens 119fa9e4066Sahrens assert(types & ZFS_TYPE_VOLUME); 120fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 121fa9e4066Sahrens } 122fa9e4066Sahrens 123fa9e4066Sahrens /* 124fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 12514843421SMatthew Ahrens * provide a more meaningful error message. We call zfs_error_aux() to 12614843421SMatthew Ahrens * explain exactly why the name was not valid. 127fa9e4066Sahrens */ 128fa9e4066Sahrens static int 129f18faf3fSek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 130f18faf3fSek110237 boolean_t modifying) 131fa9e4066Sahrens { 132fa9e4066Sahrens namecheck_err_t why; 133fa9e4066Sahrens char what; 134fa9e4066Sahrens 135fa9e4066Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 13699653d4eSeschrock if (hdl != NULL) { 137fa9e4066Sahrens switch (why) { 138b81d61a6Slling case NAME_ERR_TOOLONG: 13999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14099653d4eSeschrock "name is too long")); 141b81d61a6Slling break; 142b81d61a6Slling 143fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 14499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14599653d4eSeschrock "leading slash in name")); 146fa9e4066Sahrens break; 147fa9e4066Sahrens 148fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 14999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15099653d4eSeschrock "empty component in name")); 151fa9e4066Sahrens break; 152fa9e4066Sahrens 153fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 15499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15599653d4eSeschrock "trailing slash in name")); 156fa9e4066Sahrens break; 157fa9e4066Sahrens 158fa9e4066Sahrens case NAME_ERR_INVALCHAR: 15999653d4eSeschrock zfs_error_aux(hdl, 160fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 16199653d4eSeschrock "'%c' in name"), what); 162fa9e4066Sahrens break; 163fa9e4066Sahrens 164fa9e4066Sahrens case NAME_ERR_MULTIPLE_AT: 16599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 16699653d4eSeschrock "multiple '@' delimiters in name")); 167fa9e4066Sahrens break; 1685ad82045Snd150628 1695ad82045Snd150628 case NAME_ERR_NOLETTER: 1705ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1715ad82045Snd150628 "pool doesn't begin with a letter")); 1725ad82045Snd150628 break; 1735ad82045Snd150628 1745ad82045Snd150628 case NAME_ERR_RESERVED: 1755ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1765ad82045Snd150628 "name is reserved")); 1775ad82045Snd150628 break; 1785ad82045Snd150628 1795ad82045Snd150628 case NAME_ERR_DISKLIKE: 1805ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1815ad82045Snd150628 "reserved disk name")); 1825ad82045Snd150628 break; 183fa9e4066Sahrens } 184fa9e4066Sahrens } 185fa9e4066Sahrens 186fa9e4066Sahrens return (0); 187fa9e4066Sahrens } 188fa9e4066Sahrens 189fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 19099653d4eSeschrock if (hdl != NULL) 19199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19299653d4eSeschrock "snapshot delimiter '@' in filesystem name")); 193fa9e4066Sahrens return (0); 194fa9e4066Sahrens } 195fa9e4066Sahrens 1961d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 1971d452cf5Sahrens if (hdl != NULL) 1981d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 199d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 2001d452cf5Sahrens return (0); 2011d452cf5Sahrens } 2021d452cf5Sahrens 203f18faf3fSek110237 if (modifying && strchr(path, '%') != NULL) { 204f18faf3fSek110237 if (hdl != NULL) 205f18faf3fSek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 206f18faf3fSek110237 "invalid character %c in name"), '%'); 207f18faf3fSek110237 return (0); 208f18faf3fSek110237 } 209f18faf3fSek110237 21099653d4eSeschrock return (-1); 211fa9e4066Sahrens } 212fa9e4066Sahrens 213fa9e4066Sahrens int 214fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 215fa9e4066Sahrens { 216e7cbe64fSgw25295 if (type == ZFS_TYPE_POOL) 217e7cbe64fSgw25295 return (zpool_name_valid(NULL, B_FALSE, name)); 218f18faf3fSek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 219fa9e4066Sahrens } 220fa9e4066Sahrens 221fa9e4066Sahrens /* 222e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 223e9dbad6fSeschrock * properties into a separate nvlist. 224e9dbad6fSeschrock */ 225fac3008cSeschrock static nvlist_t * 226fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 227e9dbad6fSeschrock { 228e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 229e9dbad6fSeschrock nvpair_t *elem; 230e9dbad6fSeschrock nvlist_t *propval; 231fac3008cSeschrock nvlist_t *nvl; 232e9dbad6fSeschrock 233fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 234fac3008cSeschrock (void) no_memory(hdl); 235fac3008cSeschrock return (NULL); 236fac3008cSeschrock } 237e9dbad6fSeschrock 238e9dbad6fSeschrock elem = NULL; 239fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 240e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 241e9dbad6fSeschrock continue; 242e9dbad6fSeschrock 243e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 244fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 245fac3008cSeschrock nvlist_free(nvl); 246fac3008cSeschrock (void) no_memory(hdl); 247fac3008cSeschrock return (NULL); 248fac3008cSeschrock } 249e9dbad6fSeschrock } 250e9dbad6fSeschrock 251fac3008cSeschrock return (nvl); 252e9dbad6fSeschrock } 253e9dbad6fSeschrock 25429ab75c9Srm160521 static zpool_handle_t * 25529ab75c9Srm160521 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 25629ab75c9Srm160521 { 25729ab75c9Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 25829ab75c9Srm160521 zpool_handle_t *zph; 25929ab75c9Srm160521 26029ab75c9Srm160521 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 26129ab75c9Srm160521 if (hdl->libzfs_pool_handles != NULL) 26229ab75c9Srm160521 zph->zpool_next = hdl->libzfs_pool_handles; 26329ab75c9Srm160521 hdl->libzfs_pool_handles = zph; 26429ab75c9Srm160521 } 26529ab75c9Srm160521 return (zph); 26629ab75c9Srm160521 } 26729ab75c9Srm160521 26829ab75c9Srm160521 static zpool_handle_t * 26929ab75c9Srm160521 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 27029ab75c9Srm160521 { 27129ab75c9Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 27229ab75c9Srm160521 zpool_handle_t *zph = hdl->libzfs_pool_handles; 27329ab75c9Srm160521 27429ab75c9Srm160521 while ((zph != NULL) && 27529ab75c9Srm160521 (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 27629ab75c9Srm160521 zph = zph->zpool_next; 27729ab75c9Srm160521 return (zph); 27829ab75c9Srm160521 } 27929ab75c9Srm160521 28029ab75c9Srm160521 /* 28129ab75c9Srm160521 * Returns a handle to the pool that contains the provided dataset. 28229ab75c9Srm160521 * If a handle to that pool already exists then that handle is returned. 28329ab75c9Srm160521 * Otherwise, a new handle is created and added to the list of handles. 28429ab75c9Srm160521 */ 28529ab75c9Srm160521 static zpool_handle_t * 28629ab75c9Srm160521 zpool_handle(zfs_handle_t *zhp) 28729ab75c9Srm160521 { 28829ab75c9Srm160521 char *pool_name; 28929ab75c9Srm160521 int len; 29029ab75c9Srm160521 zpool_handle_t *zph; 29129ab75c9Srm160521 29229ab75c9Srm160521 len = strcspn(zhp->zfs_name, "/@") + 1; 29329ab75c9Srm160521 pool_name = zfs_alloc(zhp->zfs_hdl, len); 29429ab75c9Srm160521 (void) strlcpy(pool_name, zhp->zfs_name, len); 29529ab75c9Srm160521 29629ab75c9Srm160521 zph = zpool_find_handle(zhp, pool_name, len); 29729ab75c9Srm160521 if (zph == NULL) 29829ab75c9Srm160521 zph = zpool_add_handle(zhp, pool_name); 29929ab75c9Srm160521 30029ab75c9Srm160521 free(pool_name); 30129ab75c9Srm160521 return (zph); 30229ab75c9Srm160521 } 30329ab75c9Srm160521 30429ab75c9Srm160521 void 30529ab75c9Srm160521 zpool_free_handles(libzfs_handle_t *hdl) 30629ab75c9Srm160521 { 30729ab75c9Srm160521 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 30829ab75c9Srm160521 30929ab75c9Srm160521 while (zph != NULL) { 31029ab75c9Srm160521 next = zph->zpool_next; 31129ab75c9Srm160521 zpool_close(zph); 31229ab75c9Srm160521 zph = next; 31329ab75c9Srm160521 } 31429ab75c9Srm160521 hdl->libzfs_pool_handles = NULL; 31529ab75c9Srm160521 } 31629ab75c9Srm160521 317e9dbad6fSeschrock /* 318fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 319fa9e4066Sahrens */ 320fa9e4066Sahrens static int 321ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 322fa9e4066Sahrens { 323e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 324fa9e4066Sahrens 325ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 326fa9e4066Sahrens 327ebedde84SEric Taylor while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3287f7322feSeschrock if (errno == ENOMEM) { 329ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 33099653d4eSeschrock return (-1); 331e9dbad6fSeschrock } 3327f7322feSeschrock } else { 333fa9e4066Sahrens return (-1); 3347f7322feSeschrock } 3357f7322feSeschrock } 336ebedde84SEric Taylor return (0); 337fac3008cSeschrock } 338fac3008cSeschrock 33992241e0bSTom Erickson /* 34092241e0bSTom Erickson * Utility function to get the received properties of the given object. 34192241e0bSTom Erickson */ 34292241e0bSTom Erickson static int 34392241e0bSTom Erickson get_recvd_props_ioctl(zfs_handle_t *zhp) 34492241e0bSTom Erickson { 34592241e0bSTom Erickson libzfs_handle_t *hdl = zhp->zfs_hdl; 34692241e0bSTom Erickson nvlist_t *recvdprops; 34792241e0bSTom Erickson zfs_cmd_t zc = { 0 }; 34892241e0bSTom Erickson int err; 34992241e0bSTom Erickson 35092241e0bSTom Erickson if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 35192241e0bSTom Erickson return (-1); 35292241e0bSTom Erickson 35392241e0bSTom Erickson (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35492241e0bSTom Erickson 35592241e0bSTom Erickson while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 35692241e0bSTom Erickson if (errno == ENOMEM) { 35792241e0bSTom Erickson if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 35892241e0bSTom Erickson return (-1); 35992241e0bSTom Erickson } 36092241e0bSTom Erickson } else { 36192241e0bSTom Erickson zcmd_free_nvlists(&zc); 36292241e0bSTom Erickson return (-1); 36392241e0bSTom Erickson } 36492241e0bSTom Erickson } 36592241e0bSTom Erickson 36692241e0bSTom Erickson err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 36792241e0bSTom Erickson zcmd_free_nvlists(&zc); 36892241e0bSTom Erickson if (err != 0) 36992241e0bSTom Erickson return (-1); 37092241e0bSTom Erickson 37192241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 37292241e0bSTom Erickson zhp->zfs_recvd_props = recvdprops; 37392241e0bSTom Erickson 37492241e0bSTom Erickson return (0); 37592241e0bSTom Erickson } 37692241e0bSTom Erickson 377ebedde84SEric Taylor static int 378ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 379ebedde84SEric Taylor { 380ebedde84SEric Taylor nvlist_t *allprops, *userprops; 381ebedde84SEric Taylor 382ebedde84SEric Taylor zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 383ebedde84SEric Taylor 384ebedde84SEric Taylor if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 385ebedde84SEric Taylor return (-1); 386ebedde84SEric Taylor } 387fac3008cSeschrock 38814843421SMatthew Ahrens /* 38914843421SMatthew Ahrens * XXX Why do we store the user props separately, in addition to 39014843421SMatthew Ahrens * storing them in zfs_props? 39114843421SMatthew Ahrens */ 392fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 393fac3008cSeschrock nvlist_free(allprops); 394fac3008cSeschrock return (-1); 395fac3008cSeschrock } 396fac3008cSeschrock 39799653d4eSeschrock nvlist_free(zhp->zfs_props); 398fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 39999653d4eSeschrock 400fac3008cSeschrock zhp->zfs_props = allprops; 401fac3008cSeschrock zhp->zfs_user_props = userprops; 40299653d4eSeschrock 403fa9e4066Sahrens return (0); 404fa9e4066Sahrens } 405fa9e4066Sahrens 406ebedde84SEric Taylor static int 407ebedde84SEric Taylor get_stats(zfs_handle_t *zhp) 408ebedde84SEric Taylor { 409ebedde84SEric Taylor int rc = 0; 410ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 411ebedde84SEric Taylor 412ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 413ebedde84SEric Taylor return (-1); 414ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) != 0) 415ebedde84SEric Taylor rc = -1; 416ebedde84SEric Taylor else if (put_stats_zhdl(zhp, &zc) != 0) 417ebedde84SEric Taylor rc = -1; 418ebedde84SEric Taylor zcmd_free_nvlists(&zc); 419ebedde84SEric Taylor return (rc); 420ebedde84SEric Taylor } 421ebedde84SEric Taylor 422fa9e4066Sahrens /* 423fa9e4066Sahrens * Refresh the properties currently stored in the handle. 424fa9e4066Sahrens */ 425fa9e4066Sahrens void 426fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 427fa9e4066Sahrens { 428fa9e4066Sahrens (void) get_stats(zhp); 429fa9e4066Sahrens } 430fa9e4066Sahrens 431fa9e4066Sahrens /* 432fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 433fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 434fa9e4066Sahrens */ 435ebedde84SEric Taylor static int 436ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 437fa9e4066Sahrens { 438503ad85cSMatthew Ahrens if (put_stats_zhdl(zhp, zc) != 0) 439ebedde84SEric Taylor return (-1); 44031fd60d3Sahrens 441fa9e4066Sahrens /* 442fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 443fa9e4066Sahrens * the high-level type. 444fa9e4066Sahrens */ 445a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 446a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 447a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 448a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 449a2eea2e1Sahrens else 450a2eea2e1Sahrens abort(); 451a2eea2e1Sahrens 452fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 453fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 454fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 455fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 456fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 457fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 458fa9e4066Sahrens else 45999653d4eSeschrock abort(); /* we should never see any other types */ 460fa9e4066Sahrens 46129ab75c9Srm160521 zhp->zpool_hdl = zpool_handle(zhp); 462ebedde84SEric Taylor return (0); 463ebedde84SEric Taylor } 464ebedde84SEric Taylor 465ebedde84SEric Taylor zfs_handle_t * 466ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 467ebedde84SEric Taylor { 468ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 469ebedde84SEric Taylor 470ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 471ebedde84SEric Taylor 472ebedde84SEric Taylor if (zhp == NULL) 473ebedde84SEric Taylor return (NULL); 474ebedde84SEric Taylor 475ebedde84SEric Taylor zhp->zfs_hdl = hdl; 476ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 477ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 478ebedde84SEric Taylor free(zhp); 479ebedde84SEric Taylor return (NULL); 480ebedde84SEric Taylor } 481ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 482ebedde84SEric Taylor zcmd_free_nvlists(&zc); 483ebedde84SEric Taylor free(zhp); 484ebedde84SEric Taylor return (NULL); 485ebedde84SEric Taylor } 486ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 487ebedde84SEric Taylor free(zhp); 488ebedde84SEric Taylor zhp = NULL; 489ebedde84SEric Taylor } 490ebedde84SEric Taylor zcmd_free_nvlists(&zc); 491ebedde84SEric Taylor return (zhp); 492ebedde84SEric Taylor } 493ebedde84SEric Taylor 494ebedde84SEric Taylor static zfs_handle_t * 495ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 496ebedde84SEric Taylor { 497ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 498ebedde84SEric Taylor 499ebedde84SEric Taylor if (zhp == NULL) 500ebedde84SEric Taylor return (NULL); 501ebedde84SEric Taylor 502ebedde84SEric Taylor zhp->zfs_hdl = hdl; 503ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 504ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 505ebedde84SEric Taylor free(zhp); 506ebedde84SEric Taylor return (NULL); 507ebedde84SEric Taylor } 508fa9e4066Sahrens return (zhp); 509fa9e4066Sahrens } 510fa9e4066Sahrens 511fa9e4066Sahrens /* 512fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 513fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 514fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 515fa9e4066Sahrens */ 516fa9e4066Sahrens zfs_handle_t * 51799653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 518fa9e4066Sahrens { 519fa9e4066Sahrens zfs_handle_t *zhp; 52099653d4eSeschrock char errbuf[1024]; 52199653d4eSeschrock 52299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 52399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 524fa9e4066Sahrens 525fa9e4066Sahrens /* 52699653d4eSeschrock * Validate the name before we even try to open it. 527fa9e4066Sahrens */ 528f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 52999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 53099653d4eSeschrock "invalid dataset name")); 53199653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 532fa9e4066Sahrens return (NULL); 533fa9e4066Sahrens } 534fa9e4066Sahrens 535fa9e4066Sahrens /* 536fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 537fa9e4066Sahrens */ 538fa9e4066Sahrens errno = 0; 53999653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 540ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 541fa9e4066Sahrens return (NULL); 542fa9e4066Sahrens } 543fa9e4066Sahrens 544fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 54599653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 54694de1d4cSeschrock zfs_close(zhp); 547fa9e4066Sahrens return (NULL); 548fa9e4066Sahrens } 549fa9e4066Sahrens 550fa9e4066Sahrens return (zhp); 551fa9e4066Sahrens } 552fa9e4066Sahrens 553fa9e4066Sahrens /* 554fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 555fa9e4066Sahrens */ 556fa9e4066Sahrens void 557fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 558fa9e4066Sahrens { 559fa9e4066Sahrens if (zhp->zfs_mntopts) 560fa9e4066Sahrens free(zhp->zfs_mntopts); 56199653d4eSeschrock nvlist_free(zhp->zfs_props); 562e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 56392241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 564fa9e4066Sahrens free(zhp); 565fa9e4066Sahrens } 566fa9e4066Sahrens 567ebedde84SEric Taylor typedef struct mnttab_node { 568ebedde84SEric Taylor struct mnttab mtn_mt; 569ebedde84SEric Taylor avl_node_t mtn_node; 570ebedde84SEric Taylor } mnttab_node_t; 571ebedde84SEric Taylor 572ebedde84SEric Taylor static int 573ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 574ebedde84SEric Taylor { 575ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 576ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 577ebedde84SEric Taylor int rv; 578ebedde84SEric Taylor 579ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 580ebedde84SEric Taylor 581ebedde84SEric Taylor if (rv == 0) 582ebedde84SEric Taylor return (0); 583ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 584ebedde84SEric Taylor } 585ebedde84SEric Taylor 586ebedde84SEric Taylor void 587ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 588ebedde84SEric Taylor { 589ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 590ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 591ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 592b2634b9cSEric Taylor } 593b2634b9cSEric Taylor 594b2634b9cSEric Taylor void 595b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 596b2634b9cSEric Taylor { 597b2634b9cSEric Taylor struct mnttab entry; 598ebedde84SEric Taylor 599ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 600ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 601ebedde84SEric Taylor mnttab_node_t *mtn; 602ebedde84SEric Taylor 603ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 604ebedde84SEric Taylor continue; 605ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 606ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 607ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 608ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 609ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 610ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 611ebedde84SEric Taylor } 612ebedde84SEric Taylor } 613ebedde84SEric Taylor 614ebedde84SEric Taylor void 615ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 616ebedde84SEric Taylor { 617ebedde84SEric Taylor void *cookie = NULL; 618ebedde84SEric Taylor mnttab_node_t *mtn; 619ebedde84SEric Taylor 620ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 621ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 622ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 623ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 624ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 625ebedde84SEric Taylor free(mtn); 626ebedde84SEric Taylor } 627ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 628ebedde84SEric Taylor } 629ebedde84SEric Taylor 630b2634b9cSEric Taylor void 631b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 632b2634b9cSEric Taylor { 633b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 634b2634b9cSEric Taylor } 635b2634b9cSEric Taylor 636ebedde84SEric Taylor int 637ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 638ebedde84SEric Taylor struct mnttab *entry) 639ebedde84SEric Taylor { 640ebedde84SEric Taylor mnttab_node_t find; 641ebedde84SEric Taylor mnttab_node_t *mtn; 642ebedde84SEric Taylor 643b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 644b2634b9cSEric Taylor struct mnttab srch = { 0 }; 645b2634b9cSEric Taylor 646b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 647b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 648b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 649b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 650b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 651b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 652b2634b9cSEric Taylor return (0); 653b2634b9cSEric Taylor else 654b2634b9cSEric Taylor return (ENOENT); 655b2634b9cSEric Taylor } 656b2634b9cSEric Taylor 657ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 658b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 659ebedde84SEric Taylor 660ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 661ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 662ebedde84SEric Taylor if (mtn) { 663ebedde84SEric Taylor *entry = mtn->mtn_mt; 664ebedde84SEric Taylor return (0); 665ebedde84SEric Taylor } 666ebedde84SEric Taylor return (ENOENT); 667ebedde84SEric Taylor } 668ebedde84SEric Taylor 669ebedde84SEric Taylor void 670ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 671ebedde84SEric Taylor const char *mountp, const char *mntopts) 672ebedde84SEric Taylor { 673ebedde84SEric Taylor mnttab_node_t *mtn; 674ebedde84SEric Taylor 675ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 676ebedde84SEric Taylor return; 677ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 678ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 679ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 680ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 681ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 682ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 683ebedde84SEric Taylor } 684ebedde84SEric Taylor 685ebedde84SEric Taylor void 686ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 687ebedde84SEric Taylor { 688ebedde84SEric Taylor mnttab_node_t find; 689ebedde84SEric Taylor mnttab_node_t *ret; 690ebedde84SEric Taylor 691ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 692ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 693ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 694ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 695ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 696ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 697ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 698ebedde84SEric Taylor free(ret); 699ebedde84SEric Taylor } 700ebedde84SEric Taylor } 701ebedde84SEric Taylor 7027b97dc1aSrm160521 int 7037b97dc1aSrm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7047b97dc1aSrm160521 { 70529ab75c9Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7067b97dc1aSrm160521 7077b97dc1aSrm160521 if (zpool_handle == NULL) 7087b97dc1aSrm160521 return (-1); 7097b97dc1aSrm160521 7107b97dc1aSrm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7117b97dc1aSrm160521 ZPOOL_PROP_VERSION, NULL); 7127b97dc1aSrm160521 return (0); 7137b97dc1aSrm160521 } 7147b97dc1aSrm160521 7157b97dc1aSrm160521 /* 7167b97dc1aSrm160521 * The choice of reservation property depends on the SPA version. 7177b97dc1aSrm160521 */ 7187b97dc1aSrm160521 static int 7197b97dc1aSrm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7207b97dc1aSrm160521 { 7217b97dc1aSrm160521 int spa_version; 7227b97dc1aSrm160521 7237b97dc1aSrm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7247b97dc1aSrm160521 return (-1); 7257b97dc1aSrm160521 7267b97dc1aSrm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7277b97dc1aSrm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7287b97dc1aSrm160521 else 7297b97dc1aSrm160521 *resv_prop = ZFS_PROP_RESERVATION; 7307b97dc1aSrm160521 7317b97dc1aSrm160521 return (0); 7327b97dc1aSrm160521 } 7337b97dc1aSrm160521 734b1b8ab34Slling /* 735e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 736e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 737e9dbad6fSeschrock * strings. 738fa9e4066Sahrens */ 7390a48a24eStimh nvlist_t * 7400a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 741990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 742fa9e4066Sahrens { 743e9dbad6fSeschrock nvpair_t *elem; 744e9dbad6fSeschrock uint64_t intval; 745e9dbad6fSeschrock char *strval; 746990b4856Slling zfs_prop_t prop; 747e9dbad6fSeschrock nvlist_t *ret; 748da6c28aaSamw int chosen_normal = -1; 749da6c28aaSamw int chosen_utf = -1; 750990b4856Slling 751e9dbad6fSeschrock if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 752e9dbad6fSeschrock (void) no_memory(hdl); 753e9dbad6fSeschrock return (NULL); 754e9dbad6fSeschrock } 755fa9e4066Sahrens 75614843421SMatthew Ahrens /* 75714843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 75814843421SMatthew Ahrens */ 75914843421SMatthew Ahrens 760e9dbad6fSeschrock elem = NULL; 761e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 762990b4856Slling const char *propname = nvpair_name(elem); 76399653d4eSeschrock 76414843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 76514843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 766fa9e4066Sahrens /* 76714843421SMatthew Ahrens * This is a user property: make sure it's a 768990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 769990b4856Slling */ 770990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 771990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 772990b4856Slling "'%s' must be a string"), propname); 773990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 774990b4856Slling goto error; 775990b4856Slling } 776990b4856Slling 777990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 778e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 779e9dbad6fSeschrock "property name '%s' is too long"), 780e9dbad6fSeschrock propname); 781990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 782e9dbad6fSeschrock goto error; 783e9dbad6fSeschrock } 784e9dbad6fSeschrock 785e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 786e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 787e9dbad6fSeschrock (void) no_memory(hdl); 788e9dbad6fSeschrock goto error; 789e9dbad6fSeschrock } 790e9dbad6fSeschrock continue; 791e9dbad6fSeschrock } 792fa9e4066Sahrens 79314843421SMatthew Ahrens /* 79414843421SMatthew Ahrens * Currently, only user properties can be modified on 79514843421SMatthew Ahrens * snapshots. 79614843421SMatthew Ahrens */ 797bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 798bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 799bb0ade09Sahrens "this property can not be modified for snapshots")); 800bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 801bb0ade09Sahrens goto error; 802bb0ade09Sahrens } 803bb0ade09Sahrens 80414843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 80514843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 80614843421SMatthew Ahrens char newpropname[128]; 80714843421SMatthew Ahrens char domain[128]; 80814843421SMatthew Ahrens uint64_t rid; 80914843421SMatthew Ahrens uint64_t valary[3]; 81014843421SMatthew Ahrens 81114843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 81214843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 81314843421SMatthew Ahrens zfs_error_aux(hdl, 81414843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 81514843421SMatthew Ahrens "'%s' has an invalid user/group name"), 81614843421SMatthew Ahrens propname); 81714843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 81814843421SMatthew Ahrens goto error; 81914843421SMatthew Ahrens } 82014843421SMatthew Ahrens 82114843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 82214843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 82314843421SMatthew Ahrens zfs_error_aux(hdl, 82414843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 82514843421SMatthew Ahrens propname); 82614843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 82714843421SMatthew Ahrens errbuf); 82814843421SMatthew Ahrens goto error; 82914843421SMatthew Ahrens } 83014843421SMatthew Ahrens 83114843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 83214843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 83314843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 83414843421SMatthew Ahrens intval = 0; 83514843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 83614843421SMatthew Ahrens strval, &intval) != 0) { 83714843421SMatthew Ahrens (void) zfs_error(hdl, 83814843421SMatthew Ahrens EZFS_BADPROP, errbuf); 83914843421SMatthew Ahrens goto error; 84014843421SMatthew Ahrens } 84114843421SMatthew Ahrens } else if (nvpair_type(elem) == 84214843421SMatthew Ahrens DATA_TYPE_UINT64) { 84314843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 84414843421SMatthew Ahrens if (intval == 0) { 84514843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 84614843421SMatthew Ahrens "use 'none' to disable " 84714843421SMatthew Ahrens "userquota/groupquota")); 84814843421SMatthew Ahrens goto error; 84914843421SMatthew Ahrens } 85014843421SMatthew Ahrens } else { 85114843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 85214843421SMatthew Ahrens "'%s' must be a number"), propname); 85314843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 85414843421SMatthew Ahrens goto error; 85514843421SMatthew Ahrens } 85614843421SMatthew Ahrens 8572d5843dbSMatthew Ahrens /* 8582d5843dbSMatthew Ahrens * Encode the prop name as 8592d5843dbSMatthew Ahrens * userquota@<hex-rid>-domain, to make it easy 8602d5843dbSMatthew Ahrens * for the kernel to decode. 8612d5843dbSMatthew Ahrens */ 86214843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 8632d5843dbSMatthew Ahrens "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 8642d5843dbSMatthew Ahrens (longlong_t)rid, domain); 86514843421SMatthew Ahrens valary[0] = uqtype; 86614843421SMatthew Ahrens valary[1] = rid; 86714843421SMatthew Ahrens valary[2] = intval; 86814843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 86914843421SMatthew Ahrens valary, 3) != 0) { 87014843421SMatthew Ahrens (void) no_memory(hdl); 87114843421SMatthew Ahrens goto error; 87214843421SMatthew Ahrens } 87314843421SMatthew Ahrens continue; 87414843421SMatthew Ahrens } 87514843421SMatthew Ahrens 87614843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 87714843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 87814843421SMatthew Ahrens "invalid property '%s'"), propname); 87914843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 88014843421SMatthew Ahrens goto error; 88114843421SMatthew Ahrens } 88214843421SMatthew Ahrens 883e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 884e9dbad6fSeschrock zfs_error_aux(hdl, 885e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 886e9dbad6fSeschrock "apply to datasets of this type"), propname); 887e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 888e9dbad6fSeschrock goto error; 889e9dbad6fSeschrock } 890e9dbad6fSeschrock 891e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 892da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 893e9dbad6fSeschrock zfs_error_aux(hdl, 894e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 895e9dbad6fSeschrock propname); 896e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 897e9dbad6fSeschrock goto error; 898e9dbad6fSeschrock } 899e9dbad6fSeschrock 900990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 901990b4856Slling &strval, &intval, errbuf) != 0) 902e9dbad6fSeschrock goto error; 903e9dbad6fSeschrock 904e9dbad6fSeschrock /* 905e9dbad6fSeschrock * Perform some additional checks for specific properties. 906e9dbad6fSeschrock */ 907e9dbad6fSeschrock switch (prop) { 908e7437265Sahrens case ZFS_PROP_VERSION: 909e7437265Sahrens { 910e7437265Sahrens int version; 911e7437265Sahrens 912e7437265Sahrens if (zhp == NULL) 913e7437265Sahrens break; 914e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 915e7437265Sahrens if (intval < version) { 916e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 917e7437265Sahrens "Can not downgrade; already at version %u"), 918e7437265Sahrens version); 919e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 920e7437265Sahrens goto error; 921e7437265Sahrens } 922e7437265Sahrens break; 923e7437265Sahrens } 924e7437265Sahrens 925e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 926e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 927e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 928e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 929e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 930e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 931e9dbad6fSeschrock "'%s' must be power of 2 from %u " 932e9dbad6fSeschrock "to %uk"), propname, 933e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 934e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 935e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 936e9dbad6fSeschrock goto error; 937e9dbad6fSeschrock } 938e9dbad6fSeschrock break; 939e9dbad6fSeschrock 940f3861e1aSahl case ZFS_PROP_SHAREISCSI: 941f3861e1aSahl if (strcmp(strval, "off") != 0 && 942f3861e1aSahl strcmp(strval, "on") != 0 && 943f3861e1aSahl strcmp(strval, "type=disk") != 0) { 944f3861e1aSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 945f3861e1aSahl "'%s' must be 'on', 'off', or 'type=disk'"), 946f3861e1aSahl propname); 947f3861e1aSahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 948f3861e1aSahl goto error; 949f3861e1aSahl } 950f3861e1aSahl 951f3861e1aSahl break; 952f3861e1aSahl 9534201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 9544201a95eSRic Aleshire { 9554201a95eSRic Aleshire /* 9564201a95eSRic Aleshire * Verify the mlslabel string and convert to 9574201a95eSRic Aleshire * internal hex label string. 9584201a95eSRic Aleshire */ 9594201a95eSRic Aleshire 9604201a95eSRic Aleshire m_label_t *new_sl; 9614201a95eSRic Aleshire char *hex = NULL; /* internal label string */ 9624201a95eSRic Aleshire 9634201a95eSRic Aleshire /* Default value is already OK. */ 9644201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 9654201a95eSRic Aleshire break; 9664201a95eSRic Aleshire 9674201a95eSRic Aleshire /* Verify the label can be converted to binary form */ 9684201a95eSRic Aleshire if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 9694201a95eSRic Aleshire (str_to_label(strval, &new_sl, MAC_LABEL, 9704201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1)) { 9714201a95eSRic Aleshire goto badlabel; 9724201a95eSRic Aleshire } 9734201a95eSRic Aleshire 9744201a95eSRic Aleshire /* Now translate to hex internal label string */ 9754201a95eSRic Aleshire if (label_to_str(new_sl, &hex, M_INTERNAL, 9764201a95eSRic Aleshire DEF_NAMES) != 0) { 9774201a95eSRic Aleshire if (hex) 9784201a95eSRic Aleshire free(hex); 9794201a95eSRic Aleshire goto badlabel; 9804201a95eSRic Aleshire } 9814201a95eSRic Aleshire m_label_free(new_sl); 9824201a95eSRic Aleshire 9834201a95eSRic Aleshire /* If string is already in internal form, we're done. */ 9844201a95eSRic Aleshire if (strcmp(strval, hex) == 0) { 9854201a95eSRic Aleshire free(hex); 9864201a95eSRic Aleshire break; 9874201a95eSRic Aleshire } 9884201a95eSRic Aleshire 9894201a95eSRic Aleshire /* Replace the label string with the internal form. */ 990569038c9SRic Aleshire (void) nvlist_remove(ret, zfs_prop_to_name(prop), 9914201a95eSRic Aleshire DATA_TYPE_STRING); 9924201a95eSRic Aleshire verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 9934201a95eSRic Aleshire hex) == 0); 9944201a95eSRic Aleshire free(hex); 9954201a95eSRic Aleshire 9964201a95eSRic Aleshire break; 9974201a95eSRic Aleshire 9984201a95eSRic Aleshire badlabel: 9994201a95eSRic Aleshire zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10004201a95eSRic Aleshire "invalid mlslabel '%s'"), strval); 10014201a95eSRic Aleshire (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10024201a95eSRic Aleshire m_label_free(new_sl); /* OK if null */ 10034201a95eSRic Aleshire goto error; 10044201a95eSRic Aleshire 10054201a95eSRic Aleshire } 10064201a95eSRic Aleshire 1007e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 100889eef05eSrm160521 { 100989eef05eSrm160521 namecheck_err_t why; 101089eef05eSrm160521 1011e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1012e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1013e9dbad6fSeschrock break; 1014e9dbad6fSeschrock 101589eef05eSrm160521 if (mountpoint_namecheck(strval, &why)) { 101689eef05eSrm160521 switch (why) { 101789eef05eSrm160521 case NAME_ERR_LEADING_SLASH: 101889eef05eSrm160521 zfs_error_aux(hdl, 101989eef05eSrm160521 dgettext(TEXT_DOMAIN, 1020e9dbad6fSeschrock "'%s' must be an absolute path, " 1021e9dbad6fSeschrock "'none', or 'legacy'"), propname); 102289eef05eSrm160521 break; 102389eef05eSrm160521 case NAME_ERR_TOOLONG: 102489eef05eSrm160521 zfs_error_aux(hdl, 102589eef05eSrm160521 dgettext(TEXT_DOMAIN, 102689eef05eSrm160521 "component of '%s' is too long"), 102789eef05eSrm160521 propname); 102889eef05eSrm160521 break; 102989eef05eSrm160521 } 1030e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1031e9dbad6fSeschrock goto error; 1032e9dbad6fSeschrock } 103389eef05eSrm160521 } 103489eef05eSrm160521 1035f3861e1aSahl /*FALLTHRU*/ 1036e9dbad6fSeschrock 1037da6c28aaSamw case ZFS_PROP_SHARESMB: 1038f3861e1aSahl case ZFS_PROP_SHARENFS: 1039e9dbad6fSeschrock /* 1040da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1041da6c28aaSamw * properties, check if it can be set in a 1042da6c28aaSamw * global/non-global zone based on 1043f3861e1aSahl * the zoned property value: 1044fa9e4066Sahrens * 1045fa9e4066Sahrens * global zone non-global zone 1046f3861e1aSahl * -------------------------------------------------- 1047fa9e4066Sahrens * zoned=on mountpoint (no) mountpoint (yes) 1048fa9e4066Sahrens * sharenfs (no) sharenfs (no) 1049da6c28aaSamw * sharesmb (no) sharesmb (no) 1050fa9e4066Sahrens * 1051fa9e4066Sahrens * zoned=off mountpoint (yes) N/A 1052fa9e4066Sahrens * sharenfs (yes) 1053da6c28aaSamw * sharesmb (yes) 1054fa9e4066Sahrens */ 1055e9dbad6fSeschrock if (zoned) { 1056fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID) { 105799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1058e9dbad6fSeschrock "'%s' cannot be set on " 1059e9dbad6fSeschrock "dataset in a non-global zone"), 1060e9dbad6fSeschrock propname); 1061e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1062e9dbad6fSeschrock errbuf); 1063e9dbad6fSeschrock goto error; 1064da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1065da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 106699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1067e9dbad6fSeschrock "'%s' cannot be set in " 1068e9dbad6fSeschrock "a non-global zone"), propname); 1069e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1070e9dbad6fSeschrock errbuf); 1071e9dbad6fSeschrock goto error; 1072fa9e4066Sahrens } 1073fa9e4066Sahrens } else if (getzoneid() != GLOBAL_ZONEID) { 1074fa9e4066Sahrens /* 1075fa9e4066Sahrens * If zoned property is 'off', this must be in 107614843421SMatthew Ahrens * a global zone. If not, something is wrong. 1077fa9e4066Sahrens */ 107899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1079e9dbad6fSeschrock "'%s' cannot be set while dataset " 1080e9dbad6fSeschrock "'zoned' property is set"), propname); 1081e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1082e9dbad6fSeschrock goto error; 1083fa9e4066Sahrens } 1084f3861e1aSahl 108567331909Sdougm /* 108667331909Sdougm * At this point, it is legitimate to set the 108767331909Sdougm * property. Now we want to make sure that the 108867331909Sdougm * property value is valid if it is sharenfs. 108967331909Sdougm */ 1090da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1091da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 109267331909Sdougm strcmp(strval, "on") != 0 && 109367331909Sdougm strcmp(strval, "off") != 0) { 1094da6c28aaSamw zfs_share_proto_t proto; 1095da6c28aaSamw 1096da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1097da6c28aaSamw proto = PROTO_SMB; 1098da6c28aaSamw else 1099da6c28aaSamw proto = PROTO_NFS; 110067331909Sdougm 110167331909Sdougm /* 1102da6c28aaSamw * Must be an valid sharing protocol 1103da6c28aaSamw * option string so init the libshare 1104da6c28aaSamw * in order to enable the parser and 1105da6c28aaSamw * then parse the options. We use the 1106da6c28aaSamw * control API since we don't care about 1107da6c28aaSamw * the current configuration and don't 110867331909Sdougm * want the overhead of loading it 110967331909Sdougm * until we actually do something. 111067331909Sdougm */ 111167331909Sdougm 111267331909Sdougm if (zfs_init_libshare(hdl, 111367331909Sdougm SA_INIT_CONTROL_API) != SA_OK) { 1114fac3008cSeschrock /* 1115fac3008cSeschrock * An error occurred so we can't do 1116fac3008cSeschrock * anything 1117fac3008cSeschrock */ 111867331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 111967331909Sdougm "'%s' cannot be set: problem " 112067331909Sdougm "in share initialization"), 112167331909Sdougm propname); 1122fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1123fac3008cSeschrock errbuf); 112467331909Sdougm goto error; 112567331909Sdougm } 112667331909Sdougm 1127da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 112867331909Sdougm /* 112967331909Sdougm * There was an error in parsing so 113067331909Sdougm * deal with it by issuing an error 113167331909Sdougm * message and leaving after 113267331909Sdougm * uninitializing the the libshare 113367331909Sdougm * interface. 113467331909Sdougm */ 113567331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 113667331909Sdougm "'%s' cannot be set to invalid " 113767331909Sdougm "options"), propname); 1138fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1139fac3008cSeschrock errbuf); 114067331909Sdougm zfs_uninit_libshare(hdl); 114167331909Sdougm goto error; 114267331909Sdougm } 114367331909Sdougm zfs_uninit_libshare(hdl); 114467331909Sdougm } 114567331909Sdougm 1146f3861e1aSahl break; 1147da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1148da6c28aaSamw chosen_utf = (int)intval; 1149da6c28aaSamw break; 1150da6c28aaSamw case ZFS_PROP_NORMALIZE: 1151da6c28aaSamw chosen_normal = (int)intval; 1152da6c28aaSamw break; 1153fa9e4066Sahrens } 1154fa9e4066Sahrens 1155e9dbad6fSeschrock /* 1156e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1157e9dbad6fSeschrock * checks to enforce. 1158e9dbad6fSeschrock */ 1159e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1160e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1161e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1162e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1163e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1164e9dbad6fSeschrock char buf[64]; 1165e9dbad6fSeschrock 1166e9dbad6fSeschrock switch (prop) { 1167e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1168a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1169e9dbad6fSeschrock if (intval > volsize) { 1170e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1171e9dbad6fSeschrock "'%s' is greater than current " 1172e9dbad6fSeschrock "volume size"), propname); 1173e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1174e9dbad6fSeschrock errbuf); 1175e9dbad6fSeschrock goto error; 1176e9dbad6fSeschrock } 1177e9dbad6fSeschrock break; 1178e9dbad6fSeschrock 1179e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1180e9dbad6fSeschrock if (intval % blocksize != 0) { 1181e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1182e9dbad6fSeschrock sizeof (buf)); 1183e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1184e9dbad6fSeschrock "'%s' must be a multiple of " 1185e9dbad6fSeschrock "volume block size (%s)"), 1186e9dbad6fSeschrock propname, buf); 1187e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1188e9dbad6fSeschrock errbuf); 1189e9dbad6fSeschrock goto error; 1190e9dbad6fSeschrock } 1191e9dbad6fSeschrock 1192e9dbad6fSeschrock if (intval == 0) { 1193e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1194e9dbad6fSeschrock "'%s' cannot be zero"), 1195e9dbad6fSeschrock propname); 1196e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1197e9dbad6fSeschrock errbuf); 1198e9dbad6fSeschrock goto error; 1199e9dbad6fSeschrock } 1200f3861e1aSahl break; 1201e9dbad6fSeschrock } 1202e9dbad6fSeschrock } 1203e9dbad6fSeschrock } 1204e9dbad6fSeschrock 1205e9dbad6fSeschrock /* 1206da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1207da6c28aaSamw * enforce rejection of non-UTF8 names. 1208da6c28aaSamw * 1209da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1210da6c28aaSamw * was explicitly not chosen, it is an error. 1211da6c28aaSamw */ 1212de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1213da6c28aaSamw if (nvlist_add_uint64(ret, 1214da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1215da6c28aaSamw (void) no_memory(hdl); 1216da6c28aaSamw goto error; 1217da6c28aaSamw } 1218de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1219da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1220da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1221da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1222da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1223da6c28aaSamw goto error; 1224da6c28aaSamw } 1225da6c28aaSamw 1226da6c28aaSamw /* 1227e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1228e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1229e9dbad6fSeschrock */ 1230e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 1231e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1232e9dbad6fSeschrock &intval) == 0) { 1233e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 1234e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1235a9b821a0Sck153898 uint64_t old_reservation; 1236e9dbad6fSeschrock uint64_t new_reservation; 1237a9b821a0Sck153898 zfs_prop_t resv_prop; 1238a9b821a0Sck153898 12397b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1240a9b821a0Sck153898 goto error; 1241a9b821a0Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 1242e9dbad6fSeschrock 1243e9dbad6fSeschrock if (old_volsize == old_reservation && 1244a9b821a0Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 1245e9dbad6fSeschrock &new_reservation) != 0) { 1246e9dbad6fSeschrock if (nvlist_add_uint64(ret, 1247a9b821a0Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 1248e9dbad6fSeschrock (void) no_memory(hdl); 1249e9dbad6fSeschrock goto error; 1250e9dbad6fSeschrock } 1251e9dbad6fSeschrock } 1252e9dbad6fSeschrock } 1253e9dbad6fSeschrock return (ret); 1254e9dbad6fSeschrock 1255e9dbad6fSeschrock error: 1256e9dbad6fSeschrock nvlist_free(ret); 1257e9dbad6fSeschrock return (NULL); 1258e9dbad6fSeschrock } 1259e9dbad6fSeschrock 126092241e0bSTom Erickson void 126192241e0bSTom Erickson zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 126292241e0bSTom Erickson char *errbuf) 126392241e0bSTom Erickson { 126492241e0bSTom Erickson switch (err) { 126592241e0bSTom Erickson 126692241e0bSTom Erickson case ENOSPC: 126792241e0bSTom Erickson /* 126892241e0bSTom Erickson * For quotas and reservations, ENOSPC indicates 126992241e0bSTom Erickson * something different; setting a quota or reservation 127092241e0bSTom Erickson * doesn't use any disk space. 127192241e0bSTom Erickson */ 127292241e0bSTom Erickson switch (prop) { 127392241e0bSTom Erickson case ZFS_PROP_QUOTA: 127492241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 127592241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127692241e0bSTom Erickson "size is less than current used or " 127792241e0bSTom Erickson "reserved space")); 127892241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 127992241e0bSTom Erickson break; 128092241e0bSTom Erickson 128192241e0bSTom Erickson case ZFS_PROP_RESERVATION: 128292241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 128392241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128492241e0bSTom Erickson "size is greater than available space")); 128592241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128692241e0bSTom Erickson break; 128792241e0bSTom Erickson 128892241e0bSTom Erickson default: 128992241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 129092241e0bSTom Erickson break; 129192241e0bSTom Erickson } 129292241e0bSTom Erickson break; 129392241e0bSTom Erickson 129492241e0bSTom Erickson case EBUSY: 129592241e0bSTom Erickson (void) zfs_standard_error(hdl, EBUSY, errbuf); 129692241e0bSTom Erickson break; 129792241e0bSTom Erickson 129892241e0bSTom Erickson case EROFS: 129992241e0bSTom Erickson (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 130092241e0bSTom Erickson break; 130192241e0bSTom Erickson 130292241e0bSTom Erickson case ENOTSUP: 130392241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130492241e0bSTom Erickson "pool and or dataset must be upgraded to set this " 130592241e0bSTom Erickson "property or value")); 130692241e0bSTom Erickson (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 130792241e0bSTom Erickson break; 130892241e0bSTom Erickson 130992241e0bSTom Erickson case ERANGE: 131092241e0bSTom Erickson if (prop == ZFS_PROP_COMPRESSION) { 131192241e0bSTom Erickson (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 131292241e0bSTom Erickson "property setting is not allowed on " 131392241e0bSTom Erickson "bootable datasets")); 131492241e0bSTom Erickson (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 131592241e0bSTom Erickson } else { 131692241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 131792241e0bSTom Erickson } 131892241e0bSTom Erickson break; 131992241e0bSTom Erickson 132092241e0bSTom Erickson case EOVERFLOW: 132192241e0bSTom Erickson /* 132292241e0bSTom Erickson * This platform can't address a volume this big. 132392241e0bSTom Erickson */ 132492241e0bSTom Erickson #ifdef _ILP32 132592241e0bSTom Erickson if (prop == ZFS_PROP_VOLSIZE) { 132692241e0bSTom Erickson (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132792241e0bSTom Erickson break; 132892241e0bSTom Erickson } 132992241e0bSTom Erickson #endif 133092241e0bSTom Erickson /* FALLTHROUGH */ 133192241e0bSTom Erickson default: 133292241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 133392241e0bSTom Erickson } 133492241e0bSTom Erickson } 133592241e0bSTom Erickson 1336e9dbad6fSeschrock /* 1337e9dbad6fSeschrock * Given a property name and value, set the property for the given dataset. 1338e9dbad6fSeschrock */ 1339e9dbad6fSeschrock int 1340e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1341e9dbad6fSeschrock { 1342e9dbad6fSeschrock zfs_cmd_t zc = { 0 }; 1343e9dbad6fSeschrock int ret = -1; 1344e9dbad6fSeschrock prop_changelist_t *cl = NULL; 1345e9dbad6fSeschrock char errbuf[1024]; 1346e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1347e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1348e9dbad6fSeschrock zfs_prop_t prop; 13490068372bSMark J Musante boolean_t do_prefix; 13500068372bSMark J Musante uint64_t idx; 1351e9dbad6fSeschrock 1352e9dbad6fSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1353e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1354e9dbad6fSeschrock zhp->zfs_name); 1355e9dbad6fSeschrock 1356e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1357e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1358e9dbad6fSeschrock (void) no_memory(hdl); 1359e9dbad6fSeschrock goto error; 1360e9dbad6fSeschrock } 1361e9dbad6fSeschrock 13620a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1363e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1364e9dbad6fSeschrock goto error; 1365990b4856Slling 1366e9dbad6fSeschrock nvlist_free(nvl); 1367e9dbad6fSeschrock nvl = realprops; 1368e9dbad6fSeschrock 1369e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1370e9dbad6fSeschrock 13710069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1372e9dbad6fSeschrock goto error; 1373fa9e4066Sahrens 1374fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 137599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1376fa9e4066Sahrens "child dataset with inherited mountpoint is used " 137799653d4eSeschrock "in a non-global zone")); 137899653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1379fa9e4066Sahrens goto error; 1380fa9e4066Sahrens } 1381fa9e4066Sahrens 13820068372bSMark J Musante /* 13830068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 13840068372bSMark J Musante * then we want to prevent unmounting & remounting it. 13850068372bSMark J Musante */ 13860068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13870068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 13880068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1389a227b7f4Shs24103 1390a227b7f4Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1391fa9e4066Sahrens goto error; 1392fa9e4066Sahrens 1393fa9e4066Sahrens /* 1394fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1395fa9e4066Sahrens */ 1396fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1397fa9e4066Sahrens 1398990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1399e9dbad6fSeschrock goto error; 1400e9dbad6fSeschrock 1401ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1402743a77edSAlan Wright 1403fa9e4066Sahrens if (ret != 0) { 140492241e0bSTom Erickson zfs_setprop_error(hdl, prop, errno, errbuf); 1405fa9e4066Sahrens } else { 1406a227b7f4Shs24103 if (do_prefix) 1407a227b7f4Shs24103 ret = changelist_postfix(cl); 1408a227b7f4Shs24103 1409fa9e4066Sahrens /* 1410fa9e4066Sahrens * Refresh the statistics so the new property value 1411fa9e4066Sahrens * is reflected. 1412fa9e4066Sahrens */ 1413a227b7f4Shs24103 if (ret == 0) 1414fa9e4066Sahrens (void) get_stats(zhp); 1415fa9e4066Sahrens } 1416fa9e4066Sahrens 1417fa9e4066Sahrens error: 1418e9dbad6fSeschrock nvlist_free(nvl); 1419e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1420e9dbad6fSeschrock if (cl) 1421fa9e4066Sahrens changelist_free(cl); 1422fa9e4066Sahrens return (ret); 1423fa9e4066Sahrens } 1424fa9e4066Sahrens 1425fa9e4066Sahrens /* 142692241e0bSTom Erickson * Given a property, inherit the value from the parent dataset, or if received 142792241e0bSTom Erickson * is TRUE, revert to the received value, if any. 1428fa9e4066Sahrens */ 1429fa9e4066Sahrens int 143092241e0bSTom Erickson zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1431fa9e4066Sahrens { 1432fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1433fa9e4066Sahrens int ret; 1434fa9e4066Sahrens prop_changelist_t *cl; 143599653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 143699653d4eSeschrock char errbuf[1024]; 1437e9dbad6fSeschrock zfs_prop_t prop; 143899653d4eSeschrock 143999653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 144099653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1441fa9e4066Sahrens 144292241e0bSTom Erickson zc.zc_cookie = received; 1443990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1444e9dbad6fSeschrock /* 1445e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1446e9dbad6fSeschrock * small, so just do it here. 1447e9dbad6fSeschrock */ 1448e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1449e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1450e9dbad6fSeschrock "invalid property")); 1451e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1452e9dbad6fSeschrock } 1453e9dbad6fSeschrock 1454e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1455e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1456e9dbad6fSeschrock 1457e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1458e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1459e9dbad6fSeschrock 1460e9dbad6fSeschrock return (0); 1461e9dbad6fSeschrock } 1462e9dbad6fSeschrock 1463fa9e4066Sahrens /* 1464fa9e4066Sahrens * Verify that this property is inheritable. 1465fa9e4066Sahrens */ 146699653d4eSeschrock if (zfs_prop_readonly(prop)) 146799653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1468fa9e4066Sahrens 146992241e0bSTom Erickson if (!zfs_prop_inheritable(prop) && !received) 147099653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1471fa9e4066Sahrens 1472fa9e4066Sahrens /* 1473fa9e4066Sahrens * Check to see if the value applies to this type 1474fa9e4066Sahrens */ 147599653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 147699653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1477fa9e4066Sahrens 1478bf7c2d40Srm160521 /* 1479bf7c2d40Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 1480bf7c2d40Srm160521 */ 1481bf7c2d40Srm160521 propname = zfs_prop_to_name(prop); 1482fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1483e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1484fa9e4066Sahrens 1485fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1486fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 148799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 148899653d4eSeschrock "dataset is used in a non-global zone")); 148999653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1490fa9e4066Sahrens } 1491fa9e4066Sahrens 1492fa9e4066Sahrens /* 1493fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1494fa9e4066Sahrens */ 14950069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1496fa9e4066Sahrens return (-1); 1497fa9e4066Sahrens 1498fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 149999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 150099653d4eSeschrock "child dataset with inherited mountpoint is used " 150199653d4eSeschrock "in a non-global zone")); 150299653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1503fa9e4066Sahrens goto error; 1504fa9e4066Sahrens } 1505fa9e4066Sahrens 1506fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1507fa9e4066Sahrens goto error; 1508fa9e4066Sahrens 1509e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 151099653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1511fa9e4066Sahrens } else { 1512fa9e4066Sahrens 1513efc555ebSnd150628 if ((ret = changelist_postfix(cl)) != 0) 1514fa9e4066Sahrens goto error; 1515fa9e4066Sahrens 1516fa9e4066Sahrens /* 1517fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1518fa9e4066Sahrens */ 1519fa9e4066Sahrens (void) get_stats(zhp); 1520fa9e4066Sahrens } 1521fa9e4066Sahrens 1522fa9e4066Sahrens error: 1523fa9e4066Sahrens changelist_free(cl); 1524fa9e4066Sahrens return (ret); 1525fa9e4066Sahrens } 1526fa9e4066Sahrens 1527fa9e4066Sahrens /* 15287f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 15297f7322feSeschrock * extract them appropriately. 15307f7322feSeschrock */ 15317f7322feSeschrock static uint64_t 15327f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15337f7322feSeschrock { 15347f7322feSeschrock nvlist_t *nv; 15357f7322feSeschrock uint64_t value; 15367f7322feSeschrock 1537a2eea2e1Sahrens *source = NULL; 15387f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15397f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1540990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1541990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15427f7322feSeschrock } else { 15432e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15442e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15457f7322feSeschrock value = zfs_prop_default_numeric(prop); 15467f7322feSeschrock *source = ""; 15477f7322feSeschrock } 15487f7322feSeschrock 15497f7322feSeschrock return (value); 15507f7322feSeschrock } 15517f7322feSeschrock 15527f7322feSeschrock static char * 15537f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15547f7322feSeschrock { 15557f7322feSeschrock nvlist_t *nv; 15567f7322feSeschrock char *value; 15577f7322feSeschrock 1558a2eea2e1Sahrens *source = NULL; 15597f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15607f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1561990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1562990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15637f7322feSeschrock } else { 15642e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15652e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15667f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15677f7322feSeschrock value = ""; 15687f7322feSeschrock *source = ""; 15697f7322feSeschrock } 15707f7322feSeschrock 15717f7322feSeschrock return (value); 15727f7322feSeschrock } 15737f7322feSeschrock 157492241e0bSTom Erickson static boolean_t 157592241e0bSTom Erickson zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157692241e0bSTom Erickson { 157792241e0bSTom Erickson return (zhp->zfs_props == zhp->zfs_recvd_props); 157892241e0bSTom Erickson } 157992241e0bSTom Erickson 158092241e0bSTom Erickson static void 158192241e0bSTom Erickson zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158292241e0bSTom Erickson { 158392241e0bSTom Erickson *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158492241e0bSTom Erickson zhp->zfs_props = zhp->zfs_recvd_props; 158592241e0bSTom Erickson } 158692241e0bSTom Erickson 158792241e0bSTom Erickson static void 158892241e0bSTom Erickson zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158992241e0bSTom Erickson { 159092241e0bSTom Erickson zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 159192241e0bSTom Erickson *cookie = 0; 159292241e0bSTom Erickson } 159392241e0bSTom Erickson 15947f7322feSeschrock /* 1595fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1596fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1597fa9e4066Sahrens * 1598fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1599fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1600fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1601fa9e4066Sahrens * the source "temporary". 1602fa9e4066Sahrens */ 160399653d4eSeschrock static int 1604990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 160599653d4eSeschrock char **source, uint64_t *val) 1606fa9e4066Sahrens { 1607bd00f61bSrm160521 zfs_cmd_t zc = { 0 }; 160896510749Stimh nvlist_t *zplprops = NULL; 1609fa9e4066Sahrens struct mnttab mnt; 16103ccfa83cSahrens char *mntopt_on = NULL; 16113ccfa83cSahrens char *mntopt_off = NULL; 161292241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1613fa9e4066Sahrens 1614fa9e4066Sahrens *source = NULL; 1615fa9e4066Sahrens 16163ccfa83cSahrens switch (prop) { 16173ccfa83cSahrens case ZFS_PROP_ATIME: 16183ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 16193ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 16203ccfa83cSahrens break; 16213ccfa83cSahrens 16223ccfa83cSahrens case ZFS_PROP_DEVICES: 16233ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 16243ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 16253ccfa83cSahrens break; 16263ccfa83cSahrens 16273ccfa83cSahrens case ZFS_PROP_EXEC: 16283ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 16293ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 16303ccfa83cSahrens break; 16313ccfa83cSahrens 16323ccfa83cSahrens case ZFS_PROP_READONLY: 16333ccfa83cSahrens mntopt_on = MNTOPT_RO; 16343ccfa83cSahrens mntopt_off = MNTOPT_RW; 16353ccfa83cSahrens break; 16363ccfa83cSahrens 16373ccfa83cSahrens case ZFS_PROP_SETUID: 16383ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 16393ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 16403ccfa83cSahrens break; 16413ccfa83cSahrens 16423ccfa83cSahrens case ZFS_PROP_XATTR: 16433ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 16443ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 16453ccfa83cSahrens break; 1646da6c28aaSamw 1647da6c28aaSamw case ZFS_PROP_NBMAND: 1648da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1649da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1650da6c28aaSamw break; 16513ccfa83cSahrens } 16523ccfa83cSahrens 16533bb79becSeschrock /* 16543bb79becSeschrock * Because looking up the mount options is potentially expensive 16553bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16563bb79becSeschrock * we're looking up a property which requires its presence. 16573bb79becSeschrock */ 16583bb79becSeschrock if (!zhp->zfs_mntcheck && 16593ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1660ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1661ebedde84SEric Taylor struct mnttab entry; 16623bb79becSeschrock 1663ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1664ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 16653ccfa83cSahrens entry.mnt_mntopts); 16663ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 16673bb79becSeschrock return (-1); 16683ccfa83cSahrens } 16693bb79becSeschrock 16703bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 16713bb79becSeschrock } 16723bb79becSeschrock 1673fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1674fa9e4066Sahrens mnt.mnt_mntopts = ""; 1675fa9e4066Sahrens else 1676fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1677fa9e4066Sahrens 1678fa9e4066Sahrens switch (prop) { 1679fa9e4066Sahrens case ZFS_PROP_ATIME: 1680fa9e4066Sahrens case ZFS_PROP_DEVICES: 1681fa9e4066Sahrens case ZFS_PROP_EXEC: 16823ccfa83cSahrens case ZFS_PROP_READONLY: 16833ccfa83cSahrens case ZFS_PROP_SETUID: 16843ccfa83cSahrens case ZFS_PROP_XATTR: 1685da6c28aaSamw case ZFS_PROP_NBMAND: 168699653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1687fa9e4066Sahrens 168892241e0bSTom Erickson if (received) 168992241e0bSTom Erickson break; 169092241e0bSTom Erickson 16913ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 169299653d4eSeschrock *val = B_TRUE; 1693fa9e4066Sahrens if (src) 1694990b4856Slling *src = ZPROP_SRC_TEMPORARY; 16953ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 169699653d4eSeschrock *val = B_FALSE; 1697fa9e4066Sahrens if (src) 1698990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1699fa9e4066Sahrens } 170099653d4eSeschrock break; 1701fa9e4066Sahrens 17023ccfa83cSahrens case ZFS_PROP_CANMOUNT: 1703fa9e4066Sahrens case ZFS_PROP_QUOTA: 1704a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1705fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1706a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1707a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 170892241e0bSTom Erickson 170992241e0bSTom Erickson if (*source == NULL) { 171092241e0bSTom Erickson /* not default, must be local */ 1711fa9e4066Sahrens *source = zhp->zfs_name; 171292241e0bSTom Erickson } 171399653d4eSeschrock break; 1714fa9e4066Sahrens 1715fa9e4066Sahrens case ZFS_PROP_MOUNTED: 171699653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 171799653d4eSeschrock break; 1718fa9e4066Sahrens 171939c23413Seschrock case ZFS_PROP_NUMCLONES: 172039c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 172139c23413Seschrock break; 172239c23413Seschrock 1723bd00f61bSrm160521 case ZFS_PROP_VERSION: 1724de8267e0Stimh case ZFS_PROP_NORMALIZE: 1725de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1726de8267e0Stimh case ZFS_PROP_CASE: 1727de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1728de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17293d7934e1Srm160521 return (-1); 1730bd00f61bSrm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1731de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1732de8267e0Stimh zcmd_free_nvlists(&zc); 1733f4b94bdeSMatthew Ahrens return (-1); 1734bd00f61bSrm160521 } 1735de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1736de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1737de8267e0Stimh val) != 0) { 1738de8267e0Stimh zcmd_free_nvlists(&zc); 1739f4b94bdeSMatthew Ahrens return (-1); 1740de8267e0Stimh } 174196510749Stimh if (zplprops) 174296510749Stimh nvlist_free(zplprops); 1743de8267e0Stimh zcmd_free_nvlists(&zc); 1744bd00f61bSrm160521 break; 1745bd00f61bSrm160521 1746fa9e4066Sahrens default: 1747e7437265Sahrens switch (zfs_prop_get_type(prop)) { 174891ebeef5Sahrens case PROP_TYPE_NUMBER: 174991ebeef5Sahrens case PROP_TYPE_INDEX: 1750e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 175174e7dc98SMatthew Ahrens /* 175214843421SMatthew Ahrens * If we tried to use a default value for a 175374e7dc98SMatthew Ahrens * readonly property, it means that it was not 175431f572c2STom Erickson * present. 175574e7dc98SMatthew Ahrens */ 175674e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 175731f572c2STom Erickson *source != NULL && (*source)[0] == '\0') { 175831f572c2STom Erickson *source = NULL; 175974e7dc98SMatthew Ahrens } 1760e7437265Sahrens break; 1761e7437265Sahrens 176291ebeef5Sahrens case PROP_TYPE_STRING: 1763e7437265Sahrens default: 176499653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 176599653d4eSeschrock "cannot get non-numeric property")); 176699653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 176799653d4eSeschrock dgettext(TEXT_DOMAIN, "internal error"))); 1768fa9e4066Sahrens } 1769e7437265Sahrens } 1770fa9e4066Sahrens 1771fa9e4066Sahrens return (0); 1772fa9e4066Sahrens } 1773fa9e4066Sahrens 1774fa9e4066Sahrens /* 1775fa9e4066Sahrens * Calculate the source type, given the raw source string. 1776fa9e4066Sahrens */ 1777fa9e4066Sahrens static void 1778990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1779fa9e4066Sahrens char *statbuf, size_t statlen) 1780fa9e4066Sahrens { 1781990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1782fa9e4066Sahrens return; 1783fa9e4066Sahrens 1784fa9e4066Sahrens if (source == NULL) { 1785990b4856Slling *srctype = ZPROP_SRC_NONE; 1786fa9e4066Sahrens } else if (source[0] == '\0') { 1787990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 178892241e0bSTom Erickson } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 178992241e0bSTom Erickson *srctype = ZPROP_SRC_RECEIVED; 1790fa9e4066Sahrens } else { 1791fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1792990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1793fa9e4066Sahrens } else { 1794fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1795990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1796fa9e4066Sahrens } 1797fa9e4066Sahrens } 1798fa9e4066Sahrens 1799fa9e4066Sahrens } 1800fa9e4066Sahrens 180192241e0bSTom Erickson int 180292241e0bSTom Erickson zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180392241e0bSTom Erickson size_t proplen, boolean_t literal) 180492241e0bSTom Erickson { 180592241e0bSTom Erickson zfs_prop_t prop; 180692241e0bSTom Erickson int err = 0; 180792241e0bSTom Erickson 180892241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 180992241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 181092241e0bSTom Erickson return (-1); 181192241e0bSTom Erickson 181292241e0bSTom Erickson prop = zfs_name_to_prop(propname); 181392241e0bSTom Erickson 181492241e0bSTom Erickson if (prop != ZPROP_INVAL) { 181592241e0bSTom Erickson uint64_t cookie; 181692241e0bSTom Erickson if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181792241e0bSTom Erickson return (-1); 181892241e0bSTom Erickson zfs_set_recvd_props_mode(zhp, &cookie); 181992241e0bSTom Erickson err = zfs_prop_get(zhp, prop, propbuf, proplen, 182092241e0bSTom Erickson NULL, NULL, 0, literal); 182192241e0bSTom Erickson zfs_unset_recvd_props_mode(zhp, &cookie); 182292241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 182392241e0bSTom Erickson return (-1); 182492241e0bSTom Erickson } else { 182592241e0bSTom Erickson nvlist_t *propval; 182692241e0bSTom Erickson char *recvdval; 182792241e0bSTom Erickson if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 182892241e0bSTom Erickson propname, &propval) != 0) 182992241e0bSTom Erickson return (-1); 183092241e0bSTom Erickson verify(nvlist_lookup_string(propval, ZPROP_VALUE, 183192241e0bSTom Erickson &recvdval) == 0); 183292241e0bSTom Erickson (void) strlcpy(propbuf, recvdval, proplen); 183392241e0bSTom Erickson } 183492241e0bSTom Erickson 183592241e0bSTom Erickson return (err == 0 ? 0 : -1); 183692241e0bSTom Erickson } 183792241e0bSTom Erickson 1838fa9e4066Sahrens /* 1839fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1840fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1841fa9e4066Sahrens * human-readable form. 1842fa9e4066Sahrens * 1843fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1844fa9e4066Sahrens */ 1845fa9e4066Sahrens int 1846fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1847990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1848fa9e4066Sahrens { 1849fa9e4066Sahrens char *source = NULL; 1850fa9e4066Sahrens uint64_t val; 1851fa9e4066Sahrens char *str; 1852e9dbad6fSeschrock const char *strval; 185392241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1854fa9e4066Sahrens 1855fa9e4066Sahrens /* 1856fa9e4066Sahrens * Check to see if this property applies to our object 1857fa9e4066Sahrens */ 1858fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1859fa9e4066Sahrens return (-1); 1860fa9e4066Sahrens 186192241e0bSTom Erickson if (received && zfs_prop_readonly(prop)) 186292241e0bSTom Erickson return (-1); 186392241e0bSTom Erickson 1864fa9e4066Sahrens if (src) 1865990b4856Slling *src = ZPROP_SRC_NONE; 1866fa9e4066Sahrens 1867fa9e4066Sahrens switch (prop) { 1868fa9e4066Sahrens case ZFS_PROP_CREATION: 1869fa9e4066Sahrens /* 1870fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1871fa9e4066Sahrens * this into a string unless 'literal' is specified. 1872fa9e4066Sahrens */ 1873fa9e4066Sahrens { 1874a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1875a2eea2e1Sahrens time_t time = (time_t)val; 1876fa9e4066Sahrens struct tm t; 1877fa9e4066Sahrens 1878fa9e4066Sahrens if (literal || 1879fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1880fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1881fa9e4066Sahrens &t) == 0) 1882a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1883fa9e4066Sahrens } 1884fa9e4066Sahrens break; 1885fa9e4066Sahrens 1886fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1887fa9e4066Sahrens /* 1888fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1889fa9e4066Sahrens * 1890fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1891fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1892fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1893fa9e4066Sahrens * 1894fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1895fa9e4066Sahrens * root to any values we return. 1896fa9e4066Sahrens */ 189729ab75c9Srm160521 18987f7322feSeschrock str = getprop_string(zhp, prop, &source); 1899fa9e4066Sahrens 1900b21718f0Sgw25295 if (str[0] == '/') { 190129ab75c9Srm160521 char buf[MAXPATHLEN]; 190229ab75c9Srm160521 char *root = buf; 19037f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 1904fa9e4066Sahrens 1905fa9e4066Sahrens if (relpath[0] == '/') 1906fa9e4066Sahrens relpath++; 1907b21718f0Sgw25295 190829ab75c9Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 190929ab75c9Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 191029ab75c9Srm160521 (strcmp(root, "-") == 0)) 191129ab75c9Srm160521 root[0] = '\0'; 1912b21718f0Sgw25295 /* 1913b21718f0Sgw25295 * Special case an alternate root of '/'. This will 1914b21718f0Sgw25295 * avoid having multiple leading slashes in the 1915b21718f0Sgw25295 * mountpoint path. 1916b21718f0Sgw25295 */ 1917b21718f0Sgw25295 if (strcmp(root, "/") == 0) 1918b21718f0Sgw25295 root++; 1919b21718f0Sgw25295 1920b21718f0Sgw25295 /* 1921b21718f0Sgw25295 * If the mountpoint is '/' then skip over this 1922b21718f0Sgw25295 * if we are obtaining either an alternate root or 1923b21718f0Sgw25295 * an inherited mountpoint. 1924b21718f0Sgw25295 */ 1925b21718f0Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 1926b21718f0Sgw25295 relpath[0] != '\0')) 19277f7322feSeschrock str++; 1928fa9e4066Sahrens 1929fa9e4066Sahrens if (relpath[0] == '\0') 1930fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19317f7322feSeschrock root, str); 1932fa9e4066Sahrens else 1933fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19347f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1935fa9e4066Sahrens relpath); 1936fa9e4066Sahrens } else { 1937fa9e4066Sahrens /* 'legacy' or 'none' */ 19387f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1939fa9e4066Sahrens } 1940fa9e4066Sahrens 1941fa9e4066Sahrens break; 1942fa9e4066Sahrens 1943fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1944a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1945fa9e4066Sahrens proplen); 1946fa9e4066Sahrens /* 1947fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1948fa9e4066Sahrens * it doesn't apply to this dataset. 1949fa9e4066Sahrens */ 1950fa9e4066Sahrens if (propbuf[0] == '\0') 1951fa9e4066Sahrens return (-1); 1952fa9e4066Sahrens break; 1953fa9e4066Sahrens 1954fa9e4066Sahrens case ZFS_PROP_QUOTA: 1955a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1956fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1957a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1958a9799022Sck153898 195999653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 196099653d4eSeschrock return (-1); 1961fa9e4066Sahrens 1962fa9e4066Sahrens /* 1963fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 1964fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 1965fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 1966fa9e4066Sahrens * that its set locally. 1967fa9e4066Sahrens */ 1968fa9e4066Sahrens if (val == 0) { 1969fa9e4066Sahrens if (literal) 1970fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 1971fa9e4066Sahrens else 1972fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 1973fa9e4066Sahrens } else { 1974fa9e4066Sahrens if (literal) 19755ad82045Snd150628 (void) snprintf(propbuf, proplen, "%llu", 19765ad82045Snd150628 (u_longlong_t)val); 1977fa9e4066Sahrens else 1978fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 1979fa9e4066Sahrens } 1980fa9e4066Sahrens break; 1981fa9e4066Sahrens 1982fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 198399653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 198499653d4eSeschrock return (-1); 1985b24ab676SJeff Bonwick (void) snprintf(propbuf, proplen, "%llu.%02llux", 1986b24ab676SJeff Bonwick (u_longlong_t)(val / 100), 1987b24ab676SJeff Bonwick (u_longlong_t)(val % 100)); 1988fa9e4066Sahrens break; 1989fa9e4066Sahrens 1990fa9e4066Sahrens case ZFS_PROP_TYPE: 1991fa9e4066Sahrens switch (zhp->zfs_type) { 1992fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 1993fa9e4066Sahrens str = "filesystem"; 1994fa9e4066Sahrens break; 1995fa9e4066Sahrens case ZFS_TYPE_VOLUME: 1996fa9e4066Sahrens str = "volume"; 1997fa9e4066Sahrens break; 1998fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 1999fa9e4066Sahrens str = "snapshot"; 2000fa9e4066Sahrens break; 2001fa9e4066Sahrens default: 200299653d4eSeschrock abort(); 2003fa9e4066Sahrens } 2004fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2005fa9e4066Sahrens break; 2006fa9e4066Sahrens 2007fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2008fa9e4066Sahrens /* 2009fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2010fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2011fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2012fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2013fa9e4066Sahrens */ 201499653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 201599653d4eSeschrock src, &source, &val) != 0) 201699653d4eSeschrock return (-1); 201799653d4eSeschrock if (val) 2018fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2019fa9e4066Sahrens else 2020fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2021fa9e4066Sahrens break; 2022fa9e4066Sahrens 2023fa9e4066Sahrens case ZFS_PROP_NAME: 2024fa9e4066Sahrens /* 2025fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2026fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2027fa9e4066Sahrens * consumers. 2028fa9e4066Sahrens */ 2029fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2030fa9e4066Sahrens break; 2031fa9e4066Sahrens 20324201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 20334201a95eSRic Aleshire { 20344201a95eSRic Aleshire m_label_t *new_sl = NULL; 20354201a95eSRic Aleshire char *ascii = NULL; /* human readable label */ 20364201a95eSRic Aleshire 20374201a95eSRic Aleshire (void) strlcpy(propbuf, 20384201a95eSRic Aleshire getprop_string(zhp, prop, &source), proplen); 20394201a95eSRic Aleshire 20404201a95eSRic Aleshire if (literal || (strcasecmp(propbuf, 20414201a95eSRic Aleshire ZFS_MLSLABEL_DEFAULT) == 0)) 20424201a95eSRic Aleshire break; 20434201a95eSRic Aleshire 20444201a95eSRic Aleshire /* 20454201a95eSRic Aleshire * Try to translate the internal hex string to 20464201a95eSRic Aleshire * human-readable output. If there are any 20474201a95eSRic Aleshire * problems just use the hex string. 20484201a95eSRic Aleshire */ 20494201a95eSRic Aleshire 20504201a95eSRic Aleshire if (str_to_label(propbuf, &new_sl, MAC_LABEL, 20514201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1) { 20524201a95eSRic Aleshire m_label_free(new_sl); 20534201a95eSRic Aleshire break; 20544201a95eSRic Aleshire } 20554201a95eSRic Aleshire 20564201a95eSRic Aleshire if (label_to_str(new_sl, &ascii, M_LABEL, 20574201a95eSRic Aleshire DEF_NAMES) != 0) { 20584201a95eSRic Aleshire if (ascii) 20594201a95eSRic Aleshire free(ascii); 20604201a95eSRic Aleshire m_label_free(new_sl); 20614201a95eSRic Aleshire break; 20624201a95eSRic Aleshire } 20634201a95eSRic Aleshire m_label_free(new_sl); 20644201a95eSRic Aleshire 20654201a95eSRic Aleshire (void) strlcpy(propbuf, ascii, proplen); 20664201a95eSRic Aleshire free(ascii); 20674201a95eSRic Aleshire } 20684201a95eSRic Aleshire break; 20694201a95eSRic Aleshire 2070fa9e4066Sahrens default: 2071e7437265Sahrens switch (zfs_prop_get_type(prop)) { 207291ebeef5Sahrens case PROP_TYPE_NUMBER: 2073e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2074e7437265Sahrens &source, &val) != 0) 2075e7437265Sahrens return (-1); 2076e7437265Sahrens if (literal) 2077e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2078e7437265Sahrens (u_longlong_t)val); 2079e7437265Sahrens else 2080e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2081e7437265Sahrens break; 2082e7437265Sahrens 208391ebeef5Sahrens case PROP_TYPE_STRING: 2084e7437265Sahrens (void) strlcpy(propbuf, 2085e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 2086e7437265Sahrens break; 2087e7437265Sahrens 208891ebeef5Sahrens case PROP_TYPE_INDEX: 2089fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2090fe192f76Sahrens &source, &val) != 0) 2091fe192f76Sahrens return (-1); 2092fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2093e7437265Sahrens return (-1); 2094e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2095e7437265Sahrens break; 2096e7437265Sahrens 2097e7437265Sahrens default: 209899653d4eSeschrock abort(); 2099fa9e4066Sahrens } 2100e7437265Sahrens } 2101fa9e4066Sahrens 2102fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2103fa9e4066Sahrens 2104fa9e4066Sahrens return (0); 2105fa9e4066Sahrens } 2106fa9e4066Sahrens 2107fa9e4066Sahrens /* 2108fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2109fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2110fa9e4066Sahrens * hard-coded property types. 2111fa9e4066Sahrens */ 2112fa9e4066Sahrens uint64_t 2113fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2114fa9e4066Sahrens { 2115fa9e4066Sahrens char *source; 211699653d4eSeschrock uint64_t val; 2117fa9e4066Sahrens 21183cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 211999653d4eSeschrock 212099653d4eSeschrock return (val); 2121fa9e4066Sahrens } 2122fa9e4066Sahrens 21237b97dc1aSrm160521 int 21247b97dc1aSrm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21257b97dc1aSrm160521 { 21267b97dc1aSrm160521 char buf[64]; 21277b97dc1aSrm160521 212814843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21297b97dc1aSrm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21307b97dc1aSrm160521 } 21317b97dc1aSrm160521 2132fa9e4066Sahrens /* 2133fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2134fa9e4066Sahrens */ 2135fa9e4066Sahrens int 2136fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2137990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2138fa9e4066Sahrens { 2139fa9e4066Sahrens char *source; 2140fa9e4066Sahrens 2141fa9e4066Sahrens /* 2142fa9e4066Sahrens * Check to see if this property applies to our object 2143fa9e4066Sahrens */ 2144e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2145ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 214699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 214799653d4eSeschrock zfs_prop_to_name(prop))); 2148e45ce728Sahrens } 2149fa9e4066Sahrens 2150fa9e4066Sahrens if (src) 2151990b4856Slling *src = ZPROP_SRC_NONE; 2152fa9e4066Sahrens 215399653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 215499653d4eSeschrock return (-1); 2155fa9e4066Sahrens 2156fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2157fa9e4066Sahrens 2158fa9e4066Sahrens return (0); 2159fa9e4066Sahrens } 2160fa9e4066Sahrens 216114843421SMatthew Ahrens static int 216214843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 216314843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 216414843421SMatthew Ahrens { 216514843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 216614843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 216714843421SMatthew Ahrens idmap_stat status; 216814843421SMatthew Ahrens int err = EINVAL; 216914843421SMatthew Ahrens 217014843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 217114843421SMatthew Ahrens goto out; 217214843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 217314843421SMatthew Ahrens goto out; 217414843421SMatthew Ahrens 217514843421SMatthew Ahrens if (isuser) { 217614843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 217714843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 217814843421SMatthew Ahrens } else { 217914843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 218014843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 218114843421SMatthew Ahrens } 218214843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 218314843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 218414843421SMatthew Ahrens status == IDMAP_SUCCESS) 218514843421SMatthew Ahrens err = 0; 218614843421SMatthew Ahrens else 218714843421SMatthew Ahrens err = EINVAL; 218814843421SMatthew Ahrens out: 218914843421SMatthew Ahrens if (get_hdl) 219014843421SMatthew Ahrens idmap_get_destroy(get_hdl); 219114843421SMatthew Ahrens if (idmap_hdl) 219214843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 219314843421SMatthew Ahrens return (err); 219414843421SMatthew Ahrens } 219514843421SMatthew Ahrens 219614843421SMatthew Ahrens /* 219714843421SMatthew Ahrens * convert the propname into parameters needed by kernel 219814843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 219914843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 220014843421SMatthew Ahrens */ 220114843421SMatthew Ahrens static int 220214843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 220314843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 220414843421SMatthew Ahrens { 220514843421SMatthew Ahrens zfs_userquota_prop_t type; 220614843421SMatthew Ahrens char *cp, *end; 22073b12c289SMatthew Ahrens char *numericsid = NULL; 220814843421SMatthew Ahrens boolean_t isuser; 220914843421SMatthew Ahrens 221014843421SMatthew Ahrens domain[0] = '\0'; 221114843421SMatthew Ahrens 221214843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 221314843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 221414843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 221514843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 221614843421SMatthew Ahrens break; 221714843421SMatthew Ahrens } 221814843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 221914843421SMatthew Ahrens return (EINVAL); 222014843421SMatthew Ahrens *typep = type; 222114843421SMatthew Ahrens 222214843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 222314843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 222414843421SMatthew Ahrens 222514843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 222614843421SMatthew Ahrens 222714843421SMatthew Ahrens if (strchr(cp, '@')) { 222814843421SMatthew Ahrens /* 222914843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 22303b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 223114843421SMatthew Ahrens */ 22323b12c289SMatthew Ahrens directory_error_t e; 223314843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 223414843421SMatthew Ahrens return (ENOENT); 22353b12c289SMatthew Ahrens if (isuser) { 22363b12c289SMatthew Ahrens e = directory_sid_from_user_name(NULL, 22373b12c289SMatthew Ahrens cp, &numericsid); 22383b12c289SMatthew Ahrens } else { 22393b12c289SMatthew Ahrens e = directory_sid_from_group_name(NULL, 22403b12c289SMatthew Ahrens cp, &numericsid); 22413b12c289SMatthew Ahrens } 22423b12c289SMatthew Ahrens if (e != NULL) { 22433b12c289SMatthew Ahrens directory_error_free(e); 224414843421SMatthew Ahrens return (ENOENT); 22453b12c289SMatthew Ahrens } 22463b12c289SMatthew Ahrens if (numericsid == NULL) 224714843421SMatthew Ahrens return (ENOENT); 22483b12c289SMatthew Ahrens cp = numericsid; 22493b12c289SMatthew Ahrens /* will be further decoded below */ 22503b12c289SMatthew Ahrens } 22513b12c289SMatthew Ahrens 22523b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 225314843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 22543b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 225514843421SMatthew Ahrens cp = strrchr(domain, '-'); 225614843421SMatthew Ahrens *cp = '\0'; 225714843421SMatthew Ahrens cp++; 225814843421SMatthew Ahrens 225914843421SMatthew Ahrens errno = 0; 226014843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 22613b12c289SMatthew Ahrens if (numericsid) { 22623b12c289SMatthew Ahrens free(numericsid); 22633b12c289SMatthew Ahrens numericsid = NULL; 22643b12c289SMatthew Ahrens } 2265777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 226614843421SMatthew Ahrens return (EINVAL); 226714843421SMatthew Ahrens } else if (!isdigit(*cp)) { 226814843421SMatthew Ahrens /* 226914843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 227014843421SMatthew Ahrens * turned into a uid/gid 227114843421SMatthew Ahrens */ 227214843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 227314843421SMatthew Ahrens return (ENOENT); 227414843421SMatthew Ahrens if (isuser) { 227514843421SMatthew Ahrens struct passwd *pw; 227614843421SMatthew Ahrens pw = getpwnam(cp); 227714843421SMatthew Ahrens if (pw == NULL) 227814843421SMatthew Ahrens return (ENOENT); 227914843421SMatthew Ahrens *ridp = pw->pw_uid; 228014843421SMatthew Ahrens } else { 228114843421SMatthew Ahrens struct group *gr; 228214843421SMatthew Ahrens gr = getgrnam(cp); 228314843421SMatthew Ahrens if (gr == NULL) 228414843421SMatthew Ahrens return (ENOENT); 228514843421SMatthew Ahrens *ridp = gr->gr_gid; 228614843421SMatthew Ahrens } 228714843421SMatthew Ahrens } else { 228814843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 228914843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 229014843421SMatthew Ahrens idmap_rid_t rid; 229114843421SMatthew Ahrens char *mapdomain; 229214843421SMatthew Ahrens 229314843421SMatthew Ahrens if (*end != '\0') 229414843421SMatthew Ahrens return (EINVAL); 229514843421SMatthew Ahrens if (id > MAXUID) { 229614843421SMatthew Ahrens /* It's an ephemeral ID. */ 229714843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 229814843421SMatthew Ahrens &mapdomain, &rid) != 0) 229914843421SMatthew Ahrens return (ENOENT); 23003b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 230114843421SMatthew Ahrens *ridp = rid; 230214843421SMatthew Ahrens } else { 230314843421SMatthew Ahrens *ridp = id; 230414843421SMatthew Ahrens } 230514843421SMatthew Ahrens } 230614843421SMatthew Ahrens 23073b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 230814843421SMatthew Ahrens return (0); 230914843421SMatthew Ahrens } 231014843421SMatthew Ahrens 2311edea4b55SLin Ling static int 2312edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2313edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 231414843421SMatthew Ahrens { 231514843421SMatthew Ahrens int err; 231614843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 231714843421SMatthew Ahrens 231814843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 231914843421SMatthew Ahrens 232014843421SMatthew Ahrens err = userquota_propname_decode(propname, 232114843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2322edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2323edea4b55SLin Ling zc.zc_objset_type = *typep; 232414843421SMatthew Ahrens if (err) 232514843421SMatthew Ahrens return (err); 232614843421SMatthew Ahrens 232714843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 232814843421SMatthew Ahrens if (err) 232914843421SMatthew Ahrens return (err); 233014843421SMatthew Ahrens 2331edea4b55SLin Ling *propvalue = zc.zc_cookie; 2332edea4b55SLin Ling return (0); 2333edea4b55SLin Ling } 2334edea4b55SLin Ling 2335edea4b55SLin Ling int 2336edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2337edea4b55SLin Ling uint64_t *propvalue) 2338edea4b55SLin Ling { 2339edea4b55SLin Ling zfs_userquota_prop_t type; 2340edea4b55SLin Ling 2341edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2342edea4b55SLin Ling &type)); 2343edea4b55SLin Ling } 2344edea4b55SLin Ling 2345edea4b55SLin Ling int 2346edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2347edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 2348edea4b55SLin Ling { 2349edea4b55SLin Ling int err; 2350edea4b55SLin Ling uint64_t propvalue; 2351edea4b55SLin Ling zfs_userquota_prop_t type; 2352edea4b55SLin Ling 2353edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2354edea4b55SLin Ling &type); 2355edea4b55SLin Ling 2356edea4b55SLin Ling if (err) 2357edea4b55SLin Ling return (err); 2358edea4b55SLin Ling 235914843421SMatthew Ahrens if (literal) { 2360edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 2361edea4b55SLin Ling } else if (propvalue == 0 && 236214843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 236314843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 236414843421SMatthew Ahrens } else { 2365edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 236614843421SMatthew Ahrens } 236714843421SMatthew Ahrens return (0); 236814843421SMatthew Ahrens } 236914843421SMatthew Ahrens 2370fa9e4066Sahrens /* 2371fa9e4066Sahrens * Returns the name of the given zfs handle. 2372fa9e4066Sahrens */ 2373fa9e4066Sahrens const char * 2374fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2375fa9e4066Sahrens { 2376fa9e4066Sahrens return (zhp->zfs_name); 2377fa9e4066Sahrens } 2378fa9e4066Sahrens 2379fa9e4066Sahrens /* 2380fa9e4066Sahrens * Returns the type of the given zfs handle. 2381fa9e4066Sahrens */ 2382fa9e4066Sahrens zfs_type_t 2383fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2384fa9e4066Sahrens { 2385fa9e4066Sahrens return (zhp->zfs_type); 2386fa9e4066Sahrens } 2387fa9e4066Sahrens 2388ebedde84SEric Taylor static int 2389ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2390ebedde84SEric Taylor { 2391ebedde84SEric Taylor int rc; 2392ebedde84SEric Taylor uint64_t orig_cookie; 2393ebedde84SEric Taylor 2394ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2395ebedde84SEric Taylor top: 2396ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2397ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2398ebedde84SEric Taylor 2399ebedde84SEric Taylor if (rc == -1) { 2400ebedde84SEric Taylor switch (errno) { 2401ebedde84SEric Taylor case ENOMEM: 2402ebedde84SEric Taylor /* expand nvlist memory and try again */ 2403ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2404ebedde84SEric Taylor zcmd_free_nvlists(zc); 2405ebedde84SEric Taylor return (-1); 2406ebedde84SEric Taylor } 2407ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2408ebedde84SEric Taylor goto top; 2409ebedde84SEric Taylor /* 2410ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2411ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2412ebedde84SEric Taylor * has been removed since we obtained the handle. 2413ebedde84SEric Taylor */ 2414ebedde84SEric Taylor case ESRCH: 2415ebedde84SEric Taylor case ENOENT: 2416ebedde84SEric Taylor rc = 1; 2417ebedde84SEric Taylor break; 2418ebedde84SEric Taylor default: 2419ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2420ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2421ebedde84SEric Taylor "cannot iterate filesystems")); 2422ebedde84SEric Taylor break; 2423ebedde84SEric Taylor } 2424ebedde84SEric Taylor } 2425ebedde84SEric Taylor return (rc); 2426ebedde84SEric Taylor } 2427ebedde84SEric Taylor 2428fa9e4066Sahrens /* 24297f7322feSeschrock * Iterate over all child filesystems 2430fa9e4066Sahrens */ 2431fa9e4066Sahrens int 24327f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2433fa9e4066Sahrens { 2434fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2435fa9e4066Sahrens zfs_handle_t *nzhp; 2436fa9e4066Sahrens int ret; 2437fa9e4066Sahrens 24383cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24393cb34c60Sahrens return (0); 24403cb34c60Sahrens 2441ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2442ebedde84SEric Taylor return (-1); 2443ebedde84SEric Taylor 2444ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2445ebedde84SEric Taylor &zc)) == 0) { 2446fa9e4066Sahrens /* 2447fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2448fa9e4066Sahrens * that the pool has since been removed. 2449fa9e4066Sahrens */ 2450ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2451ebedde84SEric Taylor &zc)) == NULL) { 2452fa9e4066Sahrens continue; 2453fa9e4066Sahrens } 2454fa9e4066Sahrens 2455ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2456ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2457ebedde84SEric Taylor return (ret); 2458ebedde84SEric Taylor } 2459ebedde84SEric Taylor } 2460ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2461ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 24627f7322feSeschrock } 24637f7322feSeschrock 24647f7322feSeschrock /* 24657f7322feSeschrock * Iterate over all snapshots 24667f7322feSeschrock */ 24677f7322feSeschrock int 24687f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24697f7322feSeschrock { 24707f7322feSeschrock zfs_cmd_t zc = { 0 }; 24717f7322feSeschrock zfs_handle_t *nzhp; 24727f7322feSeschrock int ret; 2473fa9e4066Sahrens 24743cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24753cb34c60Sahrens return (0); 24763cb34c60Sahrens 2477ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2478ebedde84SEric Taylor return (-1); 2479ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2480ebedde84SEric Taylor &zc)) == 0) { 2481fa9e4066Sahrens 2482ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2483ebedde84SEric Taylor &zc)) == NULL) { 2484fa9e4066Sahrens continue; 2485fa9e4066Sahrens } 2486fa9e4066Sahrens 2487ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2488ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2489ebedde84SEric Taylor return (ret); 2490ebedde84SEric Taylor } 2491ebedde84SEric Taylor } 2492ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2493ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2494fa9e4066Sahrens } 2495fa9e4066Sahrens 2496fa9e4066Sahrens /* 24977f7322feSeschrock * Iterate over all children, snapshots and filesystems 24987f7322feSeschrock */ 24997f7322feSeschrock int 25007f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25017f7322feSeschrock { 25027f7322feSeschrock int ret; 25037f7322feSeschrock 25047f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25057f7322feSeschrock return (ret); 25067f7322feSeschrock 25077f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 25087f7322feSeschrock } 25097f7322feSeschrock 25107f7322feSeschrock /* 2511fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2512fa9e4066Sahrens * Can return NULL if this is a pool. 2513fa9e4066Sahrens */ 2514fa9e4066Sahrens static int 2515fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2516fa9e4066Sahrens { 2517fa9e4066Sahrens char *loc; 2518fa9e4066Sahrens 2519fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2520fa9e4066Sahrens return (-1); 2521fa9e4066Sahrens 2522fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2523fa9e4066Sahrens buf[loc - path] = '\0'; 2524fa9e4066Sahrens 2525fa9e4066Sahrens return (0); 2526fa9e4066Sahrens } 2527fa9e4066Sahrens 2528fa9e4066Sahrens /* 25297f1f55eaSvb160487 * If accept_ancestor is false, then check to make sure that the given path has 25307f1f55eaSvb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25317f1f55eaSvb160487 * closest existing ancestor for the given path. In prefixlen return the 25327f1f55eaSvb160487 * length of already existing prefix of the given path. We also fetch the 25337f1f55eaSvb160487 * 'zoned' property, which is used to validate property settings when creating 25347f1f55eaSvb160487 * new datasets. 2535fa9e4066Sahrens */ 2536fa9e4066Sahrens static int 25377f1f55eaSvb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25387f1f55eaSvb160487 boolean_t accept_ancestor, int *prefixlen) 2539fa9e4066Sahrens { 2540fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2541fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2542fa9e4066Sahrens char *slash; 25437f7322feSeschrock zfs_handle_t *zhp; 254499653d4eSeschrock char errbuf[1024]; 254599653d4eSeschrock 2546deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2547deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2548fa9e4066Sahrens 2549fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2550fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 255199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 255299653d4eSeschrock "missing dataset name")); 255399653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2554fa9e4066Sahrens } 2555fa9e4066Sahrens 2556fa9e4066Sahrens /* check to see if the pool exists */ 2557fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2558fa9e4066Sahrens slash = parent + strlen(parent); 2559fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2560fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 256199653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2562fa9e4066Sahrens errno == ENOENT) { 256399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 256499653d4eSeschrock "no such pool '%s'"), zc.zc_name); 256599653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2566fa9e4066Sahrens } 2567fa9e4066Sahrens 2568fa9e4066Sahrens /* check to see if the parent dataset exists */ 25697f1f55eaSvb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 25707f1f55eaSvb160487 if (errno == ENOENT && accept_ancestor) { 25717f1f55eaSvb160487 /* 25727f1f55eaSvb160487 * Go deeper to find an ancestor, give up on top level. 25737f1f55eaSvb160487 */ 25747f1f55eaSvb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 25757f1f55eaSvb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25767f1f55eaSvb160487 "no such pool '%s'"), zc.zc_name); 25777f1f55eaSvb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 25787f1f55eaSvb160487 } 25797f1f55eaSvb160487 } else if (errno == ENOENT) { 258099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 258199653d4eSeschrock "parent does not exist")); 258299653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 25837f1f55eaSvb160487 } else 258499653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2585fa9e4066Sahrens } 2586fa9e4066Sahrens 2587e9dbad6fSeschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2588fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2589e9dbad6fSeschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 259099653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 25917f7322feSeschrock zfs_close(zhp); 2592fa9e4066Sahrens return (-1); 2593fa9e4066Sahrens } 2594fa9e4066Sahrens 2595fa9e4066Sahrens /* make sure parent is a filesystem */ 25967f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 259799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 259899653d4eSeschrock "parent is not a filesystem")); 259999653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26007f7322feSeschrock zfs_close(zhp); 2601fa9e4066Sahrens return (-1); 2602fa9e4066Sahrens } 2603fa9e4066Sahrens 26047f7322feSeschrock zfs_close(zhp); 26057f1f55eaSvb160487 if (prefixlen != NULL) 26067f1f55eaSvb160487 *prefixlen = strlen(parent); 26077f1f55eaSvb160487 return (0); 26087f1f55eaSvb160487 } 26097f1f55eaSvb160487 26107f1f55eaSvb160487 /* 26117f1f55eaSvb160487 * Finds whether the dataset of the given type(s) exists. 26127f1f55eaSvb160487 */ 26137f1f55eaSvb160487 boolean_t 26147f1f55eaSvb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26157f1f55eaSvb160487 { 26167f1f55eaSvb160487 zfs_handle_t *zhp; 26177f1f55eaSvb160487 2618f18faf3fSek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26197f1f55eaSvb160487 return (B_FALSE); 26207f1f55eaSvb160487 26217f1f55eaSvb160487 /* 26227f1f55eaSvb160487 * Try to get stats for the dataset, which will tell us if it exists. 26237f1f55eaSvb160487 */ 26247f1f55eaSvb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26257f1f55eaSvb160487 int ds_type = zhp->zfs_type; 26267f1f55eaSvb160487 26277f1f55eaSvb160487 zfs_close(zhp); 26287f1f55eaSvb160487 if (types & ds_type) 26297f1f55eaSvb160487 return (B_TRUE); 26307f1f55eaSvb160487 } 26317f1f55eaSvb160487 return (B_FALSE); 26327f1f55eaSvb160487 } 26337f1f55eaSvb160487 26347f1f55eaSvb160487 /* 26353cb34c60Sahrens * Given a path to 'target', create all the ancestors between 26363cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 26373cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26383cb34c60Sahrens */ 26393cb34c60Sahrens int 26403cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26413cb34c60Sahrens { 26423cb34c60Sahrens zfs_handle_t *h; 26433cb34c60Sahrens char *cp; 26443cb34c60Sahrens const char *opname; 26453cb34c60Sahrens 26463cb34c60Sahrens /* make sure prefix exists */ 26473cb34c60Sahrens cp = target + prefixlen; 26483cb34c60Sahrens if (*cp != '/') { 26493cb34c60Sahrens assert(strchr(cp, '/') == NULL); 26503cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26513cb34c60Sahrens } else { 26523cb34c60Sahrens *cp = '\0'; 26533cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26543cb34c60Sahrens *cp = '/'; 26553cb34c60Sahrens } 26563cb34c60Sahrens if (h == NULL) 26573cb34c60Sahrens return (-1); 26583cb34c60Sahrens zfs_close(h); 26593cb34c60Sahrens 26603cb34c60Sahrens /* 26613cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26623cb34c60Sahrens * up to the prefixlen-long one. 26633cb34c60Sahrens */ 26643cb34c60Sahrens for (cp = target + prefixlen + 1; 26653cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 26663cb34c60Sahrens char *logstr; 26673cb34c60Sahrens 26683cb34c60Sahrens *cp = '\0'; 26693cb34c60Sahrens 26703cb34c60Sahrens h = make_dataset_handle(hdl, target); 26713cb34c60Sahrens if (h) { 26723cb34c60Sahrens /* it already exists, nothing to do here */ 26733cb34c60Sahrens zfs_close(h); 26743cb34c60Sahrens continue; 26753cb34c60Sahrens } 26763cb34c60Sahrens 26773cb34c60Sahrens logstr = hdl->libzfs_log_str; 26783cb34c60Sahrens hdl->libzfs_log_str = NULL; 26793cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 26803cb34c60Sahrens NULL) != 0) { 26813cb34c60Sahrens hdl->libzfs_log_str = logstr; 26823cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 26833cb34c60Sahrens goto ancestorerr; 26843cb34c60Sahrens } 26853cb34c60Sahrens 26863cb34c60Sahrens hdl->libzfs_log_str = logstr; 26873cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26883cb34c60Sahrens if (h == NULL) { 26893cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 26903cb34c60Sahrens goto ancestorerr; 26913cb34c60Sahrens } 26923cb34c60Sahrens 26933cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 26943cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 26953cb34c60Sahrens goto ancestorerr; 26963cb34c60Sahrens } 26973cb34c60Sahrens 26983cb34c60Sahrens if (zfs_share(h) != 0) { 26993cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27003cb34c60Sahrens goto ancestorerr; 27013cb34c60Sahrens } 27023cb34c60Sahrens 27033cb34c60Sahrens zfs_close(h); 27043cb34c60Sahrens } 27053cb34c60Sahrens 27063cb34c60Sahrens return (0); 27073cb34c60Sahrens 27083cb34c60Sahrens ancestorerr: 27093cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27103cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 27113cb34c60Sahrens return (-1); 27123cb34c60Sahrens } 27133cb34c60Sahrens 27143cb34c60Sahrens /* 27157f1f55eaSvb160487 * Creates non-existing ancestors of the given path. 27167f1f55eaSvb160487 */ 27177f1f55eaSvb160487 int 27187f1f55eaSvb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27197f1f55eaSvb160487 { 27207f1f55eaSvb160487 int prefix; 27217f1f55eaSvb160487 uint64_t zoned; 27227f1f55eaSvb160487 char *path_copy; 27237f1f55eaSvb160487 int rc; 27247f1f55eaSvb160487 27257f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 27267f1f55eaSvb160487 return (-1); 27277f1f55eaSvb160487 27287f1f55eaSvb160487 if ((path_copy = strdup(path)) != NULL) { 27297f1f55eaSvb160487 rc = create_parents(hdl, path_copy, prefix); 27307f1f55eaSvb160487 free(path_copy); 27317f1f55eaSvb160487 } 27327f1f55eaSvb160487 if (path_copy == NULL || rc != 0) 27337f1f55eaSvb160487 return (-1); 27347f1f55eaSvb160487 2735fa9e4066Sahrens return (0); 2736fa9e4066Sahrens } 2737fa9e4066Sahrens 2738fa9e4066Sahrens /* 2739e9dbad6fSeschrock * Create a new filesystem or volume. 2740fa9e4066Sahrens */ 2741fa9e4066Sahrens int 274299653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2743e9dbad6fSeschrock nvlist_t *props) 2744fa9e4066Sahrens { 2745fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2746fa9e4066Sahrens int ret; 2747fa9e4066Sahrens uint64_t size = 0; 2748fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 274999653d4eSeschrock char errbuf[1024]; 2750e9dbad6fSeschrock uint64_t zoned; 275199653d4eSeschrock 275299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 275399653d4eSeschrock "cannot create '%s'"), path); 2754fa9e4066Sahrens 2755fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2756f18faf3fSek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 275799653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2758fa9e4066Sahrens 2759fa9e4066Sahrens /* validate parents exist */ 27607f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2761fa9e4066Sahrens return (-1); 2762fa9e4066Sahrens 2763fa9e4066Sahrens /* 2764fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2765fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2766fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2767fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2768fa9e4066Sahrens * first try to see if the dataset exists. 2769fa9e4066Sahrens */ 2770fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2771990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 277299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 277399653d4eSeschrock "dataset already exists")); 277499653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2775fa9e4066Sahrens } 2776fa9e4066Sahrens 2777fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2778fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2779fa9e4066Sahrens else 2780fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2781fa9e4066Sahrens 27820a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2783b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2784e9dbad6fSeschrock return (-1); 2785e9dbad6fSeschrock 2786fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 27875c5460e9Seschrock /* 27885c5460e9Seschrock * If we are creating a volume, the size and block size must 27895c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 27905c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 27915c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 27925c5460e9Seschrock * zero. 27935c5460e9Seschrock */ 2794e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2795e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2796e9dbad6fSeschrock nvlist_free(props); 279799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2798e9dbad6fSeschrock "missing volume size")); 2799e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2800fa9e4066Sahrens } 2801fa9e4066Sahrens 2802e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2803e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2804e9dbad6fSeschrock &blocksize)) != 0) { 2805e9dbad6fSeschrock if (ret == ENOENT) { 2806e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2807e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2808e9dbad6fSeschrock } else { 2809e9dbad6fSeschrock nvlist_free(props); 281099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2811e9dbad6fSeschrock "missing volume block size")); 2812e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2813e9dbad6fSeschrock } 2814e9dbad6fSeschrock } 2815e9dbad6fSeschrock 2816e9dbad6fSeschrock if (size == 0) { 2817e9dbad6fSeschrock nvlist_free(props); 2818e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2819e9dbad6fSeschrock "volume size cannot be zero")); 2820e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28215c5460e9Seschrock } 28225c5460e9Seschrock 28235c5460e9Seschrock if (size % blocksize != 0) { 2824e9dbad6fSeschrock nvlist_free(props); 282599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2826e9dbad6fSeschrock "volume size must be a multiple of volume block " 2827e9dbad6fSeschrock "size")); 2828e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2829e9dbad6fSeschrock } 28305c5460e9Seschrock } 28315c5460e9Seschrock 2832990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2833e9dbad6fSeschrock return (-1); 2834e9dbad6fSeschrock nvlist_free(props); 2835fa9e4066Sahrens 2836fa9e4066Sahrens /* create the dataset */ 2837ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2838fa9e4066Sahrens 2839e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2840e9dbad6fSeschrock 2841fa9e4066Sahrens /* check for failure */ 2842fa9e4066Sahrens if (ret != 0) { 2843fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2844fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2845fa9e4066Sahrens 2846fa9e4066Sahrens switch (errno) { 2847fa9e4066Sahrens case ENOENT: 284899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 284999653d4eSeschrock "no such parent '%s'"), parent); 285099653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2851fa9e4066Sahrens 2852fa9e4066Sahrens case EINVAL: 285399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2854d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 285599653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2856fa9e4066Sahrens 2857fa9e4066Sahrens case EDOM: 285899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2859e9dbad6fSeschrock "volume block size must be power of 2 from " 2860e9dbad6fSeschrock "%u to %uk"), 2861fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2862fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 286399653d4eSeschrock 2864e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 286599653d4eSeschrock 286640feaa91Sahrens case ENOTSUP: 286740feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 286840feaa91Sahrens "pool must be upgraded to set this " 286940feaa91Sahrens "property or value")); 287040feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2871fa9e4066Sahrens #ifdef _ILP32 2872fa9e4066Sahrens case EOVERFLOW: 2873fa9e4066Sahrens /* 2874fa9e4066Sahrens * This platform can't address a volume this big. 2875fa9e4066Sahrens */ 287699653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 287799653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 287899653d4eSeschrock errbuf)); 2879fa9e4066Sahrens #endif 288099653d4eSeschrock /* FALLTHROUGH */ 2881fa9e4066Sahrens default: 288299653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2883fa9e4066Sahrens } 2884fa9e4066Sahrens } 2885fa9e4066Sahrens 2886fa9e4066Sahrens return (0); 2887fa9e4066Sahrens } 2888fa9e4066Sahrens 2889fa9e4066Sahrens /* 2890fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2891fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2892fa9e4066Sahrens */ 2893fa9e4066Sahrens int 2894842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2895fa9e4066Sahrens { 2896fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2897fa9e4066Sahrens 2898fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2899fa9e4066Sahrens 2900e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2901f3861e1aSahl /* 2902ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2903ecd6cf80Smarks * abort the request. This would only happen for a 2904ecd6cf80Smarks * non-privileged user. 2905f3861e1aSahl */ 2906ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2907ecd6cf80Smarks return (-1); 2908ecd6cf80Smarks } 2909f3861e1aSahl 2910fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2911fa9e4066Sahrens } else { 2912fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2913fa9e4066Sahrens } 2914fa9e4066Sahrens 2915842727c2SChris Kirby zc.zc_defer_destroy = defer; 2916ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2917ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 291899653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 291999653d4eSeschrock zhp->zfs_name)); 29201d452cf5Sahrens } 2921fa9e4066Sahrens 2922fa9e4066Sahrens remove_mountpoint(zhp); 2923fa9e4066Sahrens 2924fa9e4066Sahrens return (0); 2925fa9e4066Sahrens } 2926fa9e4066Sahrens 29271d452cf5Sahrens struct destroydata { 29281d452cf5Sahrens char *snapname; 29291d452cf5Sahrens boolean_t gotone; 29303ccfa83cSahrens boolean_t closezhp; 29311d452cf5Sahrens }; 29321d452cf5Sahrens 29331d452cf5Sahrens static int 2934681d9761SEric Taylor zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29351d452cf5Sahrens { 29361d452cf5Sahrens struct destroydata *dd = arg; 29371d452cf5Sahrens zfs_handle_t *szhp; 29381d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 29393ccfa83cSahrens boolean_t closezhp = dd->closezhp; 2940681d9761SEric Taylor int rv = 0; 29411d452cf5Sahrens 2942e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2943e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2944e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29451d452cf5Sahrens 29461d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 29471d452cf5Sahrens if (szhp) { 29481d452cf5Sahrens dd->gotone = B_TRUE; 29491d452cf5Sahrens zfs_close(szhp); 29501d452cf5Sahrens } 29511d452cf5Sahrens 29523ccfa83cSahrens dd->closezhp = B_TRUE; 2953681d9761SEric Taylor if (!dd->gotone) 2954681d9761SEric Taylor rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 29553ccfa83cSahrens if (closezhp) 29563ccfa83cSahrens zfs_close(zhp); 29573ccfa83cSahrens return (rv); 29581d452cf5Sahrens } 29591d452cf5Sahrens 29601d452cf5Sahrens /* 29611d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 29621d452cf5Sahrens */ 29631d452cf5Sahrens int 2964842727c2SChris Kirby zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 29651d452cf5Sahrens { 29661d452cf5Sahrens zfs_cmd_t zc = { 0 }; 29671d452cf5Sahrens int ret; 29681d452cf5Sahrens struct destroydata dd = { 0 }; 29691d452cf5Sahrens 29701d452cf5Sahrens dd.snapname = snapname; 2971681d9761SEric Taylor (void) zfs_check_snap_cb(zhp, &dd); 29721d452cf5Sahrens 29731d452cf5Sahrens if (!dd.gotone) { 2974ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 29751d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 29761d452cf5Sahrens zhp->zfs_name, snapname)); 29771d452cf5Sahrens } 29781d452cf5Sahrens 29791d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2980e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 2981842727c2SChris Kirby zc.zc_defer_destroy = defer; 29821d452cf5Sahrens 2983ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 29841d452cf5Sahrens if (ret != 0) { 29851d452cf5Sahrens char errbuf[1024]; 29861d452cf5Sahrens 29871d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 29881d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 29891d452cf5Sahrens 29901d452cf5Sahrens switch (errno) { 29911d452cf5Sahrens case EEXIST: 29921d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 29931d452cf5Sahrens "snapshot is cloned")); 29941d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 29951d452cf5Sahrens 29961d452cf5Sahrens default: 29971d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 29981d452cf5Sahrens errbuf)); 29991d452cf5Sahrens } 30001d452cf5Sahrens } 30011d452cf5Sahrens 30021d452cf5Sahrens return (0); 30031d452cf5Sahrens } 30041d452cf5Sahrens 3005fa9e4066Sahrens /* 3006fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 3007fa9e4066Sahrens */ 3008fa9e4066Sahrens int 3009e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3010fa9e4066Sahrens { 3011fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3012fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 3013fa9e4066Sahrens int ret; 301499653d4eSeschrock char errbuf[1024]; 301599653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3016e9dbad6fSeschrock zfs_type_t type; 3017e9dbad6fSeschrock uint64_t zoned; 3018fa9e4066Sahrens 3019fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3020fa9e4066Sahrens 302199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 302299653d4eSeschrock "cannot create '%s'"), target); 302399653d4eSeschrock 3024fa9e4066Sahrens /* validate the target name */ 3025f18faf3fSek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 302699653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3027fa9e4066Sahrens 3028fa9e4066Sahrens /* validate parents exist */ 30297f1f55eaSvb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3030fa9e4066Sahrens return (-1); 3031fa9e4066Sahrens 3032fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3033fa9e4066Sahrens 3034fa9e4066Sahrens /* do the clone */ 3035e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 3036fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30375f8e1617Snn35248 type = ZFS_TYPE_VOLUME; 3038e9dbad6fSeschrock } else { 3039fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30405f8e1617Snn35248 type = ZFS_TYPE_FILESYSTEM; 3041e9dbad6fSeschrock } 3042e9dbad6fSeschrock 3043e9dbad6fSeschrock if (props) { 30440a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30450a48a24eStimh zhp, errbuf)) == NULL) 3046e9dbad6fSeschrock return (-1); 3047e9dbad6fSeschrock 3048990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3049e9dbad6fSeschrock nvlist_free(props); 3050e9dbad6fSeschrock return (-1); 3051e9dbad6fSeschrock } 3052e9dbad6fSeschrock 3053e9dbad6fSeschrock nvlist_free(props); 3054e9dbad6fSeschrock } 3055fa9e4066Sahrens 3056fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 3057e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 3058ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3059fa9e4066Sahrens 3060e9dbad6fSeschrock zcmd_free_nvlists(&zc); 3061e9dbad6fSeschrock 3062fa9e4066Sahrens if (ret != 0) { 3063fa9e4066Sahrens switch (errno) { 3064fa9e4066Sahrens 3065fa9e4066Sahrens case ENOENT: 3066fa9e4066Sahrens /* 3067fa9e4066Sahrens * The parent doesn't exist. We should have caught this 3068fa9e4066Sahrens * above, but there may a race condition that has since 3069fa9e4066Sahrens * destroyed the parent. 3070fa9e4066Sahrens * 3071fa9e4066Sahrens * At this point, we don't know whether it's the source 3072fa9e4066Sahrens * that doesn't exist anymore, or whether the target 3073fa9e4066Sahrens * dataset doesn't exist. 3074fa9e4066Sahrens */ 307599653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 307699653d4eSeschrock "no such parent '%s'"), parent); 307799653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3078fa9e4066Sahrens 307999653d4eSeschrock case EXDEV: 308099653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 308199653d4eSeschrock "source and target pools differ")); 308299653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 308399653d4eSeschrock errbuf)); 308499653d4eSeschrock 308599653d4eSeschrock default: 308699653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 308799653d4eSeschrock errbuf)); 308899653d4eSeschrock } 308999653d4eSeschrock } 309099653d4eSeschrock 309199653d4eSeschrock return (ret); 309299653d4eSeschrock } 309399653d4eSeschrock 309499653d4eSeschrock /* 309599653d4eSeschrock * Promotes the given clone fs to be the clone parent. 309699653d4eSeschrock */ 309799653d4eSeschrock int 309899653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 309999653d4eSeschrock { 310099653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 310199653d4eSeschrock zfs_cmd_t zc = { 0 }; 310299653d4eSeschrock char parent[MAXPATHLEN]; 310399653d4eSeschrock int ret; 310499653d4eSeschrock char errbuf[1024]; 310599653d4eSeschrock 310699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 310799653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 310899653d4eSeschrock 310999653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 311099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 311199653d4eSeschrock "snapshots can not be promoted")); 311299653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 311399653d4eSeschrock } 311499653d4eSeschrock 31153cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 311699653d4eSeschrock if (parent[0] == '\0') { 311799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 311899653d4eSeschrock "not a cloned filesystem")); 311999653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 312099653d4eSeschrock } 312199653d4eSeschrock 31223cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3123e9dbad6fSeschrock sizeof (zc.zc_value)); 312499653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3125ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 312699653d4eSeschrock 312799653d4eSeschrock if (ret != 0) { 31280b69c2f0Sahrens int save_errno = errno; 3129fa9e4066Sahrens 31300b69c2f0Sahrens switch (save_errno) { 3131fa9e4066Sahrens case EEXIST: 3132681d9761SEric Taylor /* There is a conflicting snapshot name. */ 313399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3134681d9761SEric Taylor "conflicting snapshot '%s' from parent '%s'"), 3135681d9761SEric Taylor zc.zc_string, parent); 313699653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3137fa9e4066Sahrens 3138fa9e4066Sahrens default: 31390b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3140fa9e4066Sahrens } 3141fa9e4066Sahrens } 3142e9dbad6fSeschrock return (ret); 31431d452cf5Sahrens } 31441d452cf5Sahrens 3145fa9e4066Sahrens /* 314672bdce51Sahl * Takes a snapshot of the given dataset. 3147fa9e4066Sahrens */ 3148fa9e4066Sahrens int 3149bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3150bb0ade09Sahrens nvlist_t *props) 3151fa9e4066Sahrens { 3152fa9e4066Sahrens const char *delim; 3153bb0ade09Sahrens char parent[ZFS_MAXNAMELEN]; 3154fa9e4066Sahrens zfs_handle_t *zhp; 3155fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3156fa9e4066Sahrens int ret; 315799653d4eSeschrock char errbuf[1024]; 3158fa9e4066Sahrens 315999653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 316099653d4eSeschrock "cannot snapshot '%s'"), path); 316199653d4eSeschrock 316299653d4eSeschrock /* validate the target name */ 3163f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 316499653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3165fa9e4066Sahrens 3166bb0ade09Sahrens if (props) { 3167bb0ade09Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3168bb0ade09Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 3169bb0ade09Sahrens return (-1); 3170bb0ade09Sahrens 3171bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3172bb0ade09Sahrens nvlist_free(props); 3173bb0ade09Sahrens return (-1); 3174bb0ade09Sahrens } 3175bb0ade09Sahrens 3176bb0ade09Sahrens nvlist_free(props); 3177bb0ade09Sahrens } 3178bb0ade09Sahrens 3179fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 31801d452cf5Sahrens delim = strchr(path, '@'); 3181fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3182fa9e4066Sahrens parent[delim - path] = '\0'; 3183fa9e4066Sahrens 318499653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3185fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3186bb0ade09Sahrens zcmd_free_nvlists(&zc); 3187fa9e4066Sahrens return (-1); 3188fa9e4066Sahrens } 3189fa9e4066Sahrens 31901d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3191e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3192ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3193ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3194ecd6cf80Smarks else 3195ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 31961d452cf5Sahrens zc.zc_cookie = recursive; 3197ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3198fa9e4066Sahrens 3199bb0ade09Sahrens zcmd_free_nvlists(&zc); 3200bb0ade09Sahrens 32011d452cf5Sahrens /* 32021d452cf5Sahrens * if it was recursive, the one that actually failed will be in 32031d452cf5Sahrens * zc.zc_name. 32041d452cf5Sahrens */ 3205681d9761SEric Taylor if (ret != 0) { 32061d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3207e9dbad6fSeschrock "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 320899653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3209681d9761SEric Taylor } 3210fa9e4066Sahrens 3211fa9e4066Sahrens zfs_close(zhp); 3212fa9e4066Sahrens 3213fa9e4066Sahrens return (ret); 3214fa9e4066Sahrens } 3215fa9e4066Sahrens 3216fa9e4066Sahrens /* 3217b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3218b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3219b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3220b12a1c38Slling * group. 3221fa9e4066Sahrens */ 3222b12a1c38Slling typedef struct rollback_data { 3223b12a1c38Slling const char *cb_target; /* the snapshot */ 3224b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3225c391e322Sahrens boolean_t cb_error; 322699653d4eSeschrock boolean_t cb_dependent; 3227c391e322Sahrens boolean_t cb_force; 3228b12a1c38Slling } rollback_data_t; 3229b12a1c38Slling 3230b12a1c38Slling static int 3231b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3232b12a1c38Slling { 3233b12a1c38Slling rollback_data_t *cbp = data; 3234b12a1c38Slling 3235b12a1c38Slling if (!cbp->cb_dependent) { 3236b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3237b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3238b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3239b12a1c38Slling cbp->cb_create) { 3240ecd6cf80Smarks char *logstr; 3241b12a1c38Slling 324299653d4eSeschrock cbp->cb_dependent = B_TRUE; 32434ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32444ccbb6e7Sahrens rollback_destroy, cbp); 324599653d4eSeschrock cbp->cb_dependent = B_FALSE; 3246b12a1c38Slling 3247ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3248ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 3249842727c2SChris Kirby cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3250ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3251b12a1c38Slling } 3252b12a1c38Slling } else { 3253c391e322Sahrens /* We must destroy this clone; first unmount it */ 3254c391e322Sahrens prop_changelist_t *clp; 3255c391e322Sahrens 32560069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3257c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3258c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3259c391e322Sahrens cbp->cb_error = B_TRUE; 3260c391e322Sahrens zfs_close(zhp); 3261c391e322Sahrens return (0); 3262c391e322Sahrens } 3263842727c2SChris Kirby if (zfs_destroy(zhp, B_FALSE) != 0) 3264c391e322Sahrens cbp->cb_error = B_TRUE; 3265c391e322Sahrens else 3266c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3267ba7b046eSahrens (void) changelist_postfix(clp); 3268c391e322Sahrens changelist_free(clp); 3269b12a1c38Slling } 3270b12a1c38Slling 3271b12a1c38Slling zfs_close(zhp); 3272b12a1c38Slling return (0); 3273b12a1c38Slling } 3274b12a1c38Slling 3275b12a1c38Slling /* 32764ccbb6e7Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 32774ccbb6e7Sahrens * data changes since then and making it the active dataset. 32784ccbb6e7Sahrens * 32794ccbb6e7Sahrens * Any snapshots more recent than the target are destroyed, along with 32804ccbb6e7Sahrens * their dependents. 3281b12a1c38Slling */ 32824ccbb6e7Sahrens int 3283c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3284fa9e4066Sahrens { 32854ccbb6e7Sahrens rollback_data_t cb = { 0 }; 32864ccbb6e7Sahrens int err; 3287fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 32887b97dc1aSrm160521 boolean_t restore_resv = 0; 32897b97dc1aSrm160521 uint64_t old_volsize, new_volsize; 32907b97dc1aSrm160521 zfs_prop_t resv_prop; 3291fa9e4066Sahrens 3292fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3293fa9e4066Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3294fa9e4066Sahrens 32954ccbb6e7Sahrens /* 32964ccbb6e7Sahrens * Destroy all recent snapshots and its dependends. 32974ccbb6e7Sahrens */ 3298c391e322Sahrens cb.cb_force = force; 32994ccbb6e7Sahrens cb.cb_target = snap->zfs_name; 33004ccbb6e7Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33014ccbb6e7Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33024ccbb6e7Sahrens 3303c391e322Sahrens if (cb.cb_error) 3304c391e322Sahrens return (-1); 33054ccbb6e7Sahrens 33064ccbb6e7Sahrens /* 33074ccbb6e7Sahrens * Now that we have verified that the snapshot is the latest, 33084ccbb6e7Sahrens * rollback to the given snapshot. 33094ccbb6e7Sahrens */ 33104ccbb6e7Sahrens 33117b97dc1aSrm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33127b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33137b97dc1aSrm160521 return (-1); 33147b97dc1aSrm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33157b97dc1aSrm160521 restore_resv = 33167b97dc1aSrm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33177b97dc1aSrm160521 } 3318fa9e4066Sahrens 3319fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3320fa9e4066Sahrens 3321e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3322fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3323fa9e4066Sahrens else 3324fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3325fa9e4066Sahrens 3326fa9e4066Sahrens /* 33274ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 33284ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 33294ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 33304ccbb6e7Sahrens * an unlikely race condition where the user has taken a 33314ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 33327b97dc1aSrm160521 * 3333fa9e4066Sahrens */ 33344ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3335ece3d9b3Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 333699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 333799653d4eSeschrock zhp->zfs_name); 3338b9415e83Srm160521 return (err); 3339b9415e83Srm160521 } 3340fa9e4066Sahrens 33417b97dc1aSrm160521 /* 33427b97dc1aSrm160521 * For volumes, if the pre-rollback volsize matched the pre- 33437b97dc1aSrm160521 * rollback reservation and the volsize has changed then set 33447b97dc1aSrm160521 * the reservation property to the post-rollback volsize. 33457b97dc1aSrm160521 * Make a new handle since the rollback closed the dataset. 33467b97dc1aSrm160521 */ 3347b9415e83Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3348b9415e83Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 33497b97dc1aSrm160521 if (restore_resv) { 33507b97dc1aSrm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33517b97dc1aSrm160521 if (old_volsize != new_volsize) 3352b9415e83Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 3353b9415e83Srm160521 new_volsize); 33547b97dc1aSrm160521 } 33557b97dc1aSrm160521 zfs_close(zhp); 33567b97dc1aSrm160521 } 33574ccbb6e7Sahrens return (err); 3358b12a1c38Slling } 3359b12a1c38Slling 3360b12a1c38Slling /* 3361fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3362fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3363fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3364fa9e4066Sahrens * libzfs_graph.c. 3365fa9e4066Sahrens */ 3366fa9e4066Sahrens int 33673bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33683bb79becSeschrock zfs_iter_f func, void *data) 3369fa9e4066Sahrens { 3370fa9e4066Sahrens char **dependents; 3371fa9e4066Sahrens size_t count; 3372fa9e4066Sahrens int i; 3373fa9e4066Sahrens zfs_handle_t *child; 3374fa9e4066Sahrens int ret = 0; 3375fa9e4066Sahrens 33763bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 33773bb79becSeschrock &dependents, &count) != 0) 33783bb79becSeschrock return (-1); 33793bb79becSeschrock 3380fa9e4066Sahrens for (i = 0; i < count; i++) { 338199653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 338299653d4eSeschrock dependents[i])) == NULL) 3383fa9e4066Sahrens continue; 3384fa9e4066Sahrens 3385fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3386fa9e4066Sahrens break; 3387fa9e4066Sahrens } 3388fa9e4066Sahrens 3389fa9e4066Sahrens for (i = 0; i < count; i++) 3390fa9e4066Sahrens free(dependents[i]); 3391fa9e4066Sahrens free(dependents); 3392fa9e4066Sahrens 3393fa9e4066Sahrens return (ret); 3394fa9e4066Sahrens } 3395fa9e4066Sahrens 3396fa9e4066Sahrens /* 3397fa9e4066Sahrens * Renames the given dataset. 3398fa9e4066Sahrens */ 3399fa9e4066Sahrens int 34007f1f55eaSvb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3401fa9e4066Sahrens { 3402fa9e4066Sahrens int ret; 3403fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3404fa9e4066Sahrens char *delim; 3405cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3406cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3407cdf5b4caSmmusante char *parentname = NULL; 3408fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 340999653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 341099653d4eSeschrock char errbuf[1024]; 3411fa9e4066Sahrens 3412fa9e4066Sahrens /* if we have the same exact name, just return success */ 3413fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3414fa9e4066Sahrens return (0); 3415fa9e4066Sahrens 341699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 341799653d4eSeschrock "cannot rename to '%s'"), target); 341899653d4eSeschrock 3419fa9e4066Sahrens /* 3420fa9e4066Sahrens * Make sure the target name is valid 3421fa9e4066Sahrens */ 3422fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 342398579b20Snd150628 if ((strchr(target, '@') == NULL) || 342498579b20Snd150628 *target == '@') { 342598579b20Snd150628 /* 342698579b20Snd150628 * Snapshot target name is abbreviated, 342798579b20Snd150628 * reconstruct full dataset name 342898579b20Snd150628 */ 342998579b20Snd150628 (void) strlcpy(parent, zhp->zfs_name, 343098579b20Snd150628 sizeof (parent)); 343198579b20Snd150628 delim = strchr(parent, '@'); 343298579b20Snd150628 if (strchr(target, '@') == NULL) 343398579b20Snd150628 *(++delim) = '\0'; 343498579b20Snd150628 else 343598579b20Snd150628 *delim = '\0'; 343698579b20Snd150628 (void) strlcat(parent, target, sizeof (parent)); 343798579b20Snd150628 target = parent; 343898579b20Snd150628 } else { 3439fa9e4066Sahrens /* 3440fa9e4066Sahrens * Make sure we're renaming within the same dataset. 3441fa9e4066Sahrens */ 344298579b20Snd150628 delim = strchr(target, '@'); 344398579b20Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 344498579b20Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 344599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 344698579b20Snd150628 "snapshots must be part of same " 344798579b20Snd150628 "dataset")); 344898579b20Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 344998579b20Snd150628 errbuf)); 3450fa9e4066Sahrens } 345198579b20Snd150628 } 3452f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 345398579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3454fa9e4066Sahrens } else { 3455cdf5b4caSmmusante if (recursive) { 3456cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3457cdf5b4caSmmusante "recursive rename must be a snapshot")); 3458cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3459cdf5b4caSmmusante } 3460cdf5b4caSmmusante 3461f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 346298579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3463e9dbad6fSeschrock uint64_t unused; 3464e9dbad6fSeschrock 3465fa9e4066Sahrens /* validate parents */ 34667f1f55eaSvb160487 if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3467fa9e4066Sahrens return (-1); 3468fa9e4066Sahrens 3469fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3470fa9e4066Sahrens 3471fa9e4066Sahrens /* make sure we're in the same pool */ 3472fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3473fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3474fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 347599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 347699653d4eSeschrock "datasets must be within same pool")); 347799653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3478fa9e4066Sahrens } 3479f2fdf992Snd150628 3480f2fdf992Snd150628 /* new name cannot be a child of the current dataset name */ 3481f2fdf992Snd150628 if (strncmp(parent, zhp->zfs_name, 3482f2fdf992Snd150628 strlen(zhp->zfs_name)) == 0) { 3483f2fdf992Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3484f2fdf992Snd150628 "New dataset name cannot be a descendent of " 3485f2fdf992Snd150628 "current dataset name")); 3486f2fdf992Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3487f2fdf992Snd150628 } 3488fa9e4066Sahrens } 3489fa9e4066Sahrens 349099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 349199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 349299653d4eSeschrock 3493fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3494fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 349599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 349699653d4eSeschrock "dataset is used in a non-global zone")); 349799653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3498fa9e4066Sahrens } 3499fa9e4066Sahrens 3500cdf5b4caSmmusante if (recursive) { 3501cdf5b4caSmmusante 3502f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3503f0c5ee21Smmusante if (parentname == NULL) { 3504f0c5ee21Smmusante ret = -1; 3505f0c5ee21Smmusante goto error; 3506f0c5ee21Smmusante } 3507cdf5b4caSmmusante delim = strchr(parentname, '@'); 3508cdf5b4caSmmusante *delim = '\0'; 3509990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3510cdf5b4caSmmusante if (zhrp == NULL) { 3511f0c5ee21Smmusante ret = -1; 3512f0c5ee21Smmusante goto error; 3513cdf5b4caSmmusante } 3514cdf5b4caSmmusante 3515cdf5b4caSmmusante } else { 35160069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 351799653d4eSeschrock return (-1); 3518fa9e4066Sahrens 3519fa9e4066Sahrens if (changelist_haszonedchild(cl)) { 352099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 352199653d4eSeschrock "child dataset with inherited mountpoint is used " 352299653d4eSeschrock "in a non-global zone")); 3523e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3524fa9e4066Sahrens goto error; 3525fa9e4066Sahrens } 3526fa9e4066Sahrens 3527fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 3528fa9e4066Sahrens goto error; 3529cdf5b4caSmmusante } 3530fa9e4066Sahrens 3531e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3532fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3533fa9e4066Sahrens else 3534fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3535fa9e4066Sahrens 353698579b20Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3537e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 353898579b20Snd150628 3539cdf5b4caSmmusante zc.zc_cookie = recursive; 3540cdf5b4caSmmusante 3541ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3542cdf5b4caSmmusante /* 3543cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3544cdf5b4caSmmusante * be in zc.zc_name 3545cdf5b4caSmmusante */ 3546cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35473cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3548cdf5b4caSmmusante 3549cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3550cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3551cdf5b4caSmmusante "a child dataset already has a snapshot " 3552cdf5b4caSmmusante "with the new name")); 3553a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3554cdf5b4caSmmusante } else { 355599653d4eSeschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3556cdf5b4caSmmusante } 3557fa9e4066Sahrens 3558fa9e4066Sahrens /* 3559fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3560fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3561fa9e4066Sahrens */ 3562681d9761SEric Taylor if (!recursive) 3563fa9e4066Sahrens (void) changelist_postfix(cl); 3564cdf5b4caSmmusante } else { 3565681d9761SEric Taylor if (!recursive) { 3566fa9e4066Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 3567fa9e4066Sahrens ret = changelist_postfix(cl); 3568fa9e4066Sahrens } 3569cdf5b4caSmmusante } 3570fa9e4066Sahrens 3571fa9e4066Sahrens error: 3572cdf5b4caSmmusante if (parentname) { 3573cdf5b4caSmmusante free(parentname); 3574cdf5b4caSmmusante } 3575cdf5b4caSmmusante if (zhrp) { 3576cdf5b4caSmmusante zfs_close(zhrp); 3577cdf5b4caSmmusante } 3578cdf5b4caSmmusante if (cl) { 3579fa9e4066Sahrens changelist_free(cl); 3580cdf5b4caSmmusante } 3581fa9e4066Sahrens return (ret); 3582fa9e4066Sahrens } 3583fa9e4066Sahrens 3584e9dbad6fSeschrock nvlist_t * 3585e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3586e9dbad6fSeschrock { 3587e9dbad6fSeschrock return (zhp->zfs_user_props); 3588e9dbad6fSeschrock } 3589e9dbad6fSeschrock 359092241e0bSTom Erickson nvlist_t * 359192241e0bSTom Erickson zfs_get_recvd_props(zfs_handle_t *zhp) 359292241e0bSTom Erickson { 359392241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 359492241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 359592241e0bSTom Erickson return (NULL); 359692241e0bSTom Erickson return (zhp->zfs_recvd_props); 359792241e0bSTom Erickson } 359892241e0bSTom Erickson 3599e9dbad6fSeschrock /* 3600e9dbad6fSeschrock * This function is used by 'zfs list' to determine the exact set of columns to 3601e9dbad6fSeschrock * display, and their maximum widths. This does two main things: 3602e9dbad6fSeschrock * 3603e9dbad6fSeschrock * - If this is a list of all properties, then expand the list to include 3604e9dbad6fSeschrock * all native properties, and set a flag so that for each dataset we look 3605e9dbad6fSeschrock * for new unique user properties and add them to the list. 3606e9dbad6fSeschrock * 3607e9dbad6fSeschrock * - For non fixed-width properties, keep track of the maximum width seen 360892241e0bSTom Erickson * so that we can size the column appropriately. If the user has 360992241e0bSTom Erickson * requested received property values, we also need to compute the width 361092241e0bSTom Erickson * of the RECEIVED column. 3611e9dbad6fSeschrock */ 3612e9dbad6fSeschrock int 361392241e0bSTom Erickson zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 3614e9dbad6fSeschrock { 3615e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3616990b4856Slling zprop_list_t *entry; 3617990b4856Slling zprop_list_t **last, **start; 3618e9dbad6fSeschrock nvlist_t *userprops, *propval; 3619e9dbad6fSeschrock nvpair_t *elem; 3620e9dbad6fSeschrock char *strval; 3621e9dbad6fSeschrock char buf[ZFS_MAXPROPLEN]; 3622e9dbad6fSeschrock 3623990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3624e9dbad6fSeschrock return (-1); 3625e9dbad6fSeschrock 3626e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3627e9dbad6fSeschrock 3628e9dbad6fSeschrock entry = *plp; 3629e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3630e9dbad6fSeschrock /* 3631e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3632e9dbad6fSeschrock * start by incrementing our list pointer to the first 3633e9dbad6fSeschrock * non-native property. 3634e9dbad6fSeschrock */ 3635e9dbad6fSeschrock start = plp; 3636e9dbad6fSeschrock while (*start != NULL) { 3637990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3638e9dbad6fSeschrock break; 3639e9dbad6fSeschrock start = &(*start)->pl_next; 3640e9dbad6fSeschrock } 3641e9dbad6fSeschrock 3642e9dbad6fSeschrock elem = NULL; 3643e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3644e9dbad6fSeschrock /* 3645e9dbad6fSeschrock * See if we've already found this property in our list. 3646e9dbad6fSeschrock */ 3647e9dbad6fSeschrock for (last = start; *last != NULL; 3648e9dbad6fSeschrock last = &(*last)->pl_next) { 3649e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3650e9dbad6fSeschrock nvpair_name(elem)) == 0) 3651e9dbad6fSeschrock break; 3652e9dbad6fSeschrock } 3653e9dbad6fSeschrock 3654e9dbad6fSeschrock if (*last == NULL) { 3655e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3656990b4856Slling sizeof (zprop_list_t))) == NULL || 3657e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3658e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3659e9dbad6fSeschrock free(entry); 3660e9dbad6fSeschrock return (-1); 3661e9dbad6fSeschrock } 3662e9dbad6fSeschrock 3663990b4856Slling entry->pl_prop = ZPROP_INVAL; 3664e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3665e9dbad6fSeschrock entry->pl_all = B_TRUE; 3666e9dbad6fSeschrock *last = entry; 3667e9dbad6fSeschrock } 3668e9dbad6fSeschrock } 3669e9dbad6fSeschrock } 3670e9dbad6fSeschrock 3671e9dbad6fSeschrock /* 3672e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3673e9dbad6fSeschrock */ 3674e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3675e9dbad6fSeschrock if (entry->pl_fixed) 3676e9dbad6fSeschrock continue; 3677e9dbad6fSeschrock 3678990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3679e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3680e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3681e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3682e9dbad6fSeschrock entry->pl_width = strlen(buf); 3683e9dbad6fSeschrock } 368492241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 368592241e0bSTom Erickson zfs_prop_to_name(entry->pl_prop), 368692241e0bSTom Erickson buf, sizeof (buf), B_FALSE) == 0) 368792241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 368892241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 368992241e0bSTom Erickson } else { 369092241e0bSTom Erickson if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 369192241e0bSTom Erickson &propval) == 0) { 3692e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3693990b4856Slling ZPROP_VALUE, &strval) == 0); 3694e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3695e9dbad6fSeschrock entry->pl_width = strlen(strval); 3696e9dbad6fSeschrock } 369792241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 369892241e0bSTom Erickson entry->pl_user_prop, 369992241e0bSTom Erickson buf, sizeof (buf), B_FALSE) == 0) 370092241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 370192241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 370292241e0bSTom Erickson } 3703e9dbad6fSeschrock } 3704e9dbad6fSeschrock 3705e9dbad6fSeschrock return (0); 3706e9dbad6fSeschrock } 3707ecd6cf80Smarks 3708ecd6cf80Smarks int 3709ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 3710ecd6cf80Smarks { 3711ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3712ecd6cf80Smarks nvlist_t *nvp; 3713ecd6cf80Smarks gid_t gid; 3714ecd6cf80Smarks uid_t uid; 3715ecd6cf80Smarks const gid_t *groups; 3716ecd6cf80Smarks int group_cnt; 3717ecd6cf80Smarks int error; 3718ecd6cf80Smarks 3719ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 3720ecd6cf80Smarks return (no_memory(hdl)); 3721ecd6cf80Smarks 3722ecd6cf80Smarks uid = ucred_geteuid(cred); 3723ecd6cf80Smarks gid = ucred_getegid(cred); 3724ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 3725ecd6cf80Smarks 3726ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 3727ecd6cf80Smarks return (1); 3728ecd6cf80Smarks 3729ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 3730ecd6cf80Smarks nvlist_free(nvp); 3731ecd6cf80Smarks return (1); 3732ecd6cf80Smarks } 3733ecd6cf80Smarks 3734ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 3735ecd6cf80Smarks nvlist_free(nvp); 3736ecd6cf80Smarks return (1); 3737ecd6cf80Smarks } 3738ecd6cf80Smarks 3739ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 3740ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 3741ecd6cf80Smarks nvlist_free(nvp); 3742ecd6cf80Smarks return (1); 3743ecd6cf80Smarks } 3744ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3745ecd6cf80Smarks 3746990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 3747ecd6cf80Smarks return (-1); 3748ecd6cf80Smarks 3749ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 3750ecd6cf80Smarks nvlist_free(nvp); 3751ecd6cf80Smarks return (error); 3752ecd6cf80Smarks } 3753ecd6cf80Smarks 3754ecd6cf80Smarks int 3755ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3756743a77edSAlan Wright char *resource, void *export, void *sharetab, 3757743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3758ecd6cf80Smarks { 3759ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3760ecd6cf80Smarks int error; 3761ecd6cf80Smarks 3762ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3763ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3764743a77edSAlan Wright if (resource) 3765743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3766ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3767ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3768da6c28aaSamw zc.zc_share.z_sharetype = operation; 3769ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3770ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3771ecd6cf80Smarks return (error); 3772ecd6cf80Smarks } 37732e5e9e19SSanjeev Bagewadi 37742e5e9e19SSanjeev Bagewadi void 37752e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37762e5e9e19SSanjeev Bagewadi { 37772e5e9e19SSanjeev Bagewadi nvpair_t *curr; 37782e5e9e19SSanjeev Bagewadi 37792e5e9e19SSanjeev Bagewadi /* 37802e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 37812e5e9e19SSanjeev Bagewadi * properties. 37822e5e9e19SSanjeev Bagewadi */ 37832e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 37842e5e9e19SSanjeev Bagewadi 37852e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37862e5e9e19SSanjeev Bagewadi 37872e5e9e19SSanjeev Bagewadi while (curr) { 37882e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37892e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37902e5e9e19SSanjeev Bagewadi 379114843421SMatthew Ahrens /* 3792faaa6415SEric Schrock * User properties will result in ZPROP_INVAL, and since we 3793faaa6415SEric Schrock * only know how to prune standard ZFS properties, we always 3794faaa6415SEric Schrock * leave these in the list. This can also happen if we 3795faaa6415SEric Schrock * encounter an unknown DSL property (when running older 3796faaa6415SEric Schrock * software, for example). 379714843421SMatthew Ahrens */ 379814843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 37992e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 38002e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 38012e5e9e19SSanjeev Bagewadi curr = next; 38022e5e9e19SSanjeev Bagewadi } 38032e5e9e19SSanjeev Bagewadi } 3804743a77edSAlan Wright 3805743a77edSAlan Wright static int 3806743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3807743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3808743a77edSAlan Wright { 3809743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3810743a77edSAlan Wright nvlist_t *nvlist = NULL; 3811743a77edSAlan Wright int error; 3812743a77edSAlan Wright 3813743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3814743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3815743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3816743a77edSAlan Wright 3817743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3818743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3819743a77edSAlan Wright (void) no_memory(hdl); 3820743a77edSAlan Wright return (NULL); 3821743a77edSAlan Wright } 3822743a77edSAlan Wright } 3823743a77edSAlan Wright 3824743a77edSAlan Wright switch (cmd) { 3825743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3826743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3827743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3828743a77edSAlan Wright break; 3829743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3830743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3831743a77edSAlan Wright resource1) != 0) { 3832743a77edSAlan Wright (void) no_memory(hdl); 3833743a77edSAlan Wright return (-1); 3834743a77edSAlan Wright } 3835743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 3836743a77edSAlan Wright resource2) != 0) { 3837743a77edSAlan Wright (void) no_memory(hdl); 3838743a77edSAlan Wright return (-1); 3839743a77edSAlan Wright } 3840743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 3841743a77edSAlan Wright nvlist_free(nvlist); 3842743a77edSAlan Wright return (-1); 3843743a77edSAlan Wright } 3844743a77edSAlan Wright break; 3845743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3846743a77edSAlan Wright break; 3847743a77edSAlan Wright default: 3848743a77edSAlan Wright return (-1); 3849743a77edSAlan Wright } 3850743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 3851743a77edSAlan Wright if (nvlist) 3852743a77edSAlan Wright nvlist_free(nvlist); 3853743a77edSAlan Wright return (error); 3854743a77edSAlan Wright } 3855743a77edSAlan Wright 3856743a77edSAlan Wright int 3857743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 3858743a77edSAlan Wright char *path, char *resource) 3859743a77edSAlan Wright { 3860743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 3861743a77edSAlan Wright resource, NULL)); 3862743a77edSAlan Wright } 3863743a77edSAlan Wright 3864743a77edSAlan Wright int 3865743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 3866743a77edSAlan Wright char *path, char *resource) 3867743a77edSAlan Wright { 3868743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 3869743a77edSAlan Wright resource, NULL)); 3870743a77edSAlan Wright } 3871743a77edSAlan Wright 3872743a77edSAlan Wright int 3873743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 3874743a77edSAlan Wright { 3875743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 3876743a77edSAlan Wright NULL, NULL)); 3877743a77edSAlan Wright } 3878743a77edSAlan Wright 3879743a77edSAlan Wright int 3880743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 3881743a77edSAlan Wright char *oldname, char *newname) 3882743a77edSAlan Wright { 3883743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 3884743a77edSAlan Wright oldname, newname)); 3885743a77edSAlan Wright } 388614843421SMatthew Ahrens 388714843421SMatthew Ahrens int 388814843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 388914843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 389014843421SMatthew Ahrens { 389114843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 389214843421SMatthew Ahrens int error; 389314843421SMatthew Ahrens zfs_useracct_t buf[100]; 389414843421SMatthew Ahrens 389514843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 389614843421SMatthew Ahrens 389714843421SMatthew Ahrens zc.zc_objset_type = type; 389814843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 389914843421SMatthew Ahrens 390014843421SMatthew Ahrens /* CONSTCOND */ 390114843421SMatthew Ahrens while (1) { 390214843421SMatthew Ahrens zfs_useracct_t *zua = buf; 390314843421SMatthew Ahrens 390414843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 390514843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 390614843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 390714843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 390814843421SMatthew Ahrens break; 390914843421SMatthew Ahrens 391014843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 39110aea4b19SMatthew Ahrens error = func(arg, zua->zu_domain, zua->zu_rid, 39120aea4b19SMatthew Ahrens zua->zu_space); 39130aea4b19SMatthew Ahrens if (error != 0) 39140aea4b19SMatthew Ahrens return (error); 391514843421SMatthew Ahrens zua++; 391614843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 391714843421SMatthew Ahrens } 391814843421SMatthew Ahrens } 391914843421SMatthew Ahrens 392014843421SMatthew Ahrens return (error); 392114843421SMatthew Ahrens } 3922842727c2SChris Kirby 3923842727c2SChris Kirby int 3924842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 3925*818119b8SChris Kirby boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 3926842727c2SChris Kirby { 3927842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 3928842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 3929842727c2SChris Kirby 3930842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3931842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3932ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 3933ca45db41SChris Kirby >= sizeof (zc.zc_string)) 3934ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 3935842727c2SChris Kirby zc.zc_cookie = recursive; 3936ca45db41SChris Kirby zc.zc_temphold = temphold; 3937842727c2SChris Kirby 3938842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 3939842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 3940842727c2SChris Kirby 3941842727c2SChris Kirby /* 3942842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 3943842727c2SChris Kirby * zc.zc_name. 3944842727c2SChris Kirby */ 3945842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3946842727c2SChris Kirby "cannot hold '%s@%s'"), zc.zc_name, snapname); 3947842727c2SChris Kirby switch (errno) { 394815508ac0SChris Kirby case E2BIG: 394915508ac0SChris Kirby /* 395015508ac0SChris Kirby * Temporary tags wind up having the ds object id 395115508ac0SChris Kirby * prepended. So even if we passed the length check 395215508ac0SChris Kirby * above, it's still possible for the tag to wind 395315508ac0SChris Kirby * up being slightly too long. 395415508ac0SChris Kirby */ 395515508ac0SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 3956842727c2SChris Kirby case ENOTSUP: 3957842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3958842727c2SChris Kirby "pool must be upgraded")); 3959842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3960842727c2SChris Kirby case EINVAL: 3961842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3962842727c2SChris Kirby case EEXIST: 3963842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 3964*818119b8SChris Kirby case ENOENT: 3965*818119b8SChris Kirby if (enoent_ok) 3966*818119b8SChris Kirby return (0); 3967*818119b8SChris Kirby /* FALLTHROUGH */ 3968842727c2SChris Kirby default: 3969842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 3970842727c2SChris Kirby } 3971842727c2SChris Kirby } 3972842727c2SChris Kirby 3973842727c2SChris Kirby return (0); 3974842727c2SChris Kirby } 3975842727c2SChris Kirby 3976ca45db41SChris Kirby struct hold_range_arg { 3977ca45db41SChris Kirby zfs_handle_t *origin; 3978ca45db41SChris Kirby const char *fromsnap; 3979ca45db41SChris Kirby const char *tosnap; 3980ca45db41SChris Kirby char lastsnapheld[ZFS_MAXNAMELEN]; 3981ca45db41SChris Kirby const char *tag; 3982ca45db41SChris Kirby boolean_t temphold; 3983ca45db41SChris Kirby boolean_t seento; 3984ca45db41SChris Kirby boolean_t seenfrom; 3985ca45db41SChris Kirby boolean_t holding; 3986ca45db41SChris Kirby }; 3987ca45db41SChris Kirby 3988ca45db41SChris Kirby static int 3989ca45db41SChris Kirby zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 3990ca45db41SChris Kirby { 3991ca45db41SChris Kirby struct hold_range_arg *hra = arg; 3992ca45db41SChris Kirby const char *thissnap; 3993ca45db41SChris Kirby int error; 3994ca45db41SChris Kirby 3995ca45db41SChris Kirby thissnap = strchr(zfs_get_name(zhp), '@') + 1; 3996ca45db41SChris Kirby 3997ca45db41SChris Kirby if (hra->fromsnap && !hra->seenfrom && 3998ca45db41SChris Kirby strcmp(hra->fromsnap, thissnap) == 0) 3999ca45db41SChris Kirby hra->seenfrom = B_TRUE; 4000ca45db41SChris Kirby 4001ca45db41SChris Kirby /* snap is older or newer than the desired range, ignore it */ 4002ca45db41SChris Kirby if (hra->seento || !hra->seenfrom) { 4003ca45db41SChris Kirby zfs_close(zhp); 4004ca45db41SChris Kirby return (0); 4005ca45db41SChris Kirby } 4006ca45db41SChris Kirby 4007ca45db41SChris Kirby if (hra->holding) { 4008*818119b8SChris Kirby /* We could be racing with destroy, so ignore ENOENT. */ 4009ca45db41SChris Kirby error = zfs_hold(hra->origin, thissnap, hra->tag, B_FALSE, 4010*818119b8SChris Kirby hra->temphold, B_TRUE); 4011ca45db41SChris Kirby if (error == 0) { 4012ca45db41SChris Kirby (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 4013ca45db41SChris Kirby sizeof (hra->lastsnapheld)); 4014ca45db41SChris Kirby } 4015ca45db41SChris Kirby } else { 4016ca45db41SChris Kirby error = zfs_release(hra->origin, thissnap, hra->tag, B_FALSE); 4017ca45db41SChris Kirby } 4018ca45db41SChris Kirby 4019ca45db41SChris Kirby if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 4020ca45db41SChris Kirby hra->seento = B_TRUE; 4021ca45db41SChris Kirby 4022ca45db41SChris Kirby zfs_close(zhp); 4023ca45db41SChris Kirby return (error); 4024ca45db41SChris Kirby } 4025ca45db41SChris Kirby 4026ca45db41SChris Kirby /* 4027ca45db41SChris Kirby * Add a user hold on the set of snapshots starting with fromsnap up to 4028ca45db41SChris Kirby * and including tosnap. If we're unable to to acquire a particular hold, 4029ca45db41SChris Kirby * undo any holds up to that point. 4030ca45db41SChris Kirby */ 4031ca45db41SChris Kirby int 4032ca45db41SChris Kirby zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 4033ca45db41SChris Kirby const char *tag, boolean_t temphold) 4034ca45db41SChris Kirby { 4035ca45db41SChris Kirby struct hold_range_arg arg = { 0 }; 4036ca45db41SChris Kirby int error; 4037ca45db41SChris Kirby 4038ca45db41SChris Kirby arg.origin = zhp; 4039ca45db41SChris Kirby arg.fromsnap = fromsnap; 4040ca45db41SChris Kirby arg.tosnap = tosnap; 4041ca45db41SChris Kirby arg.tag = tag; 4042ca45db41SChris Kirby arg.temphold = temphold; 4043ca45db41SChris Kirby arg.holding = B_TRUE; 4044ca45db41SChris Kirby 4045ca45db41SChris Kirby error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 4046ca45db41SChris Kirby 4047ca45db41SChris Kirby /* 4048ca45db41SChris Kirby * Make sure we either hold the entire range or none. 4049ca45db41SChris Kirby */ 4050ca45db41SChris Kirby if (error && arg.lastsnapheld[0] != '\0') { 4051ca45db41SChris Kirby (void) zfs_release_range(zhp, fromsnap, 4052ca45db41SChris Kirby (const char *)arg.lastsnapheld, tag); 4053ca45db41SChris Kirby } 4054ca45db41SChris Kirby return (error); 4055ca45db41SChris Kirby } 4056ca45db41SChris Kirby 4057842727c2SChris Kirby int 4058842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4059842727c2SChris Kirby boolean_t recursive) 4060842727c2SChris Kirby { 4061842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 4062842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 4063842727c2SChris Kirby 4064842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4065842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4066ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 4067ca45db41SChris Kirby >= sizeof (zc.zc_string)) 4068ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 4069842727c2SChris Kirby zc.zc_cookie = recursive; 4070842727c2SChris Kirby 4071842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 4072842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 4073842727c2SChris Kirby 4074842727c2SChris Kirby /* 4075842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 4076842727c2SChris Kirby * zc.zc_name. 4077842727c2SChris Kirby */ 4078842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4079842727c2SChris Kirby "cannot release '%s@%s'"), zc.zc_name, snapname); 4080842727c2SChris Kirby switch (errno) { 4081842727c2SChris Kirby case ESRCH: 4082842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 4083842727c2SChris Kirby case ENOTSUP: 4084842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4085842727c2SChris Kirby "pool must be upgraded")); 4086842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4087842727c2SChris Kirby case EINVAL: 4088842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4089842727c2SChris Kirby default: 4090842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4091842727c2SChris Kirby } 4092842727c2SChris Kirby } 4093842727c2SChris Kirby 4094842727c2SChris Kirby return (0); 4095842727c2SChris Kirby } 4096ca45db41SChris Kirby 4097ca45db41SChris Kirby /* 4098ca45db41SChris Kirby * Release a user hold from the set of snapshots starting with fromsnap 4099ca45db41SChris Kirby * up to and including tosnap. 4100ca45db41SChris Kirby */ 4101ca45db41SChris Kirby int 4102ca45db41SChris Kirby zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 4103ca45db41SChris Kirby const char *tag) 4104ca45db41SChris Kirby { 4105ca45db41SChris Kirby struct hold_range_arg arg = { 0 }; 4106ca45db41SChris Kirby 4107ca45db41SChris Kirby arg.origin = zhp; 4108ca45db41SChris Kirby arg.fromsnap = fromsnap; 4109ca45db41SChris Kirby arg.tosnap = tosnap; 4110ca45db41SChris Kirby arg.tag = tag; 4111ca45db41SChris Kirby 4112ca45db41SChris Kirby return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 4113ca45db41SChris Kirby } 4114