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 /* 23818119b8SChris Kirby * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #include <ctype.h> 28fa9e4066Sahrens #include <errno.h> 29fa9e4066Sahrens #include <libintl.h> 30fa9e4066Sahrens #include <math.h> 31fa9e4066Sahrens #include <stdio.h> 32fa9e4066Sahrens #include <stdlib.h> 33fa9e4066Sahrens #include <strings.h> 34fa9e4066Sahrens #include <unistd.h> 353cb34c60Sahrens #include <stddef.h> 36fa9e4066Sahrens #include <zone.h> 3799653d4eSeschrock #include <fcntl.h> 38fa9e4066Sahrens #include <sys/mntent.h> 39b12a1c38Slling #include <sys/mount.h> 40ecd6cf80Smarks #include <priv.h> 41ecd6cf80Smarks #include <pwd.h> 42ecd6cf80Smarks #include <grp.h> 43ecd6cf80Smarks #include <stddef.h> 44ecd6cf80Smarks #include <ucred.h> 4514843421SMatthew Ahrens #include <idmap.h> 4614843421SMatthew Ahrens #include <aclutils.h> 473b12c289SMatthew Ahrens #include <directory.h> 48fa9e4066Sahrens 49c1449561SEric Taylor #include <sys/dnode.h> 50fa9e4066Sahrens #include <sys/spa.h> 51e9dbad6fSeschrock #include <sys/zap.h> 52fa9e4066Sahrens #include <libzfs.h> 53fa9e4066Sahrens 54fa9e4066Sahrens #include "zfs_namecheck.h" 55fa9e4066Sahrens #include "zfs_prop.h" 56fa9e4066Sahrens #include "libzfs_impl.h" 57ecd6cf80Smarks #include "zfs_deleg.h" 58fa9e4066Sahrens 5914843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned, 6014843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 61cdf5b4caSmmusante 62fa9e4066Sahrens /* 63fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 64fa9e4066Sahrens * readable form. 65fa9e4066Sahrens */ 66fa9e4066Sahrens const char * 67fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 68fa9e4066Sahrens { 69fa9e4066Sahrens switch (type) { 70fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 71fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 72fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 73fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 74fa9e4066Sahrens case ZFS_TYPE_VOLUME: 75fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 76fa9e4066Sahrens } 77fa9e4066Sahrens 78fa9e4066Sahrens return (NULL); 79fa9e4066Sahrens } 80fa9e4066Sahrens 81fa9e4066Sahrens /* 82fa9e4066Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 83fa9e4066Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 84fa9e4066Sahrens * We guess what the type would have been based on the path and the mask of 85fa9e4066Sahrens * acceptable types. 86fa9e4066Sahrens */ 87fa9e4066Sahrens static const char * 88fa9e4066Sahrens path_to_str(const char *path, int types) 89fa9e4066Sahrens { 90fa9e4066Sahrens /* 91fa9e4066Sahrens * When given a single type, always report the exact type. 92fa9e4066Sahrens */ 93fa9e4066Sahrens if (types == ZFS_TYPE_SNAPSHOT) 94fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 95fa9e4066Sahrens if (types == ZFS_TYPE_FILESYSTEM) 96fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 97fa9e4066Sahrens if (types == ZFS_TYPE_VOLUME) 98fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 99fa9e4066Sahrens 100fa9e4066Sahrens /* 101fa9e4066Sahrens * The user is requesting more than one type of dataset. If this is the 102fa9e4066Sahrens * case, consult the path itself. If we're looking for a snapshot, and 103fa9e4066Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 104fa9e4066Sahrens * snapshot attribute and try again. 105fa9e4066Sahrens */ 106fa9e4066Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 107fa9e4066Sahrens if (strchr(path, '@') != NULL) 108fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 109fa9e4066Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 110fa9e4066Sahrens } 111fa9e4066Sahrens 112fa9e4066Sahrens /* 113fa9e4066Sahrens * The user has requested either filesystems or volumes. 114fa9e4066Sahrens * We have no way of knowing a priori what type this would be, so always 115fa9e4066Sahrens * report it as "filesystem" or "volume", our two primitive types. 116fa9e4066Sahrens */ 117fa9e4066Sahrens if (types & ZFS_TYPE_FILESYSTEM) 118fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 119fa9e4066Sahrens 120fa9e4066Sahrens assert(types & ZFS_TYPE_VOLUME); 121fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 122fa9e4066Sahrens } 123fa9e4066Sahrens 124fa9e4066Sahrens /* 125fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 12614843421SMatthew Ahrens * provide a more meaningful error message. We call zfs_error_aux() to 12714843421SMatthew Ahrens * explain exactly why the name was not valid. 128fa9e4066Sahrens */ 129fa9e4066Sahrens static int 130f18faf3fSek zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 131f18faf3fSek boolean_t modifying) 132fa9e4066Sahrens { 133fa9e4066Sahrens namecheck_err_t why; 134fa9e4066Sahrens char what; 135fa9e4066Sahrens 136fa9e4066Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 13799653d4eSeschrock if (hdl != NULL) { 138fa9e4066Sahrens switch (why) { 139b81d61a6Slling case NAME_ERR_TOOLONG: 14099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14199653d4eSeschrock "name is too long")); 142b81d61a6Slling break; 143b81d61a6Slling 144fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 14599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14699653d4eSeschrock "leading slash in name")); 147fa9e4066Sahrens break; 148fa9e4066Sahrens 149fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 15099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15199653d4eSeschrock "empty component in name")); 152fa9e4066Sahrens break; 153fa9e4066Sahrens 154fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 15599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15699653d4eSeschrock "trailing slash in name")); 157fa9e4066Sahrens break; 158fa9e4066Sahrens 159fa9e4066Sahrens case NAME_ERR_INVALCHAR: 16099653d4eSeschrock zfs_error_aux(hdl, 161fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 16299653d4eSeschrock "'%c' in name"), what); 163fa9e4066Sahrens break; 164fa9e4066Sahrens 165fa9e4066Sahrens case NAME_ERR_MULTIPLE_AT: 16699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 16799653d4eSeschrock "multiple '@' delimiters in name")); 168fa9e4066Sahrens break; 1695ad82045Snd 1705ad82045Snd case NAME_ERR_NOLETTER: 1715ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1725ad82045Snd "pool doesn't begin with a letter")); 1735ad82045Snd break; 1745ad82045Snd 1755ad82045Snd case NAME_ERR_RESERVED: 1765ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1775ad82045Snd "name is reserved")); 1785ad82045Snd break; 1795ad82045Snd 1805ad82045Snd case NAME_ERR_DISKLIKE: 1815ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1825ad82045Snd "reserved disk name")); 1835ad82045Snd break; 184fa9e4066Sahrens } 185fa9e4066Sahrens } 186fa9e4066Sahrens 187fa9e4066Sahrens return (0); 188fa9e4066Sahrens } 189fa9e4066Sahrens 190fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 19199653d4eSeschrock if (hdl != NULL) 19299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19399653d4eSeschrock "snapshot delimiter '@' in filesystem name")); 194fa9e4066Sahrens return (0); 195fa9e4066Sahrens } 196fa9e4066Sahrens 1971d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 1981d452cf5Sahrens if (hdl != NULL) 1991d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 200d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 2011d452cf5Sahrens return (0); 2021d452cf5Sahrens } 2031d452cf5Sahrens 204f18faf3fSek if (modifying && strchr(path, '%') != NULL) { 205f18faf3fSek if (hdl != NULL) 206f18faf3fSek zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 207f18faf3fSek "invalid character %c in name"), '%'); 208f18faf3fSek return (0); 209f18faf3fSek } 210f18faf3fSek 21199653d4eSeschrock return (-1); 212fa9e4066Sahrens } 213fa9e4066Sahrens 214fa9e4066Sahrens int 215fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 216fa9e4066Sahrens { 217e7cbe64fSgw if (type == ZFS_TYPE_POOL) 218e7cbe64fSgw return (zpool_name_valid(NULL, B_FALSE, name)); 219f18faf3fSek return (zfs_validate_name(NULL, name, type, B_FALSE)); 220fa9e4066Sahrens } 221fa9e4066Sahrens 222e9dbad6fSeschrock /* 223e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 224e9dbad6fSeschrock * properties into a separate nvlist. 225e9dbad6fSeschrock */ 226fac3008cSeschrock static nvlist_t * 227fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 228e9dbad6fSeschrock { 229e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 230e9dbad6fSeschrock nvpair_t *elem; 231e9dbad6fSeschrock nvlist_t *propval; 232fac3008cSeschrock nvlist_t *nvl; 233e9dbad6fSeschrock 234fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 235fac3008cSeschrock (void) no_memory(hdl); 236fac3008cSeschrock return (NULL); 237fac3008cSeschrock } 238e9dbad6fSeschrock 239e9dbad6fSeschrock elem = NULL; 240fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 241e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 242e9dbad6fSeschrock continue; 243e9dbad6fSeschrock 244e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 245fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 246fac3008cSeschrock nvlist_free(nvl); 247fac3008cSeschrock (void) no_memory(hdl); 248fac3008cSeschrock return (NULL); 249fac3008cSeschrock } 250e9dbad6fSeschrock } 251e9dbad6fSeschrock 252fac3008cSeschrock return (nvl); 253e9dbad6fSeschrock } 254e9dbad6fSeschrock 25529ab75c9Srm static zpool_handle_t * 25629ab75c9Srm zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 25729ab75c9Srm { 25829ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 25929ab75c9Srm zpool_handle_t *zph; 26029ab75c9Srm 26129ab75c9Srm if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 26229ab75c9Srm if (hdl->libzfs_pool_handles != NULL) 26329ab75c9Srm zph->zpool_next = hdl->libzfs_pool_handles; 26429ab75c9Srm hdl->libzfs_pool_handles = zph; 26529ab75c9Srm } 26629ab75c9Srm return (zph); 26729ab75c9Srm } 26829ab75c9Srm 26929ab75c9Srm static zpool_handle_t * 27029ab75c9Srm zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 27129ab75c9Srm { 27229ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 27329ab75c9Srm zpool_handle_t *zph = hdl->libzfs_pool_handles; 27429ab75c9Srm 27529ab75c9Srm while ((zph != NULL) && 27629ab75c9Srm (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 27729ab75c9Srm zph = zph->zpool_next; 27829ab75c9Srm return (zph); 27929ab75c9Srm } 28029ab75c9Srm 28129ab75c9Srm /* 28229ab75c9Srm * Returns a handle to the pool that contains the provided dataset. 28329ab75c9Srm * If a handle to that pool already exists then that handle is returned. 28429ab75c9Srm * Otherwise, a new handle is created and added to the list of handles. 28529ab75c9Srm */ 28629ab75c9Srm static zpool_handle_t * 28729ab75c9Srm zpool_handle(zfs_handle_t *zhp) 28829ab75c9Srm { 28929ab75c9Srm char *pool_name; 29029ab75c9Srm int len; 29129ab75c9Srm zpool_handle_t *zph; 29229ab75c9Srm 29329ab75c9Srm len = strcspn(zhp->zfs_name, "/@") + 1; 29429ab75c9Srm pool_name = zfs_alloc(zhp->zfs_hdl, len); 29529ab75c9Srm (void) strlcpy(pool_name, zhp->zfs_name, len); 29629ab75c9Srm 29729ab75c9Srm zph = zpool_find_handle(zhp, pool_name, len); 29829ab75c9Srm if (zph == NULL) 29929ab75c9Srm zph = zpool_add_handle(zhp, pool_name); 30029ab75c9Srm 30129ab75c9Srm free(pool_name); 30229ab75c9Srm return (zph); 30329ab75c9Srm } 30429ab75c9Srm 30529ab75c9Srm void 30629ab75c9Srm zpool_free_handles(libzfs_handle_t *hdl) 30729ab75c9Srm { 30829ab75c9Srm zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 30929ab75c9Srm 31029ab75c9Srm while (zph != NULL) { 31129ab75c9Srm next = zph->zpool_next; 31229ab75c9Srm zpool_close(zph); 31329ab75c9Srm zph = next; 31429ab75c9Srm } 31529ab75c9Srm hdl->libzfs_pool_handles = NULL; 31629ab75c9Srm } 31729ab75c9Srm 318fa9e4066Sahrens /* 319fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 320fa9e4066Sahrens */ 321fa9e4066Sahrens static int 322ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 323fa9e4066Sahrens { 324e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 325fa9e4066Sahrens 326ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 327fa9e4066Sahrens 328ebedde84SEric Taylor while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3297f7322feSeschrock if (errno == ENOMEM) { 330ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 33199653d4eSeschrock return (-1); 332e9dbad6fSeschrock } 3337f7322feSeschrock } else { 3347f7322feSeschrock return (-1); 3357f7322feSeschrock } 3367f7322feSeschrock } 337ebedde84SEric Taylor return (0); 338ebedde84SEric Taylor } 339fa9e4066Sahrens 34092241e0bSTom Erickson /* 34192241e0bSTom Erickson * Utility function to get the received properties of the given object. 34292241e0bSTom Erickson */ 34392241e0bSTom Erickson static int 34492241e0bSTom Erickson get_recvd_props_ioctl(zfs_handle_t *zhp) 34592241e0bSTom Erickson { 34692241e0bSTom Erickson libzfs_handle_t *hdl = zhp->zfs_hdl; 34792241e0bSTom Erickson nvlist_t *recvdprops; 34892241e0bSTom Erickson zfs_cmd_t zc = { 0 }; 34992241e0bSTom Erickson int err; 35092241e0bSTom Erickson 35192241e0bSTom Erickson if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 35292241e0bSTom Erickson return (-1); 35392241e0bSTom Erickson 35492241e0bSTom Erickson (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35592241e0bSTom Erickson 35692241e0bSTom Erickson while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 35792241e0bSTom Erickson if (errno == ENOMEM) { 35892241e0bSTom Erickson if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 35992241e0bSTom Erickson return (-1); 36092241e0bSTom Erickson } 36192241e0bSTom Erickson } else { 36292241e0bSTom Erickson zcmd_free_nvlists(&zc); 36392241e0bSTom Erickson return (-1); 36492241e0bSTom Erickson } 36592241e0bSTom Erickson } 36692241e0bSTom Erickson 36792241e0bSTom Erickson err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 36892241e0bSTom Erickson zcmd_free_nvlists(&zc); 36992241e0bSTom Erickson if (err != 0) 37092241e0bSTom Erickson return (-1); 37192241e0bSTom Erickson 37292241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 37392241e0bSTom Erickson zhp->zfs_recvd_props = recvdprops; 37492241e0bSTom Erickson 37592241e0bSTom Erickson return (0); 37692241e0bSTom Erickson } 37792241e0bSTom Erickson 378ebedde84SEric Taylor static int 379ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 380ebedde84SEric Taylor { 381ebedde84SEric Taylor nvlist_t *allprops, *userprops; 382fa9e4066Sahrens 383ebedde84SEric Taylor zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 384ebedde84SEric Taylor 385ebedde84SEric Taylor if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 38699653d4eSeschrock return (-1); 38799653d4eSeschrock } 388fa9e4066Sahrens 38914843421SMatthew Ahrens /* 39014843421SMatthew Ahrens * XXX Why do we store the user props separately, in addition to 39114843421SMatthew Ahrens * storing them in zfs_props? 39214843421SMatthew Ahrens */ 393fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 394fac3008cSeschrock nvlist_free(allprops); 395e9dbad6fSeschrock return (-1); 396fac3008cSeschrock } 397fac3008cSeschrock 398fac3008cSeschrock nvlist_free(zhp->zfs_props); 399fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 400fac3008cSeschrock 401fac3008cSeschrock zhp->zfs_props = allprops; 402fac3008cSeschrock zhp->zfs_user_props = userprops; 40399653d4eSeschrock 404fa9e4066Sahrens return (0); 405fa9e4066Sahrens } 406fa9e4066Sahrens 407ebedde84SEric Taylor static int 408ebedde84SEric Taylor get_stats(zfs_handle_t *zhp) 409ebedde84SEric Taylor { 410ebedde84SEric Taylor int rc = 0; 411ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 412ebedde84SEric Taylor 413ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 414ebedde84SEric Taylor return (-1); 415ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) != 0) 416ebedde84SEric Taylor rc = -1; 417ebedde84SEric Taylor else if (put_stats_zhdl(zhp, &zc) != 0) 418ebedde84SEric Taylor rc = -1; 419ebedde84SEric Taylor zcmd_free_nvlists(&zc); 420ebedde84SEric Taylor return (rc); 421ebedde84SEric Taylor } 422ebedde84SEric Taylor 423fa9e4066Sahrens /* 424fa9e4066Sahrens * Refresh the properties currently stored in the handle. 425fa9e4066Sahrens */ 426fa9e4066Sahrens void 427fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 428fa9e4066Sahrens { 429fa9e4066Sahrens (void) get_stats(zhp); 430fa9e4066Sahrens } 431fa9e4066Sahrens 432fa9e4066Sahrens /* 433fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 434fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 435fa9e4066Sahrens */ 436ebedde84SEric Taylor static int 437ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 438fa9e4066Sahrens { 439503ad85cSMatthew Ahrens if (put_stats_zhdl(zhp, zc) != 0) 440ebedde84SEric Taylor return (-1); 44131fd60d3Sahrens 442fa9e4066Sahrens /* 443fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 444fa9e4066Sahrens * the high-level type. 445fa9e4066Sahrens */ 446a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 447a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 448a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 449a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 450a2eea2e1Sahrens else 451a2eea2e1Sahrens abort(); 452a2eea2e1Sahrens 453fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 454fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 455fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 456fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 457fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 458fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 459fa9e4066Sahrens else 46099653d4eSeschrock abort(); /* we should never see any other types */ 461fa9e4066Sahrens 462*9fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 463*9fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States return (-1); 464*9fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 465ebedde84SEric Taylor return (0); 466ebedde84SEric Taylor } 467ebedde84SEric Taylor 468ebedde84SEric Taylor zfs_handle_t * 469ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 470ebedde84SEric Taylor { 471ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 472ebedde84SEric Taylor 473ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 474ebedde84SEric Taylor 475ebedde84SEric Taylor if (zhp == NULL) 476ebedde84SEric Taylor return (NULL); 477ebedde84SEric Taylor 478ebedde84SEric Taylor zhp->zfs_hdl = hdl; 479ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 480ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 481ebedde84SEric Taylor free(zhp); 482ebedde84SEric Taylor return (NULL); 483ebedde84SEric Taylor } 484ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 485ebedde84SEric Taylor zcmd_free_nvlists(&zc); 486ebedde84SEric Taylor free(zhp); 487ebedde84SEric Taylor return (NULL); 488ebedde84SEric Taylor } 489ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 490ebedde84SEric Taylor free(zhp); 491ebedde84SEric Taylor zhp = NULL; 492ebedde84SEric Taylor } 493ebedde84SEric Taylor zcmd_free_nvlists(&zc); 494ebedde84SEric Taylor return (zhp); 495ebedde84SEric Taylor } 496ebedde84SEric Taylor 497ebedde84SEric Taylor static zfs_handle_t * 498ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 499ebedde84SEric Taylor { 500ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 501ebedde84SEric Taylor 502ebedde84SEric Taylor if (zhp == NULL) 503ebedde84SEric Taylor return (NULL); 504ebedde84SEric Taylor 505ebedde84SEric Taylor zhp->zfs_hdl = hdl; 506ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 507ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 508ebedde84SEric Taylor free(zhp); 509ebedde84SEric Taylor return (NULL); 510ebedde84SEric Taylor } 511fa9e4066Sahrens return (zhp); 512fa9e4066Sahrens } 513fa9e4066Sahrens 514fa9e4066Sahrens /* 515fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 516fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 517fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 518fa9e4066Sahrens */ 519fa9e4066Sahrens zfs_handle_t * 52099653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 521fa9e4066Sahrens { 522fa9e4066Sahrens zfs_handle_t *zhp; 52399653d4eSeschrock char errbuf[1024]; 52499653d4eSeschrock 52599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 52699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 527fa9e4066Sahrens 528fa9e4066Sahrens /* 52999653d4eSeschrock * Validate the name before we even try to open it. 530fa9e4066Sahrens */ 531f18faf3fSek if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 53299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 53399653d4eSeschrock "invalid dataset name")); 53499653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 535fa9e4066Sahrens return (NULL); 536fa9e4066Sahrens } 537fa9e4066Sahrens 538fa9e4066Sahrens /* 539fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 540fa9e4066Sahrens */ 541fa9e4066Sahrens errno = 0; 54299653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 543ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 544fa9e4066Sahrens return (NULL); 545fa9e4066Sahrens } 546fa9e4066Sahrens 547fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 54899653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 54994de1d4cSeschrock zfs_close(zhp); 550fa9e4066Sahrens return (NULL); 551fa9e4066Sahrens } 552fa9e4066Sahrens 553fa9e4066Sahrens return (zhp); 554fa9e4066Sahrens } 555fa9e4066Sahrens 556fa9e4066Sahrens /* 557fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 558fa9e4066Sahrens */ 559fa9e4066Sahrens void 560fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 561fa9e4066Sahrens { 562fa9e4066Sahrens if (zhp->zfs_mntopts) 563fa9e4066Sahrens free(zhp->zfs_mntopts); 564e9dbad6fSeschrock nvlist_free(zhp->zfs_props); 565e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 56692241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 567fa9e4066Sahrens free(zhp); 568fa9e4066Sahrens } 569fa9e4066Sahrens 570ebedde84SEric Taylor typedef struct mnttab_node { 571ebedde84SEric Taylor struct mnttab mtn_mt; 572ebedde84SEric Taylor avl_node_t mtn_node; 573ebedde84SEric Taylor } mnttab_node_t; 574ebedde84SEric Taylor 575ebedde84SEric Taylor static int 576ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 577ebedde84SEric Taylor { 578ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 579ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 580ebedde84SEric Taylor int rv; 581ebedde84SEric Taylor 582ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 583ebedde84SEric Taylor 584ebedde84SEric Taylor if (rv == 0) 585ebedde84SEric Taylor return (0); 586ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 587ebedde84SEric Taylor } 588ebedde84SEric Taylor 589ebedde84SEric Taylor void 590ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 591ebedde84SEric Taylor { 592ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 593ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 594ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 595b2634b9cSEric Taylor } 596b2634b9cSEric Taylor 597b2634b9cSEric Taylor void 598b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 599b2634b9cSEric Taylor { 600b2634b9cSEric Taylor struct mnttab entry; 601ebedde84SEric Taylor 602ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 603ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 604ebedde84SEric Taylor mnttab_node_t *mtn; 605ebedde84SEric Taylor 606ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 607ebedde84SEric Taylor continue; 608ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 609ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 610ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 611ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 612ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 613ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 614ebedde84SEric Taylor } 615ebedde84SEric Taylor } 616ebedde84SEric Taylor 617ebedde84SEric Taylor void 618ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 619ebedde84SEric Taylor { 620ebedde84SEric Taylor void *cookie = NULL; 621ebedde84SEric Taylor mnttab_node_t *mtn; 622ebedde84SEric Taylor 623ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 624ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 625ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 626ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 627ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 628ebedde84SEric Taylor free(mtn); 629ebedde84SEric Taylor } 630ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 631ebedde84SEric Taylor } 632ebedde84SEric Taylor 633b2634b9cSEric Taylor void 634b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 635b2634b9cSEric Taylor { 636b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 637b2634b9cSEric Taylor } 638b2634b9cSEric Taylor 639ebedde84SEric Taylor int 640ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 641ebedde84SEric Taylor struct mnttab *entry) 642ebedde84SEric Taylor { 643ebedde84SEric Taylor mnttab_node_t find; 644ebedde84SEric Taylor mnttab_node_t *mtn; 645ebedde84SEric Taylor 646b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 647b2634b9cSEric Taylor struct mnttab srch = { 0 }; 648b2634b9cSEric Taylor 649b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 650b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 651b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 652b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 653b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 654b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 655b2634b9cSEric Taylor return (0); 656b2634b9cSEric Taylor else 657b2634b9cSEric Taylor return (ENOENT); 658b2634b9cSEric Taylor } 659b2634b9cSEric Taylor 660ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 661b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 662ebedde84SEric Taylor 663ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 664ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 665ebedde84SEric Taylor if (mtn) { 666ebedde84SEric Taylor *entry = mtn->mtn_mt; 667ebedde84SEric Taylor return (0); 668ebedde84SEric Taylor } 669ebedde84SEric Taylor return (ENOENT); 670ebedde84SEric Taylor } 671ebedde84SEric Taylor 672ebedde84SEric Taylor void 673ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 674ebedde84SEric Taylor const char *mountp, const char *mntopts) 675ebedde84SEric Taylor { 676ebedde84SEric Taylor mnttab_node_t *mtn; 677ebedde84SEric Taylor 678ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 679ebedde84SEric Taylor return; 680ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 681ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 682ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 683ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 684ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 685ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 686ebedde84SEric Taylor } 687ebedde84SEric Taylor 688ebedde84SEric Taylor void 689ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 690ebedde84SEric Taylor { 691ebedde84SEric Taylor mnttab_node_t find; 692ebedde84SEric Taylor mnttab_node_t *ret; 693ebedde84SEric Taylor 694ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 695ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 696ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 697ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 698ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 699ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 700ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 701ebedde84SEric Taylor free(ret); 702ebedde84SEric Taylor } 703ebedde84SEric Taylor } 704ebedde84SEric Taylor 7057b97dc1aSrm int 7067b97dc1aSrm zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7077b97dc1aSrm { 70829ab75c9Srm zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7097b97dc1aSrm 7107b97dc1aSrm if (zpool_handle == NULL) 7117b97dc1aSrm return (-1); 7127b97dc1aSrm 7137b97dc1aSrm *spa_version = zpool_get_prop_int(zpool_handle, 7147b97dc1aSrm ZPOOL_PROP_VERSION, NULL); 7157b97dc1aSrm return (0); 7167b97dc1aSrm } 7177b97dc1aSrm 7187b97dc1aSrm /* 7197b97dc1aSrm * The choice of reservation property depends on the SPA version. 7207b97dc1aSrm */ 7217b97dc1aSrm static int 7227b97dc1aSrm zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7237b97dc1aSrm { 7247b97dc1aSrm int spa_version; 7257b97dc1aSrm 7267b97dc1aSrm if (zfs_spa_version(zhp, &spa_version) < 0) 7277b97dc1aSrm return (-1); 7287b97dc1aSrm 7297b97dc1aSrm if (spa_version >= SPA_VERSION_REFRESERVATION) 7307b97dc1aSrm *resv_prop = ZFS_PROP_REFRESERVATION; 7317b97dc1aSrm else 7327b97dc1aSrm *resv_prop = ZFS_PROP_RESERVATION; 7337b97dc1aSrm 7347b97dc1aSrm return (0); 7357b97dc1aSrm } 7367b97dc1aSrm 737e9dbad6fSeschrock /* 738e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 739e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 740e9dbad6fSeschrock * strings. 741e9dbad6fSeschrock */ 7420a48a24eStimh nvlist_t * 7430a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 744990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 745e9dbad6fSeschrock { 746e9dbad6fSeschrock nvpair_t *elem; 747e9dbad6fSeschrock uint64_t intval; 748e9dbad6fSeschrock char *strval; 749990b4856Slling zfs_prop_t prop; 750e9dbad6fSeschrock nvlist_t *ret; 751da6c28aaSamw int chosen_normal = -1; 752da6c28aaSamw int chosen_utf = -1; 753e9dbad6fSeschrock 754990b4856Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 755990b4856Slling (void) no_memory(hdl); 756990b4856Slling return (NULL); 757e9dbad6fSeschrock } 758e9dbad6fSeschrock 75914843421SMatthew Ahrens /* 76014843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 76114843421SMatthew Ahrens */ 76214843421SMatthew Ahrens 763e9dbad6fSeschrock elem = NULL; 764e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 765990b4856Slling const char *propname = nvpair_name(elem); 766e9dbad6fSeschrock 76714843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 76814843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 769990b4856Slling /* 77014843421SMatthew Ahrens * This is a user property: make sure it's a 771990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 772990b4856Slling */ 773990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 774990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 775990b4856Slling "'%s' must be a string"), propname); 776990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 777990b4856Slling goto error; 778990b4856Slling } 779990b4856Slling 780990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 781990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 782990b4856Slling "property name '%s' is too long"), 783990b4856Slling propname); 784990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 785990b4856Slling goto error; 786fa9e4066Sahrens } 787fa9e4066Sahrens 788e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 789e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 790e9dbad6fSeschrock (void) no_memory(hdl); 791e9dbad6fSeschrock goto error; 792e9dbad6fSeschrock } 793e9dbad6fSeschrock continue; 794e9dbad6fSeschrock } 795e9dbad6fSeschrock 79614843421SMatthew Ahrens /* 79714843421SMatthew Ahrens * Currently, only user properties can be modified on 79814843421SMatthew Ahrens * snapshots. 79914843421SMatthew Ahrens */ 800bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 801bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 802bb0ade09Sahrens "this property can not be modified for snapshots")); 803bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 804bb0ade09Sahrens goto error; 805bb0ade09Sahrens } 806bb0ade09Sahrens 80714843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 80814843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 80914843421SMatthew Ahrens char newpropname[128]; 81014843421SMatthew Ahrens char domain[128]; 81114843421SMatthew Ahrens uint64_t rid; 81214843421SMatthew Ahrens uint64_t valary[3]; 81314843421SMatthew Ahrens 81414843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 81514843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 81614843421SMatthew Ahrens zfs_error_aux(hdl, 81714843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 81814843421SMatthew Ahrens "'%s' has an invalid user/group name"), 81914843421SMatthew Ahrens propname); 82014843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 82114843421SMatthew Ahrens goto error; 82214843421SMatthew Ahrens } 82314843421SMatthew Ahrens 82414843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 82514843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 82614843421SMatthew Ahrens zfs_error_aux(hdl, 82714843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 82814843421SMatthew Ahrens propname); 82914843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 83014843421SMatthew Ahrens errbuf); 83114843421SMatthew Ahrens goto error; 83214843421SMatthew Ahrens } 83314843421SMatthew Ahrens 83414843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 83514843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 83614843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 83714843421SMatthew Ahrens intval = 0; 83814843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 83914843421SMatthew Ahrens strval, &intval) != 0) { 84014843421SMatthew Ahrens (void) zfs_error(hdl, 84114843421SMatthew Ahrens EZFS_BADPROP, errbuf); 84214843421SMatthew Ahrens goto error; 84314843421SMatthew Ahrens } 84414843421SMatthew Ahrens } else if (nvpair_type(elem) == 84514843421SMatthew Ahrens DATA_TYPE_UINT64) { 84614843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 84714843421SMatthew Ahrens if (intval == 0) { 84814843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 84914843421SMatthew Ahrens "use 'none' to disable " 85014843421SMatthew Ahrens "userquota/groupquota")); 85114843421SMatthew Ahrens goto error; 85214843421SMatthew Ahrens } 85314843421SMatthew Ahrens } else { 85414843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 85514843421SMatthew Ahrens "'%s' must be a number"), propname); 85614843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 85714843421SMatthew Ahrens goto error; 85814843421SMatthew Ahrens } 85914843421SMatthew Ahrens 8602d5843dbSMatthew Ahrens /* 8612d5843dbSMatthew Ahrens * Encode the prop name as 8622d5843dbSMatthew Ahrens * userquota@<hex-rid>-domain, to make it easy 8632d5843dbSMatthew Ahrens * for the kernel to decode. 8642d5843dbSMatthew Ahrens */ 86514843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 8662d5843dbSMatthew Ahrens "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 8672d5843dbSMatthew Ahrens (longlong_t)rid, domain); 86814843421SMatthew Ahrens valary[0] = uqtype; 86914843421SMatthew Ahrens valary[1] = rid; 87014843421SMatthew Ahrens valary[2] = intval; 87114843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 87214843421SMatthew Ahrens valary, 3) != 0) { 87314843421SMatthew Ahrens (void) no_memory(hdl); 87414843421SMatthew Ahrens goto error; 87514843421SMatthew Ahrens } 87614843421SMatthew Ahrens continue; 87714843421SMatthew Ahrens } 87814843421SMatthew Ahrens 87914843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 88014843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 88114843421SMatthew Ahrens "invalid property '%s'"), propname); 88214843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 88314843421SMatthew Ahrens goto error; 88414843421SMatthew Ahrens } 88514843421SMatthew Ahrens 886e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 887e9dbad6fSeschrock zfs_error_aux(hdl, 888e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 889e9dbad6fSeschrock "apply to datasets of this type"), propname); 890e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 891e9dbad6fSeschrock goto error; 892e9dbad6fSeschrock } 893e9dbad6fSeschrock 894e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 895da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 896e9dbad6fSeschrock zfs_error_aux(hdl, 897e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 898e9dbad6fSeschrock propname); 899e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 900e9dbad6fSeschrock goto error; 901e9dbad6fSeschrock } 902e9dbad6fSeschrock 903990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 904990b4856Slling &strval, &intval, errbuf) != 0) 905e9dbad6fSeschrock goto error; 906fa9e4066Sahrens 907e9dbad6fSeschrock /* 908e9dbad6fSeschrock * Perform some additional checks for specific properties. 909e9dbad6fSeschrock */ 910e9dbad6fSeschrock switch (prop) { 911e7437265Sahrens case ZFS_PROP_VERSION: 912e7437265Sahrens { 913e7437265Sahrens int version; 914e7437265Sahrens 915e7437265Sahrens if (zhp == NULL) 916e7437265Sahrens break; 917e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 918e7437265Sahrens if (intval < version) { 919e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 920e7437265Sahrens "Can not downgrade; already at version %u"), 921e7437265Sahrens version); 922e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 923e7437265Sahrens goto error; 924e7437265Sahrens } 925e7437265Sahrens break; 926e7437265Sahrens } 927e7437265Sahrens 928e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 929e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 930e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 931e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 932e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 93399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 934e9dbad6fSeschrock "'%s' must be power of 2 from %u " 935e9dbad6fSeschrock "to %uk"), propname, 936e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 937e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 938e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 939e9dbad6fSeschrock goto error; 940fa9e4066Sahrens } 941fa9e4066Sahrens break; 942fa9e4066Sahrens 9434201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 9444201a95eSRic Aleshire { 9454201a95eSRic Aleshire /* 9464201a95eSRic Aleshire * Verify the mlslabel string and convert to 9474201a95eSRic Aleshire * internal hex label string. 9484201a95eSRic Aleshire */ 9494201a95eSRic Aleshire 9504201a95eSRic Aleshire m_label_t *new_sl; 9514201a95eSRic Aleshire char *hex = NULL; /* internal label string */ 9524201a95eSRic Aleshire 9534201a95eSRic Aleshire /* Default value is already OK. */ 9544201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 9554201a95eSRic Aleshire break; 9564201a95eSRic Aleshire 9574201a95eSRic Aleshire /* Verify the label can be converted to binary form */ 9584201a95eSRic Aleshire if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 9594201a95eSRic Aleshire (str_to_label(strval, &new_sl, MAC_LABEL, 9604201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1)) { 9614201a95eSRic Aleshire goto badlabel; 9624201a95eSRic Aleshire } 9634201a95eSRic Aleshire 9644201a95eSRic Aleshire /* Now translate to hex internal label string */ 9654201a95eSRic Aleshire if (label_to_str(new_sl, &hex, M_INTERNAL, 9664201a95eSRic Aleshire DEF_NAMES) != 0) { 9674201a95eSRic Aleshire if (hex) 9684201a95eSRic Aleshire free(hex); 9694201a95eSRic Aleshire goto badlabel; 9704201a95eSRic Aleshire } 9714201a95eSRic Aleshire m_label_free(new_sl); 9724201a95eSRic Aleshire 9734201a95eSRic Aleshire /* If string is already in internal form, we're done. */ 9744201a95eSRic Aleshire if (strcmp(strval, hex) == 0) { 9754201a95eSRic Aleshire free(hex); 9764201a95eSRic Aleshire break; 9774201a95eSRic Aleshire } 9784201a95eSRic Aleshire 9794201a95eSRic Aleshire /* Replace the label string with the internal form. */ 980569038c9SRic Aleshire (void) nvlist_remove(ret, zfs_prop_to_name(prop), 9814201a95eSRic Aleshire DATA_TYPE_STRING); 9824201a95eSRic Aleshire verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 9834201a95eSRic Aleshire hex) == 0); 9844201a95eSRic Aleshire free(hex); 9854201a95eSRic Aleshire 9864201a95eSRic Aleshire break; 9874201a95eSRic Aleshire 9884201a95eSRic Aleshire badlabel: 9894201a95eSRic Aleshire zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9904201a95eSRic Aleshire "invalid mlslabel '%s'"), strval); 9914201a95eSRic Aleshire (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9924201a95eSRic Aleshire m_label_free(new_sl); /* OK if null */ 9934201a95eSRic Aleshire goto error; 9944201a95eSRic Aleshire 9954201a95eSRic Aleshire } 9964201a95eSRic Aleshire 997e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 99889eef05eSrm { 99989eef05eSrm namecheck_err_t why; 100089eef05eSrm 1001e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1002e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1003e9dbad6fSeschrock break; 1004fa9e4066Sahrens 100589eef05eSrm if (mountpoint_namecheck(strval, &why)) { 100689eef05eSrm switch (why) { 100789eef05eSrm case NAME_ERR_LEADING_SLASH: 100889eef05eSrm zfs_error_aux(hdl, 100989eef05eSrm dgettext(TEXT_DOMAIN, 101089eef05eSrm "'%s' must be an absolute path, " 101189eef05eSrm "'none', or 'legacy'"), propname); 101289eef05eSrm break; 101389eef05eSrm case NAME_ERR_TOOLONG: 101489eef05eSrm zfs_error_aux(hdl, 101589eef05eSrm dgettext(TEXT_DOMAIN, 101689eef05eSrm "component of '%s' is too long"), 101789eef05eSrm propname); 101889eef05eSrm break; 101989eef05eSrm } 1020e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1021e9dbad6fSeschrock goto error; 1022fa9e4066Sahrens } 102389eef05eSrm } 102489eef05eSrm 1025f3861e1aSahl /*FALLTHRU*/ 1026fa9e4066Sahrens 1027da6c28aaSamw case ZFS_PROP_SHARESMB: 1028f3861e1aSahl case ZFS_PROP_SHARENFS: 1029f3861e1aSahl /* 1030da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1031da6c28aaSamw * properties, check if it can be set in a 1032da6c28aaSamw * global/non-global zone based on 1033f3861e1aSahl * the zoned property value: 1034f3861e1aSahl * 1035f3861e1aSahl * global zone non-global zone 1036f3861e1aSahl * -------------------------------------------------- 1037f3861e1aSahl * zoned=on mountpoint (no) mountpoint (yes) 1038f3861e1aSahl * sharenfs (no) sharenfs (no) 1039da6c28aaSamw * sharesmb (no) sharesmb (no) 1040f3861e1aSahl * 1041f3861e1aSahl * zoned=off mountpoint (yes) N/A 1042f3861e1aSahl * sharenfs (yes) 1043da6c28aaSamw * sharesmb (yes) 1044f3861e1aSahl */ 1045e9dbad6fSeschrock if (zoned) { 1046e9dbad6fSeschrock if (getzoneid() == GLOBAL_ZONEID) { 1047e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1048e9dbad6fSeschrock "'%s' cannot be set on " 1049e9dbad6fSeschrock "dataset in a non-global zone"), 1050e9dbad6fSeschrock propname); 1051e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1052e9dbad6fSeschrock errbuf); 1053e9dbad6fSeschrock goto error; 1054da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1055da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 1056e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1057e9dbad6fSeschrock "'%s' cannot be set in " 1058e9dbad6fSeschrock "a non-global zone"), propname); 1059e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1060e9dbad6fSeschrock errbuf); 1061e9dbad6fSeschrock goto error; 1062fa9e4066Sahrens } 1063e9dbad6fSeschrock } else if (getzoneid() != GLOBAL_ZONEID) { 1064e9dbad6fSeschrock /* 1065e9dbad6fSeschrock * If zoned property is 'off', this must be in 106614843421SMatthew Ahrens * a global zone. If not, something is wrong. 1067e9dbad6fSeschrock */ 1068e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1069e9dbad6fSeschrock "'%s' cannot be set while dataset " 1070e9dbad6fSeschrock "'zoned' property is set"), propname); 1071e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1072e9dbad6fSeschrock goto error; 1073fa9e4066Sahrens } 1074f3861e1aSahl 107567331909Sdougm /* 107667331909Sdougm * At this point, it is legitimate to set the 107767331909Sdougm * property. Now we want to make sure that the 107867331909Sdougm * property value is valid if it is sharenfs. 107967331909Sdougm */ 1080da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1081da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 1082fac3008cSeschrock strcmp(strval, "on") != 0 && 1083fac3008cSeschrock strcmp(strval, "off") != 0) { 1084da6c28aaSamw zfs_share_proto_t proto; 1085da6c28aaSamw 1086da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1087da6c28aaSamw proto = PROTO_SMB; 1088da6c28aaSamw else 1089da6c28aaSamw proto = PROTO_NFS; 109067331909Sdougm 109167331909Sdougm /* 1092da6c28aaSamw * Must be an valid sharing protocol 1093da6c28aaSamw * option string so init the libshare 1094da6c28aaSamw * in order to enable the parser and 1095da6c28aaSamw * then parse the options. We use the 1096da6c28aaSamw * control API since we don't care about 1097da6c28aaSamw * the current configuration and don't 109867331909Sdougm * want the overhead of loading it 109967331909Sdougm * until we actually do something. 110067331909Sdougm */ 110167331909Sdougm 1102fac3008cSeschrock if (zfs_init_libshare(hdl, 1103fac3008cSeschrock SA_INIT_CONTROL_API) != SA_OK) { 1104fac3008cSeschrock /* 1105fac3008cSeschrock * An error occurred so we can't do 1106fac3008cSeschrock * anything 1107fac3008cSeschrock */ 1108fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1109fac3008cSeschrock "'%s' cannot be set: problem " 1110fac3008cSeschrock "in share initialization"), 1111fac3008cSeschrock propname); 1112fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1113fac3008cSeschrock errbuf); 1114fac3008cSeschrock goto error; 1115fac3008cSeschrock } 111667331909Sdougm 1117da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 1118fac3008cSeschrock /* 1119fac3008cSeschrock * There was an error in parsing so 1120fac3008cSeschrock * deal with it by issuing an error 1121fac3008cSeschrock * message and leaving after 1122fac3008cSeschrock * uninitializing the the libshare 1123fac3008cSeschrock * interface. 1124fac3008cSeschrock */ 1125fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1126fac3008cSeschrock "'%s' cannot be set to invalid " 1127fac3008cSeschrock "options"), propname); 1128fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1129fac3008cSeschrock errbuf); 1130fac3008cSeschrock zfs_uninit_libshare(hdl); 1131fac3008cSeschrock goto error; 1132fac3008cSeschrock } 113367331909Sdougm zfs_uninit_libshare(hdl); 113467331909Sdougm } 113567331909Sdougm 1136da6c28aaSamw break; 1137da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1138da6c28aaSamw chosen_utf = (int)intval; 1139da6c28aaSamw break; 1140da6c28aaSamw case ZFS_PROP_NORMALIZE: 1141da6c28aaSamw chosen_normal = (int)intval; 1142f3861e1aSahl break; 1143e9dbad6fSeschrock } 1144fa9e4066Sahrens 1145e9dbad6fSeschrock /* 1146e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1147e9dbad6fSeschrock * checks to enforce. 1148e9dbad6fSeschrock */ 1149e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1150e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1151e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1152e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1153e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1154e9dbad6fSeschrock char buf[64]; 1155e9dbad6fSeschrock 1156e9dbad6fSeschrock switch (prop) { 1157e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1158a9799022Sck case ZFS_PROP_REFRESERVATION: 1159e9dbad6fSeschrock if (intval > volsize) { 1160e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1161e9dbad6fSeschrock "'%s' is greater than current " 1162e9dbad6fSeschrock "volume size"), propname); 1163e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1164e9dbad6fSeschrock errbuf); 1165e9dbad6fSeschrock goto error; 1166e9dbad6fSeschrock } 1167e9dbad6fSeschrock break; 1168e9dbad6fSeschrock 1169e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1170e9dbad6fSeschrock if (intval % blocksize != 0) { 1171e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1172e9dbad6fSeschrock sizeof (buf)); 1173e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1174e9dbad6fSeschrock "'%s' must be a multiple of " 1175e9dbad6fSeschrock "volume block size (%s)"), 1176e9dbad6fSeschrock propname, buf); 1177e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1178e9dbad6fSeschrock errbuf); 1179e9dbad6fSeschrock goto error; 1180e9dbad6fSeschrock } 1181e9dbad6fSeschrock 1182e9dbad6fSeschrock if (intval == 0) { 1183e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1184e9dbad6fSeschrock "'%s' cannot be zero"), 1185e9dbad6fSeschrock propname); 1186e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1187e9dbad6fSeschrock errbuf); 1188e9dbad6fSeschrock goto error; 1189e9dbad6fSeschrock } 1190f3861e1aSahl break; 1191fa9e4066Sahrens } 1192e9dbad6fSeschrock } 1193e9dbad6fSeschrock } 1194fa9e4066Sahrens 1195da6c28aaSamw /* 1196da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1197da6c28aaSamw * enforce rejection of non-UTF8 names. 1198da6c28aaSamw * 1199da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1200da6c28aaSamw * was explicitly not chosen, it is an error. 1201da6c28aaSamw */ 1202de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1203da6c28aaSamw if (nvlist_add_uint64(ret, 1204da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1205da6c28aaSamw (void) no_memory(hdl); 1206da6c28aaSamw goto error; 1207da6c28aaSamw } 1208de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1209da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1210da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1211da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1212da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1213da6c28aaSamw goto error; 1214da6c28aaSamw } 1215da6c28aaSamw 1216e9dbad6fSeschrock /* 1217e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1218e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1219e9dbad6fSeschrock */ 1220e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 1221e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1222e9dbad6fSeschrock &intval) == 0) { 1223e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 1224e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1225a9b821a0Sck uint64_t old_reservation; 1226e9dbad6fSeschrock uint64_t new_reservation; 1227a9b821a0Sck zfs_prop_t resv_prop; 1228a9b821a0Sck 12297b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1230a9b821a0Sck goto error; 1231a9b821a0Sck old_reservation = zfs_prop_get_int(zhp, resv_prop); 1232e9dbad6fSeschrock 1233e9dbad6fSeschrock if (old_volsize == old_reservation && 1234a9b821a0Sck nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 1235e9dbad6fSeschrock &new_reservation) != 0) { 1236e9dbad6fSeschrock if (nvlist_add_uint64(ret, 1237a9b821a0Sck zfs_prop_to_name(resv_prop), intval) != 0) { 1238e9dbad6fSeschrock (void) no_memory(hdl); 1239e9dbad6fSeschrock goto error; 1240e9dbad6fSeschrock } 1241fa9e4066Sahrens } 1242fa9e4066Sahrens } 1243e9dbad6fSeschrock return (ret); 1244fa9e4066Sahrens 1245e9dbad6fSeschrock error: 1246e9dbad6fSeschrock nvlist_free(ret); 1247e9dbad6fSeschrock return (NULL); 1248fa9e4066Sahrens } 1249fa9e4066Sahrens 125092241e0bSTom Erickson void 125192241e0bSTom Erickson zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 125292241e0bSTom Erickson char *errbuf) 125392241e0bSTom Erickson { 125492241e0bSTom Erickson switch (err) { 125592241e0bSTom Erickson 125692241e0bSTom Erickson case ENOSPC: 125792241e0bSTom Erickson /* 125892241e0bSTom Erickson * For quotas and reservations, ENOSPC indicates 125992241e0bSTom Erickson * something different; setting a quota or reservation 126092241e0bSTom Erickson * doesn't use any disk space. 126192241e0bSTom Erickson */ 126292241e0bSTom Erickson switch (prop) { 126392241e0bSTom Erickson case ZFS_PROP_QUOTA: 126492241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 126592241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 126692241e0bSTom Erickson "size is less than current used or " 126792241e0bSTom Erickson "reserved space")); 126892241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 126992241e0bSTom Erickson break; 127092241e0bSTom Erickson 127192241e0bSTom Erickson case ZFS_PROP_RESERVATION: 127292241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 127392241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127492241e0bSTom Erickson "size is greater than available space")); 127592241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 127692241e0bSTom Erickson break; 127792241e0bSTom Erickson 127892241e0bSTom Erickson default: 127992241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 128092241e0bSTom Erickson break; 128192241e0bSTom Erickson } 128292241e0bSTom Erickson break; 128392241e0bSTom Erickson 128492241e0bSTom Erickson case EBUSY: 128592241e0bSTom Erickson (void) zfs_standard_error(hdl, EBUSY, errbuf); 128692241e0bSTom Erickson break; 128792241e0bSTom Erickson 128892241e0bSTom Erickson case EROFS: 128992241e0bSTom Erickson (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 129092241e0bSTom Erickson break; 129192241e0bSTom Erickson 129292241e0bSTom Erickson case ENOTSUP: 129392241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 129492241e0bSTom Erickson "pool and or dataset must be upgraded to set this " 129592241e0bSTom Erickson "property or value")); 129692241e0bSTom Erickson (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 129792241e0bSTom Erickson break; 129892241e0bSTom Erickson 129992241e0bSTom Erickson case ERANGE: 130092241e0bSTom Erickson if (prop == ZFS_PROP_COMPRESSION) { 130192241e0bSTom Erickson (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130292241e0bSTom Erickson "property setting is not allowed on " 130392241e0bSTom Erickson "bootable datasets")); 130492241e0bSTom Erickson (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 130592241e0bSTom Erickson } else { 130692241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 130792241e0bSTom Erickson } 130892241e0bSTom Erickson break; 130992241e0bSTom Erickson 1310ab003da8SJim Dunham case EINVAL: 1311ab003da8SJim Dunham if (prop == ZPROP_INVAL) { 1312ab003da8SJim Dunham (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1313ab003da8SJim Dunham } else { 1314ab003da8SJim Dunham (void) zfs_standard_error(hdl, err, errbuf); 1315ab003da8SJim Dunham } 1316ab003da8SJim Dunham break; 1317ab003da8SJim Dunham 131892241e0bSTom Erickson case EOVERFLOW: 131992241e0bSTom Erickson /* 132092241e0bSTom Erickson * This platform can't address a volume this big. 132192241e0bSTom Erickson */ 132292241e0bSTom Erickson #ifdef _ILP32 132392241e0bSTom Erickson if (prop == ZFS_PROP_VOLSIZE) { 132492241e0bSTom Erickson (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132592241e0bSTom Erickson break; 132692241e0bSTom Erickson } 132792241e0bSTom Erickson #endif 132892241e0bSTom Erickson /* FALLTHROUGH */ 132992241e0bSTom Erickson default: 133092241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 133192241e0bSTom Erickson } 133292241e0bSTom Erickson } 133392241e0bSTom Erickson 1334fa9e4066Sahrens /* 1335fa9e4066Sahrens * Given a property name and value, set the property for the given dataset. 1336fa9e4066Sahrens */ 1337fa9e4066Sahrens int 1338e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1339fa9e4066Sahrens { 1340fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1341e9dbad6fSeschrock int ret = -1; 1342e9dbad6fSeschrock prop_changelist_t *cl = NULL; 134399653d4eSeschrock char errbuf[1024]; 134499653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1345e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1346e9dbad6fSeschrock zfs_prop_t prop; 13470068372bSMark J Musante boolean_t do_prefix; 13480068372bSMark J Musante uint64_t idx; 134999653d4eSeschrock 135099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1351e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 135299653d4eSeschrock zhp->zfs_name); 135399653d4eSeschrock 1354e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1355e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1356e9dbad6fSeschrock (void) no_memory(hdl); 1357e9dbad6fSeschrock goto error; 1358fa9e4066Sahrens } 1359fa9e4066Sahrens 13600a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1361e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1362e9dbad6fSeschrock goto error; 1363990b4856Slling 1364e9dbad6fSeschrock nvlist_free(nvl); 1365e9dbad6fSeschrock nvl = realprops; 1366e9dbad6fSeschrock 1367e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1368e9dbad6fSeschrock 13690069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1370e9dbad6fSeschrock goto error; 1371fa9e4066Sahrens 1372fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 137399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 137499653d4eSeschrock "child dataset with inherited mountpoint is used " 137599653d4eSeschrock "in a non-global zone")); 137699653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1377fa9e4066Sahrens goto error; 1378fa9e4066Sahrens } 1379fa9e4066Sahrens 13800068372bSMark J Musante /* 13810068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 13820068372bSMark J Musante * then we want to prevent unmounting & remounting it. 13830068372bSMark J Musante */ 13840068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13850068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 13860068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1387a227b7f4Shs 1388a227b7f4Shs if (do_prefix && (ret = changelist_prefix(cl)) != 0) 13890068372bSMark J Musante goto error; 1390fa9e4066Sahrens 1391fa9e4066Sahrens /* 1392fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1393fa9e4066Sahrens */ 1394fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1395fa9e4066Sahrens 1396990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1397e9dbad6fSeschrock goto error; 1398e9dbad6fSeschrock 1399ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1400743a77edSAlan Wright 1401fa9e4066Sahrens if (ret != 0) { 140292241e0bSTom Erickson zfs_setprop_error(hdl, prop, errno, errbuf); 1403fa9e4066Sahrens } else { 1404a227b7f4Shs if (do_prefix) 1405a227b7f4Shs ret = changelist_postfix(cl); 1406a227b7f4Shs 1407fa9e4066Sahrens /* 1408fa9e4066Sahrens * Refresh the statistics so the new property value 1409fa9e4066Sahrens * is reflected. 1410fa9e4066Sahrens */ 1411a227b7f4Shs if (ret == 0) 1412e9dbad6fSeschrock (void) get_stats(zhp); 1413fa9e4066Sahrens } 1414fa9e4066Sahrens 1415fa9e4066Sahrens error: 1416e9dbad6fSeschrock nvlist_free(nvl); 1417e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1418e9dbad6fSeschrock if (cl) 1419e9dbad6fSeschrock changelist_free(cl); 1420fa9e4066Sahrens return (ret); 1421fa9e4066Sahrens } 1422fa9e4066Sahrens 1423fa9e4066Sahrens /* 142492241e0bSTom Erickson * Given a property, inherit the value from the parent dataset, or if received 142592241e0bSTom Erickson * is TRUE, revert to the received value, if any. 1426fa9e4066Sahrens */ 1427fa9e4066Sahrens int 142892241e0bSTom Erickson zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1429fa9e4066Sahrens { 1430fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1431fa9e4066Sahrens int ret; 1432fa9e4066Sahrens prop_changelist_t *cl; 143399653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 143499653d4eSeschrock char errbuf[1024]; 1435e9dbad6fSeschrock zfs_prop_t prop; 143699653d4eSeschrock 143799653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 143899653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1439fa9e4066Sahrens 144092241e0bSTom Erickson zc.zc_cookie = received; 1441990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1442e9dbad6fSeschrock /* 1443e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1444e9dbad6fSeschrock * small, so just do it here. 1445e9dbad6fSeschrock */ 1446e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1447e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1448e9dbad6fSeschrock "invalid property")); 1449e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1450e9dbad6fSeschrock } 1451e9dbad6fSeschrock 1452e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1453e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1454e9dbad6fSeschrock 1455e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1456e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1457e9dbad6fSeschrock 1458e9dbad6fSeschrock return (0); 1459e9dbad6fSeschrock } 1460e9dbad6fSeschrock 1461fa9e4066Sahrens /* 1462fa9e4066Sahrens * Verify that this property is inheritable. 1463fa9e4066Sahrens */ 146499653d4eSeschrock if (zfs_prop_readonly(prop)) 146599653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1466fa9e4066Sahrens 146792241e0bSTom Erickson if (!zfs_prop_inheritable(prop) && !received) 146899653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1469fa9e4066Sahrens 1470fa9e4066Sahrens /* 1471fa9e4066Sahrens * Check to see if the value applies to this type 1472fa9e4066Sahrens */ 147399653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 147499653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1475fa9e4066Sahrens 1476bf7c2d40Srm /* 1477bf7c2d40Srm * Normalize the name, to get rid of shorthand abbrevations. 1478bf7c2d40Srm */ 1479bf7c2d40Srm propname = zfs_prop_to_name(prop); 1480fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1481e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1482fa9e4066Sahrens 1483fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1484fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 148599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 148699653d4eSeschrock "dataset is used in a non-global zone")); 148799653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1488fa9e4066Sahrens } 1489fa9e4066Sahrens 1490fa9e4066Sahrens /* 1491fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1492fa9e4066Sahrens */ 14930069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1494fa9e4066Sahrens return (-1); 1495fa9e4066Sahrens 1496fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 149799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 149899653d4eSeschrock "child dataset with inherited mountpoint is used " 149999653d4eSeschrock "in a non-global zone")); 150099653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1501fa9e4066Sahrens goto error; 1502fa9e4066Sahrens } 1503fa9e4066Sahrens 1504fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1505fa9e4066Sahrens goto error; 1506fa9e4066Sahrens 1507e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 150899653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1509fa9e4066Sahrens } else { 1510fa9e4066Sahrens 1511efc555ebSnd if ((ret = changelist_postfix(cl)) != 0) 1512fa9e4066Sahrens goto error; 1513fa9e4066Sahrens 1514fa9e4066Sahrens /* 1515fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1516fa9e4066Sahrens */ 1517fa9e4066Sahrens (void) get_stats(zhp); 1518fa9e4066Sahrens } 1519fa9e4066Sahrens 1520fa9e4066Sahrens error: 1521fa9e4066Sahrens changelist_free(cl); 1522fa9e4066Sahrens return (ret); 1523fa9e4066Sahrens } 1524fa9e4066Sahrens 15257f7322feSeschrock /* 15267f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 15277f7322feSeschrock * extract them appropriately. 15287f7322feSeschrock */ 15297f7322feSeschrock static uint64_t 15307f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15317f7322feSeschrock { 15327f7322feSeschrock nvlist_t *nv; 15337f7322feSeschrock uint64_t value; 15347f7322feSeschrock 1535a2eea2e1Sahrens *source = NULL; 15367f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15377f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1538990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1539990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15407f7322feSeschrock } else { 15412e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15422e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15437f7322feSeschrock value = zfs_prop_default_numeric(prop); 15447f7322feSeschrock *source = ""; 15457f7322feSeschrock } 15467f7322feSeschrock 15477f7322feSeschrock return (value); 15487f7322feSeschrock } 15497f7322feSeschrock 15507f7322feSeschrock static char * 15517f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15527f7322feSeschrock { 15537f7322feSeschrock nvlist_t *nv; 15547f7322feSeschrock char *value; 15557f7322feSeschrock 1556a2eea2e1Sahrens *source = NULL; 15577f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15587f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1559990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1560990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15617f7322feSeschrock } else { 15622e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15632e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15647f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15657f7322feSeschrock value = ""; 15667f7322feSeschrock *source = ""; 15677f7322feSeschrock } 15687f7322feSeschrock 15697f7322feSeschrock return (value); 15707f7322feSeschrock } 15717f7322feSeschrock 157292241e0bSTom Erickson static boolean_t 157392241e0bSTom Erickson zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157492241e0bSTom Erickson { 157592241e0bSTom Erickson return (zhp->zfs_props == zhp->zfs_recvd_props); 157692241e0bSTom Erickson } 157792241e0bSTom Erickson 157892241e0bSTom Erickson static void 157992241e0bSTom Erickson zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158092241e0bSTom Erickson { 158192241e0bSTom Erickson *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158292241e0bSTom Erickson zhp->zfs_props = zhp->zfs_recvd_props; 158392241e0bSTom Erickson } 158492241e0bSTom Erickson 158592241e0bSTom Erickson static void 158692241e0bSTom Erickson zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158792241e0bSTom Erickson { 158892241e0bSTom Erickson zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 158992241e0bSTom Erickson *cookie = 0; 159092241e0bSTom Erickson } 159192241e0bSTom Erickson 1592fa9e4066Sahrens /* 1593fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1594fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1595fa9e4066Sahrens * 1596fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1597fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1598fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1599fa9e4066Sahrens * the source "temporary". 1600fa9e4066Sahrens */ 160199653d4eSeschrock static int 1602990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 160399653d4eSeschrock char **source, uint64_t *val) 1604fa9e4066Sahrens { 1605bd00f61bSrm zfs_cmd_t zc = { 0 }; 160696510749Stimh nvlist_t *zplprops = NULL; 1607fa9e4066Sahrens struct mnttab mnt; 16083ccfa83cSahrens char *mntopt_on = NULL; 16093ccfa83cSahrens char *mntopt_off = NULL; 161092241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1611fa9e4066Sahrens 1612fa9e4066Sahrens *source = NULL; 1613fa9e4066Sahrens 16143ccfa83cSahrens switch (prop) { 16153ccfa83cSahrens case ZFS_PROP_ATIME: 16163ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 16173ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 16183ccfa83cSahrens break; 16193ccfa83cSahrens 16203ccfa83cSahrens case ZFS_PROP_DEVICES: 16213ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 16223ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 16233ccfa83cSahrens break; 16243ccfa83cSahrens 16253ccfa83cSahrens case ZFS_PROP_EXEC: 16263ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 16273ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 16283ccfa83cSahrens break; 16293ccfa83cSahrens 16303ccfa83cSahrens case ZFS_PROP_READONLY: 16313ccfa83cSahrens mntopt_on = MNTOPT_RO; 16323ccfa83cSahrens mntopt_off = MNTOPT_RW; 16333ccfa83cSahrens break; 16343ccfa83cSahrens 16353ccfa83cSahrens case ZFS_PROP_SETUID: 16363ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 16373ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 16383ccfa83cSahrens break; 16393ccfa83cSahrens 16403ccfa83cSahrens case ZFS_PROP_XATTR: 16413ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 16423ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 16433ccfa83cSahrens break; 1644da6c28aaSamw 1645da6c28aaSamw case ZFS_PROP_NBMAND: 1646da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1647da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1648da6c28aaSamw break; 16493ccfa83cSahrens } 16503ccfa83cSahrens 16513bb79becSeschrock /* 16523bb79becSeschrock * Because looking up the mount options is potentially expensive 16533bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16543bb79becSeschrock * we're looking up a property which requires its presence. 16553bb79becSeschrock */ 16563bb79becSeschrock if (!zhp->zfs_mntcheck && 16573ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1658ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1659ebedde84SEric Taylor struct mnttab entry; 16603bb79becSeschrock 1661ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1662ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 16633ccfa83cSahrens entry.mnt_mntopts); 16643ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 16653ccfa83cSahrens return (-1); 16663ccfa83cSahrens } 16673bb79becSeschrock 16683bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 16693bb79becSeschrock } 16703bb79becSeschrock 1671fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1672fa9e4066Sahrens mnt.mnt_mntopts = ""; 1673fa9e4066Sahrens else 1674fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1675fa9e4066Sahrens 1676fa9e4066Sahrens switch (prop) { 1677fa9e4066Sahrens case ZFS_PROP_ATIME: 1678fa9e4066Sahrens case ZFS_PROP_DEVICES: 1679fa9e4066Sahrens case ZFS_PROP_EXEC: 16803ccfa83cSahrens case ZFS_PROP_READONLY: 16813ccfa83cSahrens case ZFS_PROP_SETUID: 16823ccfa83cSahrens case ZFS_PROP_XATTR: 1683da6c28aaSamw case ZFS_PROP_NBMAND: 168499653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1685fa9e4066Sahrens 168692241e0bSTom Erickson if (received) 168792241e0bSTom Erickson break; 168892241e0bSTom Erickson 16893ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 169099653d4eSeschrock *val = B_TRUE; 1691fa9e4066Sahrens if (src) 1692990b4856Slling *src = ZPROP_SRC_TEMPORARY; 16933ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 169499653d4eSeschrock *val = B_FALSE; 1695fa9e4066Sahrens if (src) 1696990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1697fa9e4066Sahrens } 169899653d4eSeschrock break; 1699fa9e4066Sahrens 17003ccfa83cSahrens case ZFS_PROP_CANMOUNT: 1701d41c4376SMark J Musante case ZFS_PROP_VOLSIZE: 1702fa9e4066Sahrens case ZFS_PROP_QUOTA: 1703a9799022Sck case ZFS_PROP_REFQUOTA: 1704fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1705a9799022Sck case ZFS_PROP_REFRESERVATION: 1706a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 170792241e0bSTom Erickson 170892241e0bSTom Erickson if (*source == NULL) { 170992241e0bSTom Erickson /* not default, must be local */ 1710fa9e4066Sahrens *source = zhp->zfs_name; 171192241e0bSTom Erickson } 171299653d4eSeschrock break; 1713fa9e4066Sahrens 1714fa9e4066Sahrens case ZFS_PROP_MOUNTED: 171599653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 171699653d4eSeschrock break; 1717fa9e4066Sahrens 171839c23413Seschrock case ZFS_PROP_NUMCLONES: 171939c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 172039c23413Seschrock break; 172139c23413Seschrock 1722bd00f61bSrm case ZFS_PROP_VERSION: 1723de8267e0Stimh case ZFS_PROP_NORMALIZE: 1724de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1725de8267e0Stimh case ZFS_PROP_CASE: 1726de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1727de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17283d7934e1Srm return (-1); 1729bd00f61bSrm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1730de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1731de8267e0Stimh zcmd_free_nvlists(&zc); 1732f4b94bdeSMatthew Ahrens return (-1); 1733bd00f61bSrm } 1734de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1735de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1736de8267e0Stimh val) != 0) { 1737de8267e0Stimh zcmd_free_nvlists(&zc); 1738f4b94bdeSMatthew Ahrens return (-1); 1739de8267e0Stimh } 174096510749Stimh if (zplprops) 174196510749Stimh nvlist_free(zplprops); 1742de8267e0Stimh zcmd_free_nvlists(&zc); 1743bd00f61bSrm break; 1744bd00f61bSrm 1745fa9e4066Sahrens default: 1746e7437265Sahrens switch (zfs_prop_get_type(prop)) { 174791ebeef5Sahrens case PROP_TYPE_NUMBER: 174891ebeef5Sahrens case PROP_TYPE_INDEX: 1749e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 175074e7dc98SMatthew Ahrens /* 175114843421SMatthew Ahrens * If we tried to use a default value for a 175274e7dc98SMatthew Ahrens * readonly property, it means that it was not 175331f572c2STom Erickson * present. 175474e7dc98SMatthew Ahrens */ 175574e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 175631f572c2STom Erickson *source != NULL && (*source)[0] == '\0') { 175731f572c2STom Erickson *source = NULL; 175874e7dc98SMatthew Ahrens } 1759e7437265Sahrens break; 1760e7437265Sahrens 176191ebeef5Sahrens case PROP_TYPE_STRING: 1762e7437265Sahrens default: 1763e7437265Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1764e7437265Sahrens "cannot get non-numeric property")); 1765e7437265Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 1766e7437265Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 1767e7437265Sahrens } 1768fa9e4066Sahrens } 1769fa9e4066Sahrens 1770fa9e4066Sahrens return (0); 1771fa9e4066Sahrens } 1772fa9e4066Sahrens 1773fa9e4066Sahrens /* 1774fa9e4066Sahrens * Calculate the source type, given the raw source string. 1775fa9e4066Sahrens */ 1776fa9e4066Sahrens static void 1777990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1778fa9e4066Sahrens char *statbuf, size_t statlen) 1779fa9e4066Sahrens { 1780990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1781fa9e4066Sahrens return; 1782fa9e4066Sahrens 1783fa9e4066Sahrens if (source == NULL) { 1784990b4856Slling *srctype = ZPROP_SRC_NONE; 1785fa9e4066Sahrens } else if (source[0] == '\0') { 1786990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 178792241e0bSTom Erickson } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 178892241e0bSTom Erickson *srctype = ZPROP_SRC_RECEIVED; 1789fa9e4066Sahrens } else { 1790fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1791990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1792fa9e4066Sahrens } else { 1793fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1794990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1795fa9e4066Sahrens } 1796fa9e4066Sahrens } 1797fa9e4066Sahrens 1798fa9e4066Sahrens } 1799fa9e4066Sahrens 180092241e0bSTom Erickson int 180192241e0bSTom Erickson zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180292241e0bSTom Erickson size_t proplen, boolean_t literal) 180392241e0bSTom Erickson { 180492241e0bSTom Erickson zfs_prop_t prop; 180592241e0bSTom Erickson int err = 0; 180692241e0bSTom Erickson 180792241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 180892241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 180992241e0bSTom Erickson return (-1); 181092241e0bSTom Erickson 181192241e0bSTom Erickson prop = zfs_name_to_prop(propname); 181292241e0bSTom Erickson 181392241e0bSTom Erickson if (prop != ZPROP_INVAL) { 181492241e0bSTom Erickson uint64_t cookie; 181592241e0bSTom Erickson if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181692241e0bSTom Erickson return (-1); 181792241e0bSTom Erickson zfs_set_recvd_props_mode(zhp, &cookie); 181892241e0bSTom Erickson err = zfs_prop_get(zhp, prop, propbuf, proplen, 181992241e0bSTom Erickson NULL, NULL, 0, literal); 182092241e0bSTom Erickson zfs_unset_recvd_props_mode(zhp, &cookie); 182192241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 182292241e0bSTom Erickson return (-1); 182392241e0bSTom Erickson } else { 182492241e0bSTom Erickson nvlist_t *propval; 182592241e0bSTom Erickson char *recvdval; 182692241e0bSTom Erickson if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 182792241e0bSTom Erickson propname, &propval) != 0) 182892241e0bSTom Erickson return (-1); 182992241e0bSTom Erickson verify(nvlist_lookup_string(propval, ZPROP_VALUE, 183092241e0bSTom Erickson &recvdval) == 0); 183192241e0bSTom Erickson (void) strlcpy(propbuf, recvdval, proplen); 183292241e0bSTom Erickson } 183392241e0bSTom Erickson 183492241e0bSTom Erickson return (err == 0 ? 0 : -1); 183592241e0bSTom Erickson } 183692241e0bSTom Erickson 1837fa9e4066Sahrens /* 1838fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1839fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1840fa9e4066Sahrens * human-readable form. 1841fa9e4066Sahrens * 1842fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1843fa9e4066Sahrens */ 1844fa9e4066Sahrens int 1845fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1846990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1847fa9e4066Sahrens { 1848fa9e4066Sahrens char *source = NULL; 1849fa9e4066Sahrens uint64_t val; 1850fa9e4066Sahrens char *str; 1851e9dbad6fSeschrock const char *strval; 185292241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1853fa9e4066Sahrens 1854fa9e4066Sahrens /* 1855fa9e4066Sahrens * Check to see if this property applies to our object 1856fa9e4066Sahrens */ 1857fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1858fa9e4066Sahrens return (-1); 1859fa9e4066Sahrens 186092241e0bSTom Erickson if (received && zfs_prop_readonly(prop)) 186192241e0bSTom Erickson return (-1); 186292241e0bSTom Erickson 1863fa9e4066Sahrens if (src) 1864990b4856Slling *src = ZPROP_SRC_NONE; 1865fa9e4066Sahrens 1866fa9e4066Sahrens switch (prop) { 1867fa9e4066Sahrens case ZFS_PROP_CREATION: 1868fa9e4066Sahrens /* 1869fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1870fa9e4066Sahrens * this into a string unless 'literal' is specified. 1871fa9e4066Sahrens */ 1872fa9e4066Sahrens { 1873a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1874a2eea2e1Sahrens time_t time = (time_t)val; 1875fa9e4066Sahrens struct tm t; 1876fa9e4066Sahrens 1877fa9e4066Sahrens if (literal || 1878fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1879fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1880fa9e4066Sahrens &t) == 0) 1881a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1882fa9e4066Sahrens } 1883fa9e4066Sahrens break; 1884fa9e4066Sahrens 1885fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1886fa9e4066Sahrens /* 1887fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1888fa9e4066Sahrens * 1889fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1890fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1891fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1892fa9e4066Sahrens * 1893fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1894fa9e4066Sahrens * root to any values we return. 1895fa9e4066Sahrens */ 189629ab75c9Srm 18977f7322feSeschrock str = getprop_string(zhp, prop, &source); 1898fa9e4066Sahrens 1899b21718f0Sgw if (str[0] == '/') { 190029ab75c9Srm char buf[MAXPATHLEN]; 190129ab75c9Srm char *root = buf; 1902a79992aaSTom Erickson const char *relpath; 1903fa9e4066Sahrens 1904a79992aaSTom Erickson /* 1905a79992aaSTom Erickson * If we inherit the mountpoint, even from a dataset 1906a79992aaSTom Erickson * with a received value, the source will be the path of 1907a79992aaSTom Erickson * the dataset we inherit from. If source is 1908a79992aaSTom Erickson * ZPROP_SOURCE_VAL_RECVD, the received value is not 1909a79992aaSTom Erickson * inherited. 1910a79992aaSTom Erickson */ 1911a79992aaSTom Erickson if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 1912a79992aaSTom Erickson relpath = ""; 1913a79992aaSTom Erickson } else { 1914a79992aaSTom Erickson relpath = zhp->zfs_name + strlen(source); 1915a79992aaSTom Erickson if (relpath[0] == '/') 1916a79992aaSTom Erickson relpath++; 1917a79992aaSTom Erickson } 1918b21718f0Sgw 191929ab75c9Srm if ((zpool_get_prop(zhp->zpool_hdl, 192029ab75c9Srm ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 192129ab75c9Srm (strcmp(root, "-") == 0)) 192229ab75c9Srm root[0] = '\0'; 1923b21718f0Sgw /* 1924b21718f0Sgw * Special case an alternate root of '/'. This will 1925b21718f0Sgw * avoid having multiple leading slashes in the 1926b21718f0Sgw * mountpoint path. 1927b21718f0Sgw */ 1928b21718f0Sgw if (strcmp(root, "/") == 0) 1929b21718f0Sgw root++; 1930b21718f0Sgw 1931b21718f0Sgw /* 1932b21718f0Sgw * If the mountpoint is '/' then skip over this 1933b21718f0Sgw * if we are obtaining either an alternate root or 1934b21718f0Sgw * an inherited mountpoint. 1935b21718f0Sgw */ 1936b21718f0Sgw if (str[1] == '\0' && (root[0] != '\0' || 1937b21718f0Sgw relpath[0] != '\0')) 19387f7322feSeschrock str++; 1939fa9e4066Sahrens 1940fa9e4066Sahrens if (relpath[0] == '\0') 1941fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19427f7322feSeschrock root, str); 1943fa9e4066Sahrens else 1944fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19457f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1946fa9e4066Sahrens relpath); 1947fa9e4066Sahrens } else { 1948fa9e4066Sahrens /* 'legacy' or 'none' */ 19497f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1950fa9e4066Sahrens } 1951fa9e4066Sahrens 1952fa9e4066Sahrens break; 1953fa9e4066Sahrens 1954fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1955a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1956fa9e4066Sahrens proplen); 1957fa9e4066Sahrens /* 1958fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1959fa9e4066Sahrens * it doesn't apply to this dataset. 1960fa9e4066Sahrens */ 1961fa9e4066Sahrens if (propbuf[0] == '\0') 1962fa9e4066Sahrens return (-1); 1963fa9e4066Sahrens break; 1964fa9e4066Sahrens 1965fa9e4066Sahrens case ZFS_PROP_QUOTA: 1966a9799022Sck case ZFS_PROP_REFQUOTA: 1967fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1968a9799022Sck case ZFS_PROP_REFRESERVATION: 1969a9799022Sck 197099653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 197199653d4eSeschrock return (-1); 1972fa9e4066Sahrens 1973fa9e4066Sahrens /* 1974fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 1975fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 1976fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 1977fa9e4066Sahrens * that its set locally. 1978fa9e4066Sahrens */ 1979fa9e4066Sahrens if (val == 0) { 1980fa9e4066Sahrens if (literal) 1981fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 1982fa9e4066Sahrens else 1983fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 1984fa9e4066Sahrens } else { 1985fa9e4066Sahrens if (literal) 19865ad82045Snd (void) snprintf(propbuf, proplen, "%llu", 1987b1b8ab34Slling (u_longlong_t)val); 1988fa9e4066Sahrens else 1989fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 1990fa9e4066Sahrens } 1991fa9e4066Sahrens break; 1992fa9e4066Sahrens 1993fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 199499653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 199599653d4eSeschrock return (-1); 1996b24ab676SJeff Bonwick (void) snprintf(propbuf, proplen, "%llu.%02llux", 1997b24ab676SJeff Bonwick (u_longlong_t)(val / 100), 1998b24ab676SJeff Bonwick (u_longlong_t)(val % 100)); 1999fa9e4066Sahrens break; 2000fa9e4066Sahrens 2001fa9e4066Sahrens case ZFS_PROP_TYPE: 2002fa9e4066Sahrens switch (zhp->zfs_type) { 2003fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 2004fa9e4066Sahrens str = "filesystem"; 2005fa9e4066Sahrens break; 2006fa9e4066Sahrens case ZFS_TYPE_VOLUME: 2007fa9e4066Sahrens str = "volume"; 2008fa9e4066Sahrens break; 2009fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 2010fa9e4066Sahrens str = "snapshot"; 2011fa9e4066Sahrens break; 2012fa9e4066Sahrens default: 201399653d4eSeschrock abort(); 2014fa9e4066Sahrens } 2015fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2016fa9e4066Sahrens break; 2017fa9e4066Sahrens 2018fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2019fa9e4066Sahrens /* 2020fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2021fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2022fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2023fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2024fa9e4066Sahrens */ 202599653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 202699653d4eSeschrock src, &source, &val) != 0) 202799653d4eSeschrock return (-1); 202899653d4eSeschrock if (val) 2029fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2030fa9e4066Sahrens else 2031fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2032fa9e4066Sahrens break; 2033fa9e4066Sahrens 2034fa9e4066Sahrens case ZFS_PROP_NAME: 2035fa9e4066Sahrens /* 2036fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2037fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2038fa9e4066Sahrens * consumers. 2039fa9e4066Sahrens */ 2040fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2041fa9e4066Sahrens break; 2042fa9e4066Sahrens 20434201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 20444201a95eSRic Aleshire { 20454201a95eSRic Aleshire m_label_t *new_sl = NULL; 20464201a95eSRic Aleshire char *ascii = NULL; /* human readable label */ 20474201a95eSRic Aleshire 20484201a95eSRic Aleshire (void) strlcpy(propbuf, 20494201a95eSRic Aleshire getprop_string(zhp, prop, &source), proplen); 20504201a95eSRic Aleshire 20514201a95eSRic Aleshire if (literal || (strcasecmp(propbuf, 20524201a95eSRic Aleshire ZFS_MLSLABEL_DEFAULT) == 0)) 20534201a95eSRic Aleshire break; 20544201a95eSRic Aleshire 20554201a95eSRic Aleshire /* 20564201a95eSRic Aleshire * Try to translate the internal hex string to 20574201a95eSRic Aleshire * human-readable output. If there are any 20584201a95eSRic Aleshire * problems just use the hex string. 20594201a95eSRic Aleshire */ 20604201a95eSRic Aleshire 20614201a95eSRic Aleshire if (str_to_label(propbuf, &new_sl, MAC_LABEL, 20624201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1) { 20634201a95eSRic Aleshire m_label_free(new_sl); 20644201a95eSRic Aleshire break; 20654201a95eSRic Aleshire } 20664201a95eSRic Aleshire 20674201a95eSRic Aleshire if (label_to_str(new_sl, &ascii, M_LABEL, 20684201a95eSRic Aleshire DEF_NAMES) != 0) { 20694201a95eSRic Aleshire if (ascii) 20704201a95eSRic Aleshire free(ascii); 20714201a95eSRic Aleshire m_label_free(new_sl); 20724201a95eSRic Aleshire break; 20734201a95eSRic Aleshire } 20744201a95eSRic Aleshire m_label_free(new_sl); 20754201a95eSRic Aleshire 20764201a95eSRic Aleshire (void) strlcpy(propbuf, ascii, proplen); 20774201a95eSRic Aleshire free(ascii); 20784201a95eSRic Aleshire } 20794201a95eSRic Aleshire break; 20804201a95eSRic Aleshire 2081fa9e4066Sahrens default: 2082e7437265Sahrens switch (zfs_prop_get_type(prop)) { 208391ebeef5Sahrens case PROP_TYPE_NUMBER: 2084e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2085e7437265Sahrens &source, &val) != 0) 2086e7437265Sahrens return (-1); 2087e7437265Sahrens if (literal) 2088e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2089e7437265Sahrens (u_longlong_t)val); 2090e7437265Sahrens else 2091e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2092e7437265Sahrens break; 2093e7437265Sahrens 209491ebeef5Sahrens case PROP_TYPE_STRING: 2095e7437265Sahrens (void) strlcpy(propbuf, 2096e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 2097e7437265Sahrens break; 2098e7437265Sahrens 209991ebeef5Sahrens case PROP_TYPE_INDEX: 2100fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2101fe192f76Sahrens &source, &val) != 0) 2102fe192f76Sahrens return (-1); 2103fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2104e7437265Sahrens return (-1); 2105e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2106e7437265Sahrens break; 2107e7437265Sahrens 2108e7437265Sahrens default: 2109e7437265Sahrens abort(); 2110e7437265Sahrens } 2111fa9e4066Sahrens } 2112fa9e4066Sahrens 2113fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2114fa9e4066Sahrens 2115fa9e4066Sahrens return (0); 2116fa9e4066Sahrens } 2117fa9e4066Sahrens 2118fa9e4066Sahrens /* 2119fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2120fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2121fa9e4066Sahrens * hard-coded property types. 2122fa9e4066Sahrens */ 2123fa9e4066Sahrens uint64_t 2124fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2125fa9e4066Sahrens { 2126fa9e4066Sahrens char *source; 212799653d4eSeschrock uint64_t val; 212899653d4eSeschrock 21293cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 2130fa9e4066Sahrens 213199653d4eSeschrock return (val); 2132fa9e4066Sahrens } 2133fa9e4066Sahrens 21347b97dc1aSrm int 21357b97dc1aSrm zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21367b97dc1aSrm { 21377b97dc1aSrm char buf[64]; 21387b97dc1aSrm 213914843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21407b97dc1aSrm return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21417b97dc1aSrm } 21427b97dc1aSrm 2143fa9e4066Sahrens /* 2144fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2145fa9e4066Sahrens */ 2146fa9e4066Sahrens int 2147fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2148990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2149fa9e4066Sahrens { 2150fa9e4066Sahrens char *source; 2151fa9e4066Sahrens 2152fa9e4066Sahrens /* 2153fa9e4066Sahrens * Check to see if this property applies to our object 2154fa9e4066Sahrens */ 2155e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2156ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 215799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 215899653d4eSeschrock zfs_prop_to_name(prop))); 2159e45ce728Sahrens } 2160fa9e4066Sahrens 2161fa9e4066Sahrens if (src) 2162990b4856Slling *src = ZPROP_SRC_NONE; 2163fa9e4066Sahrens 216499653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 216599653d4eSeschrock return (-1); 2166fa9e4066Sahrens 2167fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2168fa9e4066Sahrens 2169fa9e4066Sahrens return (0); 2170fa9e4066Sahrens } 2171fa9e4066Sahrens 217214843421SMatthew Ahrens static int 217314843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 217414843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 217514843421SMatthew Ahrens { 217614843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 217714843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 217814843421SMatthew Ahrens idmap_stat status; 217914843421SMatthew Ahrens int err = EINVAL; 218014843421SMatthew Ahrens 218114843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 218214843421SMatthew Ahrens goto out; 218314843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 218414843421SMatthew Ahrens goto out; 218514843421SMatthew Ahrens 218614843421SMatthew Ahrens if (isuser) { 218714843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 218814843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 218914843421SMatthew Ahrens } else { 219014843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 219114843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 219214843421SMatthew Ahrens } 219314843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 219414843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 219514843421SMatthew Ahrens status == IDMAP_SUCCESS) 219614843421SMatthew Ahrens err = 0; 219714843421SMatthew Ahrens else 219814843421SMatthew Ahrens err = EINVAL; 219914843421SMatthew Ahrens out: 220014843421SMatthew Ahrens if (get_hdl) 220114843421SMatthew Ahrens idmap_get_destroy(get_hdl); 220214843421SMatthew Ahrens if (idmap_hdl) 220314843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 220414843421SMatthew Ahrens return (err); 220514843421SMatthew Ahrens } 220614843421SMatthew Ahrens 220714843421SMatthew Ahrens /* 220814843421SMatthew Ahrens * convert the propname into parameters needed by kernel 220914843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 221014843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 221114843421SMatthew Ahrens */ 221214843421SMatthew Ahrens static int 221314843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 221414843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 221514843421SMatthew Ahrens { 221614843421SMatthew Ahrens zfs_userquota_prop_t type; 221714843421SMatthew Ahrens char *cp, *end; 22183b12c289SMatthew Ahrens char *numericsid = NULL; 221914843421SMatthew Ahrens boolean_t isuser; 222014843421SMatthew Ahrens 222114843421SMatthew Ahrens domain[0] = '\0'; 222214843421SMatthew Ahrens 222314843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 222414843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 222514843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 222614843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 222714843421SMatthew Ahrens break; 222814843421SMatthew Ahrens } 222914843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 223014843421SMatthew Ahrens return (EINVAL); 223114843421SMatthew Ahrens *typep = type; 223214843421SMatthew Ahrens 223314843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 223414843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 223514843421SMatthew Ahrens 223614843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 223714843421SMatthew Ahrens 223814843421SMatthew Ahrens if (strchr(cp, '@')) { 223914843421SMatthew Ahrens /* 224014843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 22413b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 224214843421SMatthew Ahrens */ 22433b12c289SMatthew Ahrens directory_error_t e; 224414843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 224514843421SMatthew Ahrens return (ENOENT); 22463b12c289SMatthew Ahrens if (isuser) { 22473b12c289SMatthew Ahrens e = directory_sid_from_user_name(NULL, 22483b12c289SMatthew Ahrens cp, &numericsid); 22493b12c289SMatthew Ahrens } else { 22503b12c289SMatthew Ahrens e = directory_sid_from_group_name(NULL, 22513b12c289SMatthew Ahrens cp, &numericsid); 22523b12c289SMatthew Ahrens } 22533b12c289SMatthew Ahrens if (e != NULL) { 22543b12c289SMatthew Ahrens directory_error_free(e); 225514843421SMatthew Ahrens return (ENOENT); 22563b12c289SMatthew Ahrens } 22573b12c289SMatthew Ahrens if (numericsid == NULL) 225814843421SMatthew Ahrens return (ENOENT); 22593b12c289SMatthew Ahrens cp = numericsid; 22603b12c289SMatthew Ahrens /* will be further decoded below */ 22613b12c289SMatthew Ahrens } 22623b12c289SMatthew Ahrens 22633b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 226414843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 22653b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 226614843421SMatthew Ahrens cp = strrchr(domain, '-'); 226714843421SMatthew Ahrens *cp = '\0'; 226814843421SMatthew Ahrens cp++; 226914843421SMatthew Ahrens 227014843421SMatthew Ahrens errno = 0; 227114843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 22723b12c289SMatthew Ahrens if (numericsid) { 22733b12c289SMatthew Ahrens free(numericsid); 22743b12c289SMatthew Ahrens numericsid = NULL; 22753b12c289SMatthew Ahrens } 2276777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 227714843421SMatthew Ahrens return (EINVAL); 227814843421SMatthew Ahrens } else if (!isdigit(*cp)) { 227914843421SMatthew Ahrens /* 228014843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 228114843421SMatthew Ahrens * turned into a uid/gid 228214843421SMatthew Ahrens */ 228314843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 228414843421SMatthew Ahrens return (ENOENT); 228514843421SMatthew Ahrens if (isuser) { 228614843421SMatthew Ahrens struct passwd *pw; 228714843421SMatthew Ahrens pw = getpwnam(cp); 228814843421SMatthew Ahrens if (pw == NULL) 228914843421SMatthew Ahrens return (ENOENT); 229014843421SMatthew Ahrens *ridp = pw->pw_uid; 229114843421SMatthew Ahrens } else { 229214843421SMatthew Ahrens struct group *gr; 229314843421SMatthew Ahrens gr = getgrnam(cp); 229414843421SMatthew Ahrens if (gr == NULL) 229514843421SMatthew Ahrens return (ENOENT); 229614843421SMatthew Ahrens *ridp = gr->gr_gid; 229714843421SMatthew Ahrens } 229814843421SMatthew Ahrens } else { 229914843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 230014843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 230114843421SMatthew Ahrens idmap_rid_t rid; 230214843421SMatthew Ahrens char *mapdomain; 230314843421SMatthew Ahrens 230414843421SMatthew Ahrens if (*end != '\0') 230514843421SMatthew Ahrens return (EINVAL); 230614843421SMatthew Ahrens if (id > MAXUID) { 230714843421SMatthew Ahrens /* It's an ephemeral ID. */ 230814843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 230914843421SMatthew Ahrens &mapdomain, &rid) != 0) 231014843421SMatthew Ahrens return (ENOENT); 23113b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 231214843421SMatthew Ahrens *ridp = rid; 231314843421SMatthew Ahrens } else { 231414843421SMatthew Ahrens *ridp = id; 231514843421SMatthew Ahrens } 231614843421SMatthew Ahrens } 231714843421SMatthew Ahrens 23183b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 231914843421SMatthew Ahrens return (0); 232014843421SMatthew Ahrens } 232114843421SMatthew Ahrens 2322edea4b55SLin Ling static int 2323edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2324edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 232514843421SMatthew Ahrens { 232614843421SMatthew Ahrens int err; 232714843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 232814843421SMatthew Ahrens 232914843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 233014843421SMatthew Ahrens 233114843421SMatthew Ahrens err = userquota_propname_decode(propname, 233214843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2333edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2334edea4b55SLin Ling zc.zc_objset_type = *typep; 233514843421SMatthew Ahrens if (err) 233614843421SMatthew Ahrens return (err); 233714843421SMatthew Ahrens 233814843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 233914843421SMatthew Ahrens if (err) 234014843421SMatthew Ahrens return (err); 234114843421SMatthew Ahrens 2342edea4b55SLin Ling *propvalue = zc.zc_cookie; 2343edea4b55SLin Ling return (0); 2344edea4b55SLin Ling } 2345edea4b55SLin Ling 2346edea4b55SLin Ling int 2347edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2348edea4b55SLin Ling uint64_t *propvalue) 2349edea4b55SLin Ling { 2350edea4b55SLin Ling zfs_userquota_prop_t type; 2351edea4b55SLin Ling 2352edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2353edea4b55SLin Ling &type)); 2354edea4b55SLin Ling } 2355edea4b55SLin Ling 2356edea4b55SLin Ling int 2357edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2358edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 2359edea4b55SLin Ling { 2360edea4b55SLin Ling int err; 2361edea4b55SLin Ling uint64_t propvalue; 2362edea4b55SLin Ling zfs_userquota_prop_t type; 2363edea4b55SLin Ling 2364edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2365edea4b55SLin Ling &type); 2366edea4b55SLin Ling 2367edea4b55SLin Ling if (err) 2368edea4b55SLin Ling return (err); 2369edea4b55SLin Ling 237014843421SMatthew Ahrens if (literal) { 2371edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 2372edea4b55SLin Ling } else if (propvalue == 0 && 237314843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 237414843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 237514843421SMatthew Ahrens } else { 2376edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 237714843421SMatthew Ahrens } 237814843421SMatthew Ahrens return (0); 237914843421SMatthew Ahrens } 238014843421SMatthew Ahrens 2381fa9e4066Sahrens /* 2382fa9e4066Sahrens * Returns the name of the given zfs handle. 2383fa9e4066Sahrens */ 2384fa9e4066Sahrens const char * 2385fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2386fa9e4066Sahrens { 2387fa9e4066Sahrens return (zhp->zfs_name); 2388fa9e4066Sahrens } 2389fa9e4066Sahrens 2390fa9e4066Sahrens /* 2391fa9e4066Sahrens * Returns the type of the given zfs handle. 2392fa9e4066Sahrens */ 2393fa9e4066Sahrens zfs_type_t 2394fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2395fa9e4066Sahrens { 2396fa9e4066Sahrens return (zhp->zfs_type); 2397fa9e4066Sahrens } 2398fa9e4066Sahrens 2399ebedde84SEric Taylor static int 2400ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2401ebedde84SEric Taylor { 2402ebedde84SEric Taylor int rc; 2403ebedde84SEric Taylor uint64_t orig_cookie; 2404ebedde84SEric Taylor 2405ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2406ebedde84SEric Taylor top: 2407ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2408ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2409ebedde84SEric Taylor 2410ebedde84SEric Taylor if (rc == -1) { 2411ebedde84SEric Taylor switch (errno) { 2412ebedde84SEric Taylor case ENOMEM: 2413ebedde84SEric Taylor /* expand nvlist memory and try again */ 2414ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2415ebedde84SEric Taylor zcmd_free_nvlists(zc); 2416ebedde84SEric Taylor return (-1); 2417ebedde84SEric Taylor } 2418ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2419ebedde84SEric Taylor goto top; 2420ebedde84SEric Taylor /* 2421ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2422ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2423ebedde84SEric Taylor * has been removed since we obtained the handle. 2424ebedde84SEric Taylor */ 2425ebedde84SEric Taylor case ESRCH: 2426ebedde84SEric Taylor case ENOENT: 2427ebedde84SEric Taylor rc = 1; 2428ebedde84SEric Taylor break; 2429ebedde84SEric Taylor default: 2430ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2431ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2432ebedde84SEric Taylor "cannot iterate filesystems")); 2433ebedde84SEric Taylor break; 2434ebedde84SEric Taylor } 2435ebedde84SEric Taylor } 2436ebedde84SEric Taylor return (rc); 2437ebedde84SEric Taylor } 2438ebedde84SEric Taylor 2439fa9e4066Sahrens /* 24407f7322feSeschrock * Iterate over all child filesystems 2441fa9e4066Sahrens */ 2442fa9e4066Sahrens int 24437f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2444fa9e4066Sahrens { 2445fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2446fa9e4066Sahrens zfs_handle_t *nzhp; 2447fa9e4066Sahrens int ret; 2448fa9e4066Sahrens 24493cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24503cb34c60Sahrens return (0); 24513cb34c60Sahrens 2452ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2453ebedde84SEric Taylor return (-1); 2454ebedde84SEric Taylor 2455ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2456ebedde84SEric Taylor &zc)) == 0) { 2457fa9e4066Sahrens /* 2458fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2459fa9e4066Sahrens * that the pool has since been removed. 2460fa9e4066Sahrens */ 2461ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2462ebedde84SEric Taylor &zc)) == NULL) { 2463fa9e4066Sahrens continue; 2464ebedde84SEric Taylor } 2465fa9e4066Sahrens 2466ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2467ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2468fa9e4066Sahrens return (ret); 2469ebedde84SEric Taylor } 2470fa9e4066Sahrens } 2471ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2472ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 24737f7322feSeschrock } 24747f7322feSeschrock 24757f7322feSeschrock /* 24767f7322feSeschrock * Iterate over all snapshots 24777f7322feSeschrock */ 24787f7322feSeschrock int 24797f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24807f7322feSeschrock { 24817f7322feSeschrock zfs_cmd_t zc = { 0 }; 24827f7322feSeschrock zfs_handle_t *nzhp; 24837f7322feSeschrock int ret; 2484fa9e4066Sahrens 24853cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24863cb34c60Sahrens return (0); 24873cb34c60Sahrens 2488ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2489ebedde84SEric Taylor return (-1); 2490ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2491ebedde84SEric Taylor &zc)) == 0) { 2492fa9e4066Sahrens 2493ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2494ebedde84SEric Taylor &zc)) == NULL) { 2495fa9e4066Sahrens continue; 2496ebedde84SEric Taylor } 2497fa9e4066Sahrens 2498ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2499ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2500fa9e4066Sahrens return (ret); 2501ebedde84SEric Taylor } 2502fa9e4066Sahrens } 2503ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2504ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2505fa9e4066Sahrens } 2506fa9e4066Sahrens 25077f7322feSeschrock /* 25087f7322feSeschrock * Iterate over all children, snapshots and filesystems 25097f7322feSeschrock */ 25107f7322feSeschrock int 25117f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25127f7322feSeschrock { 25137f7322feSeschrock int ret; 25147f7322feSeschrock 25157f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25167f7322feSeschrock return (ret); 25177f7322feSeschrock 25187f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 25197f7322feSeschrock } 25207f7322feSeschrock 2521d41c4376SMark J Musante /* 2522d41c4376SMark J Musante * Is one dataset name a child dataset of another? 2523d41c4376SMark J Musante * 2524d41c4376SMark J Musante * Needs to handle these cases: 2525d41c4376SMark J Musante * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 2526d41c4376SMark J Musante * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 2527d41c4376SMark J Musante * Descendant? No. No. No. Yes. 2528d41c4376SMark J Musante */ 2529d41c4376SMark J Musante static boolean_t 2530d41c4376SMark J Musante is_descendant(const char *ds1, const char *ds2) 2531d41c4376SMark J Musante { 2532d41c4376SMark J Musante size_t d1len = strlen(ds1); 2533d41c4376SMark J Musante 2534d41c4376SMark J Musante /* ds2 can't be a descendant if it's smaller */ 2535d41c4376SMark J Musante if (strlen(ds2) < d1len) 2536d41c4376SMark J Musante return (B_FALSE); 2537d41c4376SMark J Musante 2538d41c4376SMark J Musante /* otherwise, compare strings and verify that there's a '/' char */ 2539d41c4376SMark J Musante return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 2540d41c4376SMark J Musante } 2541d41c4376SMark J Musante 2542fa9e4066Sahrens /* 2543fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2544fa9e4066Sahrens * Can return NULL if this is a pool. 2545fa9e4066Sahrens */ 2546fa9e4066Sahrens static int 2547fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2548fa9e4066Sahrens { 2549fa9e4066Sahrens char *loc; 2550fa9e4066Sahrens 2551fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2552fa9e4066Sahrens return (-1); 2553fa9e4066Sahrens 2554fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2555fa9e4066Sahrens buf[loc - path] = '\0'; 2556fa9e4066Sahrens 2557fa9e4066Sahrens return (0); 2558fa9e4066Sahrens } 2559fa9e4066Sahrens 2560fa9e4066Sahrens /* 25617f1f55eaSvb * If accept_ancestor is false, then check to make sure that the given path has 25627f1f55eaSvb * a parent, and that it exists. If accept_ancestor is true, then find the 25637f1f55eaSvb * closest existing ancestor for the given path. In prefixlen return the 25647f1f55eaSvb * length of already existing prefix of the given path. We also fetch the 25657f1f55eaSvb * 'zoned' property, which is used to validate property settings when creating 25667f1f55eaSvb * new datasets. 2567fa9e4066Sahrens */ 2568fa9e4066Sahrens static int 25697f1f55eaSvb check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25707f1f55eaSvb boolean_t accept_ancestor, int *prefixlen) 2571fa9e4066Sahrens { 2572fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2573fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2574fa9e4066Sahrens char *slash; 25757f7322feSeschrock zfs_handle_t *zhp; 257699653d4eSeschrock char errbuf[1024]; 2577d41c4376SMark J Musante uint64_t is_zoned; 257899653d4eSeschrock 2579deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2580deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2581fa9e4066Sahrens 2582fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2583fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 258499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 258599653d4eSeschrock "missing dataset name")); 258699653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2587fa9e4066Sahrens } 2588fa9e4066Sahrens 2589fa9e4066Sahrens /* check to see if the pool exists */ 2590fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2591fa9e4066Sahrens slash = parent + strlen(parent); 2592fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2593fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 259499653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2595fa9e4066Sahrens errno == ENOENT) { 259699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 259799653d4eSeschrock "no such pool '%s'"), zc.zc_name); 259899653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2599fa9e4066Sahrens } 2600fa9e4066Sahrens 2601fa9e4066Sahrens /* check to see if the parent dataset exists */ 26027f1f55eaSvb while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 26037f1f55eaSvb if (errno == ENOENT && accept_ancestor) { 26047f1f55eaSvb /* 26057f1f55eaSvb * Go deeper to find an ancestor, give up on top level. 26067f1f55eaSvb */ 26077f1f55eaSvb if (parent_name(parent, parent, sizeof (parent)) != 0) { 26087f1f55eaSvb zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26097f1f55eaSvb "no such pool '%s'"), zc.zc_name); 26107f1f55eaSvb return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26117f1f55eaSvb } 26127f1f55eaSvb } else if (errno == ENOENT) { 261399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 261499653d4eSeschrock "parent does not exist")); 261599653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26167f1f55eaSvb } else 261799653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2618fa9e4066Sahrens } 2619fa9e4066Sahrens 2620d41c4376SMark J Musante is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2621d41c4376SMark J Musante if (zoned != NULL) 2622d41c4376SMark J Musante *zoned = is_zoned; 2623d41c4376SMark J Musante 2624fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2625d41c4376SMark J Musante if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 262699653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26277f7322feSeschrock zfs_close(zhp); 2628fa9e4066Sahrens return (-1); 2629fa9e4066Sahrens } 2630fa9e4066Sahrens 2631fa9e4066Sahrens /* make sure parent is a filesystem */ 26327f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 263399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 263499653d4eSeschrock "parent is not a filesystem")); 263599653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26367f7322feSeschrock zfs_close(zhp); 2637fa9e4066Sahrens return (-1); 2638fa9e4066Sahrens } 2639fa9e4066Sahrens 26407f7322feSeschrock zfs_close(zhp); 26417f1f55eaSvb if (prefixlen != NULL) 26427f1f55eaSvb *prefixlen = strlen(parent); 26437f1f55eaSvb return (0); 26447f1f55eaSvb } 26457f1f55eaSvb 26467f1f55eaSvb /* 26477f1f55eaSvb * Finds whether the dataset of the given type(s) exists. 26487f1f55eaSvb */ 26497f1f55eaSvb boolean_t 26507f1f55eaSvb zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26517f1f55eaSvb { 26527f1f55eaSvb zfs_handle_t *zhp; 26537f1f55eaSvb 2654f18faf3fSek if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26557f1f55eaSvb return (B_FALSE); 26567f1f55eaSvb 26577f1f55eaSvb /* 26587f1f55eaSvb * Try to get stats for the dataset, which will tell us if it exists. 26597f1f55eaSvb */ 26607f1f55eaSvb if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26617f1f55eaSvb int ds_type = zhp->zfs_type; 26627f1f55eaSvb 26637f1f55eaSvb zfs_close(zhp); 26647f1f55eaSvb if (types & ds_type) 26657f1f55eaSvb return (B_TRUE); 26667f1f55eaSvb } 26677f1f55eaSvb return (B_FALSE); 26687f1f55eaSvb } 26697f1f55eaSvb 26703cb34c60Sahrens /* 26713cb34c60Sahrens * Given a path to 'target', create all the ancestors between 26723cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 26733cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26743cb34c60Sahrens */ 26753cb34c60Sahrens int 26763cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26773cb34c60Sahrens { 26783cb34c60Sahrens zfs_handle_t *h; 26793cb34c60Sahrens char *cp; 26803cb34c60Sahrens const char *opname; 26813cb34c60Sahrens 26823cb34c60Sahrens /* make sure prefix exists */ 26833cb34c60Sahrens cp = target + prefixlen; 26843cb34c60Sahrens if (*cp != '/') { 26853cb34c60Sahrens assert(strchr(cp, '/') == NULL); 26863cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26873cb34c60Sahrens } else { 26883cb34c60Sahrens *cp = '\0'; 26893cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26903cb34c60Sahrens *cp = '/'; 26913cb34c60Sahrens } 26923cb34c60Sahrens if (h == NULL) 26933cb34c60Sahrens return (-1); 26943cb34c60Sahrens zfs_close(h); 26953cb34c60Sahrens 26963cb34c60Sahrens /* 26973cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26983cb34c60Sahrens * up to the prefixlen-long one. 26993cb34c60Sahrens */ 27003cb34c60Sahrens for (cp = target + prefixlen + 1; 27013cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 27023cb34c60Sahrens char *logstr; 27033cb34c60Sahrens 27043cb34c60Sahrens *cp = '\0'; 27053cb34c60Sahrens 27063cb34c60Sahrens h = make_dataset_handle(hdl, target); 27073cb34c60Sahrens if (h) { 27083cb34c60Sahrens /* it already exists, nothing to do here */ 27093cb34c60Sahrens zfs_close(h); 27103cb34c60Sahrens continue; 27113cb34c60Sahrens } 27123cb34c60Sahrens 27133cb34c60Sahrens logstr = hdl->libzfs_log_str; 27143cb34c60Sahrens hdl->libzfs_log_str = NULL; 27153cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27163cb34c60Sahrens NULL) != 0) { 27173cb34c60Sahrens hdl->libzfs_log_str = logstr; 27183cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27193cb34c60Sahrens goto ancestorerr; 27203cb34c60Sahrens } 27213cb34c60Sahrens 27223cb34c60Sahrens hdl->libzfs_log_str = logstr; 27233cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27243cb34c60Sahrens if (h == NULL) { 27253cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27263cb34c60Sahrens goto ancestorerr; 27273cb34c60Sahrens } 27283cb34c60Sahrens 27293cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27303cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27313cb34c60Sahrens goto ancestorerr; 27323cb34c60Sahrens } 27333cb34c60Sahrens 27343cb34c60Sahrens if (zfs_share(h) != 0) { 27353cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27363cb34c60Sahrens goto ancestorerr; 27373cb34c60Sahrens } 27383cb34c60Sahrens 27393cb34c60Sahrens zfs_close(h); 27403cb34c60Sahrens } 27413cb34c60Sahrens 27423cb34c60Sahrens return (0); 27433cb34c60Sahrens 27443cb34c60Sahrens ancestorerr: 27453cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27463cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 27473cb34c60Sahrens return (-1); 27483cb34c60Sahrens } 27493cb34c60Sahrens 27507f1f55eaSvb /* 27517f1f55eaSvb * Creates non-existing ancestors of the given path. 27527f1f55eaSvb */ 27537f1f55eaSvb int 27547f1f55eaSvb zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27557f1f55eaSvb { 27567f1f55eaSvb int prefix; 27577f1f55eaSvb char *path_copy; 27587f1f55eaSvb int rc; 27597f1f55eaSvb 2760d41c4376SMark J Musante if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27617f1f55eaSvb return (-1); 27627f1f55eaSvb 27637f1f55eaSvb if ((path_copy = strdup(path)) != NULL) { 27647f1f55eaSvb rc = create_parents(hdl, path_copy, prefix); 27657f1f55eaSvb free(path_copy); 27667f1f55eaSvb } 27677f1f55eaSvb if (path_copy == NULL || rc != 0) 27687f1f55eaSvb return (-1); 27697f1f55eaSvb 2770fa9e4066Sahrens return (0); 2771fa9e4066Sahrens } 2772fa9e4066Sahrens 2773fa9e4066Sahrens /* 2774e9dbad6fSeschrock * Create a new filesystem or volume. 2775fa9e4066Sahrens */ 2776fa9e4066Sahrens int 277799653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2778e9dbad6fSeschrock nvlist_t *props) 2779fa9e4066Sahrens { 2780fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2781fa9e4066Sahrens int ret; 2782fa9e4066Sahrens uint64_t size = 0; 2783fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 278499653d4eSeschrock char errbuf[1024]; 2785e9dbad6fSeschrock uint64_t zoned; 278699653d4eSeschrock 278799653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 278899653d4eSeschrock "cannot create '%s'"), path); 2789fa9e4066Sahrens 2790fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2791f18faf3fSek if (!zfs_validate_name(hdl, path, type, B_TRUE)) 279299653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2793fa9e4066Sahrens 2794fa9e4066Sahrens /* validate parents exist */ 27957f1f55eaSvb if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2796fa9e4066Sahrens return (-1); 2797fa9e4066Sahrens 2798fa9e4066Sahrens /* 2799fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2800fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2801fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2802fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2803fa9e4066Sahrens * first try to see if the dataset exists. 2804fa9e4066Sahrens */ 2805fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2806990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 280799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 280899653d4eSeschrock "dataset already exists")); 280999653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2810fa9e4066Sahrens } 2811fa9e4066Sahrens 2812fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2813fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2814fa9e4066Sahrens else 2815fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2816fa9e4066Sahrens 28170a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2818b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2819e9dbad6fSeschrock return (-1); 2820e9dbad6fSeschrock 2821fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 28225c5460e9Seschrock /* 28235c5460e9Seschrock * If we are creating a volume, the size and block size must 28245c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 28255c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28265c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 28275c5460e9Seschrock * zero. 28285c5460e9Seschrock */ 2829e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2830e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2831e9dbad6fSeschrock nvlist_free(props); 283299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2833e9dbad6fSeschrock "missing volume size")); 2834e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2835e9dbad6fSeschrock } 2836e9dbad6fSeschrock 2837e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2838e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2839e9dbad6fSeschrock &blocksize)) != 0) { 2840e9dbad6fSeschrock if (ret == ENOENT) { 2841e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2842e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2843e9dbad6fSeschrock } else { 2844e9dbad6fSeschrock nvlist_free(props); 2845e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2846e9dbad6fSeschrock "missing volume block size")); 2847e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2848e9dbad6fSeschrock } 2849fa9e4066Sahrens } 2850fa9e4066Sahrens 2851e9dbad6fSeschrock if (size == 0) { 2852e9dbad6fSeschrock nvlist_free(props); 285399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2854e9dbad6fSeschrock "volume size cannot be zero")); 2855e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28565c5460e9Seschrock } 28575c5460e9Seschrock 28585c5460e9Seschrock if (size % blocksize != 0) { 2859e9dbad6fSeschrock nvlist_free(props); 286099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2861e9dbad6fSeschrock "volume size must be a multiple of volume block " 2862e9dbad6fSeschrock "size")); 2863e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28645c5460e9Seschrock } 2865fa9e4066Sahrens } 2866fa9e4066Sahrens 2867990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2868e9dbad6fSeschrock return (-1); 2869e9dbad6fSeschrock nvlist_free(props); 2870e9dbad6fSeschrock 2871fa9e4066Sahrens /* create the dataset */ 2872ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2873fa9e4066Sahrens 2874e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2875e9dbad6fSeschrock 2876fa9e4066Sahrens /* check for failure */ 2877fa9e4066Sahrens if (ret != 0) { 2878fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2879fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2880fa9e4066Sahrens 2881fa9e4066Sahrens switch (errno) { 2882fa9e4066Sahrens case ENOENT: 288399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 288499653d4eSeschrock "no such parent '%s'"), parent); 288599653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2886fa9e4066Sahrens 2887fa9e4066Sahrens case EINVAL: 288899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2889d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 289099653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2891fa9e4066Sahrens 2892fa9e4066Sahrens case EDOM: 289399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2894e9dbad6fSeschrock "volume block size must be power of 2 from " 2895e9dbad6fSeschrock "%u to %uk"), 2896fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2897fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 289899653d4eSeschrock 2899e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 290099653d4eSeschrock 290140feaa91Sahrens case ENOTSUP: 290240feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 290340feaa91Sahrens "pool must be upgraded to set this " 290440feaa91Sahrens "property or value")); 290540feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2906fa9e4066Sahrens #ifdef _ILP32 2907fa9e4066Sahrens case EOVERFLOW: 2908fa9e4066Sahrens /* 2909fa9e4066Sahrens * This platform can't address a volume this big. 2910fa9e4066Sahrens */ 291199653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 291299653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 291399653d4eSeschrock errbuf)); 2914fa9e4066Sahrens #endif 291599653d4eSeschrock /* FALLTHROUGH */ 2916fa9e4066Sahrens default: 291799653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2918fa9e4066Sahrens } 2919fa9e4066Sahrens } 2920fa9e4066Sahrens 2921fa9e4066Sahrens return (0); 2922fa9e4066Sahrens } 2923fa9e4066Sahrens 2924fa9e4066Sahrens /* 2925fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2926fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2927fa9e4066Sahrens */ 2928fa9e4066Sahrens int 2929842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2930fa9e4066Sahrens { 2931fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2932fa9e4066Sahrens 2933fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2934fa9e4066Sahrens 2935e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2936fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2937fa9e4066Sahrens } else { 2938fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2939fa9e4066Sahrens } 2940fa9e4066Sahrens 2941842727c2SChris Kirby zc.zc_defer_destroy = defer; 2942ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2943ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 294499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 294599653d4eSeschrock zhp->zfs_name)); 29461d452cf5Sahrens } 2947fa9e4066Sahrens 2948fa9e4066Sahrens remove_mountpoint(zhp); 2949fa9e4066Sahrens 2950fa9e4066Sahrens return (0); 2951fa9e4066Sahrens } 2952fa9e4066Sahrens 29531d452cf5Sahrens struct destroydata { 29541d452cf5Sahrens char *snapname; 29551d452cf5Sahrens boolean_t gotone; 29563ccfa83cSahrens boolean_t closezhp; 29571d452cf5Sahrens }; 29581d452cf5Sahrens 29591d452cf5Sahrens static int 2960681d9761SEric Taylor zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29611d452cf5Sahrens { 29621d452cf5Sahrens struct destroydata *dd = arg; 29631d452cf5Sahrens zfs_handle_t *szhp; 29641d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 29653ccfa83cSahrens boolean_t closezhp = dd->closezhp; 2966681d9761SEric Taylor int rv = 0; 29671d452cf5Sahrens 2968e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2969e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2970e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29711d452cf5Sahrens 29721d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 29731d452cf5Sahrens if (szhp) { 29741d452cf5Sahrens dd->gotone = B_TRUE; 29751d452cf5Sahrens zfs_close(szhp); 29761d452cf5Sahrens } 29771d452cf5Sahrens 29783ccfa83cSahrens dd->closezhp = B_TRUE; 2979681d9761SEric Taylor if (!dd->gotone) 2980681d9761SEric Taylor rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 29813ccfa83cSahrens if (closezhp) 29823ccfa83cSahrens zfs_close(zhp); 29833ccfa83cSahrens return (rv); 29841d452cf5Sahrens } 29851d452cf5Sahrens 29861d452cf5Sahrens /* 29871d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 29881d452cf5Sahrens */ 29891d452cf5Sahrens int 2990842727c2SChris Kirby zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 29911d452cf5Sahrens { 29921d452cf5Sahrens zfs_cmd_t zc = { 0 }; 29931d452cf5Sahrens int ret; 29941d452cf5Sahrens struct destroydata dd = { 0 }; 29951d452cf5Sahrens 29961d452cf5Sahrens dd.snapname = snapname; 2997681d9761SEric Taylor (void) zfs_check_snap_cb(zhp, &dd); 29981d452cf5Sahrens 29991d452cf5Sahrens if (!dd.gotone) { 3000ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 30011d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 30021d452cf5Sahrens zhp->zfs_name, snapname)); 30031d452cf5Sahrens } 30041d452cf5Sahrens 30051d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3006e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3007842727c2SChris Kirby zc.zc_defer_destroy = defer; 30081d452cf5Sahrens 3009ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 30101d452cf5Sahrens if (ret != 0) { 30111d452cf5Sahrens char errbuf[1024]; 30121d452cf5Sahrens 30131d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30141d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 30151d452cf5Sahrens 30161d452cf5Sahrens switch (errno) { 30171d452cf5Sahrens case EEXIST: 30181d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30191d452cf5Sahrens "snapshot is cloned")); 30201d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 30211d452cf5Sahrens 30221d452cf5Sahrens default: 30231d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 30241d452cf5Sahrens errbuf)); 30251d452cf5Sahrens } 30261d452cf5Sahrens } 30271d452cf5Sahrens 30281d452cf5Sahrens return (0); 30291d452cf5Sahrens } 30301d452cf5Sahrens 3031fa9e4066Sahrens /* 3032fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 3033fa9e4066Sahrens */ 3034fa9e4066Sahrens int 3035e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3036fa9e4066Sahrens { 3037fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3038fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 3039fa9e4066Sahrens int ret; 304099653d4eSeschrock char errbuf[1024]; 304199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3042e9dbad6fSeschrock zfs_type_t type; 3043e9dbad6fSeschrock uint64_t zoned; 3044fa9e4066Sahrens 3045fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3046fa9e4066Sahrens 304799653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 304899653d4eSeschrock "cannot create '%s'"), target); 304999653d4eSeschrock 3050fa9e4066Sahrens /* validate the target name */ 3051f18faf3fSek if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 305299653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3053fa9e4066Sahrens 3054fa9e4066Sahrens /* validate parents exist */ 30557f1f55eaSvb if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3056fa9e4066Sahrens return (-1); 3057fa9e4066Sahrens 3058fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3059fa9e4066Sahrens 3060fa9e4066Sahrens /* do the clone */ 3061e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 3062fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30635f8e1617Snn type = ZFS_TYPE_VOLUME; 3064e9dbad6fSeschrock } else { 3065fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30665f8e1617Snn type = ZFS_TYPE_FILESYSTEM; 3067e9dbad6fSeschrock } 3068e9dbad6fSeschrock 3069e9dbad6fSeschrock if (props) { 30700a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30710a48a24eStimh zhp, errbuf)) == NULL) 3072e9dbad6fSeschrock return (-1); 3073e9dbad6fSeschrock 3074990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3075e9dbad6fSeschrock nvlist_free(props); 3076e9dbad6fSeschrock return (-1); 3077e9dbad6fSeschrock } 3078e9dbad6fSeschrock 3079e9dbad6fSeschrock nvlist_free(props); 3080e9dbad6fSeschrock } 3081fa9e4066Sahrens 3082fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 3083e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 3084ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3085fa9e4066Sahrens 3086e9dbad6fSeschrock zcmd_free_nvlists(&zc); 3087e9dbad6fSeschrock 3088fa9e4066Sahrens if (ret != 0) { 3089fa9e4066Sahrens switch (errno) { 3090fa9e4066Sahrens 3091fa9e4066Sahrens case ENOENT: 3092fa9e4066Sahrens /* 3093fa9e4066Sahrens * The parent doesn't exist. We should have caught this 3094fa9e4066Sahrens * above, but there may a race condition that has since 3095fa9e4066Sahrens * destroyed the parent. 3096fa9e4066Sahrens * 3097fa9e4066Sahrens * At this point, we don't know whether it's the source 3098fa9e4066Sahrens * that doesn't exist anymore, or whether the target 3099fa9e4066Sahrens * dataset doesn't exist. 3100fa9e4066Sahrens */ 310199653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 310299653d4eSeschrock "no such parent '%s'"), parent); 310399653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3104fa9e4066Sahrens 310599653d4eSeschrock case EXDEV: 310699653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 310799653d4eSeschrock "source and target pools differ")); 310899653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 310999653d4eSeschrock errbuf)); 3110fa9e4066Sahrens 311199653d4eSeschrock default: 311299653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 311399653d4eSeschrock errbuf)); 311499653d4eSeschrock } 311599653d4eSeschrock } 3116fa9e4066Sahrens 311799653d4eSeschrock return (ret); 311899653d4eSeschrock } 311999653d4eSeschrock 312099653d4eSeschrock /* 312199653d4eSeschrock * Promotes the given clone fs to be the clone parent. 312299653d4eSeschrock */ 312399653d4eSeschrock int 312499653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 312599653d4eSeschrock { 312699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 312799653d4eSeschrock zfs_cmd_t zc = { 0 }; 312899653d4eSeschrock char parent[MAXPATHLEN]; 312999653d4eSeschrock int ret; 313099653d4eSeschrock char errbuf[1024]; 313199653d4eSeschrock 313299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 313399653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 313499653d4eSeschrock 313599653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 313699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 313799653d4eSeschrock "snapshots can not be promoted")); 313899653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 313999653d4eSeschrock } 314099653d4eSeschrock 31413cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 314299653d4eSeschrock if (parent[0] == '\0') { 314399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 314499653d4eSeschrock "not a cloned filesystem")); 314599653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 314699653d4eSeschrock } 314799653d4eSeschrock 31483cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3149e9dbad6fSeschrock sizeof (zc.zc_value)); 315099653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3151ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 315299653d4eSeschrock 315399653d4eSeschrock if (ret != 0) { 31540b69c2f0Sahrens int save_errno = errno; 31550b69c2f0Sahrens 31560b69c2f0Sahrens switch (save_errno) { 315799653d4eSeschrock case EEXIST: 3158681d9761SEric Taylor /* There is a conflicting snapshot name. */ 315999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3160681d9761SEric Taylor "conflicting snapshot '%s' from parent '%s'"), 3161681d9761SEric Taylor zc.zc_string, parent); 316299653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3163fa9e4066Sahrens 3164fa9e4066Sahrens default: 31650b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3166fa9e4066Sahrens } 3167fa9e4066Sahrens } 3168e9dbad6fSeschrock return (ret); 31691d452cf5Sahrens } 31701d452cf5Sahrens 3171fa9e4066Sahrens /* 317272bdce51Sahl * Takes a snapshot of the given dataset. 3173fa9e4066Sahrens */ 3174fa9e4066Sahrens int 3175bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3176bb0ade09Sahrens nvlist_t *props) 3177fa9e4066Sahrens { 3178fa9e4066Sahrens const char *delim; 3179bb0ade09Sahrens char parent[ZFS_MAXNAMELEN]; 3180fa9e4066Sahrens zfs_handle_t *zhp; 3181fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3182fa9e4066Sahrens int ret; 318399653d4eSeschrock char errbuf[1024]; 3184fa9e4066Sahrens 318599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 318699653d4eSeschrock "cannot snapshot '%s'"), path); 318799653d4eSeschrock 318899653d4eSeschrock /* validate the target name */ 3189f18faf3fSek if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 319099653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3191fa9e4066Sahrens 3192bb0ade09Sahrens if (props) { 3193bb0ade09Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3194bb0ade09Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 3195bb0ade09Sahrens return (-1); 3196bb0ade09Sahrens 3197bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3198bb0ade09Sahrens nvlist_free(props); 3199bb0ade09Sahrens return (-1); 3200bb0ade09Sahrens } 3201bb0ade09Sahrens 3202bb0ade09Sahrens nvlist_free(props); 3203bb0ade09Sahrens } 3204bb0ade09Sahrens 3205fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 32061d452cf5Sahrens delim = strchr(path, '@'); 3207fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3208fa9e4066Sahrens parent[delim - path] = '\0'; 3209fa9e4066Sahrens 321099653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3211fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3212bb0ade09Sahrens zcmd_free_nvlists(&zc); 3213fa9e4066Sahrens return (-1); 3214fa9e4066Sahrens } 3215fa9e4066Sahrens 32161d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3217e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3218ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3219ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3220ecd6cf80Smarks else 3221ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 32221d452cf5Sahrens zc.zc_cookie = recursive; 3223ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3224fa9e4066Sahrens 3225bb0ade09Sahrens zcmd_free_nvlists(&zc); 3226bb0ade09Sahrens 32271d452cf5Sahrens /* 32281d452cf5Sahrens * if it was recursive, the one that actually failed will be in 32291d452cf5Sahrens * zc.zc_name. 32301d452cf5Sahrens */ 3231681d9761SEric Taylor if (ret != 0) { 3232ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3233ecd6cf80Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 323499653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3235681d9761SEric Taylor } 3236fa9e4066Sahrens 3237fa9e4066Sahrens zfs_close(zhp); 3238fa9e4066Sahrens 3239fa9e4066Sahrens return (ret); 3240fa9e4066Sahrens } 3241fa9e4066Sahrens 3242fa9e4066Sahrens /* 3243b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3244b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3245b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3246b12a1c38Slling * group. 3247fa9e4066Sahrens */ 3248b12a1c38Slling typedef struct rollback_data { 3249b12a1c38Slling const char *cb_target; /* the snapshot */ 3250b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3251c391e322Sahrens boolean_t cb_error; 325299653d4eSeschrock boolean_t cb_dependent; 3253c391e322Sahrens boolean_t cb_force; 3254b12a1c38Slling } rollback_data_t; 3255b12a1c38Slling 3256b12a1c38Slling static int 3257b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3258b12a1c38Slling { 3259b12a1c38Slling rollback_data_t *cbp = data; 3260b12a1c38Slling 3261b12a1c38Slling if (!cbp->cb_dependent) { 3262b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3263b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3264b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3265b12a1c38Slling cbp->cb_create) { 3266ecd6cf80Smarks char *logstr; 3267b12a1c38Slling 326899653d4eSeschrock cbp->cb_dependent = B_TRUE; 32694ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32704ccbb6e7Sahrens rollback_destroy, cbp); 327199653d4eSeschrock cbp->cb_dependent = B_FALSE; 3272b12a1c38Slling 3273ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3274ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 3275842727c2SChris Kirby cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3276ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3277b12a1c38Slling } 3278b12a1c38Slling } else { 3279c391e322Sahrens /* We must destroy this clone; first unmount it */ 3280c391e322Sahrens prop_changelist_t *clp; 3281c391e322Sahrens 32820069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3283c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3284c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3285c391e322Sahrens cbp->cb_error = B_TRUE; 3286c391e322Sahrens zfs_close(zhp); 3287c391e322Sahrens return (0); 3288c391e322Sahrens } 3289842727c2SChris Kirby if (zfs_destroy(zhp, B_FALSE) != 0) 3290c391e322Sahrens cbp->cb_error = B_TRUE; 3291c391e322Sahrens else 3292c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3293ba7b046eSahrens (void) changelist_postfix(clp); 3294c391e322Sahrens changelist_free(clp); 3295b12a1c38Slling } 3296b12a1c38Slling 3297b12a1c38Slling zfs_close(zhp); 3298b12a1c38Slling return (0); 3299b12a1c38Slling } 3300b12a1c38Slling 3301b12a1c38Slling /* 3302b12a1c38Slling * Given a dataset, rollback to a specific snapshot, discarding any 3303b12a1c38Slling * data changes since then and making it the active dataset. 3304b12a1c38Slling * 3305b12a1c38Slling * Any snapshots more recent than the target are destroyed, along with 3306b12a1c38Slling * their dependents. 3307b12a1c38Slling */ 3308b12a1c38Slling int 3309c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3310b12a1c38Slling { 3311b12a1c38Slling rollback_data_t cb = { 0 }; 33124ccbb6e7Sahrens int err; 33134ccbb6e7Sahrens zfs_cmd_t zc = { 0 }; 33147b97dc1aSrm boolean_t restore_resv = 0; 33157b97dc1aSrm uint64_t old_volsize, new_volsize; 33167b97dc1aSrm zfs_prop_t resv_prop; 3317b12a1c38Slling 33184ccbb6e7Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 33194ccbb6e7Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3320b12a1c38Slling 3321b12a1c38Slling /* 3322b12a1c38Slling * Destroy all recent snapshots and its dependends. 3323b12a1c38Slling */ 3324c391e322Sahrens cb.cb_force = force; 3325b12a1c38Slling cb.cb_target = snap->zfs_name; 3326b12a1c38Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3327b12a1c38Slling (void) zfs_iter_children(zhp, rollback_destroy, &cb); 3328b12a1c38Slling 3329c391e322Sahrens if (cb.cb_error) 3330c391e322Sahrens return (-1); 3331b12a1c38Slling 3332b12a1c38Slling /* 3333b12a1c38Slling * Now that we have verified that the snapshot is the latest, 3334b12a1c38Slling * rollback to the given snapshot. 3335b12a1c38Slling */ 3336b12a1c38Slling 33377b97dc1aSrm if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33387b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33397b97dc1aSrm return (-1); 33407b97dc1aSrm old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33417b97dc1aSrm restore_resv = 33427b97dc1aSrm (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33437b97dc1aSrm } 33444ccbb6e7Sahrens 33454ccbb6e7Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 33464ccbb6e7Sahrens 33474ccbb6e7Sahrens if (ZFS_IS_VOLUME(zhp)) 33484ccbb6e7Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 33494ccbb6e7Sahrens else 33504ccbb6e7Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3351b12a1c38Slling 3352b12a1c38Slling /* 33534ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 33544ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 33554ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 33564ccbb6e7Sahrens * an unlikely race condition where the user has taken a 33574ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 33587b97dc1aSrm * 3359b12a1c38Slling */ 33604ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33614ccbb6e7Sahrens (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33624ccbb6e7Sahrens dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33634ccbb6e7Sahrens zhp->zfs_name); 3364b9415e83Srm return (err); 3365b9415e83Srm } 33667b97dc1aSrm 33677b97dc1aSrm /* 33687b97dc1aSrm * For volumes, if the pre-rollback volsize matched the pre- 33697b97dc1aSrm * rollback reservation and the volsize has changed then set 33707b97dc1aSrm * the reservation property to the post-rollback volsize. 33717b97dc1aSrm * Make a new handle since the rollback closed the dataset. 33727b97dc1aSrm */ 3373b9415e83Srm if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3374b9415e83Srm (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 33757b97dc1aSrm if (restore_resv) { 33767b97dc1aSrm new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33777b97dc1aSrm if (old_volsize != new_volsize) 3378b9415e83Srm err = zfs_prop_set_int(zhp, resv_prop, 3379b9415e83Srm new_volsize); 33807b97dc1aSrm } 33817b97dc1aSrm zfs_close(zhp); 33824ccbb6e7Sahrens } 33834ccbb6e7Sahrens return (err); 3384b12a1c38Slling } 3385b12a1c38Slling 3386fa9e4066Sahrens /* 3387fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3388fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3389fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3390fa9e4066Sahrens * libzfs_graph.c. 3391fa9e4066Sahrens */ 3392fa9e4066Sahrens int 33933bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33943bb79becSeschrock zfs_iter_f func, void *data) 3395fa9e4066Sahrens { 3396fa9e4066Sahrens char **dependents; 3397fa9e4066Sahrens size_t count; 3398fa9e4066Sahrens int i; 3399fa9e4066Sahrens zfs_handle_t *child; 3400fa9e4066Sahrens int ret = 0; 3401fa9e4066Sahrens 34023bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34033bb79becSeschrock &dependents, &count) != 0) 34043bb79becSeschrock return (-1); 34053bb79becSeschrock 3406fa9e4066Sahrens for (i = 0; i < count; i++) { 340799653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 340899653d4eSeschrock dependents[i])) == NULL) 3409fa9e4066Sahrens continue; 3410fa9e4066Sahrens 3411fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3412fa9e4066Sahrens break; 3413fa9e4066Sahrens } 3414fa9e4066Sahrens 3415fa9e4066Sahrens for (i = 0; i < count; i++) 3416fa9e4066Sahrens free(dependents[i]); 3417fa9e4066Sahrens free(dependents); 3418fa9e4066Sahrens 3419fa9e4066Sahrens return (ret); 3420fa9e4066Sahrens } 3421fa9e4066Sahrens 3422fa9e4066Sahrens /* 3423fa9e4066Sahrens * Renames the given dataset. 3424fa9e4066Sahrens */ 3425fa9e4066Sahrens int 34267f1f55eaSvb zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3427fa9e4066Sahrens { 3428fa9e4066Sahrens int ret; 3429fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3430fa9e4066Sahrens char *delim; 3431cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3432cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3433cdf5b4caSmmusante char *parentname = NULL; 3434fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 343599653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 343699653d4eSeschrock char errbuf[1024]; 3437fa9e4066Sahrens 3438fa9e4066Sahrens /* if we have the same exact name, just return success */ 3439fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3440fa9e4066Sahrens return (0); 3441fa9e4066Sahrens 344299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 344399653d4eSeschrock "cannot rename to '%s'"), target); 344499653d4eSeschrock 3445fa9e4066Sahrens /* 3446fa9e4066Sahrens * Make sure the target name is valid 3447fa9e4066Sahrens */ 3448fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 344998579b20Snd if ((strchr(target, '@') == NULL) || 345098579b20Snd *target == '@') { 345198579b20Snd /* 345298579b20Snd * Snapshot target name is abbreviated, 345398579b20Snd * reconstruct full dataset name 345498579b20Snd */ 345598579b20Snd (void) strlcpy(parent, zhp->zfs_name, 345698579b20Snd sizeof (parent)); 345798579b20Snd delim = strchr(parent, '@'); 345898579b20Snd if (strchr(target, '@') == NULL) 345998579b20Snd *(++delim) = '\0'; 346098579b20Snd else 346198579b20Snd *delim = '\0'; 346298579b20Snd (void) strlcat(parent, target, sizeof (parent)); 346398579b20Snd target = parent; 346498579b20Snd } else { 346598579b20Snd /* 346698579b20Snd * Make sure we're renaming within the same dataset. 346798579b20Snd */ 346898579b20Snd delim = strchr(target, '@'); 346998579b20Snd if (strncmp(zhp->zfs_name, target, delim - target) 347098579b20Snd != 0 || zhp->zfs_name[delim - target] != '@') { 347198579b20Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 347298579b20Snd "snapshots must be part of same " 347398579b20Snd "dataset")); 347498579b20Snd return (zfs_error(hdl, EZFS_CROSSTARGET, 3475b1b8ab34Slling errbuf)); 347698579b20Snd } 3477fa9e4066Sahrens } 3478f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 347998579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3480fa9e4066Sahrens } else { 3481cdf5b4caSmmusante if (recursive) { 3482cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3483cdf5b4caSmmusante "recursive rename must be a snapshot")); 3484cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3485cdf5b4caSmmusante } 3486cdf5b4caSmmusante 3487f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 348898579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3489e9dbad6fSeschrock 3490fa9e4066Sahrens /* validate parents */ 3491d41c4376SMark J Musante if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3492fa9e4066Sahrens return (-1); 3493fa9e4066Sahrens 3494fa9e4066Sahrens /* make sure we're in the same pool */ 3495fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3496fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3497fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 349899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 349999653d4eSeschrock "datasets must be within same pool")); 350099653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3501fa9e4066Sahrens } 3502f2fdf992Snd 3503f2fdf992Snd /* new name cannot be a child of the current dataset name */ 3504d41c4376SMark J Musante if (is_descendant(zhp->zfs_name, target)) { 3505f2fdf992Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3506d41c4376SMark J Musante "New dataset name cannot be a descendant of " 3507f2fdf992Snd "current dataset name")); 3508f2fdf992Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3509f2fdf992Snd } 3510fa9e4066Sahrens } 3511fa9e4066Sahrens 351299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 351399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 351499653d4eSeschrock 3515fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3516fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 351799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 351899653d4eSeschrock "dataset is used in a non-global zone")); 351999653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3520fa9e4066Sahrens } 3521fa9e4066Sahrens 3522cdf5b4caSmmusante if (recursive) { 3523fa9e4066Sahrens 3524f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3525f0c5ee21Smmusante if (parentname == NULL) { 3526f0c5ee21Smmusante ret = -1; 3527f0c5ee21Smmusante goto error; 3528f0c5ee21Smmusante } 3529cdf5b4caSmmusante delim = strchr(parentname, '@'); 3530cdf5b4caSmmusante *delim = '\0'; 3531990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3532cdf5b4caSmmusante if (zhrp == NULL) { 3533f0c5ee21Smmusante ret = -1; 3534f0c5ee21Smmusante goto error; 3535cdf5b4caSmmusante } 3536fa9e4066Sahrens 3537cdf5b4caSmmusante } else { 35380069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 3539cdf5b4caSmmusante return (-1); 3540cdf5b4caSmmusante 3541cdf5b4caSmmusante if (changelist_haszonedchild(cl)) { 3542cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3543cdf5b4caSmmusante "child dataset with inherited mountpoint is used " 3544cdf5b4caSmmusante "in a non-global zone")); 3545cdf5b4caSmmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3546cdf5b4caSmmusante goto error; 3547cdf5b4caSmmusante } 3548cdf5b4caSmmusante 3549cdf5b4caSmmusante if ((ret = changelist_prefix(cl)) != 0) 3550cdf5b4caSmmusante goto error; 3551cdf5b4caSmmusante } 3552fa9e4066Sahrens 3553e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3554fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3555fa9e4066Sahrens else 3556fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3557fa9e4066Sahrens 355898579b20Snd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3559e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 356098579b20Snd 3561cdf5b4caSmmusante zc.zc_cookie = recursive; 3562cdf5b4caSmmusante 3563ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3564cdf5b4caSmmusante /* 3565cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3566cdf5b4caSmmusante * be in zc.zc_name 3567cdf5b4caSmmusante */ 3568cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35693cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3570cdf5b4caSmmusante 3571cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3572cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3573cdf5b4caSmmusante "a child dataset already has a snapshot " 3574cdf5b4caSmmusante "with the new name")); 3575a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3576cdf5b4caSmmusante } else { 3577cdf5b4caSmmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3578cdf5b4caSmmusante } 3579fa9e4066Sahrens 3580fa9e4066Sahrens /* 3581fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3582fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3583fa9e4066Sahrens */ 3584681d9761SEric Taylor if (!recursive) 3585cdf5b4caSmmusante (void) changelist_postfix(cl); 3586fa9e4066Sahrens } else { 3587681d9761SEric Taylor if (!recursive) { 3588cdf5b4caSmmusante changelist_rename(cl, zfs_get_name(zhp), target); 3589cdf5b4caSmmusante ret = changelist_postfix(cl); 3590cdf5b4caSmmusante } 3591fa9e4066Sahrens } 3592fa9e4066Sahrens 3593fa9e4066Sahrens error: 3594cdf5b4caSmmusante if (parentname) { 3595cdf5b4caSmmusante free(parentname); 3596cdf5b4caSmmusante } 3597cdf5b4caSmmusante if (zhrp) { 3598cdf5b4caSmmusante zfs_close(zhrp); 3599cdf5b4caSmmusante } 3600cdf5b4caSmmusante if (cl) { 3601cdf5b4caSmmusante changelist_free(cl); 3602cdf5b4caSmmusante } 3603fa9e4066Sahrens return (ret); 3604fa9e4066Sahrens } 3605fa9e4066Sahrens 3606e9dbad6fSeschrock nvlist_t * 3607e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3608e9dbad6fSeschrock { 3609e9dbad6fSeschrock return (zhp->zfs_user_props); 3610e9dbad6fSeschrock } 3611e9dbad6fSeschrock 361292241e0bSTom Erickson nvlist_t * 361392241e0bSTom Erickson zfs_get_recvd_props(zfs_handle_t *zhp) 361492241e0bSTom Erickson { 361592241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 361692241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 361792241e0bSTom Erickson return (NULL); 361892241e0bSTom Erickson return (zhp->zfs_recvd_props); 361992241e0bSTom Erickson } 362092241e0bSTom Erickson 3621b1b8ab34Slling /* 3622b1b8ab34Slling * This function is used by 'zfs list' to determine the exact set of columns to 3623b1b8ab34Slling * display, and their maximum widths. This does two main things: 3624b1b8ab34Slling * 3625b1b8ab34Slling * - If this is a list of all properties, then expand the list to include 3626b1b8ab34Slling * all native properties, and set a flag so that for each dataset we look 3627b1b8ab34Slling * for new unique user properties and add them to the list. 3628b1b8ab34Slling * 3629b1b8ab34Slling * - For non fixed-width properties, keep track of the maximum width seen 363092241e0bSTom Erickson * so that we can size the column appropriately. If the user has 363192241e0bSTom Erickson * requested received property values, we also need to compute the width 363292241e0bSTom Erickson * of the RECEIVED column. 3633b1b8ab34Slling */ 3634b1b8ab34Slling int 363592241e0bSTom Erickson zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 3636b1b8ab34Slling { 3637b1b8ab34Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 3638990b4856Slling zprop_list_t *entry; 3639990b4856Slling zprop_list_t **last, **start; 3640b1b8ab34Slling nvlist_t *userprops, *propval; 3641b1b8ab34Slling nvpair_t *elem; 3642b1b8ab34Slling char *strval; 3643b1b8ab34Slling char buf[ZFS_MAXPROPLEN]; 3644b1b8ab34Slling 3645990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3646b1b8ab34Slling return (-1); 3647e9dbad6fSeschrock 3648e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3649e9dbad6fSeschrock 3650e9dbad6fSeschrock entry = *plp; 3651e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3652e9dbad6fSeschrock /* 3653e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3654e9dbad6fSeschrock * start by incrementing our list pointer to the first 3655e9dbad6fSeschrock * non-native property. 3656e9dbad6fSeschrock */ 3657e9dbad6fSeschrock start = plp; 3658e9dbad6fSeschrock while (*start != NULL) { 3659990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3660e9dbad6fSeschrock break; 3661e9dbad6fSeschrock start = &(*start)->pl_next; 3662e9dbad6fSeschrock } 3663e9dbad6fSeschrock 3664e9dbad6fSeschrock elem = NULL; 3665e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3666e9dbad6fSeschrock /* 3667e9dbad6fSeschrock * See if we've already found this property in our list. 3668e9dbad6fSeschrock */ 3669e9dbad6fSeschrock for (last = start; *last != NULL; 3670e9dbad6fSeschrock last = &(*last)->pl_next) { 3671e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3672e9dbad6fSeschrock nvpair_name(elem)) == 0) 3673e9dbad6fSeschrock break; 3674e9dbad6fSeschrock } 3675e9dbad6fSeschrock 3676e9dbad6fSeschrock if (*last == NULL) { 3677e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3678990b4856Slling sizeof (zprop_list_t))) == NULL || 3679e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3680e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3681e9dbad6fSeschrock free(entry); 3682e9dbad6fSeschrock return (-1); 3683e9dbad6fSeschrock } 3684e9dbad6fSeschrock 3685990b4856Slling entry->pl_prop = ZPROP_INVAL; 3686e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3687e9dbad6fSeschrock entry->pl_all = B_TRUE; 3688e9dbad6fSeschrock *last = entry; 3689e9dbad6fSeschrock } 3690e9dbad6fSeschrock } 3691e9dbad6fSeschrock } 3692e9dbad6fSeschrock 3693e9dbad6fSeschrock /* 3694e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3695e9dbad6fSeschrock */ 3696e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3697e9dbad6fSeschrock if (entry->pl_fixed) 3698e9dbad6fSeschrock continue; 3699e9dbad6fSeschrock 3700990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3701e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3702e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3703e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3704e9dbad6fSeschrock entry->pl_width = strlen(buf); 3705e9dbad6fSeschrock } 370692241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 370792241e0bSTom Erickson zfs_prop_to_name(entry->pl_prop), 370892241e0bSTom Erickson buf, sizeof (buf), B_FALSE) == 0) 370992241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 371092241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 371192241e0bSTom Erickson } else { 371292241e0bSTom Erickson if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 371392241e0bSTom Erickson &propval) == 0) { 371492241e0bSTom Erickson verify(nvlist_lookup_string(propval, 371592241e0bSTom Erickson ZPROP_VALUE, &strval) == 0); 371692241e0bSTom Erickson if (strlen(strval) > entry->pl_width) 371792241e0bSTom Erickson entry->pl_width = strlen(strval); 371892241e0bSTom Erickson } 371992241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 372092241e0bSTom Erickson entry->pl_user_prop, 372192241e0bSTom Erickson buf, sizeof (buf), B_FALSE) == 0) 372292241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 372392241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 3724e9dbad6fSeschrock } 3725e9dbad6fSeschrock } 3726e9dbad6fSeschrock 3727e9dbad6fSeschrock return (0); 3728e9dbad6fSeschrock } 3729ecd6cf80Smarks 3730ecd6cf80Smarks int 3731ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3732743a77edSAlan Wright char *resource, void *export, void *sharetab, 3733743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3734ecd6cf80Smarks { 3735ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3736ecd6cf80Smarks int error; 3737ecd6cf80Smarks 3738ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3739ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3740743a77edSAlan Wright if (resource) 3741743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3742ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3743ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3744da6c28aaSamw zc.zc_share.z_sharetype = operation; 3745ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3746ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3747ecd6cf80Smarks return (error); 3748ecd6cf80Smarks } 37492e5e9e19SSanjeev Bagewadi 37502e5e9e19SSanjeev Bagewadi void 37512e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37522e5e9e19SSanjeev Bagewadi { 37532e5e9e19SSanjeev Bagewadi nvpair_t *curr; 37542e5e9e19SSanjeev Bagewadi 37552e5e9e19SSanjeev Bagewadi /* 37562e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 37572e5e9e19SSanjeev Bagewadi * properties. 37582e5e9e19SSanjeev Bagewadi */ 37592e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 37602e5e9e19SSanjeev Bagewadi 37612e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37622e5e9e19SSanjeev Bagewadi 37632e5e9e19SSanjeev Bagewadi while (curr) { 37642e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37652e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37662e5e9e19SSanjeev Bagewadi 376714843421SMatthew Ahrens /* 3768faaa6415SEric Schrock * User properties will result in ZPROP_INVAL, and since we 3769faaa6415SEric Schrock * only know how to prune standard ZFS properties, we always 3770faaa6415SEric Schrock * leave these in the list. This can also happen if we 3771faaa6415SEric Schrock * encounter an unknown DSL property (when running older 3772faaa6415SEric Schrock * software, for example). 377314843421SMatthew Ahrens */ 377414843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 37752e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 37762e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 37772e5e9e19SSanjeev Bagewadi curr = next; 37782e5e9e19SSanjeev Bagewadi } 37792e5e9e19SSanjeev Bagewadi } 3780743a77edSAlan Wright 3781743a77edSAlan Wright static int 3782743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3783743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3784743a77edSAlan Wright { 3785743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3786743a77edSAlan Wright nvlist_t *nvlist = NULL; 3787743a77edSAlan Wright int error; 3788743a77edSAlan Wright 3789743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3790743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3791743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3792743a77edSAlan Wright 3793743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3794743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3795743a77edSAlan Wright (void) no_memory(hdl); 3796743a77edSAlan Wright return (NULL); 3797743a77edSAlan Wright } 3798743a77edSAlan Wright } 3799743a77edSAlan Wright 3800743a77edSAlan Wright switch (cmd) { 3801743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3802743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3803743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3804743a77edSAlan Wright break; 3805743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3806743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3807743a77edSAlan Wright resource1) != 0) { 3808743a77edSAlan Wright (void) no_memory(hdl); 3809743a77edSAlan Wright return (-1); 3810743a77edSAlan Wright } 3811743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 3812743a77edSAlan Wright resource2) != 0) { 3813743a77edSAlan Wright (void) no_memory(hdl); 3814743a77edSAlan Wright return (-1); 3815743a77edSAlan Wright } 3816743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 3817743a77edSAlan Wright nvlist_free(nvlist); 3818743a77edSAlan Wright return (-1); 3819743a77edSAlan Wright } 3820743a77edSAlan Wright break; 3821743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3822743a77edSAlan Wright break; 3823743a77edSAlan Wright default: 3824743a77edSAlan Wright return (-1); 3825743a77edSAlan Wright } 3826743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 3827743a77edSAlan Wright if (nvlist) 3828743a77edSAlan Wright nvlist_free(nvlist); 3829743a77edSAlan Wright return (error); 3830743a77edSAlan Wright } 3831743a77edSAlan Wright 3832743a77edSAlan Wright int 3833743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 3834743a77edSAlan Wright char *path, char *resource) 3835743a77edSAlan Wright { 3836743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 3837743a77edSAlan Wright resource, NULL)); 3838743a77edSAlan Wright } 3839743a77edSAlan Wright 3840743a77edSAlan Wright int 3841743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 3842743a77edSAlan Wright char *path, char *resource) 3843743a77edSAlan Wright { 3844743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 3845743a77edSAlan Wright resource, NULL)); 3846743a77edSAlan Wright } 3847743a77edSAlan Wright 3848743a77edSAlan Wright int 3849743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 3850743a77edSAlan Wright { 3851743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 3852743a77edSAlan Wright NULL, NULL)); 3853743a77edSAlan Wright } 3854743a77edSAlan Wright 3855743a77edSAlan Wright int 3856743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 3857743a77edSAlan Wright char *oldname, char *newname) 3858743a77edSAlan Wright { 3859743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 3860743a77edSAlan Wright oldname, newname)); 3861743a77edSAlan Wright } 386214843421SMatthew Ahrens 386314843421SMatthew Ahrens int 386414843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 386514843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 386614843421SMatthew Ahrens { 386714843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 386814843421SMatthew Ahrens int error; 386914843421SMatthew Ahrens zfs_useracct_t buf[100]; 387014843421SMatthew Ahrens 387114843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 387214843421SMatthew Ahrens 387314843421SMatthew Ahrens zc.zc_objset_type = type; 387414843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 387514843421SMatthew Ahrens 387614843421SMatthew Ahrens /* CONSTCOND */ 387714843421SMatthew Ahrens while (1) { 387814843421SMatthew Ahrens zfs_useracct_t *zua = buf; 387914843421SMatthew Ahrens 388014843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 388114843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 388214843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 388314843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 388414843421SMatthew Ahrens break; 388514843421SMatthew Ahrens 388614843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 38870aea4b19SMatthew Ahrens error = func(arg, zua->zu_domain, zua->zu_rid, 38880aea4b19SMatthew Ahrens zua->zu_space); 38890aea4b19SMatthew Ahrens if (error != 0) 38900aea4b19SMatthew Ahrens return (error); 389114843421SMatthew Ahrens zua++; 389214843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 389314843421SMatthew Ahrens } 389414843421SMatthew Ahrens } 389514843421SMatthew Ahrens 389614843421SMatthew Ahrens return (error); 389714843421SMatthew Ahrens } 3898842727c2SChris Kirby 3899842727c2SChris Kirby int 3900842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 3901818119b8SChris Kirby boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 3902842727c2SChris Kirby { 3903842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 3904842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 3905842727c2SChris Kirby 3906842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3907842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3908ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 3909ca45db41SChris Kirby >= sizeof (zc.zc_string)) 3910ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 3911842727c2SChris Kirby zc.zc_cookie = recursive; 3912ca45db41SChris Kirby zc.zc_temphold = temphold; 3913842727c2SChris Kirby 3914842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 3915842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 3916842727c2SChris Kirby 3917842727c2SChris Kirby /* 3918842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 3919842727c2SChris Kirby * zc.zc_name. 3920842727c2SChris Kirby */ 3921842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3922842727c2SChris Kirby "cannot hold '%s@%s'"), zc.zc_name, snapname); 3923842727c2SChris Kirby switch (errno) { 392415508ac0SChris Kirby case E2BIG: 392515508ac0SChris Kirby /* 392615508ac0SChris Kirby * Temporary tags wind up having the ds object id 392715508ac0SChris Kirby * prepended. So even if we passed the length check 392815508ac0SChris Kirby * above, it's still possible for the tag to wind 392915508ac0SChris Kirby * up being slightly too long. 393015508ac0SChris Kirby */ 393115508ac0SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 3932842727c2SChris Kirby case ENOTSUP: 3933842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3934842727c2SChris Kirby "pool must be upgraded")); 3935842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3936842727c2SChris Kirby case EINVAL: 3937842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3938842727c2SChris Kirby case EEXIST: 3939842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 3940818119b8SChris Kirby case ENOENT: 3941818119b8SChris Kirby if (enoent_ok) 3942818119b8SChris Kirby return (0); 3943818119b8SChris Kirby /* FALLTHROUGH */ 3944842727c2SChris Kirby default: 3945842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 3946842727c2SChris Kirby } 3947842727c2SChris Kirby } 3948842727c2SChris Kirby 3949842727c2SChris Kirby return (0); 3950842727c2SChris Kirby } 3951842727c2SChris Kirby 3952ca45db41SChris Kirby struct hold_range_arg { 3953ca45db41SChris Kirby zfs_handle_t *origin; 3954ca45db41SChris Kirby const char *fromsnap; 3955ca45db41SChris Kirby const char *tosnap; 3956ca45db41SChris Kirby char lastsnapheld[ZFS_MAXNAMELEN]; 3957ca45db41SChris Kirby const char *tag; 3958ca45db41SChris Kirby boolean_t temphold; 3959ca45db41SChris Kirby boolean_t seento; 3960ca45db41SChris Kirby boolean_t seenfrom; 3961ca45db41SChris Kirby boolean_t holding; 3962620252bcSChris Kirby boolean_t recursive; 396354a91118SChris Kirby snapfilter_cb_t *filter_cb; 396454a91118SChris Kirby void *filter_cb_arg; 3965ca45db41SChris Kirby }; 3966ca45db41SChris Kirby 3967ca45db41SChris Kirby static int 3968ca45db41SChris Kirby zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 3969ca45db41SChris Kirby { 3970ca45db41SChris Kirby struct hold_range_arg *hra = arg; 3971ca45db41SChris Kirby const char *thissnap; 3972ca45db41SChris Kirby int error; 3973ca45db41SChris Kirby 3974ca45db41SChris Kirby thissnap = strchr(zfs_get_name(zhp), '@') + 1; 3975ca45db41SChris Kirby 3976ca45db41SChris Kirby if (hra->fromsnap && !hra->seenfrom && 3977ca45db41SChris Kirby strcmp(hra->fromsnap, thissnap) == 0) 3978ca45db41SChris Kirby hra->seenfrom = B_TRUE; 3979ca45db41SChris Kirby 3980ca45db41SChris Kirby /* snap is older or newer than the desired range, ignore it */ 3981ca45db41SChris Kirby if (hra->seento || !hra->seenfrom) { 3982ca45db41SChris Kirby zfs_close(zhp); 3983ca45db41SChris Kirby return (0); 3984ca45db41SChris Kirby } 3985ca45db41SChris Kirby 398654a91118SChris Kirby if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 398754a91118SChris Kirby hra->seento = B_TRUE; 398854a91118SChris Kirby 398954a91118SChris Kirby if (hra->filter_cb != NULL && 399054a91118SChris Kirby hra->filter_cb(zhp, hra->filter_cb_arg) == B_FALSE) { 399154a91118SChris Kirby zfs_close(zhp); 399254a91118SChris Kirby return (0); 399354a91118SChris Kirby } 399454a91118SChris Kirby 3995ca45db41SChris Kirby if (hra->holding) { 3996818119b8SChris Kirby /* We could be racing with destroy, so ignore ENOENT. */ 3997620252bcSChris Kirby error = zfs_hold(hra->origin, thissnap, hra->tag, 3998620252bcSChris Kirby hra->recursive, hra->temphold, B_TRUE); 3999ca45db41SChris Kirby if (error == 0) { 4000ca45db41SChris Kirby (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 4001ca45db41SChris Kirby sizeof (hra->lastsnapheld)); 4002ca45db41SChris Kirby } 4003ca45db41SChris Kirby } else { 4004620252bcSChris Kirby error = zfs_release(hra->origin, thissnap, hra->tag, 4005620252bcSChris Kirby hra->recursive); 4006ca45db41SChris Kirby } 4007ca45db41SChris Kirby 4008ca45db41SChris Kirby zfs_close(zhp); 4009ca45db41SChris Kirby return (error); 4010ca45db41SChris Kirby } 4011ca45db41SChris Kirby 4012ca45db41SChris Kirby /* 4013ca45db41SChris Kirby * Add a user hold on the set of snapshots starting with fromsnap up to 4014ca45db41SChris Kirby * and including tosnap. If we're unable to to acquire a particular hold, 4015ca45db41SChris Kirby * undo any holds up to that point. 4016ca45db41SChris Kirby */ 4017ca45db41SChris Kirby int 4018ca45db41SChris Kirby zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 401954a91118SChris Kirby const char *tag, boolean_t recursive, boolean_t temphold, 402054a91118SChris Kirby snapfilter_cb_t filter_cb, void *cbarg) 4021ca45db41SChris Kirby { 4022ca45db41SChris Kirby struct hold_range_arg arg = { 0 }; 4023ca45db41SChris Kirby int error; 4024ca45db41SChris Kirby 4025ca45db41SChris Kirby arg.origin = zhp; 4026ca45db41SChris Kirby arg.fromsnap = fromsnap; 4027ca45db41SChris Kirby arg.tosnap = tosnap; 4028ca45db41SChris Kirby arg.tag = tag; 4029ca45db41SChris Kirby arg.temphold = temphold; 4030ca45db41SChris Kirby arg.holding = B_TRUE; 4031620252bcSChris Kirby arg.recursive = recursive; 4032c6fd73aeSChris Kirby arg.seenfrom = (fromsnap == NULL); 403354a91118SChris Kirby arg.filter_cb = filter_cb; 403454a91118SChris Kirby arg.filter_cb_arg = cbarg; 4035ca45db41SChris Kirby 4036ca45db41SChris Kirby error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 4037ca45db41SChris Kirby 4038ca45db41SChris Kirby /* 4039ca45db41SChris Kirby * Make sure we either hold the entire range or none. 4040ca45db41SChris Kirby */ 4041ca45db41SChris Kirby if (error && arg.lastsnapheld[0] != '\0') { 4042ca45db41SChris Kirby (void) zfs_release_range(zhp, fromsnap, 4043620252bcSChris Kirby (const char *)arg.lastsnapheld, tag, recursive); 4044ca45db41SChris Kirby } 4045ca45db41SChris Kirby return (error); 4046ca45db41SChris Kirby } 4047ca45db41SChris Kirby 4048842727c2SChris Kirby int 4049842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4050842727c2SChris Kirby boolean_t recursive) 4051842727c2SChris Kirby { 4052842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 4053842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 4054842727c2SChris Kirby 4055842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4056842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4057ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 4058ca45db41SChris Kirby >= sizeof (zc.zc_string)) 4059ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 4060842727c2SChris Kirby zc.zc_cookie = recursive; 4061842727c2SChris Kirby 4062842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 4063842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 4064842727c2SChris Kirby 4065842727c2SChris Kirby /* 4066842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 4067842727c2SChris Kirby * zc.zc_name. 4068842727c2SChris Kirby */ 4069842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4070620252bcSChris Kirby "cannot release '%s' from '%s@%s'"), tag, zc.zc_name, 4071620252bcSChris Kirby snapname); 4072842727c2SChris Kirby switch (errno) { 4073842727c2SChris Kirby case ESRCH: 4074842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 4075842727c2SChris Kirby case ENOTSUP: 4076842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4077842727c2SChris Kirby "pool must be upgraded")); 4078842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4079842727c2SChris Kirby case EINVAL: 4080842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4081842727c2SChris Kirby default: 4082842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4083842727c2SChris Kirby } 4084842727c2SChris Kirby } 4085842727c2SChris Kirby 4086842727c2SChris Kirby return (0); 4087842727c2SChris Kirby } 4088ca45db41SChris Kirby 4089ca45db41SChris Kirby /* 4090ca45db41SChris Kirby * Release a user hold from the set of snapshots starting with fromsnap 4091ca45db41SChris Kirby * up to and including tosnap. 4092ca45db41SChris Kirby */ 4093ca45db41SChris Kirby int 4094ca45db41SChris Kirby zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 4095620252bcSChris Kirby const char *tag, boolean_t recursive) 4096ca45db41SChris Kirby { 4097ca45db41SChris Kirby struct hold_range_arg arg = { 0 }; 4098ca45db41SChris Kirby 4099ca45db41SChris Kirby arg.origin = zhp; 4100ca45db41SChris Kirby arg.fromsnap = fromsnap; 4101ca45db41SChris Kirby arg.tosnap = tosnap; 4102ca45db41SChris Kirby arg.tag = tag; 4103620252bcSChris Kirby arg.recursive = recursive; 4104c6fd73aeSChris Kirby arg.seenfrom = (fromsnap == NULL); 4105ca45db41SChris Kirby 4106ca45db41SChris Kirby return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 4107ca45db41SChris Kirby } 4108c1449561SEric Taylor 4109c1449561SEric Taylor uint64_t 4110c1449561SEric Taylor zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4111c1449561SEric Taylor { 4112c1449561SEric Taylor uint64_t numdb; 4113c1449561SEric Taylor uint64_t nblocks, volblocksize; 4114c1449561SEric Taylor int ncopies; 4115c1449561SEric Taylor char *strval; 4116c1449561SEric Taylor 4117c1449561SEric Taylor if (nvlist_lookup_string(props, 4118c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4119c1449561SEric Taylor ncopies = atoi(strval); 4120c1449561SEric Taylor else 4121c1449561SEric Taylor ncopies = 1; 4122c1449561SEric Taylor if (nvlist_lookup_uint64(props, 4123c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4124c1449561SEric Taylor &volblocksize) != 0) 4125c1449561SEric Taylor volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4126c1449561SEric Taylor nblocks = volsize/volblocksize; 4127c1449561SEric Taylor /* start with metadnode L0-L6 */ 4128c1449561SEric Taylor numdb = 7; 4129c1449561SEric Taylor /* calculate number of indirects */ 4130c1449561SEric Taylor while (nblocks > 1) { 4131c1449561SEric Taylor nblocks += DNODES_PER_LEVEL - 1; 4132c1449561SEric Taylor nblocks /= DNODES_PER_LEVEL; 4133c1449561SEric Taylor numdb += nblocks; 4134c1449561SEric Taylor } 4135c1449561SEric Taylor numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4136c1449561SEric Taylor volsize *= ncopies; 4137c1449561SEric Taylor /* 4138c1449561SEric Taylor * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4139c1449561SEric Taylor * compressed, but in practice they compress down to about 4140c1449561SEric Taylor * 1100 bytes 4141c1449561SEric Taylor */ 4142c1449561SEric Taylor numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4143c1449561SEric Taylor volsize += numdb; 4144c1449561SEric Taylor return (volsize); 4145c1449561SEric Taylor } 4146