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 /* 2336db6475SEric Taylor * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24fa9e4066Sahrens */ 25fa9e4066Sahrens 26fa9e4066Sahrens #include <ctype.h> 27fa9e4066Sahrens #include <errno.h> 28fa9e4066Sahrens #include <libintl.h> 29fa9e4066Sahrens #include <math.h> 30fa9e4066Sahrens #include <stdio.h> 31fa9e4066Sahrens #include <stdlib.h> 32fa9e4066Sahrens #include <strings.h> 33fa9e4066Sahrens #include <unistd.h> 343cb34c60Sahrens #include <stddef.h> 35fa9e4066Sahrens #include <zone.h> 3699653d4eSeschrock #include <fcntl.h> 37fa9e4066Sahrens #include <sys/mntent.h> 38b12a1c38Slling #include <sys/mount.h> 39ecd6cf80Smarks #include <priv.h> 40ecd6cf80Smarks #include <pwd.h> 41ecd6cf80Smarks #include <grp.h> 42ecd6cf80Smarks #include <stddef.h> 43ecd6cf80Smarks #include <ucred.h> 4414843421SMatthew Ahrens #include <idmap.h> 4514843421SMatthew Ahrens #include <aclutils.h> 463b12c289SMatthew Ahrens #include <directory.h> 47fa9e4066Sahrens 48c1449561SEric Taylor #include <sys/dnode.h> 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 4619fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 4629fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States return (-1); 4639fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 464ebedde84SEric Taylor return (0); 465ebedde84SEric Taylor } 466ebedde84SEric Taylor 467ebedde84SEric Taylor zfs_handle_t * 468ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 469ebedde84SEric Taylor { 470ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 471ebedde84SEric Taylor 472ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 473ebedde84SEric Taylor 474ebedde84SEric Taylor if (zhp == NULL) 475ebedde84SEric Taylor return (NULL); 476ebedde84SEric Taylor 477ebedde84SEric Taylor zhp->zfs_hdl = hdl; 478ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 479ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 480ebedde84SEric Taylor free(zhp); 481ebedde84SEric Taylor return (NULL); 482ebedde84SEric Taylor } 483ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 484ebedde84SEric Taylor zcmd_free_nvlists(&zc); 485ebedde84SEric Taylor free(zhp); 486ebedde84SEric Taylor return (NULL); 487ebedde84SEric Taylor } 488ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 489ebedde84SEric Taylor free(zhp); 490ebedde84SEric Taylor zhp = NULL; 491ebedde84SEric Taylor } 492ebedde84SEric Taylor zcmd_free_nvlists(&zc); 493ebedde84SEric Taylor return (zhp); 494ebedde84SEric Taylor } 495ebedde84SEric Taylor 496ebedde84SEric Taylor static zfs_handle_t * 497ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 498ebedde84SEric Taylor { 499ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 500ebedde84SEric Taylor 501ebedde84SEric Taylor if (zhp == NULL) 502ebedde84SEric Taylor return (NULL); 503ebedde84SEric Taylor 504ebedde84SEric Taylor zhp->zfs_hdl = hdl; 505ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 506ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 507ebedde84SEric Taylor free(zhp); 508ebedde84SEric Taylor return (NULL); 509ebedde84SEric Taylor } 510fa9e4066Sahrens return (zhp); 511fa9e4066Sahrens } 512fa9e4066Sahrens 513fa9e4066Sahrens /* 514fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 515fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 516fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 517fa9e4066Sahrens */ 518fa9e4066Sahrens zfs_handle_t * 51999653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 520fa9e4066Sahrens { 521fa9e4066Sahrens zfs_handle_t *zhp; 52299653d4eSeschrock char errbuf[1024]; 52399653d4eSeschrock 52499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 52599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 526fa9e4066Sahrens 527fa9e4066Sahrens /* 52899653d4eSeschrock * Validate the name before we even try to open it. 529fa9e4066Sahrens */ 530f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 53199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 53299653d4eSeschrock "invalid dataset name")); 53399653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 534fa9e4066Sahrens return (NULL); 535fa9e4066Sahrens } 536fa9e4066Sahrens 537fa9e4066Sahrens /* 538fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 539fa9e4066Sahrens */ 540fa9e4066Sahrens errno = 0; 54199653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 542ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 543fa9e4066Sahrens return (NULL); 544fa9e4066Sahrens } 545fa9e4066Sahrens 546fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 54799653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 54894de1d4cSeschrock zfs_close(zhp); 549fa9e4066Sahrens return (NULL); 550fa9e4066Sahrens } 551fa9e4066Sahrens 552fa9e4066Sahrens return (zhp); 553fa9e4066Sahrens } 554fa9e4066Sahrens 555fa9e4066Sahrens /* 556fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 557fa9e4066Sahrens */ 558fa9e4066Sahrens void 559fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 560fa9e4066Sahrens { 561fa9e4066Sahrens if (zhp->zfs_mntopts) 562fa9e4066Sahrens free(zhp->zfs_mntopts); 56399653d4eSeschrock nvlist_free(zhp->zfs_props); 564e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 56592241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 566fa9e4066Sahrens free(zhp); 567fa9e4066Sahrens } 568fa9e4066Sahrens 569ebedde84SEric Taylor typedef struct mnttab_node { 570ebedde84SEric Taylor struct mnttab mtn_mt; 571ebedde84SEric Taylor avl_node_t mtn_node; 572ebedde84SEric Taylor } mnttab_node_t; 573ebedde84SEric Taylor 574ebedde84SEric Taylor static int 575ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 576ebedde84SEric Taylor { 577ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 578ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 579ebedde84SEric Taylor int rv; 580ebedde84SEric Taylor 581ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 582ebedde84SEric Taylor 583ebedde84SEric Taylor if (rv == 0) 584ebedde84SEric Taylor return (0); 585ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 586ebedde84SEric Taylor } 587ebedde84SEric Taylor 588ebedde84SEric Taylor void 589ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 590ebedde84SEric Taylor { 591ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 592ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 593ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 594b2634b9cSEric Taylor } 595b2634b9cSEric Taylor 596b2634b9cSEric Taylor void 597b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 598b2634b9cSEric Taylor { 599b2634b9cSEric Taylor struct mnttab entry; 600ebedde84SEric Taylor 601ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 602ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 603ebedde84SEric Taylor mnttab_node_t *mtn; 604ebedde84SEric Taylor 605ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 606ebedde84SEric Taylor continue; 607ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 608ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 609ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 610ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 611ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 612ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 613ebedde84SEric Taylor } 614ebedde84SEric Taylor } 615ebedde84SEric Taylor 616ebedde84SEric Taylor void 617ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 618ebedde84SEric Taylor { 619ebedde84SEric Taylor void *cookie = NULL; 620ebedde84SEric Taylor mnttab_node_t *mtn; 621ebedde84SEric Taylor 622ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 623ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 624ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 625ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 626ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 627ebedde84SEric Taylor free(mtn); 628ebedde84SEric Taylor } 629ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 630ebedde84SEric Taylor } 631ebedde84SEric Taylor 632b2634b9cSEric Taylor void 633b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 634b2634b9cSEric Taylor { 635b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 636b2634b9cSEric Taylor } 637b2634b9cSEric Taylor 638ebedde84SEric Taylor int 639ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 640ebedde84SEric Taylor struct mnttab *entry) 641ebedde84SEric Taylor { 642ebedde84SEric Taylor mnttab_node_t find; 643ebedde84SEric Taylor mnttab_node_t *mtn; 644ebedde84SEric Taylor 645b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 646b2634b9cSEric Taylor struct mnttab srch = { 0 }; 647b2634b9cSEric Taylor 648b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 649b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 650b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 651b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 652b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 653b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 654b2634b9cSEric Taylor return (0); 655b2634b9cSEric Taylor else 656b2634b9cSEric Taylor return (ENOENT); 657b2634b9cSEric Taylor } 658b2634b9cSEric Taylor 659ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 660b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 661ebedde84SEric Taylor 662ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 663ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 664ebedde84SEric Taylor if (mtn) { 665ebedde84SEric Taylor *entry = mtn->mtn_mt; 666ebedde84SEric Taylor return (0); 667ebedde84SEric Taylor } 668ebedde84SEric Taylor return (ENOENT); 669ebedde84SEric Taylor } 670ebedde84SEric Taylor 671ebedde84SEric Taylor void 672ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 673ebedde84SEric Taylor const char *mountp, const char *mntopts) 674ebedde84SEric Taylor { 675ebedde84SEric Taylor mnttab_node_t *mtn; 676ebedde84SEric Taylor 677ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 678ebedde84SEric Taylor return; 679ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 680ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 681ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 682ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 683ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 684ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 685ebedde84SEric Taylor } 686ebedde84SEric Taylor 687ebedde84SEric Taylor void 688ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 689ebedde84SEric Taylor { 690ebedde84SEric Taylor mnttab_node_t find; 691ebedde84SEric Taylor mnttab_node_t *ret; 692ebedde84SEric Taylor 693ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 694ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 695ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 696ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 697ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 698ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 699ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 700ebedde84SEric Taylor free(ret); 701ebedde84SEric Taylor } 702ebedde84SEric Taylor } 703ebedde84SEric Taylor 7047b97dc1aSrm160521 int 7057b97dc1aSrm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7067b97dc1aSrm160521 { 70729ab75c9Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7087b97dc1aSrm160521 7097b97dc1aSrm160521 if (zpool_handle == NULL) 7107b97dc1aSrm160521 return (-1); 7117b97dc1aSrm160521 7127b97dc1aSrm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7137b97dc1aSrm160521 ZPOOL_PROP_VERSION, NULL); 7147b97dc1aSrm160521 return (0); 7157b97dc1aSrm160521 } 7167b97dc1aSrm160521 7177b97dc1aSrm160521 /* 7187b97dc1aSrm160521 * The choice of reservation property depends on the SPA version. 7197b97dc1aSrm160521 */ 7207b97dc1aSrm160521 static int 7217b97dc1aSrm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7227b97dc1aSrm160521 { 7237b97dc1aSrm160521 int spa_version; 7247b97dc1aSrm160521 7257b97dc1aSrm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7267b97dc1aSrm160521 return (-1); 7277b97dc1aSrm160521 7287b97dc1aSrm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7297b97dc1aSrm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7307b97dc1aSrm160521 else 7317b97dc1aSrm160521 *resv_prop = ZFS_PROP_RESERVATION; 7327b97dc1aSrm160521 7337b97dc1aSrm160521 return (0); 7347b97dc1aSrm160521 } 7357b97dc1aSrm160521 736b1b8ab34Slling /* 737e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 738e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 739e9dbad6fSeschrock * strings. 740fa9e4066Sahrens */ 7410a48a24eStimh nvlist_t * 7420a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 743990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 744fa9e4066Sahrens { 745e9dbad6fSeschrock nvpair_t *elem; 746e9dbad6fSeschrock uint64_t intval; 747e9dbad6fSeschrock char *strval; 748990b4856Slling zfs_prop_t prop; 749e9dbad6fSeschrock nvlist_t *ret; 750da6c28aaSamw int chosen_normal = -1; 751da6c28aaSamw int chosen_utf = -1; 752990b4856Slling 753e9dbad6fSeschrock if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 754e9dbad6fSeschrock (void) no_memory(hdl); 755e9dbad6fSeschrock return (NULL); 756e9dbad6fSeschrock } 757fa9e4066Sahrens 75814843421SMatthew Ahrens /* 75914843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 76014843421SMatthew Ahrens */ 76114843421SMatthew Ahrens 762e9dbad6fSeschrock elem = NULL; 763e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 764990b4856Slling const char *propname = nvpair_name(elem); 76599653d4eSeschrock 76614843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 76714843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 768fa9e4066Sahrens /* 76914843421SMatthew Ahrens * This is a user property: make sure it's a 770990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 771990b4856Slling */ 772990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 773990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 774990b4856Slling "'%s' must be a string"), propname); 775990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 776990b4856Slling goto error; 777990b4856Slling } 778990b4856Slling 779990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 780e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 781e9dbad6fSeschrock "property name '%s' is too long"), 782e9dbad6fSeschrock propname); 783990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 784e9dbad6fSeschrock goto error; 785e9dbad6fSeschrock } 786e9dbad6fSeschrock 787e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 788e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 789e9dbad6fSeschrock (void) no_memory(hdl); 790e9dbad6fSeschrock goto error; 791e9dbad6fSeschrock } 792e9dbad6fSeschrock continue; 793e9dbad6fSeschrock } 794fa9e4066Sahrens 79514843421SMatthew Ahrens /* 79614843421SMatthew Ahrens * Currently, only user properties can be modified on 79714843421SMatthew Ahrens * snapshots. 79814843421SMatthew Ahrens */ 799bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 800bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 801bb0ade09Sahrens "this property can not be modified for snapshots")); 802bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 803bb0ade09Sahrens goto error; 804bb0ade09Sahrens } 805bb0ade09Sahrens 80614843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 80714843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 80814843421SMatthew Ahrens char newpropname[128]; 80914843421SMatthew Ahrens char domain[128]; 81014843421SMatthew Ahrens uint64_t rid; 81114843421SMatthew Ahrens uint64_t valary[3]; 81214843421SMatthew Ahrens 81314843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 81414843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 81514843421SMatthew Ahrens zfs_error_aux(hdl, 81614843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 81714843421SMatthew Ahrens "'%s' has an invalid user/group name"), 81814843421SMatthew Ahrens propname); 81914843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 82014843421SMatthew Ahrens goto error; 82114843421SMatthew Ahrens } 82214843421SMatthew Ahrens 82314843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 82414843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 82514843421SMatthew Ahrens zfs_error_aux(hdl, 82614843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 82714843421SMatthew Ahrens propname); 82814843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 82914843421SMatthew Ahrens errbuf); 83014843421SMatthew Ahrens goto error; 83114843421SMatthew Ahrens } 83214843421SMatthew Ahrens 83314843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 83414843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 83514843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 83614843421SMatthew Ahrens intval = 0; 83714843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 83814843421SMatthew Ahrens strval, &intval) != 0) { 83914843421SMatthew Ahrens (void) zfs_error(hdl, 84014843421SMatthew Ahrens EZFS_BADPROP, errbuf); 84114843421SMatthew Ahrens goto error; 84214843421SMatthew Ahrens } 84314843421SMatthew Ahrens } else if (nvpair_type(elem) == 84414843421SMatthew Ahrens DATA_TYPE_UINT64) { 84514843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 84614843421SMatthew Ahrens if (intval == 0) { 84714843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 84814843421SMatthew Ahrens "use 'none' to disable " 84914843421SMatthew Ahrens "userquota/groupquota")); 85014843421SMatthew Ahrens goto error; 85114843421SMatthew Ahrens } 85214843421SMatthew Ahrens } else { 85314843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 85414843421SMatthew Ahrens "'%s' must be a number"), propname); 85514843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 85614843421SMatthew Ahrens goto error; 85714843421SMatthew Ahrens } 85814843421SMatthew Ahrens 8592d5843dbSMatthew Ahrens /* 8602d5843dbSMatthew Ahrens * Encode the prop name as 8612d5843dbSMatthew Ahrens * userquota@<hex-rid>-domain, to make it easy 8622d5843dbSMatthew Ahrens * for the kernel to decode. 8632d5843dbSMatthew Ahrens */ 86414843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 8652d5843dbSMatthew Ahrens "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 8662d5843dbSMatthew Ahrens (longlong_t)rid, domain); 86714843421SMatthew Ahrens valary[0] = uqtype; 86814843421SMatthew Ahrens valary[1] = rid; 86914843421SMatthew Ahrens valary[2] = intval; 87014843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 87114843421SMatthew Ahrens valary, 3) != 0) { 87214843421SMatthew Ahrens (void) no_memory(hdl); 87314843421SMatthew Ahrens goto error; 87414843421SMatthew Ahrens } 87514843421SMatthew Ahrens continue; 87614843421SMatthew Ahrens } 87714843421SMatthew Ahrens 87814843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 87914843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 88014843421SMatthew Ahrens "invalid property '%s'"), propname); 88114843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 88214843421SMatthew Ahrens goto error; 88314843421SMatthew Ahrens } 88414843421SMatthew Ahrens 885e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 886e9dbad6fSeschrock zfs_error_aux(hdl, 887e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 888e9dbad6fSeschrock "apply to datasets of this type"), propname); 889e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 890e9dbad6fSeschrock goto error; 891e9dbad6fSeschrock } 892e9dbad6fSeschrock 893e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 894da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 895e9dbad6fSeschrock zfs_error_aux(hdl, 896e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 897e9dbad6fSeschrock propname); 898e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 899e9dbad6fSeschrock goto error; 900e9dbad6fSeschrock } 901e9dbad6fSeschrock 902990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 903990b4856Slling &strval, &intval, errbuf) != 0) 904e9dbad6fSeschrock goto error; 905e9dbad6fSeschrock 906e9dbad6fSeschrock /* 907e9dbad6fSeschrock * Perform some additional checks for specific properties. 908e9dbad6fSeschrock */ 909e9dbad6fSeschrock switch (prop) { 910e7437265Sahrens case ZFS_PROP_VERSION: 911e7437265Sahrens { 912e7437265Sahrens int version; 913e7437265Sahrens 914e7437265Sahrens if (zhp == NULL) 915e7437265Sahrens break; 916e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 917e7437265Sahrens if (intval < version) { 918e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 919e7437265Sahrens "Can not downgrade; already at version %u"), 920e7437265Sahrens version); 921e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 922e7437265Sahrens goto error; 923e7437265Sahrens } 924e7437265Sahrens break; 925e7437265Sahrens } 926e7437265Sahrens 927e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 928e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 929e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 930e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 931e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 932e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 933e9dbad6fSeschrock "'%s' must be power of 2 from %u " 934e9dbad6fSeschrock "to %uk"), propname, 935e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 936e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 937e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 938e9dbad6fSeschrock goto error; 939e9dbad6fSeschrock } 940e9dbad6fSeschrock break; 941e9dbad6fSeschrock 9424201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 9434201a95eSRic Aleshire { 9444201a95eSRic Aleshire /* 9454201a95eSRic Aleshire * Verify the mlslabel string and convert to 9464201a95eSRic Aleshire * internal hex label string. 9474201a95eSRic Aleshire */ 9484201a95eSRic Aleshire 9494201a95eSRic Aleshire m_label_t *new_sl; 9504201a95eSRic Aleshire char *hex = NULL; /* internal label string */ 9514201a95eSRic Aleshire 9524201a95eSRic Aleshire /* Default value is already OK. */ 9534201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 9544201a95eSRic Aleshire break; 9554201a95eSRic Aleshire 9564201a95eSRic Aleshire /* Verify the label can be converted to binary form */ 9574201a95eSRic Aleshire if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 9584201a95eSRic Aleshire (str_to_label(strval, &new_sl, MAC_LABEL, 9594201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1)) { 9604201a95eSRic Aleshire goto badlabel; 9614201a95eSRic Aleshire } 9624201a95eSRic Aleshire 9634201a95eSRic Aleshire /* Now translate to hex internal label string */ 9644201a95eSRic Aleshire if (label_to_str(new_sl, &hex, M_INTERNAL, 9654201a95eSRic Aleshire DEF_NAMES) != 0) { 9664201a95eSRic Aleshire if (hex) 9674201a95eSRic Aleshire free(hex); 9684201a95eSRic Aleshire goto badlabel; 9694201a95eSRic Aleshire } 9704201a95eSRic Aleshire m_label_free(new_sl); 9714201a95eSRic Aleshire 9724201a95eSRic Aleshire /* If string is already in internal form, we're done. */ 9734201a95eSRic Aleshire if (strcmp(strval, hex) == 0) { 9744201a95eSRic Aleshire free(hex); 9754201a95eSRic Aleshire break; 9764201a95eSRic Aleshire } 9774201a95eSRic Aleshire 9784201a95eSRic Aleshire /* Replace the label string with the internal form. */ 979569038c9SRic Aleshire (void) nvlist_remove(ret, zfs_prop_to_name(prop), 9804201a95eSRic Aleshire DATA_TYPE_STRING); 9814201a95eSRic Aleshire verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 9824201a95eSRic Aleshire hex) == 0); 9834201a95eSRic Aleshire free(hex); 9844201a95eSRic Aleshire 9854201a95eSRic Aleshire break; 9864201a95eSRic Aleshire 9874201a95eSRic Aleshire badlabel: 9884201a95eSRic Aleshire zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9894201a95eSRic Aleshire "invalid mlslabel '%s'"), strval); 9904201a95eSRic Aleshire (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9914201a95eSRic Aleshire m_label_free(new_sl); /* OK if null */ 9924201a95eSRic Aleshire goto error; 9934201a95eSRic Aleshire 9944201a95eSRic Aleshire } 9954201a95eSRic Aleshire 996e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 99789eef05eSrm160521 { 99889eef05eSrm160521 namecheck_err_t why; 99989eef05eSrm160521 1000e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1001e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1002e9dbad6fSeschrock break; 1003e9dbad6fSeschrock 100489eef05eSrm160521 if (mountpoint_namecheck(strval, &why)) { 100589eef05eSrm160521 switch (why) { 100689eef05eSrm160521 case NAME_ERR_LEADING_SLASH: 100789eef05eSrm160521 zfs_error_aux(hdl, 100889eef05eSrm160521 dgettext(TEXT_DOMAIN, 1009e9dbad6fSeschrock "'%s' must be an absolute path, " 1010e9dbad6fSeschrock "'none', or 'legacy'"), propname); 101189eef05eSrm160521 break; 101289eef05eSrm160521 case NAME_ERR_TOOLONG: 101389eef05eSrm160521 zfs_error_aux(hdl, 101489eef05eSrm160521 dgettext(TEXT_DOMAIN, 101589eef05eSrm160521 "component of '%s' is too long"), 101689eef05eSrm160521 propname); 101789eef05eSrm160521 break; 101889eef05eSrm160521 } 1019e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1020e9dbad6fSeschrock goto error; 1021e9dbad6fSeschrock } 102289eef05eSrm160521 } 102389eef05eSrm160521 1024f3861e1aSahl /*FALLTHRU*/ 1025e9dbad6fSeschrock 1026da6c28aaSamw case ZFS_PROP_SHARESMB: 1027f3861e1aSahl case ZFS_PROP_SHARENFS: 1028e9dbad6fSeschrock /* 1029da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1030da6c28aaSamw * properties, check if it can be set in a 1031da6c28aaSamw * global/non-global zone based on 1032f3861e1aSahl * the zoned property value: 1033fa9e4066Sahrens * 1034fa9e4066Sahrens * global zone non-global zone 1035f3861e1aSahl * -------------------------------------------------- 1036fa9e4066Sahrens * zoned=on mountpoint (no) mountpoint (yes) 1037fa9e4066Sahrens * sharenfs (no) sharenfs (no) 1038da6c28aaSamw * sharesmb (no) sharesmb (no) 1039fa9e4066Sahrens * 1040fa9e4066Sahrens * zoned=off mountpoint (yes) N/A 1041fa9e4066Sahrens * sharenfs (yes) 1042da6c28aaSamw * sharesmb (yes) 1043fa9e4066Sahrens */ 1044e9dbad6fSeschrock if (zoned) { 1045fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID) { 104699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1047e9dbad6fSeschrock "'%s' cannot be set on " 1048e9dbad6fSeschrock "dataset in a non-global zone"), 1049e9dbad6fSeschrock propname); 1050e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1051e9dbad6fSeschrock errbuf); 1052e9dbad6fSeschrock goto error; 1053da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1054da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 105599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1056e9dbad6fSeschrock "'%s' cannot be set in " 1057e9dbad6fSeschrock "a non-global zone"), propname); 1058e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1059e9dbad6fSeschrock errbuf); 1060e9dbad6fSeschrock goto error; 1061fa9e4066Sahrens } 1062fa9e4066Sahrens } else if (getzoneid() != GLOBAL_ZONEID) { 1063fa9e4066Sahrens /* 1064fa9e4066Sahrens * If zoned property is 'off', this must be in 106514843421SMatthew Ahrens * a global zone. If not, something is wrong. 1066fa9e4066Sahrens */ 106799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1068e9dbad6fSeschrock "'%s' cannot be set while dataset " 1069e9dbad6fSeschrock "'zoned' property is set"), propname); 1070e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1071e9dbad6fSeschrock goto error; 1072fa9e4066Sahrens } 1073f3861e1aSahl 107467331909Sdougm /* 107567331909Sdougm * At this point, it is legitimate to set the 107667331909Sdougm * property. Now we want to make sure that the 107767331909Sdougm * property value is valid if it is sharenfs. 107867331909Sdougm */ 1079da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1080da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 108167331909Sdougm strcmp(strval, "on") != 0 && 108267331909Sdougm strcmp(strval, "off") != 0) { 1083da6c28aaSamw zfs_share_proto_t proto; 1084da6c28aaSamw 1085da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1086da6c28aaSamw proto = PROTO_SMB; 1087da6c28aaSamw else 1088da6c28aaSamw proto = PROTO_NFS; 108967331909Sdougm 109067331909Sdougm /* 1091da6c28aaSamw * Must be an valid sharing protocol 1092da6c28aaSamw * option string so init the libshare 1093da6c28aaSamw * in order to enable the parser and 1094da6c28aaSamw * then parse the options. We use the 1095da6c28aaSamw * control API since we don't care about 1096da6c28aaSamw * the current configuration and don't 109767331909Sdougm * want the overhead of loading it 109867331909Sdougm * until we actually do something. 109967331909Sdougm */ 110067331909Sdougm 110167331909Sdougm if (zfs_init_libshare(hdl, 110267331909Sdougm SA_INIT_CONTROL_API) != SA_OK) { 1103fac3008cSeschrock /* 1104fac3008cSeschrock * An error occurred so we can't do 1105fac3008cSeschrock * anything 1106fac3008cSeschrock */ 110767331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 110867331909Sdougm "'%s' cannot be set: problem " 110967331909Sdougm "in share initialization"), 111067331909Sdougm propname); 1111fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1112fac3008cSeschrock errbuf); 111367331909Sdougm goto error; 111467331909Sdougm } 111567331909Sdougm 1116da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 111767331909Sdougm /* 111867331909Sdougm * There was an error in parsing so 111967331909Sdougm * deal with it by issuing an error 112067331909Sdougm * message and leaving after 112167331909Sdougm * uninitializing the the libshare 112267331909Sdougm * interface. 112367331909Sdougm */ 112467331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 112567331909Sdougm "'%s' cannot be set to invalid " 112667331909Sdougm "options"), propname); 1127fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1128fac3008cSeschrock errbuf); 112967331909Sdougm zfs_uninit_libshare(hdl); 113067331909Sdougm goto error; 113167331909Sdougm } 113267331909Sdougm zfs_uninit_libshare(hdl); 113367331909Sdougm } 113467331909Sdougm 1135f3861e1aSahl break; 1136da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1137da6c28aaSamw chosen_utf = (int)intval; 1138da6c28aaSamw break; 1139da6c28aaSamw case ZFS_PROP_NORMALIZE: 1140da6c28aaSamw chosen_normal = (int)intval; 1141da6c28aaSamw break; 1142fa9e4066Sahrens } 1143fa9e4066Sahrens 1144e9dbad6fSeschrock /* 1145e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1146e9dbad6fSeschrock * checks to enforce. 1147e9dbad6fSeschrock */ 1148e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1149e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1150e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1151e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1152e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1153e9dbad6fSeschrock char buf[64]; 1154e9dbad6fSeschrock 1155e9dbad6fSeschrock switch (prop) { 1156e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1157a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1158e9dbad6fSeschrock if (intval > volsize) { 1159e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1160e9dbad6fSeschrock "'%s' is greater than current " 1161e9dbad6fSeschrock "volume size"), propname); 1162e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1163e9dbad6fSeschrock errbuf); 1164e9dbad6fSeschrock goto error; 1165e9dbad6fSeschrock } 1166e9dbad6fSeschrock break; 1167e9dbad6fSeschrock 1168e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1169e9dbad6fSeschrock if (intval % blocksize != 0) { 1170e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1171e9dbad6fSeschrock sizeof (buf)); 1172e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1173e9dbad6fSeschrock "'%s' must be a multiple of " 1174e9dbad6fSeschrock "volume block size (%s)"), 1175e9dbad6fSeschrock propname, buf); 1176e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1177e9dbad6fSeschrock errbuf); 1178e9dbad6fSeschrock goto error; 1179e9dbad6fSeschrock } 1180e9dbad6fSeschrock 1181e9dbad6fSeschrock if (intval == 0) { 1182e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1183e9dbad6fSeschrock "'%s' cannot be zero"), 1184e9dbad6fSeschrock propname); 1185e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1186e9dbad6fSeschrock errbuf); 1187e9dbad6fSeschrock goto error; 1188e9dbad6fSeschrock } 1189f3861e1aSahl break; 1190e9dbad6fSeschrock } 1191e9dbad6fSeschrock } 1192e9dbad6fSeschrock } 1193e9dbad6fSeschrock 1194e9dbad6fSeschrock /* 1195da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1196da6c28aaSamw * enforce rejection of non-UTF8 names. 1197da6c28aaSamw * 1198da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1199da6c28aaSamw * was explicitly not chosen, it is an error. 1200da6c28aaSamw */ 1201de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1202da6c28aaSamw if (nvlist_add_uint64(ret, 1203da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1204da6c28aaSamw (void) no_memory(hdl); 1205da6c28aaSamw goto error; 1206da6c28aaSamw } 1207de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1208da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1209da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1210da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1211da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1212da6c28aaSamw goto error; 1213da6c28aaSamw } 1214e9dbad6fSeschrock return (ret); 1215e9dbad6fSeschrock 1216e9dbad6fSeschrock error: 1217e9dbad6fSeschrock nvlist_free(ret); 1218e9dbad6fSeschrock return (NULL); 1219e9dbad6fSeschrock } 1220e9dbad6fSeschrock 122136db6475SEric Taylor int 122236db6475SEric Taylor zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) 122336db6475SEric Taylor { 122436db6475SEric Taylor uint64_t old_volsize; 122536db6475SEric Taylor uint64_t new_volsize; 122636db6475SEric Taylor uint64_t old_reservation; 122736db6475SEric Taylor uint64_t new_reservation; 122836db6475SEric Taylor zfs_prop_t resv_prop; 122936db6475SEric Taylor 123036db6475SEric Taylor /* 123136db6475SEric Taylor * If this is an existing volume, and someone is setting the volsize, 123236db6475SEric Taylor * make sure that it matches the reservation, or add it if necessary. 123336db6475SEric Taylor */ 123436db6475SEric Taylor old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 123536db6475SEric Taylor if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 123636db6475SEric Taylor return (-1); 123736db6475SEric Taylor old_reservation = zfs_prop_get_int(zhp, resv_prop); 123836db6475SEric Taylor if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) != 123936db6475SEric Taylor old_reservation) || nvlist_lookup_uint64(nvl, 124036db6475SEric Taylor zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) { 124136db6475SEric Taylor return (0); 124236db6475SEric Taylor } 124336db6475SEric Taylor if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 124436db6475SEric Taylor &new_volsize) != 0) 124536db6475SEric Taylor return (-1); 124636db6475SEric Taylor new_reservation = zvol_volsize_to_reservation(new_volsize, 124736db6475SEric Taylor zhp->zfs_props); 124836db6475SEric Taylor if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), 124936db6475SEric Taylor new_reservation) != 0) { 125036db6475SEric Taylor (void) no_memory(zhp->zfs_hdl); 125136db6475SEric Taylor return (-1); 125236db6475SEric Taylor } 125336db6475SEric Taylor return (1); 125436db6475SEric Taylor } 125536db6475SEric Taylor 125692241e0bSTom Erickson void 125792241e0bSTom Erickson zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 125892241e0bSTom Erickson char *errbuf) 125992241e0bSTom Erickson { 126092241e0bSTom Erickson switch (err) { 126192241e0bSTom Erickson 126292241e0bSTom Erickson case ENOSPC: 126392241e0bSTom Erickson /* 126492241e0bSTom Erickson * For quotas and reservations, ENOSPC indicates 126592241e0bSTom Erickson * something different; setting a quota or reservation 126692241e0bSTom Erickson * doesn't use any disk space. 126792241e0bSTom Erickson */ 126892241e0bSTom Erickson switch (prop) { 126992241e0bSTom Erickson case ZFS_PROP_QUOTA: 127092241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 127192241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127292241e0bSTom Erickson "size is less than current used or " 127392241e0bSTom Erickson "reserved space")); 127492241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 127592241e0bSTom Erickson break; 127692241e0bSTom Erickson 127792241e0bSTom Erickson case ZFS_PROP_RESERVATION: 127892241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 127992241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128092241e0bSTom Erickson "size is greater than available space")); 128192241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128292241e0bSTom Erickson break; 128392241e0bSTom Erickson 128492241e0bSTom Erickson default: 128592241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 128692241e0bSTom Erickson break; 128792241e0bSTom Erickson } 128892241e0bSTom Erickson break; 128992241e0bSTom Erickson 129092241e0bSTom Erickson case EBUSY: 129192241e0bSTom Erickson (void) zfs_standard_error(hdl, EBUSY, errbuf); 129292241e0bSTom Erickson break; 129392241e0bSTom Erickson 129492241e0bSTom Erickson case EROFS: 129592241e0bSTom Erickson (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 129692241e0bSTom Erickson break; 129792241e0bSTom Erickson 129892241e0bSTom Erickson case ENOTSUP: 129992241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130092241e0bSTom Erickson "pool and or dataset must be upgraded to set this " 130192241e0bSTom Erickson "property or value")); 130292241e0bSTom Erickson (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 130392241e0bSTom Erickson break; 130492241e0bSTom Erickson 130592241e0bSTom Erickson case ERANGE: 130692241e0bSTom Erickson if (prop == ZFS_PROP_COMPRESSION) { 130792241e0bSTom Erickson (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130892241e0bSTom Erickson "property setting is not allowed on " 130992241e0bSTom Erickson "bootable datasets")); 131092241e0bSTom Erickson (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 131192241e0bSTom Erickson } else { 131292241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 131392241e0bSTom Erickson } 131492241e0bSTom Erickson break; 131592241e0bSTom Erickson 1316ab003da8SJim Dunham case EINVAL: 1317ab003da8SJim Dunham if (prop == ZPROP_INVAL) { 1318ab003da8SJim Dunham (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1319ab003da8SJim Dunham } else { 1320ab003da8SJim Dunham (void) zfs_standard_error(hdl, err, errbuf); 1321ab003da8SJim Dunham } 1322ab003da8SJim Dunham break; 1323ab003da8SJim Dunham 132492241e0bSTom Erickson case EOVERFLOW: 132592241e0bSTom Erickson /* 132692241e0bSTom Erickson * This platform can't address a volume this big. 132792241e0bSTom Erickson */ 132892241e0bSTom Erickson #ifdef _ILP32 132992241e0bSTom Erickson if (prop == ZFS_PROP_VOLSIZE) { 133092241e0bSTom Erickson (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 133192241e0bSTom Erickson break; 133292241e0bSTom Erickson } 133392241e0bSTom Erickson #endif 133492241e0bSTom Erickson /* FALLTHROUGH */ 133592241e0bSTom Erickson default: 133692241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 133792241e0bSTom Erickson } 133892241e0bSTom Erickson } 133992241e0bSTom Erickson 1340e9dbad6fSeschrock /* 1341e9dbad6fSeschrock * Given a property name and value, set the property for the given dataset. 1342e9dbad6fSeschrock */ 1343e9dbad6fSeschrock int 1344e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1345e9dbad6fSeschrock { 1346e9dbad6fSeschrock zfs_cmd_t zc = { 0 }; 1347e9dbad6fSeschrock int ret = -1; 1348e9dbad6fSeschrock prop_changelist_t *cl = NULL; 1349e9dbad6fSeschrock char errbuf[1024]; 1350e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1351e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1352e9dbad6fSeschrock zfs_prop_t prop; 13530068372bSMark J Musante boolean_t do_prefix; 13540068372bSMark J Musante uint64_t idx; 135536db6475SEric Taylor int added_resv; 1356e9dbad6fSeschrock 1357e9dbad6fSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1358e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1359e9dbad6fSeschrock zhp->zfs_name); 1360e9dbad6fSeschrock 1361e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1362e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1363e9dbad6fSeschrock (void) no_memory(hdl); 1364e9dbad6fSeschrock goto error; 1365e9dbad6fSeschrock } 1366e9dbad6fSeschrock 13670a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1368e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1369e9dbad6fSeschrock goto error; 1370990b4856Slling 1371e9dbad6fSeschrock nvlist_free(nvl); 1372e9dbad6fSeschrock nvl = realprops; 1373e9dbad6fSeschrock 1374e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1375e9dbad6fSeschrock 137636db6475SEric Taylor if (prop == ZFS_PROP_VOLSIZE) { 137736db6475SEric Taylor if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) 137836db6475SEric Taylor goto error; 137936db6475SEric Taylor } 138036db6475SEric Taylor 13810069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1382e9dbad6fSeschrock goto error; 1383fa9e4066Sahrens 1384fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 138599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1386fa9e4066Sahrens "child dataset with inherited mountpoint is used " 138799653d4eSeschrock "in a non-global zone")); 138899653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1389fa9e4066Sahrens goto error; 1390fa9e4066Sahrens } 1391fa9e4066Sahrens 13920068372bSMark J Musante /* 13930068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 13940068372bSMark J Musante * then we want to prevent unmounting & remounting it. 13950068372bSMark J Musante */ 13960068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13970068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 13980068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1399a227b7f4Shs24103 1400a227b7f4Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1401fa9e4066Sahrens goto error; 1402fa9e4066Sahrens 1403fa9e4066Sahrens /* 1404fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1405fa9e4066Sahrens */ 1406fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1407fa9e4066Sahrens 1408990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1409e9dbad6fSeschrock goto error; 1410e9dbad6fSeschrock 1411ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1412743a77edSAlan Wright 1413fa9e4066Sahrens if (ret != 0) { 141492241e0bSTom Erickson zfs_setprop_error(hdl, prop, errno, errbuf); 141536db6475SEric Taylor if (added_resv && errno == ENOSPC) { 141636db6475SEric Taylor /* clean up the volsize property we tried to set */ 141736db6475SEric Taylor uint64_t old_volsize = zfs_prop_get_int(zhp, 141836db6475SEric Taylor ZFS_PROP_VOLSIZE); 141936db6475SEric Taylor nvlist_free(nvl); 142036db6475SEric Taylor zcmd_free_nvlists(&zc); 142136db6475SEric Taylor if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 142236db6475SEric Taylor goto error; 142336db6475SEric Taylor if (nvlist_add_uint64(nvl, 142436db6475SEric Taylor zfs_prop_to_name(ZFS_PROP_VOLSIZE), 142536db6475SEric Taylor old_volsize) != 0) 142636db6475SEric Taylor goto error; 142736db6475SEric Taylor if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 142836db6475SEric Taylor goto error; 142936db6475SEric Taylor (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 143036db6475SEric Taylor } 1431fa9e4066Sahrens } else { 1432a227b7f4Shs24103 if (do_prefix) 1433a227b7f4Shs24103 ret = changelist_postfix(cl); 1434a227b7f4Shs24103 1435fa9e4066Sahrens /* 1436fa9e4066Sahrens * Refresh the statistics so the new property value 1437fa9e4066Sahrens * is reflected. 1438fa9e4066Sahrens */ 1439a227b7f4Shs24103 if (ret == 0) 1440fa9e4066Sahrens (void) get_stats(zhp); 1441fa9e4066Sahrens } 1442fa9e4066Sahrens 1443fa9e4066Sahrens error: 1444e9dbad6fSeschrock nvlist_free(nvl); 1445e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1446e9dbad6fSeschrock if (cl) 1447fa9e4066Sahrens changelist_free(cl); 1448fa9e4066Sahrens return (ret); 1449fa9e4066Sahrens } 1450fa9e4066Sahrens 1451fa9e4066Sahrens /* 145292241e0bSTom Erickson * Given a property, inherit the value from the parent dataset, or if received 145392241e0bSTom Erickson * is TRUE, revert to the received value, if any. 1454fa9e4066Sahrens */ 1455fa9e4066Sahrens int 145692241e0bSTom Erickson zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1457fa9e4066Sahrens { 1458fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1459fa9e4066Sahrens int ret; 1460fa9e4066Sahrens prop_changelist_t *cl; 146199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 146299653d4eSeschrock char errbuf[1024]; 1463e9dbad6fSeschrock zfs_prop_t prop; 146499653d4eSeschrock 146599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 146699653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1467fa9e4066Sahrens 146892241e0bSTom Erickson zc.zc_cookie = received; 1469990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1470e9dbad6fSeschrock /* 1471e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1472e9dbad6fSeschrock * small, so just do it here. 1473e9dbad6fSeschrock */ 1474e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1475e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1476e9dbad6fSeschrock "invalid property")); 1477e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1478e9dbad6fSeschrock } 1479e9dbad6fSeschrock 1480e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1481e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1482e9dbad6fSeschrock 1483e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1484e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1485e9dbad6fSeschrock 1486e9dbad6fSeschrock return (0); 1487e9dbad6fSeschrock } 1488e9dbad6fSeschrock 1489fa9e4066Sahrens /* 1490fa9e4066Sahrens * Verify that this property is inheritable. 1491fa9e4066Sahrens */ 149299653d4eSeschrock if (zfs_prop_readonly(prop)) 149399653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1494fa9e4066Sahrens 149592241e0bSTom Erickson if (!zfs_prop_inheritable(prop) && !received) 149699653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1497fa9e4066Sahrens 1498fa9e4066Sahrens /* 1499fa9e4066Sahrens * Check to see if the value applies to this type 1500fa9e4066Sahrens */ 150199653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 150299653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1503fa9e4066Sahrens 1504bf7c2d40Srm160521 /* 150536db6475SEric Taylor * Normalize the name, to get rid of shorthand abbreviations. 1506bf7c2d40Srm160521 */ 1507bf7c2d40Srm160521 propname = zfs_prop_to_name(prop); 1508fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1509e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1510fa9e4066Sahrens 1511fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1512fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 151399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 151499653d4eSeschrock "dataset is used in a non-global zone")); 151599653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1516fa9e4066Sahrens } 1517fa9e4066Sahrens 1518fa9e4066Sahrens /* 1519fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1520fa9e4066Sahrens */ 15210069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1522fa9e4066Sahrens return (-1); 1523fa9e4066Sahrens 1524fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 152599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 152699653d4eSeschrock "child dataset with inherited mountpoint is used " 152799653d4eSeschrock "in a non-global zone")); 152899653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1529fa9e4066Sahrens goto error; 1530fa9e4066Sahrens } 1531fa9e4066Sahrens 1532fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1533fa9e4066Sahrens goto error; 1534fa9e4066Sahrens 1535e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 153699653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1537fa9e4066Sahrens } else { 1538fa9e4066Sahrens 1539efc555ebSnd150628 if ((ret = changelist_postfix(cl)) != 0) 1540fa9e4066Sahrens goto error; 1541fa9e4066Sahrens 1542fa9e4066Sahrens /* 1543fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1544fa9e4066Sahrens */ 1545fa9e4066Sahrens (void) get_stats(zhp); 1546fa9e4066Sahrens } 1547fa9e4066Sahrens 1548fa9e4066Sahrens error: 1549fa9e4066Sahrens changelist_free(cl); 1550fa9e4066Sahrens return (ret); 1551fa9e4066Sahrens } 1552fa9e4066Sahrens 1553fa9e4066Sahrens /* 15547f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 15557f7322feSeschrock * extract them appropriately. 15567f7322feSeschrock */ 15577f7322feSeschrock static uint64_t 15587f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15597f7322feSeschrock { 15607f7322feSeschrock nvlist_t *nv; 15617f7322feSeschrock uint64_t value; 15627f7322feSeschrock 1563a2eea2e1Sahrens *source = NULL; 15647f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15657f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1566990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1567990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15687f7322feSeschrock } else { 15692e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15702e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15717f7322feSeschrock value = zfs_prop_default_numeric(prop); 15727f7322feSeschrock *source = ""; 15737f7322feSeschrock } 15747f7322feSeschrock 15757f7322feSeschrock return (value); 15767f7322feSeschrock } 15777f7322feSeschrock 15787f7322feSeschrock static char * 15797f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15807f7322feSeschrock { 15817f7322feSeschrock nvlist_t *nv; 15827f7322feSeschrock char *value; 15837f7322feSeschrock 1584a2eea2e1Sahrens *source = NULL; 15857f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15867f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1587990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1588990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15897f7322feSeschrock } else { 15902e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15912e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15927f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15937f7322feSeschrock value = ""; 15947f7322feSeschrock *source = ""; 15957f7322feSeschrock } 15967f7322feSeschrock 15977f7322feSeschrock return (value); 15987f7322feSeschrock } 15997f7322feSeschrock 160092241e0bSTom Erickson static boolean_t 160192241e0bSTom Erickson zfs_is_recvd_props_mode(zfs_handle_t *zhp) 160292241e0bSTom Erickson { 160392241e0bSTom Erickson return (zhp->zfs_props == zhp->zfs_recvd_props); 160492241e0bSTom Erickson } 160592241e0bSTom Erickson 160692241e0bSTom Erickson static void 160792241e0bSTom Erickson zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 160892241e0bSTom Erickson { 160992241e0bSTom Erickson *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 161092241e0bSTom Erickson zhp->zfs_props = zhp->zfs_recvd_props; 161192241e0bSTom Erickson } 161292241e0bSTom Erickson 161392241e0bSTom Erickson static void 161492241e0bSTom Erickson zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 161592241e0bSTom Erickson { 161692241e0bSTom Erickson zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 161792241e0bSTom Erickson *cookie = 0; 161892241e0bSTom Erickson } 161992241e0bSTom Erickson 16207f7322feSeschrock /* 1621fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1622fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1623fa9e4066Sahrens * 1624fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1625fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1626fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1627fa9e4066Sahrens * the source "temporary". 1628fa9e4066Sahrens */ 162999653d4eSeschrock static int 1630990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 163199653d4eSeschrock char **source, uint64_t *val) 1632fa9e4066Sahrens { 1633bd00f61bSrm160521 zfs_cmd_t zc = { 0 }; 163496510749Stimh nvlist_t *zplprops = NULL; 1635fa9e4066Sahrens struct mnttab mnt; 16363ccfa83cSahrens char *mntopt_on = NULL; 16373ccfa83cSahrens char *mntopt_off = NULL; 163892241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1639fa9e4066Sahrens 1640fa9e4066Sahrens *source = NULL; 1641fa9e4066Sahrens 16423ccfa83cSahrens switch (prop) { 16433ccfa83cSahrens case ZFS_PROP_ATIME: 16443ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 16453ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 16463ccfa83cSahrens break; 16473ccfa83cSahrens 16483ccfa83cSahrens case ZFS_PROP_DEVICES: 16493ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 16503ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 16513ccfa83cSahrens break; 16523ccfa83cSahrens 16533ccfa83cSahrens case ZFS_PROP_EXEC: 16543ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 16553ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 16563ccfa83cSahrens break; 16573ccfa83cSahrens 16583ccfa83cSahrens case ZFS_PROP_READONLY: 16593ccfa83cSahrens mntopt_on = MNTOPT_RO; 16603ccfa83cSahrens mntopt_off = MNTOPT_RW; 16613ccfa83cSahrens break; 16623ccfa83cSahrens 16633ccfa83cSahrens case ZFS_PROP_SETUID: 16643ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 16653ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 16663ccfa83cSahrens break; 16673ccfa83cSahrens 16683ccfa83cSahrens case ZFS_PROP_XATTR: 16693ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 16703ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 16713ccfa83cSahrens break; 1672da6c28aaSamw 1673da6c28aaSamw case ZFS_PROP_NBMAND: 1674da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1675da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1676da6c28aaSamw break; 16773ccfa83cSahrens } 16783ccfa83cSahrens 16793bb79becSeschrock /* 16803bb79becSeschrock * Because looking up the mount options is potentially expensive 16813bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16823bb79becSeschrock * we're looking up a property which requires its presence. 16833bb79becSeschrock */ 16843bb79becSeschrock if (!zhp->zfs_mntcheck && 16853ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1686ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1687ebedde84SEric Taylor struct mnttab entry; 16883bb79becSeschrock 1689ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1690ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 16913ccfa83cSahrens entry.mnt_mntopts); 16923ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 16933bb79becSeschrock return (-1); 16943ccfa83cSahrens } 16953bb79becSeschrock 16963bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 16973bb79becSeschrock } 16983bb79becSeschrock 1699fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1700fa9e4066Sahrens mnt.mnt_mntopts = ""; 1701fa9e4066Sahrens else 1702fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1703fa9e4066Sahrens 1704fa9e4066Sahrens switch (prop) { 1705fa9e4066Sahrens case ZFS_PROP_ATIME: 1706fa9e4066Sahrens case ZFS_PROP_DEVICES: 1707fa9e4066Sahrens case ZFS_PROP_EXEC: 17083ccfa83cSahrens case ZFS_PROP_READONLY: 17093ccfa83cSahrens case ZFS_PROP_SETUID: 17103ccfa83cSahrens case ZFS_PROP_XATTR: 1711da6c28aaSamw case ZFS_PROP_NBMAND: 171299653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1713fa9e4066Sahrens 171492241e0bSTom Erickson if (received) 171592241e0bSTom Erickson break; 171692241e0bSTom Erickson 17173ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 171899653d4eSeschrock *val = B_TRUE; 1719fa9e4066Sahrens if (src) 1720990b4856Slling *src = ZPROP_SRC_TEMPORARY; 17213ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 172299653d4eSeschrock *val = B_FALSE; 1723fa9e4066Sahrens if (src) 1724990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1725fa9e4066Sahrens } 172699653d4eSeschrock break; 1727fa9e4066Sahrens 17283ccfa83cSahrens case ZFS_PROP_CANMOUNT: 1729d41c4376SMark J Musante case ZFS_PROP_VOLSIZE: 1730fa9e4066Sahrens case ZFS_PROP_QUOTA: 1731a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1732fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1733a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1734a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 173592241e0bSTom Erickson 173692241e0bSTom Erickson if (*source == NULL) { 173792241e0bSTom Erickson /* not default, must be local */ 1738fa9e4066Sahrens *source = zhp->zfs_name; 173992241e0bSTom Erickson } 174099653d4eSeschrock break; 1741fa9e4066Sahrens 1742fa9e4066Sahrens case ZFS_PROP_MOUNTED: 174399653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 174499653d4eSeschrock break; 1745fa9e4066Sahrens 174639c23413Seschrock case ZFS_PROP_NUMCLONES: 174739c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 174839c23413Seschrock break; 174939c23413Seschrock 1750bd00f61bSrm160521 case ZFS_PROP_VERSION: 1751de8267e0Stimh case ZFS_PROP_NORMALIZE: 1752de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1753de8267e0Stimh case ZFS_PROP_CASE: 1754de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1755de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17563d7934e1Srm160521 return (-1); 1757bd00f61bSrm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1758de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1759de8267e0Stimh zcmd_free_nvlists(&zc); 1760f4b94bdeSMatthew Ahrens return (-1); 1761bd00f61bSrm160521 } 1762de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1763de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1764de8267e0Stimh val) != 0) { 1765de8267e0Stimh zcmd_free_nvlists(&zc); 1766f4b94bdeSMatthew Ahrens return (-1); 1767de8267e0Stimh } 176896510749Stimh if (zplprops) 176996510749Stimh nvlist_free(zplprops); 1770de8267e0Stimh zcmd_free_nvlists(&zc); 1771bd00f61bSrm160521 break; 1772bd00f61bSrm160521 1773fa9e4066Sahrens default: 1774e7437265Sahrens switch (zfs_prop_get_type(prop)) { 177591ebeef5Sahrens case PROP_TYPE_NUMBER: 177691ebeef5Sahrens case PROP_TYPE_INDEX: 1777e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 177874e7dc98SMatthew Ahrens /* 177914843421SMatthew Ahrens * If we tried to use a default value for a 178074e7dc98SMatthew Ahrens * readonly property, it means that it was not 178131f572c2STom Erickson * present. 178274e7dc98SMatthew Ahrens */ 178374e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 178431f572c2STom Erickson *source != NULL && (*source)[0] == '\0') { 178531f572c2STom Erickson *source = NULL; 178674e7dc98SMatthew Ahrens } 1787e7437265Sahrens break; 1788e7437265Sahrens 178991ebeef5Sahrens case PROP_TYPE_STRING: 1790e7437265Sahrens default: 179199653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 179299653d4eSeschrock "cannot get non-numeric property")); 179399653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 179499653d4eSeschrock dgettext(TEXT_DOMAIN, "internal error"))); 1795fa9e4066Sahrens } 1796e7437265Sahrens } 1797fa9e4066Sahrens 1798fa9e4066Sahrens return (0); 1799fa9e4066Sahrens } 1800fa9e4066Sahrens 1801fa9e4066Sahrens /* 1802fa9e4066Sahrens * Calculate the source type, given the raw source string. 1803fa9e4066Sahrens */ 1804fa9e4066Sahrens static void 1805990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1806fa9e4066Sahrens char *statbuf, size_t statlen) 1807fa9e4066Sahrens { 1808990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1809fa9e4066Sahrens return; 1810fa9e4066Sahrens 1811fa9e4066Sahrens if (source == NULL) { 1812990b4856Slling *srctype = ZPROP_SRC_NONE; 1813fa9e4066Sahrens } else if (source[0] == '\0') { 1814990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 181592241e0bSTom Erickson } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 181692241e0bSTom Erickson *srctype = ZPROP_SRC_RECEIVED; 1817fa9e4066Sahrens } else { 1818fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1819990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1820fa9e4066Sahrens } else { 1821fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1822990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1823fa9e4066Sahrens } 1824fa9e4066Sahrens } 1825fa9e4066Sahrens 1826fa9e4066Sahrens } 1827fa9e4066Sahrens 182892241e0bSTom Erickson int 182992241e0bSTom Erickson zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 183092241e0bSTom Erickson size_t proplen, boolean_t literal) 183192241e0bSTom Erickson { 183292241e0bSTom Erickson zfs_prop_t prop; 183392241e0bSTom Erickson int err = 0; 183492241e0bSTom Erickson 183592241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 183692241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 183792241e0bSTom Erickson return (-1); 183892241e0bSTom Erickson 183992241e0bSTom Erickson prop = zfs_name_to_prop(propname); 184092241e0bSTom Erickson 184192241e0bSTom Erickson if (prop != ZPROP_INVAL) { 184292241e0bSTom Erickson uint64_t cookie; 184392241e0bSTom Erickson if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 184492241e0bSTom Erickson return (-1); 184592241e0bSTom Erickson zfs_set_recvd_props_mode(zhp, &cookie); 184692241e0bSTom Erickson err = zfs_prop_get(zhp, prop, propbuf, proplen, 184792241e0bSTom Erickson NULL, NULL, 0, literal); 184892241e0bSTom Erickson zfs_unset_recvd_props_mode(zhp, &cookie); 184992241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 185092241e0bSTom Erickson return (-1); 185192241e0bSTom Erickson } else { 185292241e0bSTom Erickson nvlist_t *propval; 185392241e0bSTom Erickson char *recvdval; 185492241e0bSTom Erickson if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 185592241e0bSTom Erickson propname, &propval) != 0) 185692241e0bSTom Erickson return (-1); 185792241e0bSTom Erickson verify(nvlist_lookup_string(propval, ZPROP_VALUE, 185892241e0bSTom Erickson &recvdval) == 0); 185992241e0bSTom Erickson (void) strlcpy(propbuf, recvdval, proplen); 186092241e0bSTom Erickson } 186192241e0bSTom Erickson 186292241e0bSTom Erickson return (err == 0 ? 0 : -1); 186392241e0bSTom Erickson } 186492241e0bSTom Erickson 1865fa9e4066Sahrens /* 1866fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1867fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1868fa9e4066Sahrens * human-readable form. 1869fa9e4066Sahrens * 1870fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1871fa9e4066Sahrens */ 1872fa9e4066Sahrens int 1873fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1874990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1875fa9e4066Sahrens { 1876fa9e4066Sahrens char *source = NULL; 1877fa9e4066Sahrens uint64_t val; 1878fa9e4066Sahrens char *str; 1879e9dbad6fSeschrock const char *strval; 188092241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1881fa9e4066Sahrens 1882fa9e4066Sahrens /* 1883fa9e4066Sahrens * Check to see if this property applies to our object 1884fa9e4066Sahrens */ 1885fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1886fa9e4066Sahrens return (-1); 1887fa9e4066Sahrens 188892241e0bSTom Erickson if (received && zfs_prop_readonly(prop)) 188992241e0bSTom Erickson return (-1); 189092241e0bSTom Erickson 1891fa9e4066Sahrens if (src) 1892990b4856Slling *src = ZPROP_SRC_NONE; 1893fa9e4066Sahrens 1894fa9e4066Sahrens switch (prop) { 1895fa9e4066Sahrens case ZFS_PROP_CREATION: 1896fa9e4066Sahrens /* 1897fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1898fa9e4066Sahrens * this into a string unless 'literal' is specified. 1899fa9e4066Sahrens */ 1900fa9e4066Sahrens { 1901a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1902a2eea2e1Sahrens time_t time = (time_t)val; 1903fa9e4066Sahrens struct tm t; 1904fa9e4066Sahrens 1905fa9e4066Sahrens if (literal || 1906fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1907fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1908fa9e4066Sahrens &t) == 0) 1909a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1910fa9e4066Sahrens } 1911fa9e4066Sahrens break; 1912fa9e4066Sahrens 1913fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1914fa9e4066Sahrens /* 1915fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1916fa9e4066Sahrens * 1917fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1918fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1919fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1920fa9e4066Sahrens * 1921fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1922fa9e4066Sahrens * root to any values we return. 1923fa9e4066Sahrens */ 192429ab75c9Srm160521 19257f7322feSeschrock str = getprop_string(zhp, prop, &source); 1926fa9e4066Sahrens 1927b21718f0Sgw25295 if (str[0] == '/') { 192829ab75c9Srm160521 char buf[MAXPATHLEN]; 192929ab75c9Srm160521 char *root = buf; 1930a79992aaSTom Erickson const char *relpath; 1931fa9e4066Sahrens 1932a79992aaSTom Erickson /* 1933a79992aaSTom Erickson * If we inherit the mountpoint, even from a dataset 1934a79992aaSTom Erickson * with a received value, the source will be the path of 1935a79992aaSTom Erickson * the dataset we inherit from. If source is 1936a79992aaSTom Erickson * ZPROP_SOURCE_VAL_RECVD, the received value is not 1937a79992aaSTom Erickson * inherited. 1938a79992aaSTom Erickson */ 1939a79992aaSTom Erickson if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 1940a79992aaSTom Erickson relpath = ""; 1941a79992aaSTom Erickson } else { 1942a79992aaSTom Erickson relpath = zhp->zfs_name + strlen(source); 1943fa9e4066Sahrens if (relpath[0] == '/') 1944fa9e4066Sahrens relpath++; 1945a79992aaSTom Erickson } 1946b21718f0Sgw25295 194729ab75c9Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 194829ab75c9Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 194929ab75c9Srm160521 (strcmp(root, "-") == 0)) 195029ab75c9Srm160521 root[0] = '\0'; 1951b21718f0Sgw25295 /* 1952b21718f0Sgw25295 * Special case an alternate root of '/'. This will 1953b21718f0Sgw25295 * avoid having multiple leading slashes in the 1954b21718f0Sgw25295 * mountpoint path. 1955b21718f0Sgw25295 */ 1956b21718f0Sgw25295 if (strcmp(root, "/") == 0) 1957b21718f0Sgw25295 root++; 1958b21718f0Sgw25295 1959b21718f0Sgw25295 /* 1960b21718f0Sgw25295 * If the mountpoint is '/' then skip over this 1961b21718f0Sgw25295 * if we are obtaining either an alternate root or 1962b21718f0Sgw25295 * an inherited mountpoint. 1963b21718f0Sgw25295 */ 1964b21718f0Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 1965b21718f0Sgw25295 relpath[0] != '\0')) 19667f7322feSeschrock str++; 1967fa9e4066Sahrens 1968fa9e4066Sahrens if (relpath[0] == '\0') 1969fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19707f7322feSeschrock root, str); 1971fa9e4066Sahrens else 1972fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19737f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1974fa9e4066Sahrens relpath); 1975fa9e4066Sahrens } else { 1976fa9e4066Sahrens /* 'legacy' or 'none' */ 19777f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1978fa9e4066Sahrens } 1979fa9e4066Sahrens 1980fa9e4066Sahrens break; 1981fa9e4066Sahrens 1982fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1983a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1984fa9e4066Sahrens proplen); 1985fa9e4066Sahrens /* 1986fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1987fa9e4066Sahrens * it doesn't apply to this dataset. 1988fa9e4066Sahrens */ 1989fa9e4066Sahrens if (propbuf[0] == '\0') 1990fa9e4066Sahrens return (-1); 1991fa9e4066Sahrens break; 1992fa9e4066Sahrens 1993fa9e4066Sahrens case ZFS_PROP_QUOTA: 1994a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1995fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1996a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1997a9799022Sck153898 199899653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 199999653d4eSeschrock return (-1); 2000fa9e4066Sahrens 2001fa9e4066Sahrens /* 2002fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 2003fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 2004fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 2005fa9e4066Sahrens * that its set locally. 2006fa9e4066Sahrens */ 2007fa9e4066Sahrens if (val == 0) { 2008fa9e4066Sahrens if (literal) 2009fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 2010fa9e4066Sahrens else 2011fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 2012fa9e4066Sahrens } else { 2013fa9e4066Sahrens if (literal) 20145ad82045Snd150628 (void) snprintf(propbuf, proplen, "%llu", 20155ad82045Snd150628 (u_longlong_t)val); 2016fa9e4066Sahrens else 2017fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 2018fa9e4066Sahrens } 2019fa9e4066Sahrens break; 2020fa9e4066Sahrens 2021fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 202299653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 202399653d4eSeschrock return (-1); 2024b24ab676SJeff Bonwick (void) snprintf(propbuf, proplen, "%llu.%02llux", 2025b24ab676SJeff Bonwick (u_longlong_t)(val / 100), 2026b24ab676SJeff Bonwick (u_longlong_t)(val % 100)); 2027fa9e4066Sahrens break; 2028fa9e4066Sahrens 2029fa9e4066Sahrens case ZFS_PROP_TYPE: 2030fa9e4066Sahrens switch (zhp->zfs_type) { 2031fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 2032fa9e4066Sahrens str = "filesystem"; 2033fa9e4066Sahrens break; 2034fa9e4066Sahrens case ZFS_TYPE_VOLUME: 2035fa9e4066Sahrens str = "volume"; 2036fa9e4066Sahrens break; 2037fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 2038fa9e4066Sahrens str = "snapshot"; 2039fa9e4066Sahrens break; 2040fa9e4066Sahrens default: 204199653d4eSeschrock abort(); 2042fa9e4066Sahrens } 2043fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2044fa9e4066Sahrens break; 2045fa9e4066Sahrens 2046fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2047fa9e4066Sahrens /* 2048fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2049fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2050fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2051fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2052fa9e4066Sahrens */ 205399653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 205499653d4eSeschrock src, &source, &val) != 0) 205599653d4eSeschrock return (-1); 205699653d4eSeschrock if (val) 2057fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2058fa9e4066Sahrens else 2059fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2060fa9e4066Sahrens break; 2061fa9e4066Sahrens 2062fa9e4066Sahrens case ZFS_PROP_NAME: 2063fa9e4066Sahrens /* 2064fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2065fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2066fa9e4066Sahrens * consumers. 2067fa9e4066Sahrens */ 2068fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2069fa9e4066Sahrens break; 2070fa9e4066Sahrens 20714201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 20724201a95eSRic Aleshire { 20734201a95eSRic Aleshire m_label_t *new_sl = NULL; 20744201a95eSRic Aleshire char *ascii = NULL; /* human readable label */ 20754201a95eSRic Aleshire 20764201a95eSRic Aleshire (void) strlcpy(propbuf, 20774201a95eSRic Aleshire getprop_string(zhp, prop, &source), proplen); 20784201a95eSRic Aleshire 20794201a95eSRic Aleshire if (literal || (strcasecmp(propbuf, 20804201a95eSRic Aleshire ZFS_MLSLABEL_DEFAULT) == 0)) 20814201a95eSRic Aleshire break; 20824201a95eSRic Aleshire 20834201a95eSRic Aleshire /* 20844201a95eSRic Aleshire * Try to translate the internal hex string to 20854201a95eSRic Aleshire * human-readable output. If there are any 20864201a95eSRic Aleshire * problems just use the hex string. 20874201a95eSRic Aleshire */ 20884201a95eSRic Aleshire 20894201a95eSRic Aleshire if (str_to_label(propbuf, &new_sl, MAC_LABEL, 20904201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1) { 20914201a95eSRic Aleshire m_label_free(new_sl); 20924201a95eSRic Aleshire break; 20934201a95eSRic Aleshire } 20944201a95eSRic Aleshire 20954201a95eSRic Aleshire if (label_to_str(new_sl, &ascii, M_LABEL, 20964201a95eSRic Aleshire DEF_NAMES) != 0) { 20974201a95eSRic Aleshire if (ascii) 20984201a95eSRic Aleshire free(ascii); 20994201a95eSRic Aleshire m_label_free(new_sl); 21004201a95eSRic Aleshire break; 21014201a95eSRic Aleshire } 21024201a95eSRic Aleshire m_label_free(new_sl); 21034201a95eSRic Aleshire 21044201a95eSRic Aleshire (void) strlcpy(propbuf, ascii, proplen); 21054201a95eSRic Aleshire free(ascii); 21064201a95eSRic Aleshire } 21074201a95eSRic Aleshire break; 21084201a95eSRic Aleshire 2109fa9e4066Sahrens default: 2110e7437265Sahrens switch (zfs_prop_get_type(prop)) { 211191ebeef5Sahrens case PROP_TYPE_NUMBER: 2112e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2113e7437265Sahrens &source, &val) != 0) 2114e7437265Sahrens return (-1); 2115e7437265Sahrens if (literal) 2116e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2117e7437265Sahrens (u_longlong_t)val); 2118e7437265Sahrens else 2119e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2120e7437265Sahrens break; 2121e7437265Sahrens 212291ebeef5Sahrens case PROP_TYPE_STRING: 2123e7437265Sahrens (void) strlcpy(propbuf, 2124e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 2125e7437265Sahrens break; 2126e7437265Sahrens 212791ebeef5Sahrens case PROP_TYPE_INDEX: 2128fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2129fe192f76Sahrens &source, &val) != 0) 2130fe192f76Sahrens return (-1); 2131fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2132e7437265Sahrens return (-1); 2133e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2134e7437265Sahrens break; 2135e7437265Sahrens 2136e7437265Sahrens default: 213799653d4eSeschrock abort(); 2138fa9e4066Sahrens } 2139e7437265Sahrens } 2140fa9e4066Sahrens 2141fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2142fa9e4066Sahrens 2143fa9e4066Sahrens return (0); 2144fa9e4066Sahrens } 2145fa9e4066Sahrens 2146fa9e4066Sahrens /* 2147fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2148fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2149fa9e4066Sahrens * hard-coded property types. 2150fa9e4066Sahrens */ 2151fa9e4066Sahrens uint64_t 2152fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2153fa9e4066Sahrens { 2154fa9e4066Sahrens char *source; 215599653d4eSeschrock uint64_t val; 2156fa9e4066Sahrens 21573cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 215899653d4eSeschrock 215999653d4eSeschrock return (val); 2160fa9e4066Sahrens } 2161fa9e4066Sahrens 21627b97dc1aSrm160521 int 21637b97dc1aSrm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21647b97dc1aSrm160521 { 21657b97dc1aSrm160521 char buf[64]; 21667b97dc1aSrm160521 216714843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21687b97dc1aSrm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21697b97dc1aSrm160521 } 21707b97dc1aSrm160521 2171fa9e4066Sahrens /* 2172fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2173fa9e4066Sahrens */ 2174fa9e4066Sahrens int 2175fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2176990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2177fa9e4066Sahrens { 2178fa9e4066Sahrens char *source; 2179fa9e4066Sahrens 2180fa9e4066Sahrens /* 2181fa9e4066Sahrens * Check to see if this property applies to our object 2182fa9e4066Sahrens */ 2183e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2184ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 218599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 218699653d4eSeschrock zfs_prop_to_name(prop))); 2187e45ce728Sahrens } 2188fa9e4066Sahrens 2189fa9e4066Sahrens if (src) 2190990b4856Slling *src = ZPROP_SRC_NONE; 2191fa9e4066Sahrens 219299653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 219399653d4eSeschrock return (-1); 2194fa9e4066Sahrens 2195fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2196fa9e4066Sahrens 2197fa9e4066Sahrens return (0); 2198fa9e4066Sahrens } 2199fa9e4066Sahrens 220014843421SMatthew Ahrens static int 220114843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 220214843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 220314843421SMatthew Ahrens { 220414843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 220514843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 220614843421SMatthew Ahrens idmap_stat status; 220714843421SMatthew Ahrens int err = EINVAL; 220814843421SMatthew Ahrens 220914843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 221014843421SMatthew Ahrens goto out; 221114843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 221214843421SMatthew Ahrens goto out; 221314843421SMatthew Ahrens 221414843421SMatthew Ahrens if (isuser) { 221514843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 221614843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 221714843421SMatthew Ahrens } else { 221814843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 221914843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 222014843421SMatthew Ahrens } 222114843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 222214843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 222314843421SMatthew Ahrens status == IDMAP_SUCCESS) 222414843421SMatthew Ahrens err = 0; 222514843421SMatthew Ahrens else 222614843421SMatthew Ahrens err = EINVAL; 222714843421SMatthew Ahrens out: 222814843421SMatthew Ahrens if (get_hdl) 222914843421SMatthew Ahrens idmap_get_destroy(get_hdl); 223014843421SMatthew Ahrens if (idmap_hdl) 223114843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 223214843421SMatthew Ahrens return (err); 223314843421SMatthew Ahrens } 223414843421SMatthew Ahrens 223514843421SMatthew Ahrens /* 223614843421SMatthew Ahrens * convert the propname into parameters needed by kernel 223714843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 223814843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 223914843421SMatthew Ahrens */ 224014843421SMatthew Ahrens static int 224114843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 224214843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 224314843421SMatthew Ahrens { 224414843421SMatthew Ahrens zfs_userquota_prop_t type; 224514843421SMatthew Ahrens char *cp, *end; 22463b12c289SMatthew Ahrens char *numericsid = NULL; 224714843421SMatthew Ahrens boolean_t isuser; 224814843421SMatthew Ahrens 224914843421SMatthew Ahrens domain[0] = '\0'; 225014843421SMatthew Ahrens 225114843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 225214843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 225314843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 225414843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 225514843421SMatthew Ahrens break; 225614843421SMatthew Ahrens } 225714843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 225814843421SMatthew Ahrens return (EINVAL); 225914843421SMatthew Ahrens *typep = type; 226014843421SMatthew Ahrens 226114843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 226214843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 226314843421SMatthew Ahrens 226414843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 226514843421SMatthew Ahrens 226614843421SMatthew Ahrens if (strchr(cp, '@')) { 226714843421SMatthew Ahrens /* 226814843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 22693b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 227014843421SMatthew Ahrens */ 22713b12c289SMatthew Ahrens directory_error_t e; 227214843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 227314843421SMatthew Ahrens return (ENOENT); 22743b12c289SMatthew Ahrens if (isuser) { 22753b12c289SMatthew Ahrens e = directory_sid_from_user_name(NULL, 22763b12c289SMatthew Ahrens cp, &numericsid); 22773b12c289SMatthew Ahrens } else { 22783b12c289SMatthew Ahrens e = directory_sid_from_group_name(NULL, 22793b12c289SMatthew Ahrens cp, &numericsid); 22803b12c289SMatthew Ahrens } 22813b12c289SMatthew Ahrens if (e != NULL) { 22823b12c289SMatthew Ahrens directory_error_free(e); 228314843421SMatthew Ahrens return (ENOENT); 22843b12c289SMatthew Ahrens } 22853b12c289SMatthew Ahrens if (numericsid == NULL) 228614843421SMatthew Ahrens return (ENOENT); 22873b12c289SMatthew Ahrens cp = numericsid; 22883b12c289SMatthew Ahrens /* will be further decoded below */ 22893b12c289SMatthew Ahrens } 22903b12c289SMatthew Ahrens 22913b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 229214843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 22933b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 229414843421SMatthew Ahrens cp = strrchr(domain, '-'); 229514843421SMatthew Ahrens *cp = '\0'; 229614843421SMatthew Ahrens cp++; 229714843421SMatthew Ahrens 229814843421SMatthew Ahrens errno = 0; 229914843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 23003b12c289SMatthew Ahrens if (numericsid) { 23013b12c289SMatthew Ahrens free(numericsid); 23023b12c289SMatthew Ahrens numericsid = NULL; 23033b12c289SMatthew Ahrens } 2304777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 230514843421SMatthew Ahrens return (EINVAL); 230614843421SMatthew Ahrens } else if (!isdigit(*cp)) { 230714843421SMatthew Ahrens /* 230814843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 230914843421SMatthew Ahrens * turned into a uid/gid 231014843421SMatthew Ahrens */ 231114843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 231214843421SMatthew Ahrens return (ENOENT); 231314843421SMatthew Ahrens if (isuser) { 231414843421SMatthew Ahrens struct passwd *pw; 231514843421SMatthew Ahrens pw = getpwnam(cp); 231614843421SMatthew Ahrens if (pw == NULL) 231714843421SMatthew Ahrens return (ENOENT); 231814843421SMatthew Ahrens *ridp = pw->pw_uid; 231914843421SMatthew Ahrens } else { 232014843421SMatthew Ahrens struct group *gr; 232114843421SMatthew Ahrens gr = getgrnam(cp); 232214843421SMatthew Ahrens if (gr == NULL) 232314843421SMatthew Ahrens return (ENOENT); 232414843421SMatthew Ahrens *ridp = gr->gr_gid; 232514843421SMatthew Ahrens } 232614843421SMatthew Ahrens } else { 232714843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 232814843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 232914843421SMatthew Ahrens idmap_rid_t rid; 233014843421SMatthew Ahrens char *mapdomain; 233114843421SMatthew Ahrens 233214843421SMatthew Ahrens if (*end != '\0') 233314843421SMatthew Ahrens return (EINVAL); 233414843421SMatthew Ahrens if (id > MAXUID) { 233514843421SMatthew Ahrens /* It's an ephemeral ID. */ 233614843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 233714843421SMatthew Ahrens &mapdomain, &rid) != 0) 233814843421SMatthew Ahrens return (ENOENT); 23393b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 234014843421SMatthew Ahrens *ridp = rid; 234114843421SMatthew Ahrens } else { 234214843421SMatthew Ahrens *ridp = id; 234314843421SMatthew Ahrens } 234414843421SMatthew Ahrens } 234514843421SMatthew Ahrens 23463b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 234714843421SMatthew Ahrens return (0); 234814843421SMatthew Ahrens } 234914843421SMatthew Ahrens 2350edea4b55SLin Ling static int 2351edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2352edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 235314843421SMatthew Ahrens { 235414843421SMatthew Ahrens int err; 235514843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 235614843421SMatthew Ahrens 235714843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 235814843421SMatthew Ahrens 235914843421SMatthew Ahrens err = userquota_propname_decode(propname, 236014843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2361edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2362edea4b55SLin Ling zc.zc_objset_type = *typep; 236314843421SMatthew Ahrens if (err) 236414843421SMatthew Ahrens return (err); 236514843421SMatthew Ahrens 236614843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 236714843421SMatthew Ahrens if (err) 236814843421SMatthew Ahrens return (err); 236914843421SMatthew Ahrens 2370edea4b55SLin Ling *propvalue = zc.zc_cookie; 2371edea4b55SLin Ling return (0); 2372edea4b55SLin Ling } 2373edea4b55SLin Ling 2374edea4b55SLin Ling int 2375edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2376edea4b55SLin Ling uint64_t *propvalue) 2377edea4b55SLin Ling { 2378edea4b55SLin Ling zfs_userquota_prop_t type; 2379edea4b55SLin Ling 2380edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2381edea4b55SLin Ling &type)); 2382edea4b55SLin Ling } 2383edea4b55SLin Ling 2384edea4b55SLin Ling int 2385edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2386edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 2387edea4b55SLin Ling { 2388edea4b55SLin Ling int err; 2389edea4b55SLin Ling uint64_t propvalue; 2390edea4b55SLin Ling zfs_userquota_prop_t type; 2391edea4b55SLin Ling 2392edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2393edea4b55SLin Ling &type); 2394edea4b55SLin Ling 2395edea4b55SLin Ling if (err) 2396edea4b55SLin Ling return (err); 2397edea4b55SLin Ling 239814843421SMatthew Ahrens if (literal) { 2399edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 2400edea4b55SLin Ling } else if (propvalue == 0 && 240114843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 240214843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 240314843421SMatthew Ahrens } else { 2404edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 240514843421SMatthew Ahrens } 240614843421SMatthew Ahrens return (0); 240714843421SMatthew Ahrens } 240814843421SMatthew Ahrens 2409fa9e4066Sahrens /* 2410fa9e4066Sahrens * Returns the name of the given zfs handle. 2411fa9e4066Sahrens */ 2412fa9e4066Sahrens const char * 2413fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2414fa9e4066Sahrens { 2415fa9e4066Sahrens return (zhp->zfs_name); 2416fa9e4066Sahrens } 2417fa9e4066Sahrens 2418fa9e4066Sahrens /* 2419fa9e4066Sahrens * Returns the type of the given zfs handle. 2420fa9e4066Sahrens */ 2421fa9e4066Sahrens zfs_type_t 2422fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2423fa9e4066Sahrens { 2424fa9e4066Sahrens return (zhp->zfs_type); 2425fa9e4066Sahrens } 2426fa9e4066Sahrens 2427ebedde84SEric Taylor static int 2428ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2429ebedde84SEric Taylor { 2430ebedde84SEric Taylor int rc; 2431ebedde84SEric Taylor uint64_t orig_cookie; 2432ebedde84SEric Taylor 2433ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2434ebedde84SEric Taylor top: 2435ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2436ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2437ebedde84SEric Taylor 2438ebedde84SEric Taylor if (rc == -1) { 2439ebedde84SEric Taylor switch (errno) { 2440ebedde84SEric Taylor case ENOMEM: 2441ebedde84SEric Taylor /* expand nvlist memory and try again */ 2442ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2443ebedde84SEric Taylor zcmd_free_nvlists(zc); 2444ebedde84SEric Taylor return (-1); 2445ebedde84SEric Taylor } 2446ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2447ebedde84SEric Taylor goto top; 2448ebedde84SEric Taylor /* 2449ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2450ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2451ebedde84SEric Taylor * has been removed since we obtained the handle. 2452ebedde84SEric Taylor */ 2453ebedde84SEric Taylor case ESRCH: 2454ebedde84SEric Taylor case ENOENT: 2455ebedde84SEric Taylor rc = 1; 2456ebedde84SEric Taylor break; 2457ebedde84SEric Taylor default: 2458ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2459ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2460ebedde84SEric Taylor "cannot iterate filesystems")); 2461ebedde84SEric Taylor break; 2462ebedde84SEric Taylor } 2463ebedde84SEric Taylor } 2464ebedde84SEric Taylor return (rc); 2465ebedde84SEric Taylor } 2466ebedde84SEric Taylor 2467fa9e4066Sahrens /* 24687f7322feSeschrock * Iterate over all child filesystems 2469fa9e4066Sahrens */ 2470fa9e4066Sahrens int 24717f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2472fa9e4066Sahrens { 2473fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2474fa9e4066Sahrens zfs_handle_t *nzhp; 2475fa9e4066Sahrens int ret; 2476fa9e4066Sahrens 24773cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24783cb34c60Sahrens return (0); 24793cb34c60Sahrens 2480ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2481ebedde84SEric Taylor return (-1); 2482ebedde84SEric Taylor 2483ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2484ebedde84SEric Taylor &zc)) == 0) { 2485fa9e4066Sahrens /* 2486fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2487fa9e4066Sahrens * that the pool has since been removed. 2488fa9e4066Sahrens */ 2489ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2490ebedde84SEric Taylor &zc)) == NULL) { 2491fa9e4066Sahrens continue; 2492fa9e4066Sahrens } 2493fa9e4066Sahrens 2494ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2495ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2496ebedde84SEric Taylor return (ret); 2497ebedde84SEric Taylor } 2498ebedde84SEric Taylor } 2499ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2500ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 25017f7322feSeschrock } 25027f7322feSeschrock 25037f7322feSeschrock /* 25047f7322feSeschrock * Iterate over all snapshots 25057f7322feSeschrock */ 25067f7322feSeschrock int 25077f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25087f7322feSeschrock { 25097f7322feSeschrock zfs_cmd_t zc = { 0 }; 25107f7322feSeschrock zfs_handle_t *nzhp; 25117f7322feSeschrock int ret; 2512fa9e4066Sahrens 25133cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 25143cb34c60Sahrens return (0); 25153cb34c60Sahrens 2516ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2517ebedde84SEric Taylor return (-1); 2518ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2519ebedde84SEric Taylor &zc)) == 0) { 2520fa9e4066Sahrens 2521ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2522ebedde84SEric Taylor &zc)) == NULL) { 2523fa9e4066Sahrens continue; 2524fa9e4066Sahrens } 2525fa9e4066Sahrens 2526ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2527ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2528ebedde84SEric Taylor return (ret); 2529ebedde84SEric Taylor } 2530ebedde84SEric Taylor } 2531ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2532ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2533fa9e4066Sahrens } 2534fa9e4066Sahrens 2535fa9e4066Sahrens /* 25367f7322feSeschrock * Iterate over all children, snapshots and filesystems 25377f7322feSeschrock */ 25387f7322feSeschrock int 25397f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25407f7322feSeschrock { 25417f7322feSeschrock int ret; 25427f7322feSeschrock 25437f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25447f7322feSeschrock return (ret); 25457f7322feSeschrock 25467f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 25477f7322feSeschrock } 25487f7322feSeschrock 25497f7322feSeschrock /* 2550d41c4376SMark J Musante * Is one dataset name a child dataset of another? 2551d41c4376SMark J Musante * 2552d41c4376SMark J Musante * Needs to handle these cases: 2553d41c4376SMark J Musante * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 2554d41c4376SMark J Musante * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 2555d41c4376SMark J Musante * Descendant? No. No. No. Yes. 2556d41c4376SMark J Musante */ 2557d41c4376SMark J Musante static boolean_t 2558d41c4376SMark J Musante is_descendant(const char *ds1, const char *ds2) 2559d41c4376SMark J Musante { 2560d41c4376SMark J Musante size_t d1len = strlen(ds1); 2561d41c4376SMark J Musante 2562d41c4376SMark J Musante /* ds2 can't be a descendant if it's smaller */ 2563d41c4376SMark J Musante if (strlen(ds2) < d1len) 2564d41c4376SMark J Musante return (B_FALSE); 2565d41c4376SMark J Musante 2566d41c4376SMark J Musante /* otherwise, compare strings and verify that there's a '/' char */ 2567d41c4376SMark J Musante return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 2568d41c4376SMark J Musante } 2569d41c4376SMark J Musante 2570d41c4376SMark J Musante /* 2571fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2572fa9e4066Sahrens * Can return NULL if this is a pool. 2573fa9e4066Sahrens */ 2574fa9e4066Sahrens static int 2575fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2576fa9e4066Sahrens { 2577fa9e4066Sahrens char *loc; 2578fa9e4066Sahrens 2579fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2580fa9e4066Sahrens return (-1); 2581fa9e4066Sahrens 2582fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2583fa9e4066Sahrens buf[loc - path] = '\0'; 2584fa9e4066Sahrens 2585fa9e4066Sahrens return (0); 2586fa9e4066Sahrens } 2587fa9e4066Sahrens 2588fa9e4066Sahrens /* 25897f1f55eaSvb160487 * If accept_ancestor is false, then check to make sure that the given path has 25907f1f55eaSvb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25917f1f55eaSvb160487 * closest existing ancestor for the given path. In prefixlen return the 25927f1f55eaSvb160487 * length of already existing prefix of the given path. We also fetch the 25937f1f55eaSvb160487 * 'zoned' property, which is used to validate property settings when creating 25947f1f55eaSvb160487 * new datasets. 2595fa9e4066Sahrens */ 2596fa9e4066Sahrens static int 25977f1f55eaSvb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25987f1f55eaSvb160487 boolean_t accept_ancestor, int *prefixlen) 2599fa9e4066Sahrens { 2600fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2601fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2602fa9e4066Sahrens char *slash; 26037f7322feSeschrock zfs_handle_t *zhp; 260499653d4eSeschrock char errbuf[1024]; 2605d41c4376SMark J Musante uint64_t is_zoned; 260699653d4eSeschrock 2607deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2608deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2609fa9e4066Sahrens 2610fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2611fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 261299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 261399653d4eSeschrock "missing dataset name")); 261499653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2615fa9e4066Sahrens } 2616fa9e4066Sahrens 2617fa9e4066Sahrens /* check to see if the pool exists */ 2618fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2619fa9e4066Sahrens slash = parent + strlen(parent); 2620fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2621fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 262299653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2623fa9e4066Sahrens errno == ENOENT) { 262499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 262599653d4eSeschrock "no such pool '%s'"), zc.zc_name); 262699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2627fa9e4066Sahrens } 2628fa9e4066Sahrens 2629fa9e4066Sahrens /* check to see if the parent dataset exists */ 26307f1f55eaSvb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 26317f1f55eaSvb160487 if (errno == ENOENT && accept_ancestor) { 26327f1f55eaSvb160487 /* 26337f1f55eaSvb160487 * Go deeper to find an ancestor, give up on top level. 26347f1f55eaSvb160487 */ 26357f1f55eaSvb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 26367f1f55eaSvb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26377f1f55eaSvb160487 "no such pool '%s'"), zc.zc_name); 26387f1f55eaSvb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26397f1f55eaSvb160487 } 26407f1f55eaSvb160487 } else if (errno == ENOENT) { 264199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 264299653d4eSeschrock "parent does not exist")); 264399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26447f1f55eaSvb160487 } else 264599653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2646fa9e4066Sahrens } 2647fa9e4066Sahrens 2648d41c4376SMark J Musante is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2649d41c4376SMark J Musante if (zoned != NULL) 2650d41c4376SMark J Musante *zoned = is_zoned; 2651d41c4376SMark J Musante 2652fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2653d41c4376SMark J Musante if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 265499653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26557f7322feSeschrock zfs_close(zhp); 2656fa9e4066Sahrens return (-1); 2657fa9e4066Sahrens } 2658fa9e4066Sahrens 2659fa9e4066Sahrens /* make sure parent is a filesystem */ 26607f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 266199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 266299653d4eSeschrock "parent is not a filesystem")); 266399653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26647f7322feSeschrock zfs_close(zhp); 2665fa9e4066Sahrens return (-1); 2666fa9e4066Sahrens } 2667fa9e4066Sahrens 26687f7322feSeschrock zfs_close(zhp); 26697f1f55eaSvb160487 if (prefixlen != NULL) 26707f1f55eaSvb160487 *prefixlen = strlen(parent); 26717f1f55eaSvb160487 return (0); 26727f1f55eaSvb160487 } 26737f1f55eaSvb160487 26747f1f55eaSvb160487 /* 26757f1f55eaSvb160487 * Finds whether the dataset of the given type(s) exists. 26767f1f55eaSvb160487 */ 26777f1f55eaSvb160487 boolean_t 26787f1f55eaSvb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26797f1f55eaSvb160487 { 26807f1f55eaSvb160487 zfs_handle_t *zhp; 26817f1f55eaSvb160487 2682f18faf3fSek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26837f1f55eaSvb160487 return (B_FALSE); 26847f1f55eaSvb160487 26857f1f55eaSvb160487 /* 26867f1f55eaSvb160487 * Try to get stats for the dataset, which will tell us if it exists. 26877f1f55eaSvb160487 */ 26887f1f55eaSvb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26897f1f55eaSvb160487 int ds_type = zhp->zfs_type; 26907f1f55eaSvb160487 26917f1f55eaSvb160487 zfs_close(zhp); 26927f1f55eaSvb160487 if (types & ds_type) 26937f1f55eaSvb160487 return (B_TRUE); 26947f1f55eaSvb160487 } 26957f1f55eaSvb160487 return (B_FALSE); 26967f1f55eaSvb160487 } 26977f1f55eaSvb160487 26987f1f55eaSvb160487 /* 26993cb34c60Sahrens * Given a path to 'target', create all the ancestors between 27003cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 27013cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 27023cb34c60Sahrens */ 27033cb34c60Sahrens int 27043cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 27053cb34c60Sahrens { 27063cb34c60Sahrens zfs_handle_t *h; 27073cb34c60Sahrens char *cp; 27083cb34c60Sahrens const char *opname; 27093cb34c60Sahrens 27103cb34c60Sahrens /* make sure prefix exists */ 27113cb34c60Sahrens cp = target + prefixlen; 27123cb34c60Sahrens if (*cp != '/') { 27133cb34c60Sahrens assert(strchr(cp, '/') == NULL); 27143cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27153cb34c60Sahrens } else { 27163cb34c60Sahrens *cp = '\0'; 27173cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27183cb34c60Sahrens *cp = '/'; 27193cb34c60Sahrens } 27203cb34c60Sahrens if (h == NULL) 27213cb34c60Sahrens return (-1); 27223cb34c60Sahrens zfs_close(h); 27233cb34c60Sahrens 27243cb34c60Sahrens /* 27253cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 27263cb34c60Sahrens * up to the prefixlen-long one. 27273cb34c60Sahrens */ 27283cb34c60Sahrens for (cp = target + prefixlen + 1; 27293cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 27303cb34c60Sahrens char *logstr; 27313cb34c60Sahrens 27323cb34c60Sahrens *cp = '\0'; 27333cb34c60Sahrens 27343cb34c60Sahrens h = make_dataset_handle(hdl, target); 27353cb34c60Sahrens if (h) { 27363cb34c60Sahrens /* it already exists, nothing to do here */ 27373cb34c60Sahrens zfs_close(h); 27383cb34c60Sahrens continue; 27393cb34c60Sahrens } 27403cb34c60Sahrens 27413cb34c60Sahrens logstr = hdl->libzfs_log_str; 27423cb34c60Sahrens hdl->libzfs_log_str = NULL; 27433cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27443cb34c60Sahrens NULL) != 0) { 27453cb34c60Sahrens hdl->libzfs_log_str = logstr; 27463cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27473cb34c60Sahrens goto ancestorerr; 27483cb34c60Sahrens } 27493cb34c60Sahrens 27503cb34c60Sahrens hdl->libzfs_log_str = logstr; 27513cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27523cb34c60Sahrens if (h == NULL) { 27533cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27543cb34c60Sahrens goto ancestorerr; 27553cb34c60Sahrens } 27563cb34c60Sahrens 27573cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27583cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27593cb34c60Sahrens goto ancestorerr; 27603cb34c60Sahrens } 27613cb34c60Sahrens 27623cb34c60Sahrens if (zfs_share(h) != 0) { 27633cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27643cb34c60Sahrens goto ancestorerr; 27653cb34c60Sahrens } 27663cb34c60Sahrens 27673cb34c60Sahrens zfs_close(h); 27683cb34c60Sahrens } 27693cb34c60Sahrens 27703cb34c60Sahrens return (0); 27713cb34c60Sahrens 27723cb34c60Sahrens ancestorerr: 27733cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27743cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 27753cb34c60Sahrens return (-1); 27763cb34c60Sahrens } 27773cb34c60Sahrens 27783cb34c60Sahrens /* 27797f1f55eaSvb160487 * Creates non-existing ancestors of the given path. 27807f1f55eaSvb160487 */ 27817f1f55eaSvb160487 int 27827f1f55eaSvb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27837f1f55eaSvb160487 { 27847f1f55eaSvb160487 int prefix; 27857f1f55eaSvb160487 char *path_copy; 27867f1f55eaSvb160487 int rc; 27877f1f55eaSvb160487 2788d41c4376SMark J Musante if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27897f1f55eaSvb160487 return (-1); 27907f1f55eaSvb160487 27917f1f55eaSvb160487 if ((path_copy = strdup(path)) != NULL) { 27927f1f55eaSvb160487 rc = create_parents(hdl, path_copy, prefix); 27937f1f55eaSvb160487 free(path_copy); 27947f1f55eaSvb160487 } 27957f1f55eaSvb160487 if (path_copy == NULL || rc != 0) 27967f1f55eaSvb160487 return (-1); 27977f1f55eaSvb160487 2798fa9e4066Sahrens return (0); 2799fa9e4066Sahrens } 2800fa9e4066Sahrens 2801fa9e4066Sahrens /* 2802e9dbad6fSeschrock * Create a new filesystem or volume. 2803fa9e4066Sahrens */ 2804fa9e4066Sahrens int 280599653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2806e9dbad6fSeschrock nvlist_t *props) 2807fa9e4066Sahrens { 2808fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2809fa9e4066Sahrens int ret; 2810fa9e4066Sahrens uint64_t size = 0; 2811fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 281299653d4eSeschrock char errbuf[1024]; 2813e9dbad6fSeschrock uint64_t zoned; 281499653d4eSeschrock 281599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 281699653d4eSeschrock "cannot create '%s'"), path); 2817fa9e4066Sahrens 2818fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2819f18faf3fSek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 282099653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2821fa9e4066Sahrens 2822fa9e4066Sahrens /* validate parents exist */ 28237f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2824fa9e4066Sahrens return (-1); 2825fa9e4066Sahrens 2826fa9e4066Sahrens /* 2827fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2828fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2829fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2830fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2831fa9e4066Sahrens * first try to see if the dataset exists. 2832fa9e4066Sahrens */ 2833fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2834990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 283599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 283699653d4eSeschrock "dataset already exists")); 283799653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2838fa9e4066Sahrens } 2839fa9e4066Sahrens 2840fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2841fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2842fa9e4066Sahrens else 2843fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2844fa9e4066Sahrens 28450a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2846b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2847e9dbad6fSeschrock return (-1); 2848e9dbad6fSeschrock 2849fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 28505c5460e9Seschrock /* 28515c5460e9Seschrock * If we are creating a volume, the size and block size must 28525c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 28535c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28545c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 28555c5460e9Seschrock * zero. 28565c5460e9Seschrock */ 2857e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2858e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2859e9dbad6fSeschrock nvlist_free(props); 286099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2861e9dbad6fSeschrock "missing volume size")); 2862e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2863fa9e4066Sahrens } 2864fa9e4066Sahrens 2865e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2866e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2867e9dbad6fSeschrock &blocksize)) != 0) { 2868e9dbad6fSeschrock if (ret == ENOENT) { 2869e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2870e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2871e9dbad6fSeschrock } else { 2872e9dbad6fSeschrock nvlist_free(props); 287399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2874e9dbad6fSeschrock "missing volume block size")); 2875e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2876e9dbad6fSeschrock } 2877e9dbad6fSeschrock } 2878e9dbad6fSeschrock 2879e9dbad6fSeschrock if (size == 0) { 2880e9dbad6fSeschrock nvlist_free(props); 2881e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2882e9dbad6fSeschrock "volume size cannot be zero")); 2883e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28845c5460e9Seschrock } 28855c5460e9Seschrock 28865c5460e9Seschrock if (size % blocksize != 0) { 2887e9dbad6fSeschrock nvlist_free(props); 288899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2889e9dbad6fSeschrock "volume size must be a multiple of volume block " 2890e9dbad6fSeschrock "size")); 2891e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2892e9dbad6fSeschrock } 28935c5460e9Seschrock } 28945c5460e9Seschrock 2895990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2896e9dbad6fSeschrock return (-1); 2897e9dbad6fSeschrock nvlist_free(props); 2898fa9e4066Sahrens 2899fa9e4066Sahrens /* create the dataset */ 2900ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2901fa9e4066Sahrens 2902e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2903e9dbad6fSeschrock 2904fa9e4066Sahrens /* check for failure */ 2905fa9e4066Sahrens if (ret != 0) { 2906fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2907fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2908fa9e4066Sahrens 2909fa9e4066Sahrens switch (errno) { 2910fa9e4066Sahrens case ENOENT: 291199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 291299653d4eSeschrock "no such parent '%s'"), parent); 291399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2914fa9e4066Sahrens 2915fa9e4066Sahrens case EINVAL: 291699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2917d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 291899653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2919fa9e4066Sahrens 2920fa9e4066Sahrens case EDOM: 292199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2922e9dbad6fSeschrock "volume block size must be power of 2 from " 2923e9dbad6fSeschrock "%u to %uk"), 2924fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2925fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 292699653d4eSeschrock 2927e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 292899653d4eSeschrock 292940feaa91Sahrens case ENOTSUP: 293040feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 293140feaa91Sahrens "pool must be upgraded to set this " 293240feaa91Sahrens "property or value")); 293340feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2934fa9e4066Sahrens #ifdef _ILP32 2935fa9e4066Sahrens case EOVERFLOW: 2936fa9e4066Sahrens /* 2937fa9e4066Sahrens * This platform can't address a volume this big. 2938fa9e4066Sahrens */ 293999653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 294099653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 294199653d4eSeschrock errbuf)); 2942fa9e4066Sahrens #endif 294399653d4eSeschrock /* FALLTHROUGH */ 2944fa9e4066Sahrens default: 294599653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2946fa9e4066Sahrens } 2947fa9e4066Sahrens } 2948fa9e4066Sahrens 2949fa9e4066Sahrens return (0); 2950fa9e4066Sahrens } 2951fa9e4066Sahrens 2952fa9e4066Sahrens /* 2953fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2954fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2955fa9e4066Sahrens */ 2956fa9e4066Sahrens int 2957842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2958fa9e4066Sahrens { 2959fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2960fa9e4066Sahrens 2961fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2962fa9e4066Sahrens 2963e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2964fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2965fa9e4066Sahrens } else { 2966fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2967fa9e4066Sahrens } 2968fa9e4066Sahrens 2969842727c2SChris Kirby zc.zc_defer_destroy = defer; 2970ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2971ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 297299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 297399653d4eSeschrock zhp->zfs_name)); 29741d452cf5Sahrens } 2975fa9e4066Sahrens 2976fa9e4066Sahrens remove_mountpoint(zhp); 2977fa9e4066Sahrens 2978fa9e4066Sahrens return (0); 2979fa9e4066Sahrens } 2980fa9e4066Sahrens 29811d452cf5Sahrens struct destroydata { 29821d452cf5Sahrens char *snapname; 29831d452cf5Sahrens boolean_t gotone; 29843ccfa83cSahrens boolean_t closezhp; 29851d452cf5Sahrens }; 29861d452cf5Sahrens 29871d452cf5Sahrens static int 2988681d9761SEric Taylor zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29891d452cf5Sahrens { 29901d452cf5Sahrens struct destroydata *dd = arg; 29911d452cf5Sahrens zfs_handle_t *szhp; 29921d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 29933ccfa83cSahrens boolean_t closezhp = dd->closezhp; 2994681d9761SEric Taylor int rv = 0; 29951d452cf5Sahrens 2996e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2997e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2998e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29991d452cf5Sahrens 30001d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 30011d452cf5Sahrens if (szhp) { 30021d452cf5Sahrens dd->gotone = B_TRUE; 30031d452cf5Sahrens zfs_close(szhp); 30041d452cf5Sahrens } 30051d452cf5Sahrens 30063ccfa83cSahrens dd->closezhp = B_TRUE; 3007681d9761SEric Taylor if (!dd->gotone) 3008681d9761SEric Taylor rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 30093ccfa83cSahrens if (closezhp) 30103ccfa83cSahrens zfs_close(zhp); 30113ccfa83cSahrens return (rv); 30121d452cf5Sahrens } 30131d452cf5Sahrens 30141d452cf5Sahrens /* 30151d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 30161d452cf5Sahrens */ 30171d452cf5Sahrens int 3018842727c2SChris Kirby zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 30191d452cf5Sahrens { 30201d452cf5Sahrens zfs_cmd_t zc = { 0 }; 30211d452cf5Sahrens int ret; 30221d452cf5Sahrens struct destroydata dd = { 0 }; 30231d452cf5Sahrens 30241d452cf5Sahrens dd.snapname = snapname; 3025681d9761SEric Taylor (void) zfs_check_snap_cb(zhp, &dd); 30261d452cf5Sahrens 30271d452cf5Sahrens if (!dd.gotone) { 3028ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 30291d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 30301d452cf5Sahrens zhp->zfs_name, snapname)); 30311d452cf5Sahrens } 30321d452cf5Sahrens 30331d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3034e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3035842727c2SChris Kirby zc.zc_defer_destroy = defer; 30361d452cf5Sahrens 3037ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 30381d452cf5Sahrens if (ret != 0) { 30391d452cf5Sahrens char errbuf[1024]; 30401d452cf5Sahrens 30411d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30421d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 30431d452cf5Sahrens 30441d452cf5Sahrens switch (errno) { 30451d452cf5Sahrens case EEXIST: 30461d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30471d452cf5Sahrens "snapshot is cloned")); 30481d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 30491d452cf5Sahrens 30501d452cf5Sahrens default: 30511d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 30521d452cf5Sahrens errbuf)); 30531d452cf5Sahrens } 30541d452cf5Sahrens } 30551d452cf5Sahrens 30561d452cf5Sahrens return (0); 30571d452cf5Sahrens } 30581d452cf5Sahrens 3059fa9e4066Sahrens /* 3060fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 3061fa9e4066Sahrens */ 3062fa9e4066Sahrens int 3063e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3064fa9e4066Sahrens { 3065fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3066fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 3067fa9e4066Sahrens int ret; 306899653d4eSeschrock char errbuf[1024]; 306999653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3070e9dbad6fSeschrock zfs_type_t type; 3071e9dbad6fSeschrock uint64_t zoned; 3072fa9e4066Sahrens 3073fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3074fa9e4066Sahrens 307599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 307699653d4eSeschrock "cannot create '%s'"), target); 307799653d4eSeschrock 3078fa9e4066Sahrens /* validate the target name */ 3079f18faf3fSek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 308099653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3081fa9e4066Sahrens 3082fa9e4066Sahrens /* validate parents exist */ 30837f1f55eaSvb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3084fa9e4066Sahrens return (-1); 3085fa9e4066Sahrens 3086fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3087fa9e4066Sahrens 3088fa9e4066Sahrens /* do the clone */ 3089e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 3090fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30915f8e1617Snn35248 type = ZFS_TYPE_VOLUME; 3092e9dbad6fSeschrock } else { 3093fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30945f8e1617Snn35248 type = ZFS_TYPE_FILESYSTEM; 3095e9dbad6fSeschrock } 3096e9dbad6fSeschrock 3097e9dbad6fSeschrock if (props) { 30980a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30990a48a24eStimh zhp, errbuf)) == NULL) 3100e9dbad6fSeschrock return (-1); 3101e9dbad6fSeschrock 3102990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3103e9dbad6fSeschrock nvlist_free(props); 3104e9dbad6fSeschrock return (-1); 3105e9dbad6fSeschrock } 3106e9dbad6fSeschrock 3107e9dbad6fSeschrock nvlist_free(props); 3108e9dbad6fSeschrock } 3109fa9e4066Sahrens 3110fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 3111e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 3112ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3113fa9e4066Sahrens 3114e9dbad6fSeschrock zcmd_free_nvlists(&zc); 3115e9dbad6fSeschrock 3116fa9e4066Sahrens if (ret != 0) { 3117fa9e4066Sahrens switch (errno) { 3118fa9e4066Sahrens 3119fa9e4066Sahrens case ENOENT: 3120fa9e4066Sahrens /* 3121fa9e4066Sahrens * The parent doesn't exist. We should have caught this 3122fa9e4066Sahrens * above, but there may a race condition that has since 3123fa9e4066Sahrens * destroyed the parent. 3124fa9e4066Sahrens * 3125fa9e4066Sahrens * At this point, we don't know whether it's the source 3126fa9e4066Sahrens * that doesn't exist anymore, or whether the target 3127fa9e4066Sahrens * dataset doesn't exist. 3128fa9e4066Sahrens */ 312999653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 313099653d4eSeschrock "no such parent '%s'"), parent); 313199653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3132fa9e4066Sahrens 313399653d4eSeschrock case EXDEV: 313499653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 313599653d4eSeschrock "source and target pools differ")); 313699653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 313799653d4eSeschrock errbuf)); 313899653d4eSeschrock 313999653d4eSeschrock default: 314099653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 314199653d4eSeschrock errbuf)); 314299653d4eSeschrock } 314399653d4eSeschrock } 314499653d4eSeschrock 314599653d4eSeschrock return (ret); 314699653d4eSeschrock } 314799653d4eSeschrock 314899653d4eSeschrock /* 314999653d4eSeschrock * Promotes the given clone fs to be the clone parent. 315099653d4eSeschrock */ 315199653d4eSeschrock int 315299653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 315399653d4eSeschrock { 315499653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 315599653d4eSeschrock zfs_cmd_t zc = { 0 }; 315699653d4eSeschrock char parent[MAXPATHLEN]; 315799653d4eSeschrock int ret; 315899653d4eSeschrock char errbuf[1024]; 315999653d4eSeschrock 316099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 316199653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 316299653d4eSeschrock 316399653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 316499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 316599653d4eSeschrock "snapshots can not be promoted")); 316699653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 316799653d4eSeschrock } 316899653d4eSeschrock 31693cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 317099653d4eSeschrock if (parent[0] == '\0') { 317199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 317299653d4eSeschrock "not a cloned filesystem")); 317399653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 317499653d4eSeschrock } 317599653d4eSeschrock 31763cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3177e9dbad6fSeschrock sizeof (zc.zc_value)); 317899653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3179ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 318099653d4eSeschrock 318199653d4eSeschrock if (ret != 0) { 31820b69c2f0Sahrens int save_errno = errno; 3183fa9e4066Sahrens 31840b69c2f0Sahrens switch (save_errno) { 3185fa9e4066Sahrens case EEXIST: 3186681d9761SEric Taylor /* There is a conflicting snapshot name. */ 318799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3188681d9761SEric Taylor "conflicting snapshot '%s' from parent '%s'"), 3189681d9761SEric Taylor zc.zc_string, parent); 319099653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3191fa9e4066Sahrens 3192fa9e4066Sahrens default: 31930b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3194fa9e4066Sahrens } 3195fa9e4066Sahrens } 3196e9dbad6fSeschrock return (ret); 31971d452cf5Sahrens } 31981d452cf5Sahrens 3199fa9e4066Sahrens /* 320072bdce51Sahl * Takes a snapshot of the given dataset. 3201fa9e4066Sahrens */ 3202fa9e4066Sahrens int 3203bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3204bb0ade09Sahrens nvlist_t *props) 3205fa9e4066Sahrens { 3206fa9e4066Sahrens const char *delim; 3207bb0ade09Sahrens char parent[ZFS_MAXNAMELEN]; 3208fa9e4066Sahrens zfs_handle_t *zhp; 3209fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3210fa9e4066Sahrens int ret; 321199653d4eSeschrock char errbuf[1024]; 3212fa9e4066Sahrens 321399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 321499653d4eSeschrock "cannot snapshot '%s'"), path); 321599653d4eSeschrock 321699653d4eSeschrock /* validate the target name */ 3217f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 321899653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3219fa9e4066Sahrens 3220bb0ade09Sahrens if (props) { 3221bb0ade09Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3222bb0ade09Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 3223bb0ade09Sahrens return (-1); 3224bb0ade09Sahrens 3225bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3226bb0ade09Sahrens nvlist_free(props); 3227bb0ade09Sahrens return (-1); 3228bb0ade09Sahrens } 3229bb0ade09Sahrens 3230bb0ade09Sahrens nvlist_free(props); 3231bb0ade09Sahrens } 3232bb0ade09Sahrens 3233fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 32341d452cf5Sahrens delim = strchr(path, '@'); 3235fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3236fa9e4066Sahrens parent[delim - path] = '\0'; 3237fa9e4066Sahrens 323899653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3239fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3240bb0ade09Sahrens zcmd_free_nvlists(&zc); 3241fa9e4066Sahrens return (-1); 3242fa9e4066Sahrens } 3243fa9e4066Sahrens 32441d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3245e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3246ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3247ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3248ecd6cf80Smarks else 3249ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 32501d452cf5Sahrens zc.zc_cookie = recursive; 3251ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3252fa9e4066Sahrens 3253bb0ade09Sahrens zcmd_free_nvlists(&zc); 3254bb0ade09Sahrens 32551d452cf5Sahrens /* 32561d452cf5Sahrens * if it was recursive, the one that actually failed will be in 32571d452cf5Sahrens * zc.zc_name. 32581d452cf5Sahrens */ 3259681d9761SEric Taylor if (ret != 0) { 32601d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3261e9dbad6fSeschrock "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 326299653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3263681d9761SEric Taylor } 3264fa9e4066Sahrens 3265fa9e4066Sahrens zfs_close(zhp); 3266fa9e4066Sahrens 3267fa9e4066Sahrens return (ret); 3268fa9e4066Sahrens } 3269fa9e4066Sahrens 3270fa9e4066Sahrens /* 3271b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3272b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3273b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3274b12a1c38Slling * group. 3275fa9e4066Sahrens */ 3276b12a1c38Slling typedef struct rollback_data { 3277b12a1c38Slling const char *cb_target; /* the snapshot */ 3278b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3279c391e322Sahrens boolean_t cb_error; 328099653d4eSeschrock boolean_t cb_dependent; 3281c391e322Sahrens boolean_t cb_force; 3282b12a1c38Slling } rollback_data_t; 3283b12a1c38Slling 3284b12a1c38Slling static int 3285b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3286b12a1c38Slling { 3287b12a1c38Slling rollback_data_t *cbp = data; 3288b12a1c38Slling 3289b12a1c38Slling if (!cbp->cb_dependent) { 3290b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3291b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3292b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3293b12a1c38Slling cbp->cb_create) { 3294ecd6cf80Smarks char *logstr; 3295b12a1c38Slling 329699653d4eSeschrock cbp->cb_dependent = B_TRUE; 32974ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32984ccbb6e7Sahrens rollback_destroy, cbp); 329999653d4eSeschrock cbp->cb_dependent = B_FALSE; 3300b12a1c38Slling 3301ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3302ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 3303842727c2SChris Kirby cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3304ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3305b12a1c38Slling } 3306b12a1c38Slling } else { 3307c391e322Sahrens /* We must destroy this clone; first unmount it */ 3308c391e322Sahrens prop_changelist_t *clp; 3309c391e322Sahrens 33100069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3311c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3312c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3313c391e322Sahrens cbp->cb_error = B_TRUE; 3314c391e322Sahrens zfs_close(zhp); 3315c391e322Sahrens return (0); 3316c391e322Sahrens } 3317842727c2SChris Kirby if (zfs_destroy(zhp, B_FALSE) != 0) 3318c391e322Sahrens cbp->cb_error = B_TRUE; 3319c391e322Sahrens else 3320c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3321ba7b046eSahrens (void) changelist_postfix(clp); 3322c391e322Sahrens changelist_free(clp); 3323b12a1c38Slling } 3324b12a1c38Slling 3325b12a1c38Slling zfs_close(zhp); 3326b12a1c38Slling return (0); 3327b12a1c38Slling } 3328b12a1c38Slling 3329b12a1c38Slling /* 33304ccbb6e7Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33314ccbb6e7Sahrens * data changes since then and making it the active dataset. 33324ccbb6e7Sahrens * 33334ccbb6e7Sahrens * Any snapshots more recent than the target are destroyed, along with 33344ccbb6e7Sahrens * their dependents. 3335b12a1c38Slling */ 33364ccbb6e7Sahrens int 3337c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3338fa9e4066Sahrens { 33394ccbb6e7Sahrens rollback_data_t cb = { 0 }; 33404ccbb6e7Sahrens int err; 3341fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 33427b97dc1aSrm160521 boolean_t restore_resv = 0; 33437b97dc1aSrm160521 uint64_t old_volsize, new_volsize; 33447b97dc1aSrm160521 zfs_prop_t resv_prop; 3345fa9e4066Sahrens 3346fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3347fa9e4066Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3348fa9e4066Sahrens 33494ccbb6e7Sahrens /* 33504ccbb6e7Sahrens * Destroy all recent snapshots and its dependends. 33514ccbb6e7Sahrens */ 3352c391e322Sahrens cb.cb_force = force; 33534ccbb6e7Sahrens cb.cb_target = snap->zfs_name; 33544ccbb6e7Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33554ccbb6e7Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33564ccbb6e7Sahrens 3357c391e322Sahrens if (cb.cb_error) 3358c391e322Sahrens return (-1); 33594ccbb6e7Sahrens 33604ccbb6e7Sahrens /* 33614ccbb6e7Sahrens * Now that we have verified that the snapshot is the latest, 33624ccbb6e7Sahrens * rollback to the given snapshot. 33634ccbb6e7Sahrens */ 33644ccbb6e7Sahrens 33657b97dc1aSrm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33667b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33677b97dc1aSrm160521 return (-1); 33687b97dc1aSrm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33697b97dc1aSrm160521 restore_resv = 33707b97dc1aSrm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33717b97dc1aSrm160521 } 3372fa9e4066Sahrens 3373fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3374fa9e4066Sahrens 3375e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3376fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3377fa9e4066Sahrens else 3378fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3379fa9e4066Sahrens 3380fa9e4066Sahrens /* 33814ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 33824ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 33834ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 33844ccbb6e7Sahrens * an unlikely race condition where the user has taken a 33854ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 33867b97dc1aSrm160521 * 3387fa9e4066Sahrens */ 33884ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3389ece3d9b3Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 339099653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 339199653d4eSeschrock zhp->zfs_name); 3392b9415e83Srm160521 return (err); 3393b9415e83Srm160521 } 3394fa9e4066Sahrens 33957b97dc1aSrm160521 /* 33967b97dc1aSrm160521 * For volumes, if the pre-rollback volsize matched the pre- 33977b97dc1aSrm160521 * rollback reservation and the volsize has changed then set 33987b97dc1aSrm160521 * the reservation property to the post-rollback volsize. 33997b97dc1aSrm160521 * Make a new handle since the rollback closed the dataset. 34007b97dc1aSrm160521 */ 3401b9415e83Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3402b9415e83Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 34037b97dc1aSrm160521 if (restore_resv) { 34047b97dc1aSrm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 34057b97dc1aSrm160521 if (old_volsize != new_volsize) 3406b9415e83Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 3407b9415e83Srm160521 new_volsize); 34087b97dc1aSrm160521 } 34097b97dc1aSrm160521 zfs_close(zhp); 34107b97dc1aSrm160521 } 34114ccbb6e7Sahrens return (err); 3412b12a1c38Slling } 3413b12a1c38Slling 3414b12a1c38Slling /* 3415fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3416fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3417fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3418fa9e4066Sahrens * libzfs_graph.c. 3419fa9e4066Sahrens */ 3420fa9e4066Sahrens int 34213bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 34223bb79becSeschrock zfs_iter_f func, void *data) 3423fa9e4066Sahrens { 3424fa9e4066Sahrens char **dependents; 3425fa9e4066Sahrens size_t count; 3426fa9e4066Sahrens int i; 3427fa9e4066Sahrens zfs_handle_t *child; 3428fa9e4066Sahrens int ret = 0; 3429fa9e4066Sahrens 34303bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34313bb79becSeschrock &dependents, &count) != 0) 34323bb79becSeschrock return (-1); 34333bb79becSeschrock 3434fa9e4066Sahrens for (i = 0; i < count; i++) { 343599653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 343699653d4eSeschrock dependents[i])) == NULL) 3437fa9e4066Sahrens continue; 3438fa9e4066Sahrens 3439fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3440fa9e4066Sahrens break; 3441fa9e4066Sahrens } 3442fa9e4066Sahrens 3443fa9e4066Sahrens for (i = 0; i < count; i++) 3444fa9e4066Sahrens free(dependents[i]); 3445fa9e4066Sahrens free(dependents); 3446fa9e4066Sahrens 3447fa9e4066Sahrens return (ret); 3448fa9e4066Sahrens } 3449fa9e4066Sahrens 3450fa9e4066Sahrens /* 3451fa9e4066Sahrens * Renames the given dataset. 3452fa9e4066Sahrens */ 3453fa9e4066Sahrens int 34547f1f55eaSvb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3455fa9e4066Sahrens { 3456fa9e4066Sahrens int ret; 3457fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3458fa9e4066Sahrens char *delim; 3459cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3460cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3461cdf5b4caSmmusante char *parentname = NULL; 3462fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 346399653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 346499653d4eSeschrock char errbuf[1024]; 3465fa9e4066Sahrens 3466fa9e4066Sahrens /* if we have the same exact name, just return success */ 3467fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3468fa9e4066Sahrens return (0); 3469fa9e4066Sahrens 347099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 347199653d4eSeschrock "cannot rename to '%s'"), target); 347299653d4eSeschrock 3473fa9e4066Sahrens /* 3474fa9e4066Sahrens * Make sure the target name is valid 3475fa9e4066Sahrens */ 3476fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 347798579b20Snd150628 if ((strchr(target, '@') == NULL) || 347898579b20Snd150628 *target == '@') { 347998579b20Snd150628 /* 348098579b20Snd150628 * Snapshot target name is abbreviated, 348198579b20Snd150628 * reconstruct full dataset name 348298579b20Snd150628 */ 348398579b20Snd150628 (void) strlcpy(parent, zhp->zfs_name, 348498579b20Snd150628 sizeof (parent)); 348598579b20Snd150628 delim = strchr(parent, '@'); 348698579b20Snd150628 if (strchr(target, '@') == NULL) 348798579b20Snd150628 *(++delim) = '\0'; 348898579b20Snd150628 else 348998579b20Snd150628 *delim = '\0'; 349098579b20Snd150628 (void) strlcat(parent, target, sizeof (parent)); 349198579b20Snd150628 target = parent; 349298579b20Snd150628 } else { 3493fa9e4066Sahrens /* 3494fa9e4066Sahrens * Make sure we're renaming within the same dataset. 3495fa9e4066Sahrens */ 349698579b20Snd150628 delim = strchr(target, '@'); 349798579b20Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 349898579b20Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 349999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 350098579b20Snd150628 "snapshots must be part of same " 350198579b20Snd150628 "dataset")); 350298579b20Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 350398579b20Snd150628 errbuf)); 3504fa9e4066Sahrens } 350598579b20Snd150628 } 3506f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 350798579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3508fa9e4066Sahrens } else { 3509cdf5b4caSmmusante if (recursive) { 3510cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3511cdf5b4caSmmusante "recursive rename must be a snapshot")); 3512cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3513cdf5b4caSmmusante } 3514cdf5b4caSmmusante 3515f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 351698579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3517e9dbad6fSeschrock 3518fa9e4066Sahrens /* validate parents */ 3519d41c4376SMark J Musante if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3520fa9e4066Sahrens return (-1); 3521fa9e4066Sahrens 3522fa9e4066Sahrens /* make sure we're in the same pool */ 3523fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3524fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3525fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 352699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 352799653d4eSeschrock "datasets must be within same pool")); 352899653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3529fa9e4066Sahrens } 3530f2fdf992Snd150628 3531f2fdf992Snd150628 /* new name cannot be a child of the current dataset name */ 3532d41c4376SMark J Musante if (is_descendant(zhp->zfs_name, target)) { 3533f2fdf992Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3534d41c4376SMark J Musante "New dataset name cannot be a descendant of " 3535f2fdf992Snd150628 "current dataset name")); 3536f2fdf992Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3537f2fdf992Snd150628 } 3538fa9e4066Sahrens } 3539fa9e4066Sahrens 354099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 354199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 354299653d4eSeschrock 3543fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3544fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 354599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 354699653d4eSeschrock "dataset is used in a non-global zone")); 354799653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3548fa9e4066Sahrens } 3549fa9e4066Sahrens 3550cdf5b4caSmmusante if (recursive) { 3551cdf5b4caSmmusante 3552f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3553f0c5ee21Smmusante if (parentname == NULL) { 3554f0c5ee21Smmusante ret = -1; 3555f0c5ee21Smmusante goto error; 3556f0c5ee21Smmusante } 3557cdf5b4caSmmusante delim = strchr(parentname, '@'); 3558cdf5b4caSmmusante *delim = '\0'; 3559990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3560cdf5b4caSmmusante if (zhrp == NULL) { 3561f0c5ee21Smmusante ret = -1; 3562f0c5ee21Smmusante goto error; 3563cdf5b4caSmmusante } 3564cdf5b4caSmmusante 3565cdf5b4caSmmusante } else { 35660069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 356799653d4eSeschrock return (-1); 3568fa9e4066Sahrens 3569fa9e4066Sahrens if (changelist_haszonedchild(cl)) { 357099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 357199653d4eSeschrock "child dataset with inherited mountpoint is used " 357299653d4eSeschrock "in a non-global zone")); 3573e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3574fa9e4066Sahrens goto error; 3575fa9e4066Sahrens } 3576fa9e4066Sahrens 3577fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 3578fa9e4066Sahrens goto error; 3579cdf5b4caSmmusante } 3580fa9e4066Sahrens 3581e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3582fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3583fa9e4066Sahrens else 3584fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3585fa9e4066Sahrens 358698579b20Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3587e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 358898579b20Snd150628 3589cdf5b4caSmmusante zc.zc_cookie = recursive; 3590cdf5b4caSmmusante 3591ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3592cdf5b4caSmmusante /* 3593cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3594cdf5b4caSmmusante * be in zc.zc_name 3595cdf5b4caSmmusante */ 3596cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35973cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3598cdf5b4caSmmusante 3599cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3600cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3601cdf5b4caSmmusante "a child dataset already has a snapshot " 3602cdf5b4caSmmusante "with the new name")); 3603a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3604cdf5b4caSmmusante } else { 360599653d4eSeschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3606cdf5b4caSmmusante } 3607fa9e4066Sahrens 3608fa9e4066Sahrens /* 3609fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3610fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3611fa9e4066Sahrens */ 3612681d9761SEric Taylor if (!recursive) 3613fa9e4066Sahrens (void) changelist_postfix(cl); 3614cdf5b4caSmmusante } else { 3615681d9761SEric Taylor if (!recursive) { 3616fa9e4066Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 3617fa9e4066Sahrens ret = changelist_postfix(cl); 3618fa9e4066Sahrens } 3619cdf5b4caSmmusante } 3620fa9e4066Sahrens 3621fa9e4066Sahrens error: 3622cdf5b4caSmmusante if (parentname) { 3623cdf5b4caSmmusante free(parentname); 3624cdf5b4caSmmusante } 3625cdf5b4caSmmusante if (zhrp) { 3626cdf5b4caSmmusante zfs_close(zhrp); 3627cdf5b4caSmmusante } 3628cdf5b4caSmmusante if (cl) { 3629fa9e4066Sahrens changelist_free(cl); 3630cdf5b4caSmmusante } 3631fa9e4066Sahrens return (ret); 3632fa9e4066Sahrens } 3633fa9e4066Sahrens 3634e9dbad6fSeschrock nvlist_t * 3635e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3636e9dbad6fSeschrock { 3637e9dbad6fSeschrock return (zhp->zfs_user_props); 3638e9dbad6fSeschrock } 3639e9dbad6fSeschrock 364092241e0bSTom Erickson nvlist_t * 364192241e0bSTom Erickson zfs_get_recvd_props(zfs_handle_t *zhp) 364292241e0bSTom Erickson { 364392241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 364492241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 364592241e0bSTom Erickson return (NULL); 364692241e0bSTom Erickson return (zhp->zfs_recvd_props); 364792241e0bSTom Erickson } 364892241e0bSTom Erickson 3649e9dbad6fSeschrock /* 3650e9dbad6fSeschrock * This function is used by 'zfs list' to determine the exact set of columns to 3651e9dbad6fSeschrock * display, and their maximum widths. This does two main things: 3652e9dbad6fSeschrock * 3653e9dbad6fSeschrock * - If this is a list of all properties, then expand the list to include 3654e9dbad6fSeschrock * all native properties, and set a flag so that for each dataset we look 3655e9dbad6fSeschrock * for new unique user properties and add them to the list. 3656e9dbad6fSeschrock * 3657e9dbad6fSeschrock * - For non fixed-width properties, keep track of the maximum width seen 365892241e0bSTom Erickson * so that we can size the column appropriately. If the user has 365992241e0bSTom Erickson * requested received property values, we also need to compute the width 366092241e0bSTom Erickson * of the RECEIVED column. 3661e9dbad6fSeschrock */ 3662e9dbad6fSeschrock int 366392241e0bSTom Erickson zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 3664e9dbad6fSeschrock { 3665e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3666990b4856Slling zprop_list_t *entry; 3667990b4856Slling zprop_list_t **last, **start; 3668e9dbad6fSeschrock nvlist_t *userprops, *propval; 3669e9dbad6fSeschrock nvpair_t *elem; 3670e9dbad6fSeschrock char *strval; 3671e9dbad6fSeschrock char buf[ZFS_MAXPROPLEN]; 3672e9dbad6fSeschrock 3673990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3674e9dbad6fSeschrock return (-1); 3675e9dbad6fSeschrock 3676e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3677e9dbad6fSeschrock 3678e9dbad6fSeschrock entry = *plp; 3679e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3680e9dbad6fSeschrock /* 3681e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3682e9dbad6fSeschrock * start by incrementing our list pointer to the first 3683e9dbad6fSeschrock * non-native property. 3684e9dbad6fSeschrock */ 3685e9dbad6fSeschrock start = plp; 3686e9dbad6fSeschrock while (*start != NULL) { 3687990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3688e9dbad6fSeschrock break; 3689e9dbad6fSeschrock start = &(*start)->pl_next; 3690e9dbad6fSeschrock } 3691e9dbad6fSeschrock 3692e9dbad6fSeschrock elem = NULL; 3693e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3694e9dbad6fSeschrock /* 3695e9dbad6fSeschrock * See if we've already found this property in our list. 3696e9dbad6fSeschrock */ 3697e9dbad6fSeschrock for (last = start; *last != NULL; 3698e9dbad6fSeschrock last = &(*last)->pl_next) { 3699e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3700e9dbad6fSeschrock nvpair_name(elem)) == 0) 3701e9dbad6fSeschrock break; 3702e9dbad6fSeschrock } 3703e9dbad6fSeschrock 3704e9dbad6fSeschrock if (*last == NULL) { 3705e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3706990b4856Slling sizeof (zprop_list_t))) == NULL || 3707e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3708e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3709e9dbad6fSeschrock free(entry); 3710e9dbad6fSeschrock return (-1); 3711e9dbad6fSeschrock } 3712e9dbad6fSeschrock 3713990b4856Slling entry->pl_prop = ZPROP_INVAL; 3714e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3715e9dbad6fSeschrock entry->pl_all = B_TRUE; 3716e9dbad6fSeschrock *last = entry; 3717e9dbad6fSeschrock } 3718e9dbad6fSeschrock } 3719e9dbad6fSeschrock } 3720e9dbad6fSeschrock 3721e9dbad6fSeschrock /* 3722e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3723e9dbad6fSeschrock */ 3724e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3725e9dbad6fSeschrock if (entry->pl_fixed) 3726e9dbad6fSeschrock continue; 3727e9dbad6fSeschrock 3728990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3729e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3730e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3731e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3732e9dbad6fSeschrock entry->pl_width = strlen(buf); 3733e9dbad6fSeschrock } 373492241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 373592241e0bSTom Erickson zfs_prop_to_name(entry->pl_prop), 373692241e0bSTom Erickson buf, sizeof (buf), B_FALSE) == 0) 373792241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 373892241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 373992241e0bSTom Erickson } else { 374092241e0bSTom Erickson if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 374192241e0bSTom Erickson &propval) == 0) { 3742e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3743990b4856Slling ZPROP_VALUE, &strval) == 0); 3744e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3745e9dbad6fSeschrock entry->pl_width = strlen(strval); 3746e9dbad6fSeschrock } 374792241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 374892241e0bSTom Erickson entry->pl_user_prop, 374992241e0bSTom Erickson buf, sizeof (buf), B_FALSE) == 0) 375092241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 375192241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 375292241e0bSTom Erickson } 3753e9dbad6fSeschrock } 3754e9dbad6fSeschrock 3755e9dbad6fSeschrock return (0); 3756e9dbad6fSeschrock } 3757ecd6cf80Smarks 3758ecd6cf80Smarks int 3759ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3760743a77edSAlan Wright char *resource, void *export, void *sharetab, 3761743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3762ecd6cf80Smarks { 3763ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3764ecd6cf80Smarks int error; 3765ecd6cf80Smarks 3766ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3767ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3768743a77edSAlan Wright if (resource) 3769743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3770ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3771ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3772da6c28aaSamw zc.zc_share.z_sharetype = operation; 3773ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3774ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3775ecd6cf80Smarks return (error); 3776ecd6cf80Smarks } 37772e5e9e19SSanjeev Bagewadi 37782e5e9e19SSanjeev Bagewadi void 37792e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37802e5e9e19SSanjeev Bagewadi { 37812e5e9e19SSanjeev Bagewadi nvpair_t *curr; 37822e5e9e19SSanjeev Bagewadi 37832e5e9e19SSanjeev Bagewadi /* 37842e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 37852e5e9e19SSanjeev Bagewadi * properties. 37862e5e9e19SSanjeev Bagewadi */ 37872e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 37882e5e9e19SSanjeev Bagewadi 37892e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37902e5e9e19SSanjeev Bagewadi 37912e5e9e19SSanjeev Bagewadi while (curr) { 37922e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37932e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37942e5e9e19SSanjeev Bagewadi 379514843421SMatthew Ahrens /* 3796faaa6415SEric Schrock * User properties will result in ZPROP_INVAL, and since we 3797faaa6415SEric Schrock * only know how to prune standard ZFS properties, we always 3798faaa6415SEric Schrock * leave these in the list. This can also happen if we 3799faaa6415SEric Schrock * encounter an unknown DSL property (when running older 3800faaa6415SEric Schrock * software, for example). 380114843421SMatthew Ahrens */ 380214843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 38032e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 38042e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 38052e5e9e19SSanjeev Bagewadi curr = next; 38062e5e9e19SSanjeev Bagewadi } 38072e5e9e19SSanjeev Bagewadi } 3808743a77edSAlan Wright 3809743a77edSAlan Wright static int 3810743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3811743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3812743a77edSAlan Wright { 3813743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3814743a77edSAlan Wright nvlist_t *nvlist = NULL; 3815743a77edSAlan Wright int error; 3816743a77edSAlan Wright 3817743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3818743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3819743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3820743a77edSAlan Wright 3821743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3822743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3823743a77edSAlan Wright (void) no_memory(hdl); 3824743a77edSAlan Wright return (NULL); 3825743a77edSAlan Wright } 3826743a77edSAlan Wright } 3827743a77edSAlan Wright 3828743a77edSAlan Wright switch (cmd) { 3829743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3830743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3831743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3832743a77edSAlan Wright break; 3833743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3834743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3835743a77edSAlan Wright resource1) != 0) { 3836743a77edSAlan Wright (void) no_memory(hdl); 3837743a77edSAlan Wright return (-1); 3838743a77edSAlan Wright } 3839743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 3840743a77edSAlan Wright resource2) != 0) { 3841743a77edSAlan Wright (void) no_memory(hdl); 3842743a77edSAlan Wright return (-1); 3843743a77edSAlan Wright } 3844743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 3845743a77edSAlan Wright nvlist_free(nvlist); 3846743a77edSAlan Wright return (-1); 3847743a77edSAlan Wright } 3848743a77edSAlan Wright break; 3849743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3850743a77edSAlan Wright break; 3851743a77edSAlan Wright default: 3852743a77edSAlan Wright return (-1); 3853743a77edSAlan Wright } 3854743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 3855743a77edSAlan Wright if (nvlist) 3856743a77edSAlan Wright nvlist_free(nvlist); 3857743a77edSAlan Wright return (error); 3858743a77edSAlan Wright } 3859743a77edSAlan Wright 3860743a77edSAlan Wright int 3861743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 3862743a77edSAlan Wright char *path, char *resource) 3863743a77edSAlan Wright { 3864743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 3865743a77edSAlan Wright resource, NULL)); 3866743a77edSAlan Wright } 3867743a77edSAlan Wright 3868743a77edSAlan Wright int 3869743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 3870743a77edSAlan Wright char *path, char *resource) 3871743a77edSAlan Wright { 3872743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 3873743a77edSAlan Wright resource, NULL)); 3874743a77edSAlan Wright } 3875743a77edSAlan Wright 3876743a77edSAlan Wright int 3877743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 3878743a77edSAlan Wright { 3879743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 3880743a77edSAlan Wright NULL, NULL)); 3881743a77edSAlan Wright } 3882743a77edSAlan Wright 3883743a77edSAlan Wright int 3884743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 3885743a77edSAlan Wright char *oldname, char *newname) 3886743a77edSAlan Wright { 3887743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 3888743a77edSAlan Wright oldname, newname)); 3889743a77edSAlan Wright } 389014843421SMatthew Ahrens 389114843421SMatthew Ahrens int 389214843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 389314843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 389414843421SMatthew Ahrens { 389514843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 389614843421SMatthew Ahrens int error; 389714843421SMatthew Ahrens zfs_useracct_t buf[100]; 389814843421SMatthew Ahrens 389914843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 390014843421SMatthew Ahrens 390114843421SMatthew Ahrens zc.zc_objset_type = type; 390214843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 390314843421SMatthew Ahrens 390414843421SMatthew Ahrens /* CONSTCOND */ 390514843421SMatthew Ahrens while (1) { 390614843421SMatthew Ahrens zfs_useracct_t *zua = buf; 390714843421SMatthew Ahrens 390814843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 390914843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 391014843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 391114843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 391214843421SMatthew Ahrens break; 391314843421SMatthew Ahrens 391414843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 39150aea4b19SMatthew Ahrens error = func(arg, zua->zu_domain, zua->zu_rid, 39160aea4b19SMatthew Ahrens zua->zu_space); 39170aea4b19SMatthew Ahrens if (error != 0) 39180aea4b19SMatthew Ahrens return (error); 391914843421SMatthew Ahrens zua++; 392014843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 392114843421SMatthew Ahrens } 392214843421SMatthew Ahrens } 392314843421SMatthew Ahrens 392414843421SMatthew Ahrens return (error); 392514843421SMatthew Ahrens } 3926842727c2SChris Kirby 3927842727c2SChris Kirby int 3928842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 3929c99e4bdcSChris Kirby boolean_t recursive, boolean_t temphold, boolean_t enoent_ok, 3930*a7f53a56SChris Kirby int cleanup_fd, uint64_t dsobj, uint64_t createtxg) 3931842727c2SChris Kirby { 3932842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 3933842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 3934842727c2SChris Kirby 3935*a7f53a56SChris Kirby ASSERT(!recursive || dsobj == 0); 3936*a7f53a56SChris Kirby 3937842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3938842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3939ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 3940ca45db41SChris Kirby >= sizeof (zc.zc_string)) 3941ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 3942842727c2SChris Kirby zc.zc_cookie = recursive; 3943ca45db41SChris Kirby zc.zc_temphold = temphold; 3944c99e4bdcSChris Kirby zc.zc_cleanup_fd = cleanup_fd; 3945*a7f53a56SChris Kirby zc.zc_sendobj = dsobj; 3946*a7f53a56SChris Kirby zc.zc_createtxg = createtxg; 3947842727c2SChris Kirby 3948842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 3949842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 3950842727c2SChris Kirby 3951842727c2SChris Kirby /* 3952842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 3953842727c2SChris Kirby * zc.zc_name. 3954842727c2SChris Kirby */ 3955842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3956842727c2SChris Kirby "cannot hold '%s@%s'"), zc.zc_name, snapname); 3957842727c2SChris Kirby switch (errno) { 395815508ac0SChris Kirby case E2BIG: 395915508ac0SChris Kirby /* 396015508ac0SChris Kirby * Temporary tags wind up having the ds object id 396115508ac0SChris Kirby * prepended. So even if we passed the length check 396215508ac0SChris Kirby * above, it's still possible for the tag to wind 396315508ac0SChris Kirby * up being slightly too long. 396415508ac0SChris Kirby */ 396515508ac0SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 3966842727c2SChris Kirby case ENOTSUP: 3967842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3968842727c2SChris Kirby "pool must be upgraded")); 3969842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3970842727c2SChris Kirby case EINVAL: 3971842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3972842727c2SChris Kirby case EEXIST: 3973842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 3974818119b8SChris Kirby case ENOENT: 3975818119b8SChris Kirby if (enoent_ok) 3976*a7f53a56SChris Kirby return (ENOENT); 3977818119b8SChris Kirby /* FALLTHROUGH */ 3978842727c2SChris Kirby default: 3979842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 3980842727c2SChris Kirby } 3981842727c2SChris Kirby } 3982842727c2SChris Kirby 3983842727c2SChris Kirby return (0); 3984842727c2SChris Kirby } 3985842727c2SChris Kirby 3986842727c2SChris Kirby int 3987842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 3988842727c2SChris Kirby boolean_t recursive) 3989842727c2SChris Kirby { 3990842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 3991842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 3992842727c2SChris Kirby 3993842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3994842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3995ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 3996ca45db41SChris Kirby >= sizeof (zc.zc_string)) 3997ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 3998842727c2SChris Kirby zc.zc_cookie = recursive; 3999842727c2SChris Kirby 4000842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 4001842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 4002842727c2SChris Kirby 4003842727c2SChris Kirby /* 4004842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 4005842727c2SChris Kirby * zc.zc_name. 4006842727c2SChris Kirby */ 4007842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4008620252bcSChris Kirby "cannot release '%s' from '%s@%s'"), tag, zc.zc_name, 4009620252bcSChris Kirby snapname); 4010842727c2SChris Kirby switch (errno) { 4011842727c2SChris Kirby case ESRCH: 4012842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 4013842727c2SChris Kirby case ENOTSUP: 4014842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4015842727c2SChris Kirby "pool must be upgraded")); 4016842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4017842727c2SChris Kirby case EINVAL: 4018842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4019842727c2SChris Kirby default: 4020842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4021842727c2SChris Kirby } 4022842727c2SChris Kirby } 4023842727c2SChris Kirby 4024842727c2SChris Kirby return (0); 4025842727c2SChris Kirby } 4026ca45db41SChris Kirby 4027c1449561SEric Taylor uint64_t 4028c1449561SEric Taylor zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4029c1449561SEric Taylor { 4030c1449561SEric Taylor uint64_t numdb; 4031c1449561SEric Taylor uint64_t nblocks, volblocksize; 4032c1449561SEric Taylor int ncopies; 4033c1449561SEric Taylor char *strval; 4034c1449561SEric Taylor 4035c1449561SEric Taylor if (nvlist_lookup_string(props, 4036c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4037c1449561SEric Taylor ncopies = atoi(strval); 4038c1449561SEric Taylor else 4039c1449561SEric Taylor ncopies = 1; 4040c1449561SEric Taylor if (nvlist_lookup_uint64(props, 4041c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4042c1449561SEric Taylor &volblocksize) != 0) 4043c1449561SEric Taylor volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4044c1449561SEric Taylor nblocks = volsize/volblocksize; 4045c1449561SEric Taylor /* start with metadnode L0-L6 */ 4046c1449561SEric Taylor numdb = 7; 4047c1449561SEric Taylor /* calculate number of indirects */ 4048c1449561SEric Taylor while (nblocks > 1) { 4049c1449561SEric Taylor nblocks += DNODES_PER_LEVEL - 1; 4050c1449561SEric Taylor nblocks /= DNODES_PER_LEVEL; 4051c1449561SEric Taylor numdb += nblocks; 4052c1449561SEric Taylor } 4053c1449561SEric Taylor numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4054c1449561SEric Taylor volsize *= ncopies; 4055c1449561SEric Taylor /* 4056c1449561SEric Taylor * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4057c1449561SEric Taylor * compressed, but in practice they compress down to about 4058c1449561SEric Taylor * 1100 bytes 4059c1449561SEric Taylor */ 4060c1449561SEric Taylor numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4061c1449561SEric Taylor volsize += numdb; 4062c1449561SEric Taylor return (volsize); 4063c1449561SEric Taylor } 4064