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 130f18faf3fSek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 131f18faf3fSek110237 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; 1695ad82045Snd150628 1705ad82045Snd150628 case NAME_ERR_NOLETTER: 1715ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1725ad82045Snd150628 "pool doesn't begin with a letter")); 1735ad82045Snd150628 break; 1745ad82045Snd150628 1755ad82045Snd150628 case NAME_ERR_RESERVED: 1765ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1775ad82045Snd150628 "name is reserved")); 1785ad82045Snd150628 break; 1795ad82045Snd150628 1805ad82045Snd150628 case NAME_ERR_DISKLIKE: 1815ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1825ad82045Snd150628 "reserved disk name")); 1835ad82045Snd150628 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 204f18faf3fSek110237 if (modifying && strchr(path, '%') != NULL) { 205f18faf3fSek110237 if (hdl != NULL) 206f18faf3fSek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 207f18faf3fSek110237 "invalid character %c in name"), '%'); 208f18faf3fSek110237 return (0); 209f18faf3fSek110237 } 210f18faf3fSek110237 21199653d4eSeschrock return (-1); 212fa9e4066Sahrens } 213fa9e4066Sahrens 214fa9e4066Sahrens int 215fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 216fa9e4066Sahrens { 217e7cbe64fSgw25295 if (type == ZFS_TYPE_POOL) 218e7cbe64fSgw25295 return (zpool_name_valid(NULL, B_FALSE, name)); 219f18faf3fSek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 220fa9e4066Sahrens } 221fa9e4066Sahrens 222fa9e4066Sahrens /* 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 25529ab75c9Srm160521 static zpool_handle_t * 25629ab75c9Srm160521 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 25729ab75c9Srm160521 { 25829ab75c9Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 25929ab75c9Srm160521 zpool_handle_t *zph; 26029ab75c9Srm160521 26129ab75c9Srm160521 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 26229ab75c9Srm160521 if (hdl->libzfs_pool_handles != NULL) 26329ab75c9Srm160521 zph->zpool_next = hdl->libzfs_pool_handles; 26429ab75c9Srm160521 hdl->libzfs_pool_handles = zph; 26529ab75c9Srm160521 } 26629ab75c9Srm160521 return (zph); 26729ab75c9Srm160521 } 26829ab75c9Srm160521 26929ab75c9Srm160521 static zpool_handle_t * 27029ab75c9Srm160521 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 27129ab75c9Srm160521 { 27229ab75c9Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 27329ab75c9Srm160521 zpool_handle_t *zph = hdl->libzfs_pool_handles; 27429ab75c9Srm160521 27529ab75c9Srm160521 while ((zph != NULL) && 27629ab75c9Srm160521 (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 27729ab75c9Srm160521 zph = zph->zpool_next; 27829ab75c9Srm160521 return (zph); 27929ab75c9Srm160521 } 28029ab75c9Srm160521 28129ab75c9Srm160521 /* 28229ab75c9Srm160521 * Returns a handle to the pool that contains the provided dataset. 28329ab75c9Srm160521 * If a handle to that pool already exists then that handle is returned. 28429ab75c9Srm160521 * Otherwise, a new handle is created and added to the list of handles. 28529ab75c9Srm160521 */ 28629ab75c9Srm160521 static zpool_handle_t * 28729ab75c9Srm160521 zpool_handle(zfs_handle_t *zhp) 28829ab75c9Srm160521 { 28929ab75c9Srm160521 char *pool_name; 29029ab75c9Srm160521 int len; 29129ab75c9Srm160521 zpool_handle_t *zph; 29229ab75c9Srm160521 29329ab75c9Srm160521 len = strcspn(zhp->zfs_name, "/@") + 1; 29429ab75c9Srm160521 pool_name = zfs_alloc(zhp->zfs_hdl, len); 29529ab75c9Srm160521 (void) strlcpy(pool_name, zhp->zfs_name, len); 29629ab75c9Srm160521 29729ab75c9Srm160521 zph = zpool_find_handle(zhp, pool_name, len); 29829ab75c9Srm160521 if (zph == NULL) 29929ab75c9Srm160521 zph = zpool_add_handle(zhp, pool_name); 30029ab75c9Srm160521 30129ab75c9Srm160521 free(pool_name); 30229ab75c9Srm160521 return (zph); 30329ab75c9Srm160521 } 30429ab75c9Srm160521 30529ab75c9Srm160521 void 30629ab75c9Srm160521 zpool_free_handles(libzfs_handle_t *hdl) 30729ab75c9Srm160521 { 30829ab75c9Srm160521 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 30929ab75c9Srm160521 31029ab75c9Srm160521 while (zph != NULL) { 31129ab75c9Srm160521 next = zph->zpool_next; 31229ab75c9Srm160521 zpool_close(zph); 31329ab75c9Srm160521 zph = next; 31429ab75c9Srm160521 } 31529ab75c9Srm160521 hdl->libzfs_pool_handles = NULL; 31629ab75c9Srm160521 } 31729ab75c9Srm160521 318e9dbad6fSeschrock /* 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 { 334fa9e4066Sahrens return (-1); 3357f7322feSeschrock } 3367f7322feSeschrock } 337ebedde84SEric Taylor return (0); 338fac3008cSeschrock } 339fac3008cSeschrock 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; 382ebedde84SEric Taylor 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) { 386ebedde84SEric Taylor return (-1); 387ebedde84SEric Taylor } 388fac3008cSeschrock 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); 395fac3008cSeschrock return (-1); 396fac3008cSeschrock } 397fac3008cSeschrock 39899653d4eSeschrock nvlist_free(zhp->zfs_props); 399fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 40099653d4eSeschrock 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 46229ab75c9Srm160521 zhp->zpool_hdl = zpool_handle(zhp); 463ebedde84SEric Taylor return (0); 464ebedde84SEric Taylor } 465ebedde84SEric Taylor 466ebedde84SEric Taylor zfs_handle_t * 467ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 468ebedde84SEric Taylor { 469ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 470ebedde84SEric Taylor 471ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 472ebedde84SEric Taylor 473ebedde84SEric Taylor if (zhp == NULL) 474ebedde84SEric Taylor return (NULL); 475ebedde84SEric Taylor 476ebedde84SEric Taylor zhp->zfs_hdl = hdl; 477ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 478ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 479ebedde84SEric Taylor free(zhp); 480ebedde84SEric Taylor return (NULL); 481ebedde84SEric Taylor } 482ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 483ebedde84SEric Taylor zcmd_free_nvlists(&zc); 484ebedde84SEric Taylor free(zhp); 485ebedde84SEric Taylor return (NULL); 486ebedde84SEric Taylor } 487ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 488ebedde84SEric Taylor free(zhp); 489ebedde84SEric Taylor zhp = NULL; 490ebedde84SEric Taylor } 491ebedde84SEric Taylor zcmd_free_nvlists(&zc); 492ebedde84SEric Taylor return (zhp); 493ebedde84SEric Taylor } 494ebedde84SEric Taylor 495ebedde84SEric Taylor static zfs_handle_t * 496ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 497ebedde84SEric Taylor { 498ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 499ebedde84SEric Taylor 500ebedde84SEric Taylor if (zhp == NULL) 501ebedde84SEric Taylor return (NULL); 502ebedde84SEric Taylor 503ebedde84SEric Taylor zhp->zfs_hdl = hdl; 504ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 505ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 506ebedde84SEric Taylor free(zhp); 507ebedde84SEric Taylor return (NULL); 508ebedde84SEric Taylor } 509fa9e4066Sahrens return (zhp); 510fa9e4066Sahrens } 511fa9e4066Sahrens 512fa9e4066Sahrens /* 513fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 514fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 515fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 516fa9e4066Sahrens */ 517fa9e4066Sahrens zfs_handle_t * 51899653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 519fa9e4066Sahrens { 520fa9e4066Sahrens zfs_handle_t *zhp; 52199653d4eSeschrock char errbuf[1024]; 52299653d4eSeschrock 52399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 52499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 525fa9e4066Sahrens 526fa9e4066Sahrens /* 52799653d4eSeschrock * Validate the name before we even try to open it. 528fa9e4066Sahrens */ 529f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 53099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 53199653d4eSeschrock "invalid dataset name")); 53299653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 533fa9e4066Sahrens return (NULL); 534fa9e4066Sahrens } 535fa9e4066Sahrens 536fa9e4066Sahrens /* 537fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 538fa9e4066Sahrens */ 539fa9e4066Sahrens errno = 0; 54099653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 541ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 542fa9e4066Sahrens return (NULL); 543fa9e4066Sahrens } 544fa9e4066Sahrens 545fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 54699653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 54794de1d4cSeschrock zfs_close(zhp); 548fa9e4066Sahrens return (NULL); 549fa9e4066Sahrens } 550fa9e4066Sahrens 551fa9e4066Sahrens return (zhp); 552fa9e4066Sahrens } 553fa9e4066Sahrens 554fa9e4066Sahrens /* 555fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 556fa9e4066Sahrens */ 557fa9e4066Sahrens void 558fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 559fa9e4066Sahrens { 560fa9e4066Sahrens if (zhp->zfs_mntopts) 561fa9e4066Sahrens free(zhp->zfs_mntopts); 56299653d4eSeschrock nvlist_free(zhp->zfs_props); 563e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 56492241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 565fa9e4066Sahrens free(zhp); 566fa9e4066Sahrens } 567fa9e4066Sahrens 568ebedde84SEric Taylor typedef struct mnttab_node { 569ebedde84SEric Taylor struct mnttab mtn_mt; 570ebedde84SEric Taylor avl_node_t mtn_node; 571ebedde84SEric Taylor } mnttab_node_t; 572ebedde84SEric Taylor 573ebedde84SEric Taylor static int 574ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 575ebedde84SEric Taylor { 576ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 577ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 578ebedde84SEric Taylor int rv; 579ebedde84SEric Taylor 580ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 581ebedde84SEric Taylor 582ebedde84SEric Taylor if (rv == 0) 583ebedde84SEric Taylor return (0); 584ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 585ebedde84SEric Taylor } 586ebedde84SEric Taylor 587ebedde84SEric Taylor void 588ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 589ebedde84SEric Taylor { 590ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 591ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 592ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 593b2634b9cSEric Taylor } 594b2634b9cSEric Taylor 595b2634b9cSEric Taylor void 596b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 597b2634b9cSEric Taylor { 598b2634b9cSEric Taylor struct mnttab entry; 599ebedde84SEric Taylor 600ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 601ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 602ebedde84SEric Taylor mnttab_node_t *mtn; 603ebedde84SEric Taylor 604ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 605ebedde84SEric Taylor continue; 606ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 607ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 608ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 609ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 610ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 611ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 612ebedde84SEric Taylor } 613ebedde84SEric Taylor } 614ebedde84SEric Taylor 615ebedde84SEric Taylor void 616ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 617ebedde84SEric Taylor { 618ebedde84SEric Taylor void *cookie = NULL; 619ebedde84SEric Taylor mnttab_node_t *mtn; 620ebedde84SEric Taylor 621ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 622ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 623ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 624ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 625ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 626ebedde84SEric Taylor free(mtn); 627ebedde84SEric Taylor } 628ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 629ebedde84SEric Taylor } 630ebedde84SEric Taylor 631b2634b9cSEric Taylor void 632b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 633b2634b9cSEric Taylor { 634b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 635b2634b9cSEric Taylor } 636b2634b9cSEric Taylor 637ebedde84SEric Taylor int 638ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 639ebedde84SEric Taylor struct mnttab *entry) 640ebedde84SEric Taylor { 641ebedde84SEric Taylor mnttab_node_t find; 642ebedde84SEric Taylor mnttab_node_t *mtn; 643ebedde84SEric Taylor 644b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 645b2634b9cSEric Taylor struct mnttab srch = { 0 }; 646b2634b9cSEric Taylor 647b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 648b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 649b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 650b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 651b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 652b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 653b2634b9cSEric Taylor return (0); 654b2634b9cSEric Taylor else 655b2634b9cSEric Taylor return (ENOENT); 656b2634b9cSEric Taylor } 657b2634b9cSEric Taylor 658ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 659b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 660ebedde84SEric Taylor 661ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 662ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 663ebedde84SEric Taylor if (mtn) { 664ebedde84SEric Taylor *entry = mtn->mtn_mt; 665ebedde84SEric Taylor return (0); 666ebedde84SEric Taylor } 667ebedde84SEric Taylor return (ENOENT); 668ebedde84SEric Taylor } 669ebedde84SEric Taylor 670ebedde84SEric Taylor void 671ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 672ebedde84SEric Taylor const char *mountp, const char *mntopts) 673ebedde84SEric Taylor { 674ebedde84SEric Taylor mnttab_node_t *mtn; 675ebedde84SEric Taylor 676ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 677ebedde84SEric Taylor return; 678ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 679ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 680ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 681ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 682ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 683ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 684ebedde84SEric Taylor } 685ebedde84SEric Taylor 686ebedde84SEric Taylor void 687ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 688ebedde84SEric Taylor { 689ebedde84SEric Taylor mnttab_node_t find; 690ebedde84SEric Taylor mnttab_node_t *ret; 691ebedde84SEric Taylor 692ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 693ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 694ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 695ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 696ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 697ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 698ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 699ebedde84SEric Taylor free(ret); 700ebedde84SEric Taylor } 701ebedde84SEric Taylor } 702ebedde84SEric Taylor 7037b97dc1aSrm160521 int 7047b97dc1aSrm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7057b97dc1aSrm160521 { 70629ab75c9Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7077b97dc1aSrm160521 7087b97dc1aSrm160521 if (zpool_handle == NULL) 7097b97dc1aSrm160521 return (-1); 7107b97dc1aSrm160521 7117b97dc1aSrm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7127b97dc1aSrm160521 ZPOOL_PROP_VERSION, NULL); 7137b97dc1aSrm160521 return (0); 7147b97dc1aSrm160521 } 7157b97dc1aSrm160521 7167b97dc1aSrm160521 /* 7177b97dc1aSrm160521 * The choice of reservation property depends on the SPA version. 7187b97dc1aSrm160521 */ 7197b97dc1aSrm160521 static int 7207b97dc1aSrm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7217b97dc1aSrm160521 { 7227b97dc1aSrm160521 int spa_version; 7237b97dc1aSrm160521 7247b97dc1aSrm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7257b97dc1aSrm160521 return (-1); 7267b97dc1aSrm160521 7277b97dc1aSrm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7287b97dc1aSrm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7297b97dc1aSrm160521 else 7307b97dc1aSrm160521 *resv_prop = ZFS_PROP_RESERVATION; 7317b97dc1aSrm160521 7327b97dc1aSrm160521 return (0); 7337b97dc1aSrm160521 } 7347b97dc1aSrm160521 735b1b8ab34Slling /* 736e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 737e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 738e9dbad6fSeschrock * strings. 739fa9e4066Sahrens */ 7400a48a24eStimh nvlist_t * 7410a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 742990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 743fa9e4066Sahrens { 744e9dbad6fSeschrock nvpair_t *elem; 745e9dbad6fSeschrock uint64_t intval; 746e9dbad6fSeschrock char *strval; 747990b4856Slling zfs_prop_t prop; 748e9dbad6fSeschrock nvlist_t *ret; 749da6c28aaSamw int chosen_normal = -1; 750da6c28aaSamw int chosen_utf = -1; 751990b4856Slling 752e9dbad6fSeschrock if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 753e9dbad6fSeschrock (void) no_memory(hdl); 754e9dbad6fSeschrock return (NULL); 755e9dbad6fSeschrock } 756fa9e4066Sahrens 75714843421SMatthew Ahrens /* 75814843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 75914843421SMatthew Ahrens */ 76014843421SMatthew Ahrens 761e9dbad6fSeschrock elem = NULL; 762e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 763990b4856Slling const char *propname = nvpair_name(elem); 76499653d4eSeschrock 76514843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 76614843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 767fa9e4066Sahrens /* 76814843421SMatthew Ahrens * This is a user property: make sure it's a 769990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 770990b4856Slling */ 771990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 772990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 773990b4856Slling "'%s' must be a string"), propname); 774990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 775990b4856Slling goto error; 776990b4856Slling } 777990b4856Slling 778990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 779e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 780e9dbad6fSeschrock "property name '%s' is too long"), 781e9dbad6fSeschrock propname); 782990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 783e9dbad6fSeschrock goto error; 784e9dbad6fSeschrock } 785e9dbad6fSeschrock 786e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 787e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 788e9dbad6fSeschrock (void) no_memory(hdl); 789e9dbad6fSeschrock goto error; 790e9dbad6fSeschrock } 791e9dbad6fSeschrock continue; 792e9dbad6fSeschrock } 793fa9e4066Sahrens 79414843421SMatthew Ahrens /* 79514843421SMatthew Ahrens * Currently, only user properties can be modified on 79614843421SMatthew Ahrens * snapshots. 79714843421SMatthew Ahrens */ 798bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 799bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 800bb0ade09Sahrens "this property can not be modified for snapshots")); 801bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 802bb0ade09Sahrens goto error; 803bb0ade09Sahrens } 804bb0ade09Sahrens 80514843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 80614843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 80714843421SMatthew Ahrens char newpropname[128]; 80814843421SMatthew Ahrens char domain[128]; 80914843421SMatthew Ahrens uint64_t rid; 81014843421SMatthew Ahrens uint64_t valary[3]; 81114843421SMatthew Ahrens 81214843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 81314843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 81414843421SMatthew Ahrens zfs_error_aux(hdl, 81514843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 81614843421SMatthew Ahrens "'%s' has an invalid user/group name"), 81714843421SMatthew Ahrens propname); 81814843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 81914843421SMatthew Ahrens goto error; 82014843421SMatthew Ahrens } 82114843421SMatthew Ahrens 82214843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 82314843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 82414843421SMatthew Ahrens zfs_error_aux(hdl, 82514843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 82614843421SMatthew Ahrens propname); 82714843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 82814843421SMatthew Ahrens errbuf); 82914843421SMatthew Ahrens goto error; 83014843421SMatthew Ahrens } 83114843421SMatthew Ahrens 83214843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 83314843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 83414843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 83514843421SMatthew Ahrens intval = 0; 83614843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 83714843421SMatthew Ahrens strval, &intval) != 0) { 83814843421SMatthew Ahrens (void) zfs_error(hdl, 83914843421SMatthew Ahrens EZFS_BADPROP, errbuf); 84014843421SMatthew Ahrens goto error; 84114843421SMatthew Ahrens } 84214843421SMatthew Ahrens } else if (nvpair_type(elem) == 84314843421SMatthew Ahrens DATA_TYPE_UINT64) { 84414843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 84514843421SMatthew Ahrens if (intval == 0) { 84614843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 84714843421SMatthew Ahrens "use 'none' to disable " 84814843421SMatthew Ahrens "userquota/groupquota")); 84914843421SMatthew Ahrens goto error; 85014843421SMatthew Ahrens } 85114843421SMatthew Ahrens } else { 85214843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 85314843421SMatthew Ahrens "'%s' must be a number"), propname); 85414843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 85514843421SMatthew Ahrens goto error; 85614843421SMatthew Ahrens } 85714843421SMatthew Ahrens 8582d5843dbSMatthew Ahrens /* 8592d5843dbSMatthew Ahrens * Encode the prop name as 8602d5843dbSMatthew Ahrens * userquota@<hex-rid>-domain, to make it easy 8612d5843dbSMatthew Ahrens * for the kernel to decode. 8622d5843dbSMatthew Ahrens */ 86314843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 8642d5843dbSMatthew Ahrens "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 8652d5843dbSMatthew Ahrens (longlong_t)rid, domain); 86614843421SMatthew Ahrens valary[0] = uqtype; 86714843421SMatthew Ahrens valary[1] = rid; 86814843421SMatthew Ahrens valary[2] = intval; 86914843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 87014843421SMatthew Ahrens valary, 3) != 0) { 87114843421SMatthew Ahrens (void) no_memory(hdl); 87214843421SMatthew Ahrens goto error; 87314843421SMatthew Ahrens } 87414843421SMatthew Ahrens continue; 87514843421SMatthew Ahrens } 87614843421SMatthew Ahrens 87714843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 87814843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 87914843421SMatthew Ahrens "invalid property '%s'"), propname); 88014843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 88114843421SMatthew Ahrens goto error; 88214843421SMatthew Ahrens } 88314843421SMatthew Ahrens 884e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 885e9dbad6fSeschrock zfs_error_aux(hdl, 886e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 887e9dbad6fSeschrock "apply to datasets of this type"), propname); 888e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 889e9dbad6fSeschrock goto error; 890e9dbad6fSeschrock } 891e9dbad6fSeschrock 892e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 893da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 894e9dbad6fSeschrock zfs_error_aux(hdl, 895e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 896e9dbad6fSeschrock propname); 897e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 898e9dbad6fSeschrock goto error; 899e9dbad6fSeschrock } 900e9dbad6fSeschrock 901990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 902990b4856Slling &strval, &intval, errbuf) != 0) 903e9dbad6fSeschrock goto error; 904e9dbad6fSeschrock 905e9dbad6fSeschrock /* 906e9dbad6fSeschrock * Perform some additional checks for specific properties. 907e9dbad6fSeschrock */ 908e9dbad6fSeschrock switch (prop) { 909e7437265Sahrens case ZFS_PROP_VERSION: 910e7437265Sahrens { 911e7437265Sahrens int version; 912e7437265Sahrens 913e7437265Sahrens if (zhp == NULL) 914e7437265Sahrens break; 915e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 916e7437265Sahrens if (intval < version) { 917e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 918e7437265Sahrens "Can not downgrade; already at version %u"), 919e7437265Sahrens version); 920e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 921e7437265Sahrens goto error; 922e7437265Sahrens } 923e7437265Sahrens break; 924e7437265Sahrens } 925e7437265Sahrens 926e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 927e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 928e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 929e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 930e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 931e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 932e9dbad6fSeschrock "'%s' must be power of 2 from %u " 933e9dbad6fSeschrock "to %uk"), propname, 934e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 935e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 936e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 937e9dbad6fSeschrock goto error; 938e9dbad6fSeschrock } 939e9dbad6fSeschrock break; 940e9dbad6fSeschrock 941f3861e1aSahl case ZFS_PROP_SHAREISCSI: 942f3861e1aSahl if (strcmp(strval, "off") != 0 && 943f3861e1aSahl strcmp(strval, "on") != 0 && 944f3861e1aSahl strcmp(strval, "type=disk") != 0) { 945f3861e1aSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 946f3861e1aSahl "'%s' must be 'on', 'off', or 'type=disk'"), 947f3861e1aSahl propname); 948f3861e1aSahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 949f3861e1aSahl goto error; 950f3861e1aSahl } 951f3861e1aSahl 952f3861e1aSahl break; 953f3861e1aSahl 9544201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 9554201a95eSRic Aleshire { 9564201a95eSRic Aleshire /* 9574201a95eSRic Aleshire * Verify the mlslabel string and convert to 9584201a95eSRic Aleshire * internal hex label string. 9594201a95eSRic Aleshire */ 9604201a95eSRic Aleshire 9614201a95eSRic Aleshire m_label_t *new_sl; 9624201a95eSRic Aleshire char *hex = NULL; /* internal label string */ 9634201a95eSRic Aleshire 9644201a95eSRic Aleshire /* Default value is already OK. */ 9654201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 9664201a95eSRic Aleshire break; 9674201a95eSRic Aleshire 9684201a95eSRic Aleshire /* Verify the label can be converted to binary form */ 9694201a95eSRic Aleshire if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 9704201a95eSRic Aleshire (str_to_label(strval, &new_sl, MAC_LABEL, 9714201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1)) { 9724201a95eSRic Aleshire goto badlabel; 9734201a95eSRic Aleshire } 9744201a95eSRic Aleshire 9754201a95eSRic Aleshire /* Now translate to hex internal label string */ 9764201a95eSRic Aleshire if (label_to_str(new_sl, &hex, M_INTERNAL, 9774201a95eSRic Aleshire DEF_NAMES) != 0) { 9784201a95eSRic Aleshire if (hex) 9794201a95eSRic Aleshire free(hex); 9804201a95eSRic Aleshire goto badlabel; 9814201a95eSRic Aleshire } 9824201a95eSRic Aleshire m_label_free(new_sl); 9834201a95eSRic Aleshire 9844201a95eSRic Aleshire /* If string is already in internal form, we're done. */ 9854201a95eSRic Aleshire if (strcmp(strval, hex) == 0) { 9864201a95eSRic Aleshire free(hex); 9874201a95eSRic Aleshire break; 9884201a95eSRic Aleshire } 9894201a95eSRic Aleshire 9904201a95eSRic Aleshire /* Replace the label string with the internal form. */ 991569038c9SRic Aleshire (void) nvlist_remove(ret, zfs_prop_to_name(prop), 9924201a95eSRic Aleshire DATA_TYPE_STRING); 9934201a95eSRic Aleshire verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 9944201a95eSRic Aleshire hex) == 0); 9954201a95eSRic Aleshire free(hex); 9964201a95eSRic Aleshire 9974201a95eSRic Aleshire break; 9984201a95eSRic Aleshire 9994201a95eSRic Aleshire badlabel: 10004201a95eSRic Aleshire zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10014201a95eSRic Aleshire "invalid mlslabel '%s'"), strval); 10024201a95eSRic Aleshire (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10034201a95eSRic Aleshire m_label_free(new_sl); /* OK if null */ 10044201a95eSRic Aleshire goto error; 10054201a95eSRic Aleshire 10064201a95eSRic Aleshire } 10074201a95eSRic Aleshire 1008e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 100989eef05eSrm160521 { 101089eef05eSrm160521 namecheck_err_t why; 101189eef05eSrm160521 1012e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1013e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1014e9dbad6fSeschrock break; 1015e9dbad6fSeschrock 101689eef05eSrm160521 if (mountpoint_namecheck(strval, &why)) { 101789eef05eSrm160521 switch (why) { 101889eef05eSrm160521 case NAME_ERR_LEADING_SLASH: 101989eef05eSrm160521 zfs_error_aux(hdl, 102089eef05eSrm160521 dgettext(TEXT_DOMAIN, 1021e9dbad6fSeschrock "'%s' must be an absolute path, " 1022e9dbad6fSeschrock "'none', or 'legacy'"), propname); 102389eef05eSrm160521 break; 102489eef05eSrm160521 case NAME_ERR_TOOLONG: 102589eef05eSrm160521 zfs_error_aux(hdl, 102689eef05eSrm160521 dgettext(TEXT_DOMAIN, 102789eef05eSrm160521 "component of '%s' is too long"), 102889eef05eSrm160521 propname); 102989eef05eSrm160521 break; 103089eef05eSrm160521 } 1031e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1032e9dbad6fSeschrock goto error; 1033e9dbad6fSeschrock } 103489eef05eSrm160521 } 103589eef05eSrm160521 1036f3861e1aSahl /*FALLTHRU*/ 1037e9dbad6fSeschrock 1038da6c28aaSamw case ZFS_PROP_SHARESMB: 1039f3861e1aSahl case ZFS_PROP_SHARENFS: 1040e9dbad6fSeschrock /* 1041da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1042da6c28aaSamw * properties, check if it can be set in a 1043da6c28aaSamw * global/non-global zone based on 1044f3861e1aSahl * the zoned property value: 1045fa9e4066Sahrens * 1046fa9e4066Sahrens * global zone non-global zone 1047f3861e1aSahl * -------------------------------------------------- 1048fa9e4066Sahrens * zoned=on mountpoint (no) mountpoint (yes) 1049fa9e4066Sahrens * sharenfs (no) sharenfs (no) 1050da6c28aaSamw * sharesmb (no) sharesmb (no) 1051fa9e4066Sahrens * 1052fa9e4066Sahrens * zoned=off mountpoint (yes) N/A 1053fa9e4066Sahrens * sharenfs (yes) 1054da6c28aaSamw * sharesmb (yes) 1055fa9e4066Sahrens */ 1056e9dbad6fSeschrock if (zoned) { 1057fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID) { 105899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1059e9dbad6fSeschrock "'%s' cannot be set on " 1060e9dbad6fSeschrock "dataset in a non-global zone"), 1061e9dbad6fSeschrock propname); 1062e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1063e9dbad6fSeschrock errbuf); 1064e9dbad6fSeschrock goto error; 1065da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1066da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 106799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1068e9dbad6fSeschrock "'%s' cannot be set in " 1069e9dbad6fSeschrock "a non-global zone"), propname); 1070e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1071e9dbad6fSeschrock errbuf); 1072e9dbad6fSeschrock goto error; 1073fa9e4066Sahrens } 1074fa9e4066Sahrens } else if (getzoneid() != GLOBAL_ZONEID) { 1075fa9e4066Sahrens /* 1076fa9e4066Sahrens * If zoned property is 'off', this must be in 107714843421SMatthew Ahrens * a global zone. If not, something is wrong. 1078fa9e4066Sahrens */ 107999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1080e9dbad6fSeschrock "'%s' cannot be set while dataset " 1081e9dbad6fSeschrock "'zoned' property is set"), propname); 1082e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1083e9dbad6fSeschrock goto error; 1084fa9e4066Sahrens } 1085f3861e1aSahl 108667331909Sdougm /* 108767331909Sdougm * At this point, it is legitimate to set the 108867331909Sdougm * property. Now we want to make sure that the 108967331909Sdougm * property value is valid if it is sharenfs. 109067331909Sdougm */ 1091da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1092da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 109367331909Sdougm strcmp(strval, "on") != 0 && 109467331909Sdougm strcmp(strval, "off") != 0) { 1095da6c28aaSamw zfs_share_proto_t proto; 1096da6c28aaSamw 1097da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1098da6c28aaSamw proto = PROTO_SMB; 1099da6c28aaSamw else 1100da6c28aaSamw proto = PROTO_NFS; 110167331909Sdougm 110267331909Sdougm /* 1103da6c28aaSamw * Must be an valid sharing protocol 1104da6c28aaSamw * option string so init the libshare 1105da6c28aaSamw * in order to enable the parser and 1106da6c28aaSamw * then parse the options. We use the 1107da6c28aaSamw * control API since we don't care about 1108da6c28aaSamw * the current configuration and don't 110967331909Sdougm * want the overhead of loading it 111067331909Sdougm * until we actually do something. 111167331909Sdougm */ 111267331909Sdougm 111367331909Sdougm if (zfs_init_libshare(hdl, 111467331909Sdougm SA_INIT_CONTROL_API) != SA_OK) { 1115fac3008cSeschrock /* 1116fac3008cSeschrock * An error occurred so we can't do 1117fac3008cSeschrock * anything 1118fac3008cSeschrock */ 111967331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 112067331909Sdougm "'%s' cannot be set: problem " 112167331909Sdougm "in share initialization"), 112267331909Sdougm propname); 1123fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1124fac3008cSeschrock errbuf); 112567331909Sdougm goto error; 112667331909Sdougm } 112767331909Sdougm 1128da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 112967331909Sdougm /* 113067331909Sdougm * There was an error in parsing so 113167331909Sdougm * deal with it by issuing an error 113267331909Sdougm * message and leaving after 113367331909Sdougm * uninitializing the the libshare 113467331909Sdougm * interface. 113567331909Sdougm */ 113667331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 113767331909Sdougm "'%s' cannot be set to invalid " 113867331909Sdougm "options"), propname); 1139fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1140fac3008cSeschrock errbuf); 114167331909Sdougm zfs_uninit_libshare(hdl); 114267331909Sdougm goto error; 114367331909Sdougm } 114467331909Sdougm zfs_uninit_libshare(hdl); 114567331909Sdougm } 114667331909Sdougm 1147f3861e1aSahl break; 1148da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1149da6c28aaSamw chosen_utf = (int)intval; 1150da6c28aaSamw break; 1151da6c28aaSamw case ZFS_PROP_NORMALIZE: 1152da6c28aaSamw chosen_normal = (int)intval; 1153da6c28aaSamw break; 1154fa9e4066Sahrens } 1155fa9e4066Sahrens 1156e9dbad6fSeschrock /* 1157e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1158e9dbad6fSeschrock * checks to enforce. 1159e9dbad6fSeschrock */ 1160e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1161e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1162e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1163e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1164e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1165e9dbad6fSeschrock char buf[64]; 1166e9dbad6fSeschrock 1167e9dbad6fSeschrock switch (prop) { 1168e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1169a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1170e9dbad6fSeschrock if (intval > volsize) { 1171e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1172e9dbad6fSeschrock "'%s' is greater than current " 1173e9dbad6fSeschrock "volume size"), propname); 1174e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1175e9dbad6fSeschrock errbuf); 1176e9dbad6fSeschrock goto error; 1177e9dbad6fSeschrock } 1178e9dbad6fSeschrock break; 1179e9dbad6fSeschrock 1180e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1181e9dbad6fSeschrock if (intval % blocksize != 0) { 1182e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1183e9dbad6fSeschrock sizeof (buf)); 1184e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1185e9dbad6fSeschrock "'%s' must be a multiple of " 1186e9dbad6fSeschrock "volume block size (%s)"), 1187e9dbad6fSeschrock propname, buf); 1188e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1189e9dbad6fSeschrock errbuf); 1190e9dbad6fSeschrock goto error; 1191e9dbad6fSeschrock } 1192e9dbad6fSeschrock 1193e9dbad6fSeschrock if (intval == 0) { 1194e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1195e9dbad6fSeschrock "'%s' cannot be zero"), 1196e9dbad6fSeschrock propname); 1197e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1198e9dbad6fSeschrock errbuf); 1199e9dbad6fSeschrock goto error; 1200e9dbad6fSeschrock } 1201f3861e1aSahl break; 1202e9dbad6fSeschrock } 1203e9dbad6fSeschrock } 1204e9dbad6fSeschrock } 1205e9dbad6fSeschrock 1206e9dbad6fSeschrock /* 1207da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1208da6c28aaSamw * enforce rejection of non-UTF8 names. 1209da6c28aaSamw * 1210da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1211da6c28aaSamw * was explicitly not chosen, it is an error. 1212da6c28aaSamw */ 1213de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1214da6c28aaSamw if (nvlist_add_uint64(ret, 1215da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1216da6c28aaSamw (void) no_memory(hdl); 1217da6c28aaSamw goto error; 1218da6c28aaSamw } 1219de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1220da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1221da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1222da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1223da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1224da6c28aaSamw goto error; 1225da6c28aaSamw } 1226da6c28aaSamw 1227da6c28aaSamw /* 1228e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1229e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1230e9dbad6fSeschrock */ 1231e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 1232e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1233e9dbad6fSeschrock &intval) == 0) { 1234e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 1235e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1236a9b821a0Sck153898 uint64_t old_reservation; 1237e9dbad6fSeschrock uint64_t new_reservation; 1238a9b821a0Sck153898 zfs_prop_t resv_prop; 1239a9b821a0Sck153898 12407b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1241a9b821a0Sck153898 goto error; 1242a9b821a0Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 1243e9dbad6fSeschrock 1244e9dbad6fSeschrock if (old_volsize == old_reservation && 1245a9b821a0Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 1246e9dbad6fSeschrock &new_reservation) != 0) { 1247e9dbad6fSeschrock if (nvlist_add_uint64(ret, 1248a9b821a0Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 1249e9dbad6fSeschrock (void) no_memory(hdl); 1250e9dbad6fSeschrock goto error; 1251e9dbad6fSeschrock } 1252e9dbad6fSeschrock } 1253e9dbad6fSeschrock } 1254e9dbad6fSeschrock return (ret); 1255e9dbad6fSeschrock 1256e9dbad6fSeschrock error: 1257e9dbad6fSeschrock nvlist_free(ret); 1258e9dbad6fSeschrock return (NULL); 1259e9dbad6fSeschrock } 1260e9dbad6fSeschrock 126192241e0bSTom Erickson void 126292241e0bSTom Erickson zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 126392241e0bSTom Erickson char *errbuf) 126492241e0bSTom Erickson { 126592241e0bSTom Erickson switch (err) { 126692241e0bSTom Erickson 126792241e0bSTom Erickson case ENOSPC: 126892241e0bSTom Erickson /* 126992241e0bSTom Erickson * For quotas and reservations, ENOSPC indicates 127092241e0bSTom Erickson * something different; setting a quota or reservation 127192241e0bSTom Erickson * doesn't use any disk space. 127292241e0bSTom Erickson */ 127392241e0bSTom Erickson switch (prop) { 127492241e0bSTom Erickson case ZFS_PROP_QUOTA: 127592241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 127692241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127792241e0bSTom Erickson "size is less than current used or " 127892241e0bSTom Erickson "reserved space")); 127992241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128092241e0bSTom Erickson break; 128192241e0bSTom Erickson 128292241e0bSTom Erickson case ZFS_PROP_RESERVATION: 128392241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 128492241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128592241e0bSTom Erickson "size is greater than available space")); 128692241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128792241e0bSTom Erickson break; 128892241e0bSTom Erickson 128992241e0bSTom Erickson default: 129092241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 129192241e0bSTom Erickson break; 129292241e0bSTom Erickson } 129392241e0bSTom Erickson break; 129492241e0bSTom Erickson 129592241e0bSTom Erickson case EBUSY: 129692241e0bSTom Erickson (void) zfs_standard_error(hdl, EBUSY, errbuf); 129792241e0bSTom Erickson break; 129892241e0bSTom Erickson 129992241e0bSTom Erickson case EROFS: 130092241e0bSTom Erickson (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 130192241e0bSTom Erickson break; 130292241e0bSTom Erickson 130392241e0bSTom Erickson case ENOTSUP: 130492241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130592241e0bSTom Erickson "pool and or dataset must be upgraded to set this " 130692241e0bSTom Erickson "property or value")); 130792241e0bSTom Erickson (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 130892241e0bSTom Erickson break; 130992241e0bSTom Erickson 131092241e0bSTom Erickson case ERANGE: 131192241e0bSTom Erickson if (prop == ZFS_PROP_COMPRESSION) { 131292241e0bSTom Erickson (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 131392241e0bSTom Erickson "property setting is not allowed on " 131492241e0bSTom Erickson "bootable datasets")); 131592241e0bSTom Erickson (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 131692241e0bSTom Erickson } else { 131792241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 131892241e0bSTom Erickson } 131992241e0bSTom Erickson break; 132092241e0bSTom Erickson 132192241e0bSTom Erickson case EOVERFLOW: 132292241e0bSTom Erickson /* 132392241e0bSTom Erickson * This platform can't address a volume this big. 132492241e0bSTom Erickson */ 132592241e0bSTom Erickson #ifdef _ILP32 132692241e0bSTom Erickson if (prop == ZFS_PROP_VOLSIZE) { 132792241e0bSTom Erickson (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132892241e0bSTom Erickson break; 132992241e0bSTom Erickson } 133092241e0bSTom Erickson #endif 133192241e0bSTom Erickson /* FALLTHROUGH */ 133292241e0bSTom Erickson default: 133392241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 133492241e0bSTom Erickson } 133592241e0bSTom Erickson } 133692241e0bSTom Erickson 1337e9dbad6fSeschrock /* 1338e9dbad6fSeschrock * Given a property name and value, set the property for the given dataset. 1339e9dbad6fSeschrock */ 1340e9dbad6fSeschrock int 1341e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1342e9dbad6fSeschrock { 1343e9dbad6fSeschrock zfs_cmd_t zc = { 0 }; 1344e9dbad6fSeschrock int ret = -1; 1345e9dbad6fSeschrock prop_changelist_t *cl = NULL; 1346e9dbad6fSeschrock char errbuf[1024]; 1347e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1348e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1349e9dbad6fSeschrock zfs_prop_t prop; 13500068372bSMark J Musante boolean_t do_prefix; 13510068372bSMark J Musante uint64_t idx; 1352e9dbad6fSeschrock 1353e9dbad6fSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1354e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1355e9dbad6fSeschrock zhp->zfs_name); 1356e9dbad6fSeschrock 1357e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1358e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1359e9dbad6fSeschrock (void) no_memory(hdl); 1360e9dbad6fSeschrock goto error; 1361e9dbad6fSeschrock } 1362e9dbad6fSeschrock 13630a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1364e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1365e9dbad6fSeschrock goto error; 1366990b4856Slling 1367e9dbad6fSeschrock nvlist_free(nvl); 1368e9dbad6fSeschrock nvl = realprops; 1369e9dbad6fSeschrock 1370e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1371e9dbad6fSeschrock 13720069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1373e9dbad6fSeschrock goto error; 1374fa9e4066Sahrens 1375fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 137699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1377fa9e4066Sahrens "child dataset with inherited mountpoint is used " 137899653d4eSeschrock "in a non-global zone")); 137999653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1380fa9e4066Sahrens goto error; 1381fa9e4066Sahrens } 1382fa9e4066Sahrens 13830068372bSMark J Musante /* 13840068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 13850068372bSMark J Musante * then we want to prevent unmounting & remounting it. 13860068372bSMark J Musante */ 13870068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13880068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 13890068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1390a227b7f4Shs24103 1391a227b7f4Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1392fa9e4066Sahrens goto error; 1393fa9e4066Sahrens 1394fa9e4066Sahrens /* 1395fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1396fa9e4066Sahrens */ 1397fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1398fa9e4066Sahrens 1399990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1400e9dbad6fSeschrock goto error; 1401e9dbad6fSeschrock 1402ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1403743a77edSAlan Wright 1404fa9e4066Sahrens if (ret != 0) { 140592241e0bSTom Erickson zfs_setprop_error(hdl, prop, errno, errbuf); 1406fa9e4066Sahrens } else { 1407a227b7f4Shs24103 if (do_prefix) 1408a227b7f4Shs24103 ret = changelist_postfix(cl); 1409a227b7f4Shs24103 1410fa9e4066Sahrens /* 1411fa9e4066Sahrens * Refresh the statistics so the new property value 1412fa9e4066Sahrens * is reflected. 1413fa9e4066Sahrens */ 1414a227b7f4Shs24103 if (ret == 0) 1415fa9e4066Sahrens (void) get_stats(zhp); 1416fa9e4066Sahrens } 1417fa9e4066Sahrens 1418fa9e4066Sahrens error: 1419e9dbad6fSeschrock nvlist_free(nvl); 1420e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1421e9dbad6fSeschrock if (cl) 1422fa9e4066Sahrens changelist_free(cl); 1423fa9e4066Sahrens return (ret); 1424fa9e4066Sahrens } 1425fa9e4066Sahrens 1426fa9e4066Sahrens /* 142792241e0bSTom Erickson * Given a property, inherit the value from the parent dataset, or if received 142892241e0bSTom Erickson * is TRUE, revert to the received value, if any. 1429fa9e4066Sahrens */ 1430fa9e4066Sahrens int 143192241e0bSTom Erickson zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1432fa9e4066Sahrens { 1433fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1434fa9e4066Sahrens int ret; 1435fa9e4066Sahrens prop_changelist_t *cl; 143699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 143799653d4eSeschrock char errbuf[1024]; 1438e9dbad6fSeschrock zfs_prop_t prop; 143999653d4eSeschrock 144099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 144199653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1442fa9e4066Sahrens 144392241e0bSTom Erickson zc.zc_cookie = received; 1444990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1445e9dbad6fSeschrock /* 1446e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1447e9dbad6fSeschrock * small, so just do it here. 1448e9dbad6fSeschrock */ 1449e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1450e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1451e9dbad6fSeschrock "invalid property")); 1452e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1453e9dbad6fSeschrock } 1454e9dbad6fSeschrock 1455e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1456e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1457e9dbad6fSeschrock 1458e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1459e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1460e9dbad6fSeschrock 1461e9dbad6fSeschrock return (0); 1462e9dbad6fSeschrock } 1463e9dbad6fSeschrock 1464fa9e4066Sahrens /* 1465fa9e4066Sahrens * Verify that this property is inheritable. 1466fa9e4066Sahrens */ 146799653d4eSeschrock if (zfs_prop_readonly(prop)) 146899653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1469fa9e4066Sahrens 147092241e0bSTom Erickson if (!zfs_prop_inheritable(prop) && !received) 147199653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1472fa9e4066Sahrens 1473fa9e4066Sahrens /* 1474fa9e4066Sahrens * Check to see if the value applies to this type 1475fa9e4066Sahrens */ 147699653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 147799653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1478fa9e4066Sahrens 1479bf7c2d40Srm160521 /* 1480bf7c2d40Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 1481bf7c2d40Srm160521 */ 1482bf7c2d40Srm160521 propname = zfs_prop_to_name(prop); 1483fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1484e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1485fa9e4066Sahrens 1486fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1487fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 148899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 148999653d4eSeschrock "dataset is used in a non-global zone")); 149099653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1491fa9e4066Sahrens } 1492fa9e4066Sahrens 1493fa9e4066Sahrens /* 1494fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1495fa9e4066Sahrens */ 14960069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1497fa9e4066Sahrens return (-1); 1498fa9e4066Sahrens 1499fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 150099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 150199653d4eSeschrock "child dataset with inherited mountpoint is used " 150299653d4eSeschrock "in a non-global zone")); 150399653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1504fa9e4066Sahrens goto error; 1505fa9e4066Sahrens } 1506fa9e4066Sahrens 1507fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1508fa9e4066Sahrens goto error; 1509fa9e4066Sahrens 1510e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 151199653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1512fa9e4066Sahrens } else { 1513fa9e4066Sahrens 1514efc555ebSnd150628 if ((ret = changelist_postfix(cl)) != 0) 1515fa9e4066Sahrens goto error; 1516fa9e4066Sahrens 1517fa9e4066Sahrens /* 1518fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1519fa9e4066Sahrens */ 1520fa9e4066Sahrens (void) get_stats(zhp); 1521fa9e4066Sahrens } 1522fa9e4066Sahrens 1523fa9e4066Sahrens error: 1524fa9e4066Sahrens changelist_free(cl); 1525fa9e4066Sahrens return (ret); 1526fa9e4066Sahrens } 1527fa9e4066Sahrens 1528fa9e4066Sahrens /* 15297f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 15307f7322feSeschrock * extract them appropriately. 15317f7322feSeschrock */ 15327f7322feSeschrock static uint64_t 15337f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15347f7322feSeschrock { 15357f7322feSeschrock nvlist_t *nv; 15367f7322feSeschrock uint64_t value; 15377f7322feSeschrock 1538a2eea2e1Sahrens *source = NULL; 15397f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15407f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1541990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1542990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15437f7322feSeschrock } else { 15442e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15452e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15467f7322feSeschrock value = zfs_prop_default_numeric(prop); 15477f7322feSeschrock *source = ""; 15487f7322feSeschrock } 15497f7322feSeschrock 15507f7322feSeschrock return (value); 15517f7322feSeschrock } 15527f7322feSeschrock 15537f7322feSeschrock static char * 15547f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15557f7322feSeschrock { 15567f7322feSeschrock nvlist_t *nv; 15577f7322feSeschrock char *value; 15587f7322feSeschrock 1559a2eea2e1Sahrens *source = NULL; 15607f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15617f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1562990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1563990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15647f7322feSeschrock } else { 15652e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 15662e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 15677f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15687f7322feSeschrock value = ""; 15697f7322feSeschrock *source = ""; 15707f7322feSeschrock } 15717f7322feSeschrock 15727f7322feSeschrock return (value); 15737f7322feSeschrock } 15747f7322feSeschrock 157592241e0bSTom Erickson static boolean_t 157692241e0bSTom Erickson zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157792241e0bSTom Erickson { 157892241e0bSTom Erickson return (zhp->zfs_props == zhp->zfs_recvd_props); 157992241e0bSTom Erickson } 158092241e0bSTom Erickson 158192241e0bSTom Erickson static void 158292241e0bSTom Erickson zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158392241e0bSTom Erickson { 158492241e0bSTom Erickson *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158592241e0bSTom Erickson zhp->zfs_props = zhp->zfs_recvd_props; 158692241e0bSTom Erickson } 158792241e0bSTom Erickson 158892241e0bSTom Erickson static void 158992241e0bSTom Erickson zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 159092241e0bSTom Erickson { 159192241e0bSTom Erickson zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 159292241e0bSTom Erickson *cookie = 0; 159392241e0bSTom Erickson } 159492241e0bSTom Erickson 15957f7322feSeschrock /* 1596fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1597fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1598fa9e4066Sahrens * 1599fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1600fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1601fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1602fa9e4066Sahrens * the source "temporary". 1603fa9e4066Sahrens */ 160499653d4eSeschrock static int 1605990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 160699653d4eSeschrock char **source, uint64_t *val) 1607fa9e4066Sahrens { 1608bd00f61bSrm160521 zfs_cmd_t zc = { 0 }; 160996510749Stimh nvlist_t *zplprops = NULL; 1610fa9e4066Sahrens struct mnttab mnt; 16113ccfa83cSahrens char *mntopt_on = NULL; 16123ccfa83cSahrens char *mntopt_off = NULL; 161392241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1614fa9e4066Sahrens 1615fa9e4066Sahrens *source = NULL; 1616fa9e4066Sahrens 16173ccfa83cSahrens switch (prop) { 16183ccfa83cSahrens case ZFS_PROP_ATIME: 16193ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 16203ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 16213ccfa83cSahrens break; 16223ccfa83cSahrens 16233ccfa83cSahrens case ZFS_PROP_DEVICES: 16243ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 16253ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 16263ccfa83cSahrens break; 16273ccfa83cSahrens 16283ccfa83cSahrens case ZFS_PROP_EXEC: 16293ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 16303ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 16313ccfa83cSahrens break; 16323ccfa83cSahrens 16333ccfa83cSahrens case ZFS_PROP_READONLY: 16343ccfa83cSahrens mntopt_on = MNTOPT_RO; 16353ccfa83cSahrens mntopt_off = MNTOPT_RW; 16363ccfa83cSahrens break; 16373ccfa83cSahrens 16383ccfa83cSahrens case ZFS_PROP_SETUID: 16393ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 16403ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 16413ccfa83cSahrens break; 16423ccfa83cSahrens 16433ccfa83cSahrens case ZFS_PROP_XATTR: 16443ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 16453ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 16463ccfa83cSahrens break; 1647da6c28aaSamw 1648da6c28aaSamw case ZFS_PROP_NBMAND: 1649da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1650da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1651da6c28aaSamw break; 16523ccfa83cSahrens } 16533ccfa83cSahrens 16543bb79becSeschrock /* 16553bb79becSeschrock * Because looking up the mount options is potentially expensive 16563bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16573bb79becSeschrock * we're looking up a property which requires its presence. 16583bb79becSeschrock */ 16593bb79becSeschrock if (!zhp->zfs_mntcheck && 16603ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1661ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1662ebedde84SEric Taylor struct mnttab entry; 16633bb79becSeschrock 1664ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1665ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 16663ccfa83cSahrens entry.mnt_mntopts); 16673ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 16683bb79becSeschrock return (-1); 16693ccfa83cSahrens } 16703bb79becSeschrock 16713bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 16723bb79becSeschrock } 16733bb79becSeschrock 1674fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1675fa9e4066Sahrens mnt.mnt_mntopts = ""; 1676fa9e4066Sahrens else 1677fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1678fa9e4066Sahrens 1679fa9e4066Sahrens switch (prop) { 1680fa9e4066Sahrens case ZFS_PROP_ATIME: 1681fa9e4066Sahrens case ZFS_PROP_DEVICES: 1682fa9e4066Sahrens case ZFS_PROP_EXEC: 16833ccfa83cSahrens case ZFS_PROP_READONLY: 16843ccfa83cSahrens case ZFS_PROP_SETUID: 16853ccfa83cSahrens case ZFS_PROP_XATTR: 1686da6c28aaSamw case ZFS_PROP_NBMAND: 168799653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1688fa9e4066Sahrens 168992241e0bSTom Erickson if (received) 169092241e0bSTom Erickson break; 169192241e0bSTom Erickson 16923ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 169399653d4eSeschrock *val = B_TRUE; 1694fa9e4066Sahrens if (src) 1695990b4856Slling *src = ZPROP_SRC_TEMPORARY; 16963ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 169799653d4eSeschrock *val = B_FALSE; 1698fa9e4066Sahrens if (src) 1699990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1700fa9e4066Sahrens } 170199653d4eSeschrock break; 1702fa9e4066Sahrens 17033ccfa83cSahrens case ZFS_PROP_CANMOUNT: 1704*d41c4376SMark J Musante case ZFS_PROP_VOLSIZE: 1705fa9e4066Sahrens case ZFS_PROP_QUOTA: 1706a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1707fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1708a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1709a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 171092241e0bSTom Erickson 171192241e0bSTom Erickson if (*source == NULL) { 171292241e0bSTom Erickson /* not default, must be local */ 1713fa9e4066Sahrens *source = zhp->zfs_name; 171492241e0bSTom Erickson } 171599653d4eSeschrock break; 1716fa9e4066Sahrens 1717fa9e4066Sahrens case ZFS_PROP_MOUNTED: 171899653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 171999653d4eSeschrock break; 1720fa9e4066Sahrens 172139c23413Seschrock case ZFS_PROP_NUMCLONES: 172239c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 172339c23413Seschrock break; 172439c23413Seschrock 1725bd00f61bSrm160521 case ZFS_PROP_VERSION: 1726de8267e0Stimh case ZFS_PROP_NORMALIZE: 1727de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1728de8267e0Stimh case ZFS_PROP_CASE: 1729de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1730de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17313d7934e1Srm160521 return (-1); 1732bd00f61bSrm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1733de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1734de8267e0Stimh zcmd_free_nvlists(&zc); 1735f4b94bdeSMatthew Ahrens return (-1); 1736bd00f61bSrm160521 } 1737de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1738de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1739de8267e0Stimh val) != 0) { 1740de8267e0Stimh zcmd_free_nvlists(&zc); 1741f4b94bdeSMatthew Ahrens return (-1); 1742de8267e0Stimh } 174396510749Stimh if (zplprops) 174496510749Stimh nvlist_free(zplprops); 1745de8267e0Stimh zcmd_free_nvlists(&zc); 1746bd00f61bSrm160521 break; 1747bd00f61bSrm160521 1748fa9e4066Sahrens default: 1749e7437265Sahrens switch (zfs_prop_get_type(prop)) { 175091ebeef5Sahrens case PROP_TYPE_NUMBER: 175191ebeef5Sahrens case PROP_TYPE_INDEX: 1752e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 175374e7dc98SMatthew Ahrens /* 175414843421SMatthew Ahrens * If we tried to use a default value for a 175574e7dc98SMatthew Ahrens * readonly property, it means that it was not 175631f572c2STom Erickson * present. 175774e7dc98SMatthew Ahrens */ 175874e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 175931f572c2STom Erickson *source != NULL && (*source)[0] == '\0') { 176031f572c2STom Erickson *source = NULL; 176174e7dc98SMatthew Ahrens } 1762e7437265Sahrens break; 1763e7437265Sahrens 176491ebeef5Sahrens case PROP_TYPE_STRING: 1765e7437265Sahrens default: 176699653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 176799653d4eSeschrock "cannot get non-numeric property")); 176899653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 176999653d4eSeschrock dgettext(TEXT_DOMAIN, "internal error"))); 1770fa9e4066Sahrens } 1771e7437265Sahrens } 1772fa9e4066Sahrens 1773fa9e4066Sahrens return (0); 1774fa9e4066Sahrens } 1775fa9e4066Sahrens 1776fa9e4066Sahrens /* 1777fa9e4066Sahrens * Calculate the source type, given the raw source string. 1778fa9e4066Sahrens */ 1779fa9e4066Sahrens static void 1780990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1781fa9e4066Sahrens char *statbuf, size_t statlen) 1782fa9e4066Sahrens { 1783990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1784fa9e4066Sahrens return; 1785fa9e4066Sahrens 1786fa9e4066Sahrens if (source == NULL) { 1787990b4856Slling *srctype = ZPROP_SRC_NONE; 1788fa9e4066Sahrens } else if (source[0] == '\0') { 1789990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 179092241e0bSTom Erickson } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 179192241e0bSTom Erickson *srctype = ZPROP_SRC_RECEIVED; 1792fa9e4066Sahrens } else { 1793fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1794990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1795fa9e4066Sahrens } else { 1796fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1797990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1798fa9e4066Sahrens } 1799fa9e4066Sahrens } 1800fa9e4066Sahrens 1801fa9e4066Sahrens } 1802fa9e4066Sahrens 180392241e0bSTom Erickson int 180492241e0bSTom Erickson zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180592241e0bSTom Erickson size_t proplen, boolean_t literal) 180692241e0bSTom Erickson { 180792241e0bSTom Erickson zfs_prop_t prop; 180892241e0bSTom Erickson int err = 0; 180992241e0bSTom Erickson 181092241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 181192241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 181292241e0bSTom Erickson return (-1); 181392241e0bSTom Erickson 181492241e0bSTom Erickson prop = zfs_name_to_prop(propname); 181592241e0bSTom Erickson 181692241e0bSTom Erickson if (prop != ZPROP_INVAL) { 181792241e0bSTom Erickson uint64_t cookie; 181892241e0bSTom Erickson if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181992241e0bSTom Erickson return (-1); 182092241e0bSTom Erickson zfs_set_recvd_props_mode(zhp, &cookie); 182192241e0bSTom Erickson err = zfs_prop_get(zhp, prop, propbuf, proplen, 182292241e0bSTom Erickson NULL, NULL, 0, literal); 182392241e0bSTom Erickson zfs_unset_recvd_props_mode(zhp, &cookie); 182492241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 182592241e0bSTom Erickson return (-1); 182692241e0bSTom Erickson } else { 182792241e0bSTom Erickson nvlist_t *propval; 182892241e0bSTom Erickson char *recvdval; 182992241e0bSTom Erickson if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 183092241e0bSTom Erickson propname, &propval) != 0) 183192241e0bSTom Erickson return (-1); 183292241e0bSTom Erickson verify(nvlist_lookup_string(propval, ZPROP_VALUE, 183392241e0bSTom Erickson &recvdval) == 0); 183492241e0bSTom Erickson (void) strlcpy(propbuf, recvdval, proplen); 183592241e0bSTom Erickson } 183692241e0bSTom Erickson 183792241e0bSTom Erickson return (err == 0 ? 0 : -1); 183892241e0bSTom Erickson } 183992241e0bSTom Erickson 1840fa9e4066Sahrens /* 1841fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1842fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1843fa9e4066Sahrens * human-readable form. 1844fa9e4066Sahrens * 1845fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1846fa9e4066Sahrens */ 1847fa9e4066Sahrens int 1848fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1849990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1850fa9e4066Sahrens { 1851fa9e4066Sahrens char *source = NULL; 1852fa9e4066Sahrens uint64_t val; 1853fa9e4066Sahrens char *str; 1854e9dbad6fSeschrock const char *strval; 185592241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 1856fa9e4066Sahrens 1857fa9e4066Sahrens /* 1858fa9e4066Sahrens * Check to see if this property applies to our object 1859fa9e4066Sahrens */ 1860fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1861fa9e4066Sahrens return (-1); 1862fa9e4066Sahrens 186392241e0bSTom Erickson if (received && zfs_prop_readonly(prop)) 186492241e0bSTom Erickson return (-1); 186592241e0bSTom Erickson 1866fa9e4066Sahrens if (src) 1867990b4856Slling *src = ZPROP_SRC_NONE; 1868fa9e4066Sahrens 1869fa9e4066Sahrens switch (prop) { 1870fa9e4066Sahrens case ZFS_PROP_CREATION: 1871fa9e4066Sahrens /* 1872fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1873fa9e4066Sahrens * this into a string unless 'literal' is specified. 1874fa9e4066Sahrens */ 1875fa9e4066Sahrens { 1876a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1877a2eea2e1Sahrens time_t time = (time_t)val; 1878fa9e4066Sahrens struct tm t; 1879fa9e4066Sahrens 1880fa9e4066Sahrens if (literal || 1881fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1882fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1883fa9e4066Sahrens &t) == 0) 1884a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1885fa9e4066Sahrens } 1886fa9e4066Sahrens break; 1887fa9e4066Sahrens 1888fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1889fa9e4066Sahrens /* 1890fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1891fa9e4066Sahrens * 1892fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1893fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1894fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1895fa9e4066Sahrens * 1896fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1897fa9e4066Sahrens * root to any values we return. 1898fa9e4066Sahrens */ 189929ab75c9Srm160521 19007f7322feSeschrock str = getprop_string(zhp, prop, &source); 1901fa9e4066Sahrens 1902b21718f0Sgw25295 if (str[0] == '/') { 190329ab75c9Srm160521 char buf[MAXPATHLEN]; 190429ab75c9Srm160521 char *root = buf; 19057f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 1906fa9e4066Sahrens 1907fa9e4066Sahrens if (relpath[0] == '/') 1908fa9e4066Sahrens relpath++; 1909b21718f0Sgw25295 191029ab75c9Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 191129ab75c9Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 191229ab75c9Srm160521 (strcmp(root, "-") == 0)) 191329ab75c9Srm160521 root[0] = '\0'; 1914b21718f0Sgw25295 /* 1915b21718f0Sgw25295 * Special case an alternate root of '/'. This will 1916b21718f0Sgw25295 * avoid having multiple leading slashes in the 1917b21718f0Sgw25295 * mountpoint path. 1918b21718f0Sgw25295 */ 1919b21718f0Sgw25295 if (strcmp(root, "/") == 0) 1920b21718f0Sgw25295 root++; 1921b21718f0Sgw25295 1922b21718f0Sgw25295 /* 1923b21718f0Sgw25295 * If the mountpoint is '/' then skip over this 1924b21718f0Sgw25295 * if we are obtaining either an alternate root or 1925b21718f0Sgw25295 * an inherited mountpoint. 1926b21718f0Sgw25295 */ 1927b21718f0Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 1928b21718f0Sgw25295 relpath[0] != '\0')) 19297f7322feSeschrock str++; 1930fa9e4066Sahrens 1931fa9e4066Sahrens if (relpath[0] == '\0') 1932fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19337f7322feSeschrock root, str); 1934fa9e4066Sahrens else 1935fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19367f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1937fa9e4066Sahrens relpath); 1938fa9e4066Sahrens } else { 1939fa9e4066Sahrens /* 'legacy' or 'none' */ 19407f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1941fa9e4066Sahrens } 1942fa9e4066Sahrens 1943fa9e4066Sahrens break; 1944fa9e4066Sahrens 1945fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1946a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1947fa9e4066Sahrens proplen); 1948fa9e4066Sahrens /* 1949fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1950fa9e4066Sahrens * it doesn't apply to this dataset. 1951fa9e4066Sahrens */ 1952fa9e4066Sahrens if (propbuf[0] == '\0') 1953fa9e4066Sahrens return (-1); 1954fa9e4066Sahrens break; 1955fa9e4066Sahrens 1956fa9e4066Sahrens case ZFS_PROP_QUOTA: 1957a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1958fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1959a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1960a9799022Sck153898 196199653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 196299653d4eSeschrock return (-1); 1963fa9e4066Sahrens 1964fa9e4066Sahrens /* 1965fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 1966fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 1967fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 1968fa9e4066Sahrens * that its set locally. 1969fa9e4066Sahrens */ 1970fa9e4066Sahrens if (val == 0) { 1971fa9e4066Sahrens if (literal) 1972fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 1973fa9e4066Sahrens else 1974fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 1975fa9e4066Sahrens } else { 1976fa9e4066Sahrens if (literal) 19775ad82045Snd150628 (void) snprintf(propbuf, proplen, "%llu", 19785ad82045Snd150628 (u_longlong_t)val); 1979fa9e4066Sahrens else 1980fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 1981fa9e4066Sahrens } 1982fa9e4066Sahrens break; 1983fa9e4066Sahrens 1984fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 198599653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 198699653d4eSeschrock return (-1); 1987b24ab676SJeff Bonwick (void) snprintf(propbuf, proplen, "%llu.%02llux", 1988b24ab676SJeff Bonwick (u_longlong_t)(val / 100), 1989b24ab676SJeff Bonwick (u_longlong_t)(val % 100)); 1990fa9e4066Sahrens break; 1991fa9e4066Sahrens 1992fa9e4066Sahrens case ZFS_PROP_TYPE: 1993fa9e4066Sahrens switch (zhp->zfs_type) { 1994fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 1995fa9e4066Sahrens str = "filesystem"; 1996fa9e4066Sahrens break; 1997fa9e4066Sahrens case ZFS_TYPE_VOLUME: 1998fa9e4066Sahrens str = "volume"; 1999fa9e4066Sahrens break; 2000fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 2001fa9e4066Sahrens str = "snapshot"; 2002fa9e4066Sahrens break; 2003fa9e4066Sahrens default: 200499653d4eSeschrock abort(); 2005fa9e4066Sahrens } 2006fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2007fa9e4066Sahrens break; 2008fa9e4066Sahrens 2009fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2010fa9e4066Sahrens /* 2011fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2012fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2013fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2014fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2015fa9e4066Sahrens */ 201699653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 201799653d4eSeschrock src, &source, &val) != 0) 201899653d4eSeschrock return (-1); 201999653d4eSeschrock if (val) 2020fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2021fa9e4066Sahrens else 2022fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2023fa9e4066Sahrens break; 2024fa9e4066Sahrens 2025fa9e4066Sahrens case ZFS_PROP_NAME: 2026fa9e4066Sahrens /* 2027fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2028fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2029fa9e4066Sahrens * consumers. 2030fa9e4066Sahrens */ 2031fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2032fa9e4066Sahrens break; 2033fa9e4066Sahrens 20344201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 20354201a95eSRic Aleshire { 20364201a95eSRic Aleshire m_label_t *new_sl = NULL; 20374201a95eSRic Aleshire char *ascii = NULL; /* human readable label */ 20384201a95eSRic Aleshire 20394201a95eSRic Aleshire (void) strlcpy(propbuf, 20404201a95eSRic Aleshire getprop_string(zhp, prop, &source), proplen); 20414201a95eSRic Aleshire 20424201a95eSRic Aleshire if (literal || (strcasecmp(propbuf, 20434201a95eSRic Aleshire ZFS_MLSLABEL_DEFAULT) == 0)) 20444201a95eSRic Aleshire break; 20454201a95eSRic Aleshire 20464201a95eSRic Aleshire /* 20474201a95eSRic Aleshire * Try to translate the internal hex string to 20484201a95eSRic Aleshire * human-readable output. If there are any 20494201a95eSRic Aleshire * problems just use the hex string. 20504201a95eSRic Aleshire */ 20514201a95eSRic Aleshire 20524201a95eSRic Aleshire if (str_to_label(propbuf, &new_sl, MAC_LABEL, 20534201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1) { 20544201a95eSRic Aleshire m_label_free(new_sl); 20554201a95eSRic Aleshire break; 20564201a95eSRic Aleshire } 20574201a95eSRic Aleshire 20584201a95eSRic Aleshire if (label_to_str(new_sl, &ascii, M_LABEL, 20594201a95eSRic Aleshire DEF_NAMES) != 0) { 20604201a95eSRic Aleshire if (ascii) 20614201a95eSRic Aleshire free(ascii); 20624201a95eSRic Aleshire m_label_free(new_sl); 20634201a95eSRic Aleshire break; 20644201a95eSRic Aleshire } 20654201a95eSRic Aleshire m_label_free(new_sl); 20664201a95eSRic Aleshire 20674201a95eSRic Aleshire (void) strlcpy(propbuf, ascii, proplen); 20684201a95eSRic Aleshire free(ascii); 20694201a95eSRic Aleshire } 20704201a95eSRic Aleshire break; 20714201a95eSRic Aleshire 2072fa9e4066Sahrens default: 2073e7437265Sahrens switch (zfs_prop_get_type(prop)) { 207491ebeef5Sahrens case PROP_TYPE_NUMBER: 2075e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2076e7437265Sahrens &source, &val) != 0) 2077e7437265Sahrens return (-1); 2078e7437265Sahrens if (literal) 2079e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2080e7437265Sahrens (u_longlong_t)val); 2081e7437265Sahrens else 2082e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2083e7437265Sahrens break; 2084e7437265Sahrens 208591ebeef5Sahrens case PROP_TYPE_STRING: 2086e7437265Sahrens (void) strlcpy(propbuf, 2087e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 2088e7437265Sahrens break; 2089e7437265Sahrens 209091ebeef5Sahrens case PROP_TYPE_INDEX: 2091fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2092fe192f76Sahrens &source, &val) != 0) 2093fe192f76Sahrens return (-1); 2094fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2095e7437265Sahrens return (-1); 2096e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2097e7437265Sahrens break; 2098e7437265Sahrens 2099e7437265Sahrens default: 210099653d4eSeschrock abort(); 2101fa9e4066Sahrens } 2102e7437265Sahrens } 2103fa9e4066Sahrens 2104fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2105fa9e4066Sahrens 2106fa9e4066Sahrens return (0); 2107fa9e4066Sahrens } 2108fa9e4066Sahrens 2109fa9e4066Sahrens /* 2110fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2111fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2112fa9e4066Sahrens * hard-coded property types. 2113fa9e4066Sahrens */ 2114fa9e4066Sahrens uint64_t 2115fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2116fa9e4066Sahrens { 2117fa9e4066Sahrens char *source; 211899653d4eSeschrock uint64_t val; 2119fa9e4066Sahrens 21203cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 212199653d4eSeschrock 212299653d4eSeschrock return (val); 2123fa9e4066Sahrens } 2124fa9e4066Sahrens 21257b97dc1aSrm160521 int 21267b97dc1aSrm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21277b97dc1aSrm160521 { 21287b97dc1aSrm160521 char buf[64]; 21297b97dc1aSrm160521 213014843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21317b97dc1aSrm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21327b97dc1aSrm160521 } 21337b97dc1aSrm160521 2134fa9e4066Sahrens /* 2135fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2136fa9e4066Sahrens */ 2137fa9e4066Sahrens int 2138fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2139990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2140fa9e4066Sahrens { 2141fa9e4066Sahrens char *source; 2142fa9e4066Sahrens 2143fa9e4066Sahrens /* 2144fa9e4066Sahrens * Check to see if this property applies to our object 2145fa9e4066Sahrens */ 2146e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2147ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 214899653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 214999653d4eSeschrock zfs_prop_to_name(prop))); 2150e45ce728Sahrens } 2151fa9e4066Sahrens 2152fa9e4066Sahrens if (src) 2153990b4856Slling *src = ZPROP_SRC_NONE; 2154fa9e4066Sahrens 215599653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 215699653d4eSeschrock return (-1); 2157fa9e4066Sahrens 2158fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2159fa9e4066Sahrens 2160fa9e4066Sahrens return (0); 2161fa9e4066Sahrens } 2162fa9e4066Sahrens 216314843421SMatthew Ahrens static int 216414843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 216514843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 216614843421SMatthew Ahrens { 216714843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 216814843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 216914843421SMatthew Ahrens idmap_stat status; 217014843421SMatthew Ahrens int err = EINVAL; 217114843421SMatthew Ahrens 217214843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 217314843421SMatthew Ahrens goto out; 217414843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 217514843421SMatthew Ahrens goto out; 217614843421SMatthew Ahrens 217714843421SMatthew Ahrens if (isuser) { 217814843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 217914843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 218014843421SMatthew Ahrens } else { 218114843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 218214843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 218314843421SMatthew Ahrens } 218414843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 218514843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 218614843421SMatthew Ahrens status == IDMAP_SUCCESS) 218714843421SMatthew Ahrens err = 0; 218814843421SMatthew Ahrens else 218914843421SMatthew Ahrens err = EINVAL; 219014843421SMatthew Ahrens out: 219114843421SMatthew Ahrens if (get_hdl) 219214843421SMatthew Ahrens idmap_get_destroy(get_hdl); 219314843421SMatthew Ahrens if (idmap_hdl) 219414843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 219514843421SMatthew Ahrens return (err); 219614843421SMatthew Ahrens } 219714843421SMatthew Ahrens 219814843421SMatthew Ahrens /* 219914843421SMatthew Ahrens * convert the propname into parameters needed by kernel 220014843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 220114843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 220214843421SMatthew Ahrens */ 220314843421SMatthew Ahrens static int 220414843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 220514843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 220614843421SMatthew Ahrens { 220714843421SMatthew Ahrens zfs_userquota_prop_t type; 220814843421SMatthew Ahrens char *cp, *end; 22093b12c289SMatthew Ahrens char *numericsid = NULL; 221014843421SMatthew Ahrens boolean_t isuser; 221114843421SMatthew Ahrens 221214843421SMatthew Ahrens domain[0] = '\0'; 221314843421SMatthew Ahrens 221414843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 221514843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 221614843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 221714843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 221814843421SMatthew Ahrens break; 221914843421SMatthew Ahrens } 222014843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 222114843421SMatthew Ahrens return (EINVAL); 222214843421SMatthew Ahrens *typep = type; 222314843421SMatthew Ahrens 222414843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 222514843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 222614843421SMatthew Ahrens 222714843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 222814843421SMatthew Ahrens 222914843421SMatthew Ahrens if (strchr(cp, '@')) { 223014843421SMatthew Ahrens /* 223114843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 22323b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 223314843421SMatthew Ahrens */ 22343b12c289SMatthew Ahrens directory_error_t e; 223514843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 223614843421SMatthew Ahrens return (ENOENT); 22373b12c289SMatthew Ahrens if (isuser) { 22383b12c289SMatthew Ahrens e = directory_sid_from_user_name(NULL, 22393b12c289SMatthew Ahrens cp, &numericsid); 22403b12c289SMatthew Ahrens } else { 22413b12c289SMatthew Ahrens e = directory_sid_from_group_name(NULL, 22423b12c289SMatthew Ahrens cp, &numericsid); 22433b12c289SMatthew Ahrens } 22443b12c289SMatthew Ahrens if (e != NULL) { 22453b12c289SMatthew Ahrens directory_error_free(e); 224614843421SMatthew Ahrens return (ENOENT); 22473b12c289SMatthew Ahrens } 22483b12c289SMatthew Ahrens if (numericsid == NULL) 224914843421SMatthew Ahrens return (ENOENT); 22503b12c289SMatthew Ahrens cp = numericsid; 22513b12c289SMatthew Ahrens /* will be further decoded below */ 22523b12c289SMatthew Ahrens } 22533b12c289SMatthew Ahrens 22543b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 225514843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 22563b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 225714843421SMatthew Ahrens cp = strrchr(domain, '-'); 225814843421SMatthew Ahrens *cp = '\0'; 225914843421SMatthew Ahrens cp++; 226014843421SMatthew Ahrens 226114843421SMatthew Ahrens errno = 0; 226214843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 22633b12c289SMatthew Ahrens if (numericsid) { 22643b12c289SMatthew Ahrens free(numericsid); 22653b12c289SMatthew Ahrens numericsid = NULL; 22663b12c289SMatthew Ahrens } 2267777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 226814843421SMatthew Ahrens return (EINVAL); 226914843421SMatthew Ahrens } else if (!isdigit(*cp)) { 227014843421SMatthew Ahrens /* 227114843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 227214843421SMatthew Ahrens * turned into a uid/gid 227314843421SMatthew Ahrens */ 227414843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 227514843421SMatthew Ahrens return (ENOENT); 227614843421SMatthew Ahrens if (isuser) { 227714843421SMatthew Ahrens struct passwd *pw; 227814843421SMatthew Ahrens pw = getpwnam(cp); 227914843421SMatthew Ahrens if (pw == NULL) 228014843421SMatthew Ahrens return (ENOENT); 228114843421SMatthew Ahrens *ridp = pw->pw_uid; 228214843421SMatthew Ahrens } else { 228314843421SMatthew Ahrens struct group *gr; 228414843421SMatthew Ahrens gr = getgrnam(cp); 228514843421SMatthew Ahrens if (gr == NULL) 228614843421SMatthew Ahrens return (ENOENT); 228714843421SMatthew Ahrens *ridp = gr->gr_gid; 228814843421SMatthew Ahrens } 228914843421SMatthew Ahrens } else { 229014843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 229114843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 229214843421SMatthew Ahrens idmap_rid_t rid; 229314843421SMatthew Ahrens char *mapdomain; 229414843421SMatthew Ahrens 229514843421SMatthew Ahrens if (*end != '\0') 229614843421SMatthew Ahrens return (EINVAL); 229714843421SMatthew Ahrens if (id > MAXUID) { 229814843421SMatthew Ahrens /* It's an ephemeral ID. */ 229914843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 230014843421SMatthew Ahrens &mapdomain, &rid) != 0) 230114843421SMatthew Ahrens return (ENOENT); 23023b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 230314843421SMatthew Ahrens *ridp = rid; 230414843421SMatthew Ahrens } else { 230514843421SMatthew Ahrens *ridp = id; 230614843421SMatthew Ahrens } 230714843421SMatthew Ahrens } 230814843421SMatthew Ahrens 23093b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 231014843421SMatthew Ahrens return (0); 231114843421SMatthew Ahrens } 231214843421SMatthew Ahrens 2313edea4b55SLin Ling static int 2314edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2315edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 231614843421SMatthew Ahrens { 231714843421SMatthew Ahrens int err; 231814843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 231914843421SMatthew Ahrens 232014843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 232114843421SMatthew Ahrens 232214843421SMatthew Ahrens err = userquota_propname_decode(propname, 232314843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2324edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2325edea4b55SLin Ling zc.zc_objset_type = *typep; 232614843421SMatthew Ahrens if (err) 232714843421SMatthew Ahrens return (err); 232814843421SMatthew Ahrens 232914843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 233014843421SMatthew Ahrens if (err) 233114843421SMatthew Ahrens return (err); 233214843421SMatthew Ahrens 2333edea4b55SLin Ling *propvalue = zc.zc_cookie; 2334edea4b55SLin Ling return (0); 2335edea4b55SLin Ling } 2336edea4b55SLin Ling 2337edea4b55SLin Ling int 2338edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2339edea4b55SLin Ling uint64_t *propvalue) 2340edea4b55SLin Ling { 2341edea4b55SLin Ling zfs_userquota_prop_t type; 2342edea4b55SLin Ling 2343edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2344edea4b55SLin Ling &type)); 2345edea4b55SLin Ling } 2346edea4b55SLin Ling 2347edea4b55SLin Ling int 2348edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2349edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 2350edea4b55SLin Ling { 2351edea4b55SLin Ling int err; 2352edea4b55SLin Ling uint64_t propvalue; 2353edea4b55SLin Ling zfs_userquota_prop_t type; 2354edea4b55SLin Ling 2355edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2356edea4b55SLin Ling &type); 2357edea4b55SLin Ling 2358edea4b55SLin Ling if (err) 2359edea4b55SLin Ling return (err); 2360edea4b55SLin Ling 236114843421SMatthew Ahrens if (literal) { 2362edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 2363edea4b55SLin Ling } else if (propvalue == 0 && 236414843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 236514843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 236614843421SMatthew Ahrens } else { 2367edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 236814843421SMatthew Ahrens } 236914843421SMatthew Ahrens return (0); 237014843421SMatthew Ahrens } 237114843421SMatthew Ahrens 2372fa9e4066Sahrens /* 2373fa9e4066Sahrens * Returns the name of the given zfs handle. 2374fa9e4066Sahrens */ 2375fa9e4066Sahrens const char * 2376fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2377fa9e4066Sahrens { 2378fa9e4066Sahrens return (zhp->zfs_name); 2379fa9e4066Sahrens } 2380fa9e4066Sahrens 2381fa9e4066Sahrens /* 2382fa9e4066Sahrens * Returns the type of the given zfs handle. 2383fa9e4066Sahrens */ 2384fa9e4066Sahrens zfs_type_t 2385fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2386fa9e4066Sahrens { 2387fa9e4066Sahrens return (zhp->zfs_type); 2388fa9e4066Sahrens } 2389fa9e4066Sahrens 2390ebedde84SEric Taylor static int 2391ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2392ebedde84SEric Taylor { 2393ebedde84SEric Taylor int rc; 2394ebedde84SEric Taylor uint64_t orig_cookie; 2395ebedde84SEric Taylor 2396ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2397ebedde84SEric Taylor top: 2398ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2399ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2400ebedde84SEric Taylor 2401ebedde84SEric Taylor if (rc == -1) { 2402ebedde84SEric Taylor switch (errno) { 2403ebedde84SEric Taylor case ENOMEM: 2404ebedde84SEric Taylor /* expand nvlist memory and try again */ 2405ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2406ebedde84SEric Taylor zcmd_free_nvlists(zc); 2407ebedde84SEric Taylor return (-1); 2408ebedde84SEric Taylor } 2409ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2410ebedde84SEric Taylor goto top; 2411ebedde84SEric Taylor /* 2412ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2413ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2414ebedde84SEric Taylor * has been removed since we obtained the handle. 2415ebedde84SEric Taylor */ 2416ebedde84SEric Taylor case ESRCH: 2417ebedde84SEric Taylor case ENOENT: 2418ebedde84SEric Taylor rc = 1; 2419ebedde84SEric Taylor break; 2420ebedde84SEric Taylor default: 2421ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2422ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2423ebedde84SEric Taylor "cannot iterate filesystems")); 2424ebedde84SEric Taylor break; 2425ebedde84SEric Taylor } 2426ebedde84SEric Taylor } 2427ebedde84SEric Taylor return (rc); 2428ebedde84SEric Taylor } 2429ebedde84SEric Taylor 2430fa9e4066Sahrens /* 24317f7322feSeschrock * Iterate over all child filesystems 2432fa9e4066Sahrens */ 2433fa9e4066Sahrens int 24347f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2435fa9e4066Sahrens { 2436fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2437fa9e4066Sahrens zfs_handle_t *nzhp; 2438fa9e4066Sahrens int ret; 2439fa9e4066Sahrens 24403cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24413cb34c60Sahrens return (0); 24423cb34c60Sahrens 2443ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2444ebedde84SEric Taylor return (-1); 2445ebedde84SEric Taylor 2446ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2447ebedde84SEric Taylor &zc)) == 0) { 2448fa9e4066Sahrens /* 2449fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2450fa9e4066Sahrens * that the pool has since been removed. 2451fa9e4066Sahrens */ 2452ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2453ebedde84SEric Taylor &zc)) == NULL) { 2454fa9e4066Sahrens continue; 2455fa9e4066Sahrens } 2456fa9e4066Sahrens 2457ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2458ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2459ebedde84SEric Taylor return (ret); 2460ebedde84SEric Taylor } 2461ebedde84SEric Taylor } 2462ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2463ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 24647f7322feSeschrock } 24657f7322feSeschrock 24667f7322feSeschrock /* 24677f7322feSeschrock * Iterate over all snapshots 24687f7322feSeschrock */ 24697f7322feSeschrock int 24707f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24717f7322feSeschrock { 24727f7322feSeschrock zfs_cmd_t zc = { 0 }; 24737f7322feSeschrock zfs_handle_t *nzhp; 24747f7322feSeschrock int ret; 2475fa9e4066Sahrens 24763cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24773cb34c60Sahrens return (0); 24783cb34c60Sahrens 2479ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2480ebedde84SEric Taylor return (-1); 2481ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2482ebedde84SEric Taylor &zc)) == 0) { 2483fa9e4066Sahrens 2484ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2485ebedde84SEric Taylor &zc)) == NULL) { 2486fa9e4066Sahrens continue; 2487fa9e4066Sahrens } 2488fa9e4066Sahrens 2489ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2490ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2491ebedde84SEric Taylor return (ret); 2492ebedde84SEric Taylor } 2493ebedde84SEric Taylor } 2494ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2495ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2496fa9e4066Sahrens } 2497fa9e4066Sahrens 2498fa9e4066Sahrens /* 24997f7322feSeschrock * Iterate over all children, snapshots and filesystems 25007f7322feSeschrock */ 25017f7322feSeschrock int 25027f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25037f7322feSeschrock { 25047f7322feSeschrock int ret; 25057f7322feSeschrock 25067f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25077f7322feSeschrock return (ret); 25087f7322feSeschrock 25097f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 25107f7322feSeschrock } 25117f7322feSeschrock 25127f7322feSeschrock /* 2513*d41c4376SMark J Musante * Is one dataset name a child dataset of another? 2514*d41c4376SMark J Musante * 2515*d41c4376SMark J Musante * Needs to handle these cases: 2516*d41c4376SMark J Musante * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 2517*d41c4376SMark J Musante * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 2518*d41c4376SMark J Musante * Descendant? No. No. No. Yes. 2519*d41c4376SMark J Musante */ 2520*d41c4376SMark J Musante static boolean_t 2521*d41c4376SMark J Musante is_descendant(const char *ds1, const char *ds2) 2522*d41c4376SMark J Musante { 2523*d41c4376SMark J Musante size_t d1len = strlen(ds1); 2524*d41c4376SMark J Musante 2525*d41c4376SMark J Musante /* ds2 can't be a descendant if it's smaller */ 2526*d41c4376SMark J Musante if (strlen(ds2) < d1len) 2527*d41c4376SMark J Musante return (B_FALSE); 2528*d41c4376SMark J Musante 2529*d41c4376SMark J Musante /* otherwise, compare strings and verify that there's a '/' char */ 2530*d41c4376SMark J Musante return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 2531*d41c4376SMark J Musante } 2532*d41c4376SMark J Musante 2533*d41c4376SMark J Musante /* 2534fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2535fa9e4066Sahrens * Can return NULL if this is a pool. 2536fa9e4066Sahrens */ 2537fa9e4066Sahrens static int 2538fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2539fa9e4066Sahrens { 2540fa9e4066Sahrens char *loc; 2541fa9e4066Sahrens 2542fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2543fa9e4066Sahrens return (-1); 2544fa9e4066Sahrens 2545fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2546fa9e4066Sahrens buf[loc - path] = '\0'; 2547fa9e4066Sahrens 2548fa9e4066Sahrens return (0); 2549fa9e4066Sahrens } 2550fa9e4066Sahrens 2551fa9e4066Sahrens /* 25527f1f55eaSvb160487 * If accept_ancestor is false, then check to make sure that the given path has 25537f1f55eaSvb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25547f1f55eaSvb160487 * closest existing ancestor for the given path. In prefixlen return the 25557f1f55eaSvb160487 * length of already existing prefix of the given path. We also fetch the 25567f1f55eaSvb160487 * 'zoned' property, which is used to validate property settings when creating 25577f1f55eaSvb160487 * new datasets. 2558fa9e4066Sahrens */ 2559fa9e4066Sahrens static int 25607f1f55eaSvb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25617f1f55eaSvb160487 boolean_t accept_ancestor, int *prefixlen) 2562fa9e4066Sahrens { 2563fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2564fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2565fa9e4066Sahrens char *slash; 25667f7322feSeschrock zfs_handle_t *zhp; 256799653d4eSeschrock char errbuf[1024]; 2568*d41c4376SMark J Musante uint64_t is_zoned; 256999653d4eSeschrock 2570deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2571deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2572fa9e4066Sahrens 2573fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2574fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 257599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 257699653d4eSeschrock "missing dataset name")); 257799653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2578fa9e4066Sahrens } 2579fa9e4066Sahrens 2580fa9e4066Sahrens /* check to see if the pool exists */ 2581fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2582fa9e4066Sahrens slash = parent + strlen(parent); 2583fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2584fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 258599653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2586fa9e4066Sahrens errno == ENOENT) { 258799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 258899653d4eSeschrock "no such pool '%s'"), zc.zc_name); 258999653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2590fa9e4066Sahrens } 2591fa9e4066Sahrens 2592fa9e4066Sahrens /* check to see if the parent dataset exists */ 25937f1f55eaSvb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 25947f1f55eaSvb160487 if (errno == ENOENT && accept_ancestor) { 25957f1f55eaSvb160487 /* 25967f1f55eaSvb160487 * Go deeper to find an ancestor, give up on top level. 25977f1f55eaSvb160487 */ 25987f1f55eaSvb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 25997f1f55eaSvb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26007f1f55eaSvb160487 "no such pool '%s'"), zc.zc_name); 26017f1f55eaSvb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26027f1f55eaSvb160487 } 26037f1f55eaSvb160487 } else if (errno == ENOENT) { 260499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 260599653d4eSeschrock "parent does not exist")); 260699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26077f1f55eaSvb160487 } else 260899653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2609fa9e4066Sahrens } 2610fa9e4066Sahrens 2611*d41c4376SMark J Musante is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2612*d41c4376SMark J Musante if (zoned != NULL) 2613*d41c4376SMark J Musante *zoned = is_zoned; 2614*d41c4376SMark J Musante 2615fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2616*d41c4376SMark J Musante if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 261799653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26187f7322feSeschrock zfs_close(zhp); 2619fa9e4066Sahrens return (-1); 2620fa9e4066Sahrens } 2621fa9e4066Sahrens 2622fa9e4066Sahrens /* make sure parent is a filesystem */ 26237f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 262499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 262599653d4eSeschrock "parent is not a filesystem")); 262699653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26277f7322feSeschrock zfs_close(zhp); 2628fa9e4066Sahrens return (-1); 2629fa9e4066Sahrens } 2630fa9e4066Sahrens 26317f7322feSeschrock zfs_close(zhp); 26327f1f55eaSvb160487 if (prefixlen != NULL) 26337f1f55eaSvb160487 *prefixlen = strlen(parent); 26347f1f55eaSvb160487 return (0); 26357f1f55eaSvb160487 } 26367f1f55eaSvb160487 26377f1f55eaSvb160487 /* 26387f1f55eaSvb160487 * Finds whether the dataset of the given type(s) exists. 26397f1f55eaSvb160487 */ 26407f1f55eaSvb160487 boolean_t 26417f1f55eaSvb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26427f1f55eaSvb160487 { 26437f1f55eaSvb160487 zfs_handle_t *zhp; 26447f1f55eaSvb160487 2645f18faf3fSek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26467f1f55eaSvb160487 return (B_FALSE); 26477f1f55eaSvb160487 26487f1f55eaSvb160487 /* 26497f1f55eaSvb160487 * Try to get stats for the dataset, which will tell us if it exists. 26507f1f55eaSvb160487 */ 26517f1f55eaSvb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26527f1f55eaSvb160487 int ds_type = zhp->zfs_type; 26537f1f55eaSvb160487 26547f1f55eaSvb160487 zfs_close(zhp); 26557f1f55eaSvb160487 if (types & ds_type) 26567f1f55eaSvb160487 return (B_TRUE); 26577f1f55eaSvb160487 } 26587f1f55eaSvb160487 return (B_FALSE); 26597f1f55eaSvb160487 } 26607f1f55eaSvb160487 26617f1f55eaSvb160487 /* 26623cb34c60Sahrens * Given a path to 'target', create all the ancestors between 26633cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 26643cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26653cb34c60Sahrens */ 26663cb34c60Sahrens int 26673cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26683cb34c60Sahrens { 26693cb34c60Sahrens zfs_handle_t *h; 26703cb34c60Sahrens char *cp; 26713cb34c60Sahrens const char *opname; 26723cb34c60Sahrens 26733cb34c60Sahrens /* make sure prefix exists */ 26743cb34c60Sahrens cp = target + prefixlen; 26753cb34c60Sahrens if (*cp != '/') { 26763cb34c60Sahrens assert(strchr(cp, '/') == NULL); 26773cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26783cb34c60Sahrens } else { 26793cb34c60Sahrens *cp = '\0'; 26803cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26813cb34c60Sahrens *cp = '/'; 26823cb34c60Sahrens } 26833cb34c60Sahrens if (h == NULL) 26843cb34c60Sahrens return (-1); 26853cb34c60Sahrens zfs_close(h); 26863cb34c60Sahrens 26873cb34c60Sahrens /* 26883cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26893cb34c60Sahrens * up to the prefixlen-long one. 26903cb34c60Sahrens */ 26913cb34c60Sahrens for (cp = target + prefixlen + 1; 26923cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 26933cb34c60Sahrens char *logstr; 26943cb34c60Sahrens 26953cb34c60Sahrens *cp = '\0'; 26963cb34c60Sahrens 26973cb34c60Sahrens h = make_dataset_handle(hdl, target); 26983cb34c60Sahrens if (h) { 26993cb34c60Sahrens /* it already exists, nothing to do here */ 27003cb34c60Sahrens zfs_close(h); 27013cb34c60Sahrens continue; 27023cb34c60Sahrens } 27033cb34c60Sahrens 27043cb34c60Sahrens logstr = hdl->libzfs_log_str; 27053cb34c60Sahrens hdl->libzfs_log_str = NULL; 27063cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27073cb34c60Sahrens NULL) != 0) { 27083cb34c60Sahrens hdl->libzfs_log_str = logstr; 27093cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27103cb34c60Sahrens goto ancestorerr; 27113cb34c60Sahrens } 27123cb34c60Sahrens 27133cb34c60Sahrens hdl->libzfs_log_str = logstr; 27143cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27153cb34c60Sahrens if (h == NULL) { 27163cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27173cb34c60Sahrens goto ancestorerr; 27183cb34c60Sahrens } 27193cb34c60Sahrens 27203cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27213cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27223cb34c60Sahrens goto ancestorerr; 27233cb34c60Sahrens } 27243cb34c60Sahrens 27253cb34c60Sahrens if (zfs_share(h) != 0) { 27263cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27273cb34c60Sahrens goto ancestorerr; 27283cb34c60Sahrens } 27293cb34c60Sahrens 27303cb34c60Sahrens zfs_close(h); 27313cb34c60Sahrens } 27323cb34c60Sahrens 27333cb34c60Sahrens return (0); 27343cb34c60Sahrens 27353cb34c60Sahrens ancestorerr: 27363cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27373cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 27383cb34c60Sahrens return (-1); 27393cb34c60Sahrens } 27403cb34c60Sahrens 27413cb34c60Sahrens /* 27427f1f55eaSvb160487 * Creates non-existing ancestors of the given path. 27437f1f55eaSvb160487 */ 27447f1f55eaSvb160487 int 27457f1f55eaSvb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27467f1f55eaSvb160487 { 27477f1f55eaSvb160487 int prefix; 27487f1f55eaSvb160487 char *path_copy; 27497f1f55eaSvb160487 int rc; 27507f1f55eaSvb160487 2751*d41c4376SMark J Musante if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27527f1f55eaSvb160487 return (-1); 27537f1f55eaSvb160487 27547f1f55eaSvb160487 if ((path_copy = strdup(path)) != NULL) { 27557f1f55eaSvb160487 rc = create_parents(hdl, path_copy, prefix); 27567f1f55eaSvb160487 free(path_copy); 27577f1f55eaSvb160487 } 27587f1f55eaSvb160487 if (path_copy == NULL || rc != 0) 27597f1f55eaSvb160487 return (-1); 27607f1f55eaSvb160487 2761fa9e4066Sahrens return (0); 2762fa9e4066Sahrens } 2763fa9e4066Sahrens 2764fa9e4066Sahrens /* 2765e9dbad6fSeschrock * Create a new filesystem or volume. 2766fa9e4066Sahrens */ 2767fa9e4066Sahrens int 276899653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2769e9dbad6fSeschrock nvlist_t *props) 2770fa9e4066Sahrens { 2771fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2772fa9e4066Sahrens int ret; 2773fa9e4066Sahrens uint64_t size = 0; 2774fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 277599653d4eSeschrock char errbuf[1024]; 2776e9dbad6fSeschrock uint64_t zoned; 277799653d4eSeschrock 277899653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 277999653d4eSeschrock "cannot create '%s'"), path); 2780fa9e4066Sahrens 2781fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2782f18faf3fSek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 278399653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2784fa9e4066Sahrens 2785fa9e4066Sahrens /* validate parents exist */ 27867f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2787fa9e4066Sahrens return (-1); 2788fa9e4066Sahrens 2789fa9e4066Sahrens /* 2790fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2791fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2792fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2793fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2794fa9e4066Sahrens * first try to see if the dataset exists. 2795fa9e4066Sahrens */ 2796fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2797990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 279899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 279999653d4eSeschrock "dataset already exists")); 280099653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2801fa9e4066Sahrens } 2802fa9e4066Sahrens 2803fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2804fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2805fa9e4066Sahrens else 2806fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2807fa9e4066Sahrens 28080a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2809b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2810e9dbad6fSeschrock return (-1); 2811e9dbad6fSeschrock 2812fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 28135c5460e9Seschrock /* 28145c5460e9Seschrock * If we are creating a volume, the size and block size must 28155c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 28165c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28175c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 28185c5460e9Seschrock * zero. 28195c5460e9Seschrock */ 2820e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2821e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2822e9dbad6fSeschrock nvlist_free(props); 282399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2824e9dbad6fSeschrock "missing volume size")); 2825e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2826fa9e4066Sahrens } 2827fa9e4066Sahrens 2828e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2829e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2830e9dbad6fSeschrock &blocksize)) != 0) { 2831e9dbad6fSeschrock if (ret == ENOENT) { 2832e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2833e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2834e9dbad6fSeschrock } else { 2835e9dbad6fSeschrock nvlist_free(props); 283699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2837e9dbad6fSeschrock "missing volume block size")); 2838e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2839e9dbad6fSeschrock } 2840e9dbad6fSeschrock } 2841e9dbad6fSeschrock 2842e9dbad6fSeschrock if (size == 0) { 2843e9dbad6fSeschrock nvlist_free(props); 2844e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2845e9dbad6fSeschrock "volume size cannot be zero")); 2846e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28475c5460e9Seschrock } 28485c5460e9Seschrock 28495c5460e9Seschrock if (size % blocksize != 0) { 2850e9dbad6fSeschrock nvlist_free(props); 285199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2852e9dbad6fSeschrock "volume size must be a multiple of volume block " 2853e9dbad6fSeschrock "size")); 2854e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2855e9dbad6fSeschrock } 28565c5460e9Seschrock } 28575c5460e9Seschrock 2858990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2859e9dbad6fSeschrock return (-1); 2860e9dbad6fSeschrock nvlist_free(props); 2861fa9e4066Sahrens 2862fa9e4066Sahrens /* create the dataset */ 2863ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2864fa9e4066Sahrens 2865e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2866e9dbad6fSeschrock 2867fa9e4066Sahrens /* check for failure */ 2868fa9e4066Sahrens if (ret != 0) { 2869fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2870fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2871fa9e4066Sahrens 2872fa9e4066Sahrens switch (errno) { 2873fa9e4066Sahrens case ENOENT: 287499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 287599653d4eSeschrock "no such parent '%s'"), parent); 287699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2877fa9e4066Sahrens 2878fa9e4066Sahrens case EINVAL: 287999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2880d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 288199653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2882fa9e4066Sahrens 2883fa9e4066Sahrens case EDOM: 288499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2885e9dbad6fSeschrock "volume block size must be power of 2 from " 2886e9dbad6fSeschrock "%u to %uk"), 2887fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2888fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 288999653d4eSeschrock 2890e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 289199653d4eSeschrock 289240feaa91Sahrens case ENOTSUP: 289340feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 289440feaa91Sahrens "pool must be upgraded to set this " 289540feaa91Sahrens "property or value")); 289640feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2897fa9e4066Sahrens #ifdef _ILP32 2898fa9e4066Sahrens case EOVERFLOW: 2899fa9e4066Sahrens /* 2900fa9e4066Sahrens * This platform can't address a volume this big. 2901fa9e4066Sahrens */ 290299653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 290399653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 290499653d4eSeschrock errbuf)); 2905fa9e4066Sahrens #endif 290699653d4eSeschrock /* FALLTHROUGH */ 2907fa9e4066Sahrens default: 290899653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2909fa9e4066Sahrens } 2910fa9e4066Sahrens } 2911fa9e4066Sahrens 2912fa9e4066Sahrens return (0); 2913fa9e4066Sahrens } 2914fa9e4066Sahrens 2915fa9e4066Sahrens /* 2916fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2917fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2918fa9e4066Sahrens */ 2919fa9e4066Sahrens int 2920842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2921fa9e4066Sahrens { 2922fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2923fa9e4066Sahrens 2924fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2925fa9e4066Sahrens 2926e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2927f3861e1aSahl /* 2928ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2929ecd6cf80Smarks * abort the request. This would only happen for a 2930ecd6cf80Smarks * non-privileged user. 2931f3861e1aSahl */ 2932ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2933ecd6cf80Smarks return (-1); 2934ecd6cf80Smarks } 2935f3861e1aSahl 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 */ 3051f18faf3fSek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 305299653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3053fa9e4066Sahrens 3054fa9e4066Sahrens /* validate parents exist */ 30557f1f55eaSvb160487 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; 30635f8e1617Snn35248 type = ZFS_TYPE_VOLUME; 3064e9dbad6fSeschrock } else { 3065fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30665f8e1617Snn35248 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)); 311099653d4eSeschrock 311199653d4eSeschrock default: 311299653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 311399653d4eSeschrock errbuf)); 311499653d4eSeschrock } 311599653d4eSeschrock } 311699653d4eSeschrock 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; 3155fa9e4066Sahrens 31560b69c2f0Sahrens switch (save_errno) { 3157fa9e4066Sahrens 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 */ 3189f18faf3fSek110237 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) { 32321d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3233e9dbad6fSeschrock "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 /* 33024ccbb6e7Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33034ccbb6e7Sahrens * data changes since then and making it the active dataset. 33044ccbb6e7Sahrens * 33054ccbb6e7Sahrens * Any snapshots more recent than the target are destroyed, along with 33064ccbb6e7Sahrens * their dependents. 3307b12a1c38Slling */ 33084ccbb6e7Sahrens int 3309c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3310fa9e4066Sahrens { 33114ccbb6e7Sahrens rollback_data_t cb = { 0 }; 33124ccbb6e7Sahrens int err; 3313fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 33147b97dc1aSrm160521 boolean_t restore_resv = 0; 33157b97dc1aSrm160521 uint64_t old_volsize, new_volsize; 33167b97dc1aSrm160521 zfs_prop_t resv_prop; 3317fa9e4066Sahrens 3318fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3319fa9e4066Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3320fa9e4066Sahrens 33214ccbb6e7Sahrens /* 33224ccbb6e7Sahrens * Destroy all recent snapshots and its dependends. 33234ccbb6e7Sahrens */ 3324c391e322Sahrens cb.cb_force = force; 33254ccbb6e7Sahrens cb.cb_target = snap->zfs_name; 33264ccbb6e7Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33274ccbb6e7Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33284ccbb6e7Sahrens 3329c391e322Sahrens if (cb.cb_error) 3330c391e322Sahrens return (-1); 33314ccbb6e7Sahrens 33324ccbb6e7Sahrens /* 33334ccbb6e7Sahrens * Now that we have verified that the snapshot is the latest, 33344ccbb6e7Sahrens * rollback to the given snapshot. 33354ccbb6e7Sahrens */ 33364ccbb6e7Sahrens 33377b97dc1aSrm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33387b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33397b97dc1aSrm160521 return (-1); 33407b97dc1aSrm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33417b97dc1aSrm160521 restore_resv = 33427b97dc1aSrm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33437b97dc1aSrm160521 } 3344fa9e4066Sahrens 3345fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3346fa9e4066Sahrens 3347e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3348fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3349fa9e4066Sahrens else 3350fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3351fa9e4066Sahrens 3352fa9e4066Sahrens /* 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. 33587b97dc1aSrm160521 * 3359fa9e4066Sahrens */ 33604ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3361ece3d9b3Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 336299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 336399653d4eSeschrock zhp->zfs_name); 3364b9415e83Srm160521 return (err); 3365b9415e83Srm160521 } 3366fa9e4066Sahrens 33677b97dc1aSrm160521 /* 33687b97dc1aSrm160521 * For volumes, if the pre-rollback volsize matched the pre- 33697b97dc1aSrm160521 * rollback reservation and the volsize has changed then set 33707b97dc1aSrm160521 * the reservation property to the post-rollback volsize. 33717b97dc1aSrm160521 * Make a new handle since the rollback closed the dataset. 33727b97dc1aSrm160521 */ 3373b9415e83Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3374b9415e83Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 33757b97dc1aSrm160521 if (restore_resv) { 33767b97dc1aSrm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33777b97dc1aSrm160521 if (old_volsize != new_volsize) 3378b9415e83Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 3379b9415e83Srm160521 new_volsize); 33807b97dc1aSrm160521 } 33817b97dc1aSrm160521 zfs_close(zhp); 33827b97dc1aSrm160521 } 33834ccbb6e7Sahrens return (err); 3384b12a1c38Slling } 3385b12a1c38Slling 3386b12a1c38Slling /* 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 34267f1f55eaSvb160487 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) { 344998579b20Snd150628 if ((strchr(target, '@') == NULL) || 345098579b20Snd150628 *target == '@') { 345198579b20Snd150628 /* 345298579b20Snd150628 * Snapshot target name is abbreviated, 345398579b20Snd150628 * reconstruct full dataset name 345498579b20Snd150628 */ 345598579b20Snd150628 (void) strlcpy(parent, zhp->zfs_name, 345698579b20Snd150628 sizeof (parent)); 345798579b20Snd150628 delim = strchr(parent, '@'); 345898579b20Snd150628 if (strchr(target, '@') == NULL) 345998579b20Snd150628 *(++delim) = '\0'; 346098579b20Snd150628 else 346198579b20Snd150628 *delim = '\0'; 346298579b20Snd150628 (void) strlcat(parent, target, sizeof (parent)); 346398579b20Snd150628 target = parent; 346498579b20Snd150628 } else { 3465fa9e4066Sahrens /* 3466fa9e4066Sahrens * Make sure we're renaming within the same dataset. 3467fa9e4066Sahrens */ 346898579b20Snd150628 delim = strchr(target, '@'); 346998579b20Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 347098579b20Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 347199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 347298579b20Snd150628 "snapshots must be part of same " 347398579b20Snd150628 "dataset")); 347498579b20Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 347598579b20Snd150628 errbuf)); 3476fa9e4066Sahrens } 347798579b20Snd150628 } 3478f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 347998579b20Snd150628 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 3487f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 348898579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3489e9dbad6fSeschrock 3490fa9e4066Sahrens /* validate parents */ 3491*d41c4376SMark 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 } 3502f2fdf992Snd150628 3503f2fdf992Snd150628 /* new name cannot be a child of the current dataset name */ 3504*d41c4376SMark J Musante if (is_descendant(zhp->zfs_name, target)) { 3505f2fdf992Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3506*d41c4376SMark J Musante "New dataset name cannot be a descendant of " 3507f2fdf992Snd150628 "current dataset name")); 3508f2fdf992Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3509f2fdf992Snd150628 } 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) { 3523cdf5b4caSmmusante 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 } 3536cdf5b4caSmmusante 3537cdf5b4caSmmusante } else { 35380069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 353999653d4eSeschrock return (-1); 3540fa9e4066Sahrens 3541fa9e4066Sahrens if (changelist_haszonedchild(cl)) { 354299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 354399653d4eSeschrock "child dataset with inherited mountpoint is used " 354499653d4eSeschrock "in a non-global zone")); 3545e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3546fa9e4066Sahrens goto error; 3547fa9e4066Sahrens } 3548fa9e4066Sahrens 3549fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 3550fa9e4066Sahrens 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 355898579b20Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3559e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 356098579b20Snd150628 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 { 357799653d4eSeschrock (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) 3585fa9e4066Sahrens (void) changelist_postfix(cl); 3586cdf5b4caSmmusante } else { 3587681d9761SEric Taylor if (!recursive) { 3588fa9e4066Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 3589fa9e4066Sahrens ret = changelist_postfix(cl); 3590fa9e4066Sahrens } 3591cdf5b4caSmmusante } 3592fa9e4066Sahrens 3593fa9e4066Sahrens error: 3594cdf5b4caSmmusante if (parentname) { 3595cdf5b4caSmmusante free(parentname); 3596cdf5b4caSmmusante } 3597cdf5b4caSmmusante if (zhrp) { 3598cdf5b4caSmmusante zfs_close(zhrp); 3599cdf5b4caSmmusante } 3600cdf5b4caSmmusante if (cl) { 3601fa9e4066Sahrens 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 3621e9dbad6fSeschrock /* 3622e9dbad6fSeschrock * This function is used by 'zfs list' to determine the exact set of columns to 3623e9dbad6fSeschrock * display, and their maximum widths. This does two main things: 3624e9dbad6fSeschrock * 3625e9dbad6fSeschrock * - If this is a list of all properties, then expand the list to include 3626e9dbad6fSeschrock * all native properties, and set a flag so that for each dataset we look 3627e9dbad6fSeschrock * for new unique user properties and add them to the list. 3628e9dbad6fSeschrock * 3629e9dbad6fSeschrock * - 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. 3633e9dbad6fSeschrock */ 3634e9dbad6fSeschrock int 363592241e0bSTom Erickson zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 3636e9dbad6fSeschrock { 3637e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3638990b4856Slling zprop_list_t *entry; 3639990b4856Slling zprop_list_t **last, **start; 3640e9dbad6fSeschrock nvlist_t *userprops, *propval; 3641e9dbad6fSeschrock nvpair_t *elem; 3642e9dbad6fSeschrock char *strval; 3643e9dbad6fSeschrock char buf[ZFS_MAXPROPLEN]; 3644e9dbad6fSeschrock 3645990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3646e9dbad6fSeschrock 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) { 3714e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3715990b4856Slling ZPROP_VALUE, &strval) == 0); 3716e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3717e9dbad6fSeschrock entry->pl_width = strlen(strval); 3718e9dbad6fSeschrock } 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); 372492241e0bSTom Erickson } 3725e9dbad6fSeschrock } 3726e9dbad6fSeschrock 3727e9dbad6fSeschrock return (0); 3728e9dbad6fSeschrock } 3729ecd6cf80Smarks 3730ecd6cf80Smarks int 3731ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 3732ecd6cf80Smarks { 3733ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3734ecd6cf80Smarks nvlist_t *nvp; 3735ecd6cf80Smarks gid_t gid; 3736ecd6cf80Smarks uid_t uid; 3737ecd6cf80Smarks const gid_t *groups; 3738ecd6cf80Smarks int group_cnt; 3739ecd6cf80Smarks int error; 3740ecd6cf80Smarks 3741ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 3742ecd6cf80Smarks return (no_memory(hdl)); 3743ecd6cf80Smarks 3744ecd6cf80Smarks uid = ucred_geteuid(cred); 3745ecd6cf80Smarks gid = ucred_getegid(cred); 3746ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 3747ecd6cf80Smarks 3748ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 3749ecd6cf80Smarks return (1); 3750ecd6cf80Smarks 3751ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 3752ecd6cf80Smarks nvlist_free(nvp); 3753ecd6cf80Smarks return (1); 3754ecd6cf80Smarks } 3755ecd6cf80Smarks 3756ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 3757ecd6cf80Smarks nvlist_free(nvp); 3758ecd6cf80Smarks return (1); 3759ecd6cf80Smarks } 3760ecd6cf80Smarks 3761ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 3762ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 3763ecd6cf80Smarks nvlist_free(nvp); 3764ecd6cf80Smarks return (1); 3765ecd6cf80Smarks } 3766ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3767ecd6cf80Smarks 3768990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 3769ecd6cf80Smarks return (-1); 3770ecd6cf80Smarks 3771ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 3772ecd6cf80Smarks nvlist_free(nvp); 3773ecd6cf80Smarks return (error); 3774ecd6cf80Smarks } 3775ecd6cf80Smarks 3776ecd6cf80Smarks int 3777ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3778743a77edSAlan Wright char *resource, void *export, void *sharetab, 3779743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3780ecd6cf80Smarks { 3781ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3782ecd6cf80Smarks int error; 3783ecd6cf80Smarks 3784ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3785ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3786743a77edSAlan Wright if (resource) 3787743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3788ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3789ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3790da6c28aaSamw zc.zc_share.z_sharetype = operation; 3791ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3792ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3793ecd6cf80Smarks return (error); 3794ecd6cf80Smarks } 37952e5e9e19SSanjeev Bagewadi 37962e5e9e19SSanjeev Bagewadi void 37972e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37982e5e9e19SSanjeev Bagewadi { 37992e5e9e19SSanjeev Bagewadi nvpair_t *curr; 38002e5e9e19SSanjeev Bagewadi 38012e5e9e19SSanjeev Bagewadi /* 38022e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 38032e5e9e19SSanjeev Bagewadi * properties. 38042e5e9e19SSanjeev Bagewadi */ 38052e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 38062e5e9e19SSanjeev Bagewadi 38072e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 38082e5e9e19SSanjeev Bagewadi 38092e5e9e19SSanjeev Bagewadi while (curr) { 38102e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 38112e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 38122e5e9e19SSanjeev Bagewadi 381314843421SMatthew Ahrens /* 3814faaa6415SEric Schrock * User properties will result in ZPROP_INVAL, and since we 3815faaa6415SEric Schrock * only know how to prune standard ZFS properties, we always 3816faaa6415SEric Schrock * leave these in the list. This can also happen if we 3817faaa6415SEric Schrock * encounter an unknown DSL property (when running older 3818faaa6415SEric Schrock * software, for example). 381914843421SMatthew Ahrens */ 382014843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 38212e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 38222e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 38232e5e9e19SSanjeev Bagewadi curr = next; 38242e5e9e19SSanjeev Bagewadi } 38252e5e9e19SSanjeev Bagewadi } 3826743a77edSAlan Wright 3827743a77edSAlan Wright static int 3828743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3829743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3830743a77edSAlan Wright { 3831743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3832743a77edSAlan Wright nvlist_t *nvlist = NULL; 3833743a77edSAlan Wright int error; 3834743a77edSAlan Wright 3835743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3836743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3837743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3838743a77edSAlan Wright 3839743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3840743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3841743a77edSAlan Wright (void) no_memory(hdl); 3842743a77edSAlan Wright return (NULL); 3843743a77edSAlan Wright } 3844743a77edSAlan Wright } 3845743a77edSAlan Wright 3846743a77edSAlan Wright switch (cmd) { 3847743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3848743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3849743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3850743a77edSAlan Wright break; 3851743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3852743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3853743a77edSAlan Wright resource1) != 0) { 3854743a77edSAlan Wright (void) no_memory(hdl); 3855743a77edSAlan Wright return (-1); 3856743a77edSAlan Wright } 3857743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 3858743a77edSAlan Wright resource2) != 0) { 3859743a77edSAlan Wright (void) no_memory(hdl); 3860743a77edSAlan Wright return (-1); 3861743a77edSAlan Wright } 3862743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 3863743a77edSAlan Wright nvlist_free(nvlist); 3864743a77edSAlan Wright return (-1); 3865743a77edSAlan Wright } 3866743a77edSAlan Wright break; 3867743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3868743a77edSAlan Wright break; 3869743a77edSAlan Wright default: 3870743a77edSAlan Wright return (-1); 3871743a77edSAlan Wright } 3872743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 3873743a77edSAlan Wright if (nvlist) 3874743a77edSAlan Wright nvlist_free(nvlist); 3875743a77edSAlan Wright return (error); 3876743a77edSAlan Wright } 3877743a77edSAlan Wright 3878743a77edSAlan Wright int 3879743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 3880743a77edSAlan Wright char *path, char *resource) 3881743a77edSAlan Wright { 3882743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 3883743a77edSAlan Wright resource, NULL)); 3884743a77edSAlan Wright } 3885743a77edSAlan Wright 3886743a77edSAlan Wright int 3887743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 3888743a77edSAlan Wright char *path, char *resource) 3889743a77edSAlan Wright { 3890743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 3891743a77edSAlan Wright resource, NULL)); 3892743a77edSAlan Wright } 3893743a77edSAlan Wright 3894743a77edSAlan Wright int 3895743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 3896743a77edSAlan Wright { 3897743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 3898743a77edSAlan Wright NULL, NULL)); 3899743a77edSAlan Wright } 3900743a77edSAlan Wright 3901743a77edSAlan Wright int 3902743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 3903743a77edSAlan Wright char *oldname, char *newname) 3904743a77edSAlan Wright { 3905743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 3906743a77edSAlan Wright oldname, newname)); 3907743a77edSAlan Wright } 390814843421SMatthew Ahrens 390914843421SMatthew Ahrens int 391014843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 391114843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 391214843421SMatthew Ahrens { 391314843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 391414843421SMatthew Ahrens int error; 391514843421SMatthew Ahrens zfs_useracct_t buf[100]; 391614843421SMatthew Ahrens 391714843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 391814843421SMatthew Ahrens 391914843421SMatthew Ahrens zc.zc_objset_type = type; 392014843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 392114843421SMatthew Ahrens 392214843421SMatthew Ahrens /* CONSTCOND */ 392314843421SMatthew Ahrens while (1) { 392414843421SMatthew Ahrens zfs_useracct_t *zua = buf; 392514843421SMatthew Ahrens 392614843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 392714843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 392814843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 392914843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 393014843421SMatthew Ahrens break; 393114843421SMatthew Ahrens 393214843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 39330aea4b19SMatthew Ahrens error = func(arg, zua->zu_domain, zua->zu_rid, 39340aea4b19SMatthew Ahrens zua->zu_space); 39350aea4b19SMatthew Ahrens if (error != 0) 39360aea4b19SMatthew Ahrens return (error); 393714843421SMatthew Ahrens zua++; 393814843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 393914843421SMatthew Ahrens } 394014843421SMatthew Ahrens } 394114843421SMatthew Ahrens 394214843421SMatthew Ahrens return (error); 394314843421SMatthew Ahrens } 3944842727c2SChris Kirby 3945842727c2SChris Kirby int 3946842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 3947818119b8SChris Kirby boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 3948842727c2SChris Kirby { 3949842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 3950842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 3951842727c2SChris Kirby 3952842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3953842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 3954ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 3955ca45db41SChris Kirby >= sizeof (zc.zc_string)) 3956ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 3957842727c2SChris Kirby zc.zc_cookie = recursive; 3958ca45db41SChris Kirby zc.zc_temphold = temphold; 3959842727c2SChris Kirby 3960842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 3961842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 3962842727c2SChris Kirby 3963842727c2SChris Kirby /* 3964842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 3965842727c2SChris Kirby * zc.zc_name. 3966842727c2SChris Kirby */ 3967842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3968842727c2SChris Kirby "cannot hold '%s@%s'"), zc.zc_name, snapname); 3969842727c2SChris Kirby switch (errno) { 397015508ac0SChris Kirby case E2BIG: 397115508ac0SChris Kirby /* 397215508ac0SChris Kirby * Temporary tags wind up having the ds object id 397315508ac0SChris Kirby * prepended. So even if we passed the length check 397415508ac0SChris Kirby * above, it's still possible for the tag to wind 397515508ac0SChris Kirby * up being slightly too long. 397615508ac0SChris Kirby */ 397715508ac0SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 3978842727c2SChris Kirby case ENOTSUP: 3979842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3980842727c2SChris Kirby "pool must be upgraded")); 3981842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3982842727c2SChris Kirby case EINVAL: 3983842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3984842727c2SChris Kirby case EEXIST: 3985842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 3986818119b8SChris Kirby case ENOENT: 3987818119b8SChris Kirby if (enoent_ok) 3988818119b8SChris Kirby return (0); 3989818119b8SChris Kirby /* FALLTHROUGH */ 3990842727c2SChris Kirby default: 3991842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 3992842727c2SChris Kirby } 3993842727c2SChris Kirby } 3994842727c2SChris Kirby 3995842727c2SChris Kirby return (0); 3996842727c2SChris Kirby } 3997842727c2SChris Kirby 3998ca45db41SChris Kirby struct hold_range_arg { 3999ca45db41SChris Kirby zfs_handle_t *origin; 4000ca45db41SChris Kirby const char *fromsnap; 4001ca45db41SChris Kirby const char *tosnap; 4002ca45db41SChris Kirby char lastsnapheld[ZFS_MAXNAMELEN]; 4003ca45db41SChris Kirby const char *tag; 4004ca45db41SChris Kirby boolean_t temphold; 4005ca45db41SChris Kirby boolean_t seento; 4006ca45db41SChris Kirby boolean_t seenfrom; 4007ca45db41SChris Kirby boolean_t holding; 4008ca45db41SChris Kirby }; 4009ca45db41SChris Kirby 4010ca45db41SChris Kirby static int 4011ca45db41SChris Kirby zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 4012ca45db41SChris Kirby { 4013ca45db41SChris Kirby struct hold_range_arg *hra = arg; 4014ca45db41SChris Kirby const char *thissnap; 4015ca45db41SChris Kirby int error; 4016ca45db41SChris Kirby 4017ca45db41SChris Kirby thissnap = strchr(zfs_get_name(zhp), '@') + 1; 4018ca45db41SChris Kirby 4019ca45db41SChris Kirby if (hra->fromsnap && !hra->seenfrom && 4020ca45db41SChris Kirby strcmp(hra->fromsnap, thissnap) == 0) 4021ca45db41SChris Kirby hra->seenfrom = B_TRUE; 4022ca45db41SChris Kirby 4023ca45db41SChris Kirby /* snap is older or newer than the desired range, ignore it */ 4024ca45db41SChris Kirby if (hra->seento || !hra->seenfrom) { 4025ca45db41SChris Kirby zfs_close(zhp); 4026ca45db41SChris Kirby return (0); 4027ca45db41SChris Kirby } 4028ca45db41SChris Kirby 4029ca45db41SChris Kirby if (hra->holding) { 4030818119b8SChris Kirby /* We could be racing with destroy, so ignore ENOENT. */ 4031ca45db41SChris Kirby error = zfs_hold(hra->origin, thissnap, hra->tag, B_FALSE, 4032818119b8SChris Kirby hra->temphold, B_TRUE); 4033ca45db41SChris Kirby if (error == 0) { 4034ca45db41SChris Kirby (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 4035ca45db41SChris Kirby sizeof (hra->lastsnapheld)); 4036ca45db41SChris Kirby } 4037ca45db41SChris Kirby } else { 4038ca45db41SChris Kirby error = zfs_release(hra->origin, thissnap, hra->tag, B_FALSE); 4039ca45db41SChris Kirby } 4040ca45db41SChris Kirby 4041ca45db41SChris Kirby if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 4042ca45db41SChris Kirby hra->seento = B_TRUE; 4043ca45db41SChris Kirby 4044ca45db41SChris Kirby zfs_close(zhp); 4045ca45db41SChris Kirby return (error); 4046ca45db41SChris Kirby } 4047ca45db41SChris Kirby 4048ca45db41SChris Kirby /* 4049ca45db41SChris Kirby * Add a user hold on the set of snapshots starting with fromsnap up to 4050ca45db41SChris Kirby * and including tosnap. If we're unable to to acquire a particular hold, 4051ca45db41SChris Kirby * undo any holds up to that point. 4052ca45db41SChris Kirby */ 4053ca45db41SChris Kirby int 4054ca45db41SChris Kirby zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 4055ca45db41SChris Kirby const char *tag, boolean_t temphold) 4056ca45db41SChris Kirby { 4057ca45db41SChris Kirby struct hold_range_arg arg = { 0 }; 4058ca45db41SChris Kirby int error; 4059ca45db41SChris Kirby 4060ca45db41SChris Kirby arg.origin = zhp; 4061ca45db41SChris Kirby arg.fromsnap = fromsnap; 4062ca45db41SChris Kirby arg.tosnap = tosnap; 4063ca45db41SChris Kirby arg.tag = tag; 4064ca45db41SChris Kirby arg.temphold = temphold; 4065ca45db41SChris Kirby arg.holding = B_TRUE; 4066ca45db41SChris Kirby 4067ca45db41SChris Kirby error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 4068ca45db41SChris Kirby 4069ca45db41SChris Kirby /* 4070ca45db41SChris Kirby * Make sure we either hold the entire range or none. 4071ca45db41SChris Kirby */ 4072ca45db41SChris Kirby if (error && arg.lastsnapheld[0] != '\0') { 4073ca45db41SChris Kirby (void) zfs_release_range(zhp, fromsnap, 4074ca45db41SChris Kirby (const char *)arg.lastsnapheld, tag); 4075ca45db41SChris Kirby } 4076ca45db41SChris Kirby return (error); 4077ca45db41SChris Kirby } 4078ca45db41SChris Kirby 4079842727c2SChris Kirby int 4080842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4081842727c2SChris Kirby boolean_t recursive) 4082842727c2SChris Kirby { 4083842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 4084842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 4085842727c2SChris Kirby 4086842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4087842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4088ca45db41SChris Kirby if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 4089ca45db41SChris Kirby >= sizeof (zc.zc_string)) 4090ca45db41SChris Kirby return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 4091842727c2SChris Kirby zc.zc_cookie = recursive; 4092842727c2SChris Kirby 4093842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 4094842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 4095842727c2SChris Kirby 4096842727c2SChris Kirby /* 4097842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 4098842727c2SChris Kirby * zc.zc_name. 4099842727c2SChris Kirby */ 4100842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4101842727c2SChris Kirby "cannot release '%s@%s'"), zc.zc_name, snapname); 4102842727c2SChris Kirby switch (errno) { 4103842727c2SChris Kirby case ESRCH: 4104842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 4105842727c2SChris Kirby case ENOTSUP: 4106842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4107842727c2SChris Kirby "pool must be upgraded")); 4108842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4109842727c2SChris Kirby case EINVAL: 4110842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4111842727c2SChris Kirby default: 4112842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4113842727c2SChris Kirby } 4114842727c2SChris Kirby } 4115842727c2SChris Kirby 4116842727c2SChris Kirby return (0); 4117842727c2SChris Kirby } 4118ca45db41SChris Kirby 4119ca45db41SChris Kirby /* 4120ca45db41SChris Kirby * Release a user hold from the set of snapshots starting with fromsnap 4121ca45db41SChris Kirby * up to and including tosnap. 4122ca45db41SChris Kirby */ 4123ca45db41SChris Kirby int 4124ca45db41SChris Kirby zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 4125ca45db41SChris Kirby const char *tag) 4126ca45db41SChris Kirby { 4127ca45db41SChris Kirby struct hold_range_arg arg = { 0 }; 4128ca45db41SChris Kirby 4129ca45db41SChris Kirby arg.origin = zhp; 4130ca45db41SChris Kirby arg.fromsnap = fromsnap; 4131ca45db41SChris Kirby arg.tosnap = tosnap; 4132ca45db41SChris Kirby arg.tag = tag; 4133ca45db41SChris Kirby 4134ca45db41SChris Kirby return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 4135ca45db41SChris Kirby } 4136c1449561SEric Taylor 4137c1449561SEric Taylor uint64_t 4138c1449561SEric Taylor zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4139c1449561SEric Taylor { 4140c1449561SEric Taylor uint64_t numdb; 4141c1449561SEric Taylor uint64_t nblocks, volblocksize; 4142c1449561SEric Taylor int ncopies; 4143c1449561SEric Taylor char *strval; 4144c1449561SEric Taylor 4145c1449561SEric Taylor if (nvlist_lookup_string(props, 4146c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4147c1449561SEric Taylor ncopies = atoi(strval); 4148c1449561SEric Taylor else 4149c1449561SEric Taylor ncopies = 1; 4150c1449561SEric Taylor if (nvlist_lookup_uint64(props, 4151c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4152c1449561SEric Taylor &volblocksize) != 0) 4153c1449561SEric Taylor volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4154c1449561SEric Taylor nblocks = volsize/volblocksize; 4155c1449561SEric Taylor /* start with metadnode L0-L6 */ 4156c1449561SEric Taylor numdb = 7; 4157c1449561SEric Taylor /* calculate number of indirects */ 4158c1449561SEric Taylor while (nblocks > 1) { 4159c1449561SEric Taylor nblocks += DNODES_PER_LEVEL - 1; 4160c1449561SEric Taylor nblocks /= DNODES_PER_LEVEL; 4161c1449561SEric Taylor numdb += nblocks; 4162c1449561SEric Taylor } 4163c1449561SEric Taylor numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4164c1449561SEric Taylor volsize *= ncopies; 4165c1449561SEric Taylor /* 4166c1449561SEric Taylor * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4167c1449561SEric Taylor * compressed, but in practice they compress down to about 4168c1449561SEric Taylor * 1100 bytes 4169c1449561SEric Taylor */ 4170c1449561SEric Taylor numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4171c1449561SEric Taylor volsize += numdb; 4172c1449561SEric Taylor return (volsize); 4173c1449561SEric Taylor } 4174