1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5ea8dc4b6Seschrock * Common Development and Distribution License (the "License"). 6ea8dc4b6Seschrock * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21f3861e1aSahl 22fa9e4066Sahrens /* 232e5e9e19SSanjeev Bagewadi * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #include <assert.h> 28fa9e4066Sahrens #include <ctype.h> 29fa9e4066Sahrens #include <errno.h> 30fa9e4066Sahrens #include <libdevinfo.h> 31fa9e4066Sahrens #include <libintl.h> 32fa9e4066Sahrens #include <math.h> 33fa9e4066Sahrens #include <stdio.h> 34fa9e4066Sahrens #include <stdlib.h> 35fa9e4066Sahrens #include <strings.h> 36fa9e4066Sahrens #include <unistd.h> 373cb34c60Sahrens #include <stddef.h> 38fa9e4066Sahrens #include <zone.h> 3999653d4eSeschrock #include <fcntl.h> 40fa9e4066Sahrens #include <sys/mntent.h> 41b12a1c38Slling #include <sys/mount.h> 42ecd6cf80Smarks #include <sys/avl.h> 43ecd6cf80Smarks #include <priv.h> 44ecd6cf80Smarks #include <pwd.h> 45ecd6cf80Smarks #include <grp.h> 46ecd6cf80Smarks #include <stddef.h> 47ecd6cf80Smarks #include <ucred.h> 4814843421SMatthew Ahrens #include <idmap.h> 4914843421SMatthew Ahrens #include <aclutils.h> 503b12c289SMatthew Ahrens #include <directory.h> 51fa9e4066Sahrens 52fa9e4066Sahrens #include <sys/spa.h> 53e9dbad6fSeschrock #include <sys/zap.h> 54fa9e4066Sahrens #include <libzfs.h> 55fa9e4066Sahrens 56fa9e4066Sahrens #include "zfs_namecheck.h" 57fa9e4066Sahrens #include "zfs_prop.h" 58fa9e4066Sahrens #include "libzfs_impl.h" 59ecd6cf80Smarks #include "zfs_deleg.h" 60fa9e4066Sahrens 61cdf5b4caSmmusante static int zvol_create_link_common(libzfs_handle_t *, const char *, int); 6214843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned, 6314843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 64cdf5b4caSmmusante 65fa9e4066Sahrens /* 66fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 67fa9e4066Sahrens * readable form. 68fa9e4066Sahrens */ 69fa9e4066Sahrens const char * 70fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 71fa9e4066Sahrens { 72fa9e4066Sahrens switch (type) { 73fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 74fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 75fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 76fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 77fa9e4066Sahrens case ZFS_TYPE_VOLUME: 78fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 79fa9e4066Sahrens } 80fa9e4066Sahrens 81fa9e4066Sahrens return (NULL); 82fa9e4066Sahrens } 83fa9e4066Sahrens 84fa9e4066Sahrens /* 85fa9e4066Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 86fa9e4066Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 87fa9e4066Sahrens * We guess what the type would have been based on the path and the mask of 88fa9e4066Sahrens * acceptable types. 89fa9e4066Sahrens */ 90fa9e4066Sahrens static const char * 91fa9e4066Sahrens path_to_str(const char *path, int types) 92fa9e4066Sahrens { 93fa9e4066Sahrens /* 94fa9e4066Sahrens * When given a single type, always report the exact type. 95fa9e4066Sahrens */ 96fa9e4066Sahrens if (types == ZFS_TYPE_SNAPSHOT) 97fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 98fa9e4066Sahrens if (types == ZFS_TYPE_FILESYSTEM) 99fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 100fa9e4066Sahrens if (types == ZFS_TYPE_VOLUME) 101fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 102fa9e4066Sahrens 103fa9e4066Sahrens /* 104fa9e4066Sahrens * The user is requesting more than one type of dataset. If this is the 105fa9e4066Sahrens * case, consult the path itself. If we're looking for a snapshot, and 106fa9e4066Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 107fa9e4066Sahrens * snapshot attribute and try again. 108fa9e4066Sahrens */ 109fa9e4066Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 110fa9e4066Sahrens if (strchr(path, '@') != NULL) 111fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 112fa9e4066Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 113fa9e4066Sahrens } 114fa9e4066Sahrens 115fa9e4066Sahrens /* 116fa9e4066Sahrens * The user has requested either filesystems or volumes. 117fa9e4066Sahrens * We have no way of knowing a priori what type this would be, so always 118fa9e4066Sahrens * report it as "filesystem" or "volume", our two primitive types. 119fa9e4066Sahrens */ 120fa9e4066Sahrens if (types & ZFS_TYPE_FILESYSTEM) 121fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 122fa9e4066Sahrens 123fa9e4066Sahrens assert(types & ZFS_TYPE_VOLUME); 124fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 125fa9e4066Sahrens } 126fa9e4066Sahrens 127fa9e4066Sahrens /* 128fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 12914843421SMatthew Ahrens * provide a more meaningful error message. We call zfs_error_aux() to 13014843421SMatthew Ahrens * explain exactly why the name was not valid. 131fa9e4066Sahrens */ 132fa9e4066Sahrens static int 133f18faf3fSek zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 134f18faf3fSek boolean_t modifying) 135fa9e4066Sahrens { 136fa9e4066Sahrens namecheck_err_t why; 137fa9e4066Sahrens char what; 138fa9e4066Sahrens 139fa9e4066Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 14099653d4eSeschrock if (hdl != NULL) { 141fa9e4066Sahrens switch (why) { 142b81d61a6Slling case NAME_ERR_TOOLONG: 14399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14499653d4eSeschrock "name is too long")); 145b81d61a6Slling break; 146b81d61a6Slling 147fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 14899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14999653d4eSeschrock "leading slash in name")); 150fa9e4066Sahrens break; 151fa9e4066Sahrens 152fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 15399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15499653d4eSeschrock "empty component in name")); 155fa9e4066Sahrens break; 156fa9e4066Sahrens 157fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 15899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15999653d4eSeschrock "trailing slash in name")); 160fa9e4066Sahrens break; 161fa9e4066Sahrens 162fa9e4066Sahrens case NAME_ERR_INVALCHAR: 16399653d4eSeschrock zfs_error_aux(hdl, 164fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 16599653d4eSeschrock "'%c' in name"), what); 166fa9e4066Sahrens break; 167fa9e4066Sahrens 168fa9e4066Sahrens case NAME_ERR_MULTIPLE_AT: 16999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 17099653d4eSeschrock "multiple '@' delimiters in name")); 171fa9e4066Sahrens break; 1725ad82045Snd 1735ad82045Snd case NAME_ERR_NOLETTER: 1745ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1755ad82045Snd "pool doesn't begin with a letter")); 1765ad82045Snd break; 1775ad82045Snd 1785ad82045Snd case NAME_ERR_RESERVED: 1795ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1805ad82045Snd "name is reserved")); 1815ad82045Snd break; 1825ad82045Snd 1835ad82045Snd case NAME_ERR_DISKLIKE: 1845ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1855ad82045Snd "reserved disk name")); 1865ad82045Snd break; 187fa9e4066Sahrens } 188fa9e4066Sahrens } 189fa9e4066Sahrens 190fa9e4066Sahrens return (0); 191fa9e4066Sahrens } 192fa9e4066Sahrens 193fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 19499653d4eSeschrock if (hdl != NULL) 19599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19699653d4eSeschrock "snapshot delimiter '@' in filesystem name")); 197fa9e4066Sahrens return (0); 198fa9e4066Sahrens } 199fa9e4066Sahrens 2001d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 2011d452cf5Sahrens if (hdl != NULL) 2021d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 203d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 2041d452cf5Sahrens return (0); 2051d452cf5Sahrens } 2061d452cf5Sahrens 207f18faf3fSek if (modifying && strchr(path, '%') != NULL) { 208f18faf3fSek if (hdl != NULL) 209f18faf3fSek zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 210f18faf3fSek "invalid character %c in name"), '%'); 211f18faf3fSek return (0); 212f18faf3fSek } 213f18faf3fSek 21499653d4eSeschrock return (-1); 215fa9e4066Sahrens } 216fa9e4066Sahrens 217fa9e4066Sahrens int 218fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 219fa9e4066Sahrens { 220e7cbe64fSgw if (type == ZFS_TYPE_POOL) 221e7cbe64fSgw return (zpool_name_valid(NULL, B_FALSE, name)); 222f18faf3fSek return (zfs_validate_name(NULL, name, type, B_FALSE)); 223fa9e4066Sahrens } 224fa9e4066Sahrens 225e9dbad6fSeschrock /* 226e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 227e9dbad6fSeschrock * properties into a separate nvlist. 228e9dbad6fSeschrock */ 229fac3008cSeschrock static nvlist_t * 230fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 231e9dbad6fSeschrock { 232e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 233e9dbad6fSeschrock nvpair_t *elem; 234e9dbad6fSeschrock nvlist_t *propval; 235fac3008cSeschrock nvlist_t *nvl; 236e9dbad6fSeschrock 237fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 238fac3008cSeschrock (void) no_memory(hdl); 239fac3008cSeschrock return (NULL); 240fac3008cSeschrock } 241e9dbad6fSeschrock 242e9dbad6fSeschrock elem = NULL; 243fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 244e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 245e9dbad6fSeschrock continue; 246e9dbad6fSeschrock 247e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 248fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 249fac3008cSeschrock nvlist_free(nvl); 250fac3008cSeschrock (void) no_memory(hdl); 251fac3008cSeschrock return (NULL); 252fac3008cSeschrock } 253e9dbad6fSeschrock } 254e9dbad6fSeschrock 255fac3008cSeschrock return (nvl); 256e9dbad6fSeschrock } 257e9dbad6fSeschrock 25829ab75c9Srm static zpool_handle_t * 25929ab75c9Srm zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 26029ab75c9Srm { 26129ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 26229ab75c9Srm zpool_handle_t *zph; 26329ab75c9Srm 26429ab75c9Srm if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 26529ab75c9Srm if (hdl->libzfs_pool_handles != NULL) 26629ab75c9Srm zph->zpool_next = hdl->libzfs_pool_handles; 26729ab75c9Srm hdl->libzfs_pool_handles = zph; 26829ab75c9Srm } 26929ab75c9Srm return (zph); 27029ab75c9Srm } 27129ab75c9Srm 27229ab75c9Srm static zpool_handle_t * 27329ab75c9Srm zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 27429ab75c9Srm { 27529ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 27629ab75c9Srm zpool_handle_t *zph = hdl->libzfs_pool_handles; 27729ab75c9Srm 27829ab75c9Srm while ((zph != NULL) && 27929ab75c9Srm (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 28029ab75c9Srm zph = zph->zpool_next; 28129ab75c9Srm return (zph); 28229ab75c9Srm } 28329ab75c9Srm 28429ab75c9Srm /* 28529ab75c9Srm * Returns a handle to the pool that contains the provided dataset. 28629ab75c9Srm * If a handle to that pool already exists then that handle is returned. 28729ab75c9Srm * Otherwise, a new handle is created and added to the list of handles. 28829ab75c9Srm */ 28929ab75c9Srm static zpool_handle_t * 29029ab75c9Srm zpool_handle(zfs_handle_t *zhp) 29129ab75c9Srm { 29229ab75c9Srm char *pool_name; 29329ab75c9Srm int len; 29429ab75c9Srm zpool_handle_t *zph; 29529ab75c9Srm 29629ab75c9Srm len = strcspn(zhp->zfs_name, "/@") + 1; 29729ab75c9Srm pool_name = zfs_alloc(zhp->zfs_hdl, len); 29829ab75c9Srm (void) strlcpy(pool_name, zhp->zfs_name, len); 29929ab75c9Srm 30029ab75c9Srm zph = zpool_find_handle(zhp, pool_name, len); 30129ab75c9Srm if (zph == NULL) 30229ab75c9Srm zph = zpool_add_handle(zhp, pool_name); 30329ab75c9Srm 30429ab75c9Srm free(pool_name); 30529ab75c9Srm return (zph); 30629ab75c9Srm } 30729ab75c9Srm 30829ab75c9Srm void 30929ab75c9Srm zpool_free_handles(libzfs_handle_t *hdl) 31029ab75c9Srm { 31129ab75c9Srm zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 31229ab75c9Srm 31329ab75c9Srm while (zph != NULL) { 31429ab75c9Srm next = zph->zpool_next; 31529ab75c9Srm zpool_close(zph); 31629ab75c9Srm zph = next; 31729ab75c9Srm } 31829ab75c9Srm hdl->libzfs_pool_handles = NULL; 31929ab75c9Srm } 32029ab75c9Srm 321fa9e4066Sahrens /* 322fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 323fa9e4066Sahrens */ 324fa9e4066Sahrens static int 325ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 326fa9e4066Sahrens { 327e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 328fa9e4066Sahrens 329ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 330fa9e4066Sahrens 331ebedde84SEric Taylor while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3327f7322feSeschrock if (errno == ENOMEM) { 333ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 33499653d4eSeschrock return (-1); 335e9dbad6fSeschrock } 3367f7322feSeschrock } else { 3377f7322feSeschrock return (-1); 3387f7322feSeschrock } 3397f7322feSeschrock } 340ebedde84SEric Taylor return (0); 341ebedde84SEric Taylor } 342fa9e4066Sahrens 343ebedde84SEric Taylor static int 344ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 345ebedde84SEric Taylor { 346ebedde84SEric Taylor nvlist_t *allprops, *userprops; 347fa9e4066Sahrens 348ebedde84SEric Taylor zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 349ebedde84SEric Taylor 350ebedde84SEric Taylor if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 35199653d4eSeschrock return (-1); 35299653d4eSeschrock } 353fa9e4066Sahrens 35414843421SMatthew Ahrens /* 35514843421SMatthew Ahrens * XXX Why do we store the user props separately, in addition to 35614843421SMatthew Ahrens * storing them in zfs_props? 35714843421SMatthew Ahrens */ 358fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 359fac3008cSeschrock nvlist_free(allprops); 360e9dbad6fSeschrock return (-1); 361fac3008cSeschrock } 362fac3008cSeschrock 363fac3008cSeschrock nvlist_free(zhp->zfs_props); 364fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 365fac3008cSeschrock 366fac3008cSeschrock zhp->zfs_props = allprops; 367fac3008cSeschrock zhp->zfs_user_props = userprops; 36899653d4eSeschrock 369fa9e4066Sahrens return (0); 370fa9e4066Sahrens } 371fa9e4066Sahrens 372ebedde84SEric Taylor static int 373ebedde84SEric Taylor get_stats(zfs_handle_t *zhp) 374ebedde84SEric Taylor { 375ebedde84SEric Taylor int rc = 0; 376ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 377ebedde84SEric Taylor 378ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 379ebedde84SEric Taylor return (-1); 380ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) != 0) 381ebedde84SEric Taylor rc = -1; 382ebedde84SEric Taylor else if (put_stats_zhdl(zhp, &zc) != 0) 383ebedde84SEric Taylor rc = -1; 384ebedde84SEric Taylor zcmd_free_nvlists(&zc); 385ebedde84SEric Taylor return (rc); 386ebedde84SEric Taylor } 387ebedde84SEric Taylor 388fa9e4066Sahrens /* 389fa9e4066Sahrens * Refresh the properties currently stored in the handle. 390fa9e4066Sahrens */ 391fa9e4066Sahrens void 392fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 393fa9e4066Sahrens { 394fa9e4066Sahrens (void) get_stats(zhp); 395fa9e4066Sahrens } 396fa9e4066Sahrens 397fa9e4066Sahrens /* 398fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 399fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 400fa9e4066Sahrens */ 401ebedde84SEric Taylor static int 402ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 403fa9e4066Sahrens { 404*503ad85cSMatthew Ahrens if (put_stats_zhdl(zhp, zc) != 0) 405ebedde84SEric Taylor return (-1); 40631fd60d3Sahrens 407fa9e4066Sahrens /* 408fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 409fa9e4066Sahrens * the high-level type. 410fa9e4066Sahrens */ 411a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 412a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 413a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 414a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 415a2eea2e1Sahrens else 416a2eea2e1Sahrens abort(); 417a2eea2e1Sahrens 418fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 419fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 420fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 421fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 422fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 423fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 424fa9e4066Sahrens else 42599653d4eSeschrock abort(); /* we should never see any other types */ 426fa9e4066Sahrens 42729ab75c9Srm zhp->zpool_hdl = zpool_handle(zhp); 428ebedde84SEric Taylor return (0); 429ebedde84SEric Taylor } 430ebedde84SEric Taylor 431ebedde84SEric Taylor zfs_handle_t * 432ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 433ebedde84SEric Taylor { 434ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 435ebedde84SEric Taylor 436ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 437ebedde84SEric Taylor 438ebedde84SEric Taylor if (zhp == NULL) 439ebedde84SEric Taylor return (NULL); 440ebedde84SEric Taylor 441ebedde84SEric Taylor zhp->zfs_hdl = hdl; 442ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 443ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 444ebedde84SEric Taylor free(zhp); 445ebedde84SEric Taylor return (NULL); 446ebedde84SEric Taylor } 447ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 448ebedde84SEric Taylor zcmd_free_nvlists(&zc); 449ebedde84SEric Taylor free(zhp); 450ebedde84SEric Taylor return (NULL); 451ebedde84SEric Taylor } 452ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 453ebedde84SEric Taylor free(zhp); 454ebedde84SEric Taylor zhp = NULL; 455ebedde84SEric Taylor } 456ebedde84SEric Taylor zcmd_free_nvlists(&zc); 457ebedde84SEric Taylor return (zhp); 458ebedde84SEric Taylor } 459ebedde84SEric Taylor 460ebedde84SEric Taylor static zfs_handle_t * 461ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 462ebedde84SEric Taylor { 463ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 464ebedde84SEric Taylor 465ebedde84SEric Taylor if (zhp == NULL) 466ebedde84SEric Taylor return (NULL); 467ebedde84SEric Taylor 468ebedde84SEric Taylor zhp->zfs_hdl = hdl; 469ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 470ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 471ebedde84SEric Taylor free(zhp); 472ebedde84SEric Taylor return (NULL); 473ebedde84SEric Taylor } 474fa9e4066Sahrens return (zhp); 475fa9e4066Sahrens } 476fa9e4066Sahrens 477fa9e4066Sahrens /* 478fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 479fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 480fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 481fa9e4066Sahrens */ 482fa9e4066Sahrens zfs_handle_t * 48399653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 484fa9e4066Sahrens { 485fa9e4066Sahrens zfs_handle_t *zhp; 48699653d4eSeschrock char errbuf[1024]; 48799653d4eSeschrock 48899653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 48999653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 490fa9e4066Sahrens 491fa9e4066Sahrens /* 49299653d4eSeschrock * Validate the name before we even try to open it. 493fa9e4066Sahrens */ 494f18faf3fSek if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 49599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 49699653d4eSeschrock "invalid dataset name")); 49799653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 498fa9e4066Sahrens return (NULL); 499fa9e4066Sahrens } 500fa9e4066Sahrens 501fa9e4066Sahrens /* 502fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 503fa9e4066Sahrens */ 504fa9e4066Sahrens errno = 0; 50599653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 506ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 507fa9e4066Sahrens return (NULL); 508fa9e4066Sahrens } 509fa9e4066Sahrens 510fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 51199653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 51294de1d4cSeschrock zfs_close(zhp); 513fa9e4066Sahrens return (NULL); 514fa9e4066Sahrens } 515fa9e4066Sahrens 516fa9e4066Sahrens return (zhp); 517fa9e4066Sahrens } 518fa9e4066Sahrens 519fa9e4066Sahrens /* 520fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 521fa9e4066Sahrens */ 522fa9e4066Sahrens void 523fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 524fa9e4066Sahrens { 525fa9e4066Sahrens if (zhp->zfs_mntopts) 526fa9e4066Sahrens free(zhp->zfs_mntopts); 527e9dbad6fSeschrock nvlist_free(zhp->zfs_props); 528e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 529fa9e4066Sahrens free(zhp); 530fa9e4066Sahrens } 531fa9e4066Sahrens 532ebedde84SEric Taylor typedef struct mnttab_node { 533ebedde84SEric Taylor struct mnttab mtn_mt; 534ebedde84SEric Taylor avl_node_t mtn_node; 535ebedde84SEric Taylor } mnttab_node_t; 536ebedde84SEric Taylor 537ebedde84SEric Taylor static int 538ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 539ebedde84SEric Taylor { 540ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 541ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 542ebedde84SEric Taylor int rv; 543ebedde84SEric Taylor 544ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 545ebedde84SEric Taylor 546ebedde84SEric Taylor if (rv == 0) 547ebedde84SEric Taylor return (0); 548ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 549ebedde84SEric Taylor } 550ebedde84SEric Taylor 551ebedde84SEric Taylor void 552ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 553ebedde84SEric Taylor { 554ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 555ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 556ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 557b2634b9cSEric Taylor } 558b2634b9cSEric Taylor 559b2634b9cSEric Taylor void 560b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 561b2634b9cSEric Taylor { 562b2634b9cSEric Taylor struct mnttab entry; 563ebedde84SEric Taylor 564ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 565ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 566ebedde84SEric Taylor mnttab_node_t *mtn; 567ebedde84SEric Taylor 568ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 569ebedde84SEric Taylor continue; 570ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 571ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 572ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 573ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 574ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 575ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 576ebedde84SEric Taylor } 577ebedde84SEric Taylor } 578ebedde84SEric Taylor 579ebedde84SEric Taylor void 580ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 581ebedde84SEric Taylor { 582ebedde84SEric Taylor void *cookie = NULL; 583ebedde84SEric Taylor mnttab_node_t *mtn; 584ebedde84SEric Taylor 585ebedde84SEric Taylor while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 586ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 587ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 588ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 589ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 590ebedde84SEric Taylor free(mtn); 591ebedde84SEric Taylor } 592ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 593ebedde84SEric Taylor } 594ebedde84SEric Taylor 595b2634b9cSEric Taylor void 596b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 597b2634b9cSEric Taylor { 598b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 599b2634b9cSEric Taylor } 600b2634b9cSEric Taylor 601ebedde84SEric Taylor int 602ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 603ebedde84SEric Taylor struct mnttab *entry) 604ebedde84SEric Taylor { 605ebedde84SEric Taylor mnttab_node_t find; 606ebedde84SEric Taylor mnttab_node_t *mtn; 607ebedde84SEric Taylor 608b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 609b2634b9cSEric Taylor struct mnttab srch = { 0 }; 610b2634b9cSEric Taylor 611b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 612b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 613b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 614b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 615b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 616b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 617b2634b9cSEric Taylor return (0); 618b2634b9cSEric Taylor else 619b2634b9cSEric Taylor return (ENOENT); 620b2634b9cSEric Taylor } 621b2634b9cSEric Taylor 622ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 623b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 624ebedde84SEric Taylor 625ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 626ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 627ebedde84SEric Taylor if (mtn) { 628ebedde84SEric Taylor *entry = mtn->mtn_mt; 629ebedde84SEric Taylor return (0); 630ebedde84SEric Taylor } 631ebedde84SEric Taylor return (ENOENT); 632ebedde84SEric Taylor } 633ebedde84SEric Taylor 634ebedde84SEric Taylor void 635ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 636ebedde84SEric Taylor const char *mountp, const char *mntopts) 637ebedde84SEric Taylor { 638ebedde84SEric Taylor mnttab_node_t *mtn; 639ebedde84SEric Taylor 640ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 641ebedde84SEric Taylor return; 642ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 643ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 644ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 645ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 646ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 647ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 648ebedde84SEric Taylor } 649ebedde84SEric Taylor 650ebedde84SEric Taylor void 651ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 652ebedde84SEric Taylor { 653ebedde84SEric Taylor mnttab_node_t find; 654ebedde84SEric Taylor mnttab_node_t *ret; 655ebedde84SEric Taylor 656ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 657ebedde84SEric Taylor if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 658ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 659ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 660ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 661ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 662ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 663ebedde84SEric Taylor free(ret); 664ebedde84SEric Taylor } 665ebedde84SEric Taylor } 666ebedde84SEric Taylor 6677b97dc1aSrm int 6687b97dc1aSrm zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 6697b97dc1aSrm { 67029ab75c9Srm zpool_handle_t *zpool_handle = zhp->zpool_hdl; 6717b97dc1aSrm 6727b97dc1aSrm if (zpool_handle == NULL) 6737b97dc1aSrm return (-1); 6747b97dc1aSrm 6757b97dc1aSrm *spa_version = zpool_get_prop_int(zpool_handle, 6767b97dc1aSrm ZPOOL_PROP_VERSION, NULL); 6777b97dc1aSrm return (0); 6787b97dc1aSrm } 6797b97dc1aSrm 6807b97dc1aSrm /* 6817b97dc1aSrm * The choice of reservation property depends on the SPA version. 6827b97dc1aSrm */ 6837b97dc1aSrm static int 6847b97dc1aSrm zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 6857b97dc1aSrm { 6867b97dc1aSrm int spa_version; 6877b97dc1aSrm 6887b97dc1aSrm if (zfs_spa_version(zhp, &spa_version) < 0) 6897b97dc1aSrm return (-1); 6907b97dc1aSrm 6917b97dc1aSrm if (spa_version >= SPA_VERSION_REFRESERVATION) 6927b97dc1aSrm *resv_prop = ZFS_PROP_REFRESERVATION; 6937b97dc1aSrm else 6947b97dc1aSrm *resv_prop = ZFS_PROP_RESERVATION; 6957b97dc1aSrm 6967b97dc1aSrm return (0); 6977b97dc1aSrm } 6987b97dc1aSrm 699e9dbad6fSeschrock /* 700e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 701e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 702e9dbad6fSeschrock * strings. 703e9dbad6fSeschrock */ 7040a48a24eStimh nvlist_t * 7050a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 706990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 707e9dbad6fSeschrock { 708e9dbad6fSeschrock nvpair_t *elem; 709e9dbad6fSeschrock uint64_t intval; 710e9dbad6fSeschrock char *strval; 711990b4856Slling zfs_prop_t prop; 712e9dbad6fSeschrock nvlist_t *ret; 713da6c28aaSamw int chosen_normal = -1; 714da6c28aaSamw int chosen_utf = -1; 715e9dbad6fSeschrock 716990b4856Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 717990b4856Slling (void) no_memory(hdl); 718990b4856Slling return (NULL); 719e9dbad6fSeschrock } 720e9dbad6fSeschrock 72114843421SMatthew Ahrens /* 72214843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 72314843421SMatthew Ahrens */ 72414843421SMatthew Ahrens 725e9dbad6fSeschrock elem = NULL; 726e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 727990b4856Slling const char *propname = nvpair_name(elem); 728e9dbad6fSeschrock 72914843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 73014843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 731990b4856Slling /* 73214843421SMatthew Ahrens * This is a user property: make sure it's a 733990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 734990b4856Slling */ 735990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 736990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 737990b4856Slling "'%s' must be a string"), propname); 738990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 739990b4856Slling goto error; 740990b4856Slling } 741990b4856Slling 742990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 743990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 744990b4856Slling "property name '%s' is too long"), 745990b4856Slling propname); 746990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 747990b4856Slling goto error; 748fa9e4066Sahrens } 749fa9e4066Sahrens 750e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 751e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 752e9dbad6fSeschrock (void) no_memory(hdl); 753e9dbad6fSeschrock goto error; 754e9dbad6fSeschrock } 755e9dbad6fSeschrock continue; 756e9dbad6fSeschrock } 757e9dbad6fSeschrock 75814843421SMatthew Ahrens /* 75914843421SMatthew Ahrens * Currently, only user properties can be modified on 76014843421SMatthew Ahrens * snapshots. 76114843421SMatthew Ahrens */ 762bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 763bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 764bb0ade09Sahrens "this property can not be modified for snapshots")); 765bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 766bb0ade09Sahrens goto error; 767bb0ade09Sahrens } 768bb0ade09Sahrens 76914843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 77014843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 77114843421SMatthew Ahrens char newpropname[128]; 77214843421SMatthew Ahrens char domain[128]; 77314843421SMatthew Ahrens uint64_t rid; 77414843421SMatthew Ahrens uint64_t valary[3]; 77514843421SMatthew Ahrens 77614843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 77714843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 77814843421SMatthew Ahrens zfs_error_aux(hdl, 77914843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 78014843421SMatthew Ahrens "'%s' has an invalid user/group name"), 78114843421SMatthew Ahrens propname); 78214843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 78314843421SMatthew Ahrens goto error; 78414843421SMatthew Ahrens } 78514843421SMatthew Ahrens 78614843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 78714843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 78814843421SMatthew Ahrens zfs_error_aux(hdl, 78914843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 79014843421SMatthew Ahrens propname); 79114843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 79214843421SMatthew Ahrens errbuf); 79314843421SMatthew Ahrens goto error; 79414843421SMatthew Ahrens } 79514843421SMatthew Ahrens 79614843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 79714843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 79814843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 79914843421SMatthew Ahrens intval = 0; 80014843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 80114843421SMatthew Ahrens strval, &intval) != 0) { 80214843421SMatthew Ahrens (void) zfs_error(hdl, 80314843421SMatthew Ahrens EZFS_BADPROP, errbuf); 80414843421SMatthew Ahrens goto error; 80514843421SMatthew Ahrens } 80614843421SMatthew Ahrens } else if (nvpair_type(elem) == 80714843421SMatthew Ahrens DATA_TYPE_UINT64) { 80814843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 80914843421SMatthew Ahrens if (intval == 0) { 81014843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 81114843421SMatthew Ahrens "use 'none' to disable " 81214843421SMatthew Ahrens "userquota/groupquota")); 81314843421SMatthew Ahrens goto error; 81414843421SMatthew Ahrens } 81514843421SMatthew Ahrens } else { 81614843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 81714843421SMatthew Ahrens "'%s' must be a number"), propname); 81814843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 81914843421SMatthew Ahrens goto error; 82014843421SMatthew Ahrens } 82114843421SMatthew Ahrens 82214843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 82314843421SMatthew Ahrens "%s%s", zfs_userquota_prop_prefixes[uqtype], 82414843421SMatthew Ahrens domain); 82514843421SMatthew Ahrens valary[0] = uqtype; 82614843421SMatthew Ahrens valary[1] = rid; 82714843421SMatthew Ahrens valary[2] = intval; 82814843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 82914843421SMatthew Ahrens valary, 3) != 0) { 83014843421SMatthew Ahrens (void) no_memory(hdl); 83114843421SMatthew Ahrens goto error; 83214843421SMatthew Ahrens } 83314843421SMatthew Ahrens continue; 83414843421SMatthew Ahrens } 83514843421SMatthew Ahrens 83614843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 83714843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 83814843421SMatthew Ahrens "invalid property '%s'"), propname); 83914843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 84014843421SMatthew Ahrens goto error; 84114843421SMatthew Ahrens } 84214843421SMatthew Ahrens 843e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 844e9dbad6fSeschrock zfs_error_aux(hdl, 845e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 846e9dbad6fSeschrock "apply to datasets of this type"), propname); 847e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 848e9dbad6fSeschrock goto error; 849e9dbad6fSeschrock } 850e9dbad6fSeschrock 851e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 852da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 853e9dbad6fSeschrock zfs_error_aux(hdl, 854e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 855e9dbad6fSeschrock propname); 856e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 857e9dbad6fSeschrock goto error; 858e9dbad6fSeschrock } 859e9dbad6fSeschrock 860990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 861990b4856Slling &strval, &intval, errbuf) != 0) 862e9dbad6fSeschrock goto error; 863fa9e4066Sahrens 864e9dbad6fSeschrock /* 865e9dbad6fSeschrock * Perform some additional checks for specific properties. 866e9dbad6fSeschrock */ 867e9dbad6fSeschrock switch (prop) { 868e7437265Sahrens case ZFS_PROP_VERSION: 869e7437265Sahrens { 870e7437265Sahrens int version; 871e7437265Sahrens 872e7437265Sahrens if (zhp == NULL) 873e7437265Sahrens break; 874e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 875e7437265Sahrens if (intval < version) { 876e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 877e7437265Sahrens "Can not downgrade; already at version %u"), 878e7437265Sahrens version); 879e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 880e7437265Sahrens goto error; 881e7437265Sahrens } 882e7437265Sahrens break; 883e7437265Sahrens } 884e7437265Sahrens 885e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 886e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 887e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 888e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 889e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 89099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 891e9dbad6fSeschrock "'%s' must be power of 2 from %u " 892e9dbad6fSeschrock "to %uk"), propname, 893e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 894e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 895e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 896e9dbad6fSeschrock goto error; 897fa9e4066Sahrens } 898fa9e4066Sahrens break; 899fa9e4066Sahrens 900f3861e1aSahl case ZFS_PROP_SHAREISCSI: 901f3861e1aSahl if (strcmp(strval, "off") != 0 && 902f3861e1aSahl strcmp(strval, "on") != 0 && 903f3861e1aSahl strcmp(strval, "type=disk") != 0) { 904f3861e1aSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 905f3861e1aSahl "'%s' must be 'on', 'off', or 'type=disk'"), 906f3861e1aSahl propname); 907f3861e1aSahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 908f3861e1aSahl goto error; 909f3861e1aSahl } 910f3861e1aSahl 911f3861e1aSahl break; 912f3861e1aSahl 913e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 91489eef05eSrm { 91589eef05eSrm namecheck_err_t why; 91689eef05eSrm 917e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 918e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 919e9dbad6fSeschrock break; 920fa9e4066Sahrens 92189eef05eSrm if (mountpoint_namecheck(strval, &why)) { 92289eef05eSrm switch (why) { 92389eef05eSrm case NAME_ERR_LEADING_SLASH: 92489eef05eSrm zfs_error_aux(hdl, 92589eef05eSrm dgettext(TEXT_DOMAIN, 92689eef05eSrm "'%s' must be an absolute path, " 92789eef05eSrm "'none', or 'legacy'"), propname); 92889eef05eSrm break; 92989eef05eSrm case NAME_ERR_TOOLONG: 93089eef05eSrm zfs_error_aux(hdl, 93189eef05eSrm dgettext(TEXT_DOMAIN, 93289eef05eSrm "component of '%s' is too long"), 93389eef05eSrm propname); 93489eef05eSrm break; 93589eef05eSrm } 936e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 937e9dbad6fSeschrock goto error; 938fa9e4066Sahrens } 93989eef05eSrm } 94089eef05eSrm 941f3861e1aSahl /*FALLTHRU*/ 942fa9e4066Sahrens 943da6c28aaSamw case ZFS_PROP_SHARESMB: 944f3861e1aSahl case ZFS_PROP_SHARENFS: 945f3861e1aSahl /* 946da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 947da6c28aaSamw * properties, check if it can be set in a 948da6c28aaSamw * global/non-global zone based on 949f3861e1aSahl * the zoned property value: 950f3861e1aSahl * 951f3861e1aSahl * global zone non-global zone 952f3861e1aSahl * -------------------------------------------------- 953f3861e1aSahl * zoned=on mountpoint (no) mountpoint (yes) 954f3861e1aSahl * sharenfs (no) sharenfs (no) 955da6c28aaSamw * sharesmb (no) sharesmb (no) 956f3861e1aSahl * 957f3861e1aSahl * zoned=off mountpoint (yes) N/A 958f3861e1aSahl * sharenfs (yes) 959da6c28aaSamw * sharesmb (yes) 960f3861e1aSahl */ 961e9dbad6fSeschrock if (zoned) { 962e9dbad6fSeschrock if (getzoneid() == GLOBAL_ZONEID) { 963e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 964e9dbad6fSeschrock "'%s' cannot be set on " 965e9dbad6fSeschrock "dataset in a non-global zone"), 966e9dbad6fSeschrock propname); 967e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 968e9dbad6fSeschrock errbuf); 969e9dbad6fSeschrock goto error; 970da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 971da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 972e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 973e9dbad6fSeschrock "'%s' cannot be set in " 974e9dbad6fSeschrock "a non-global zone"), propname); 975e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 976e9dbad6fSeschrock errbuf); 977e9dbad6fSeschrock goto error; 978fa9e4066Sahrens } 979e9dbad6fSeschrock } else if (getzoneid() != GLOBAL_ZONEID) { 980e9dbad6fSeschrock /* 981e9dbad6fSeschrock * If zoned property is 'off', this must be in 98214843421SMatthew Ahrens * a global zone. If not, something is wrong. 983e9dbad6fSeschrock */ 984e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 985e9dbad6fSeschrock "'%s' cannot be set while dataset " 986e9dbad6fSeschrock "'zoned' property is set"), propname); 987e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 988e9dbad6fSeschrock goto error; 989fa9e4066Sahrens } 990f3861e1aSahl 99167331909Sdougm /* 99267331909Sdougm * At this point, it is legitimate to set the 99367331909Sdougm * property. Now we want to make sure that the 99467331909Sdougm * property value is valid if it is sharenfs. 99567331909Sdougm */ 996da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 997da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 998fac3008cSeschrock strcmp(strval, "on") != 0 && 999fac3008cSeschrock strcmp(strval, "off") != 0) { 1000da6c28aaSamw zfs_share_proto_t proto; 1001da6c28aaSamw 1002da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1003da6c28aaSamw proto = PROTO_SMB; 1004da6c28aaSamw else 1005da6c28aaSamw proto = PROTO_NFS; 100667331909Sdougm 100767331909Sdougm /* 1008da6c28aaSamw * Must be an valid sharing protocol 1009da6c28aaSamw * option string so init the libshare 1010da6c28aaSamw * in order to enable the parser and 1011da6c28aaSamw * then parse the options. We use the 1012da6c28aaSamw * control API since we don't care about 1013da6c28aaSamw * the current configuration and don't 101467331909Sdougm * want the overhead of loading it 101567331909Sdougm * until we actually do something. 101667331909Sdougm */ 101767331909Sdougm 1018fac3008cSeschrock if (zfs_init_libshare(hdl, 1019fac3008cSeschrock SA_INIT_CONTROL_API) != SA_OK) { 1020fac3008cSeschrock /* 1021fac3008cSeschrock * An error occurred so we can't do 1022fac3008cSeschrock * anything 1023fac3008cSeschrock */ 1024fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1025fac3008cSeschrock "'%s' cannot be set: problem " 1026fac3008cSeschrock "in share initialization"), 1027fac3008cSeschrock propname); 1028fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1029fac3008cSeschrock errbuf); 1030fac3008cSeschrock goto error; 1031fac3008cSeschrock } 103267331909Sdougm 1033da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 1034fac3008cSeschrock /* 1035fac3008cSeschrock * There was an error in parsing so 1036fac3008cSeschrock * deal with it by issuing an error 1037fac3008cSeschrock * message and leaving after 1038fac3008cSeschrock * uninitializing the the libshare 1039fac3008cSeschrock * interface. 1040fac3008cSeschrock */ 1041fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1042fac3008cSeschrock "'%s' cannot be set to invalid " 1043fac3008cSeschrock "options"), propname); 1044fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1045fac3008cSeschrock errbuf); 1046fac3008cSeschrock zfs_uninit_libshare(hdl); 1047fac3008cSeschrock goto error; 1048fac3008cSeschrock } 104967331909Sdougm zfs_uninit_libshare(hdl); 105067331909Sdougm } 105167331909Sdougm 1052da6c28aaSamw break; 1053da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1054da6c28aaSamw chosen_utf = (int)intval; 1055da6c28aaSamw break; 1056da6c28aaSamw case ZFS_PROP_NORMALIZE: 1057da6c28aaSamw chosen_normal = (int)intval; 1058f3861e1aSahl break; 1059e9dbad6fSeschrock } 1060fa9e4066Sahrens 1061e9dbad6fSeschrock /* 1062e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1063e9dbad6fSeschrock * checks to enforce. 1064e9dbad6fSeschrock */ 1065e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1066e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1067e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1068e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1069e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1070e9dbad6fSeschrock char buf[64]; 1071e9dbad6fSeschrock 1072e9dbad6fSeschrock switch (prop) { 1073e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1074a9799022Sck case ZFS_PROP_REFRESERVATION: 1075e9dbad6fSeschrock if (intval > volsize) { 1076e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1077e9dbad6fSeschrock "'%s' is greater than current " 1078e9dbad6fSeschrock "volume size"), propname); 1079e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1080e9dbad6fSeschrock errbuf); 1081e9dbad6fSeschrock goto error; 1082e9dbad6fSeschrock } 1083e9dbad6fSeschrock break; 1084e9dbad6fSeschrock 1085e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1086e9dbad6fSeschrock if (intval % blocksize != 0) { 1087e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1088e9dbad6fSeschrock sizeof (buf)); 1089e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1090e9dbad6fSeschrock "'%s' must be a multiple of " 1091e9dbad6fSeschrock "volume block size (%s)"), 1092e9dbad6fSeschrock propname, buf); 1093e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1094e9dbad6fSeschrock errbuf); 1095e9dbad6fSeschrock goto error; 1096e9dbad6fSeschrock } 1097e9dbad6fSeschrock 1098e9dbad6fSeschrock if (intval == 0) { 1099e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1100e9dbad6fSeschrock "'%s' cannot be zero"), 1101e9dbad6fSeschrock propname); 1102e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1103e9dbad6fSeschrock errbuf); 1104e9dbad6fSeschrock goto error; 1105e9dbad6fSeschrock } 1106f3861e1aSahl break; 1107fa9e4066Sahrens } 1108e9dbad6fSeschrock } 1109e9dbad6fSeschrock } 1110fa9e4066Sahrens 1111da6c28aaSamw /* 1112da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1113da6c28aaSamw * enforce rejection of non-UTF8 names. 1114da6c28aaSamw * 1115da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1116da6c28aaSamw * was explicitly not chosen, it is an error. 1117da6c28aaSamw */ 1118de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1119da6c28aaSamw if (nvlist_add_uint64(ret, 1120da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1121da6c28aaSamw (void) no_memory(hdl); 1122da6c28aaSamw goto error; 1123da6c28aaSamw } 1124de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1125da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1126da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1127da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1128da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1129da6c28aaSamw goto error; 1130da6c28aaSamw } 1131da6c28aaSamw 1132e9dbad6fSeschrock /* 1133e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1134e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1135e9dbad6fSeschrock */ 1136e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 1137e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1138e9dbad6fSeschrock &intval) == 0) { 1139e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 1140e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1141a9b821a0Sck uint64_t old_reservation; 1142e9dbad6fSeschrock uint64_t new_reservation; 1143a9b821a0Sck zfs_prop_t resv_prop; 1144a9b821a0Sck 11457b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1146a9b821a0Sck goto error; 1147a9b821a0Sck old_reservation = zfs_prop_get_int(zhp, resv_prop); 1148e9dbad6fSeschrock 1149e9dbad6fSeschrock if (old_volsize == old_reservation && 1150a9b821a0Sck nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 1151e9dbad6fSeschrock &new_reservation) != 0) { 1152e9dbad6fSeschrock if (nvlist_add_uint64(ret, 1153a9b821a0Sck zfs_prop_to_name(resv_prop), intval) != 0) { 1154e9dbad6fSeschrock (void) no_memory(hdl); 1155e9dbad6fSeschrock goto error; 1156e9dbad6fSeschrock } 1157fa9e4066Sahrens } 1158fa9e4066Sahrens } 1159e9dbad6fSeschrock return (ret); 1160fa9e4066Sahrens 1161e9dbad6fSeschrock error: 1162e9dbad6fSeschrock nvlist_free(ret); 1163e9dbad6fSeschrock return (NULL); 1164fa9e4066Sahrens } 1165fa9e4066Sahrens 1166fa9e4066Sahrens /* 1167fa9e4066Sahrens * Given a property name and value, set the property for the given dataset. 1168fa9e4066Sahrens */ 1169fa9e4066Sahrens int 1170e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1171fa9e4066Sahrens { 1172fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1173e9dbad6fSeschrock int ret = -1; 1174e9dbad6fSeschrock prop_changelist_t *cl = NULL; 117599653d4eSeschrock char errbuf[1024]; 117699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1177e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1178e9dbad6fSeschrock zfs_prop_t prop; 11790068372bSMark J Musante boolean_t do_prefix; 11800068372bSMark J Musante uint64_t idx; 118199653d4eSeschrock 118299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1183e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 118499653d4eSeschrock zhp->zfs_name); 118599653d4eSeschrock 1186e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1187e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1188e9dbad6fSeschrock (void) no_memory(hdl); 1189e9dbad6fSeschrock goto error; 1190fa9e4066Sahrens } 1191fa9e4066Sahrens 11920a48a24eStimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1193e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1194e9dbad6fSeschrock goto error; 1195990b4856Slling 1196e9dbad6fSeschrock nvlist_free(nvl); 1197e9dbad6fSeschrock nvl = realprops; 1198e9dbad6fSeschrock 1199e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1200e9dbad6fSeschrock 12010069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1202e9dbad6fSeschrock goto error; 1203fa9e4066Sahrens 1204fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 120599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 120699653d4eSeschrock "child dataset with inherited mountpoint is used " 120799653d4eSeschrock "in a non-global zone")); 120899653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1209fa9e4066Sahrens goto error; 1210fa9e4066Sahrens } 1211fa9e4066Sahrens 12120068372bSMark J Musante /* 12130068372bSMark J Musante * If the dataset's canmount property is being set to noauto, 12140068372bSMark J Musante * then we want to prevent unmounting & remounting it. 12150068372bSMark J Musante */ 12160068372bSMark J Musante do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 12170068372bSMark J Musante (zprop_string_to_index(prop, propval, &idx, 12180068372bSMark J Musante ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1219a227b7f4Shs 1220a227b7f4Shs if (do_prefix && (ret = changelist_prefix(cl)) != 0) 12210068372bSMark J Musante goto error; 1222fa9e4066Sahrens 1223fa9e4066Sahrens /* 1224fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1225fa9e4066Sahrens */ 1226fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1227fa9e4066Sahrens 1228990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1229e9dbad6fSeschrock goto error; 1230e9dbad6fSeschrock 1231ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1232743a77edSAlan Wright 1233fa9e4066Sahrens if (ret != 0) { 1234fa9e4066Sahrens switch (errno) { 1235fa9e4066Sahrens 1236fa9e4066Sahrens case ENOSPC: 1237fa9e4066Sahrens /* 1238fa9e4066Sahrens * For quotas and reservations, ENOSPC indicates 1239fa9e4066Sahrens * something different; setting a quota or reservation 1240fa9e4066Sahrens * doesn't use any disk space. 1241fa9e4066Sahrens */ 1242fa9e4066Sahrens switch (prop) { 1243fa9e4066Sahrens case ZFS_PROP_QUOTA: 1244a9799022Sck case ZFS_PROP_REFQUOTA: 124599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 124699653d4eSeschrock "size is less than current used or " 124799653d4eSeschrock "reserved space")); 124899653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1249fa9e4066Sahrens break; 1250fa9e4066Sahrens 1251fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1252a9799022Sck case ZFS_PROP_REFRESERVATION: 125399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 125499653d4eSeschrock "size is greater than available space")); 125599653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1256fa9e4066Sahrens break; 1257fa9e4066Sahrens 1258fa9e4066Sahrens default: 125999653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1260fa9e4066Sahrens break; 1261fa9e4066Sahrens } 1262fa9e4066Sahrens break; 1263fa9e4066Sahrens 1264fa9e4066Sahrens case EBUSY: 126599653d4eSeschrock if (prop == ZFS_PROP_VOLBLOCKSIZE) 126699653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); 126799653d4eSeschrock else 1268e9dbad6fSeschrock (void) zfs_standard_error(hdl, EBUSY, errbuf); 1269fa9e4066Sahrens break; 1270fa9e4066Sahrens 12712a79c5feSlling case EROFS: 127299653d4eSeschrock (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 12732a79c5feSlling break; 12742a79c5feSlling 1275c9431fa1Sahl case ENOTSUP: 1276c9431fa1Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12779e6eda55Smarks "pool and or dataset must be upgraded to set this " 127840feaa91Sahrens "property or value")); 1279c9431fa1Sahl (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1280c9431fa1Sahl break; 1281c9431fa1Sahl 128215e6edf1Sgw case ERANGE: 128315e6edf1Sgw if (prop == ZFS_PROP_COMPRESSION) { 128415e6edf1Sgw (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128515e6edf1Sgw "property setting is not allowed on " 128615e6edf1Sgw "bootable datasets")); 128715e6edf1Sgw (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 128815e6edf1Sgw } else { 128915e6edf1Sgw (void) zfs_standard_error(hdl, errno, errbuf); 129015e6edf1Sgw } 129115e6edf1Sgw break; 129215e6edf1Sgw 1293fa9e4066Sahrens case EOVERFLOW: 1294fa9e4066Sahrens /* 1295fa9e4066Sahrens * This platform can't address a volume this big. 1296fa9e4066Sahrens */ 1297fa9e4066Sahrens #ifdef _ILP32 1298fa9e4066Sahrens if (prop == ZFS_PROP_VOLSIZE) { 129999653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1300fa9e4066Sahrens break; 1301fa9e4066Sahrens } 1302fa9e4066Sahrens #endif 130399653d4eSeschrock /* FALLTHROUGH */ 1304fa9e4066Sahrens default: 130599653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1306fa9e4066Sahrens } 1307fa9e4066Sahrens } else { 1308a227b7f4Shs if (do_prefix) 1309a227b7f4Shs ret = changelist_postfix(cl); 1310a227b7f4Shs 1311fa9e4066Sahrens /* 1312fa9e4066Sahrens * Refresh the statistics so the new property value 1313fa9e4066Sahrens * is reflected. 1314fa9e4066Sahrens */ 1315a227b7f4Shs if (ret == 0) 1316e9dbad6fSeschrock (void) get_stats(zhp); 1317fa9e4066Sahrens } 1318fa9e4066Sahrens 1319fa9e4066Sahrens error: 1320e9dbad6fSeschrock nvlist_free(nvl); 1321e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1322e9dbad6fSeschrock if (cl) 1323e9dbad6fSeschrock changelist_free(cl); 1324fa9e4066Sahrens return (ret); 1325fa9e4066Sahrens } 1326fa9e4066Sahrens 1327fa9e4066Sahrens /* 1328fa9e4066Sahrens * Given a property, inherit the value from the parent dataset. 1329fa9e4066Sahrens */ 1330fa9e4066Sahrens int 1331e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) 1332fa9e4066Sahrens { 1333fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1334fa9e4066Sahrens int ret; 1335fa9e4066Sahrens prop_changelist_t *cl; 133699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 133799653d4eSeschrock char errbuf[1024]; 1338e9dbad6fSeschrock zfs_prop_t prop; 133999653d4eSeschrock 134099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 134199653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1342fa9e4066Sahrens 1343990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1344e9dbad6fSeschrock /* 1345e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1346e9dbad6fSeschrock * small, so just do it here. 1347e9dbad6fSeschrock */ 1348e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1349e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1350e9dbad6fSeschrock "invalid property")); 1351e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1352e9dbad6fSeschrock } 1353e9dbad6fSeschrock 1354e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1355e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1356e9dbad6fSeschrock 1357e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1358e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1359e9dbad6fSeschrock 1360e9dbad6fSeschrock return (0); 1361e9dbad6fSeschrock } 1362e9dbad6fSeschrock 1363fa9e4066Sahrens /* 1364fa9e4066Sahrens * Verify that this property is inheritable. 1365fa9e4066Sahrens */ 136699653d4eSeschrock if (zfs_prop_readonly(prop)) 136799653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1368fa9e4066Sahrens 136999653d4eSeschrock if (!zfs_prop_inheritable(prop)) 137099653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1371fa9e4066Sahrens 1372fa9e4066Sahrens /* 1373fa9e4066Sahrens * Check to see if the value applies to this type 1374fa9e4066Sahrens */ 137599653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 137699653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1377fa9e4066Sahrens 1378bf7c2d40Srm /* 1379bf7c2d40Srm * Normalize the name, to get rid of shorthand abbrevations. 1380bf7c2d40Srm */ 1381bf7c2d40Srm propname = zfs_prop_to_name(prop); 1382fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1383e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1384fa9e4066Sahrens 1385fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1386fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 138799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 138899653d4eSeschrock "dataset is used in a non-global zone")); 138999653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1390fa9e4066Sahrens } 1391fa9e4066Sahrens 1392fa9e4066Sahrens /* 1393fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1394fa9e4066Sahrens */ 13950069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1396fa9e4066Sahrens return (-1); 1397fa9e4066Sahrens 1398fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 139999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 140099653d4eSeschrock "child dataset with inherited mountpoint is used " 140199653d4eSeschrock "in a non-global zone")); 140299653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1403fa9e4066Sahrens goto error; 1404fa9e4066Sahrens } 1405fa9e4066Sahrens 1406fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1407fa9e4066Sahrens goto error; 1408fa9e4066Sahrens 1409e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 141099653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1411fa9e4066Sahrens } else { 1412fa9e4066Sahrens 1413efc555ebSnd if ((ret = changelist_postfix(cl)) != 0) 1414fa9e4066Sahrens goto error; 1415fa9e4066Sahrens 1416fa9e4066Sahrens /* 1417fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1418fa9e4066Sahrens */ 1419fa9e4066Sahrens (void) get_stats(zhp); 1420fa9e4066Sahrens } 1421fa9e4066Sahrens 1422fa9e4066Sahrens error: 1423fa9e4066Sahrens changelist_free(cl); 1424fa9e4066Sahrens return (ret); 1425fa9e4066Sahrens } 1426fa9e4066Sahrens 14277f7322feSeschrock /* 14287f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 14297f7322feSeschrock * extract them appropriately. 14307f7322feSeschrock */ 14317f7322feSeschrock static uint64_t 14327f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 14337f7322feSeschrock { 14347f7322feSeschrock nvlist_t *nv; 14357f7322feSeschrock uint64_t value; 14367f7322feSeschrock 1437a2eea2e1Sahrens *source = NULL; 14387f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 14397f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1440990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1441990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 14427f7322feSeschrock } else { 14432e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 14442e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 14457f7322feSeschrock value = zfs_prop_default_numeric(prop); 14467f7322feSeschrock *source = ""; 14477f7322feSeschrock } 14487f7322feSeschrock 14497f7322feSeschrock return (value); 14507f7322feSeschrock } 14517f7322feSeschrock 14527f7322feSeschrock static char * 14537f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 14547f7322feSeschrock { 14557f7322feSeschrock nvlist_t *nv; 14567f7322feSeschrock char *value; 14577f7322feSeschrock 1458a2eea2e1Sahrens *source = NULL; 14597f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 14607f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1461990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1462990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 14637f7322feSeschrock } else { 14642e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 14652e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 14667f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 14677f7322feSeschrock value = ""; 14687f7322feSeschrock *source = ""; 14697f7322feSeschrock } 14707f7322feSeschrock 14717f7322feSeschrock return (value); 14727f7322feSeschrock } 14737f7322feSeschrock 1474fa9e4066Sahrens /* 1475fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1476fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1477fa9e4066Sahrens * 1478fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1479fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1480fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1481fa9e4066Sahrens * the source "temporary". 1482fa9e4066Sahrens */ 148399653d4eSeschrock static int 1484990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 148599653d4eSeschrock char **source, uint64_t *val) 1486fa9e4066Sahrens { 1487bd00f61bSrm zfs_cmd_t zc = { 0 }; 148896510749Stimh nvlist_t *zplprops = NULL; 1489fa9e4066Sahrens struct mnttab mnt; 14903ccfa83cSahrens char *mntopt_on = NULL; 14913ccfa83cSahrens char *mntopt_off = NULL; 1492fa9e4066Sahrens 1493fa9e4066Sahrens *source = NULL; 1494fa9e4066Sahrens 14953ccfa83cSahrens switch (prop) { 14963ccfa83cSahrens case ZFS_PROP_ATIME: 14973ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 14983ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 14993ccfa83cSahrens break; 15003ccfa83cSahrens 15013ccfa83cSahrens case ZFS_PROP_DEVICES: 15023ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 15033ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 15043ccfa83cSahrens break; 15053ccfa83cSahrens 15063ccfa83cSahrens case ZFS_PROP_EXEC: 15073ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 15083ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 15093ccfa83cSahrens break; 15103ccfa83cSahrens 15113ccfa83cSahrens case ZFS_PROP_READONLY: 15123ccfa83cSahrens mntopt_on = MNTOPT_RO; 15133ccfa83cSahrens mntopt_off = MNTOPT_RW; 15143ccfa83cSahrens break; 15153ccfa83cSahrens 15163ccfa83cSahrens case ZFS_PROP_SETUID: 15173ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 15183ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 15193ccfa83cSahrens break; 15203ccfa83cSahrens 15213ccfa83cSahrens case ZFS_PROP_XATTR: 15223ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 15233ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 15243ccfa83cSahrens break; 1525da6c28aaSamw 1526da6c28aaSamw case ZFS_PROP_NBMAND: 1527da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1528da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1529da6c28aaSamw break; 15303ccfa83cSahrens } 15313ccfa83cSahrens 15323bb79becSeschrock /* 15333bb79becSeschrock * Because looking up the mount options is potentially expensive 15343bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 15353bb79becSeschrock * we're looking up a property which requires its presence. 15363bb79becSeschrock */ 15373bb79becSeschrock if (!zhp->zfs_mntcheck && 15383ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1539ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 1540ebedde84SEric Taylor struct mnttab entry; 15413bb79becSeschrock 1542ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1543ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 15443ccfa83cSahrens entry.mnt_mntopts); 15453ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 15463ccfa83cSahrens return (-1); 15473ccfa83cSahrens } 15483bb79becSeschrock 15493bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 15503bb79becSeschrock } 15513bb79becSeschrock 1552fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1553fa9e4066Sahrens mnt.mnt_mntopts = ""; 1554fa9e4066Sahrens else 1555fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1556fa9e4066Sahrens 1557fa9e4066Sahrens switch (prop) { 1558fa9e4066Sahrens case ZFS_PROP_ATIME: 1559fa9e4066Sahrens case ZFS_PROP_DEVICES: 1560fa9e4066Sahrens case ZFS_PROP_EXEC: 15613ccfa83cSahrens case ZFS_PROP_READONLY: 15623ccfa83cSahrens case ZFS_PROP_SETUID: 15633ccfa83cSahrens case ZFS_PROP_XATTR: 1564da6c28aaSamw case ZFS_PROP_NBMAND: 156599653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1566fa9e4066Sahrens 15673ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 156899653d4eSeschrock *val = B_TRUE; 1569fa9e4066Sahrens if (src) 1570990b4856Slling *src = ZPROP_SRC_TEMPORARY; 15713ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 157299653d4eSeschrock *val = B_FALSE; 1573fa9e4066Sahrens if (src) 1574990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1575fa9e4066Sahrens } 157699653d4eSeschrock break; 1577fa9e4066Sahrens 15783ccfa83cSahrens case ZFS_PROP_CANMOUNT: 157999653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1580a227b7f4Shs if (*val != ZFS_CANMOUNT_ON) 1581fda77a98Srm *source = zhp->zfs_name; 1582fda77a98Srm else 1583fda77a98Srm *source = ""; /* default */ 158499653d4eSeschrock break; 1585fa9e4066Sahrens 1586fa9e4066Sahrens case ZFS_PROP_QUOTA: 1587a9799022Sck case ZFS_PROP_REFQUOTA: 1588fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1589a9799022Sck case ZFS_PROP_REFRESERVATION: 1590a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 1591a2eea2e1Sahrens if (*val == 0) 1592fa9e4066Sahrens *source = ""; /* default */ 1593fa9e4066Sahrens else 1594fa9e4066Sahrens *source = zhp->zfs_name; 159599653d4eSeschrock break; 1596fa9e4066Sahrens 1597fa9e4066Sahrens case ZFS_PROP_MOUNTED: 159899653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 159999653d4eSeschrock break; 1600fa9e4066Sahrens 160139c23413Seschrock case ZFS_PROP_NUMCLONES: 160239c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 160339c23413Seschrock break; 160439c23413Seschrock 1605bd00f61bSrm case ZFS_PROP_VERSION: 1606de8267e0Stimh case ZFS_PROP_NORMALIZE: 1607de8267e0Stimh case ZFS_PROP_UTF8ONLY: 1608de8267e0Stimh case ZFS_PROP_CASE: 1609de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1610de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 16113d7934e1Srm return (-1); 1612bd00f61bSrm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1613de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1614de8267e0Stimh zcmd_free_nvlists(&zc); 1615f4b94bdeSMatthew Ahrens return (-1); 1616bd00f61bSrm } 1617de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1618de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1619de8267e0Stimh val) != 0) { 1620de8267e0Stimh zcmd_free_nvlists(&zc); 1621f4b94bdeSMatthew Ahrens return (-1); 1622de8267e0Stimh } 162396510749Stimh if (zplprops) 162496510749Stimh nvlist_free(zplprops); 1625de8267e0Stimh zcmd_free_nvlists(&zc); 1626bd00f61bSrm break; 1627bd00f61bSrm 1628fa9e4066Sahrens default: 1629e7437265Sahrens switch (zfs_prop_get_type(prop)) { 163091ebeef5Sahrens case PROP_TYPE_NUMBER: 163191ebeef5Sahrens case PROP_TYPE_INDEX: 1632e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 163374e7dc98SMatthew Ahrens /* 163414843421SMatthew Ahrens * If we tried to use a default value for a 163574e7dc98SMatthew Ahrens * readonly property, it means that it was not 163674e7dc98SMatthew Ahrens * present; return an error. 163774e7dc98SMatthew Ahrens */ 163874e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 163974e7dc98SMatthew Ahrens *source && (*source)[0] == '\0') { 164074e7dc98SMatthew Ahrens return (-1); 164174e7dc98SMatthew Ahrens } 1642e7437265Sahrens break; 1643e7437265Sahrens 164491ebeef5Sahrens case PROP_TYPE_STRING: 1645e7437265Sahrens default: 1646e7437265Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1647e7437265Sahrens "cannot get non-numeric property")); 1648e7437265Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 1649e7437265Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 1650e7437265Sahrens } 1651fa9e4066Sahrens } 1652fa9e4066Sahrens 1653fa9e4066Sahrens return (0); 1654fa9e4066Sahrens } 1655fa9e4066Sahrens 1656fa9e4066Sahrens /* 1657fa9e4066Sahrens * Calculate the source type, given the raw source string. 1658fa9e4066Sahrens */ 1659fa9e4066Sahrens static void 1660990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1661fa9e4066Sahrens char *statbuf, size_t statlen) 1662fa9e4066Sahrens { 1663990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1664fa9e4066Sahrens return; 1665fa9e4066Sahrens 1666fa9e4066Sahrens if (source == NULL) { 1667990b4856Slling *srctype = ZPROP_SRC_NONE; 1668fa9e4066Sahrens } else if (source[0] == '\0') { 1669990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 1670fa9e4066Sahrens } else { 1671fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1672990b4856Slling *srctype = ZPROP_SRC_LOCAL; 1673fa9e4066Sahrens } else { 1674fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 1675990b4856Slling *srctype = ZPROP_SRC_INHERITED; 1676fa9e4066Sahrens } 1677fa9e4066Sahrens } 1678fa9e4066Sahrens 1679fa9e4066Sahrens } 1680fa9e4066Sahrens 1681fa9e4066Sahrens /* 1682fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1683fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1684fa9e4066Sahrens * human-readable form. 1685fa9e4066Sahrens * 1686fa9e4066Sahrens * Returns 0 on success, or -1 on error. 1687fa9e4066Sahrens */ 1688fa9e4066Sahrens int 1689fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 1690990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1691fa9e4066Sahrens { 1692fa9e4066Sahrens char *source = NULL; 1693fa9e4066Sahrens uint64_t val; 1694fa9e4066Sahrens char *str; 1695e9dbad6fSeschrock const char *strval; 1696fa9e4066Sahrens 1697fa9e4066Sahrens /* 1698fa9e4066Sahrens * Check to see if this property applies to our object 1699fa9e4066Sahrens */ 1700fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1701fa9e4066Sahrens return (-1); 1702fa9e4066Sahrens 1703fa9e4066Sahrens if (src) 1704990b4856Slling *src = ZPROP_SRC_NONE; 1705fa9e4066Sahrens 1706fa9e4066Sahrens switch (prop) { 1707fa9e4066Sahrens case ZFS_PROP_CREATION: 1708fa9e4066Sahrens /* 1709fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 1710fa9e4066Sahrens * this into a string unless 'literal' is specified. 1711fa9e4066Sahrens */ 1712fa9e4066Sahrens { 1713a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 1714a2eea2e1Sahrens time_t time = (time_t)val; 1715fa9e4066Sahrens struct tm t; 1716fa9e4066Sahrens 1717fa9e4066Sahrens if (literal || 1718fa9e4066Sahrens localtime_r(&time, &t) == NULL || 1719fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1720fa9e4066Sahrens &t) == 0) 1721a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1722fa9e4066Sahrens } 1723fa9e4066Sahrens break; 1724fa9e4066Sahrens 1725fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 1726fa9e4066Sahrens /* 1727fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 1728fa9e4066Sahrens * 1729fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 1730fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 1731fa9e4066Sahrens * after our ancestor and append it to the inherited value. 1732fa9e4066Sahrens * 1733fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 1734fa9e4066Sahrens * root to any values we return. 1735fa9e4066Sahrens */ 173629ab75c9Srm 17377f7322feSeschrock str = getprop_string(zhp, prop, &source); 1738fa9e4066Sahrens 1739b21718f0Sgw if (str[0] == '/') { 174029ab75c9Srm char buf[MAXPATHLEN]; 174129ab75c9Srm char *root = buf; 17427f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 1743fa9e4066Sahrens 1744fa9e4066Sahrens if (relpath[0] == '/') 1745fa9e4066Sahrens relpath++; 1746b21718f0Sgw 174729ab75c9Srm if ((zpool_get_prop(zhp->zpool_hdl, 174829ab75c9Srm ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 174929ab75c9Srm (strcmp(root, "-") == 0)) 175029ab75c9Srm root[0] = '\0'; 1751b21718f0Sgw /* 1752b21718f0Sgw * Special case an alternate root of '/'. This will 1753b21718f0Sgw * avoid having multiple leading slashes in the 1754b21718f0Sgw * mountpoint path. 1755b21718f0Sgw */ 1756b21718f0Sgw if (strcmp(root, "/") == 0) 1757b21718f0Sgw root++; 1758b21718f0Sgw 1759b21718f0Sgw /* 1760b21718f0Sgw * If the mountpoint is '/' then skip over this 1761b21718f0Sgw * if we are obtaining either an alternate root or 1762b21718f0Sgw * an inherited mountpoint. 1763b21718f0Sgw */ 1764b21718f0Sgw if (str[1] == '\0' && (root[0] != '\0' || 1765b21718f0Sgw relpath[0] != '\0')) 17667f7322feSeschrock str++; 1767fa9e4066Sahrens 1768fa9e4066Sahrens if (relpath[0] == '\0') 1769fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 17707f7322feSeschrock root, str); 1771fa9e4066Sahrens else 1772fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 17737f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 1774fa9e4066Sahrens relpath); 1775fa9e4066Sahrens } else { 1776fa9e4066Sahrens /* 'legacy' or 'none' */ 17777f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 1778fa9e4066Sahrens } 1779fa9e4066Sahrens 1780fa9e4066Sahrens break; 1781fa9e4066Sahrens 1782fa9e4066Sahrens case ZFS_PROP_ORIGIN: 1783a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1784fa9e4066Sahrens proplen); 1785fa9e4066Sahrens /* 1786fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 1787fa9e4066Sahrens * it doesn't apply to this dataset. 1788fa9e4066Sahrens */ 1789fa9e4066Sahrens if (propbuf[0] == '\0') 1790fa9e4066Sahrens return (-1); 1791fa9e4066Sahrens break; 1792fa9e4066Sahrens 1793fa9e4066Sahrens case ZFS_PROP_QUOTA: 1794a9799022Sck case ZFS_PROP_REFQUOTA: 1795fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1796a9799022Sck case ZFS_PROP_REFRESERVATION: 1797a9799022Sck 179899653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 179999653d4eSeschrock return (-1); 1800fa9e4066Sahrens 1801fa9e4066Sahrens /* 1802fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 1803fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 1804fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 1805fa9e4066Sahrens * that its set locally. 1806fa9e4066Sahrens */ 1807fa9e4066Sahrens if (val == 0) { 1808fa9e4066Sahrens if (literal) 1809fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 1810fa9e4066Sahrens else 1811fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 1812fa9e4066Sahrens } else { 1813fa9e4066Sahrens if (literal) 18145ad82045Snd (void) snprintf(propbuf, proplen, "%llu", 1815b1b8ab34Slling (u_longlong_t)val); 1816fa9e4066Sahrens else 1817fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 1818fa9e4066Sahrens } 1819fa9e4066Sahrens break; 1820fa9e4066Sahrens 1821fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 182299653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 182399653d4eSeschrock return (-1); 18245ad82045Snd (void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t) 18255ad82045Snd val / 100, (longlong_t)val % 100); 1826fa9e4066Sahrens break; 1827fa9e4066Sahrens 1828fa9e4066Sahrens case ZFS_PROP_TYPE: 1829fa9e4066Sahrens switch (zhp->zfs_type) { 1830fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 1831fa9e4066Sahrens str = "filesystem"; 1832fa9e4066Sahrens break; 1833fa9e4066Sahrens case ZFS_TYPE_VOLUME: 1834fa9e4066Sahrens str = "volume"; 1835fa9e4066Sahrens break; 1836fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 1837fa9e4066Sahrens str = "snapshot"; 1838fa9e4066Sahrens break; 1839fa9e4066Sahrens default: 184099653d4eSeschrock abort(); 1841fa9e4066Sahrens } 1842fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 1843fa9e4066Sahrens break; 1844fa9e4066Sahrens 1845fa9e4066Sahrens case ZFS_PROP_MOUNTED: 1846fa9e4066Sahrens /* 1847fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 1848fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 1849fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 1850fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 1851fa9e4066Sahrens */ 185299653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 185399653d4eSeschrock src, &source, &val) != 0) 185499653d4eSeschrock return (-1); 185599653d4eSeschrock if (val) 1856fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 1857fa9e4066Sahrens else 1858fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 1859fa9e4066Sahrens break; 1860fa9e4066Sahrens 1861fa9e4066Sahrens case ZFS_PROP_NAME: 1862fa9e4066Sahrens /* 1863fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 1864fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 1865fa9e4066Sahrens * consumers. 1866fa9e4066Sahrens */ 1867fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 1868fa9e4066Sahrens break; 1869fa9e4066Sahrens 1870fa9e4066Sahrens default: 1871e7437265Sahrens switch (zfs_prop_get_type(prop)) { 187291ebeef5Sahrens case PROP_TYPE_NUMBER: 1873e7437265Sahrens if (get_numeric_property(zhp, prop, src, 1874e7437265Sahrens &source, &val) != 0) 1875e7437265Sahrens return (-1); 1876e7437265Sahrens if (literal) 1877e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 1878e7437265Sahrens (u_longlong_t)val); 1879e7437265Sahrens else 1880e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 1881e7437265Sahrens break; 1882e7437265Sahrens 188391ebeef5Sahrens case PROP_TYPE_STRING: 1884e7437265Sahrens (void) strlcpy(propbuf, 1885e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 1886e7437265Sahrens break; 1887e7437265Sahrens 188891ebeef5Sahrens case PROP_TYPE_INDEX: 1889fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 1890fe192f76Sahrens &source, &val) != 0) 1891fe192f76Sahrens return (-1); 1892fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 1893e7437265Sahrens return (-1); 1894e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 1895e7437265Sahrens break; 1896e7437265Sahrens 1897e7437265Sahrens default: 1898e7437265Sahrens abort(); 1899e7437265Sahrens } 1900fa9e4066Sahrens } 1901fa9e4066Sahrens 1902fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 1903fa9e4066Sahrens 1904fa9e4066Sahrens return (0); 1905fa9e4066Sahrens } 1906fa9e4066Sahrens 1907fa9e4066Sahrens /* 1908fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 1909fa9e4066Sahrens * the given property is the appropriate type; should only be used with 1910fa9e4066Sahrens * hard-coded property types. 1911fa9e4066Sahrens */ 1912fa9e4066Sahrens uint64_t 1913fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 1914fa9e4066Sahrens { 1915fa9e4066Sahrens char *source; 191699653d4eSeschrock uint64_t val; 191799653d4eSeschrock 19183cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 1919fa9e4066Sahrens 192099653d4eSeschrock return (val); 1921fa9e4066Sahrens } 1922fa9e4066Sahrens 19237b97dc1aSrm int 19247b97dc1aSrm zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 19257b97dc1aSrm { 19267b97dc1aSrm char buf[64]; 19277b97dc1aSrm 192814843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 19297b97dc1aSrm return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 19307b97dc1aSrm } 19317b97dc1aSrm 1932fa9e4066Sahrens /* 1933fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 1934fa9e4066Sahrens */ 1935fa9e4066Sahrens int 1936fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 1937990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 1938fa9e4066Sahrens { 1939fa9e4066Sahrens char *source; 1940fa9e4066Sahrens 1941fa9e4066Sahrens /* 1942fa9e4066Sahrens * Check to see if this property applies to our object 1943fa9e4066Sahrens */ 1944e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 1945ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 194699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 194799653d4eSeschrock zfs_prop_to_name(prop))); 1948e45ce728Sahrens } 1949fa9e4066Sahrens 1950fa9e4066Sahrens if (src) 1951990b4856Slling *src = ZPROP_SRC_NONE; 1952fa9e4066Sahrens 195399653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 195499653d4eSeschrock return (-1); 1955fa9e4066Sahrens 1956fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 1957fa9e4066Sahrens 1958fa9e4066Sahrens return (0); 1959fa9e4066Sahrens } 1960fa9e4066Sahrens 196114843421SMatthew Ahrens static int 196214843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 196314843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 196414843421SMatthew Ahrens { 196514843421SMatthew Ahrens idmap_handle_t *idmap_hdl = NULL; 196614843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 196714843421SMatthew Ahrens idmap_stat status; 196814843421SMatthew Ahrens int err = EINVAL; 196914843421SMatthew Ahrens 197014843421SMatthew Ahrens if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 197114843421SMatthew Ahrens goto out; 197214843421SMatthew Ahrens if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 197314843421SMatthew Ahrens goto out; 197414843421SMatthew Ahrens 197514843421SMatthew Ahrens if (isuser) { 197614843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 197714843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 197814843421SMatthew Ahrens } else { 197914843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 198014843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 198114843421SMatthew Ahrens } 198214843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 198314843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 198414843421SMatthew Ahrens status == IDMAP_SUCCESS) 198514843421SMatthew Ahrens err = 0; 198614843421SMatthew Ahrens else 198714843421SMatthew Ahrens err = EINVAL; 198814843421SMatthew Ahrens out: 198914843421SMatthew Ahrens if (get_hdl) 199014843421SMatthew Ahrens idmap_get_destroy(get_hdl); 199114843421SMatthew Ahrens if (idmap_hdl) 199214843421SMatthew Ahrens (void) idmap_fini(idmap_hdl); 199314843421SMatthew Ahrens return (err); 199414843421SMatthew Ahrens } 199514843421SMatthew Ahrens 199614843421SMatthew Ahrens /* 199714843421SMatthew Ahrens * convert the propname into parameters needed by kernel 199814843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 199914843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 200014843421SMatthew Ahrens */ 200114843421SMatthew Ahrens static int 200214843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 200314843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 200414843421SMatthew Ahrens { 200514843421SMatthew Ahrens zfs_userquota_prop_t type; 200614843421SMatthew Ahrens char *cp, *end; 20073b12c289SMatthew Ahrens char *numericsid = NULL; 200814843421SMatthew Ahrens boolean_t isuser; 200914843421SMatthew Ahrens 201014843421SMatthew Ahrens domain[0] = '\0'; 201114843421SMatthew Ahrens 201214843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 201314843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 201414843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 201514843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 201614843421SMatthew Ahrens break; 201714843421SMatthew Ahrens } 201814843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 201914843421SMatthew Ahrens return (EINVAL); 202014843421SMatthew Ahrens *typep = type; 202114843421SMatthew Ahrens 202214843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 202314843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 202414843421SMatthew Ahrens 202514843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 202614843421SMatthew Ahrens 202714843421SMatthew Ahrens if (strchr(cp, '@')) { 202814843421SMatthew Ahrens /* 202914843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 20303b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 203114843421SMatthew Ahrens */ 20323b12c289SMatthew Ahrens directory_error_t e; 203314843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 203414843421SMatthew Ahrens return (ENOENT); 20353b12c289SMatthew Ahrens if (isuser) { 20363b12c289SMatthew Ahrens e = directory_sid_from_user_name(NULL, 20373b12c289SMatthew Ahrens cp, &numericsid); 20383b12c289SMatthew Ahrens } else { 20393b12c289SMatthew Ahrens e = directory_sid_from_group_name(NULL, 20403b12c289SMatthew Ahrens cp, &numericsid); 20413b12c289SMatthew Ahrens } 20423b12c289SMatthew Ahrens if (e != NULL) { 20433b12c289SMatthew Ahrens directory_error_free(e); 204414843421SMatthew Ahrens return (ENOENT); 20453b12c289SMatthew Ahrens } 20463b12c289SMatthew Ahrens if (numericsid == NULL) 204714843421SMatthew Ahrens return (ENOENT); 20483b12c289SMatthew Ahrens cp = numericsid; 20493b12c289SMatthew Ahrens /* will be further decoded below */ 20503b12c289SMatthew Ahrens } 20513b12c289SMatthew Ahrens 20523b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 205314843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 20543b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 205514843421SMatthew Ahrens cp = strrchr(domain, '-'); 205614843421SMatthew Ahrens *cp = '\0'; 205714843421SMatthew Ahrens cp++; 205814843421SMatthew Ahrens 205914843421SMatthew Ahrens errno = 0; 206014843421SMatthew Ahrens *ridp = strtoull(cp, &end, 10); 20613b12c289SMatthew Ahrens if (numericsid) { 20623b12c289SMatthew Ahrens free(numericsid); 20633b12c289SMatthew Ahrens numericsid = NULL; 20643b12c289SMatthew Ahrens } 2065777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 206614843421SMatthew Ahrens return (EINVAL); 206714843421SMatthew Ahrens } else if (!isdigit(*cp)) { 206814843421SMatthew Ahrens /* 206914843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 207014843421SMatthew Ahrens * turned into a uid/gid 207114843421SMatthew Ahrens */ 207214843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 207314843421SMatthew Ahrens return (ENOENT); 207414843421SMatthew Ahrens if (isuser) { 207514843421SMatthew Ahrens struct passwd *pw; 207614843421SMatthew Ahrens pw = getpwnam(cp); 207714843421SMatthew Ahrens if (pw == NULL) 207814843421SMatthew Ahrens return (ENOENT); 207914843421SMatthew Ahrens *ridp = pw->pw_uid; 208014843421SMatthew Ahrens } else { 208114843421SMatthew Ahrens struct group *gr; 208214843421SMatthew Ahrens gr = getgrnam(cp); 208314843421SMatthew Ahrens if (gr == NULL) 208414843421SMatthew Ahrens return (ENOENT); 208514843421SMatthew Ahrens *ridp = gr->gr_gid; 208614843421SMatthew Ahrens } 208714843421SMatthew Ahrens } else { 208814843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 208914843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 209014843421SMatthew Ahrens idmap_rid_t rid; 209114843421SMatthew Ahrens char *mapdomain; 209214843421SMatthew Ahrens 209314843421SMatthew Ahrens if (*end != '\0') 209414843421SMatthew Ahrens return (EINVAL); 209514843421SMatthew Ahrens if (id > MAXUID) { 209614843421SMatthew Ahrens /* It's an ephemeral ID. */ 209714843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 209814843421SMatthew Ahrens &mapdomain, &rid) != 0) 209914843421SMatthew Ahrens return (ENOENT); 21003b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 210114843421SMatthew Ahrens *ridp = rid; 210214843421SMatthew Ahrens } else { 210314843421SMatthew Ahrens *ridp = id; 210414843421SMatthew Ahrens } 210514843421SMatthew Ahrens } 210614843421SMatthew Ahrens 21073b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 210814843421SMatthew Ahrens return (0); 210914843421SMatthew Ahrens } 211014843421SMatthew Ahrens 2111edea4b55SLin Ling static int 2112edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2113edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 211414843421SMatthew Ahrens { 211514843421SMatthew Ahrens int err; 211614843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 211714843421SMatthew Ahrens 211814843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 211914843421SMatthew Ahrens 212014843421SMatthew Ahrens err = userquota_propname_decode(propname, 212114843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2122edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2123edea4b55SLin Ling zc.zc_objset_type = *typep; 212414843421SMatthew Ahrens if (err) 212514843421SMatthew Ahrens return (err); 212614843421SMatthew Ahrens 212714843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 212814843421SMatthew Ahrens if (err) 212914843421SMatthew Ahrens return (err); 213014843421SMatthew Ahrens 2131edea4b55SLin Ling *propvalue = zc.zc_cookie; 2132edea4b55SLin Ling return (0); 2133edea4b55SLin Ling } 2134edea4b55SLin Ling 2135edea4b55SLin Ling int 2136edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2137edea4b55SLin Ling uint64_t *propvalue) 2138edea4b55SLin Ling { 2139edea4b55SLin Ling zfs_userquota_prop_t type; 2140edea4b55SLin Ling 2141edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2142edea4b55SLin Ling &type)); 2143edea4b55SLin Ling } 2144edea4b55SLin Ling 2145edea4b55SLin Ling int 2146edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2147edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 2148edea4b55SLin Ling { 2149edea4b55SLin Ling int err; 2150edea4b55SLin Ling uint64_t propvalue; 2151edea4b55SLin Ling zfs_userquota_prop_t type; 2152edea4b55SLin Ling 2153edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2154edea4b55SLin Ling &type); 2155edea4b55SLin Ling 2156edea4b55SLin Ling if (err) 2157edea4b55SLin Ling return (err); 2158edea4b55SLin Ling 215914843421SMatthew Ahrens if (literal) { 2160edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 2161edea4b55SLin Ling } else if (propvalue == 0 && 216214843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 216314843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 216414843421SMatthew Ahrens } else { 2165edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 216614843421SMatthew Ahrens } 216714843421SMatthew Ahrens return (0); 216814843421SMatthew Ahrens } 216914843421SMatthew Ahrens 2170fa9e4066Sahrens /* 2171fa9e4066Sahrens * Returns the name of the given zfs handle. 2172fa9e4066Sahrens */ 2173fa9e4066Sahrens const char * 2174fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2175fa9e4066Sahrens { 2176fa9e4066Sahrens return (zhp->zfs_name); 2177fa9e4066Sahrens } 2178fa9e4066Sahrens 2179fa9e4066Sahrens /* 2180fa9e4066Sahrens * Returns the type of the given zfs handle. 2181fa9e4066Sahrens */ 2182fa9e4066Sahrens zfs_type_t 2183fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2184fa9e4066Sahrens { 2185fa9e4066Sahrens return (zhp->zfs_type); 2186fa9e4066Sahrens } 2187fa9e4066Sahrens 2188ebedde84SEric Taylor static int 2189ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 2190ebedde84SEric Taylor { 2191ebedde84SEric Taylor int rc; 2192ebedde84SEric Taylor uint64_t orig_cookie; 2193ebedde84SEric Taylor 2194ebedde84SEric Taylor orig_cookie = zc->zc_cookie; 2195ebedde84SEric Taylor top: 2196ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 2197ebedde84SEric Taylor rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 2198ebedde84SEric Taylor 2199ebedde84SEric Taylor if (rc == -1) { 2200ebedde84SEric Taylor switch (errno) { 2201ebedde84SEric Taylor case ENOMEM: 2202ebedde84SEric Taylor /* expand nvlist memory and try again */ 2203ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 2204ebedde84SEric Taylor zcmd_free_nvlists(zc); 2205ebedde84SEric Taylor return (-1); 2206ebedde84SEric Taylor } 2207ebedde84SEric Taylor zc->zc_cookie = orig_cookie; 2208ebedde84SEric Taylor goto top; 2209ebedde84SEric Taylor /* 2210ebedde84SEric Taylor * An errno value of ESRCH indicates normal completion. 2211ebedde84SEric Taylor * If ENOENT is returned, then the underlying dataset 2212ebedde84SEric Taylor * has been removed since we obtained the handle. 2213ebedde84SEric Taylor */ 2214ebedde84SEric Taylor case ESRCH: 2215ebedde84SEric Taylor case ENOENT: 2216ebedde84SEric Taylor rc = 1; 2217ebedde84SEric Taylor break; 2218ebedde84SEric Taylor default: 2219ebedde84SEric Taylor rc = zfs_standard_error(zhp->zfs_hdl, errno, 2220ebedde84SEric Taylor dgettext(TEXT_DOMAIN, 2221ebedde84SEric Taylor "cannot iterate filesystems")); 2222ebedde84SEric Taylor break; 2223ebedde84SEric Taylor } 2224ebedde84SEric Taylor } 2225ebedde84SEric Taylor return (rc); 2226ebedde84SEric Taylor } 2227ebedde84SEric Taylor 2228fa9e4066Sahrens /* 22297f7322feSeschrock * Iterate over all child filesystems 2230fa9e4066Sahrens */ 2231fa9e4066Sahrens int 22327f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2233fa9e4066Sahrens { 2234fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2235fa9e4066Sahrens zfs_handle_t *nzhp; 2236fa9e4066Sahrens int ret; 2237fa9e4066Sahrens 22383cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 22393cb34c60Sahrens return (0); 22403cb34c60Sahrens 2241ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2242ebedde84SEric Taylor return (-1); 2243ebedde84SEric Taylor 2244ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 2245ebedde84SEric Taylor &zc)) == 0) { 2246fa9e4066Sahrens /* 2247fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2248fa9e4066Sahrens * that the pool has since been removed. 2249fa9e4066Sahrens */ 2250ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2251ebedde84SEric Taylor &zc)) == NULL) { 2252fa9e4066Sahrens continue; 2253ebedde84SEric Taylor } 2254fa9e4066Sahrens 2255ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2256ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2257fa9e4066Sahrens return (ret); 2258ebedde84SEric Taylor } 2259fa9e4066Sahrens } 2260ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2261ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 22627f7322feSeschrock } 22637f7322feSeschrock 22647f7322feSeschrock /* 22657f7322feSeschrock * Iterate over all snapshots 22667f7322feSeschrock */ 22677f7322feSeschrock int 22687f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 22697f7322feSeschrock { 22707f7322feSeschrock zfs_cmd_t zc = { 0 }; 22717f7322feSeschrock zfs_handle_t *nzhp; 22727f7322feSeschrock int ret; 2273fa9e4066Sahrens 22743cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 22753cb34c60Sahrens return (0); 22763cb34c60Sahrens 2277ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2278ebedde84SEric Taylor return (-1); 2279ebedde84SEric Taylor while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 2280ebedde84SEric Taylor &zc)) == 0) { 2281fa9e4066Sahrens 2282ebedde84SEric Taylor if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 2283ebedde84SEric Taylor &zc)) == NULL) { 2284fa9e4066Sahrens continue; 2285ebedde84SEric Taylor } 2286fa9e4066Sahrens 2287ebedde84SEric Taylor if ((ret = func(nzhp, data)) != 0) { 2288ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2289fa9e4066Sahrens return (ret); 2290ebedde84SEric Taylor } 2291fa9e4066Sahrens } 2292ebedde84SEric Taylor zcmd_free_nvlists(&zc); 2293ebedde84SEric Taylor return ((ret < 0) ? ret : 0); 2294fa9e4066Sahrens } 2295fa9e4066Sahrens 22967f7322feSeschrock /* 22977f7322feSeschrock * Iterate over all children, snapshots and filesystems 22987f7322feSeschrock */ 22997f7322feSeschrock int 23007f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 23017f7322feSeschrock { 23027f7322feSeschrock int ret; 23037f7322feSeschrock 23047f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 23057f7322feSeschrock return (ret); 23067f7322feSeschrock 23077f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 23087f7322feSeschrock } 23097f7322feSeschrock 2310fa9e4066Sahrens /* 2311fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2312fa9e4066Sahrens * Can return NULL if this is a pool. 2313fa9e4066Sahrens */ 2314fa9e4066Sahrens static int 2315fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2316fa9e4066Sahrens { 2317fa9e4066Sahrens char *loc; 2318fa9e4066Sahrens 2319fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2320fa9e4066Sahrens return (-1); 2321fa9e4066Sahrens 2322fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2323fa9e4066Sahrens buf[loc - path] = '\0'; 2324fa9e4066Sahrens 2325fa9e4066Sahrens return (0); 2326fa9e4066Sahrens } 2327fa9e4066Sahrens 2328fa9e4066Sahrens /* 23297f1f55eaSvb * If accept_ancestor is false, then check to make sure that the given path has 23307f1f55eaSvb * a parent, and that it exists. If accept_ancestor is true, then find the 23317f1f55eaSvb * closest existing ancestor for the given path. In prefixlen return the 23327f1f55eaSvb * length of already existing prefix of the given path. We also fetch the 23337f1f55eaSvb * 'zoned' property, which is used to validate property settings when creating 23347f1f55eaSvb * new datasets. 2335fa9e4066Sahrens */ 2336fa9e4066Sahrens static int 23377f1f55eaSvb check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 23387f1f55eaSvb boolean_t accept_ancestor, int *prefixlen) 2339fa9e4066Sahrens { 2340fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2341fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2342fa9e4066Sahrens char *slash; 23437f7322feSeschrock zfs_handle_t *zhp; 234499653d4eSeschrock char errbuf[1024]; 234599653d4eSeschrock 2346deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 2347deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2348fa9e4066Sahrens 2349fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2350fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 235199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 235299653d4eSeschrock "missing dataset name")); 235399653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2354fa9e4066Sahrens } 2355fa9e4066Sahrens 2356fa9e4066Sahrens /* check to see if the pool exists */ 2357fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2358fa9e4066Sahrens slash = parent + strlen(parent); 2359fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2360fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 236199653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2362fa9e4066Sahrens errno == ENOENT) { 236399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 236499653d4eSeschrock "no such pool '%s'"), zc.zc_name); 236599653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2366fa9e4066Sahrens } 2367fa9e4066Sahrens 2368fa9e4066Sahrens /* check to see if the parent dataset exists */ 23697f1f55eaSvb while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 23707f1f55eaSvb if (errno == ENOENT && accept_ancestor) { 23717f1f55eaSvb /* 23727f1f55eaSvb * Go deeper to find an ancestor, give up on top level. 23737f1f55eaSvb */ 23747f1f55eaSvb if (parent_name(parent, parent, sizeof (parent)) != 0) { 23757f1f55eaSvb zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 23767f1f55eaSvb "no such pool '%s'"), zc.zc_name); 23777f1f55eaSvb return (zfs_error(hdl, EZFS_NOENT, errbuf)); 23787f1f55eaSvb } 23797f1f55eaSvb } else if (errno == ENOENT) { 238099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 238199653d4eSeschrock "parent does not exist")); 238299653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 23837f1f55eaSvb } else 238499653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2385fa9e4066Sahrens } 2386fa9e4066Sahrens 2387e9dbad6fSeschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2388fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2389e9dbad6fSeschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 239099653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 23917f7322feSeschrock zfs_close(zhp); 2392fa9e4066Sahrens return (-1); 2393fa9e4066Sahrens } 2394fa9e4066Sahrens 2395fa9e4066Sahrens /* make sure parent is a filesystem */ 23967f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 239799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 239899653d4eSeschrock "parent is not a filesystem")); 239999653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 24007f7322feSeschrock zfs_close(zhp); 2401fa9e4066Sahrens return (-1); 2402fa9e4066Sahrens } 2403fa9e4066Sahrens 24047f7322feSeschrock zfs_close(zhp); 24057f1f55eaSvb if (prefixlen != NULL) 24067f1f55eaSvb *prefixlen = strlen(parent); 24077f1f55eaSvb return (0); 24087f1f55eaSvb } 24097f1f55eaSvb 24107f1f55eaSvb /* 24117f1f55eaSvb * Finds whether the dataset of the given type(s) exists. 24127f1f55eaSvb */ 24137f1f55eaSvb boolean_t 24147f1f55eaSvb zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 24157f1f55eaSvb { 24167f1f55eaSvb zfs_handle_t *zhp; 24177f1f55eaSvb 2418f18faf3fSek if (!zfs_validate_name(hdl, path, types, B_FALSE)) 24197f1f55eaSvb return (B_FALSE); 24207f1f55eaSvb 24217f1f55eaSvb /* 24227f1f55eaSvb * Try to get stats for the dataset, which will tell us if it exists. 24237f1f55eaSvb */ 24247f1f55eaSvb if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 24257f1f55eaSvb int ds_type = zhp->zfs_type; 24267f1f55eaSvb 24277f1f55eaSvb zfs_close(zhp); 24287f1f55eaSvb if (types & ds_type) 24297f1f55eaSvb return (B_TRUE); 24307f1f55eaSvb } 24317f1f55eaSvb return (B_FALSE); 24327f1f55eaSvb } 24337f1f55eaSvb 24343cb34c60Sahrens /* 24353cb34c60Sahrens * Given a path to 'target', create all the ancestors between 24363cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 24373cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 24383cb34c60Sahrens */ 24393cb34c60Sahrens int 24403cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 24413cb34c60Sahrens { 24423cb34c60Sahrens zfs_handle_t *h; 24433cb34c60Sahrens char *cp; 24443cb34c60Sahrens const char *opname; 24453cb34c60Sahrens 24463cb34c60Sahrens /* make sure prefix exists */ 24473cb34c60Sahrens cp = target + prefixlen; 24483cb34c60Sahrens if (*cp != '/') { 24493cb34c60Sahrens assert(strchr(cp, '/') == NULL); 24503cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 24513cb34c60Sahrens } else { 24523cb34c60Sahrens *cp = '\0'; 24533cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 24543cb34c60Sahrens *cp = '/'; 24553cb34c60Sahrens } 24563cb34c60Sahrens if (h == NULL) 24573cb34c60Sahrens return (-1); 24583cb34c60Sahrens zfs_close(h); 24593cb34c60Sahrens 24603cb34c60Sahrens /* 24613cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 24623cb34c60Sahrens * up to the prefixlen-long one. 24633cb34c60Sahrens */ 24643cb34c60Sahrens for (cp = target + prefixlen + 1; 24653cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 24663cb34c60Sahrens char *logstr; 24673cb34c60Sahrens 24683cb34c60Sahrens *cp = '\0'; 24693cb34c60Sahrens 24703cb34c60Sahrens h = make_dataset_handle(hdl, target); 24713cb34c60Sahrens if (h) { 24723cb34c60Sahrens /* it already exists, nothing to do here */ 24733cb34c60Sahrens zfs_close(h); 24743cb34c60Sahrens continue; 24753cb34c60Sahrens } 24763cb34c60Sahrens 24773cb34c60Sahrens logstr = hdl->libzfs_log_str; 24783cb34c60Sahrens hdl->libzfs_log_str = NULL; 24793cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 24803cb34c60Sahrens NULL) != 0) { 24813cb34c60Sahrens hdl->libzfs_log_str = logstr; 24823cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 24833cb34c60Sahrens goto ancestorerr; 24843cb34c60Sahrens } 24853cb34c60Sahrens 24863cb34c60Sahrens hdl->libzfs_log_str = logstr; 24873cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 24883cb34c60Sahrens if (h == NULL) { 24893cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 24903cb34c60Sahrens goto ancestorerr; 24913cb34c60Sahrens } 24923cb34c60Sahrens 24933cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 24943cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 24953cb34c60Sahrens goto ancestorerr; 24963cb34c60Sahrens } 24973cb34c60Sahrens 24983cb34c60Sahrens if (zfs_share(h) != 0) { 24993cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 25003cb34c60Sahrens goto ancestorerr; 25013cb34c60Sahrens } 25023cb34c60Sahrens 25033cb34c60Sahrens zfs_close(h); 25043cb34c60Sahrens } 25053cb34c60Sahrens 25063cb34c60Sahrens return (0); 25073cb34c60Sahrens 25083cb34c60Sahrens ancestorerr: 25093cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25103cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 25113cb34c60Sahrens return (-1); 25123cb34c60Sahrens } 25133cb34c60Sahrens 25147f1f55eaSvb /* 25157f1f55eaSvb * Creates non-existing ancestors of the given path. 25167f1f55eaSvb */ 25177f1f55eaSvb int 25187f1f55eaSvb zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 25197f1f55eaSvb { 25207f1f55eaSvb int prefix; 25217f1f55eaSvb uint64_t zoned; 25227f1f55eaSvb char *path_copy; 25237f1f55eaSvb int rc; 25247f1f55eaSvb 25257f1f55eaSvb if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 25267f1f55eaSvb return (-1); 25277f1f55eaSvb 25287f1f55eaSvb if ((path_copy = strdup(path)) != NULL) { 25297f1f55eaSvb rc = create_parents(hdl, path_copy, prefix); 25307f1f55eaSvb free(path_copy); 25317f1f55eaSvb } 25327f1f55eaSvb if (path_copy == NULL || rc != 0) 25337f1f55eaSvb return (-1); 25347f1f55eaSvb 2535fa9e4066Sahrens return (0); 2536fa9e4066Sahrens } 2537fa9e4066Sahrens 2538fa9e4066Sahrens /* 2539e9dbad6fSeschrock * Create a new filesystem or volume. 2540fa9e4066Sahrens */ 2541fa9e4066Sahrens int 254299653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2543e9dbad6fSeschrock nvlist_t *props) 2544fa9e4066Sahrens { 2545fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2546fa9e4066Sahrens int ret; 2547fa9e4066Sahrens uint64_t size = 0; 2548fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 254999653d4eSeschrock char errbuf[1024]; 2550e9dbad6fSeschrock uint64_t zoned; 255199653d4eSeschrock 255299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 255399653d4eSeschrock "cannot create '%s'"), path); 2554fa9e4066Sahrens 2555fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2556f18faf3fSek if (!zfs_validate_name(hdl, path, type, B_TRUE)) 255799653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2558fa9e4066Sahrens 2559fa9e4066Sahrens /* validate parents exist */ 25607f1f55eaSvb if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2561fa9e4066Sahrens return (-1); 2562fa9e4066Sahrens 2563fa9e4066Sahrens /* 2564fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2565fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2566fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2567fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2568fa9e4066Sahrens * first try to see if the dataset exists. 2569fa9e4066Sahrens */ 2570fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2571990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 257299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 257399653d4eSeschrock "dataset already exists")); 257499653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2575fa9e4066Sahrens } 2576fa9e4066Sahrens 2577fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2578fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2579fa9e4066Sahrens else 2580fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2581fa9e4066Sahrens 25820a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 2583b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2584e9dbad6fSeschrock return (-1); 2585e9dbad6fSeschrock 2586fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 25875c5460e9Seschrock /* 25885c5460e9Seschrock * If we are creating a volume, the size and block size must 25895c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 25905c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 25915c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 25925c5460e9Seschrock * zero. 25935c5460e9Seschrock */ 2594e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2595e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2596e9dbad6fSeschrock nvlist_free(props); 259799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2598e9dbad6fSeschrock "missing volume size")); 2599e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2600e9dbad6fSeschrock } 2601e9dbad6fSeschrock 2602e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2603e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2604e9dbad6fSeschrock &blocksize)) != 0) { 2605e9dbad6fSeschrock if (ret == ENOENT) { 2606e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2607e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2608e9dbad6fSeschrock } else { 2609e9dbad6fSeschrock nvlist_free(props); 2610e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2611e9dbad6fSeschrock "missing volume block size")); 2612e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2613e9dbad6fSeschrock } 2614fa9e4066Sahrens } 2615fa9e4066Sahrens 2616e9dbad6fSeschrock if (size == 0) { 2617e9dbad6fSeschrock nvlist_free(props); 261899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2619e9dbad6fSeschrock "volume size cannot be zero")); 2620e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 26215c5460e9Seschrock } 26225c5460e9Seschrock 26235c5460e9Seschrock if (size % blocksize != 0) { 2624e9dbad6fSeschrock nvlist_free(props); 262599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2626e9dbad6fSeschrock "volume size must be a multiple of volume block " 2627e9dbad6fSeschrock "size")); 2628e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 26295c5460e9Seschrock } 2630fa9e4066Sahrens } 2631fa9e4066Sahrens 2632990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2633e9dbad6fSeschrock return (-1); 2634e9dbad6fSeschrock nvlist_free(props); 2635e9dbad6fSeschrock 2636fa9e4066Sahrens /* create the dataset */ 2637ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2638fa9e4066Sahrens 2639b1b8ab34Slling if (ret == 0 && type == ZFS_TYPE_VOLUME) { 264099653d4eSeschrock ret = zvol_create_link(hdl, path); 2641b1b8ab34Slling if (ret) { 2642b1b8ab34Slling (void) zfs_standard_error(hdl, errno, 2643b1b8ab34Slling dgettext(TEXT_DOMAIN, 2644b1b8ab34Slling "Volume successfully created, but device links " 2645b1b8ab34Slling "were not created")); 2646b1b8ab34Slling zcmd_free_nvlists(&zc); 2647b1b8ab34Slling return (-1); 2648b1b8ab34Slling } 2649b1b8ab34Slling } 2650fa9e4066Sahrens 2651e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2652e9dbad6fSeschrock 2653fa9e4066Sahrens /* check for failure */ 2654fa9e4066Sahrens if (ret != 0) { 2655fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2656fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2657fa9e4066Sahrens 2658fa9e4066Sahrens switch (errno) { 2659fa9e4066Sahrens case ENOENT: 266099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 266199653d4eSeschrock "no such parent '%s'"), parent); 266299653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2663fa9e4066Sahrens 2664fa9e4066Sahrens case EINVAL: 266599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2666d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 266799653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2668fa9e4066Sahrens 2669fa9e4066Sahrens case EDOM: 267099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2671e9dbad6fSeschrock "volume block size must be power of 2 from " 2672e9dbad6fSeschrock "%u to %uk"), 2673fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2674fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 267599653d4eSeschrock 2676e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 267799653d4eSeschrock 267840feaa91Sahrens case ENOTSUP: 267940feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 268040feaa91Sahrens "pool must be upgraded to set this " 268140feaa91Sahrens "property or value")); 268240feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2683fa9e4066Sahrens #ifdef _ILP32 2684fa9e4066Sahrens case EOVERFLOW: 2685fa9e4066Sahrens /* 2686fa9e4066Sahrens * This platform can't address a volume this big. 2687fa9e4066Sahrens */ 268899653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 268999653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 269099653d4eSeschrock errbuf)); 2691fa9e4066Sahrens #endif 269299653d4eSeschrock /* FALLTHROUGH */ 2693fa9e4066Sahrens default: 269499653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2695fa9e4066Sahrens } 2696fa9e4066Sahrens } 2697fa9e4066Sahrens 2698fa9e4066Sahrens return (0); 2699fa9e4066Sahrens } 2700fa9e4066Sahrens 2701fa9e4066Sahrens /* 2702fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2703fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2704fa9e4066Sahrens */ 2705fa9e4066Sahrens int 2706842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2707fa9e4066Sahrens { 2708fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2709fa9e4066Sahrens 2710fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2711fa9e4066Sahrens 2712e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2713f3861e1aSahl /* 2714ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2715ecd6cf80Smarks * abort the request. This would only happen for a 2716ecd6cf80Smarks * non-privileged user. 2717f3861e1aSahl */ 2718ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2719ecd6cf80Smarks return (-1); 2720ecd6cf80Smarks } 2721f3861e1aSahl 272299653d4eSeschrock if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 2723fa9e4066Sahrens return (-1); 2724fa9e4066Sahrens 2725fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2726fa9e4066Sahrens } else { 2727fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2728fa9e4066Sahrens } 2729fa9e4066Sahrens 2730842727c2SChris Kirby zc.zc_defer_destroy = defer; 2731ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2732ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 273399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 273499653d4eSeschrock zhp->zfs_name)); 27351d452cf5Sahrens } 2736fa9e4066Sahrens 2737fa9e4066Sahrens remove_mountpoint(zhp); 2738fa9e4066Sahrens 2739fa9e4066Sahrens return (0); 2740fa9e4066Sahrens } 2741fa9e4066Sahrens 27421d452cf5Sahrens struct destroydata { 27431d452cf5Sahrens char *snapname; 27441d452cf5Sahrens boolean_t gotone; 27453ccfa83cSahrens boolean_t closezhp; 27461d452cf5Sahrens }; 27471d452cf5Sahrens 27481d452cf5Sahrens static int 27491d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg) 27501d452cf5Sahrens { 27511d452cf5Sahrens struct destroydata *dd = arg; 27521d452cf5Sahrens zfs_handle_t *szhp; 27531d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 27543ccfa83cSahrens boolean_t closezhp = dd->closezhp; 27553ccfa83cSahrens int rv; 27561d452cf5Sahrens 2757e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2758e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2759e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 27601d452cf5Sahrens 27611d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 27621d452cf5Sahrens if (szhp) { 27631d452cf5Sahrens dd->gotone = B_TRUE; 27641d452cf5Sahrens zfs_close(szhp); 27651d452cf5Sahrens } 27661d452cf5Sahrens 27671d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 27681d452cf5Sahrens (void) zvol_remove_link(zhp->zfs_hdl, name); 27691d452cf5Sahrens /* 27701d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 27711d452cf5Sahrens * return an error, because then we wouldn't visit all 27721d452cf5Sahrens * the volumes. 27731d452cf5Sahrens */ 27741d452cf5Sahrens } 27751d452cf5Sahrens 27763ccfa83cSahrens dd->closezhp = B_TRUE; 27773ccfa83cSahrens rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg); 27783ccfa83cSahrens if (closezhp) 27793ccfa83cSahrens zfs_close(zhp); 27803ccfa83cSahrens return (rv); 27811d452cf5Sahrens } 27821d452cf5Sahrens 27831d452cf5Sahrens /* 27841d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 27851d452cf5Sahrens */ 27861d452cf5Sahrens int 2787842727c2SChris Kirby zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 27881d452cf5Sahrens { 27891d452cf5Sahrens zfs_cmd_t zc = { 0 }; 27901d452cf5Sahrens int ret; 27911d452cf5Sahrens struct destroydata dd = { 0 }; 27921d452cf5Sahrens 27931d452cf5Sahrens dd.snapname = snapname; 27941d452cf5Sahrens (void) zfs_remove_link_cb(zhp, &dd); 27951d452cf5Sahrens 27961d452cf5Sahrens if (!dd.gotone) { 2797ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 27981d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 27991d452cf5Sahrens zhp->zfs_name, snapname)); 28001d452cf5Sahrens } 28011d452cf5Sahrens 28021d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2803e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 2804842727c2SChris Kirby zc.zc_defer_destroy = defer; 28051d452cf5Sahrens 2806ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 28071d452cf5Sahrens if (ret != 0) { 28081d452cf5Sahrens char errbuf[1024]; 28091d452cf5Sahrens 28101d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 28111d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 28121d452cf5Sahrens 28131d452cf5Sahrens switch (errno) { 28141d452cf5Sahrens case EEXIST: 28151d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 28161d452cf5Sahrens "snapshot is cloned")); 28171d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 28181d452cf5Sahrens 28191d452cf5Sahrens default: 28201d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 28211d452cf5Sahrens errbuf)); 28221d452cf5Sahrens } 28231d452cf5Sahrens } 28241d452cf5Sahrens 28251d452cf5Sahrens return (0); 28261d452cf5Sahrens } 28271d452cf5Sahrens 2828fa9e4066Sahrens /* 2829fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 2830fa9e4066Sahrens */ 2831fa9e4066Sahrens int 2832e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 2833fa9e4066Sahrens { 2834fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2835fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2836fa9e4066Sahrens int ret; 283799653d4eSeschrock char errbuf[1024]; 283899653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2839e9dbad6fSeschrock zfs_type_t type; 2840e9dbad6fSeschrock uint64_t zoned; 2841fa9e4066Sahrens 2842fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 2843fa9e4066Sahrens 284499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 284599653d4eSeschrock "cannot create '%s'"), target); 284699653d4eSeschrock 2847fa9e4066Sahrens /* validate the target name */ 2848f18faf3fSek if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 284999653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2850fa9e4066Sahrens 2851fa9e4066Sahrens /* validate parents exist */ 28527f1f55eaSvb if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 2853fa9e4066Sahrens return (-1); 2854fa9e4066Sahrens 2855fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 2856fa9e4066Sahrens 2857fa9e4066Sahrens /* do the clone */ 2858e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2859fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 28605f8e1617Snn type = ZFS_TYPE_VOLUME; 2861e9dbad6fSeschrock } else { 2862fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 28635f8e1617Snn type = ZFS_TYPE_FILESYSTEM; 2864e9dbad6fSeschrock } 2865e9dbad6fSeschrock 2866e9dbad6fSeschrock if (props) { 28670a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 28680a48a24eStimh zhp, errbuf)) == NULL) 2869e9dbad6fSeschrock return (-1); 2870e9dbad6fSeschrock 2871990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 2872e9dbad6fSeschrock nvlist_free(props); 2873e9dbad6fSeschrock return (-1); 2874e9dbad6fSeschrock } 2875e9dbad6fSeschrock 2876e9dbad6fSeschrock nvlist_free(props); 2877e9dbad6fSeschrock } 2878fa9e4066Sahrens 2879fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 2880e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 2881ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 2882fa9e4066Sahrens 2883e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2884e9dbad6fSeschrock 2885fa9e4066Sahrens if (ret != 0) { 2886fa9e4066Sahrens switch (errno) { 2887fa9e4066Sahrens 2888fa9e4066Sahrens case ENOENT: 2889fa9e4066Sahrens /* 2890fa9e4066Sahrens * The parent doesn't exist. We should have caught this 2891fa9e4066Sahrens * above, but there may a race condition that has since 2892fa9e4066Sahrens * destroyed the parent. 2893fa9e4066Sahrens * 2894fa9e4066Sahrens * At this point, we don't know whether it's the source 2895fa9e4066Sahrens * that doesn't exist anymore, or whether the target 2896fa9e4066Sahrens * dataset doesn't exist. 2897fa9e4066Sahrens */ 289899653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 289999653d4eSeschrock "no such parent '%s'"), parent); 290099653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 2901fa9e4066Sahrens 290299653d4eSeschrock case EXDEV: 290399653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 290499653d4eSeschrock "source and target pools differ")); 290599653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 290699653d4eSeschrock errbuf)); 2907fa9e4066Sahrens 290899653d4eSeschrock default: 290999653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 291099653d4eSeschrock errbuf)); 291199653d4eSeschrock } 2912e9dbad6fSeschrock } else if (ZFS_IS_VOLUME(zhp)) { 291399653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, target); 291499653d4eSeschrock } 2915fa9e4066Sahrens 291699653d4eSeschrock return (ret); 291799653d4eSeschrock } 291899653d4eSeschrock 291999653d4eSeschrock typedef struct promote_data { 292099653d4eSeschrock char cb_mountpoint[MAXPATHLEN]; 292199653d4eSeschrock const char *cb_target; 292299653d4eSeschrock const char *cb_errbuf; 292399653d4eSeschrock uint64_t cb_pivot_txg; 292499653d4eSeschrock } promote_data_t; 292599653d4eSeschrock 292699653d4eSeschrock static int 292799653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data) 292899653d4eSeschrock { 292999653d4eSeschrock promote_data_t *pd = data; 293099653d4eSeschrock zfs_handle_t *szhp; 293199653d4eSeschrock char snapname[MAXPATHLEN]; 29323ccfa83cSahrens int rv = 0; 293399653d4eSeschrock 293499653d4eSeschrock /* We don't care about snapshots after the pivot point */ 29353ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) { 29363ccfa83cSahrens zfs_close(zhp); 293799653d4eSeschrock return (0); 29383ccfa83cSahrens } 293999653d4eSeschrock 29400b69c2f0Sahrens /* Remove the device link if it's a zvol. */ 2941e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 29420b69c2f0Sahrens (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); 294399653d4eSeschrock 294499653d4eSeschrock /* Check for conflicting names */ 2945e9dbad6fSeschrock (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); 2946e9dbad6fSeschrock (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); 294799653d4eSeschrock szhp = make_dataset_handle(zhp->zfs_hdl, snapname); 294899653d4eSeschrock if (szhp != NULL) { 294999653d4eSeschrock zfs_close(szhp); 295099653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 295199653d4eSeschrock "snapshot name '%s' from origin \n" 295299653d4eSeschrock "conflicts with '%s' from target"), 295399653d4eSeschrock zhp->zfs_name, snapname); 29543ccfa83cSahrens rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf); 295599653d4eSeschrock } 29563ccfa83cSahrens zfs_close(zhp); 29573ccfa83cSahrens return (rv); 295899653d4eSeschrock } 295999653d4eSeschrock 29600b69c2f0Sahrens static int 29610b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data) 29620b69c2f0Sahrens { 29630b69c2f0Sahrens promote_data_t *pd = data; 29640b69c2f0Sahrens 29650b69c2f0Sahrens /* We don't care about snapshots after the pivot point */ 29663ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) { 29673ccfa83cSahrens /* Create the device link if it's a zvol. */ 29683ccfa83cSahrens if (ZFS_IS_VOLUME(zhp)) 29693ccfa83cSahrens (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); 29703ccfa83cSahrens } 29710b69c2f0Sahrens 29723ccfa83cSahrens zfs_close(zhp); 29730b69c2f0Sahrens return (0); 29740b69c2f0Sahrens } 29750b69c2f0Sahrens 297699653d4eSeschrock /* 297799653d4eSeschrock * Promotes the given clone fs to be the clone parent. 297899653d4eSeschrock */ 297999653d4eSeschrock int 298099653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 298199653d4eSeschrock { 298299653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 298399653d4eSeschrock zfs_cmd_t zc = { 0 }; 298499653d4eSeschrock char parent[MAXPATHLEN]; 298599653d4eSeschrock char *cp; 298699653d4eSeschrock int ret; 298799653d4eSeschrock zfs_handle_t *pzhp; 298899653d4eSeschrock promote_data_t pd; 298999653d4eSeschrock char errbuf[1024]; 299099653d4eSeschrock 299199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 299299653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 299399653d4eSeschrock 299499653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 299599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 299699653d4eSeschrock "snapshots can not be promoted")); 299799653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 299899653d4eSeschrock } 299999653d4eSeschrock 30003cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 300199653d4eSeschrock if (parent[0] == '\0') { 300299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 300399653d4eSeschrock "not a cloned filesystem")); 300499653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 300599653d4eSeschrock } 300699653d4eSeschrock cp = strchr(parent, '@'); 300799653d4eSeschrock *cp = '\0'; 300899653d4eSeschrock 300999653d4eSeschrock /* Walk the snapshots we will be moving */ 30103cb34c60Sahrens pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); 301199653d4eSeschrock if (pzhp == NULL) 301299653d4eSeschrock return (-1); 301399653d4eSeschrock pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); 301499653d4eSeschrock zfs_close(pzhp); 301599653d4eSeschrock pd.cb_target = zhp->zfs_name; 301699653d4eSeschrock pd.cb_errbuf = errbuf; 3017990b4856Slling pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET); 301899653d4eSeschrock if (pzhp == NULL) 301999653d4eSeschrock return (-1); 302099653d4eSeschrock (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, 302199653d4eSeschrock sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); 302299653d4eSeschrock ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); 30230b69c2f0Sahrens if (ret != 0) { 30240b69c2f0Sahrens zfs_close(pzhp); 302599653d4eSeschrock return (-1); 30260b69c2f0Sahrens } 302799653d4eSeschrock 302899653d4eSeschrock /* issue the ioctl */ 30293cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3030e9dbad6fSeschrock sizeof (zc.zc_value)); 303199653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3032ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 303399653d4eSeschrock 303499653d4eSeschrock if (ret != 0) { 30350b69c2f0Sahrens int save_errno = errno; 30360b69c2f0Sahrens 30370b69c2f0Sahrens (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd); 30380b69c2f0Sahrens zfs_close(pzhp); 303999653d4eSeschrock 30400b69c2f0Sahrens switch (save_errno) { 304199653d4eSeschrock case EEXIST: 3042fa9e4066Sahrens /* 304399653d4eSeschrock * There is a conflicting snapshot name. We 304499653d4eSeschrock * should have caught this above, but they could 304599653d4eSeschrock * have renamed something in the mean time. 3046fa9e4066Sahrens */ 304799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 304899653d4eSeschrock "conflicting snapshot name from parent '%s'"), 304999653d4eSeschrock parent); 305099653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3051fa9e4066Sahrens 3052fa9e4066Sahrens default: 30530b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3054fa9e4066Sahrens } 30550b69c2f0Sahrens } else { 30560b69c2f0Sahrens (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd); 3057fa9e4066Sahrens } 3058fa9e4066Sahrens 30590b69c2f0Sahrens zfs_close(pzhp); 3060fa9e4066Sahrens return (ret); 3061fa9e4066Sahrens } 3062fa9e4066Sahrens 3063cdf5b4caSmmusante struct createdata { 3064cdf5b4caSmmusante const char *cd_snapname; 3065cdf5b4caSmmusante int cd_ifexists; 3066cdf5b4caSmmusante }; 3067cdf5b4caSmmusante 30681d452cf5Sahrens static int 30691d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg) 30701d452cf5Sahrens { 3071cdf5b4caSmmusante struct createdata *cd = arg; 3072e9dbad6fSeschrock int ret; 30731d452cf5Sahrens 30741d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 30751d452cf5Sahrens char name[MAXPATHLEN]; 30761d452cf5Sahrens 3077e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 3078e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 3079cdf5b4caSmmusante (void) strlcat(name, cd->cd_snapname, sizeof (name)); 3080cdf5b4caSmmusante (void) zvol_create_link_common(zhp->zfs_hdl, name, 3081cdf5b4caSmmusante cd->cd_ifexists); 30821d452cf5Sahrens /* 30831d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 30841d452cf5Sahrens * return an error, because then we wouldn't visit all 30851d452cf5Sahrens * the volumes. 30861d452cf5Sahrens */ 30871d452cf5Sahrens } 3088e9dbad6fSeschrock 3089cdf5b4caSmmusante ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd); 3090e9dbad6fSeschrock 3091e9dbad6fSeschrock zfs_close(zhp); 3092e9dbad6fSeschrock 3093e9dbad6fSeschrock return (ret); 30941d452cf5Sahrens } 30951d452cf5Sahrens 3096fa9e4066Sahrens /* 309772bdce51Sahl * Takes a snapshot of the given dataset. 3098fa9e4066Sahrens */ 3099fa9e4066Sahrens int 3100bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3101bb0ade09Sahrens nvlist_t *props) 3102fa9e4066Sahrens { 3103fa9e4066Sahrens const char *delim; 3104bb0ade09Sahrens char parent[ZFS_MAXNAMELEN]; 3105fa9e4066Sahrens zfs_handle_t *zhp; 3106fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3107fa9e4066Sahrens int ret; 310899653d4eSeschrock char errbuf[1024]; 3109fa9e4066Sahrens 311099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 311199653d4eSeschrock "cannot snapshot '%s'"), path); 311299653d4eSeschrock 311399653d4eSeschrock /* validate the target name */ 3114f18faf3fSek if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 311599653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3116fa9e4066Sahrens 3117bb0ade09Sahrens if (props) { 3118bb0ade09Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3119bb0ade09Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 3120bb0ade09Sahrens return (-1); 3121bb0ade09Sahrens 3122bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3123bb0ade09Sahrens nvlist_free(props); 3124bb0ade09Sahrens return (-1); 3125bb0ade09Sahrens } 3126bb0ade09Sahrens 3127bb0ade09Sahrens nvlist_free(props); 3128bb0ade09Sahrens } 3129bb0ade09Sahrens 3130fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 31311d452cf5Sahrens delim = strchr(path, '@'); 3132fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3133fa9e4066Sahrens parent[delim - path] = '\0'; 3134fa9e4066Sahrens 313599653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3136fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3137bb0ade09Sahrens zcmd_free_nvlists(&zc); 3138fa9e4066Sahrens return (-1); 3139fa9e4066Sahrens } 3140fa9e4066Sahrens 31411d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3142e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3143ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3144ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3145ecd6cf80Smarks else 3146ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 31471d452cf5Sahrens zc.zc_cookie = recursive; 3148ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3149fa9e4066Sahrens 3150bb0ade09Sahrens zcmd_free_nvlists(&zc); 3151bb0ade09Sahrens 31521d452cf5Sahrens /* 31531d452cf5Sahrens * if it was recursive, the one that actually failed will be in 31541d452cf5Sahrens * zc.zc_name. 31551d452cf5Sahrens */ 3156ecd6cf80Smarks if (ret != 0) 3157ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3158ecd6cf80Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 3159ecd6cf80Smarks 31601d452cf5Sahrens if (ret == 0 && recursive) { 3161cdf5b4caSmmusante struct createdata cd; 3162cdf5b4caSmmusante 3163cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3164cdf5b4caSmmusante cd.cd_ifexists = B_FALSE; 3165cdf5b4caSmmusante (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd); 31661d452cf5Sahrens } 3167fa9e4066Sahrens if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { 316899653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, path); 31691d452cf5Sahrens if (ret != 0) { 3170ecd6cf80Smarks (void) zfs_standard_error(hdl, errno, 3171ecd6cf80Smarks dgettext(TEXT_DOMAIN, 3172ecd6cf80Smarks "Volume successfully snapshotted, but device links " 3173ecd6cf80Smarks "were not created")); 3174ecd6cf80Smarks zfs_close(zhp); 3175ecd6cf80Smarks return (-1); 31761d452cf5Sahrens } 3177fa9e4066Sahrens } 3178fa9e4066Sahrens 317999653d4eSeschrock if (ret != 0) 318099653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3181fa9e4066Sahrens 3182fa9e4066Sahrens zfs_close(zhp); 3183fa9e4066Sahrens 3184fa9e4066Sahrens return (ret); 3185fa9e4066Sahrens } 3186fa9e4066Sahrens 3187fa9e4066Sahrens /* 3188b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3189b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3190b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3191b12a1c38Slling * group. 3192fa9e4066Sahrens */ 3193b12a1c38Slling typedef struct rollback_data { 3194b12a1c38Slling const char *cb_target; /* the snapshot */ 3195b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3196c391e322Sahrens boolean_t cb_error; 319799653d4eSeschrock boolean_t cb_dependent; 3198c391e322Sahrens boolean_t cb_force; 3199b12a1c38Slling } rollback_data_t; 3200b12a1c38Slling 3201b12a1c38Slling static int 3202b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3203b12a1c38Slling { 3204b12a1c38Slling rollback_data_t *cbp = data; 3205b12a1c38Slling 3206b12a1c38Slling if (!cbp->cb_dependent) { 3207b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3208b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3209b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3210b12a1c38Slling cbp->cb_create) { 3211ecd6cf80Smarks char *logstr; 3212b12a1c38Slling 321399653d4eSeschrock cbp->cb_dependent = B_TRUE; 32144ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32154ccbb6e7Sahrens rollback_destroy, cbp); 321699653d4eSeschrock cbp->cb_dependent = B_FALSE; 3217b12a1c38Slling 3218ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3219ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 3220842727c2SChris Kirby cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3221ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3222b12a1c38Slling } 3223b12a1c38Slling } else { 3224c391e322Sahrens /* We must destroy this clone; first unmount it */ 3225c391e322Sahrens prop_changelist_t *clp; 3226c391e322Sahrens 32270069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3228c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3229c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3230c391e322Sahrens cbp->cb_error = B_TRUE; 3231c391e322Sahrens zfs_close(zhp); 3232c391e322Sahrens return (0); 3233c391e322Sahrens } 3234842727c2SChris Kirby if (zfs_destroy(zhp, B_FALSE) != 0) 3235c391e322Sahrens cbp->cb_error = B_TRUE; 3236c391e322Sahrens else 3237c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3238ba7b046eSahrens (void) changelist_postfix(clp); 3239c391e322Sahrens changelist_free(clp); 3240b12a1c38Slling } 3241b12a1c38Slling 3242b12a1c38Slling zfs_close(zhp); 3243b12a1c38Slling return (0); 3244b12a1c38Slling } 3245b12a1c38Slling 3246b12a1c38Slling /* 3247b12a1c38Slling * Given a dataset, rollback to a specific snapshot, discarding any 3248b12a1c38Slling * data changes since then and making it the active dataset. 3249b12a1c38Slling * 3250b12a1c38Slling * Any snapshots more recent than the target are destroyed, along with 3251b12a1c38Slling * their dependents. 3252b12a1c38Slling */ 3253b12a1c38Slling int 3254c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3255b12a1c38Slling { 3256b12a1c38Slling rollback_data_t cb = { 0 }; 32574ccbb6e7Sahrens int err; 32584ccbb6e7Sahrens zfs_cmd_t zc = { 0 }; 32597b97dc1aSrm boolean_t restore_resv = 0; 32607b97dc1aSrm uint64_t old_volsize, new_volsize; 32617b97dc1aSrm zfs_prop_t resv_prop; 3262b12a1c38Slling 32634ccbb6e7Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 32644ccbb6e7Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3265b12a1c38Slling 3266b12a1c38Slling /* 3267b12a1c38Slling * Destroy all recent snapshots and its dependends. 3268b12a1c38Slling */ 3269c391e322Sahrens cb.cb_force = force; 3270b12a1c38Slling cb.cb_target = snap->zfs_name; 3271b12a1c38Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3272b12a1c38Slling (void) zfs_iter_children(zhp, rollback_destroy, &cb); 3273b12a1c38Slling 3274c391e322Sahrens if (cb.cb_error) 3275c391e322Sahrens return (-1); 3276b12a1c38Slling 3277b12a1c38Slling /* 3278b12a1c38Slling * Now that we have verified that the snapshot is the latest, 3279b12a1c38Slling * rollback to the given snapshot. 3280b12a1c38Slling */ 3281b12a1c38Slling 32827b97dc1aSrm if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 32837b97dc1aSrm if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 32847b97dc1aSrm return (-1); 32857b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 32867b97dc1aSrm return (-1); 32877b97dc1aSrm old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 32887b97dc1aSrm restore_resv = 32897b97dc1aSrm (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 32907b97dc1aSrm } 32914ccbb6e7Sahrens 32924ccbb6e7Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 32934ccbb6e7Sahrens 32944ccbb6e7Sahrens if (ZFS_IS_VOLUME(zhp)) 32954ccbb6e7Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 32964ccbb6e7Sahrens else 32974ccbb6e7Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3298b12a1c38Slling 3299b12a1c38Slling /* 33004ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 33014ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 33024ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 33034ccbb6e7Sahrens * an unlikely race condition where the user has taken a 33044ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 33057b97dc1aSrm * 3306b12a1c38Slling */ 33074ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33084ccbb6e7Sahrens (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33094ccbb6e7Sahrens dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33104ccbb6e7Sahrens zhp->zfs_name); 3311b9415e83Srm return (err); 3312b9415e83Srm } 33137b97dc1aSrm 33147b97dc1aSrm /* 33157b97dc1aSrm * For volumes, if the pre-rollback volsize matched the pre- 33167b97dc1aSrm * rollback reservation and the volsize has changed then set 33177b97dc1aSrm * the reservation property to the post-rollback volsize. 33187b97dc1aSrm * Make a new handle since the rollback closed the dataset. 33197b97dc1aSrm */ 3320b9415e83Srm if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3321b9415e83Srm (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3322b9415e83Srm if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) { 3323b9415e83Srm zfs_close(zhp); 33247b97dc1aSrm return (err); 3325b9415e83Srm } 33267b97dc1aSrm if (restore_resv) { 33277b97dc1aSrm new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33287b97dc1aSrm if (old_volsize != new_volsize) 3329b9415e83Srm err = zfs_prop_set_int(zhp, resv_prop, 3330b9415e83Srm new_volsize); 33317b97dc1aSrm } 33327b97dc1aSrm zfs_close(zhp); 33334ccbb6e7Sahrens } 33344ccbb6e7Sahrens return (err); 3335b12a1c38Slling } 3336b12a1c38Slling 3337fa9e4066Sahrens /* 3338fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3339fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3340fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3341fa9e4066Sahrens * libzfs_graph.c. 3342fa9e4066Sahrens */ 3343fa9e4066Sahrens int 33443bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33453bb79becSeschrock zfs_iter_f func, void *data) 3346fa9e4066Sahrens { 3347fa9e4066Sahrens char **dependents; 3348fa9e4066Sahrens size_t count; 3349fa9e4066Sahrens int i; 3350fa9e4066Sahrens zfs_handle_t *child; 3351fa9e4066Sahrens int ret = 0; 3352fa9e4066Sahrens 33533bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 33543bb79becSeschrock &dependents, &count) != 0) 33553bb79becSeschrock return (-1); 33563bb79becSeschrock 3357fa9e4066Sahrens for (i = 0; i < count; i++) { 335899653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 335999653d4eSeschrock dependents[i])) == NULL) 3360fa9e4066Sahrens continue; 3361fa9e4066Sahrens 3362fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3363fa9e4066Sahrens break; 3364fa9e4066Sahrens } 3365fa9e4066Sahrens 3366fa9e4066Sahrens for (i = 0; i < count; i++) 3367fa9e4066Sahrens free(dependents[i]); 3368fa9e4066Sahrens free(dependents); 3369fa9e4066Sahrens 3370fa9e4066Sahrens return (ret); 3371fa9e4066Sahrens } 3372fa9e4066Sahrens 3373fa9e4066Sahrens /* 3374fa9e4066Sahrens * Renames the given dataset. 3375fa9e4066Sahrens */ 3376fa9e4066Sahrens int 33777f1f55eaSvb zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3378fa9e4066Sahrens { 3379fa9e4066Sahrens int ret; 3380fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3381fa9e4066Sahrens char *delim; 3382cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3383cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3384cdf5b4caSmmusante char *parentname = NULL; 3385fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 338699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 338799653d4eSeschrock char errbuf[1024]; 3388fa9e4066Sahrens 3389fa9e4066Sahrens /* if we have the same exact name, just return success */ 3390fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3391fa9e4066Sahrens return (0); 3392fa9e4066Sahrens 339399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 339499653d4eSeschrock "cannot rename to '%s'"), target); 339599653d4eSeschrock 3396fa9e4066Sahrens /* 3397fa9e4066Sahrens * Make sure the target name is valid 3398fa9e4066Sahrens */ 3399fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 340098579b20Snd if ((strchr(target, '@') == NULL) || 340198579b20Snd *target == '@') { 340298579b20Snd /* 340398579b20Snd * Snapshot target name is abbreviated, 340498579b20Snd * reconstruct full dataset name 340598579b20Snd */ 340698579b20Snd (void) strlcpy(parent, zhp->zfs_name, 340798579b20Snd sizeof (parent)); 340898579b20Snd delim = strchr(parent, '@'); 340998579b20Snd if (strchr(target, '@') == NULL) 341098579b20Snd *(++delim) = '\0'; 341198579b20Snd else 341298579b20Snd *delim = '\0'; 341398579b20Snd (void) strlcat(parent, target, sizeof (parent)); 341498579b20Snd target = parent; 341598579b20Snd } else { 341698579b20Snd /* 341798579b20Snd * Make sure we're renaming within the same dataset. 341898579b20Snd */ 341998579b20Snd delim = strchr(target, '@'); 342098579b20Snd if (strncmp(zhp->zfs_name, target, delim - target) 342198579b20Snd != 0 || zhp->zfs_name[delim - target] != '@') { 342298579b20Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 342398579b20Snd "snapshots must be part of same " 342498579b20Snd "dataset")); 342598579b20Snd return (zfs_error(hdl, EZFS_CROSSTARGET, 3426b1b8ab34Slling errbuf)); 342798579b20Snd } 3428fa9e4066Sahrens } 3429f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 343098579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3431fa9e4066Sahrens } else { 3432cdf5b4caSmmusante if (recursive) { 3433cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3434cdf5b4caSmmusante "recursive rename must be a snapshot")); 3435cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3436cdf5b4caSmmusante } 3437cdf5b4caSmmusante 3438f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 343998579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3440e9dbad6fSeschrock uint64_t unused; 3441e9dbad6fSeschrock 3442fa9e4066Sahrens /* validate parents */ 34437f1f55eaSvb if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3444fa9e4066Sahrens return (-1); 3445fa9e4066Sahrens 3446fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3447fa9e4066Sahrens 3448fa9e4066Sahrens /* make sure we're in the same pool */ 3449fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3450fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3451fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 345299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 345399653d4eSeschrock "datasets must be within same pool")); 345499653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3455fa9e4066Sahrens } 3456f2fdf992Snd 3457f2fdf992Snd /* new name cannot be a child of the current dataset name */ 3458f2fdf992Snd if (strncmp(parent, zhp->zfs_name, 3459b1b8ab34Slling strlen(zhp->zfs_name)) == 0) { 3460f2fdf992Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3461f2fdf992Snd "New dataset name cannot be a descendent of " 3462f2fdf992Snd "current dataset name")); 3463f2fdf992Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3464f2fdf992Snd } 3465fa9e4066Sahrens } 3466fa9e4066Sahrens 346799653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 346899653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 346999653d4eSeschrock 3470fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3471fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 347299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 347399653d4eSeschrock "dataset is used in a non-global zone")); 347499653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3475fa9e4066Sahrens } 3476fa9e4066Sahrens 3477cdf5b4caSmmusante if (recursive) { 3478cdf5b4caSmmusante struct destroydata dd; 3479fa9e4066Sahrens 3480f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3481f0c5ee21Smmusante if (parentname == NULL) { 3482f0c5ee21Smmusante ret = -1; 3483f0c5ee21Smmusante goto error; 3484f0c5ee21Smmusante } 3485cdf5b4caSmmusante delim = strchr(parentname, '@'); 3486cdf5b4caSmmusante *delim = '\0'; 3487990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3488cdf5b4caSmmusante if (zhrp == NULL) { 3489f0c5ee21Smmusante ret = -1; 3490f0c5ee21Smmusante goto error; 3491cdf5b4caSmmusante } 3492fa9e4066Sahrens 3493cdf5b4caSmmusante dd.snapname = delim + 1; 3494cdf5b4caSmmusante dd.gotone = B_FALSE; 3495f0c5ee21Smmusante dd.closezhp = B_TRUE; 3496cdf5b4caSmmusante 3497cdf5b4caSmmusante /* We remove any zvol links prior to renaming them */ 3498cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd); 3499cdf5b4caSmmusante if (ret) { 3500cdf5b4caSmmusante goto error; 3501cdf5b4caSmmusante } 3502cdf5b4caSmmusante } else { 35030069fd67STim Haley if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 3504cdf5b4caSmmusante return (-1); 3505cdf5b4caSmmusante 3506cdf5b4caSmmusante if (changelist_haszonedchild(cl)) { 3507cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3508cdf5b4caSmmusante "child dataset with inherited mountpoint is used " 3509cdf5b4caSmmusante "in a non-global zone")); 3510cdf5b4caSmmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3511cdf5b4caSmmusante goto error; 3512cdf5b4caSmmusante } 3513cdf5b4caSmmusante 3514cdf5b4caSmmusante if ((ret = changelist_prefix(cl)) != 0) 3515cdf5b4caSmmusante goto error; 3516cdf5b4caSmmusante } 3517fa9e4066Sahrens 3518e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3519fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3520fa9e4066Sahrens else 3521fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3522fa9e4066Sahrens 352398579b20Snd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3524e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 352598579b20Snd 3526cdf5b4caSmmusante zc.zc_cookie = recursive; 3527cdf5b4caSmmusante 3528ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3529cdf5b4caSmmusante /* 3530cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3531cdf5b4caSmmusante * be in zc.zc_name 3532cdf5b4caSmmusante */ 3533cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35343cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3535cdf5b4caSmmusante 3536cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3537cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3538cdf5b4caSmmusante "a child dataset already has a snapshot " 3539cdf5b4caSmmusante "with the new name")); 3540a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3541cdf5b4caSmmusante } else { 3542cdf5b4caSmmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3543cdf5b4caSmmusante } 3544fa9e4066Sahrens 3545fa9e4066Sahrens /* 3546fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3547fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3548fa9e4066Sahrens */ 3549cdf5b4caSmmusante if (recursive) { 3550cdf5b4caSmmusante struct createdata cd; 3551cdf5b4caSmmusante 3552cdf5b4caSmmusante /* only create links for datasets that had existed */ 3553cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3554cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3555cdf5b4caSmmusante (void) zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3556cdf5b4caSmmusante &cd); 3557cdf5b4caSmmusante } else { 3558cdf5b4caSmmusante (void) changelist_postfix(cl); 3559cdf5b4caSmmusante } 3560fa9e4066Sahrens } else { 3561cdf5b4caSmmusante if (recursive) { 3562cdf5b4caSmmusante struct createdata cd; 3563cdf5b4caSmmusante 3564cdf5b4caSmmusante /* only create links for datasets that had existed */ 3565cdf5b4caSmmusante cd.cd_snapname = strchr(target, '@') + 1; 3566cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3567cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3568cdf5b4caSmmusante &cd); 3569cdf5b4caSmmusante } else { 3570cdf5b4caSmmusante changelist_rename(cl, zfs_get_name(zhp), target); 3571cdf5b4caSmmusante ret = changelist_postfix(cl); 3572cdf5b4caSmmusante } 3573fa9e4066Sahrens } 3574fa9e4066Sahrens 3575fa9e4066Sahrens error: 3576cdf5b4caSmmusante if (parentname) { 3577cdf5b4caSmmusante free(parentname); 3578cdf5b4caSmmusante } 3579cdf5b4caSmmusante if (zhrp) { 3580cdf5b4caSmmusante zfs_close(zhrp); 3581cdf5b4caSmmusante } 3582cdf5b4caSmmusante if (cl) { 3583cdf5b4caSmmusante changelist_free(cl); 3584cdf5b4caSmmusante } 3585fa9e4066Sahrens return (ret); 3586fa9e4066Sahrens } 3587fa9e4066Sahrens 3588fa9e4066Sahrens /* 3589fa9e4066Sahrens * Given a zvol dataset, issue the ioctl to create the appropriate minor node, 3590fa9e4066Sahrens * poke devfsadm to create the /dev link, and then wait for the link to appear. 3591fa9e4066Sahrens */ 3592fa9e4066Sahrens int 359399653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset) 3594cdf5b4caSmmusante { 3595cdf5b4caSmmusante return (zvol_create_link_common(hdl, dataset, B_FALSE)); 3596cdf5b4caSmmusante } 3597cdf5b4caSmmusante 3598cdf5b4caSmmusante static int 3599cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists) 3600fa9e4066Sahrens { 3601fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 360299653d4eSeschrock di_devlink_handle_t dhdl; 3603ecd6cf80Smarks priv_set_t *priv_effective; 3604ecd6cf80Smarks int privileged; 3605fa9e4066Sahrens 3606fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3607fa9e4066Sahrens 3608fa9e4066Sahrens /* 3609fa9e4066Sahrens * Issue the appropriate ioctl. 3610fa9e4066Sahrens */ 361199653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { 3612fa9e4066Sahrens switch (errno) { 3613fa9e4066Sahrens case EEXIST: 3614fa9e4066Sahrens /* 3615fa9e4066Sahrens * Silently ignore the case where the link already 3616fa9e4066Sahrens * exists. This allows 'zfs volinit' to be run multiple 3617fa9e4066Sahrens * times without errors. 3618fa9e4066Sahrens */ 3619fa9e4066Sahrens return (0); 3620fa9e4066Sahrens 3621cdf5b4caSmmusante case ENOENT: 3622cdf5b4caSmmusante /* 3623cdf5b4caSmmusante * Dataset does not exist in the kernel. If we 3624cdf5b4caSmmusante * don't care (see zfs_rename), then ignore the 3625cdf5b4caSmmusante * error quietly. 3626cdf5b4caSmmusante */ 3627cdf5b4caSmmusante if (ifexists) { 3628cdf5b4caSmmusante return (0); 3629cdf5b4caSmmusante } 3630cdf5b4caSmmusante 3631cdf5b4caSmmusante /* FALLTHROUGH */ 3632cdf5b4caSmmusante 3633fa9e4066Sahrens default: 3634ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 363599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 363699653d4eSeschrock "for '%s'"), dataset)); 3637fa9e4066Sahrens } 3638fa9e4066Sahrens } 3639fa9e4066Sahrens 3640fa9e4066Sahrens /* 3641ecd6cf80Smarks * If privileged call devfsadm and wait for the links to 3642ecd6cf80Smarks * magically appear. 3643ecd6cf80Smarks * Otherwise, print out an informational message. 3644fa9e4066Sahrens */ 3645ecd6cf80Smarks 3646ecd6cf80Smarks priv_effective = priv_allocset(); 3647ecd6cf80Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 3648ecd6cf80Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 3649ecd6cf80Smarks priv_freeset(priv_effective); 3650ecd6cf80Smarks 3651ecd6cf80Smarks if (privileged) { 3652ecd6cf80Smarks if ((dhdl = di_devlink_init(ZFS_DRIVER, 3653ecd6cf80Smarks DI_MAKE_LINK)) == NULL) { 3654ecd6cf80Smarks zfs_error_aux(hdl, strerror(errno)); 3655d1e7ea07SEric Taylor (void) zfs_error_fmt(hdl, errno, 3656ecd6cf80Smarks dgettext(TEXT_DOMAIN, "cannot create device links " 3657ecd6cf80Smarks "for '%s'"), dataset); 3658ecd6cf80Smarks (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); 3659ecd6cf80Smarks return (-1); 3660ecd6cf80Smarks } else { 3661ecd6cf80Smarks (void) di_devlink_fini(&dhdl); 3662ecd6cf80Smarks } 3663fa9e4066Sahrens } else { 3664ecd6cf80Smarks char pathname[MAXPATHLEN]; 3665ecd6cf80Smarks struct stat64 statbuf; 3666ecd6cf80Smarks int i; 3667ecd6cf80Smarks 3668ecd6cf80Smarks #define MAX_WAIT 10 3669ecd6cf80Smarks 3670ecd6cf80Smarks /* 3671ecd6cf80Smarks * This is the poor mans way of waiting for the link 3672ecd6cf80Smarks * to show up. If after 10 seconds we still don't 3673ecd6cf80Smarks * have it, then print out a message. 3674ecd6cf80Smarks */ 3675ecd6cf80Smarks (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s", 3676ecd6cf80Smarks dataset); 3677ecd6cf80Smarks 3678ecd6cf80Smarks for (i = 0; i != MAX_WAIT; i++) { 3679ecd6cf80Smarks if (stat64(pathname, &statbuf) == 0) 3680ecd6cf80Smarks break; 3681ecd6cf80Smarks (void) sleep(1); 3682ecd6cf80Smarks } 3683ecd6cf80Smarks if (i == MAX_WAIT) 3684ecd6cf80Smarks (void) printf(gettext("%s may not be immediately " 3685ecd6cf80Smarks "available\n"), pathname); 3686fa9e4066Sahrens } 3687fa9e4066Sahrens 3688fa9e4066Sahrens return (0); 3689fa9e4066Sahrens } 3690fa9e4066Sahrens 3691fa9e4066Sahrens /* 3692fa9e4066Sahrens * Remove a minor node for the given zvol and the associated /dev links. 3693fa9e4066Sahrens */ 3694fa9e4066Sahrens int 369599653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) 3696fa9e4066Sahrens { 3697fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3698fa9e4066Sahrens 3699fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3700fa9e4066Sahrens 370199653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { 3702fa9e4066Sahrens switch (errno) { 3703fa9e4066Sahrens case ENXIO: 3704fa9e4066Sahrens /* 3705fa9e4066Sahrens * Silently ignore the case where the link no longer 3706fa9e4066Sahrens * exists, so that 'zfs volfini' can be run multiple 3707fa9e4066Sahrens * times without errors. 3708fa9e4066Sahrens */ 3709fa9e4066Sahrens return (0); 3710fa9e4066Sahrens 3711fa9e4066Sahrens default: 3712ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 371399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot remove device " 371499653d4eSeschrock "links for '%s'"), dataset)); 3715fa9e4066Sahrens } 3716fa9e4066Sahrens } 3717fa9e4066Sahrens 3718fa9e4066Sahrens return (0); 3719fa9e4066Sahrens } 3720e9dbad6fSeschrock 3721e9dbad6fSeschrock nvlist_t * 3722e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3723e9dbad6fSeschrock { 3724e9dbad6fSeschrock return (zhp->zfs_user_props); 3725e9dbad6fSeschrock } 3726e9dbad6fSeschrock 3727b1b8ab34Slling /* 3728b1b8ab34Slling * This function is used by 'zfs list' to determine the exact set of columns to 3729b1b8ab34Slling * display, and their maximum widths. This does two main things: 3730b1b8ab34Slling * 3731b1b8ab34Slling * - If this is a list of all properties, then expand the list to include 3732b1b8ab34Slling * all native properties, and set a flag so that for each dataset we look 3733b1b8ab34Slling * for new unique user properties and add them to the list. 3734b1b8ab34Slling * 3735b1b8ab34Slling * - For non fixed-width properties, keep track of the maximum width seen 3736b1b8ab34Slling * so that we can size the column appropriately. 3737b1b8ab34Slling */ 3738b1b8ab34Slling int 3739990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) 3740b1b8ab34Slling { 3741b1b8ab34Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 3742990b4856Slling zprop_list_t *entry; 3743990b4856Slling zprop_list_t **last, **start; 3744b1b8ab34Slling nvlist_t *userprops, *propval; 3745b1b8ab34Slling nvpair_t *elem; 3746b1b8ab34Slling char *strval; 3747b1b8ab34Slling char buf[ZFS_MAXPROPLEN]; 3748b1b8ab34Slling 3749990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3750b1b8ab34Slling return (-1); 3751e9dbad6fSeschrock 3752e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3753e9dbad6fSeschrock 3754e9dbad6fSeschrock entry = *plp; 3755e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3756e9dbad6fSeschrock /* 3757e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3758e9dbad6fSeschrock * start by incrementing our list pointer to the first 3759e9dbad6fSeschrock * non-native property. 3760e9dbad6fSeschrock */ 3761e9dbad6fSeschrock start = plp; 3762e9dbad6fSeschrock while (*start != NULL) { 3763990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3764e9dbad6fSeschrock break; 3765e9dbad6fSeschrock start = &(*start)->pl_next; 3766e9dbad6fSeschrock } 3767e9dbad6fSeschrock 3768e9dbad6fSeschrock elem = NULL; 3769e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3770e9dbad6fSeschrock /* 3771e9dbad6fSeschrock * See if we've already found this property in our list. 3772e9dbad6fSeschrock */ 3773e9dbad6fSeschrock for (last = start; *last != NULL; 3774e9dbad6fSeschrock last = &(*last)->pl_next) { 3775e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3776e9dbad6fSeschrock nvpair_name(elem)) == 0) 3777e9dbad6fSeschrock break; 3778e9dbad6fSeschrock } 3779e9dbad6fSeschrock 3780e9dbad6fSeschrock if (*last == NULL) { 3781e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3782990b4856Slling sizeof (zprop_list_t))) == NULL || 3783e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3784e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3785e9dbad6fSeschrock free(entry); 3786e9dbad6fSeschrock return (-1); 3787e9dbad6fSeschrock } 3788e9dbad6fSeschrock 3789990b4856Slling entry->pl_prop = ZPROP_INVAL; 3790e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3791e9dbad6fSeschrock entry->pl_all = B_TRUE; 3792e9dbad6fSeschrock *last = entry; 3793e9dbad6fSeschrock } 3794e9dbad6fSeschrock } 3795e9dbad6fSeschrock } 3796e9dbad6fSeschrock 3797e9dbad6fSeschrock /* 3798e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3799e9dbad6fSeschrock */ 3800e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3801e9dbad6fSeschrock if (entry->pl_fixed) 3802e9dbad6fSeschrock continue; 3803e9dbad6fSeschrock 3804990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3805e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3806e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3807e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3808e9dbad6fSeschrock entry->pl_width = strlen(buf); 3809e9dbad6fSeschrock } 3810e9dbad6fSeschrock } else if (nvlist_lookup_nvlist(userprops, 3811e9dbad6fSeschrock entry->pl_user_prop, &propval) == 0) { 3812e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3813990b4856Slling ZPROP_VALUE, &strval) == 0); 3814e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3815e9dbad6fSeschrock entry->pl_width = strlen(strval); 3816e9dbad6fSeschrock } 3817e9dbad6fSeschrock } 3818e9dbad6fSeschrock 3819e9dbad6fSeschrock return (0); 3820e9dbad6fSeschrock } 3821ecd6cf80Smarks 3822ecd6cf80Smarks int 3823ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 3824ecd6cf80Smarks { 3825ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3826ecd6cf80Smarks nvlist_t *nvp; 3827ecd6cf80Smarks gid_t gid; 3828ecd6cf80Smarks uid_t uid; 3829ecd6cf80Smarks const gid_t *groups; 3830ecd6cf80Smarks int group_cnt; 3831ecd6cf80Smarks int error; 3832ecd6cf80Smarks 3833ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 3834ecd6cf80Smarks return (no_memory(hdl)); 3835ecd6cf80Smarks 3836ecd6cf80Smarks uid = ucred_geteuid(cred); 3837ecd6cf80Smarks gid = ucred_getegid(cred); 3838ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 3839ecd6cf80Smarks 3840ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 3841ecd6cf80Smarks return (1); 3842ecd6cf80Smarks 3843ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 3844ecd6cf80Smarks nvlist_free(nvp); 3845ecd6cf80Smarks return (1); 3846ecd6cf80Smarks } 3847ecd6cf80Smarks 3848ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 3849ecd6cf80Smarks nvlist_free(nvp); 3850ecd6cf80Smarks return (1); 3851ecd6cf80Smarks } 3852ecd6cf80Smarks 3853ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 3854ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 3855ecd6cf80Smarks nvlist_free(nvp); 3856ecd6cf80Smarks return (1); 3857ecd6cf80Smarks } 3858ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3859ecd6cf80Smarks 3860990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 3861ecd6cf80Smarks return (-1); 3862ecd6cf80Smarks 3863ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 3864ecd6cf80Smarks nvlist_free(nvp); 3865ecd6cf80Smarks return (error); 3866ecd6cf80Smarks } 3867ecd6cf80Smarks 3868ecd6cf80Smarks int 3869ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3870743a77edSAlan Wright char *resource, void *export, void *sharetab, 3871743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 3872ecd6cf80Smarks { 3873ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3874ecd6cf80Smarks int error; 3875ecd6cf80Smarks 3876ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3877ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3878743a77edSAlan Wright if (resource) 3879743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3880ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3881ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3882da6c28aaSamw zc.zc_share.z_sharetype = operation; 3883ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 3884ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3885ecd6cf80Smarks return (error); 3886ecd6cf80Smarks } 38872e5e9e19SSanjeev Bagewadi 38882e5e9e19SSanjeev Bagewadi void 38892e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 38902e5e9e19SSanjeev Bagewadi { 38912e5e9e19SSanjeev Bagewadi nvpair_t *curr; 38922e5e9e19SSanjeev Bagewadi 38932e5e9e19SSanjeev Bagewadi /* 38942e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 38952e5e9e19SSanjeev Bagewadi * properties. 38962e5e9e19SSanjeev Bagewadi */ 38972e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 38982e5e9e19SSanjeev Bagewadi 38992e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 39002e5e9e19SSanjeev Bagewadi 39012e5e9e19SSanjeev Bagewadi while (curr) { 39022e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 39032e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 39042e5e9e19SSanjeev Bagewadi 390514843421SMatthew Ahrens /* 390614843421SMatthew Ahrens * We leave user:props in the nvlist, so there will be 390714843421SMatthew Ahrens * some ZPROP_INVAL. To be extra safe, don't prune 390814843421SMatthew Ahrens * those. 390914843421SMatthew Ahrens */ 391014843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 39112e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 39122e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 39132e5e9e19SSanjeev Bagewadi curr = next; 39142e5e9e19SSanjeev Bagewadi } 39152e5e9e19SSanjeev Bagewadi } 3916743a77edSAlan Wright 3917743a77edSAlan Wright static int 3918743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3919743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3920743a77edSAlan Wright { 3921743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 3922743a77edSAlan Wright nvlist_t *nvlist = NULL; 3923743a77edSAlan Wright int error; 3924743a77edSAlan Wright 3925743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3926743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3927743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 3928743a77edSAlan Wright 3929743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 3930743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3931743a77edSAlan Wright (void) no_memory(hdl); 3932743a77edSAlan Wright return (NULL); 3933743a77edSAlan Wright } 3934743a77edSAlan Wright } 3935743a77edSAlan Wright 3936743a77edSAlan Wright switch (cmd) { 3937743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3938743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3939743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3940743a77edSAlan Wright break; 3941743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3942743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3943743a77edSAlan Wright resource1) != 0) { 3944743a77edSAlan Wright (void) no_memory(hdl); 3945743a77edSAlan Wright return (-1); 3946743a77edSAlan Wright } 3947743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 3948743a77edSAlan Wright resource2) != 0) { 3949743a77edSAlan Wright (void) no_memory(hdl); 3950743a77edSAlan Wright return (-1); 3951743a77edSAlan Wright } 3952743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 3953743a77edSAlan Wright nvlist_free(nvlist); 3954743a77edSAlan Wright return (-1); 3955743a77edSAlan Wright } 3956743a77edSAlan Wright break; 3957743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3958743a77edSAlan Wright break; 3959743a77edSAlan Wright default: 3960743a77edSAlan Wright return (-1); 3961743a77edSAlan Wright } 3962743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 3963743a77edSAlan Wright if (nvlist) 3964743a77edSAlan Wright nvlist_free(nvlist); 3965743a77edSAlan Wright return (error); 3966743a77edSAlan Wright } 3967743a77edSAlan Wright 3968743a77edSAlan Wright int 3969743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 3970743a77edSAlan Wright char *path, char *resource) 3971743a77edSAlan Wright { 3972743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 3973743a77edSAlan Wright resource, NULL)); 3974743a77edSAlan Wright } 3975743a77edSAlan Wright 3976743a77edSAlan Wright int 3977743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 3978743a77edSAlan Wright char *path, char *resource) 3979743a77edSAlan Wright { 3980743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 3981743a77edSAlan Wright resource, NULL)); 3982743a77edSAlan Wright } 3983743a77edSAlan Wright 3984743a77edSAlan Wright int 3985743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 3986743a77edSAlan Wright { 3987743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 3988743a77edSAlan Wright NULL, NULL)); 3989743a77edSAlan Wright } 3990743a77edSAlan Wright 3991743a77edSAlan Wright int 3992743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 3993743a77edSAlan Wright char *oldname, char *newname) 3994743a77edSAlan Wright { 3995743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 3996743a77edSAlan Wright oldname, newname)); 3997743a77edSAlan Wright } 399814843421SMatthew Ahrens 399914843421SMatthew Ahrens int 400014843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 400114843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 400214843421SMatthew Ahrens { 400314843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 400414843421SMatthew Ahrens int error; 400514843421SMatthew Ahrens zfs_useracct_t buf[100]; 400614843421SMatthew Ahrens 400714843421SMatthew Ahrens (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 400814843421SMatthew Ahrens 400914843421SMatthew Ahrens zc.zc_objset_type = type; 401014843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 401114843421SMatthew Ahrens 401214843421SMatthew Ahrens /* CONSTCOND */ 401314843421SMatthew Ahrens while (1) { 401414843421SMatthew Ahrens zfs_useracct_t *zua = buf; 401514843421SMatthew Ahrens 401614843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 401714843421SMatthew Ahrens error = ioctl(zhp->zfs_hdl->libzfs_fd, 401814843421SMatthew Ahrens ZFS_IOC_USERSPACE_MANY, &zc); 401914843421SMatthew Ahrens if (error || zc.zc_nvlist_dst_size == 0) 402014843421SMatthew Ahrens break; 402114843421SMatthew Ahrens 402214843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 40230aea4b19SMatthew Ahrens error = func(arg, zua->zu_domain, zua->zu_rid, 40240aea4b19SMatthew Ahrens zua->zu_space); 40250aea4b19SMatthew Ahrens if (error != 0) 40260aea4b19SMatthew Ahrens return (error); 402714843421SMatthew Ahrens zua++; 402814843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 402914843421SMatthew Ahrens } 403014843421SMatthew Ahrens } 403114843421SMatthew Ahrens 403214843421SMatthew Ahrens return (error); 403314843421SMatthew Ahrens } 4034842727c2SChris Kirby 4035842727c2SChris Kirby int 4036842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 4037842727c2SChris Kirby boolean_t recursive) 4038842727c2SChris Kirby { 4039842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 4040842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 4041842727c2SChris Kirby 4042842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4043842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4044842727c2SChris Kirby (void) strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)); 4045842727c2SChris Kirby zc.zc_cookie = recursive; 4046842727c2SChris Kirby 4047842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 4048842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 4049842727c2SChris Kirby 4050842727c2SChris Kirby /* 4051842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 4052842727c2SChris Kirby * zc.zc_name. 4053842727c2SChris Kirby */ 4054842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4055842727c2SChris Kirby "cannot hold '%s@%s'"), zc.zc_name, snapname); 4056842727c2SChris Kirby switch (errno) { 4057842727c2SChris Kirby case ENOTSUP: 4058842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4059842727c2SChris Kirby "pool must be upgraded")); 4060842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4061842727c2SChris Kirby case EINVAL: 4062842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4063842727c2SChris Kirby case EEXIST: 4064842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 4065842727c2SChris Kirby default: 4066842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4067842727c2SChris Kirby } 4068842727c2SChris Kirby } 4069842727c2SChris Kirby 4070842727c2SChris Kirby return (0); 4071842727c2SChris Kirby } 4072842727c2SChris Kirby 4073842727c2SChris Kirby int 4074842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4075842727c2SChris Kirby boolean_t recursive) 4076842727c2SChris Kirby { 4077842727c2SChris Kirby zfs_cmd_t zc = { 0 }; 4078842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 4079842727c2SChris Kirby 4080842727c2SChris Kirby (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4081842727c2SChris Kirby (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4082842727c2SChris Kirby (void) strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)); 4083842727c2SChris Kirby zc.zc_cookie = recursive; 4084842727c2SChris Kirby 4085842727c2SChris Kirby if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 4086842727c2SChris Kirby char errbuf[ZFS_MAXNAMELEN+32]; 4087842727c2SChris Kirby 4088842727c2SChris Kirby /* 4089842727c2SChris Kirby * if it was recursive, the one that actually failed will be in 4090842727c2SChris Kirby * zc.zc_name. 4091842727c2SChris Kirby */ 4092842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4093842727c2SChris Kirby "cannot release '%s@%s'"), zc.zc_name, snapname); 4094842727c2SChris Kirby switch (errno) { 4095842727c2SChris Kirby case ESRCH: 4096842727c2SChris Kirby return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 4097842727c2SChris Kirby case ENOTSUP: 4098842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4099842727c2SChris Kirby "pool must be upgraded")); 4100842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4101842727c2SChris Kirby case EINVAL: 4102842727c2SChris Kirby return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4103842727c2SChris Kirby default: 4104842727c2SChris Kirby return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4105842727c2SChris Kirby } 4106842727c2SChris Kirby } 4107842727c2SChris Kirby 4108842727c2SChris Kirby return (0); 4109842727c2SChris Kirby } 4110