1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 213126Sahl 22789Sahrens /* 2311417SChris.Kirby@sun.com * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #include <ctype.h> 28789Sahrens #include <errno.h> 29789Sahrens #include <libintl.h> 30789Sahrens #include <math.h> 31789Sahrens #include <stdio.h> 32789Sahrens #include <stdlib.h> 33789Sahrens #include <strings.h> 34789Sahrens #include <unistd.h> 355367Sahrens #include <stddef.h> 36789Sahrens #include <zone.h> 372082Seschrock #include <fcntl.h> 38789Sahrens #include <sys/mntent.h> 391294Slling #include <sys/mount.h> 404543Smarks #include <priv.h> 414543Smarks #include <pwd.h> 424543Smarks #include <grp.h> 434543Smarks #include <stddef.h> 444543Smarks #include <ucred.h> 459396SMatthew.Ahrens@Sun.COM #include <idmap.h> 469396SMatthew.Ahrens@Sun.COM #include <aclutils.h> 4710160SMatthew.Ahrens@Sun.COM #include <directory.h> 48789Sahrens 49*11449SEric.Taylor@Sun.COM #include <sys/dnode.h> 50789Sahrens #include <sys/spa.h> 512676Seschrock #include <sys/zap.h> 52789Sahrens #include <libzfs.h> 53789Sahrens 54789Sahrens #include "zfs_namecheck.h" 55789Sahrens #include "zfs_prop.h" 56789Sahrens #include "libzfs_impl.h" 574543Smarks #include "zfs_deleg.h" 58789Sahrens 599396SMatthew.Ahrens@Sun.COM static int userquota_propname_decode(const char *propname, boolean_t zoned, 609396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 614007Smmusante 62789Sahrens /* 63789Sahrens * Given a single type (not a mask of types), return the type in a human 64789Sahrens * readable form. 65789Sahrens */ 66789Sahrens const char * 67789Sahrens zfs_type_to_name(zfs_type_t type) 68789Sahrens { 69789Sahrens switch (type) { 70789Sahrens case ZFS_TYPE_FILESYSTEM: 71789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 72789Sahrens case ZFS_TYPE_SNAPSHOT: 73789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 74789Sahrens case ZFS_TYPE_VOLUME: 75789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 76789Sahrens } 77789Sahrens 78789Sahrens return (NULL); 79789Sahrens } 80789Sahrens 81789Sahrens /* 82789Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 83789Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 84789Sahrens * We guess what the type would have been based on the path and the mask of 85789Sahrens * acceptable types. 86789Sahrens */ 87789Sahrens static const char * 88789Sahrens path_to_str(const char *path, int types) 89789Sahrens { 90789Sahrens /* 91789Sahrens * When given a single type, always report the exact type. 92789Sahrens */ 93789Sahrens if (types == ZFS_TYPE_SNAPSHOT) 94789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 95789Sahrens if (types == ZFS_TYPE_FILESYSTEM) 96789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 97789Sahrens if (types == ZFS_TYPE_VOLUME) 98789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 99789Sahrens 100789Sahrens /* 101789Sahrens * The user is requesting more than one type of dataset. If this is the 102789Sahrens * case, consult the path itself. If we're looking for a snapshot, and 103789Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 104789Sahrens * snapshot attribute and try again. 105789Sahrens */ 106789Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 107789Sahrens if (strchr(path, '@') != NULL) 108789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 109789Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 110789Sahrens } 111789Sahrens 112789Sahrens /* 113789Sahrens * The user has requested either filesystems or volumes. 114789Sahrens * We have no way of knowing a priori what type this would be, so always 115789Sahrens * report it as "filesystem" or "volume", our two primitive types. 116789Sahrens */ 117789Sahrens if (types & ZFS_TYPE_FILESYSTEM) 118789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 119789Sahrens 120789Sahrens assert(types & ZFS_TYPE_VOLUME); 121789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 122789Sahrens } 123789Sahrens 124789Sahrens /* 125789Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 1269396SMatthew.Ahrens@Sun.COM * provide a more meaningful error message. We call zfs_error_aux() to 1279396SMatthew.Ahrens@Sun.COM * explain exactly why the name was not valid. 128789Sahrens */ 129789Sahrens static int 1305326Sek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 1315326Sek110237 boolean_t modifying) 132789Sahrens { 133789Sahrens namecheck_err_t why; 134789Sahrens char what; 135789Sahrens 136789Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 1372082Seschrock if (hdl != NULL) { 138789Sahrens switch (why) { 1391003Slling case NAME_ERR_TOOLONG: 1402082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1412082Seschrock "name is too long")); 1421003Slling break; 1431003Slling 144789Sahrens case NAME_ERR_LEADING_SLASH: 1452082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1462082Seschrock "leading slash in name")); 147789Sahrens break; 148789Sahrens 149789Sahrens case NAME_ERR_EMPTY_COMPONENT: 1502082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1512082Seschrock "empty component in name")); 152789Sahrens break; 153789Sahrens 154789Sahrens case NAME_ERR_TRAILING_SLASH: 1552082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1562082Seschrock "trailing slash in name")); 157789Sahrens break; 158789Sahrens 159789Sahrens case NAME_ERR_INVALCHAR: 1602082Seschrock zfs_error_aux(hdl, 161789Sahrens dgettext(TEXT_DOMAIN, "invalid character " 1622082Seschrock "'%c' in name"), what); 163789Sahrens break; 164789Sahrens 165789Sahrens case NAME_ERR_MULTIPLE_AT: 1662082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1672082Seschrock "multiple '@' delimiters in name")); 168789Sahrens break; 1692856Snd150628 1702856Snd150628 case NAME_ERR_NOLETTER: 1712856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1722856Snd150628 "pool doesn't begin with a letter")); 1732856Snd150628 break; 1742856Snd150628 1752856Snd150628 case NAME_ERR_RESERVED: 1762856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1772856Snd150628 "name is reserved")); 1782856Snd150628 break; 1792856Snd150628 1802856Snd150628 case NAME_ERR_DISKLIKE: 1812856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1822856Snd150628 "reserved disk name")); 1832856Snd150628 break; 184789Sahrens } 185789Sahrens } 186789Sahrens 187789Sahrens return (0); 188789Sahrens } 189789Sahrens 190789Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 1912082Seschrock if (hdl != NULL) 1922082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1932082Seschrock "snapshot delimiter '@' in filesystem name")); 194789Sahrens return (0); 195789Sahrens } 196789Sahrens 1972199Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 1982199Sahrens if (hdl != NULL) 1992199Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2003413Smmusante "missing '@' delimiter in snapshot name")); 2012199Sahrens return (0); 2022199Sahrens } 2032199Sahrens 2045326Sek110237 if (modifying && strchr(path, '%') != NULL) { 2055326Sek110237 if (hdl != NULL) 2065326Sek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2075326Sek110237 "invalid character %c in name"), '%'); 2085326Sek110237 return (0); 2095326Sek110237 } 2105326Sek110237 2112082Seschrock return (-1); 212789Sahrens } 213789Sahrens 214789Sahrens int 215789Sahrens zfs_name_valid(const char *name, zfs_type_t type) 216789Sahrens { 2176423Sgw25295 if (type == ZFS_TYPE_POOL) 2186423Sgw25295 return (zpool_name_valid(NULL, B_FALSE, name)); 2195326Sek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 220789Sahrens } 221789Sahrens 222789Sahrens /* 2232676Seschrock * This function takes the raw DSL properties, and filters out the user-defined 2242676Seschrock * properties into a separate nvlist. 2252676Seschrock */ 2264217Seschrock static nvlist_t * 2274217Seschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 2282676Seschrock { 2292676Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2302676Seschrock nvpair_t *elem; 2312676Seschrock nvlist_t *propval; 2324217Seschrock nvlist_t *nvl; 2334217Seschrock 2344217Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 2354217Seschrock (void) no_memory(hdl); 2364217Seschrock return (NULL); 2374217Seschrock } 2382676Seschrock 2392676Seschrock elem = NULL; 2404217Seschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 2412676Seschrock if (!zfs_prop_user(nvpair_name(elem))) 2422676Seschrock continue; 2432676Seschrock 2442676Seschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 2454217Seschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 2464217Seschrock nvlist_free(nvl); 2474217Seschrock (void) no_memory(hdl); 2484217Seschrock return (NULL); 2494217Seschrock } 2502676Seschrock } 2512676Seschrock 2524217Seschrock return (nvl); 2532676Seschrock } 2542676Seschrock 2556865Srm160521 static zpool_handle_t * 2566865Srm160521 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 2576865Srm160521 { 2586865Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 2596865Srm160521 zpool_handle_t *zph; 2606865Srm160521 2616865Srm160521 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 2626865Srm160521 if (hdl->libzfs_pool_handles != NULL) 2636865Srm160521 zph->zpool_next = hdl->libzfs_pool_handles; 2646865Srm160521 hdl->libzfs_pool_handles = zph; 2656865Srm160521 } 2666865Srm160521 return (zph); 2676865Srm160521 } 2686865Srm160521 2696865Srm160521 static zpool_handle_t * 2706865Srm160521 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 2716865Srm160521 { 2726865Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 2736865Srm160521 zpool_handle_t *zph = hdl->libzfs_pool_handles; 2746865Srm160521 2756865Srm160521 while ((zph != NULL) && 2766865Srm160521 (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 2776865Srm160521 zph = zph->zpool_next; 2786865Srm160521 return (zph); 2796865Srm160521 } 2806865Srm160521 2816865Srm160521 /* 2826865Srm160521 * Returns a handle to the pool that contains the provided dataset. 2836865Srm160521 * If a handle to that pool already exists then that handle is returned. 2846865Srm160521 * Otherwise, a new handle is created and added to the list of handles. 2856865Srm160521 */ 2866865Srm160521 static zpool_handle_t * 2876865Srm160521 zpool_handle(zfs_handle_t *zhp) 2886865Srm160521 { 2896865Srm160521 char *pool_name; 2906865Srm160521 int len; 2916865Srm160521 zpool_handle_t *zph; 2926865Srm160521 2936865Srm160521 len = strcspn(zhp->zfs_name, "/@") + 1; 2946865Srm160521 pool_name = zfs_alloc(zhp->zfs_hdl, len); 2956865Srm160521 (void) strlcpy(pool_name, zhp->zfs_name, len); 2966865Srm160521 2976865Srm160521 zph = zpool_find_handle(zhp, pool_name, len); 2986865Srm160521 if (zph == NULL) 2996865Srm160521 zph = zpool_add_handle(zhp, pool_name); 3006865Srm160521 3016865Srm160521 free(pool_name); 3026865Srm160521 return (zph); 3036865Srm160521 } 3046865Srm160521 3056865Srm160521 void 3066865Srm160521 zpool_free_handles(libzfs_handle_t *hdl) 3076865Srm160521 { 3086865Srm160521 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 3096865Srm160521 3106865Srm160521 while (zph != NULL) { 3116865Srm160521 next = zph->zpool_next; 3126865Srm160521 zpool_close(zph); 3136865Srm160521 zph = next; 3146865Srm160521 } 3156865Srm160521 hdl->libzfs_pool_handles = NULL; 3166865Srm160521 } 3176865Srm160521 3182676Seschrock /* 319789Sahrens * Utility function to gather stats (objset and zpl) for the given object. 320789Sahrens */ 321789Sahrens static int 3228228SEric.Taylor@Sun.COM get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 323789Sahrens { 3242676Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3258228SEric.Taylor@Sun.COM 3268228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 3278228SEric.Taylor@Sun.COM 3288228SEric.Taylor@Sun.COM while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3291356Seschrock if (errno == ENOMEM) { 3308228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 3312082Seschrock return (-1); 3322676Seschrock } 3331356Seschrock } else { 3341356Seschrock return (-1); 3351356Seschrock } 3361356Seschrock } 3378228SEric.Taylor@Sun.COM return (0); 3388228SEric.Taylor@Sun.COM } 3398228SEric.Taylor@Sun.COM 34011022STom.Erickson@Sun.COM /* 34111022STom.Erickson@Sun.COM * Utility function to get the received properties of the given object. 34211022STom.Erickson@Sun.COM */ 34311022STom.Erickson@Sun.COM static int 34411022STom.Erickson@Sun.COM get_recvd_props_ioctl(zfs_handle_t *zhp) 34511022STom.Erickson@Sun.COM { 34611022STom.Erickson@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 34711022STom.Erickson@Sun.COM nvlist_t *recvdprops; 34811022STom.Erickson@Sun.COM zfs_cmd_t zc = { 0 }; 34911022STom.Erickson@Sun.COM int err; 35011022STom.Erickson@Sun.COM 35111022STom.Erickson@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 35211022STom.Erickson@Sun.COM return (-1); 35311022STom.Erickson@Sun.COM 35411022STom.Erickson@Sun.COM (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35511022STom.Erickson@Sun.COM 35611022STom.Erickson@Sun.COM while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 35711022STom.Erickson@Sun.COM if (errno == ENOMEM) { 35811022STom.Erickson@Sun.COM if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 35911022STom.Erickson@Sun.COM return (-1); 36011022STom.Erickson@Sun.COM } 36111022STom.Erickson@Sun.COM } else { 36211022STom.Erickson@Sun.COM zcmd_free_nvlists(&zc); 36311022STom.Erickson@Sun.COM return (-1); 36411022STom.Erickson@Sun.COM } 36511022STom.Erickson@Sun.COM } 36611022STom.Erickson@Sun.COM 36711022STom.Erickson@Sun.COM err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 36811022STom.Erickson@Sun.COM zcmd_free_nvlists(&zc); 36911022STom.Erickson@Sun.COM if (err != 0) 37011022STom.Erickson@Sun.COM return (-1); 37111022STom.Erickson@Sun.COM 37211022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 37311022STom.Erickson@Sun.COM zhp->zfs_recvd_props = recvdprops; 37411022STom.Erickson@Sun.COM 37511022STom.Erickson@Sun.COM return (0); 37611022STom.Erickson@Sun.COM } 37711022STom.Erickson@Sun.COM 3788228SEric.Taylor@Sun.COM static int 3798228SEric.Taylor@Sun.COM put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 3808228SEric.Taylor@Sun.COM { 3818228SEric.Taylor@Sun.COM nvlist_t *allprops, *userprops; 3828228SEric.Taylor@Sun.COM 3838228SEric.Taylor@Sun.COM zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 3848228SEric.Taylor@Sun.COM 3858228SEric.Taylor@Sun.COM if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 3862082Seschrock return (-1); 3872082Seschrock } 388789Sahrens 3899396SMatthew.Ahrens@Sun.COM /* 3909396SMatthew.Ahrens@Sun.COM * XXX Why do we store the user props separately, in addition to 3919396SMatthew.Ahrens@Sun.COM * storing them in zfs_props? 3929396SMatthew.Ahrens@Sun.COM */ 3934217Seschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 3944217Seschrock nvlist_free(allprops); 3952676Seschrock return (-1); 3964217Seschrock } 3974217Seschrock 3984217Seschrock nvlist_free(zhp->zfs_props); 3994217Seschrock nvlist_free(zhp->zfs_user_props); 4004217Seschrock 4014217Seschrock zhp->zfs_props = allprops; 4024217Seschrock zhp->zfs_user_props = userprops; 4032082Seschrock 404789Sahrens return (0); 405789Sahrens } 406789Sahrens 4078228SEric.Taylor@Sun.COM static int 4088228SEric.Taylor@Sun.COM get_stats(zfs_handle_t *zhp) 4098228SEric.Taylor@Sun.COM { 4108228SEric.Taylor@Sun.COM int rc = 0; 4118228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4128228SEric.Taylor@Sun.COM 4138228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 4148228SEric.Taylor@Sun.COM return (-1); 4158228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) != 0) 4168228SEric.Taylor@Sun.COM rc = -1; 4178228SEric.Taylor@Sun.COM else if (put_stats_zhdl(zhp, &zc) != 0) 4188228SEric.Taylor@Sun.COM rc = -1; 4198228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4208228SEric.Taylor@Sun.COM return (rc); 4218228SEric.Taylor@Sun.COM } 4228228SEric.Taylor@Sun.COM 423789Sahrens /* 424789Sahrens * Refresh the properties currently stored in the handle. 425789Sahrens */ 426789Sahrens void 427789Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 428789Sahrens { 429789Sahrens (void) get_stats(zhp); 430789Sahrens } 431789Sahrens 432789Sahrens /* 433789Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 434789Sahrens * zfs_iter_* to create child handles on the fly. 435789Sahrens */ 4368228SEric.Taylor@Sun.COM static int 4378228SEric.Taylor@Sun.COM make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 438789Sahrens { 43910298SMatthew.Ahrens@Sun.COM if (put_stats_zhdl(zhp, zc) != 0) 4408228SEric.Taylor@Sun.COM return (-1); 4411758Sahrens 442789Sahrens /* 443789Sahrens * We've managed to open the dataset and gather statistics. Determine 444789Sahrens * the high-level type. 445789Sahrens */ 4462885Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 4472885Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 4482885Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 4492885Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 4502885Sahrens else 4512885Sahrens abort(); 4522885Sahrens 453789Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 454789Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 455789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 456789Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 457789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 458789Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 459789Sahrens else 4602082Seschrock abort(); /* we should never see any other types */ 461789Sahrens 4626865Srm160521 zhp->zpool_hdl = zpool_handle(zhp); 4638228SEric.Taylor@Sun.COM return (0); 4648228SEric.Taylor@Sun.COM } 4658228SEric.Taylor@Sun.COM 4668228SEric.Taylor@Sun.COM zfs_handle_t * 4678228SEric.Taylor@Sun.COM make_dataset_handle(libzfs_handle_t *hdl, const char *path) 4688228SEric.Taylor@Sun.COM { 4698228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4708228SEric.Taylor@Sun.COM 4718228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 4728228SEric.Taylor@Sun.COM 4738228SEric.Taylor@Sun.COM if (zhp == NULL) 4748228SEric.Taylor@Sun.COM return (NULL); 4758228SEric.Taylor@Sun.COM 4768228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 4778228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 4788228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 4798228SEric.Taylor@Sun.COM free(zhp); 4808228SEric.Taylor@Sun.COM return (NULL); 4818228SEric.Taylor@Sun.COM } 4828228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) == -1) { 4838228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4848228SEric.Taylor@Sun.COM free(zhp); 4858228SEric.Taylor@Sun.COM return (NULL); 4868228SEric.Taylor@Sun.COM } 4878228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, &zc) == -1) { 4888228SEric.Taylor@Sun.COM free(zhp); 4898228SEric.Taylor@Sun.COM zhp = NULL; 4908228SEric.Taylor@Sun.COM } 4918228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4928228SEric.Taylor@Sun.COM return (zhp); 4938228SEric.Taylor@Sun.COM } 4948228SEric.Taylor@Sun.COM 4958228SEric.Taylor@Sun.COM static zfs_handle_t * 4968228SEric.Taylor@Sun.COM make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 4978228SEric.Taylor@Sun.COM { 4988228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 4998228SEric.Taylor@Sun.COM 5008228SEric.Taylor@Sun.COM if (zhp == NULL) 5018228SEric.Taylor@Sun.COM return (NULL); 5028228SEric.Taylor@Sun.COM 5038228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 5048228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 5058228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, zc) == -1) { 5068228SEric.Taylor@Sun.COM free(zhp); 5078228SEric.Taylor@Sun.COM return (NULL); 5088228SEric.Taylor@Sun.COM } 509789Sahrens return (zhp); 510789Sahrens } 511789Sahrens 512789Sahrens /* 513789Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 514789Sahrens * argument is a mask of acceptable types. The function will print an 515789Sahrens * appropriate error message and return NULL if it can't be opened. 516789Sahrens */ 517789Sahrens zfs_handle_t * 5182082Seschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 519789Sahrens { 520789Sahrens zfs_handle_t *zhp; 5212082Seschrock char errbuf[1024]; 5222082Seschrock 5232082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 5242082Seschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 525789Sahrens 526789Sahrens /* 5272082Seschrock * Validate the name before we even try to open it. 528789Sahrens */ 5295326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 5302082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5312082Seschrock "invalid dataset name")); 5322082Seschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 533789Sahrens return (NULL); 534789Sahrens } 535789Sahrens 536789Sahrens /* 537789Sahrens * Try to get stats for the dataset, which will tell us if it exists. 538789Sahrens */ 539789Sahrens errno = 0; 5402082Seschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 5413237Slling (void) zfs_standard_error(hdl, errno, errbuf); 542789Sahrens return (NULL); 543789Sahrens } 544789Sahrens 545789Sahrens if (!(types & zhp->zfs_type)) { 5462082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 5472142Seschrock zfs_close(zhp); 548789Sahrens return (NULL); 549789Sahrens } 550789Sahrens 551789Sahrens return (zhp); 552789Sahrens } 553789Sahrens 554789Sahrens /* 555789Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 556789Sahrens */ 557789Sahrens void 558789Sahrens zfs_close(zfs_handle_t *zhp) 559789Sahrens { 560789Sahrens if (zhp->zfs_mntopts) 561789Sahrens free(zhp->zfs_mntopts); 5622676Seschrock nvlist_free(zhp->zfs_props); 5632676Seschrock nvlist_free(zhp->zfs_user_props); 56411022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 565789Sahrens free(zhp); 566789Sahrens } 567789Sahrens 5688228SEric.Taylor@Sun.COM typedef struct mnttab_node { 5698228SEric.Taylor@Sun.COM struct mnttab mtn_mt; 5708228SEric.Taylor@Sun.COM avl_node_t mtn_node; 5718228SEric.Taylor@Sun.COM } mnttab_node_t; 5728228SEric.Taylor@Sun.COM 5738228SEric.Taylor@Sun.COM static int 5748228SEric.Taylor@Sun.COM libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 5758228SEric.Taylor@Sun.COM { 5768228SEric.Taylor@Sun.COM const mnttab_node_t *mtn1 = arg1; 5778228SEric.Taylor@Sun.COM const mnttab_node_t *mtn2 = arg2; 5788228SEric.Taylor@Sun.COM int rv; 5798228SEric.Taylor@Sun.COM 5808228SEric.Taylor@Sun.COM rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 5818228SEric.Taylor@Sun.COM 5828228SEric.Taylor@Sun.COM if (rv == 0) 5838228SEric.Taylor@Sun.COM return (0); 5848228SEric.Taylor@Sun.COM return (rv > 0 ? 1 : -1); 5858228SEric.Taylor@Sun.COM } 5868228SEric.Taylor@Sun.COM 5878228SEric.Taylor@Sun.COM void 5888228SEric.Taylor@Sun.COM libzfs_mnttab_init(libzfs_handle_t *hdl) 5898228SEric.Taylor@Sun.COM { 5908228SEric.Taylor@Sun.COM assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 5918228SEric.Taylor@Sun.COM avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 5928228SEric.Taylor@Sun.COM sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 5938811SEric.Taylor@Sun.COM } 5948811SEric.Taylor@Sun.COM 5958811SEric.Taylor@Sun.COM void 5968811SEric.Taylor@Sun.COM libzfs_mnttab_update(libzfs_handle_t *hdl) 5978811SEric.Taylor@Sun.COM { 5988811SEric.Taylor@Sun.COM struct mnttab entry; 5998228SEric.Taylor@Sun.COM 6008228SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6018228SEric.Taylor@Sun.COM while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 6028228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6038228SEric.Taylor@Sun.COM 6048228SEric.Taylor@Sun.COM if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6058228SEric.Taylor@Sun.COM continue; 6068228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6078228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 6088228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 6098228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 6108228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 6118228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6128228SEric.Taylor@Sun.COM } 6138228SEric.Taylor@Sun.COM } 6148228SEric.Taylor@Sun.COM 6158228SEric.Taylor@Sun.COM void 6168228SEric.Taylor@Sun.COM libzfs_mnttab_fini(libzfs_handle_t *hdl) 6178228SEric.Taylor@Sun.COM { 6188228SEric.Taylor@Sun.COM void *cookie = NULL; 6198228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6208228SEric.Taylor@Sun.COM 6218228SEric.Taylor@Sun.COM while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 6228228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_special); 6238228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mountp); 6248228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_fstype); 6258228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mntopts); 6268228SEric.Taylor@Sun.COM free(mtn); 6278228SEric.Taylor@Sun.COM } 6288228SEric.Taylor@Sun.COM avl_destroy(&hdl->libzfs_mnttab_cache); 6298228SEric.Taylor@Sun.COM } 6308228SEric.Taylor@Sun.COM 6318811SEric.Taylor@Sun.COM void 6328811SEric.Taylor@Sun.COM libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 6338811SEric.Taylor@Sun.COM { 6348811SEric.Taylor@Sun.COM hdl->libzfs_mnttab_enable = enable; 6358811SEric.Taylor@Sun.COM } 6368811SEric.Taylor@Sun.COM 6378228SEric.Taylor@Sun.COM int 6388228SEric.Taylor@Sun.COM libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 6398228SEric.Taylor@Sun.COM struct mnttab *entry) 6408228SEric.Taylor@Sun.COM { 6418228SEric.Taylor@Sun.COM mnttab_node_t find; 6428228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6438228SEric.Taylor@Sun.COM 6448811SEric.Taylor@Sun.COM if (!hdl->libzfs_mnttab_enable) { 6458811SEric.Taylor@Sun.COM struct mnttab srch = { 0 }; 6468811SEric.Taylor@Sun.COM 6478811SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 6488811SEric.Taylor@Sun.COM libzfs_mnttab_fini(hdl); 6498811SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6508811SEric.Taylor@Sun.COM srch.mnt_special = (char *)fsname; 6518811SEric.Taylor@Sun.COM srch.mnt_fstype = MNTTYPE_ZFS; 6528811SEric.Taylor@Sun.COM if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 6538811SEric.Taylor@Sun.COM return (0); 6548811SEric.Taylor@Sun.COM else 6558811SEric.Taylor@Sun.COM return (ENOENT); 6568811SEric.Taylor@Sun.COM } 6578811SEric.Taylor@Sun.COM 6588228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6598811SEric.Taylor@Sun.COM libzfs_mnttab_update(hdl); 6608228SEric.Taylor@Sun.COM 6618228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6628228SEric.Taylor@Sun.COM mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 6638228SEric.Taylor@Sun.COM if (mtn) { 6648228SEric.Taylor@Sun.COM *entry = mtn->mtn_mt; 6658228SEric.Taylor@Sun.COM return (0); 6668228SEric.Taylor@Sun.COM } 6678228SEric.Taylor@Sun.COM return (ENOENT); 6688228SEric.Taylor@Sun.COM } 6698228SEric.Taylor@Sun.COM 6708228SEric.Taylor@Sun.COM void 6718228SEric.Taylor@Sun.COM libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 6728228SEric.Taylor@Sun.COM const char *mountp, const char *mntopts) 6738228SEric.Taylor@Sun.COM { 6748228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6758228SEric.Taylor@Sun.COM 6768228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6778228SEric.Taylor@Sun.COM return; 6788228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6798228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 6808228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 6818228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 6828228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 6838228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6848228SEric.Taylor@Sun.COM } 6858228SEric.Taylor@Sun.COM 6868228SEric.Taylor@Sun.COM void 6878228SEric.Taylor@Sun.COM libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 6888228SEric.Taylor@Sun.COM { 6898228SEric.Taylor@Sun.COM mnttab_node_t find; 6908228SEric.Taylor@Sun.COM mnttab_node_t *ret; 6918228SEric.Taylor@Sun.COM 6928228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6938228SEric.Taylor@Sun.COM if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 6948228SEric.Taylor@Sun.COM avl_remove(&hdl->libzfs_mnttab_cache, ret); 6958228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_special); 6968228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mountp); 6978228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_fstype); 6988228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mntopts); 6998228SEric.Taylor@Sun.COM free(ret); 7008228SEric.Taylor@Sun.COM } 7018228SEric.Taylor@Sun.COM } 7028228SEric.Taylor@Sun.COM 7035713Srm160521 int 7045713Srm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7055713Srm160521 { 7066865Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7076865Srm160521 7085713Srm160521 if (zpool_handle == NULL) 7095713Srm160521 return (-1); 7105713Srm160521 7115713Srm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7125713Srm160521 ZPOOL_PROP_VERSION, NULL); 7135713Srm160521 return (0); 7145713Srm160521 } 7155713Srm160521 7165713Srm160521 /* 7175713Srm160521 * The choice of reservation property depends on the SPA version. 7185713Srm160521 */ 7195713Srm160521 static int 7205713Srm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7215713Srm160521 { 7225713Srm160521 int spa_version; 7235713Srm160521 7245713Srm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7255713Srm160521 return (-1); 7265713Srm160521 7275713Srm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7285713Srm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7295713Srm160521 else 7305713Srm160521 *resv_prop = ZFS_PROP_RESERVATION; 7315713Srm160521 7325713Srm160521 return (0); 7335713Srm160521 } 7345713Srm160521 7353912Slling /* 7362676Seschrock * Given an nvlist of properties to set, validates that they are correct, and 7372676Seschrock * parses any numeric properties (index, boolean, etc) if they are specified as 7382676Seschrock * strings. 739789Sahrens */ 7407184Stimh nvlist_t * 7417184Stimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 7425094Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 743789Sahrens { 7442676Seschrock nvpair_t *elem; 7452676Seschrock uint64_t intval; 7462676Seschrock char *strval; 7475094Slling zfs_prop_t prop; 7482676Seschrock nvlist_t *ret; 7495331Samw int chosen_normal = -1; 7505331Samw int chosen_utf = -1; 7512676Seschrock 7525094Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 7535094Slling (void) no_memory(hdl); 7545094Slling return (NULL); 755789Sahrens } 756789Sahrens 7579396SMatthew.Ahrens@Sun.COM /* 7589396SMatthew.Ahrens@Sun.COM * Make sure this property is valid and applies to this type. 7599396SMatthew.Ahrens@Sun.COM */ 7609396SMatthew.Ahrens@Sun.COM 7612676Seschrock elem = NULL; 7622676Seschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 7635094Slling const char *propname = nvpair_name(elem); 7642676Seschrock 7659396SMatthew.Ahrens@Sun.COM prop = zfs_name_to_prop(propname); 7669396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 7675094Slling /* 7689396SMatthew.Ahrens@Sun.COM * This is a user property: make sure it's a 7695094Slling * string, and that it's less than ZAP_MAXNAMELEN. 7705094Slling */ 7715094Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 7725094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7735094Slling "'%s' must be a string"), propname); 7745094Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7755094Slling goto error; 7765094Slling } 7775094Slling 7785094Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 7795094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7805094Slling "property name '%s' is too long"), 7812676Seschrock propname); 7822676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7832676Seschrock goto error; 7842676Seschrock } 7852676Seschrock 7862676Seschrock (void) nvpair_value_string(elem, &strval); 7872676Seschrock if (nvlist_add_string(ret, propname, strval) != 0) { 7882676Seschrock (void) no_memory(hdl); 7892676Seschrock goto error; 7902676Seschrock } 7912676Seschrock continue; 792789Sahrens } 7932676Seschrock 7949396SMatthew.Ahrens@Sun.COM /* 7959396SMatthew.Ahrens@Sun.COM * Currently, only user properties can be modified on 7969396SMatthew.Ahrens@Sun.COM * snapshots. 7979396SMatthew.Ahrens@Sun.COM */ 7987265Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 7997265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8007265Sahrens "this property can not be modified for snapshots")); 8017265Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8027265Sahrens goto error; 8037265Sahrens } 8047265Sahrens 8059396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 8069396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t uqtype; 8079396SMatthew.Ahrens@Sun.COM char newpropname[128]; 8089396SMatthew.Ahrens@Sun.COM char domain[128]; 8099396SMatthew.Ahrens@Sun.COM uint64_t rid; 8109396SMatthew.Ahrens@Sun.COM uint64_t valary[3]; 8119396SMatthew.Ahrens@Sun.COM 8129396SMatthew.Ahrens@Sun.COM if (userquota_propname_decode(propname, zoned, 8139396SMatthew.Ahrens@Sun.COM &uqtype, domain, sizeof (domain), &rid) != 0) { 8149396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8159396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, 8169396SMatthew.Ahrens@Sun.COM "'%s' has an invalid user/group name"), 8179396SMatthew.Ahrens@Sun.COM propname); 8189396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8199396SMatthew.Ahrens@Sun.COM goto error; 8209396SMatthew.Ahrens@Sun.COM } 8219396SMatthew.Ahrens@Sun.COM 8229396SMatthew.Ahrens@Sun.COM if (uqtype != ZFS_PROP_USERQUOTA && 8239396SMatthew.Ahrens@Sun.COM uqtype != ZFS_PROP_GROUPQUOTA) { 8249396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8259396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8269396SMatthew.Ahrens@Sun.COM propname); 8279396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_PROPREADONLY, 8289396SMatthew.Ahrens@Sun.COM errbuf); 8299396SMatthew.Ahrens@Sun.COM goto error; 8309396SMatthew.Ahrens@Sun.COM } 8319396SMatthew.Ahrens@Sun.COM 8329396SMatthew.Ahrens@Sun.COM if (nvpair_type(elem) == DATA_TYPE_STRING) { 8339396SMatthew.Ahrens@Sun.COM (void) nvpair_value_string(elem, &strval); 8349396SMatthew.Ahrens@Sun.COM if (strcmp(strval, "none") == 0) { 8359396SMatthew.Ahrens@Sun.COM intval = 0; 8369396SMatthew.Ahrens@Sun.COM } else if (zfs_nicestrtonum(hdl, 8379396SMatthew.Ahrens@Sun.COM strval, &intval) != 0) { 8389396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, 8399396SMatthew.Ahrens@Sun.COM EZFS_BADPROP, errbuf); 8409396SMatthew.Ahrens@Sun.COM goto error; 8419396SMatthew.Ahrens@Sun.COM } 8429396SMatthew.Ahrens@Sun.COM } else if (nvpair_type(elem) == 8439396SMatthew.Ahrens@Sun.COM DATA_TYPE_UINT64) { 8449396SMatthew.Ahrens@Sun.COM (void) nvpair_value_uint64(elem, &intval); 8459396SMatthew.Ahrens@Sun.COM if (intval == 0) { 8469396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8479396SMatthew.Ahrens@Sun.COM "use 'none' to disable " 8489396SMatthew.Ahrens@Sun.COM "userquota/groupquota")); 8499396SMatthew.Ahrens@Sun.COM goto error; 8509396SMatthew.Ahrens@Sun.COM } 8519396SMatthew.Ahrens@Sun.COM } else { 8529396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8539396SMatthew.Ahrens@Sun.COM "'%s' must be a number"), propname); 8549396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8559396SMatthew.Ahrens@Sun.COM goto error; 8569396SMatthew.Ahrens@Sun.COM } 8579396SMatthew.Ahrens@Sun.COM 85810969SMatthew.Ahrens@Sun.COM /* 85910969SMatthew.Ahrens@Sun.COM * Encode the prop name as 86010969SMatthew.Ahrens@Sun.COM * userquota@<hex-rid>-domain, to make it easy 86110969SMatthew.Ahrens@Sun.COM * for the kernel to decode. 86210969SMatthew.Ahrens@Sun.COM */ 8639396SMatthew.Ahrens@Sun.COM (void) snprintf(newpropname, sizeof (newpropname), 86410969SMatthew.Ahrens@Sun.COM "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 86510969SMatthew.Ahrens@Sun.COM (longlong_t)rid, domain); 8669396SMatthew.Ahrens@Sun.COM valary[0] = uqtype; 8679396SMatthew.Ahrens@Sun.COM valary[1] = rid; 8689396SMatthew.Ahrens@Sun.COM valary[2] = intval; 8699396SMatthew.Ahrens@Sun.COM if (nvlist_add_uint64_array(ret, newpropname, 8709396SMatthew.Ahrens@Sun.COM valary, 3) != 0) { 8719396SMatthew.Ahrens@Sun.COM (void) no_memory(hdl); 8729396SMatthew.Ahrens@Sun.COM goto error; 8739396SMatthew.Ahrens@Sun.COM } 8749396SMatthew.Ahrens@Sun.COM continue; 8759396SMatthew.Ahrens@Sun.COM } 8769396SMatthew.Ahrens@Sun.COM 8779396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL) { 8789396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8799396SMatthew.Ahrens@Sun.COM "invalid property '%s'"), propname); 8809396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8819396SMatthew.Ahrens@Sun.COM goto error; 8829396SMatthew.Ahrens@Sun.COM } 8839396SMatthew.Ahrens@Sun.COM 8842676Seschrock if (!zfs_prop_valid_for_type(prop, type)) { 8852676Seschrock zfs_error_aux(hdl, 8862676Seschrock dgettext(TEXT_DOMAIN, "'%s' does not " 8872676Seschrock "apply to datasets of this type"), propname); 8882676Seschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8892676Seschrock goto error; 8902676Seschrock } 8912676Seschrock 8922676Seschrock if (zfs_prop_readonly(prop) && 8935331Samw (!zfs_prop_setonce(prop) || zhp != NULL)) { 8942676Seschrock zfs_error_aux(hdl, 8952676Seschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8962676Seschrock propname); 8972676Seschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 8982676Seschrock goto error; 8992676Seschrock } 9002676Seschrock 9015094Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 9025094Slling &strval, &intval, errbuf) != 0) 9032676Seschrock goto error; 9042676Seschrock 9052676Seschrock /* 9062676Seschrock * Perform some additional checks for specific properties. 9072676Seschrock */ 9082676Seschrock switch (prop) { 9094577Sahrens case ZFS_PROP_VERSION: 9104577Sahrens { 9114577Sahrens int version; 9124577Sahrens 9134577Sahrens if (zhp == NULL) 9144577Sahrens break; 9154577Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 9164577Sahrens if (intval < version) { 9174577Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9184577Sahrens "Can not downgrade; already at version %u"), 9194577Sahrens version); 9204577Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9214577Sahrens goto error; 9224577Sahrens } 9234577Sahrens break; 9244577Sahrens } 9254577Sahrens 9262676Seschrock case ZFS_PROP_RECORDSIZE: 9272676Seschrock case ZFS_PROP_VOLBLOCKSIZE: 9282676Seschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 9292676Seschrock if (intval < SPA_MINBLOCKSIZE || 9302676Seschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 9312082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9322676Seschrock "'%s' must be power of 2 from %u " 9332676Seschrock "to %uk"), propname, 9342676Seschrock (uint_t)SPA_MINBLOCKSIZE, 9352676Seschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 9362676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9372676Seschrock goto error; 938789Sahrens } 939789Sahrens break; 940789Sahrens 9413126Sahl case ZFS_PROP_SHAREISCSI: 9423126Sahl if (strcmp(strval, "off") != 0 && 9433126Sahl strcmp(strval, "on") != 0 && 9443126Sahl strcmp(strval, "type=disk") != 0) { 9453126Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9463126Sahl "'%s' must be 'on', 'off', or 'type=disk'"), 9473126Sahl propname); 9483126Sahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9493126Sahl goto error; 9503126Sahl } 9513126Sahl 9523126Sahl break; 9533126Sahl 95410972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 95510972SRic.Aleshire@Sun.COM { 95610972SRic.Aleshire@Sun.COM /* 95710972SRic.Aleshire@Sun.COM * Verify the mlslabel string and convert to 95810972SRic.Aleshire@Sun.COM * internal hex label string. 95910972SRic.Aleshire@Sun.COM */ 96010972SRic.Aleshire@Sun.COM 96110972SRic.Aleshire@Sun.COM m_label_t *new_sl; 96210972SRic.Aleshire@Sun.COM char *hex = NULL; /* internal label string */ 96310972SRic.Aleshire@Sun.COM 96410972SRic.Aleshire@Sun.COM /* Default value is already OK. */ 96510972SRic.Aleshire@Sun.COM if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 96610972SRic.Aleshire@Sun.COM break; 96710972SRic.Aleshire@Sun.COM 96810972SRic.Aleshire@Sun.COM /* Verify the label can be converted to binary form */ 96910972SRic.Aleshire@Sun.COM if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 97010972SRic.Aleshire@Sun.COM (str_to_label(strval, &new_sl, MAC_LABEL, 97110972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1)) { 97210972SRic.Aleshire@Sun.COM goto badlabel; 97310972SRic.Aleshire@Sun.COM } 97410972SRic.Aleshire@Sun.COM 97510972SRic.Aleshire@Sun.COM /* Now translate to hex internal label string */ 97610972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &hex, M_INTERNAL, 97710972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 97810972SRic.Aleshire@Sun.COM if (hex) 97910972SRic.Aleshire@Sun.COM free(hex); 98010972SRic.Aleshire@Sun.COM goto badlabel; 98110972SRic.Aleshire@Sun.COM } 98210972SRic.Aleshire@Sun.COM m_label_free(new_sl); 98310972SRic.Aleshire@Sun.COM 98410972SRic.Aleshire@Sun.COM /* If string is already in internal form, we're done. */ 98510972SRic.Aleshire@Sun.COM if (strcmp(strval, hex) == 0) { 98610972SRic.Aleshire@Sun.COM free(hex); 98710972SRic.Aleshire@Sun.COM break; 98810972SRic.Aleshire@Sun.COM } 98910972SRic.Aleshire@Sun.COM 99010972SRic.Aleshire@Sun.COM /* Replace the label string with the internal form. */ 99110984SRic.Aleshire@Sun.COM (void) nvlist_remove(ret, zfs_prop_to_name(prop), 99210972SRic.Aleshire@Sun.COM DATA_TYPE_STRING); 99310972SRic.Aleshire@Sun.COM verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 99410972SRic.Aleshire@Sun.COM hex) == 0); 99510972SRic.Aleshire@Sun.COM free(hex); 99610972SRic.Aleshire@Sun.COM 99710972SRic.Aleshire@Sun.COM break; 99810972SRic.Aleshire@Sun.COM 99910972SRic.Aleshire@Sun.COM badlabel: 100010972SRic.Aleshire@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 100110972SRic.Aleshire@Sun.COM "invalid mlslabel '%s'"), strval); 100210972SRic.Aleshire@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 100310972SRic.Aleshire@Sun.COM m_label_free(new_sl); /* OK if null */ 100410972SRic.Aleshire@Sun.COM goto error; 100510972SRic.Aleshire@Sun.COM 100610972SRic.Aleshire@Sun.COM } 100710972SRic.Aleshire@Sun.COM 10082676Seschrock case ZFS_PROP_MOUNTPOINT: 10094778Srm160521 { 10104778Srm160521 namecheck_err_t why; 10114778Srm160521 10122676Seschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 10132676Seschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 10142676Seschrock break; 10152676Seschrock 10164778Srm160521 if (mountpoint_namecheck(strval, &why)) { 10174778Srm160521 switch (why) { 10184778Srm160521 case NAME_ERR_LEADING_SLASH: 10194778Srm160521 zfs_error_aux(hdl, 10204778Srm160521 dgettext(TEXT_DOMAIN, 10214778Srm160521 "'%s' must be an absolute path, " 10224778Srm160521 "'none', or 'legacy'"), propname); 10234778Srm160521 break; 10244778Srm160521 case NAME_ERR_TOOLONG: 10254778Srm160521 zfs_error_aux(hdl, 10264778Srm160521 dgettext(TEXT_DOMAIN, 10274778Srm160521 "component of '%s' is too long"), 10284778Srm160521 propname); 10294778Srm160521 break; 10304778Srm160521 } 10312676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10322676Seschrock goto error; 1033789Sahrens } 10344778Srm160521 } 10354778Srm160521 10363126Sahl /*FALLTHRU*/ 10373126Sahl 10385331Samw case ZFS_PROP_SHARESMB: 10393126Sahl case ZFS_PROP_SHARENFS: 10403126Sahl /* 10415331Samw * For the mountpoint and sharenfs or sharesmb 10425331Samw * properties, check if it can be set in a 10435331Samw * global/non-global zone based on 10443126Sahl * the zoned property value: 10453126Sahl * 10463126Sahl * global zone non-global zone 10473126Sahl * -------------------------------------------------- 10483126Sahl * zoned=on mountpoint (no) mountpoint (yes) 10493126Sahl * sharenfs (no) sharenfs (no) 10505331Samw * sharesmb (no) sharesmb (no) 10513126Sahl * 10523126Sahl * zoned=off mountpoint (yes) N/A 10533126Sahl * sharenfs (yes) 10545331Samw * sharesmb (yes) 10553126Sahl */ 10562676Seschrock if (zoned) { 10572676Seschrock if (getzoneid() == GLOBAL_ZONEID) { 10582676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10592676Seschrock "'%s' cannot be set on " 10602676Seschrock "dataset in a non-global zone"), 10612676Seschrock propname); 10622676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10632676Seschrock errbuf); 10642676Seschrock goto error; 10655331Samw } else if (prop == ZFS_PROP_SHARENFS || 10665331Samw prop == ZFS_PROP_SHARESMB) { 10672676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10682676Seschrock "'%s' cannot be set in " 10692676Seschrock "a non-global zone"), propname); 10702676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10712676Seschrock errbuf); 10722676Seschrock goto error; 10732676Seschrock } 10742676Seschrock } else if (getzoneid() != GLOBAL_ZONEID) { 10752676Seschrock /* 10762676Seschrock * If zoned property is 'off', this must be in 10779396SMatthew.Ahrens@Sun.COM * a global zone. If not, something is wrong. 10782676Seschrock */ 10792676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10802676Seschrock "'%s' cannot be set while dataset " 10812676Seschrock "'zoned' property is set"), propname); 10822676Seschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 10832676Seschrock goto error; 10842676Seschrock } 10853126Sahl 10864180Sdougm /* 10874180Sdougm * At this point, it is legitimate to set the 10884180Sdougm * property. Now we want to make sure that the 10894180Sdougm * property value is valid if it is sharenfs. 10904180Sdougm */ 10915331Samw if ((prop == ZFS_PROP_SHARENFS || 10925331Samw prop == ZFS_PROP_SHARESMB) && 10934217Seschrock strcmp(strval, "on") != 0 && 10944217Seschrock strcmp(strval, "off") != 0) { 10955331Samw zfs_share_proto_t proto; 10965331Samw 10975331Samw if (prop == ZFS_PROP_SHARESMB) 10985331Samw proto = PROTO_SMB; 10995331Samw else 11005331Samw proto = PROTO_NFS; 11014180Sdougm 11024180Sdougm /* 11035331Samw * Must be an valid sharing protocol 11045331Samw * option string so init the libshare 11055331Samw * in order to enable the parser and 11065331Samw * then parse the options. We use the 11075331Samw * control API since we don't care about 11085331Samw * the current configuration and don't 11094180Sdougm * want the overhead of loading it 11104180Sdougm * until we actually do something. 11114180Sdougm */ 11124180Sdougm 11134217Seschrock if (zfs_init_libshare(hdl, 11144217Seschrock SA_INIT_CONTROL_API) != SA_OK) { 11154217Seschrock /* 11164217Seschrock * An error occurred so we can't do 11174217Seschrock * anything 11184217Seschrock */ 11194217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11204217Seschrock "'%s' cannot be set: problem " 11214217Seschrock "in share initialization"), 11224217Seschrock propname); 11234217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11244217Seschrock errbuf); 11254217Seschrock goto error; 11264217Seschrock } 11274217Seschrock 11285331Samw if (zfs_parse_options(strval, proto) != SA_OK) { 11294217Seschrock /* 11304217Seschrock * There was an error in parsing so 11314217Seschrock * deal with it by issuing an error 11324217Seschrock * message and leaving after 11334217Seschrock * uninitializing the the libshare 11344217Seschrock * interface. 11354217Seschrock */ 11364217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11374217Seschrock "'%s' cannot be set to invalid " 11384217Seschrock "options"), propname); 11394217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11404217Seschrock errbuf); 11414217Seschrock zfs_uninit_libshare(hdl); 11424217Seschrock goto error; 11434217Seschrock } 11444180Sdougm zfs_uninit_libshare(hdl); 11454180Sdougm } 11464180Sdougm 11473126Sahl break; 11485331Samw case ZFS_PROP_UTF8ONLY: 11495331Samw chosen_utf = (int)intval; 11505331Samw break; 11515331Samw case ZFS_PROP_NORMALIZE: 11525331Samw chosen_normal = (int)intval; 11535331Samw break; 11542676Seschrock } 11552676Seschrock 11562676Seschrock /* 11572676Seschrock * For changes to existing volumes, we have some additional 11582676Seschrock * checks to enforce. 11592676Seschrock */ 11602676Seschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 11612676Seschrock uint64_t volsize = zfs_prop_get_int(zhp, 11622676Seschrock ZFS_PROP_VOLSIZE); 11632676Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 11642676Seschrock ZFS_PROP_VOLBLOCKSIZE); 11652676Seschrock char buf[64]; 11662676Seschrock 11672676Seschrock switch (prop) { 11682676Seschrock case ZFS_PROP_RESERVATION: 11695378Sck153898 case ZFS_PROP_REFRESERVATION: 11702676Seschrock if (intval > volsize) { 11712676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11722676Seschrock "'%s' is greater than current " 11732676Seschrock "volume size"), propname); 11742676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11752676Seschrock errbuf); 11762676Seschrock goto error; 11772676Seschrock } 11782676Seschrock break; 11792676Seschrock 11802676Seschrock case ZFS_PROP_VOLSIZE: 11812676Seschrock if (intval % blocksize != 0) { 11822676Seschrock zfs_nicenum(blocksize, buf, 11832676Seschrock sizeof (buf)); 11842676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11852676Seschrock "'%s' must be a multiple of " 11862676Seschrock "volume block size (%s)"), 11872676Seschrock propname, buf); 11882676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11892676Seschrock errbuf); 11902676Seschrock goto error; 11912676Seschrock } 11922676Seschrock 11932676Seschrock if (intval == 0) { 11942676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11952676Seschrock "'%s' cannot be zero"), 11962676Seschrock propname); 11972676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11982676Seschrock errbuf); 11992676Seschrock goto error; 1200789Sahrens } 12013126Sahl break; 1202789Sahrens } 1203789Sahrens } 1204789Sahrens } 1205789Sahrens 12062676Seschrock /* 12075331Samw * If normalization was chosen, but no UTF8 choice was made, 12085331Samw * enforce rejection of non-UTF8 names. 12095331Samw * 12105331Samw * If normalization was chosen, but rejecting non-UTF8 names 12115331Samw * was explicitly not chosen, it is an error. 12125331Samw */ 12135498Stimh if (chosen_normal > 0 && chosen_utf < 0) { 12145331Samw if (nvlist_add_uint64(ret, 12155331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 12165331Samw (void) no_memory(hdl); 12175331Samw goto error; 12185331Samw } 12195498Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 12205331Samw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12215331Samw "'%s' must be set 'on' if normalization chosen"), 12225331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 12235331Samw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 12245331Samw goto error; 12255331Samw } 12265331Samw 12275331Samw /* 12282676Seschrock * If this is an existing volume, and someone is setting the volsize, 12292676Seschrock * make sure that it matches the reservation, or add it if necessary. 12302676Seschrock */ 12312676Seschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 12322676Seschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 12332676Seschrock &intval) == 0) { 12342676Seschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 12352676Seschrock ZFS_PROP_VOLSIZE); 12365481Sck153898 uint64_t old_reservation; 12372676Seschrock uint64_t new_reservation; 12385481Sck153898 zfs_prop_t resv_prop; 12395713Srm160521 12405713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 12415481Sck153898 goto error; 12425481Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 12432676Seschrock 12442676Seschrock if (old_volsize == old_reservation && 12455481Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 12462676Seschrock &new_reservation) != 0) { 12472676Seschrock if (nvlist_add_uint64(ret, 12485481Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 12492676Seschrock (void) no_memory(hdl); 12502676Seschrock goto error; 12512676Seschrock } 12522676Seschrock } 12532676Seschrock } 12542676Seschrock return (ret); 12552676Seschrock 12562676Seschrock error: 12572676Seschrock nvlist_free(ret); 12582676Seschrock return (NULL); 1259789Sahrens } 1260789Sahrens 126111022STom.Erickson@Sun.COM void 126211022STom.Erickson@Sun.COM zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 126311022STom.Erickson@Sun.COM char *errbuf) 126411022STom.Erickson@Sun.COM { 126511022STom.Erickson@Sun.COM switch (err) { 126611022STom.Erickson@Sun.COM 126711022STom.Erickson@Sun.COM case ENOSPC: 126811022STom.Erickson@Sun.COM /* 126911022STom.Erickson@Sun.COM * For quotas and reservations, ENOSPC indicates 127011022STom.Erickson@Sun.COM * something different; setting a quota or reservation 127111022STom.Erickson@Sun.COM * doesn't use any disk space. 127211022STom.Erickson@Sun.COM */ 127311022STom.Erickson@Sun.COM switch (prop) { 127411022STom.Erickson@Sun.COM case ZFS_PROP_QUOTA: 127511022STom.Erickson@Sun.COM case ZFS_PROP_REFQUOTA: 127611022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127711022STom.Erickson@Sun.COM "size is less than current used or " 127811022STom.Erickson@Sun.COM "reserved space")); 127911022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128011022STom.Erickson@Sun.COM break; 128111022STom.Erickson@Sun.COM 128211022STom.Erickson@Sun.COM case ZFS_PROP_RESERVATION: 128311022STom.Erickson@Sun.COM case ZFS_PROP_REFRESERVATION: 128411022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128511022STom.Erickson@Sun.COM "size is greater than available space")); 128611022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128711022STom.Erickson@Sun.COM break; 128811022STom.Erickson@Sun.COM 128911022STom.Erickson@Sun.COM default: 129011022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 129111022STom.Erickson@Sun.COM break; 129211022STom.Erickson@Sun.COM } 129311022STom.Erickson@Sun.COM break; 129411022STom.Erickson@Sun.COM 129511022STom.Erickson@Sun.COM case EBUSY: 129611022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, EBUSY, errbuf); 129711022STom.Erickson@Sun.COM break; 129811022STom.Erickson@Sun.COM 129911022STom.Erickson@Sun.COM case EROFS: 130011022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 130111022STom.Erickson@Sun.COM break; 130211022STom.Erickson@Sun.COM 130311022STom.Erickson@Sun.COM case ENOTSUP: 130411022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130511022STom.Erickson@Sun.COM "pool and or dataset must be upgraded to set this " 130611022STom.Erickson@Sun.COM "property or value")); 130711022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 130811022STom.Erickson@Sun.COM break; 130911022STom.Erickson@Sun.COM 131011022STom.Erickson@Sun.COM case ERANGE: 131111022STom.Erickson@Sun.COM if (prop == ZFS_PROP_COMPRESSION) { 131211022STom.Erickson@Sun.COM (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 131311022STom.Erickson@Sun.COM "property setting is not allowed on " 131411022STom.Erickson@Sun.COM "bootable datasets")); 131511022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 131611022STom.Erickson@Sun.COM } else { 131711022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 131811022STom.Erickson@Sun.COM } 131911022STom.Erickson@Sun.COM break; 132011022STom.Erickson@Sun.COM 132111022STom.Erickson@Sun.COM case EOVERFLOW: 132211022STom.Erickson@Sun.COM /* 132311022STom.Erickson@Sun.COM * This platform can't address a volume this big. 132411022STom.Erickson@Sun.COM */ 132511022STom.Erickson@Sun.COM #ifdef _ILP32 132611022STom.Erickson@Sun.COM if (prop == ZFS_PROP_VOLSIZE) { 132711022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132811022STom.Erickson@Sun.COM break; 132911022STom.Erickson@Sun.COM } 133011022STom.Erickson@Sun.COM #endif 133111022STom.Erickson@Sun.COM /* FALLTHROUGH */ 133211022STom.Erickson@Sun.COM default: 133311022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 133411022STom.Erickson@Sun.COM } 133511022STom.Erickson@Sun.COM } 133611022STom.Erickson@Sun.COM 1337789Sahrens /* 1338789Sahrens * Given a property name and value, set the property for the given dataset. 1339789Sahrens */ 1340789Sahrens int 13412676Seschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1342789Sahrens { 1343789Sahrens zfs_cmd_t zc = { 0 }; 13442676Seschrock int ret = -1; 13452676Seschrock prop_changelist_t *cl = NULL; 13462082Seschrock char errbuf[1024]; 13472082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 13482676Seschrock nvlist_t *nvl = NULL, *realprops; 13492676Seschrock zfs_prop_t prop; 13507509SMark.Musante@Sun.COM boolean_t do_prefix; 13517509SMark.Musante@Sun.COM uint64_t idx; 13522082Seschrock 13532082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 13542676Seschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 13552082Seschrock zhp->zfs_name); 13562082Seschrock 13572676Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 13582676Seschrock nvlist_add_string(nvl, propname, propval) != 0) { 13592676Seschrock (void) no_memory(hdl); 13602676Seschrock goto error; 1361789Sahrens } 1362789Sahrens 13637184Stimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 13642676Seschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 13652676Seschrock goto error; 13665094Slling 13672676Seschrock nvlist_free(nvl); 13682676Seschrock nvl = realprops; 13692676Seschrock 13702676Seschrock prop = zfs_name_to_prop(propname); 13712676Seschrock 13727366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 13732676Seschrock goto error; 1374789Sahrens 1375789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 13762082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13772082Seschrock "child dataset with inherited mountpoint is used " 13782082Seschrock "in a non-global zone")); 13792082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1380789Sahrens goto error; 1381789Sahrens } 1382789Sahrens 13837509SMark.Musante@Sun.COM /* 13847509SMark.Musante@Sun.COM * If the dataset's canmount property is being set to noauto, 13857509SMark.Musante@Sun.COM * then we want to prevent unmounting & remounting it. 13867509SMark.Musante@Sun.COM */ 13877509SMark.Musante@Sun.COM do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13887509SMark.Musante@Sun.COM (zprop_string_to_index(prop, propval, &idx, 13897509SMark.Musante@Sun.COM ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 13906168Shs24103 13916168Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 13927509SMark.Musante@Sun.COM goto error; 1393789Sahrens 1394789Sahrens /* 1395789Sahrens * Execute the corresponding ioctl() to set this property. 1396789Sahrens */ 1397789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1398789Sahrens 13995094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 14002676Seschrock goto error; 14012676Seschrock 14024543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 14038845Samw@Sun.COM 1404789Sahrens if (ret != 0) { 140511022STom.Erickson@Sun.COM zfs_setprop_error(hdl, prop, errno, errbuf); 1406789Sahrens } else { 14076168Shs24103 if (do_prefix) 14086168Shs24103 ret = changelist_postfix(cl); 14096168Shs24103 1410789Sahrens /* 1411789Sahrens * Refresh the statistics so the new property value 1412789Sahrens * is reflected. 1413789Sahrens */ 14146168Shs24103 if (ret == 0) 14152676Seschrock (void) get_stats(zhp); 1416789Sahrens } 1417789Sahrens 1418789Sahrens error: 14192676Seschrock nvlist_free(nvl); 14202676Seschrock zcmd_free_nvlists(&zc); 14212676Seschrock if (cl) 14222676Seschrock changelist_free(cl); 1423789Sahrens return (ret); 1424789Sahrens } 1425789Sahrens 1426789Sahrens /* 142711022STom.Erickson@Sun.COM * Given a property, inherit the value from the parent dataset, or if received 142811022STom.Erickson@Sun.COM * is TRUE, revert to the received value, if any. 1429789Sahrens */ 1430789Sahrens int 143111022STom.Erickson@Sun.COM zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1432789Sahrens { 1433789Sahrens zfs_cmd_t zc = { 0 }; 1434789Sahrens int ret; 1435789Sahrens prop_changelist_t *cl; 14362082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 14372082Seschrock char errbuf[1024]; 14382676Seschrock zfs_prop_t prop; 14392082Seschrock 14402082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 14412082Seschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1442789Sahrens 144311022STom.Erickson@Sun.COM zc.zc_cookie = received; 14445094Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 14452676Seschrock /* 14462676Seschrock * For user properties, the amount of work we have to do is very 14472676Seschrock * small, so just do it here. 14482676Seschrock */ 14492676Seschrock if (!zfs_prop_user(propname)) { 14502676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14512676Seschrock "invalid property")); 14522676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 14532676Seschrock } 14542676Seschrock 14552676Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14562676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 14572676Seschrock 14584849Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 14592676Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 14602676Seschrock 14612676Seschrock return (0); 14622676Seschrock } 14632676Seschrock 1464789Sahrens /* 1465789Sahrens * Verify that this property is inheritable. 1466789Sahrens */ 14672082Seschrock if (zfs_prop_readonly(prop)) 14682082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 14692082Seschrock 147011022STom.Erickson@Sun.COM if (!zfs_prop_inheritable(prop) && !received) 14712082Seschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1472789Sahrens 1473789Sahrens /* 1474789Sahrens * Check to see if the value applies to this type 1475789Sahrens */ 14762082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 14772082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1478789Sahrens 14793443Srm160521 /* 14803443Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 14813443Srm160521 */ 14823443Srm160521 propname = zfs_prop_to_name(prop); 1483789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14842676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1485789Sahrens 1486789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1487789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 14882082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14892082Seschrock "dataset is used in a non-global zone")); 14902082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1491789Sahrens } 1492789Sahrens 1493789Sahrens /* 1494789Sahrens * Determine datasets which will be affected by this change, if any. 1495789Sahrens */ 14967366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1497789Sahrens return (-1); 1498789Sahrens 1499789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 15002082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15012082Seschrock "child dataset with inherited mountpoint is used " 15022082Seschrock "in a non-global zone")); 15032082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1504789Sahrens goto error; 1505789Sahrens } 1506789Sahrens 1507789Sahrens if ((ret = changelist_prefix(cl)) != 0) 1508789Sahrens goto error; 1509789Sahrens 15104849Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 15112082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1512789Sahrens } else { 1513789Sahrens 15142169Snd150628 if ((ret = changelist_postfix(cl)) != 0) 1515789Sahrens goto error; 1516789Sahrens 1517789Sahrens /* 1518789Sahrens * Refresh the statistics so the new property is reflected. 1519789Sahrens */ 1520789Sahrens (void) get_stats(zhp); 1521789Sahrens } 1522789Sahrens 1523789Sahrens error: 1524789Sahrens changelist_free(cl); 1525789Sahrens return (ret); 1526789Sahrens } 1527789Sahrens 1528789Sahrens /* 15291356Seschrock * True DSL properties are stored in an nvlist. The following two functions 15301356Seschrock * extract them appropriately. 15311356Seschrock */ 15321356Seschrock static uint64_t 15331356Seschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15341356Seschrock { 15351356Seschrock nvlist_t *nv; 15361356Seschrock uint64_t value; 15371356Seschrock 15382885Sahrens *source = NULL; 15391356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15401356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15415094Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 15425094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15431356Seschrock } else { 15448802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15458802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15461356Seschrock value = zfs_prop_default_numeric(prop); 15471356Seschrock *source = ""; 15481356Seschrock } 15491356Seschrock 15501356Seschrock return (value); 15511356Seschrock } 15521356Seschrock 15531356Seschrock static char * 15541356Seschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15551356Seschrock { 15561356Seschrock nvlist_t *nv; 15571356Seschrock char *value; 15581356Seschrock 15592885Sahrens *source = NULL; 15601356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15611356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15625094Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 15635094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15641356Seschrock } else { 15658802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15668802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15671356Seschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15681356Seschrock value = ""; 15691356Seschrock *source = ""; 15701356Seschrock } 15711356Seschrock 15721356Seschrock return (value); 15731356Seschrock } 15741356Seschrock 157511022STom.Erickson@Sun.COM static boolean_t 157611022STom.Erickson@Sun.COM zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157711022STom.Erickson@Sun.COM { 157811022STom.Erickson@Sun.COM return (zhp->zfs_props == zhp->zfs_recvd_props); 157911022STom.Erickson@Sun.COM } 158011022STom.Erickson@Sun.COM 158111022STom.Erickson@Sun.COM static void 158211022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158311022STom.Erickson@Sun.COM { 158411022STom.Erickson@Sun.COM *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158511022STom.Erickson@Sun.COM zhp->zfs_props = zhp->zfs_recvd_props; 158611022STom.Erickson@Sun.COM } 158711022STom.Erickson@Sun.COM 158811022STom.Erickson@Sun.COM static void 158911022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 159011022STom.Erickson@Sun.COM { 159111022STom.Erickson@Sun.COM zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 159211022STom.Erickson@Sun.COM *cookie = 0; 159311022STom.Erickson@Sun.COM } 159411022STom.Erickson@Sun.COM 15951356Seschrock /* 1596789Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1597789Sahrens * zfs_prop_get_int() are built using this interface. 1598789Sahrens * 1599789Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1600789Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1601789Sahrens * If they differ from the on-disk values, report the current values and mark 1602789Sahrens * the source "temporary". 1603789Sahrens */ 16042082Seschrock static int 16055094Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 16062082Seschrock char **source, uint64_t *val) 1607789Sahrens { 16085147Srm160521 zfs_cmd_t zc = { 0 }; 16095592Stimh nvlist_t *zplprops = NULL; 1610789Sahrens struct mnttab mnt; 16113265Sahrens char *mntopt_on = NULL; 16123265Sahrens char *mntopt_off = NULL; 161311022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1614789Sahrens 1615789Sahrens *source = NULL; 1616789Sahrens 16173265Sahrens switch (prop) { 16183265Sahrens case ZFS_PROP_ATIME: 16193265Sahrens mntopt_on = MNTOPT_ATIME; 16203265Sahrens mntopt_off = MNTOPT_NOATIME; 16213265Sahrens break; 16223265Sahrens 16233265Sahrens case ZFS_PROP_DEVICES: 16243265Sahrens mntopt_on = MNTOPT_DEVICES; 16253265Sahrens mntopt_off = MNTOPT_NODEVICES; 16263265Sahrens break; 16273265Sahrens 16283265Sahrens case ZFS_PROP_EXEC: 16293265Sahrens mntopt_on = MNTOPT_EXEC; 16303265Sahrens mntopt_off = MNTOPT_NOEXEC; 16313265Sahrens break; 16323265Sahrens 16333265Sahrens case ZFS_PROP_READONLY: 16343265Sahrens mntopt_on = MNTOPT_RO; 16353265Sahrens mntopt_off = MNTOPT_RW; 16363265Sahrens break; 16373265Sahrens 16383265Sahrens case ZFS_PROP_SETUID: 16393265Sahrens mntopt_on = MNTOPT_SETUID; 16403265Sahrens mntopt_off = MNTOPT_NOSETUID; 16413265Sahrens break; 16423265Sahrens 16433265Sahrens case ZFS_PROP_XATTR: 16443265Sahrens mntopt_on = MNTOPT_XATTR; 16453265Sahrens mntopt_off = MNTOPT_NOXATTR; 16463265Sahrens break; 16475331Samw 16485331Samw case ZFS_PROP_NBMAND: 16495331Samw mntopt_on = MNTOPT_NBMAND; 16505331Samw mntopt_off = MNTOPT_NONBMAND; 16515331Samw break; 16523265Sahrens } 16533265Sahrens 16542474Seschrock /* 16552474Seschrock * Because looking up the mount options is potentially expensive 16562474Seschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16572474Seschrock * we're looking up a property which requires its presence. 16582474Seschrock */ 16592474Seschrock if (!zhp->zfs_mntcheck && 16603265Sahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 16618228SEric.Taylor@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 16628228SEric.Taylor@Sun.COM struct mnttab entry; 16638228SEric.Taylor@Sun.COM 16648228SEric.Taylor@Sun.COM if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 16658228SEric.Taylor@Sun.COM zhp->zfs_mntopts = zfs_strdup(hdl, 16663265Sahrens entry.mnt_mntopts); 16673265Sahrens if (zhp->zfs_mntopts == NULL) 16683265Sahrens return (-1); 16693265Sahrens } 16702474Seschrock 16712474Seschrock zhp->zfs_mntcheck = B_TRUE; 16722474Seschrock } 16732474Seschrock 1674789Sahrens if (zhp->zfs_mntopts == NULL) 1675789Sahrens mnt.mnt_mntopts = ""; 1676789Sahrens else 1677789Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1678789Sahrens 1679789Sahrens switch (prop) { 1680789Sahrens case ZFS_PROP_ATIME: 16813265Sahrens case ZFS_PROP_DEVICES: 16823265Sahrens case ZFS_PROP_EXEC: 16833265Sahrens case ZFS_PROP_READONLY: 16843265Sahrens case ZFS_PROP_SETUID: 16853265Sahrens case ZFS_PROP_XATTR: 16865331Samw case ZFS_PROP_NBMAND: 16872082Seschrock *val = getprop_uint64(zhp, prop, source); 16882082Seschrock 168911022STom.Erickson@Sun.COM if (received) 169011022STom.Erickson@Sun.COM break; 169111022STom.Erickson@Sun.COM 16923265Sahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 16932082Seschrock *val = B_TRUE; 1694789Sahrens if (src) 16955094Slling *src = ZPROP_SRC_TEMPORARY; 16963265Sahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 16972082Seschrock *val = B_FALSE; 1698789Sahrens if (src) 16995094Slling *src = ZPROP_SRC_TEMPORARY; 1700789Sahrens } 17012082Seschrock break; 1702789Sahrens 17033265Sahrens case ZFS_PROP_CANMOUNT: 1704789Sahrens case ZFS_PROP_QUOTA: 17055378Sck153898 case ZFS_PROP_REFQUOTA: 1706789Sahrens case ZFS_PROP_RESERVATION: 17075378Sck153898 case ZFS_PROP_REFRESERVATION: 17082885Sahrens *val = getprop_uint64(zhp, prop, source); 170911022STom.Erickson@Sun.COM 171011022STom.Erickson@Sun.COM if (*source == NULL) { 171111022STom.Erickson@Sun.COM /* not default, must be local */ 1712789Sahrens *source = zhp->zfs_name; 171311022STom.Erickson@Sun.COM } 17142082Seschrock break; 1715789Sahrens 1716789Sahrens case ZFS_PROP_MOUNTED: 17172082Seschrock *val = (zhp->zfs_mntopts != NULL); 17182082Seschrock break; 1719789Sahrens 17203377Seschrock case ZFS_PROP_NUMCLONES: 17213377Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 17223377Seschrock break; 17233377Seschrock 17245147Srm160521 case ZFS_PROP_VERSION: 17255498Stimh case ZFS_PROP_NORMALIZE: 17265498Stimh case ZFS_PROP_UTF8ONLY: 17275498Stimh case ZFS_PROP_CASE: 17285498Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 17295498Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17305473Srm160521 return (-1); 17315147Srm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 17325498Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 17335498Stimh zcmd_free_nvlists(&zc); 173410204SMatthew.Ahrens@Sun.COM return (-1); 17355147Srm160521 } 17365498Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 17375498Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 17385498Stimh val) != 0) { 17395498Stimh zcmd_free_nvlists(&zc); 174010204SMatthew.Ahrens@Sun.COM return (-1); 17415498Stimh } 17425592Stimh if (zplprops) 17435592Stimh nvlist_free(zplprops); 17445498Stimh zcmd_free_nvlists(&zc); 17455147Srm160521 break; 17465147Srm160521 1747789Sahrens default: 17484577Sahrens switch (zfs_prop_get_type(prop)) { 17494787Sahrens case PROP_TYPE_NUMBER: 17504787Sahrens case PROP_TYPE_INDEX: 17514577Sahrens *val = getprop_uint64(zhp, prop, source); 17527390SMatthew.Ahrens@Sun.COM /* 17539396SMatthew.Ahrens@Sun.COM * If we tried to use a default value for a 17547390SMatthew.Ahrens@Sun.COM * readonly property, it means that it was not 175511080STom.Erickson@Sun.COM * present. 17567390SMatthew.Ahrens@Sun.COM */ 17577390SMatthew.Ahrens@Sun.COM if (zfs_prop_readonly(prop) && 175811080STom.Erickson@Sun.COM *source != NULL && (*source)[0] == '\0') { 175911080STom.Erickson@Sun.COM *source = NULL; 17607390SMatthew.Ahrens@Sun.COM } 17614577Sahrens break; 17624577Sahrens 17634787Sahrens case PROP_TYPE_STRING: 17644577Sahrens default: 17654577Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 17664577Sahrens "cannot get non-numeric property")); 17674577Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 17684577Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 17694577Sahrens } 1770789Sahrens } 1771789Sahrens 1772789Sahrens return (0); 1773789Sahrens } 1774789Sahrens 1775789Sahrens /* 1776789Sahrens * Calculate the source type, given the raw source string. 1777789Sahrens */ 1778789Sahrens static void 17795094Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1780789Sahrens char *statbuf, size_t statlen) 1781789Sahrens { 17825094Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1783789Sahrens return; 1784789Sahrens 1785789Sahrens if (source == NULL) { 17865094Slling *srctype = ZPROP_SRC_NONE; 1787789Sahrens } else if (source[0] == '\0') { 17885094Slling *srctype = ZPROP_SRC_DEFAULT; 178911022STom.Erickson@Sun.COM } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 179011022STom.Erickson@Sun.COM *srctype = ZPROP_SRC_RECEIVED; 1791789Sahrens } else { 1792789Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 17935094Slling *srctype = ZPROP_SRC_LOCAL; 1794789Sahrens } else { 1795789Sahrens (void) strlcpy(statbuf, source, statlen); 17965094Slling *srctype = ZPROP_SRC_INHERITED; 1797789Sahrens } 1798789Sahrens } 1799789Sahrens 1800789Sahrens } 1801789Sahrens 180211022STom.Erickson@Sun.COM int 180311022STom.Erickson@Sun.COM zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180411022STom.Erickson@Sun.COM size_t proplen, boolean_t literal) 180511022STom.Erickson@Sun.COM { 180611022STom.Erickson@Sun.COM zfs_prop_t prop; 180711022STom.Erickson@Sun.COM int err = 0; 180811022STom.Erickson@Sun.COM 180911022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 181011022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 181111022STom.Erickson@Sun.COM return (-1); 181211022STom.Erickson@Sun.COM 181311022STom.Erickson@Sun.COM prop = zfs_name_to_prop(propname); 181411022STom.Erickson@Sun.COM 181511022STom.Erickson@Sun.COM if (prop != ZPROP_INVAL) { 181611022STom.Erickson@Sun.COM uint64_t cookie; 181711022STom.Erickson@Sun.COM if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181811022STom.Erickson@Sun.COM return (-1); 181911022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zhp, &cookie); 182011022STom.Erickson@Sun.COM err = zfs_prop_get(zhp, prop, propbuf, proplen, 182111022STom.Erickson@Sun.COM NULL, NULL, 0, literal); 182211022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zhp, &cookie); 182311022STom.Erickson@Sun.COM } else if (zfs_prop_userquota(propname)) { 182411022STom.Erickson@Sun.COM return (-1); 182511022STom.Erickson@Sun.COM } else { 182611022STom.Erickson@Sun.COM nvlist_t *propval; 182711022STom.Erickson@Sun.COM char *recvdval; 182811022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 182911022STom.Erickson@Sun.COM propname, &propval) != 0) 183011022STom.Erickson@Sun.COM return (-1); 183111022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, ZPROP_VALUE, 183211022STom.Erickson@Sun.COM &recvdval) == 0); 183311022STom.Erickson@Sun.COM (void) strlcpy(propbuf, recvdval, proplen); 183411022STom.Erickson@Sun.COM } 183511022STom.Erickson@Sun.COM 183611022STom.Erickson@Sun.COM return (err == 0 ? 0 : -1); 183711022STom.Erickson@Sun.COM } 183811022STom.Erickson@Sun.COM 1839789Sahrens /* 1840789Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1841789Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1842789Sahrens * human-readable form. 1843789Sahrens * 1844789Sahrens * Returns 0 on success, or -1 on error. 1845789Sahrens */ 1846789Sahrens int 1847789Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 18485094Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1849789Sahrens { 1850789Sahrens char *source = NULL; 1851789Sahrens uint64_t val; 1852789Sahrens char *str; 18532676Seschrock const char *strval; 185411022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1855789Sahrens 1856789Sahrens /* 1857789Sahrens * Check to see if this property applies to our object 1858789Sahrens */ 1859789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1860789Sahrens return (-1); 1861789Sahrens 186211022STom.Erickson@Sun.COM if (received && zfs_prop_readonly(prop)) 186311022STom.Erickson@Sun.COM return (-1); 186411022STom.Erickson@Sun.COM 1865789Sahrens if (src) 18665094Slling *src = ZPROP_SRC_NONE; 1867789Sahrens 1868789Sahrens switch (prop) { 1869789Sahrens case ZFS_PROP_CREATION: 1870789Sahrens /* 1871789Sahrens * 'creation' is a time_t stored in the statistics. We convert 1872789Sahrens * this into a string unless 'literal' is specified. 1873789Sahrens */ 1874789Sahrens { 18752885Sahrens val = getprop_uint64(zhp, prop, &source); 18762885Sahrens time_t time = (time_t)val; 1877789Sahrens struct tm t; 1878789Sahrens 1879789Sahrens if (literal || 1880789Sahrens localtime_r(&time, &t) == NULL || 1881789Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1882789Sahrens &t) == 0) 18832885Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1884789Sahrens } 1885789Sahrens break; 1886789Sahrens 1887789Sahrens case ZFS_PROP_MOUNTPOINT: 1888789Sahrens /* 1889789Sahrens * Getting the precise mountpoint can be tricky. 1890789Sahrens * 1891789Sahrens * - for 'none' or 'legacy', return those values. 1892789Sahrens * - for inherited mountpoints, we want to take everything 1893789Sahrens * after our ancestor and append it to the inherited value. 1894789Sahrens * 1895789Sahrens * If the pool has an alternate root, we want to prepend that 1896789Sahrens * root to any values we return. 1897789Sahrens */ 18986865Srm160521 18991356Seschrock str = getprop_string(zhp, prop, &source); 19001356Seschrock 19016612Sgw25295 if (str[0] == '/') { 19026865Srm160521 char buf[MAXPATHLEN]; 19036865Srm160521 char *root = buf; 19041356Seschrock const char *relpath = zhp->zfs_name + strlen(source); 1905789Sahrens 1906789Sahrens if (relpath[0] == '/') 1907789Sahrens relpath++; 19086612Sgw25295 19096865Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 19106865Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 19116865Srm160521 (strcmp(root, "-") == 0)) 19126865Srm160521 root[0] = '\0'; 19136612Sgw25295 /* 19146612Sgw25295 * Special case an alternate root of '/'. This will 19156612Sgw25295 * avoid having multiple leading slashes in the 19166612Sgw25295 * mountpoint path. 19176612Sgw25295 */ 19186612Sgw25295 if (strcmp(root, "/") == 0) 19196612Sgw25295 root++; 19206612Sgw25295 19216612Sgw25295 /* 19226612Sgw25295 * If the mountpoint is '/' then skip over this 19236612Sgw25295 * if we are obtaining either an alternate root or 19246612Sgw25295 * an inherited mountpoint. 19256612Sgw25295 */ 19266612Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 19276612Sgw25295 relpath[0] != '\0')) 19281356Seschrock str++; 1929789Sahrens 1930789Sahrens if (relpath[0] == '\0') 1931789Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19321356Seschrock root, str); 1933789Sahrens else 1934789Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19351356Seschrock root, str, relpath[0] == '@' ? "" : "/", 1936789Sahrens relpath); 1937789Sahrens } else { 1938789Sahrens /* 'legacy' or 'none' */ 19391356Seschrock (void) strlcpy(propbuf, str, proplen); 1940789Sahrens } 1941789Sahrens 1942789Sahrens break; 1943789Sahrens 1944789Sahrens case ZFS_PROP_ORIGIN: 19452885Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1946789Sahrens proplen); 1947789Sahrens /* 1948789Sahrens * If there is no parent at all, return failure to indicate that 1949789Sahrens * it doesn't apply to this dataset. 1950789Sahrens */ 1951789Sahrens if (propbuf[0] == '\0') 1952789Sahrens return (-1); 1953789Sahrens break; 1954789Sahrens 1955789Sahrens case ZFS_PROP_QUOTA: 19565378Sck153898 case ZFS_PROP_REFQUOTA: 1957789Sahrens case ZFS_PROP_RESERVATION: 19585378Sck153898 case ZFS_PROP_REFRESERVATION: 19595378Sck153898 19602082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19612082Seschrock return (-1); 1962789Sahrens 1963789Sahrens /* 1964789Sahrens * If quota or reservation is 0, we translate this into 'none' 1965789Sahrens * (unless literal is set), and indicate that it's the default 1966789Sahrens * value. Otherwise, we print the number nicely and indicate 1967789Sahrens * that its set locally. 1968789Sahrens */ 1969789Sahrens if (val == 0) { 1970789Sahrens if (literal) 1971789Sahrens (void) strlcpy(propbuf, "0", proplen); 1972789Sahrens else 1973789Sahrens (void) strlcpy(propbuf, "none", proplen); 1974789Sahrens } else { 1975789Sahrens if (literal) 19762856Snd150628 (void) snprintf(propbuf, proplen, "%llu", 19773912Slling (u_longlong_t)val); 1978789Sahrens else 1979789Sahrens zfs_nicenum(val, propbuf, proplen); 1980789Sahrens } 1981789Sahrens break; 1982789Sahrens 1983789Sahrens case ZFS_PROP_COMPRESSRATIO: 19842082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19852082Seschrock return (-1); 198610922SJeff.Bonwick@Sun.COM (void) snprintf(propbuf, proplen, "%llu.%02llux", 198710922SJeff.Bonwick@Sun.COM (u_longlong_t)(val / 100), 198810922SJeff.Bonwick@Sun.COM (u_longlong_t)(val % 100)); 1989789Sahrens break; 1990789Sahrens 1991789Sahrens case ZFS_PROP_TYPE: 1992789Sahrens switch (zhp->zfs_type) { 1993789Sahrens case ZFS_TYPE_FILESYSTEM: 1994789Sahrens str = "filesystem"; 1995789Sahrens break; 1996789Sahrens case ZFS_TYPE_VOLUME: 1997789Sahrens str = "volume"; 1998789Sahrens break; 1999789Sahrens case ZFS_TYPE_SNAPSHOT: 2000789Sahrens str = "snapshot"; 2001789Sahrens break; 2002789Sahrens default: 20032082Seschrock abort(); 2004789Sahrens } 2005789Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2006789Sahrens break; 2007789Sahrens 2008789Sahrens case ZFS_PROP_MOUNTED: 2009789Sahrens /* 2010789Sahrens * The 'mounted' property is a pseudo-property that described 2011789Sahrens * whether the filesystem is currently mounted. Even though 2012789Sahrens * it's a boolean value, the typical values of "on" and "off" 2013789Sahrens * don't make sense, so we translate to "yes" and "no". 2014789Sahrens */ 20152082Seschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 20162082Seschrock src, &source, &val) != 0) 20172082Seschrock return (-1); 20182082Seschrock if (val) 2019789Sahrens (void) strlcpy(propbuf, "yes", proplen); 2020789Sahrens else 2021789Sahrens (void) strlcpy(propbuf, "no", proplen); 2022789Sahrens break; 2023789Sahrens 2024789Sahrens case ZFS_PROP_NAME: 2025789Sahrens /* 2026789Sahrens * The 'name' property is a pseudo-property derived from the 2027789Sahrens * dataset name. It is presented as a real property to simplify 2028789Sahrens * consumers. 2029789Sahrens */ 2030789Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2031789Sahrens break; 2032789Sahrens 203310972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 203410972SRic.Aleshire@Sun.COM { 203510972SRic.Aleshire@Sun.COM m_label_t *new_sl = NULL; 203610972SRic.Aleshire@Sun.COM char *ascii = NULL; /* human readable label */ 203710972SRic.Aleshire@Sun.COM 203810972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, 203910972SRic.Aleshire@Sun.COM getprop_string(zhp, prop, &source), proplen); 204010972SRic.Aleshire@Sun.COM 204110972SRic.Aleshire@Sun.COM if (literal || (strcasecmp(propbuf, 204210972SRic.Aleshire@Sun.COM ZFS_MLSLABEL_DEFAULT) == 0)) 204310972SRic.Aleshire@Sun.COM break; 204410972SRic.Aleshire@Sun.COM 204510972SRic.Aleshire@Sun.COM /* 204610972SRic.Aleshire@Sun.COM * Try to translate the internal hex string to 204710972SRic.Aleshire@Sun.COM * human-readable output. If there are any 204810972SRic.Aleshire@Sun.COM * problems just use the hex string. 204910972SRic.Aleshire@Sun.COM */ 205010972SRic.Aleshire@Sun.COM 205110972SRic.Aleshire@Sun.COM if (str_to_label(propbuf, &new_sl, MAC_LABEL, 205210972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1) { 205310972SRic.Aleshire@Sun.COM m_label_free(new_sl); 205410972SRic.Aleshire@Sun.COM break; 205510972SRic.Aleshire@Sun.COM } 205610972SRic.Aleshire@Sun.COM 205710972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &ascii, M_LABEL, 205810972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 205910972SRic.Aleshire@Sun.COM if (ascii) 206010972SRic.Aleshire@Sun.COM free(ascii); 206110972SRic.Aleshire@Sun.COM m_label_free(new_sl); 206210972SRic.Aleshire@Sun.COM break; 206310972SRic.Aleshire@Sun.COM } 206410972SRic.Aleshire@Sun.COM m_label_free(new_sl); 206510972SRic.Aleshire@Sun.COM 206610972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, ascii, proplen); 206710972SRic.Aleshire@Sun.COM free(ascii); 206810972SRic.Aleshire@Sun.COM } 206910972SRic.Aleshire@Sun.COM break; 207010972SRic.Aleshire@Sun.COM 2071789Sahrens default: 20724577Sahrens switch (zfs_prop_get_type(prop)) { 20734787Sahrens case PROP_TYPE_NUMBER: 20744577Sahrens if (get_numeric_property(zhp, prop, src, 20754577Sahrens &source, &val) != 0) 20764577Sahrens return (-1); 20774577Sahrens if (literal) 20784577Sahrens (void) snprintf(propbuf, proplen, "%llu", 20794577Sahrens (u_longlong_t)val); 20804577Sahrens else 20814577Sahrens zfs_nicenum(val, propbuf, proplen); 20824577Sahrens break; 20834577Sahrens 20844787Sahrens case PROP_TYPE_STRING: 20854577Sahrens (void) strlcpy(propbuf, 20864577Sahrens getprop_string(zhp, prop, &source), proplen); 20874577Sahrens break; 20884577Sahrens 20894787Sahrens case PROP_TYPE_INDEX: 20904861Sahrens if (get_numeric_property(zhp, prop, src, 20914861Sahrens &source, &val) != 0) 20924861Sahrens return (-1); 20934861Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 20944577Sahrens return (-1); 20954577Sahrens (void) strlcpy(propbuf, strval, proplen); 20964577Sahrens break; 20974577Sahrens 20984577Sahrens default: 20994577Sahrens abort(); 21004577Sahrens } 2101789Sahrens } 2102789Sahrens 2103789Sahrens get_source(zhp, src, source, statbuf, statlen); 2104789Sahrens 2105789Sahrens return (0); 2106789Sahrens } 2107789Sahrens 2108789Sahrens /* 2109789Sahrens * Utility function to get the given numeric property. Does no validation that 2110789Sahrens * the given property is the appropriate type; should only be used with 2111789Sahrens * hard-coded property types. 2112789Sahrens */ 2113789Sahrens uint64_t 2114789Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2115789Sahrens { 2116789Sahrens char *source; 21172082Seschrock uint64_t val; 21182082Seschrock 21195367Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 21202082Seschrock 21212082Seschrock return (val); 2122789Sahrens } 2123789Sahrens 21245713Srm160521 int 21255713Srm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21265713Srm160521 { 21275713Srm160521 char buf[64]; 21285713Srm160521 21299396SMatthew.Ahrens@Sun.COM (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21305713Srm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21315713Srm160521 } 21325713Srm160521 2133789Sahrens /* 2134789Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2135789Sahrens */ 2136789Sahrens int 2137789Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 21385094Slling zprop_source_t *src, char *statbuf, size_t statlen) 2139789Sahrens { 2140789Sahrens char *source; 2141789Sahrens 2142789Sahrens /* 2143789Sahrens * Check to see if this property applies to our object 2144789Sahrens */ 21454849Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 21463237Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 21472082Seschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 21482082Seschrock zfs_prop_to_name(prop))); 21494849Sahrens } 2150789Sahrens 2151789Sahrens if (src) 21525094Slling *src = ZPROP_SRC_NONE; 2153789Sahrens 21542082Seschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 21552082Seschrock return (-1); 2156789Sahrens 2157789Sahrens get_source(zhp, src, source, statbuf, statlen); 2158789Sahrens 2159789Sahrens return (0); 2160789Sahrens } 2161789Sahrens 21629396SMatthew.Ahrens@Sun.COM static int 21639396SMatthew.Ahrens@Sun.COM idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 21649396SMatthew.Ahrens@Sun.COM char **domainp, idmap_rid_t *ridp) 21659396SMatthew.Ahrens@Sun.COM { 21669396SMatthew.Ahrens@Sun.COM idmap_handle_t *idmap_hdl = NULL; 21679396SMatthew.Ahrens@Sun.COM idmap_get_handle_t *get_hdl = NULL; 21689396SMatthew.Ahrens@Sun.COM idmap_stat status; 21699396SMatthew.Ahrens@Sun.COM int err = EINVAL; 21709396SMatthew.Ahrens@Sun.COM 21719396SMatthew.Ahrens@Sun.COM if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 21729396SMatthew.Ahrens@Sun.COM goto out; 21739396SMatthew.Ahrens@Sun.COM if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 21749396SMatthew.Ahrens@Sun.COM goto out; 21759396SMatthew.Ahrens@Sun.COM 21769396SMatthew.Ahrens@Sun.COM if (isuser) { 21779396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbyuid(get_hdl, id, 21789396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21799396SMatthew.Ahrens@Sun.COM } else { 21809396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbygid(get_hdl, id, 21819396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21829396SMatthew.Ahrens@Sun.COM } 21839396SMatthew.Ahrens@Sun.COM if (err == IDMAP_SUCCESS && 21849396SMatthew.Ahrens@Sun.COM idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 21859396SMatthew.Ahrens@Sun.COM status == IDMAP_SUCCESS) 21869396SMatthew.Ahrens@Sun.COM err = 0; 21879396SMatthew.Ahrens@Sun.COM else 21889396SMatthew.Ahrens@Sun.COM err = EINVAL; 21899396SMatthew.Ahrens@Sun.COM out: 21909396SMatthew.Ahrens@Sun.COM if (get_hdl) 21919396SMatthew.Ahrens@Sun.COM idmap_get_destroy(get_hdl); 21929396SMatthew.Ahrens@Sun.COM if (idmap_hdl) 21939396SMatthew.Ahrens@Sun.COM (void) idmap_fini(idmap_hdl); 21949396SMatthew.Ahrens@Sun.COM return (err); 21959396SMatthew.Ahrens@Sun.COM } 21969396SMatthew.Ahrens@Sun.COM 21979396SMatthew.Ahrens@Sun.COM /* 21989396SMatthew.Ahrens@Sun.COM * convert the propname into parameters needed by kernel 21999396SMatthew.Ahrens@Sun.COM * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 22009396SMatthew.Ahrens@Sun.COM * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 22019396SMatthew.Ahrens@Sun.COM */ 22029396SMatthew.Ahrens@Sun.COM static int 22039396SMatthew.Ahrens@Sun.COM userquota_propname_decode(const char *propname, boolean_t zoned, 22049396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 22059396SMatthew.Ahrens@Sun.COM { 22069396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t type; 22079396SMatthew.Ahrens@Sun.COM char *cp, *end; 220810160SMatthew.Ahrens@Sun.COM char *numericsid = NULL; 22099396SMatthew.Ahrens@Sun.COM boolean_t isuser; 22109396SMatthew.Ahrens@Sun.COM 22119396SMatthew.Ahrens@Sun.COM domain[0] = '\0'; 22129396SMatthew.Ahrens@Sun.COM 22139396SMatthew.Ahrens@Sun.COM /* Figure out the property type ({user|group}{quota|space}) */ 22149396SMatthew.Ahrens@Sun.COM for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 22159396SMatthew.Ahrens@Sun.COM if (strncmp(propname, zfs_userquota_prop_prefixes[type], 22169396SMatthew.Ahrens@Sun.COM strlen(zfs_userquota_prop_prefixes[type])) == 0) 22179396SMatthew.Ahrens@Sun.COM break; 22189396SMatthew.Ahrens@Sun.COM } 22199396SMatthew.Ahrens@Sun.COM if (type == ZFS_NUM_USERQUOTA_PROPS) 22209396SMatthew.Ahrens@Sun.COM return (EINVAL); 22219396SMatthew.Ahrens@Sun.COM *typep = type; 22229396SMatthew.Ahrens@Sun.COM 22239396SMatthew.Ahrens@Sun.COM isuser = (type == ZFS_PROP_USERQUOTA || 22249396SMatthew.Ahrens@Sun.COM type == ZFS_PROP_USERUSED); 22259396SMatthew.Ahrens@Sun.COM 22269396SMatthew.Ahrens@Sun.COM cp = strchr(propname, '@') + 1; 22279396SMatthew.Ahrens@Sun.COM 22289396SMatthew.Ahrens@Sun.COM if (strchr(cp, '@')) { 22299396SMatthew.Ahrens@Sun.COM /* 22309396SMatthew.Ahrens@Sun.COM * It's a SID name (eg "user@domain") that needs to be 223110160SMatthew.Ahrens@Sun.COM * turned into S-1-domainID-RID. 22329396SMatthew.Ahrens@Sun.COM */ 223310160SMatthew.Ahrens@Sun.COM directory_error_t e; 22349396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22359396SMatthew.Ahrens@Sun.COM return (ENOENT); 223610160SMatthew.Ahrens@Sun.COM if (isuser) { 223710160SMatthew.Ahrens@Sun.COM e = directory_sid_from_user_name(NULL, 223810160SMatthew.Ahrens@Sun.COM cp, &numericsid); 223910160SMatthew.Ahrens@Sun.COM } else { 224010160SMatthew.Ahrens@Sun.COM e = directory_sid_from_group_name(NULL, 224110160SMatthew.Ahrens@Sun.COM cp, &numericsid); 224210160SMatthew.Ahrens@Sun.COM } 224310160SMatthew.Ahrens@Sun.COM if (e != NULL) { 224410160SMatthew.Ahrens@Sun.COM directory_error_free(e); 22459396SMatthew.Ahrens@Sun.COM return (ENOENT); 224610160SMatthew.Ahrens@Sun.COM } 224710160SMatthew.Ahrens@Sun.COM if (numericsid == NULL) 224810160SMatthew.Ahrens@Sun.COM return (ENOENT); 224910160SMatthew.Ahrens@Sun.COM cp = numericsid; 225010160SMatthew.Ahrens@Sun.COM /* will be further decoded below */ 225110160SMatthew.Ahrens@Sun.COM } 225210160SMatthew.Ahrens@Sun.COM 225310160SMatthew.Ahrens@Sun.COM if (strncmp(cp, "S-1-", 4) == 0) { 22549396SMatthew.Ahrens@Sun.COM /* It's a numeric SID (eg "S-1-234-567-89") */ 225510160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, cp, domainlen); 22569396SMatthew.Ahrens@Sun.COM cp = strrchr(domain, '-'); 22579396SMatthew.Ahrens@Sun.COM *cp = '\0'; 22589396SMatthew.Ahrens@Sun.COM cp++; 22599396SMatthew.Ahrens@Sun.COM 22609396SMatthew.Ahrens@Sun.COM errno = 0; 22619396SMatthew.Ahrens@Sun.COM *ridp = strtoull(cp, &end, 10); 226210160SMatthew.Ahrens@Sun.COM if (numericsid) { 226310160SMatthew.Ahrens@Sun.COM free(numericsid); 226410160SMatthew.Ahrens@Sun.COM numericsid = NULL; 226510160SMatthew.Ahrens@Sun.COM } 22669688SMatthew.Ahrens@Sun.COM if (errno != 0 || *end != '\0') 22679396SMatthew.Ahrens@Sun.COM return (EINVAL); 22689396SMatthew.Ahrens@Sun.COM } else if (!isdigit(*cp)) { 22699396SMatthew.Ahrens@Sun.COM /* 22709396SMatthew.Ahrens@Sun.COM * It's a user/group name (eg "user") that needs to be 22719396SMatthew.Ahrens@Sun.COM * turned into a uid/gid 22729396SMatthew.Ahrens@Sun.COM */ 22739396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22749396SMatthew.Ahrens@Sun.COM return (ENOENT); 22759396SMatthew.Ahrens@Sun.COM if (isuser) { 22769396SMatthew.Ahrens@Sun.COM struct passwd *pw; 22779396SMatthew.Ahrens@Sun.COM pw = getpwnam(cp); 22789396SMatthew.Ahrens@Sun.COM if (pw == NULL) 22799396SMatthew.Ahrens@Sun.COM return (ENOENT); 22809396SMatthew.Ahrens@Sun.COM *ridp = pw->pw_uid; 22819396SMatthew.Ahrens@Sun.COM } else { 22829396SMatthew.Ahrens@Sun.COM struct group *gr; 22839396SMatthew.Ahrens@Sun.COM gr = getgrnam(cp); 22849396SMatthew.Ahrens@Sun.COM if (gr == NULL) 22859396SMatthew.Ahrens@Sun.COM return (ENOENT); 22869396SMatthew.Ahrens@Sun.COM *ridp = gr->gr_gid; 22879396SMatthew.Ahrens@Sun.COM } 22889396SMatthew.Ahrens@Sun.COM } else { 22899396SMatthew.Ahrens@Sun.COM /* It's a user/group ID (eg "12345"). */ 22909396SMatthew.Ahrens@Sun.COM uid_t id = strtoul(cp, &end, 10); 22919396SMatthew.Ahrens@Sun.COM idmap_rid_t rid; 22929396SMatthew.Ahrens@Sun.COM char *mapdomain; 22939396SMatthew.Ahrens@Sun.COM 22949396SMatthew.Ahrens@Sun.COM if (*end != '\0') 22959396SMatthew.Ahrens@Sun.COM return (EINVAL); 22969396SMatthew.Ahrens@Sun.COM if (id > MAXUID) { 22979396SMatthew.Ahrens@Sun.COM /* It's an ephemeral ID. */ 22989396SMatthew.Ahrens@Sun.COM if (idmap_id_to_numeric_domain_rid(id, isuser, 22999396SMatthew.Ahrens@Sun.COM &mapdomain, &rid) != 0) 23009396SMatthew.Ahrens@Sun.COM return (ENOENT); 230110160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, mapdomain, domainlen); 23029396SMatthew.Ahrens@Sun.COM *ridp = rid; 23039396SMatthew.Ahrens@Sun.COM } else { 23049396SMatthew.Ahrens@Sun.COM *ridp = id; 23059396SMatthew.Ahrens@Sun.COM } 23069396SMatthew.Ahrens@Sun.COM } 23079396SMatthew.Ahrens@Sun.COM 230810160SMatthew.Ahrens@Sun.COM ASSERT3P(numericsid, ==, NULL); 23099396SMatthew.Ahrens@Sun.COM return (0); 23109396SMatthew.Ahrens@Sun.COM } 23119396SMatthew.Ahrens@Sun.COM 23129469SLin.Ling@Sun.COM static int 23139469SLin.Ling@Sun.COM zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 23149469SLin.Ling@Sun.COM uint64_t *propvalue, zfs_userquota_prop_t *typep) 23159396SMatthew.Ahrens@Sun.COM { 23169396SMatthew.Ahrens@Sun.COM int err; 23179396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 23189396SMatthew.Ahrens@Sun.COM 23199396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 23209396SMatthew.Ahrens@Sun.COM 23219396SMatthew.Ahrens@Sun.COM err = userquota_propname_decode(propname, 23229396SMatthew.Ahrens@Sun.COM zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 23239469SLin.Ling@Sun.COM typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 23249469SLin.Ling@Sun.COM zc.zc_objset_type = *typep; 23259396SMatthew.Ahrens@Sun.COM if (err) 23269396SMatthew.Ahrens@Sun.COM return (err); 23279396SMatthew.Ahrens@Sun.COM 23289396SMatthew.Ahrens@Sun.COM err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 23299396SMatthew.Ahrens@Sun.COM if (err) 23309396SMatthew.Ahrens@Sun.COM return (err); 23319396SMatthew.Ahrens@Sun.COM 23329469SLin.Ling@Sun.COM *propvalue = zc.zc_cookie; 23339469SLin.Ling@Sun.COM return (0); 23349469SLin.Ling@Sun.COM } 23359469SLin.Ling@Sun.COM 23369469SLin.Ling@Sun.COM int 23379469SLin.Ling@Sun.COM zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 23389469SLin.Ling@Sun.COM uint64_t *propvalue) 23399469SLin.Ling@Sun.COM { 23409469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23419469SLin.Ling@Sun.COM 23429469SLin.Ling@Sun.COM return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 23439469SLin.Ling@Sun.COM &type)); 23449469SLin.Ling@Sun.COM } 23459469SLin.Ling@Sun.COM 23469469SLin.Ling@Sun.COM int 23479469SLin.Ling@Sun.COM zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 23489469SLin.Ling@Sun.COM char *propbuf, int proplen, boolean_t literal) 23499469SLin.Ling@Sun.COM { 23509469SLin.Ling@Sun.COM int err; 23519469SLin.Ling@Sun.COM uint64_t propvalue; 23529469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23539469SLin.Ling@Sun.COM 23549469SLin.Ling@Sun.COM err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 23559469SLin.Ling@Sun.COM &type); 23569469SLin.Ling@Sun.COM 23579469SLin.Ling@Sun.COM if (err) 23589469SLin.Ling@Sun.COM return (err); 23599469SLin.Ling@Sun.COM 23609396SMatthew.Ahrens@Sun.COM if (literal) { 23619469SLin.Ling@Sun.COM (void) snprintf(propbuf, proplen, "%llu", propvalue); 23629469SLin.Ling@Sun.COM } else if (propvalue == 0 && 23639396SMatthew.Ahrens@Sun.COM (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 23649396SMatthew.Ahrens@Sun.COM (void) strlcpy(propbuf, "none", proplen); 23659396SMatthew.Ahrens@Sun.COM } else { 23669469SLin.Ling@Sun.COM zfs_nicenum(propvalue, propbuf, proplen); 23679396SMatthew.Ahrens@Sun.COM } 23689396SMatthew.Ahrens@Sun.COM return (0); 23699396SMatthew.Ahrens@Sun.COM } 23709396SMatthew.Ahrens@Sun.COM 2371789Sahrens /* 2372789Sahrens * Returns the name of the given zfs handle. 2373789Sahrens */ 2374789Sahrens const char * 2375789Sahrens zfs_get_name(const zfs_handle_t *zhp) 2376789Sahrens { 2377789Sahrens return (zhp->zfs_name); 2378789Sahrens } 2379789Sahrens 2380789Sahrens /* 2381789Sahrens * Returns the type of the given zfs handle. 2382789Sahrens */ 2383789Sahrens zfs_type_t 2384789Sahrens zfs_get_type(const zfs_handle_t *zhp) 2385789Sahrens { 2386789Sahrens return (zhp->zfs_type); 2387789Sahrens } 2388789Sahrens 23898228SEric.Taylor@Sun.COM static int 23908228SEric.Taylor@Sun.COM zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 23918228SEric.Taylor@Sun.COM { 23928228SEric.Taylor@Sun.COM int rc; 23938228SEric.Taylor@Sun.COM uint64_t orig_cookie; 23948228SEric.Taylor@Sun.COM 23958228SEric.Taylor@Sun.COM orig_cookie = zc->zc_cookie; 23968228SEric.Taylor@Sun.COM top: 23978228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 23988228SEric.Taylor@Sun.COM rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 23998228SEric.Taylor@Sun.COM 24008228SEric.Taylor@Sun.COM if (rc == -1) { 24018228SEric.Taylor@Sun.COM switch (errno) { 24028228SEric.Taylor@Sun.COM case ENOMEM: 24038228SEric.Taylor@Sun.COM /* expand nvlist memory and try again */ 24048228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 24058228SEric.Taylor@Sun.COM zcmd_free_nvlists(zc); 24068228SEric.Taylor@Sun.COM return (-1); 24078228SEric.Taylor@Sun.COM } 24088228SEric.Taylor@Sun.COM zc->zc_cookie = orig_cookie; 24098228SEric.Taylor@Sun.COM goto top; 24108228SEric.Taylor@Sun.COM /* 24118228SEric.Taylor@Sun.COM * An errno value of ESRCH indicates normal completion. 24128228SEric.Taylor@Sun.COM * If ENOENT is returned, then the underlying dataset 24138228SEric.Taylor@Sun.COM * has been removed since we obtained the handle. 24148228SEric.Taylor@Sun.COM */ 24158228SEric.Taylor@Sun.COM case ESRCH: 24168228SEric.Taylor@Sun.COM case ENOENT: 24178228SEric.Taylor@Sun.COM rc = 1; 24188228SEric.Taylor@Sun.COM break; 24198228SEric.Taylor@Sun.COM default: 24208228SEric.Taylor@Sun.COM rc = zfs_standard_error(zhp->zfs_hdl, errno, 24218228SEric.Taylor@Sun.COM dgettext(TEXT_DOMAIN, 24228228SEric.Taylor@Sun.COM "cannot iterate filesystems")); 24238228SEric.Taylor@Sun.COM break; 24248228SEric.Taylor@Sun.COM } 24258228SEric.Taylor@Sun.COM } 24268228SEric.Taylor@Sun.COM return (rc); 24278228SEric.Taylor@Sun.COM } 24288228SEric.Taylor@Sun.COM 2429789Sahrens /* 24301356Seschrock * Iterate over all child filesystems 2431789Sahrens */ 2432789Sahrens int 24331356Seschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2434789Sahrens { 2435789Sahrens zfs_cmd_t zc = { 0 }; 2436789Sahrens zfs_handle_t *nzhp; 2437789Sahrens int ret; 2438789Sahrens 24395367Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24405367Sahrens return (0); 24415367Sahrens 24428228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24438228SEric.Taylor@Sun.COM return (-1); 24448228SEric.Taylor@Sun.COM 24458228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 24468228SEric.Taylor@Sun.COM &zc)) == 0) { 2447789Sahrens /* 2448789Sahrens * Silently ignore errors, as the only plausible explanation is 2449789Sahrens * that the pool has since been removed. 2450789Sahrens */ 24518228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24528228SEric.Taylor@Sun.COM &zc)) == NULL) { 2453789Sahrens continue; 24548228SEric.Taylor@Sun.COM } 24558228SEric.Taylor@Sun.COM 24568228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24578228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2458789Sahrens return (ret); 24598228SEric.Taylor@Sun.COM } 2460789Sahrens } 24618228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 24628228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 24631356Seschrock } 24641356Seschrock 24651356Seschrock /* 24661356Seschrock * Iterate over all snapshots 24671356Seschrock */ 24681356Seschrock int 24691356Seschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24701356Seschrock { 24711356Seschrock zfs_cmd_t zc = { 0 }; 24721356Seschrock zfs_handle_t *nzhp; 24731356Seschrock int ret; 2474789Sahrens 24755367Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24765367Sahrens return (0); 24775367Sahrens 24788228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24798228SEric.Taylor@Sun.COM return (-1); 24808228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 24818228SEric.Taylor@Sun.COM &zc)) == 0) { 24828228SEric.Taylor@Sun.COM 24838228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24848228SEric.Taylor@Sun.COM &zc)) == NULL) { 2485789Sahrens continue; 24868228SEric.Taylor@Sun.COM } 24878228SEric.Taylor@Sun.COM 24888228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24898228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2490789Sahrens return (ret); 24918228SEric.Taylor@Sun.COM } 2492789Sahrens } 24938228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 24948228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 2495789Sahrens } 2496789Sahrens 2497789Sahrens /* 24981356Seschrock * Iterate over all children, snapshots and filesystems 24991356Seschrock */ 25001356Seschrock int 25011356Seschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25021356Seschrock { 25031356Seschrock int ret; 25041356Seschrock 25051356Seschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25061356Seschrock return (ret); 25071356Seschrock 25081356Seschrock return (zfs_iter_snapshots(zhp, func, data)); 25091356Seschrock } 25101356Seschrock 25111356Seschrock /* 2512789Sahrens * Given a complete name, return just the portion that refers to the parent. 2513789Sahrens * Can return NULL if this is a pool. 2514789Sahrens */ 2515789Sahrens static int 2516789Sahrens parent_name(const char *path, char *buf, size_t buflen) 2517789Sahrens { 2518789Sahrens char *loc; 2519789Sahrens 2520789Sahrens if ((loc = strrchr(path, '/')) == NULL) 2521789Sahrens return (-1); 2522789Sahrens 2523789Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2524789Sahrens buf[loc - path] = '\0'; 2525789Sahrens 2526789Sahrens return (0); 2527789Sahrens } 2528789Sahrens 2529789Sahrens /* 25304490Svb160487 * If accept_ancestor is false, then check to make sure that the given path has 25314490Svb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25324490Svb160487 * closest existing ancestor for the given path. In prefixlen return the 25334490Svb160487 * length of already existing prefix of the given path. We also fetch the 25344490Svb160487 * 'zoned' property, which is used to validate property settings when creating 25354490Svb160487 * new datasets. 2536789Sahrens */ 2537789Sahrens static int 25384490Svb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25394490Svb160487 boolean_t accept_ancestor, int *prefixlen) 2540789Sahrens { 2541789Sahrens zfs_cmd_t zc = { 0 }; 2542789Sahrens char parent[ZFS_MAXNAMELEN]; 2543789Sahrens char *slash; 25441356Seschrock zfs_handle_t *zhp; 25452082Seschrock char errbuf[1024]; 25462082Seschrock 25478269SMark.Musante@Sun.COM (void) snprintf(errbuf, sizeof (errbuf), 25488269SMark.Musante@Sun.COM dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2549789Sahrens 2550789Sahrens /* get parent, and check to see if this is just a pool */ 2551789Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 25522082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25532082Seschrock "missing dataset name")); 25542082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2555789Sahrens } 2556789Sahrens 2557789Sahrens /* check to see if the pool exists */ 2558789Sahrens if ((slash = strchr(parent, '/')) == NULL) 2559789Sahrens slash = parent + strlen(parent); 2560789Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2561789Sahrens zc.zc_name[slash - parent] = '\0'; 25622082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2563789Sahrens errno == ENOENT) { 25642082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25652082Seschrock "no such pool '%s'"), zc.zc_name); 25662082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2567789Sahrens } 2568789Sahrens 2569789Sahrens /* check to see if the parent dataset exists */ 25704490Svb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 25714490Svb160487 if (errno == ENOENT && accept_ancestor) { 25724490Svb160487 /* 25734490Svb160487 * Go deeper to find an ancestor, give up on top level. 25744490Svb160487 */ 25754490Svb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 25764490Svb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25774490Svb160487 "no such pool '%s'"), zc.zc_name); 25784490Svb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 25794490Svb160487 } 25804490Svb160487 } else if (errno == ENOENT) { 25812082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25822082Seschrock "parent does not exist")); 25832082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 25844490Svb160487 } else 25852082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2586789Sahrens } 2587789Sahrens 25882676Seschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2589789Sahrens /* we are in a non-global zone, but parent is in the global zone */ 25902676Seschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 25912082Seschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 25921356Seschrock zfs_close(zhp); 2593789Sahrens return (-1); 2594789Sahrens } 2595789Sahrens 2596789Sahrens /* make sure parent is a filesystem */ 25971356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 25982082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25992082Seschrock "parent is not a filesystem")); 26002082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26011356Seschrock zfs_close(zhp); 2602789Sahrens return (-1); 2603789Sahrens } 2604789Sahrens 26051356Seschrock zfs_close(zhp); 26064490Svb160487 if (prefixlen != NULL) 26074490Svb160487 *prefixlen = strlen(parent); 26084490Svb160487 return (0); 26094490Svb160487 } 26104490Svb160487 26114490Svb160487 /* 26124490Svb160487 * Finds whether the dataset of the given type(s) exists. 26134490Svb160487 */ 26144490Svb160487 boolean_t 26154490Svb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26164490Svb160487 { 26174490Svb160487 zfs_handle_t *zhp; 26184490Svb160487 26195326Sek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26204490Svb160487 return (B_FALSE); 26214490Svb160487 26224490Svb160487 /* 26234490Svb160487 * Try to get stats for the dataset, which will tell us if it exists. 26244490Svb160487 */ 26254490Svb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26264490Svb160487 int ds_type = zhp->zfs_type; 26274490Svb160487 26284490Svb160487 zfs_close(zhp); 26294490Svb160487 if (types & ds_type) 26304490Svb160487 return (B_TRUE); 26314490Svb160487 } 26324490Svb160487 return (B_FALSE); 26334490Svb160487 } 26344490Svb160487 26354490Svb160487 /* 26365367Sahrens * Given a path to 'target', create all the ancestors between 26375367Sahrens * the prefixlen portion of the path, and the target itself. 26385367Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26395367Sahrens */ 26405367Sahrens int 26415367Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26425367Sahrens { 26435367Sahrens zfs_handle_t *h; 26445367Sahrens char *cp; 26455367Sahrens const char *opname; 26465367Sahrens 26475367Sahrens /* make sure prefix exists */ 26485367Sahrens cp = target + prefixlen; 26495367Sahrens if (*cp != '/') { 26505367Sahrens assert(strchr(cp, '/') == NULL); 26515367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26525367Sahrens } else { 26535367Sahrens *cp = '\0'; 26545367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26555367Sahrens *cp = '/'; 26565367Sahrens } 26575367Sahrens if (h == NULL) 26585367Sahrens return (-1); 26595367Sahrens zfs_close(h); 26605367Sahrens 26615367Sahrens /* 26625367Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26635367Sahrens * up to the prefixlen-long one. 26645367Sahrens */ 26655367Sahrens for (cp = target + prefixlen + 1; 26665367Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 26675367Sahrens char *logstr; 26685367Sahrens 26695367Sahrens *cp = '\0'; 26705367Sahrens 26715367Sahrens h = make_dataset_handle(hdl, target); 26725367Sahrens if (h) { 26735367Sahrens /* it already exists, nothing to do here */ 26745367Sahrens zfs_close(h); 26755367Sahrens continue; 26765367Sahrens } 26775367Sahrens 26785367Sahrens logstr = hdl->libzfs_log_str; 26795367Sahrens hdl->libzfs_log_str = NULL; 26805367Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 26815367Sahrens NULL) != 0) { 26825367Sahrens hdl->libzfs_log_str = logstr; 26835367Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 26845367Sahrens goto ancestorerr; 26855367Sahrens } 26865367Sahrens 26875367Sahrens hdl->libzfs_log_str = logstr; 26885367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26895367Sahrens if (h == NULL) { 26905367Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 26915367Sahrens goto ancestorerr; 26925367Sahrens } 26935367Sahrens 26945367Sahrens if (zfs_mount(h, NULL, 0) != 0) { 26955367Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 26965367Sahrens goto ancestorerr; 26975367Sahrens } 26985367Sahrens 26995367Sahrens if (zfs_share(h) != 0) { 27005367Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27015367Sahrens goto ancestorerr; 27025367Sahrens } 27035367Sahrens 27045367Sahrens zfs_close(h); 27055367Sahrens } 27065367Sahrens 27075367Sahrens return (0); 27085367Sahrens 27095367Sahrens ancestorerr: 27105367Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27115367Sahrens "failed to %s ancestor '%s'"), opname, target); 27125367Sahrens return (-1); 27135367Sahrens } 27145367Sahrens 27155367Sahrens /* 27164490Svb160487 * Creates non-existing ancestors of the given path. 27174490Svb160487 */ 27184490Svb160487 int 27194490Svb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27204490Svb160487 { 27214490Svb160487 int prefix; 27224490Svb160487 uint64_t zoned; 27234490Svb160487 char *path_copy; 27244490Svb160487 int rc; 27254490Svb160487 27264490Svb160487 if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 27274490Svb160487 return (-1); 27284490Svb160487 27294490Svb160487 if ((path_copy = strdup(path)) != NULL) { 27304490Svb160487 rc = create_parents(hdl, path_copy, prefix); 27314490Svb160487 free(path_copy); 27324490Svb160487 } 27334490Svb160487 if (path_copy == NULL || rc != 0) 27344490Svb160487 return (-1); 27354490Svb160487 2736789Sahrens return (0); 2737789Sahrens } 2738789Sahrens 2739789Sahrens /* 27402676Seschrock * Create a new filesystem or volume. 2741789Sahrens */ 2742789Sahrens int 27432082Seschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 27442676Seschrock nvlist_t *props) 2745789Sahrens { 2746789Sahrens zfs_cmd_t zc = { 0 }; 2747789Sahrens int ret; 2748789Sahrens uint64_t size = 0; 2749789Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 27502082Seschrock char errbuf[1024]; 27512676Seschrock uint64_t zoned; 27522082Seschrock 27532082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 27542082Seschrock "cannot create '%s'"), path); 2755789Sahrens 2756789Sahrens /* validate the path, taking care to note the extended error message */ 27575326Sek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 27582082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2759789Sahrens 2760789Sahrens /* validate parents exist */ 27614490Svb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2762789Sahrens return (-1); 2763789Sahrens 2764789Sahrens /* 2765789Sahrens * The failure modes when creating a dataset of a different type over 2766789Sahrens * one that already exists is a little strange. In particular, if you 2767789Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2768789Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2769789Sahrens * first try to see if the dataset exists. 2770789Sahrens */ 2771789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 27725094Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 27732082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27742082Seschrock "dataset already exists")); 27752082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2776789Sahrens } 2777789Sahrens 2778789Sahrens if (type == ZFS_TYPE_VOLUME) 2779789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2780789Sahrens else 2781789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2782789Sahrens 27837184Stimh if (props && (props = zfs_valid_proplist(hdl, type, props, 27843912Slling zoned, NULL, errbuf)) == 0) 27852676Seschrock return (-1); 27862676Seschrock 2787789Sahrens if (type == ZFS_TYPE_VOLUME) { 27881133Seschrock /* 27891133Seschrock * If we are creating a volume, the size and block size must 27901133Seschrock * satisfy a few restraints. First, the blocksize must be a 27911133Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 27921133Seschrock * volsize must be a multiple of the block size, and cannot be 27931133Seschrock * zero. 27941133Seschrock */ 27952676Seschrock if (props == NULL || nvlist_lookup_uint64(props, 27962676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 27972676Seschrock nvlist_free(props); 27982082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27992676Seschrock "missing volume size")); 28002676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2801789Sahrens } 2802789Sahrens 28032676Seschrock if ((ret = nvlist_lookup_uint64(props, 28042676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 28052676Seschrock &blocksize)) != 0) { 28062676Seschrock if (ret == ENOENT) { 28072676Seschrock blocksize = zfs_prop_default_numeric( 28082676Seschrock ZFS_PROP_VOLBLOCKSIZE); 28092676Seschrock } else { 28102676Seschrock nvlist_free(props); 28112676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28122676Seschrock "missing volume block size")); 28132676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28142676Seschrock } 28152676Seschrock } 28162676Seschrock 28172676Seschrock if (size == 0) { 28182676Seschrock nvlist_free(props); 28192082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28202676Seschrock "volume size cannot be zero")); 28212676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28221133Seschrock } 28231133Seschrock 28241133Seschrock if (size % blocksize != 0) { 28252676Seschrock nvlist_free(props); 28262082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28272676Seschrock "volume size must be a multiple of volume block " 28282676Seschrock "size")); 28292676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28301133Seschrock } 2831789Sahrens } 2832789Sahrens 28335094Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 28342676Seschrock return (-1); 28352676Seschrock nvlist_free(props); 28362676Seschrock 2837789Sahrens /* create the dataset */ 28384543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2839789Sahrens 28402676Seschrock zcmd_free_nvlists(&zc); 28412676Seschrock 2842789Sahrens /* check for failure */ 2843789Sahrens if (ret != 0) { 2844789Sahrens char parent[ZFS_MAXNAMELEN]; 2845789Sahrens (void) parent_name(path, parent, sizeof (parent)); 2846789Sahrens 2847789Sahrens switch (errno) { 2848789Sahrens case ENOENT: 28492082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28502082Seschrock "no such parent '%s'"), parent); 28512082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2852789Sahrens 2853789Sahrens case EINVAL: 28542082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28553413Smmusante "parent '%s' is not a filesystem"), parent); 28562082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2857789Sahrens 2858789Sahrens case EDOM: 28592082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28602676Seschrock "volume block size must be power of 2 from " 28612676Seschrock "%u to %uk"), 2862789Sahrens (uint_t)SPA_MINBLOCKSIZE, 2863789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 28642082Seschrock 28652676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28662082Seschrock 28674603Sahrens case ENOTSUP: 28684603Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28694603Sahrens "pool must be upgraded to set this " 28704603Sahrens "property or value")); 28714603Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2872789Sahrens #ifdef _ILP32 2873789Sahrens case EOVERFLOW: 2874789Sahrens /* 2875789Sahrens * This platform can't address a volume this big. 2876789Sahrens */ 28772082Seschrock if (type == ZFS_TYPE_VOLUME) 28782082Seschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 28792082Seschrock errbuf)); 2880789Sahrens #endif 28812082Seschrock /* FALLTHROUGH */ 2882789Sahrens default: 28832082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2884789Sahrens } 2885789Sahrens } 2886789Sahrens 2887789Sahrens return (0); 2888789Sahrens } 2889789Sahrens 2890789Sahrens /* 2891789Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2892789Sahrens * isn't mounted, and that there are no active dependents. 2893789Sahrens */ 2894789Sahrens int 289510242Schris.kirby@sun.com zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2896789Sahrens { 2897789Sahrens zfs_cmd_t zc = { 0 }; 2898789Sahrens 2899789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2900789Sahrens 29012676Seschrock if (ZFS_IS_VOLUME(zhp)) { 29023126Sahl /* 29034543Smarks * If user doesn't have permissions to unshare volume, then 29044543Smarks * abort the request. This would only happen for a 29054543Smarks * non-privileged user. 29063126Sahl */ 29074543Smarks if (zfs_unshare_iscsi(zhp) != 0) { 29084543Smarks return (-1); 29094543Smarks } 29103126Sahl 2911789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2912789Sahrens } else { 2913789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2914789Sahrens } 2915789Sahrens 291610242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 29174543Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 29183237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 29192082Seschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 29202082Seschrock zhp->zfs_name)); 29212199Sahrens } 2922789Sahrens 2923789Sahrens remove_mountpoint(zhp); 2924789Sahrens 2925789Sahrens return (0); 2926789Sahrens } 2927789Sahrens 29282199Sahrens struct destroydata { 29292199Sahrens char *snapname; 29302199Sahrens boolean_t gotone; 29313265Sahrens boolean_t closezhp; 29322199Sahrens }; 29332199Sahrens 29342199Sahrens static int 293510588SEric.Taylor@Sun.COM zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29362199Sahrens { 29372199Sahrens struct destroydata *dd = arg; 29382199Sahrens zfs_handle_t *szhp; 29392199Sahrens char name[ZFS_MAXNAMELEN]; 29403265Sahrens boolean_t closezhp = dd->closezhp; 294110588SEric.Taylor@Sun.COM int rv = 0; 29422199Sahrens 29432676Seschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 29442676Seschrock (void) strlcat(name, "@", sizeof (name)); 29452676Seschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29462199Sahrens 29472199Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 29482199Sahrens if (szhp) { 29492199Sahrens dd->gotone = B_TRUE; 29502199Sahrens zfs_close(szhp); 29512199Sahrens } 29522199Sahrens 29533265Sahrens dd->closezhp = B_TRUE; 295410588SEric.Taylor@Sun.COM if (!dd->gotone) 295510588SEric.Taylor@Sun.COM rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 29563265Sahrens if (closezhp) 29573265Sahrens zfs_close(zhp); 29583265Sahrens return (rv); 29592199Sahrens } 29602199Sahrens 29612199Sahrens /* 29622199Sahrens * Destroys all snapshots with the given name in zhp & descendants. 29632199Sahrens */ 29642199Sahrens int 296510242Schris.kirby@sun.com zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 29662199Sahrens { 29672199Sahrens zfs_cmd_t zc = { 0 }; 29682199Sahrens int ret; 29692199Sahrens struct destroydata dd = { 0 }; 29702199Sahrens 29712199Sahrens dd.snapname = snapname; 297210588SEric.Taylor@Sun.COM (void) zfs_check_snap_cb(zhp, &dd); 29732199Sahrens 29742199Sahrens if (!dd.gotone) { 29753237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 29762199Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 29772199Sahrens zhp->zfs_name, snapname)); 29782199Sahrens } 29792199Sahrens 29802199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 29812676Seschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 298210242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 29832199Sahrens 29844543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 29852199Sahrens if (ret != 0) { 29862199Sahrens char errbuf[1024]; 29872199Sahrens 29882199Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 29892199Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 29902199Sahrens 29912199Sahrens switch (errno) { 29922199Sahrens case EEXIST: 29932199Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 29942199Sahrens "snapshot is cloned")); 29952199Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 29962199Sahrens 29972199Sahrens default: 29982199Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 29992199Sahrens errbuf)); 30002199Sahrens } 30012199Sahrens } 30022199Sahrens 30032199Sahrens return (0); 30042199Sahrens } 30052199Sahrens 3006789Sahrens /* 3007789Sahrens * Clones the given dataset. The target must be of the same type as the source. 3008789Sahrens */ 3009789Sahrens int 30102676Seschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3011789Sahrens { 3012789Sahrens zfs_cmd_t zc = { 0 }; 3013789Sahrens char parent[ZFS_MAXNAMELEN]; 3014789Sahrens int ret; 30152082Seschrock char errbuf[1024]; 30162082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 30172676Seschrock zfs_type_t type; 30182676Seschrock uint64_t zoned; 3019789Sahrens 3020789Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3021789Sahrens 30222082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30232082Seschrock "cannot create '%s'"), target); 30242082Seschrock 3025789Sahrens /* validate the target name */ 30265326Sek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 30272082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3028789Sahrens 3029789Sahrens /* validate parents exist */ 30304490Svb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3031789Sahrens return (-1); 3032789Sahrens 3033789Sahrens (void) parent_name(target, parent, sizeof (parent)); 3034789Sahrens 3035789Sahrens /* do the clone */ 30362676Seschrock if (ZFS_IS_VOLUME(zhp)) { 3037789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30382744Snn35248 type = ZFS_TYPE_VOLUME; 30392676Seschrock } else { 3040789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30412744Snn35248 type = ZFS_TYPE_FILESYSTEM; 30422676Seschrock } 30432676Seschrock 30442676Seschrock if (props) { 30457184Stimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30467184Stimh zhp, errbuf)) == NULL) 30472676Seschrock return (-1); 30482676Seschrock 30495094Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 30502676Seschrock nvlist_free(props); 30512676Seschrock return (-1); 30522676Seschrock } 30532676Seschrock 30542676Seschrock nvlist_free(props); 30552676Seschrock } 3056789Sahrens 3057789Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 30582676Seschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 30594543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3060789Sahrens 30612676Seschrock zcmd_free_nvlists(&zc); 30622676Seschrock 3063789Sahrens if (ret != 0) { 3064789Sahrens switch (errno) { 3065789Sahrens 3066789Sahrens case ENOENT: 3067789Sahrens /* 3068789Sahrens * The parent doesn't exist. We should have caught this 3069789Sahrens * above, but there may a race condition that has since 3070789Sahrens * destroyed the parent. 3071789Sahrens * 3072789Sahrens * At this point, we don't know whether it's the source 3073789Sahrens * that doesn't exist anymore, or whether the target 3074789Sahrens * dataset doesn't exist. 3075789Sahrens */ 30762082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30772082Seschrock "no such parent '%s'"), parent); 30782082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 30792082Seschrock 30802082Seschrock case EXDEV: 30812082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30822082Seschrock "source and target pools differ")); 30832082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 30842082Seschrock errbuf)); 30852082Seschrock 30862082Seschrock default: 30872082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 30882082Seschrock errbuf)); 30892082Seschrock } 30902082Seschrock } 30912082Seschrock 30922082Seschrock return (ret); 30932082Seschrock } 30942082Seschrock 30952082Seschrock /* 30962082Seschrock * Promotes the given clone fs to be the clone parent. 30972082Seschrock */ 30982082Seschrock int 30992082Seschrock zfs_promote(zfs_handle_t *zhp) 31002082Seschrock { 31012082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 31022082Seschrock zfs_cmd_t zc = { 0 }; 31032082Seschrock char parent[MAXPATHLEN]; 31042082Seschrock int ret; 31052082Seschrock char errbuf[1024]; 31062082Seschrock 31072082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31082082Seschrock "cannot promote '%s'"), zhp->zfs_name); 31092082Seschrock 31102082Seschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 31112082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31122082Seschrock "snapshots can not be promoted")); 31132082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31142082Seschrock } 31152082Seschrock 31165367Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 31172082Seschrock if (parent[0] == '\0') { 31182082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31192082Seschrock "not a cloned filesystem")); 31202082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31212082Seschrock } 312210588SEric.Taylor@Sun.COM 31235367Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 31242676Seschrock sizeof (zc.zc_value)); 31252082Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 31264543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 31272082Seschrock 31282082Seschrock if (ret != 0) { 31292417Sahrens int save_errno = errno; 31302417Sahrens 31312417Sahrens switch (save_errno) { 3132789Sahrens case EEXIST: 313310588SEric.Taylor@Sun.COM /* There is a conflicting snapshot name. */ 31342082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 313510588SEric.Taylor@Sun.COM "conflicting snapshot '%s' from parent '%s'"), 313610588SEric.Taylor@Sun.COM zc.zc_string, parent); 31372082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3138789Sahrens 3139789Sahrens default: 31402417Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3141789Sahrens } 3142789Sahrens } 31432676Seschrock return (ret); 31442199Sahrens } 31452199Sahrens 3146789Sahrens /* 31473504Sahl * Takes a snapshot of the given dataset. 3148789Sahrens */ 3149789Sahrens int 31507265Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 31517265Sahrens nvlist_t *props) 3152789Sahrens { 3153789Sahrens const char *delim; 31547265Sahrens char parent[ZFS_MAXNAMELEN]; 3155789Sahrens zfs_handle_t *zhp; 3156789Sahrens zfs_cmd_t zc = { 0 }; 3157789Sahrens int ret; 31582082Seschrock char errbuf[1024]; 31592082Seschrock 31602082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31612082Seschrock "cannot snapshot '%s'"), path); 31622082Seschrock 31632082Seschrock /* validate the target name */ 31645326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 31652082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3166789Sahrens 31677265Sahrens if (props) { 31687265Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 31697265Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 31707265Sahrens return (-1); 31717265Sahrens 31727265Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 31737265Sahrens nvlist_free(props); 31747265Sahrens return (-1); 31757265Sahrens } 31767265Sahrens 31777265Sahrens nvlist_free(props); 31787265Sahrens } 31797265Sahrens 3180789Sahrens /* make sure the parent exists and is of the appropriate type */ 31812199Sahrens delim = strchr(path, '@'); 3182789Sahrens (void) strncpy(parent, path, delim - path); 3183789Sahrens parent[delim - path] = '\0'; 3184789Sahrens 31852082Seschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3186789Sahrens ZFS_TYPE_VOLUME)) == NULL) { 31877265Sahrens zcmd_free_nvlists(&zc); 3188789Sahrens return (-1); 3189789Sahrens } 3190789Sahrens 31912199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 31922676Seschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 31934543Smarks if (ZFS_IS_VOLUME(zhp)) 31944543Smarks zc.zc_objset_type = DMU_OST_ZVOL; 31954543Smarks else 31964543Smarks zc.zc_objset_type = DMU_OST_ZFS; 31972199Sahrens zc.zc_cookie = recursive; 31984543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 31992199Sahrens 32007265Sahrens zcmd_free_nvlists(&zc); 32017265Sahrens 32022199Sahrens /* 32032199Sahrens * if it was recursive, the one that actually failed will be in 32042199Sahrens * zc.zc_name. 32052199Sahrens */ 320610588SEric.Taylor@Sun.COM if (ret != 0) { 32074543Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 32084543Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 320910588SEric.Taylor@Sun.COM (void) zfs_standard_error(hdl, errno, errbuf); 32102199Sahrens } 3211789Sahrens 3212789Sahrens zfs_close(zhp); 3213789Sahrens 3214789Sahrens return (ret); 3215789Sahrens } 3216789Sahrens 3217789Sahrens /* 32181294Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 32191294Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 32201294Slling * is a dependent and we should just destroy it without checking the transaction 32211294Slling * group. 3222789Sahrens */ 32231294Slling typedef struct rollback_data { 32241294Slling const char *cb_target; /* the snapshot */ 32251294Slling uint64_t cb_create; /* creation time reference */ 32265749Sahrens boolean_t cb_error; 32272082Seschrock boolean_t cb_dependent; 32285749Sahrens boolean_t cb_force; 32291294Slling } rollback_data_t; 32301294Slling 32311294Slling static int 32321294Slling rollback_destroy(zfs_handle_t *zhp, void *data) 32331294Slling { 32341294Slling rollback_data_t *cbp = data; 32351294Slling 32361294Slling if (!cbp->cb_dependent) { 32371294Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 32381294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 32391294Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 32401294Slling cbp->cb_create) { 32414543Smarks char *logstr; 32421294Slling 32432082Seschrock cbp->cb_dependent = B_TRUE; 32445446Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32455446Sahrens rollback_destroy, cbp); 32462082Seschrock cbp->cb_dependent = B_FALSE; 32471294Slling 32484543Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 32494543Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 325010242Schris.kirby@sun.com cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 32514543Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 32521294Slling } 32531294Slling } else { 32545749Sahrens /* We must destroy this clone; first unmount it */ 32555749Sahrens prop_changelist_t *clp; 32565749Sahrens 32577366STim.Haley@Sun.COM clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 32585749Sahrens cbp->cb_force ? MS_FORCE: 0); 32595749Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 32605749Sahrens cbp->cb_error = B_TRUE; 32615749Sahrens zfs_close(zhp); 32625749Sahrens return (0); 32635749Sahrens } 326410242Schris.kirby@sun.com if (zfs_destroy(zhp, B_FALSE) != 0) 32655749Sahrens cbp->cb_error = B_TRUE; 32665749Sahrens else 32675749Sahrens changelist_remove(clp, zhp->zfs_name); 32685751Sahrens (void) changelist_postfix(clp); 32695749Sahrens changelist_free(clp); 32701294Slling } 32711294Slling 32721294Slling zfs_close(zhp); 32731294Slling return (0); 32741294Slling } 32751294Slling 32761294Slling /* 32775446Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 32785446Sahrens * data changes since then and making it the active dataset. 32795446Sahrens * 32805446Sahrens * Any snapshots more recent than the target are destroyed, along with 32815446Sahrens * their dependents. 32821294Slling */ 32835446Sahrens int 32845749Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3285789Sahrens { 32865446Sahrens rollback_data_t cb = { 0 }; 32875446Sahrens int err; 3288789Sahrens zfs_cmd_t zc = { 0 }; 32895713Srm160521 boolean_t restore_resv = 0; 32905713Srm160521 uint64_t old_volsize, new_volsize; 32915713Srm160521 zfs_prop_t resv_prop; 3292789Sahrens 3293789Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3294789Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3295789Sahrens 32965446Sahrens /* 32975446Sahrens * Destroy all recent snapshots and its dependends. 32985446Sahrens */ 32995749Sahrens cb.cb_force = force; 33005446Sahrens cb.cb_target = snap->zfs_name; 33015446Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33025446Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33035446Sahrens 33045749Sahrens if (cb.cb_error) 33055749Sahrens return (-1); 33065446Sahrens 33075446Sahrens /* 33085446Sahrens * Now that we have verified that the snapshot is the latest, 33095446Sahrens * rollback to the given snapshot. 33105446Sahrens */ 33115446Sahrens 33125713Srm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33135713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33145713Srm160521 return (-1); 33155713Srm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33165713Srm160521 restore_resv = 33175713Srm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33185713Srm160521 } 3319789Sahrens 3320789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3321789Sahrens 33222676Seschrock if (ZFS_IS_VOLUME(zhp)) 3323789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3324789Sahrens else 3325789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3326789Sahrens 3327789Sahrens /* 33285446Sahrens * We rely on zfs_iter_children() to verify that there are no 33295446Sahrens * newer snapshots for the given dataset. Therefore, we can 33305446Sahrens * simply pass the name on to the ioctl() call. There is still 33315446Sahrens * an unlikely race condition where the user has taken a 33325446Sahrens * snapshot since we verified that this was the most recent. 33335713Srm160521 * 3334789Sahrens */ 33355446Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33363237Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33372082Seschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33382082Seschrock zhp->zfs_name); 33395717Srm160521 return (err); 33405717Srm160521 } 33415713Srm160521 33425713Srm160521 /* 33435713Srm160521 * For volumes, if the pre-rollback volsize matched the pre- 33445713Srm160521 * rollback reservation and the volsize has changed then set 33455713Srm160521 * the reservation property to the post-rollback volsize. 33465713Srm160521 * Make a new handle since the rollback closed the dataset. 33475713Srm160521 */ 33485717Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 33495717Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 33505713Srm160521 if (restore_resv) { 33515713Srm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33525713Srm160521 if (old_volsize != new_volsize) 33535717Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 33545717Srm160521 new_volsize); 33555713Srm160521 } 33565713Srm160521 zfs_close(zhp); 3357789Sahrens } 33585446Sahrens return (err); 33591294Slling } 33601294Slling 33611294Slling /* 3362789Sahrens * Iterate over all dependents for a given dataset. This includes both 3363789Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3364789Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3365789Sahrens * libzfs_graph.c. 3366789Sahrens */ 3367789Sahrens int 33682474Seschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33692474Seschrock zfs_iter_f func, void *data) 3370789Sahrens { 3371789Sahrens char **dependents; 3372789Sahrens size_t count; 3373789Sahrens int i; 3374789Sahrens zfs_handle_t *child; 3375789Sahrens int ret = 0; 3376789Sahrens 33772474Seschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 33782474Seschrock &dependents, &count) != 0) 33792474Seschrock return (-1); 33802474Seschrock 3381789Sahrens for (i = 0; i < count; i++) { 33822082Seschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 33832082Seschrock dependents[i])) == NULL) 3384789Sahrens continue; 3385789Sahrens 3386789Sahrens if ((ret = func(child, data)) != 0) 3387789Sahrens break; 3388789Sahrens } 3389789Sahrens 3390789Sahrens for (i = 0; i < count; i++) 3391789Sahrens free(dependents[i]); 3392789Sahrens free(dependents); 3393789Sahrens 3394789Sahrens return (ret); 3395789Sahrens } 3396789Sahrens 3397789Sahrens /* 3398789Sahrens * Renames the given dataset. 3399789Sahrens */ 3400789Sahrens int 34014490Svb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3402789Sahrens { 3403789Sahrens int ret; 3404789Sahrens zfs_cmd_t zc = { 0 }; 3405789Sahrens char *delim; 34064007Smmusante prop_changelist_t *cl = NULL; 34074007Smmusante zfs_handle_t *zhrp = NULL; 34084007Smmusante char *parentname = NULL; 3409789Sahrens char parent[ZFS_MAXNAMELEN]; 34102082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 34112082Seschrock char errbuf[1024]; 3412789Sahrens 3413789Sahrens /* if we have the same exact name, just return success */ 3414789Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3415789Sahrens return (0); 3416789Sahrens 34172082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 34182082Seschrock "cannot rename to '%s'"), target); 34192082Seschrock 3420789Sahrens /* 3421789Sahrens * Make sure the target name is valid 3422789Sahrens */ 3423789Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 34242665Snd150628 if ((strchr(target, '@') == NULL) || 34252665Snd150628 *target == '@') { 34262665Snd150628 /* 34272665Snd150628 * Snapshot target name is abbreviated, 34282665Snd150628 * reconstruct full dataset name 34292665Snd150628 */ 34302665Snd150628 (void) strlcpy(parent, zhp->zfs_name, 34312665Snd150628 sizeof (parent)); 34322665Snd150628 delim = strchr(parent, '@'); 34332665Snd150628 if (strchr(target, '@') == NULL) 34342665Snd150628 *(++delim) = '\0'; 34352665Snd150628 else 34362665Snd150628 *delim = '\0'; 34372665Snd150628 (void) strlcat(parent, target, sizeof (parent)); 34382665Snd150628 target = parent; 34392665Snd150628 } else { 34402665Snd150628 /* 34412665Snd150628 * Make sure we're renaming within the same dataset. 34422665Snd150628 */ 34432665Snd150628 delim = strchr(target, '@'); 34442665Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 34452665Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 34462665Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34472665Snd150628 "snapshots must be part of same " 34482665Snd150628 "dataset")); 34492665Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 34503912Slling errbuf)); 34512665Snd150628 } 3452789Sahrens } 34535326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 34542665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3455789Sahrens } else { 34564007Smmusante if (recursive) { 34574007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34584007Smmusante "recursive rename must be a snapshot")); 34594007Smmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 34604007Smmusante } 34614007Smmusante 34625326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 34632665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 34642676Seschrock uint64_t unused; 34652676Seschrock 3466789Sahrens /* validate parents */ 34674490Svb160487 if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3468789Sahrens return (-1); 3469789Sahrens 3470789Sahrens (void) parent_name(target, parent, sizeof (parent)); 3471789Sahrens 3472789Sahrens /* make sure we're in the same pool */ 3473789Sahrens verify((delim = strchr(target, '/')) != NULL); 3474789Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3475789Sahrens zhp->zfs_name[delim - target] != '/') { 34762082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34772082Seschrock "datasets must be within same pool")); 34782082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3479789Sahrens } 34802440Snd150628 34812440Snd150628 /* new name cannot be a child of the current dataset name */ 34822440Snd150628 if (strncmp(parent, zhp->zfs_name, 34833912Slling strlen(zhp->zfs_name)) == 0) { 34842440Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34852440Snd150628 "New dataset name cannot be a descendent of " 34862440Snd150628 "current dataset name")); 34872440Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 34882440Snd150628 } 3489789Sahrens } 3490789Sahrens 34912082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 34922082Seschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 34932082Seschrock 3494789Sahrens if (getzoneid() == GLOBAL_ZONEID && 3495789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 34962082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34972082Seschrock "dataset is used in a non-global zone")); 34982082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3499789Sahrens } 3500789Sahrens 35014007Smmusante if (recursive) { 35024007Smmusante 35034183Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 35044183Smmusante if (parentname == NULL) { 35054183Smmusante ret = -1; 35064183Smmusante goto error; 35074183Smmusante } 35084007Smmusante delim = strchr(parentname, '@'); 35094007Smmusante *delim = '\0'; 35105094Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 35114007Smmusante if (zhrp == NULL) { 35124183Smmusante ret = -1; 35134183Smmusante goto error; 35144007Smmusante } 35154007Smmusante 35164007Smmusante } else { 35177366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 35184007Smmusante return (-1); 35194007Smmusante 35204007Smmusante if (changelist_haszonedchild(cl)) { 35214007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35224007Smmusante "child dataset with inherited mountpoint is used " 35234007Smmusante "in a non-global zone")); 35244007Smmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 35254007Smmusante goto error; 35264007Smmusante } 35274007Smmusante 35284007Smmusante if ((ret = changelist_prefix(cl)) != 0) 35294007Smmusante goto error; 3530789Sahrens } 3531789Sahrens 35322676Seschrock if (ZFS_IS_VOLUME(zhp)) 3533789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3534789Sahrens else 3535789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3536789Sahrens 35372665Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35382676Seschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 35392665Snd150628 35404007Smmusante zc.zc_cookie = recursive; 35414007Smmusante 35424543Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 35434007Smmusante /* 35444007Smmusante * if it was recursive, the one that actually failed will 35454007Smmusante * be in zc.zc_name 35464007Smmusante */ 35474007Smmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35485367Sahrens "cannot rename '%s'"), zc.zc_name); 35494007Smmusante 35504007Smmusante if (recursive && errno == EEXIST) { 35514007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35524007Smmusante "a child dataset already has a snapshot " 35534007Smmusante "with the new name")); 35544801Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 35554007Smmusante } else { 35564007Smmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 35574007Smmusante } 3558789Sahrens 3559789Sahrens /* 3560789Sahrens * On failure, we still want to remount any filesystems that 3561789Sahrens * were previously mounted, so we don't alter the system state. 3562789Sahrens */ 356310588SEric.Taylor@Sun.COM if (!recursive) 35644007Smmusante (void) changelist_postfix(cl); 3565789Sahrens } else { 356610588SEric.Taylor@Sun.COM if (!recursive) { 35674007Smmusante changelist_rename(cl, zfs_get_name(zhp), target); 35684007Smmusante ret = changelist_postfix(cl); 35694007Smmusante } 3570789Sahrens } 3571789Sahrens 3572789Sahrens error: 35734007Smmusante if (parentname) { 35744007Smmusante free(parentname); 35754007Smmusante } 35764007Smmusante if (zhrp) { 35774007Smmusante zfs_close(zhrp); 35784007Smmusante } 35794007Smmusante if (cl) { 35804007Smmusante changelist_free(cl); 35814007Smmusante } 3582789Sahrens return (ret); 3583789Sahrens } 3584789Sahrens 35852676Seschrock nvlist_t * 35862676Seschrock zfs_get_user_props(zfs_handle_t *zhp) 35872676Seschrock { 35882676Seschrock return (zhp->zfs_user_props); 35892676Seschrock } 35902676Seschrock 359111022STom.Erickson@Sun.COM nvlist_t * 359211022STom.Erickson@Sun.COM zfs_get_recvd_props(zfs_handle_t *zhp) 359311022STom.Erickson@Sun.COM { 359411022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 359511022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 359611022STom.Erickson@Sun.COM return (NULL); 359711022STom.Erickson@Sun.COM return (zhp->zfs_recvd_props); 359811022STom.Erickson@Sun.COM } 359911022STom.Erickson@Sun.COM 36002676Seschrock /* 36013912Slling * This function is used by 'zfs list' to determine the exact set of columns to 36023912Slling * display, and their maximum widths. This does two main things: 36033912Slling * 36043912Slling * - If this is a list of all properties, then expand the list to include 36053912Slling * all native properties, and set a flag so that for each dataset we look 36063912Slling * for new unique user properties and add them to the list. 36073912Slling * 36083912Slling * - For non fixed-width properties, keep track of the maximum width seen 360911022STom.Erickson@Sun.COM * so that we can size the column appropriately. If the user has 361011022STom.Erickson@Sun.COM * requested received property values, we also need to compute the width 361111022STom.Erickson@Sun.COM * of the RECEIVED column. 36123912Slling */ 36133912Slling int 361411022STom.Erickson@Sun.COM zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 36153912Slling { 36163912Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 36175094Slling zprop_list_t *entry; 36185094Slling zprop_list_t **last, **start; 36193912Slling nvlist_t *userprops, *propval; 36203912Slling nvpair_t *elem; 36213912Slling char *strval; 36223912Slling char buf[ZFS_MAXPROPLEN]; 36233912Slling 36245094Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 36253912Slling return (-1); 36262676Seschrock 36272676Seschrock userprops = zfs_get_user_props(zhp); 36282676Seschrock 36292676Seschrock entry = *plp; 36302676Seschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 36312676Seschrock /* 36322676Seschrock * Go through and add any user properties as necessary. We 36332676Seschrock * start by incrementing our list pointer to the first 36342676Seschrock * non-native property. 36352676Seschrock */ 36362676Seschrock start = plp; 36372676Seschrock while (*start != NULL) { 36385094Slling if ((*start)->pl_prop == ZPROP_INVAL) 36392676Seschrock break; 36402676Seschrock start = &(*start)->pl_next; 36412676Seschrock } 36422676Seschrock 36432676Seschrock elem = NULL; 36442676Seschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 36452676Seschrock /* 36462676Seschrock * See if we've already found this property in our list. 36472676Seschrock */ 36482676Seschrock for (last = start; *last != NULL; 36492676Seschrock last = &(*last)->pl_next) { 36502676Seschrock if (strcmp((*last)->pl_user_prop, 36512676Seschrock nvpair_name(elem)) == 0) 36522676Seschrock break; 36532676Seschrock } 36542676Seschrock 36552676Seschrock if (*last == NULL) { 36562676Seschrock if ((entry = zfs_alloc(hdl, 36575094Slling sizeof (zprop_list_t))) == NULL || 36582676Seschrock ((entry->pl_user_prop = zfs_strdup(hdl, 36592676Seschrock nvpair_name(elem)))) == NULL) { 36602676Seschrock free(entry); 36612676Seschrock return (-1); 36622676Seschrock } 36632676Seschrock 36645094Slling entry->pl_prop = ZPROP_INVAL; 36652676Seschrock entry->pl_width = strlen(nvpair_name(elem)); 36662676Seschrock entry->pl_all = B_TRUE; 36672676Seschrock *last = entry; 36682676Seschrock } 36692676Seschrock } 36702676Seschrock } 36712676Seschrock 36722676Seschrock /* 36732676Seschrock * Now go through and check the width of any non-fixed columns 36742676Seschrock */ 36752676Seschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 36762676Seschrock if (entry->pl_fixed) 36772676Seschrock continue; 36782676Seschrock 36795094Slling if (entry->pl_prop != ZPROP_INVAL) { 36802676Seschrock if (zfs_prop_get(zhp, entry->pl_prop, 36812676Seschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 36822676Seschrock if (strlen(buf) > entry->pl_width) 36832676Seschrock entry->pl_width = strlen(buf); 36842676Seschrock } 368511022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 368611022STom.Erickson@Sun.COM zfs_prop_to_name(entry->pl_prop), 368711022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 368811022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 368911022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 369011022STom.Erickson@Sun.COM } else { 369111022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 369211022STom.Erickson@Sun.COM &propval) == 0) { 369311022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, 369411022STom.Erickson@Sun.COM ZPROP_VALUE, &strval) == 0); 369511022STom.Erickson@Sun.COM if (strlen(strval) > entry->pl_width) 369611022STom.Erickson@Sun.COM entry->pl_width = strlen(strval); 369711022STom.Erickson@Sun.COM } 369811022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 369911022STom.Erickson@Sun.COM entry->pl_user_prop, 370011022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 370111022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 370211022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 37032676Seschrock } 37042676Seschrock } 37052676Seschrock 37062676Seschrock return (0); 37072676Seschrock } 37084543Smarks 37094543Smarks int 37104543Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 37114543Smarks { 37124543Smarks zfs_cmd_t zc = { 0 }; 37134543Smarks nvlist_t *nvp; 37144543Smarks gid_t gid; 37154543Smarks uid_t uid; 37164543Smarks const gid_t *groups; 37174543Smarks int group_cnt; 37184543Smarks int error; 37194543Smarks 37204543Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 37214543Smarks return (no_memory(hdl)); 37224543Smarks 37234543Smarks uid = ucred_geteuid(cred); 37244543Smarks gid = ucred_getegid(cred); 37254543Smarks group_cnt = ucred_getgroups(cred, &groups); 37264543Smarks 37274543Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 37284543Smarks return (1); 37294543Smarks 37304543Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 37314543Smarks nvlist_free(nvp); 37324543Smarks return (1); 37334543Smarks } 37344543Smarks 37354543Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 37364543Smarks nvlist_free(nvp); 37374543Smarks return (1); 37384543Smarks } 37394543Smarks 37404543Smarks if (nvlist_add_uint32_array(nvp, 37414543Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 37424543Smarks nvlist_free(nvp); 37434543Smarks return (1); 37444543Smarks } 37454543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37464543Smarks 37475094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 37484543Smarks return (-1); 37494543Smarks 37504543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 37514543Smarks nvlist_free(nvp); 37524543Smarks return (error); 37534543Smarks } 37544543Smarks 37554543Smarks int 37564543Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 37578845Samw@Sun.COM char *resource, void *export, void *sharetab, 37588845Samw@Sun.COM int sharemax, zfs_share_op_t operation) 37594543Smarks { 37604543Smarks zfs_cmd_t zc = { 0 }; 37614543Smarks int error; 37624543Smarks 37634543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37644543Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37658845Samw@Sun.COM if (resource) 37668845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 37674543Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 37684543Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 37695331Samw zc.zc_share.z_sharetype = operation; 37704543Smarks zc.zc_share.z_sharemax = sharemax; 37714543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 37724543Smarks return (error); 37734543Smarks } 37748802SSanjeev.Bagewadi@Sun.COM 37758802SSanjeev.Bagewadi@Sun.COM void 37768802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37778802SSanjeev.Bagewadi@Sun.COM { 37788802SSanjeev.Bagewadi@Sun.COM nvpair_t *curr; 37798802SSanjeev.Bagewadi@Sun.COM 37808802SSanjeev.Bagewadi@Sun.COM /* 37818802SSanjeev.Bagewadi@Sun.COM * Keep a reference to the props-table against which we prune the 37828802SSanjeev.Bagewadi@Sun.COM * properties. 37838802SSanjeev.Bagewadi@Sun.COM */ 37848802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table = props; 37858802SSanjeev.Bagewadi@Sun.COM 37868802SSanjeev.Bagewadi@Sun.COM curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37878802SSanjeev.Bagewadi@Sun.COM 37888802SSanjeev.Bagewadi@Sun.COM while (curr) { 37898802SSanjeev.Bagewadi@Sun.COM zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37908802SSanjeev.Bagewadi@Sun.COM nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37918802SSanjeev.Bagewadi@Sun.COM 37929396SMatthew.Ahrens@Sun.COM /* 379310960SEric.Schrock@Sun.COM * User properties will result in ZPROP_INVAL, and since we 379410960SEric.Schrock@Sun.COM * only know how to prune standard ZFS properties, we always 379510960SEric.Schrock@Sun.COM * leave these in the list. This can also happen if we 379610960SEric.Schrock@Sun.COM * encounter an unknown DSL property (when running older 379710960SEric.Schrock@Sun.COM * software, for example). 37989396SMatthew.Ahrens@Sun.COM */ 37999396SMatthew.Ahrens@Sun.COM if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 38008802SSanjeev.Bagewadi@Sun.COM (void) nvlist_remove(zhp->zfs_props, 38018802SSanjeev.Bagewadi@Sun.COM nvpair_name(curr), nvpair_type(curr)); 38028802SSanjeev.Bagewadi@Sun.COM curr = next; 38038802SSanjeev.Bagewadi@Sun.COM } 38048802SSanjeev.Bagewadi@Sun.COM } 38058845Samw@Sun.COM 38068845Samw@Sun.COM static int 38078845Samw@Sun.COM zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 38088845Samw@Sun.COM zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 38098845Samw@Sun.COM { 38108845Samw@Sun.COM zfs_cmd_t zc = { 0 }; 38118845Samw@Sun.COM nvlist_t *nvlist = NULL; 38128845Samw@Sun.COM int error; 38138845Samw@Sun.COM 38148845Samw@Sun.COM (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 38158845Samw@Sun.COM (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 38168845Samw@Sun.COM zc.zc_cookie = (uint64_t)cmd; 38178845Samw@Sun.COM 38188845Samw@Sun.COM if (cmd == ZFS_SMB_ACL_RENAME) { 38198845Samw@Sun.COM if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 38208845Samw@Sun.COM (void) no_memory(hdl); 38218845Samw@Sun.COM return (NULL); 38228845Samw@Sun.COM } 38238845Samw@Sun.COM } 38248845Samw@Sun.COM 38258845Samw@Sun.COM switch (cmd) { 38268845Samw@Sun.COM case ZFS_SMB_ACL_ADD: 38278845Samw@Sun.COM case ZFS_SMB_ACL_REMOVE: 38288845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 38298845Samw@Sun.COM break; 38308845Samw@Sun.COM case ZFS_SMB_ACL_RENAME: 38318845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 38328845Samw@Sun.COM resource1) != 0) { 38338845Samw@Sun.COM (void) no_memory(hdl); 38348845Samw@Sun.COM return (-1); 38358845Samw@Sun.COM } 38368845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 38378845Samw@Sun.COM resource2) != 0) { 38388845Samw@Sun.COM (void) no_memory(hdl); 38398845Samw@Sun.COM return (-1); 38408845Samw@Sun.COM } 38418845Samw@Sun.COM if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 38428845Samw@Sun.COM nvlist_free(nvlist); 38438845Samw@Sun.COM return (-1); 38448845Samw@Sun.COM } 38458845Samw@Sun.COM break; 38468845Samw@Sun.COM case ZFS_SMB_ACL_PURGE: 38478845Samw@Sun.COM break; 38488845Samw@Sun.COM default: 38498845Samw@Sun.COM return (-1); 38508845Samw@Sun.COM } 38518845Samw@Sun.COM error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 38528845Samw@Sun.COM if (nvlist) 38538845Samw@Sun.COM nvlist_free(nvlist); 38548845Samw@Sun.COM return (error); 38558845Samw@Sun.COM } 38568845Samw@Sun.COM 38578845Samw@Sun.COM int 38588845Samw@Sun.COM zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 38598845Samw@Sun.COM char *path, char *resource) 38608845Samw@Sun.COM { 38618845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 38628845Samw@Sun.COM resource, NULL)); 38638845Samw@Sun.COM } 38648845Samw@Sun.COM 38658845Samw@Sun.COM int 38668845Samw@Sun.COM zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 38678845Samw@Sun.COM char *path, char *resource) 38688845Samw@Sun.COM { 38698845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 38708845Samw@Sun.COM resource, NULL)); 38718845Samw@Sun.COM } 38728845Samw@Sun.COM 38738845Samw@Sun.COM int 38748845Samw@Sun.COM zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 38758845Samw@Sun.COM { 38768845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 38778845Samw@Sun.COM NULL, NULL)); 38788845Samw@Sun.COM } 38798845Samw@Sun.COM 38808845Samw@Sun.COM int 38818845Samw@Sun.COM zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 38828845Samw@Sun.COM char *oldname, char *newname) 38838845Samw@Sun.COM { 38848845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 38858845Samw@Sun.COM oldname, newname)); 38868845Samw@Sun.COM } 38879396SMatthew.Ahrens@Sun.COM 38889396SMatthew.Ahrens@Sun.COM int 38899396SMatthew.Ahrens@Sun.COM zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 38909396SMatthew.Ahrens@Sun.COM zfs_userspace_cb_t func, void *arg) 38919396SMatthew.Ahrens@Sun.COM { 38929396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 38939396SMatthew.Ahrens@Sun.COM int error; 38949396SMatthew.Ahrens@Sun.COM zfs_useracct_t buf[100]; 38959396SMatthew.Ahrens@Sun.COM 38969396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 38979396SMatthew.Ahrens@Sun.COM 38989396SMatthew.Ahrens@Sun.COM zc.zc_objset_type = type; 38999396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst = (uintptr_t)buf; 39009396SMatthew.Ahrens@Sun.COM 39019396SMatthew.Ahrens@Sun.COM /* CONSTCOND */ 39029396SMatthew.Ahrens@Sun.COM while (1) { 39039396SMatthew.Ahrens@Sun.COM zfs_useracct_t *zua = buf; 39049396SMatthew.Ahrens@Sun.COM 39059396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size = sizeof (buf); 39069396SMatthew.Ahrens@Sun.COM error = ioctl(zhp->zfs_hdl->libzfs_fd, 39079396SMatthew.Ahrens@Sun.COM ZFS_IOC_USERSPACE_MANY, &zc); 39089396SMatthew.Ahrens@Sun.COM if (error || zc.zc_nvlist_dst_size == 0) 39099396SMatthew.Ahrens@Sun.COM break; 39109396SMatthew.Ahrens@Sun.COM 39119396SMatthew.Ahrens@Sun.COM while (zc.zc_nvlist_dst_size > 0) { 39129554SMatthew.Ahrens@Sun.COM error = func(arg, zua->zu_domain, zua->zu_rid, 39139554SMatthew.Ahrens@Sun.COM zua->zu_space); 39149554SMatthew.Ahrens@Sun.COM if (error != 0) 39159554SMatthew.Ahrens@Sun.COM return (error); 39169396SMatthew.Ahrens@Sun.COM zua++; 39179396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 39189396SMatthew.Ahrens@Sun.COM } 39199396SMatthew.Ahrens@Sun.COM } 39209396SMatthew.Ahrens@Sun.COM 39219396SMatthew.Ahrens@Sun.COM return (error); 39229396SMatthew.Ahrens@Sun.COM } 392310242Schris.kirby@sun.com 392410242Schris.kirby@sun.com int 392510242Schris.kirby@sun.com zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 392611417SChris.Kirby@sun.com boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 392710242Schris.kirby@sun.com { 392810242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 392910242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 393010242Schris.kirby@sun.com 393110242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 393210242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 393310342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 393410342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 393510342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 393610242Schris.kirby@sun.com zc.zc_cookie = recursive; 393710342Schris.kirby@sun.com zc.zc_temphold = temphold; 393810242Schris.kirby@sun.com 393910242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 394010242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 394110242Schris.kirby@sun.com 394210242Schris.kirby@sun.com /* 394310242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 394410242Schris.kirby@sun.com * zc.zc_name. 394510242Schris.kirby@sun.com */ 394610242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 394710242Schris.kirby@sun.com "cannot hold '%s@%s'"), zc.zc_name, snapname); 394810242Schris.kirby@sun.com switch (errno) { 394910951SChris.Kirby@sun.com case E2BIG: 395010951SChris.Kirby@sun.com /* 395110951SChris.Kirby@sun.com * Temporary tags wind up having the ds object id 395210951SChris.Kirby@sun.com * prepended. So even if we passed the length check 395310951SChris.Kirby@sun.com * above, it's still possible for the tag to wind 395410951SChris.Kirby@sun.com * up being slightly too long. 395510951SChris.Kirby@sun.com */ 395610951SChris.Kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 395710242Schris.kirby@sun.com case ENOTSUP: 395810242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 395910242Schris.kirby@sun.com "pool must be upgraded")); 396010242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 396110242Schris.kirby@sun.com case EINVAL: 396210242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 396310242Schris.kirby@sun.com case EEXIST: 396410242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 396511417SChris.Kirby@sun.com case ENOENT: 396611417SChris.Kirby@sun.com if (enoent_ok) 396711417SChris.Kirby@sun.com return (0); 396811417SChris.Kirby@sun.com /* FALLTHROUGH */ 396910242Schris.kirby@sun.com default: 397010242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 397110242Schris.kirby@sun.com } 397210242Schris.kirby@sun.com } 397310242Schris.kirby@sun.com 397410242Schris.kirby@sun.com return (0); 397510242Schris.kirby@sun.com } 397610242Schris.kirby@sun.com 397710342Schris.kirby@sun.com struct hold_range_arg { 397810342Schris.kirby@sun.com zfs_handle_t *origin; 397910342Schris.kirby@sun.com const char *fromsnap; 398010342Schris.kirby@sun.com const char *tosnap; 398110342Schris.kirby@sun.com char lastsnapheld[ZFS_MAXNAMELEN]; 398210342Schris.kirby@sun.com const char *tag; 398310342Schris.kirby@sun.com boolean_t temphold; 398410342Schris.kirby@sun.com boolean_t seento; 398510342Schris.kirby@sun.com boolean_t seenfrom; 398610342Schris.kirby@sun.com boolean_t holding; 398710342Schris.kirby@sun.com }; 398810342Schris.kirby@sun.com 398910342Schris.kirby@sun.com static int 399010342Schris.kirby@sun.com zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 399110342Schris.kirby@sun.com { 399210342Schris.kirby@sun.com struct hold_range_arg *hra = arg; 399310342Schris.kirby@sun.com const char *thissnap; 399410342Schris.kirby@sun.com int error; 399510342Schris.kirby@sun.com 399610342Schris.kirby@sun.com thissnap = strchr(zfs_get_name(zhp), '@') + 1; 399710342Schris.kirby@sun.com 399810342Schris.kirby@sun.com if (hra->fromsnap && !hra->seenfrom && 399910342Schris.kirby@sun.com strcmp(hra->fromsnap, thissnap) == 0) 400010342Schris.kirby@sun.com hra->seenfrom = B_TRUE; 400110342Schris.kirby@sun.com 400210342Schris.kirby@sun.com /* snap is older or newer than the desired range, ignore it */ 400310342Schris.kirby@sun.com if (hra->seento || !hra->seenfrom) { 400410342Schris.kirby@sun.com zfs_close(zhp); 400510342Schris.kirby@sun.com return (0); 400610342Schris.kirby@sun.com } 400710342Schris.kirby@sun.com 400810342Schris.kirby@sun.com if (hra->holding) { 400911417SChris.Kirby@sun.com /* We could be racing with destroy, so ignore ENOENT. */ 401010342Schris.kirby@sun.com error = zfs_hold(hra->origin, thissnap, hra->tag, B_FALSE, 401111417SChris.Kirby@sun.com hra->temphold, B_TRUE); 401210342Schris.kirby@sun.com if (error == 0) { 401310342Schris.kirby@sun.com (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 401410342Schris.kirby@sun.com sizeof (hra->lastsnapheld)); 401510342Schris.kirby@sun.com } 401610342Schris.kirby@sun.com } else { 401710342Schris.kirby@sun.com error = zfs_release(hra->origin, thissnap, hra->tag, B_FALSE); 401810342Schris.kirby@sun.com } 401910342Schris.kirby@sun.com 402010342Schris.kirby@sun.com if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 402110342Schris.kirby@sun.com hra->seento = B_TRUE; 402210342Schris.kirby@sun.com 402310342Schris.kirby@sun.com zfs_close(zhp); 402410342Schris.kirby@sun.com return (error); 402510342Schris.kirby@sun.com } 402610342Schris.kirby@sun.com 402710342Schris.kirby@sun.com /* 402810342Schris.kirby@sun.com * Add a user hold on the set of snapshots starting with fromsnap up to 402910342Schris.kirby@sun.com * and including tosnap. If we're unable to to acquire a particular hold, 403010342Schris.kirby@sun.com * undo any holds up to that point. 403110342Schris.kirby@sun.com */ 403210342Schris.kirby@sun.com int 403310342Schris.kirby@sun.com zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 403410342Schris.kirby@sun.com const char *tag, boolean_t temphold) 403510342Schris.kirby@sun.com { 403610342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 403710342Schris.kirby@sun.com int error; 403810342Schris.kirby@sun.com 403910342Schris.kirby@sun.com arg.origin = zhp; 404010342Schris.kirby@sun.com arg.fromsnap = fromsnap; 404110342Schris.kirby@sun.com arg.tosnap = tosnap; 404210342Schris.kirby@sun.com arg.tag = tag; 404310342Schris.kirby@sun.com arg.temphold = temphold; 404410342Schris.kirby@sun.com arg.holding = B_TRUE; 404510342Schris.kirby@sun.com 404610342Schris.kirby@sun.com error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 404710342Schris.kirby@sun.com 404810342Schris.kirby@sun.com /* 404910342Schris.kirby@sun.com * Make sure we either hold the entire range or none. 405010342Schris.kirby@sun.com */ 405110342Schris.kirby@sun.com if (error && arg.lastsnapheld[0] != '\0') { 405210342Schris.kirby@sun.com (void) zfs_release_range(zhp, fromsnap, 405310342Schris.kirby@sun.com (const char *)arg.lastsnapheld, tag); 405410342Schris.kirby@sun.com } 405510342Schris.kirby@sun.com return (error); 405610342Schris.kirby@sun.com } 405710342Schris.kirby@sun.com 405810242Schris.kirby@sun.com int 405910242Schris.kirby@sun.com zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 406010242Schris.kirby@sun.com boolean_t recursive) 406110242Schris.kirby@sun.com { 406210242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 406310242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 406410242Schris.kirby@sun.com 406510242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 406610242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 406710342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 406810342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 406910342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 407010242Schris.kirby@sun.com zc.zc_cookie = recursive; 407110242Schris.kirby@sun.com 407210242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 407310242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 407410242Schris.kirby@sun.com 407510242Schris.kirby@sun.com /* 407610242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 407710242Schris.kirby@sun.com * zc.zc_name. 407810242Schris.kirby@sun.com */ 407910242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 408010242Schris.kirby@sun.com "cannot release '%s@%s'"), zc.zc_name, snapname); 408110242Schris.kirby@sun.com switch (errno) { 408210242Schris.kirby@sun.com case ESRCH: 408310242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 408410242Schris.kirby@sun.com case ENOTSUP: 408510242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 408610242Schris.kirby@sun.com "pool must be upgraded")); 408710242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 408810242Schris.kirby@sun.com case EINVAL: 408910242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 409010242Schris.kirby@sun.com default: 409110242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 409210242Schris.kirby@sun.com } 409310242Schris.kirby@sun.com } 409410242Schris.kirby@sun.com 409510242Schris.kirby@sun.com return (0); 409610242Schris.kirby@sun.com } 409710342Schris.kirby@sun.com 409810342Schris.kirby@sun.com /* 409910342Schris.kirby@sun.com * Release a user hold from the set of snapshots starting with fromsnap 410010342Schris.kirby@sun.com * up to and including tosnap. 410110342Schris.kirby@sun.com */ 410210342Schris.kirby@sun.com int 410310342Schris.kirby@sun.com zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 410410342Schris.kirby@sun.com const char *tag) 410510342Schris.kirby@sun.com { 410610342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 410710342Schris.kirby@sun.com 410810342Schris.kirby@sun.com arg.origin = zhp; 410910342Schris.kirby@sun.com arg.fromsnap = fromsnap; 411010342Schris.kirby@sun.com arg.tosnap = tosnap; 411110342Schris.kirby@sun.com arg.tag = tag; 411210342Schris.kirby@sun.com 411310342Schris.kirby@sun.com return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 411410342Schris.kirby@sun.com } 4115*11449SEric.Taylor@Sun.COM 4116*11449SEric.Taylor@Sun.COM uint64_t 4117*11449SEric.Taylor@Sun.COM zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4118*11449SEric.Taylor@Sun.COM { 4119*11449SEric.Taylor@Sun.COM uint64_t numdb; 4120*11449SEric.Taylor@Sun.COM uint64_t nblocks, volblocksize; 4121*11449SEric.Taylor@Sun.COM int ncopies; 4122*11449SEric.Taylor@Sun.COM char *strval; 4123*11449SEric.Taylor@Sun.COM 4124*11449SEric.Taylor@Sun.COM if (nvlist_lookup_string(props, 4125*11449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4126*11449SEric.Taylor@Sun.COM ncopies = atoi(strval); 4127*11449SEric.Taylor@Sun.COM else 4128*11449SEric.Taylor@Sun.COM ncopies = 1; 4129*11449SEric.Taylor@Sun.COM if (nvlist_lookup_uint64(props, 4130*11449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4131*11449SEric.Taylor@Sun.COM &volblocksize) != 0) 4132*11449SEric.Taylor@Sun.COM volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4133*11449SEric.Taylor@Sun.COM nblocks = volsize/volblocksize; 4134*11449SEric.Taylor@Sun.COM /* start with metadnode L0-L6 */ 4135*11449SEric.Taylor@Sun.COM numdb = 7; 4136*11449SEric.Taylor@Sun.COM /* calculate number of indirects */ 4137*11449SEric.Taylor@Sun.COM while (nblocks > 1) { 4138*11449SEric.Taylor@Sun.COM nblocks += DNODES_PER_LEVEL - 1; 4139*11449SEric.Taylor@Sun.COM nblocks /= DNODES_PER_LEVEL; 4140*11449SEric.Taylor@Sun.COM numdb += nblocks; 4141*11449SEric.Taylor@Sun.COM } 4142*11449SEric.Taylor@Sun.COM numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4143*11449SEric.Taylor@Sun.COM volsize *= ncopies; 4144*11449SEric.Taylor@Sun.COM /* 4145*11449SEric.Taylor@Sun.COM * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4146*11449SEric.Taylor@Sun.COM * compressed, but in practice they compress down to about 4147*11449SEric.Taylor@Sun.COM * 1100 bytes 4148*11449SEric.Taylor@Sun.COM */ 4149*11449SEric.Taylor@Sun.COM numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4150*11449SEric.Taylor@Sun.COM volsize += numdb; 4151*11449SEric.Taylor@Sun.COM return (volsize); 4152*11449SEric.Taylor@Sun.COM } 4153