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 4911449SEric.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 94110972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 94210972SRic.Aleshire@Sun.COM { 94310972SRic.Aleshire@Sun.COM /* 94410972SRic.Aleshire@Sun.COM * Verify the mlslabel string and convert to 94510972SRic.Aleshire@Sun.COM * internal hex label string. 94610972SRic.Aleshire@Sun.COM */ 94710972SRic.Aleshire@Sun.COM 94810972SRic.Aleshire@Sun.COM m_label_t *new_sl; 94910972SRic.Aleshire@Sun.COM char *hex = NULL; /* internal label string */ 95010972SRic.Aleshire@Sun.COM 95110972SRic.Aleshire@Sun.COM /* Default value is already OK. */ 95210972SRic.Aleshire@Sun.COM if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 95310972SRic.Aleshire@Sun.COM break; 95410972SRic.Aleshire@Sun.COM 95510972SRic.Aleshire@Sun.COM /* Verify the label can be converted to binary form */ 95610972SRic.Aleshire@Sun.COM if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 95710972SRic.Aleshire@Sun.COM (str_to_label(strval, &new_sl, MAC_LABEL, 95810972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1)) { 95910972SRic.Aleshire@Sun.COM goto badlabel; 96010972SRic.Aleshire@Sun.COM } 96110972SRic.Aleshire@Sun.COM 96210972SRic.Aleshire@Sun.COM /* Now translate to hex internal label string */ 96310972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &hex, M_INTERNAL, 96410972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 96510972SRic.Aleshire@Sun.COM if (hex) 96610972SRic.Aleshire@Sun.COM free(hex); 96710972SRic.Aleshire@Sun.COM goto badlabel; 96810972SRic.Aleshire@Sun.COM } 96910972SRic.Aleshire@Sun.COM m_label_free(new_sl); 97010972SRic.Aleshire@Sun.COM 97110972SRic.Aleshire@Sun.COM /* If string is already in internal form, we're done. */ 97210972SRic.Aleshire@Sun.COM if (strcmp(strval, hex) == 0) { 97310972SRic.Aleshire@Sun.COM free(hex); 97410972SRic.Aleshire@Sun.COM break; 97510972SRic.Aleshire@Sun.COM } 97610972SRic.Aleshire@Sun.COM 97710972SRic.Aleshire@Sun.COM /* Replace the label string with the internal form. */ 97810984SRic.Aleshire@Sun.COM (void) nvlist_remove(ret, zfs_prop_to_name(prop), 97910972SRic.Aleshire@Sun.COM DATA_TYPE_STRING); 98010972SRic.Aleshire@Sun.COM verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 98110972SRic.Aleshire@Sun.COM hex) == 0); 98210972SRic.Aleshire@Sun.COM free(hex); 98310972SRic.Aleshire@Sun.COM 98410972SRic.Aleshire@Sun.COM break; 98510972SRic.Aleshire@Sun.COM 98610972SRic.Aleshire@Sun.COM badlabel: 98710972SRic.Aleshire@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 98810972SRic.Aleshire@Sun.COM "invalid mlslabel '%s'"), strval); 98910972SRic.Aleshire@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 99010972SRic.Aleshire@Sun.COM m_label_free(new_sl); /* OK if null */ 99110972SRic.Aleshire@Sun.COM goto error; 99210972SRic.Aleshire@Sun.COM 99310972SRic.Aleshire@Sun.COM } 99410972SRic.Aleshire@Sun.COM 9952676Seschrock case ZFS_PROP_MOUNTPOINT: 9964778Srm160521 { 9974778Srm160521 namecheck_err_t why; 9984778Srm160521 9992676Seschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 10002676Seschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 10012676Seschrock break; 10022676Seschrock 10034778Srm160521 if (mountpoint_namecheck(strval, &why)) { 10044778Srm160521 switch (why) { 10054778Srm160521 case NAME_ERR_LEADING_SLASH: 10064778Srm160521 zfs_error_aux(hdl, 10074778Srm160521 dgettext(TEXT_DOMAIN, 10084778Srm160521 "'%s' must be an absolute path, " 10094778Srm160521 "'none', or 'legacy'"), propname); 10104778Srm160521 break; 10114778Srm160521 case NAME_ERR_TOOLONG: 10124778Srm160521 zfs_error_aux(hdl, 10134778Srm160521 dgettext(TEXT_DOMAIN, 10144778Srm160521 "component of '%s' is too long"), 10154778Srm160521 propname); 10164778Srm160521 break; 10174778Srm160521 } 10182676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10192676Seschrock goto error; 1020789Sahrens } 10214778Srm160521 } 10224778Srm160521 10233126Sahl /*FALLTHRU*/ 10243126Sahl 10255331Samw case ZFS_PROP_SHARESMB: 10263126Sahl case ZFS_PROP_SHARENFS: 10273126Sahl /* 10285331Samw * For the mountpoint and sharenfs or sharesmb 10295331Samw * properties, check if it can be set in a 10305331Samw * global/non-global zone based on 10313126Sahl * the zoned property value: 10323126Sahl * 10333126Sahl * global zone non-global zone 10343126Sahl * -------------------------------------------------- 10353126Sahl * zoned=on mountpoint (no) mountpoint (yes) 10363126Sahl * sharenfs (no) sharenfs (no) 10375331Samw * sharesmb (no) sharesmb (no) 10383126Sahl * 10393126Sahl * zoned=off mountpoint (yes) N/A 10403126Sahl * sharenfs (yes) 10415331Samw * sharesmb (yes) 10423126Sahl */ 10432676Seschrock if (zoned) { 10442676Seschrock if (getzoneid() == GLOBAL_ZONEID) { 10452676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10462676Seschrock "'%s' cannot be set on " 10472676Seschrock "dataset in a non-global zone"), 10482676Seschrock propname); 10492676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10502676Seschrock errbuf); 10512676Seschrock goto error; 10525331Samw } else if (prop == ZFS_PROP_SHARENFS || 10535331Samw prop == ZFS_PROP_SHARESMB) { 10542676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10552676Seschrock "'%s' cannot be set in " 10562676Seschrock "a non-global zone"), propname); 10572676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10582676Seschrock errbuf); 10592676Seschrock goto error; 10602676Seschrock } 10612676Seschrock } else if (getzoneid() != GLOBAL_ZONEID) { 10622676Seschrock /* 10632676Seschrock * If zoned property is 'off', this must be in 10649396SMatthew.Ahrens@Sun.COM * a global zone. If not, something is wrong. 10652676Seschrock */ 10662676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10672676Seschrock "'%s' cannot be set while dataset " 10682676Seschrock "'zoned' property is set"), propname); 10692676Seschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 10702676Seschrock goto error; 10712676Seschrock } 10723126Sahl 10734180Sdougm /* 10744180Sdougm * At this point, it is legitimate to set the 10754180Sdougm * property. Now we want to make sure that the 10764180Sdougm * property value is valid if it is sharenfs. 10774180Sdougm */ 10785331Samw if ((prop == ZFS_PROP_SHARENFS || 10795331Samw prop == ZFS_PROP_SHARESMB) && 10804217Seschrock strcmp(strval, "on") != 0 && 10814217Seschrock strcmp(strval, "off") != 0) { 10825331Samw zfs_share_proto_t proto; 10835331Samw 10845331Samw if (prop == ZFS_PROP_SHARESMB) 10855331Samw proto = PROTO_SMB; 10865331Samw else 10875331Samw proto = PROTO_NFS; 10884180Sdougm 10894180Sdougm /* 10905331Samw * Must be an valid sharing protocol 10915331Samw * option string so init the libshare 10925331Samw * in order to enable the parser and 10935331Samw * then parse the options. We use the 10945331Samw * control API since we don't care about 10955331Samw * the current configuration and don't 10964180Sdougm * want the overhead of loading it 10974180Sdougm * until we actually do something. 10984180Sdougm */ 10994180Sdougm 11004217Seschrock if (zfs_init_libshare(hdl, 11014217Seschrock SA_INIT_CONTROL_API) != SA_OK) { 11024217Seschrock /* 11034217Seschrock * An error occurred so we can't do 11044217Seschrock * anything 11054217Seschrock */ 11064217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11074217Seschrock "'%s' cannot be set: problem " 11084217Seschrock "in share initialization"), 11094217Seschrock propname); 11104217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11114217Seschrock errbuf); 11124217Seschrock goto error; 11134217Seschrock } 11144217Seschrock 11155331Samw if (zfs_parse_options(strval, proto) != SA_OK) { 11164217Seschrock /* 11174217Seschrock * There was an error in parsing so 11184217Seschrock * deal with it by issuing an error 11194217Seschrock * message and leaving after 11204217Seschrock * uninitializing the the libshare 11214217Seschrock * interface. 11224217Seschrock */ 11234217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11244217Seschrock "'%s' cannot be set to invalid " 11254217Seschrock "options"), propname); 11264217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11274217Seschrock errbuf); 11284217Seschrock zfs_uninit_libshare(hdl); 11294217Seschrock goto error; 11304217Seschrock } 11314180Sdougm zfs_uninit_libshare(hdl); 11324180Sdougm } 11334180Sdougm 11343126Sahl break; 11355331Samw case ZFS_PROP_UTF8ONLY: 11365331Samw chosen_utf = (int)intval; 11375331Samw break; 11385331Samw case ZFS_PROP_NORMALIZE: 11395331Samw chosen_normal = (int)intval; 11405331Samw break; 11412676Seschrock } 11422676Seschrock 11432676Seschrock /* 11442676Seschrock * For changes to existing volumes, we have some additional 11452676Seschrock * checks to enforce. 11462676Seschrock */ 11472676Seschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 11482676Seschrock uint64_t volsize = zfs_prop_get_int(zhp, 11492676Seschrock ZFS_PROP_VOLSIZE); 11502676Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 11512676Seschrock ZFS_PROP_VOLBLOCKSIZE); 11522676Seschrock char buf[64]; 11532676Seschrock 11542676Seschrock switch (prop) { 11552676Seschrock case ZFS_PROP_RESERVATION: 11565378Sck153898 case ZFS_PROP_REFRESERVATION: 11572676Seschrock if (intval > volsize) { 11582676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11592676Seschrock "'%s' is greater than current " 11602676Seschrock "volume size"), propname); 11612676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11622676Seschrock errbuf); 11632676Seschrock goto error; 11642676Seschrock } 11652676Seschrock break; 11662676Seschrock 11672676Seschrock case ZFS_PROP_VOLSIZE: 11682676Seschrock if (intval % blocksize != 0) { 11692676Seschrock zfs_nicenum(blocksize, buf, 11702676Seschrock sizeof (buf)); 11712676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11722676Seschrock "'%s' must be a multiple of " 11732676Seschrock "volume block size (%s)"), 11742676Seschrock propname, buf); 11752676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11762676Seschrock errbuf); 11772676Seschrock goto error; 11782676Seschrock } 11792676Seschrock 11802676Seschrock if (intval == 0) { 11812676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11822676Seschrock "'%s' cannot be zero"), 11832676Seschrock propname); 11842676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11852676Seschrock errbuf); 11862676Seschrock goto error; 1187789Sahrens } 11883126Sahl break; 1189789Sahrens } 1190789Sahrens } 1191789Sahrens } 1192789Sahrens 11932676Seschrock /* 11945331Samw * If normalization was chosen, but no UTF8 choice was made, 11955331Samw * enforce rejection of non-UTF8 names. 11965331Samw * 11975331Samw * If normalization was chosen, but rejecting non-UTF8 names 11985331Samw * was explicitly not chosen, it is an error. 11995331Samw */ 12005498Stimh if (chosen_normal > 0 && chosen_utf < 0) { 12015331Samw if (nvlist_add_uint64(ret, 12025331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 12035331Samw (void) no_memory(hdl); 12045331Samw goto error; 12055331Samw } 12065498Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 12075331Samw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12085331Samw "'%s' must be set 'on' if normalization chosen"), 12095331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 12105331Samw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 12115331Samw goto error; 12125331Samw } 12135331Samw 12145331Samw /* 12152676Seschrock * If this is an existing volume, and someone is setting the volsize, 12162676Seschrock * make sure that it matches the reservation, or add it if necessary. 12172676Seschrock */ 12182676Seschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 12192676Seschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 12202676Seschrock &intval) == 0) { 12212676Seschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 12222676Seschrock ZFS_PROP_VOLSIZE); 12235481Sck153898 uint64_t old_reservation; 12242676Seschrock uint64_t new_reservation; 12255481Sck153898 zfs_prop_t resv_prop; 12265713Srm160521 12275713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 12285481Sck153898 goto error; 12295481Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 12302676Seschrock 12312676Seschrock if (old_volsize == old_reservation && 12325481Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 12332676Seschrock &new_reservation) != 0) { 12342676Seschrock if (nvlist_add_uint64(ret, 12355481Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 12362676Seschrock (void) no_memory(hdl); 12372676Seschrock goto error; 12382676Seschrock } 12392676Seschrock } 12402676Seschrock } 12412676Seschrock return (ret); 12422676Seschrock 12432676Seschrock error: 12442676Seschrock nvlist_free(ret); 12452676Seschrock return (NULL); 1246789Sahrens } 1247789Sahrens 124811022STom.Erickson@Sun.COM void 124911022STom.Erickson@Sun.COM zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 125011022STom.Erickson@Sun.COM char *errbuf) 125111022STom.Erickson@Sun.COM { 125211022STom.Erickson@Sun.COM switch (err) { 125311022STom.Erickson@Sun.COM 125411022STom.Erickson@Sun.COM case ENOSPC: 125511022STom.Erickson@Sun.COM /* 125611022STom.Erickson@Sun.COM * For quotas and reservations, ENOSPC indicates 125711022STom.Erickson@Sun.COM * something different; setting a quota or reservation 125811022STom.Erickson@Sun.COM * doesn't use any disk space. 125911022STom.Erickson@Sun.COM */ 126011022STom.Erickson@Sun.COM switch (prop) { 126111022STom.Erickson@Sun.COM case ZFS_PROP_QUOTA: 126211022STom.Erickson@Sun.COM case ZFS_PROP_REFQUOTA: 126311022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 126411022STom.Erickson@Sun.COM "size is less than current used or " 126511022STom.Erickson@Sun.COM "reserved space")); 126611022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 126711022STom.Erickson@Sun.COM break; 126811022STom.Erickson@Sun.COM 126911022STom.Erickson@Sun.COM case ZFS_PROP_RESERVATION: 127011022STom.Erickson@Sun.COM case ZFS_PROP_REFRESERVATION: 127111022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127211022STom.Erickson@Sun.COM "size is greater than available space")); 127311022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 127411022STom.Erickson@Sun.COM break; 127511022STom.Erickson@Sun.COM 127611022STom.Erickson@Sun.COM default: 127711022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 127811022STom.Erickson@Sun.COM break; 127911022STom.Erickson@Sun.COM } 128011022STom.Erickson@Sun.COM break; 128111022STom.Erickson@Sun.COM 128211022STom.Erickson@Sun.COM case EBUSY: 128311022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, EBUSY, errbuf); 128411022STom.Erickson@Sun.COM break; 128511022STom.Erickson@Sun.COM 128611022STom.Erickson@Sun.COM case EROFS: 128711022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 128811022STom.Erickson@Sun.COM break; 128911022STom.Erickson@Sun.COM 129011022STom.Erickson@Sun.COM case ENOTSUP: 129111022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 129211022STom.Erickson@Sun.COM "pool and or dataset must be upgraded to set this " 129311022STom.Erickson@Sun.COM "property or value")); 129411022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 129511022STom.Erickson@Sun.COM break; 129611022STom.Erickson@Sun.COM 129711022STom.Erickson@Sun.COM case ERANGE: 129811022STom.Erickson@Sun.COM if (prop == ZFS_PROP_COMPRESSION) { 129911022STom.Erickson@Sun.COM (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130011022STom.Erickson@Sun.COM "property setting is not allowed on " 130111022STom.Erickson@Sun.COM "bootable datasets")); 130211022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 130311022STom.Erickson@Sun.COM } else { 130411022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 130511022STom.Erickson@Sun.COM } 130611022STom.Erickson@Sun.COM break; 130711022STom.Erickson@Sun.COM 1308*11876SJames.Dunham@Sun.COM case EINVAL: 1309*11876SJames.Dunham@Sun.COM if (prop == ZPROP_INVAL) { 1310*11876SJames.Dunham@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1311*11876SJames.Dunham@Sun.COM } else { 1312*11876SJames.Dunham@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 1313*11876SJames.Dunham@Sun.COM } 1314*11876SJames.Dunham@Sun.COM break; 1315*11876SJames.Dunham@Sun.COM 131611022STom.Erickson@Sun.COM case EOVERFLOW: 131711022STom.Erickson@Sun.COM /* 131811022STom.Erickson@Sun.COM * This platform can't address a volume this big. 131911022STom.Erickson@Sun.COM */ 132011022STom.Erickson@Sun.COM #ifdef _ILP32 132111022STom.Erickson@Sun.COM if (prop == ZFS_PROP_VOLSIZE) { 132211022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132311022STom.Erickson@Sun.COM break; 132411022STom.Erickson@Sun.COM } 132511022STom.Erickson@Sun.COM #endif 132611022STom.Erickson@Sun.COM /* FALLTHROUGH */ 132711022STom.Erickson@Sun.COM default: 132811022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 132911022STom.Erickson@Sun.COM } 133011022STom.Erickson@Sun.COM } 133111022STom.Erickson@Sun.COM 1332789Sahrens /* 1333789Sahrens * Given a property name and value, set the property for the given dataset. 1334789Sahrens */ 1335789Sahrens int 13362676Seschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1337789Sahrens { 1338789Sahrens zfs_cmd_t zc = { 0 }; 13392676Seschrock int ret = -1; 13402676Seschrock prop_changelist_t *cl = NULL; 13412082Seschrock char errbuf[1024]; 13422082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 13432676Seschrock nvlist_t *nvl = NULL, *realprops; 13442676Seschrock zfs_prop_t prop; 13457509SMark.Musante@Sun.COM boolean_t do_prefix; 13467509SMark.Musante@Sun.COM uint64_t idx; 13472082Seschrock 13482082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 13492676Seschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 13502082Seschrock zhp->zfs_name); 13512082Seschrock 13522676Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 13532676Seschrock nvlist_add_string(nvl, propname, propval) != 0) { 13542676Seschrock (void) no_memory(hdl); 13552676Seschrock goto error; 1356789Sahrens } 1357789Sahrens 13587184Stimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 13592676Seschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 13602676Seschrock goto error; 13615094Slling 13622676Seschrock nvlist_free(nvl); 13632676Seschrock nvl = realprops; 13642676Seschrock 13652676Seschrock prop = zfs_name_to_prop(propname); 13662676Seschrock 13677366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 13682676Seschrock goto error; 1369789Sahrens 1370789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 13712082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13722082Seschrock "child dataset with inherited mountpoint is used " 13732082Seschrock "in a non-global zone")); 13742082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1375789Sahrens goto error; 1376789Sahrens } 1377789Sahrens 13787509SMark.Musante@Sun.COM /* 13797509SMark.Musante@Sun.COM * If the dataset's canmount property is being set to noauto, 13807509SMark.Musante@Sun.COM * then we want to prevent unmounting & remounting it. 13817509SMark.Musante@Sun.COM */ 13827509SMark.Musante@Sun.COM do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13837509SMark.Musante@Sun.COM (zprop_string_to_index(prop, propval, &idx, 13847509SMark.Musante@Sun.COM ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 13856168Shs24103 13866168Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 13877509SMark.Musante@Sun.COM goto error; 1388789Sahrens 1389789Sahrens /* 1390789Sahrens * Execute the corresponding ioctl() to set this property. 1391789Sahrens */ 1392789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1393789Sahrens 13945094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 13952676Seschrock goto error; 13962676Seschrock 13974543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 13988845Samw@Sun.COM 1399789Sahrens if (ret != 0) { 140011022STom.Erickson@Sun.COM zfs_setprop_error(hdl, prop, errno, errbuf); 1401789Sahrens } else { 14026168Shs24103 if (do_prefix) 14036168Shs24103 ret = changelist_postfix(cl); 14046168Shs24103 1405789Sahrens /* 1406789Sahrens * Refresh the statistics so the new property value 1407789Sahrens * is reflected. 1408789Sahrens */ 14096168Shs24103 if (ret == 0) 14102676Seschrock (void) get_stats(zhp); 1411789Sahrens } 1412789Sahrens 1413789Sahrens error: 14142676Seschrock nvlist_free(nvl); 14152676Seschrock zcmd_free_nvlists(&zc); 14162676Seschrock if (cl) 14172676Seschrock changelist_free(cl); 1418789Sahrens return (ret); 1419789Sahrens } 1420789Sahrens 1421789Sahrens /* 142211022STom.Erickson@Sun.COM * Given a property, inherit the value from the parent dataset, or if received 142311022STom.Erickson@Sun.COM * is TRUE, revert to the received value, if any. 1424789Sahrens */ 1425789Sahrens int 142611022STom.Erickson@Sun.COM zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1427789Sahrens { 1428789Sahrens zfs_cmd_t zc = { 0 }; 1429789Sahrens int ret; 1430789Sahrens prop_changelist_t *cl; 14312082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 14322082Seschrock char errbuf[1024]; 14332676Seschrock zfs_prop_t prop; 14342082Seschrock 14352082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 14362082Seschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1437789Sahrens 143811022STom.Erickson@Sun.COM zc.zc_cookie = received; 14395094Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 14402676Seschrock /* 14412676Seschrock * For user properties, the amount of work we have to do is very 14422676Seschrock * small, so just do it here. 14432676Seschrock */ 14442676Seschrock if (!zfs_prop_user(propname)) { 14452676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14462676Seschrock "invalid property")); 14472676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 14482676Seschrock } 14492676Seschrock 14502676Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14512676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 14522676Seschrock 14534849Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 14542676Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 14552676Seschrock 14562676Seschrock return (0); 14572676Seschrock } 14582676Seschrock 1459789Sahrens /* 1460789Sahrens * Verify that this property is inheritable. 1461789Sahrens */ 14622082Seschrock if (zfs_prop_readonly(prop)) 14632082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 14642082Seschrock 146511022STom.Erickson@Sun.COM if (!zfs_prop_inheritable(prop) && !received) 14662082Seschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1467789Sahrens 1468789Sahrens /* 1469789Sahrens * Check to see if the value applies to this type 1470789Sahrens */ 14712082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 14722082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1473789Sahrens 14743443Srm160521 /* 14753443Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 14763443Srm160521 */ 14773443Srm160521 propname = zfs_prop_to_name(prop); 1478789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14792676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1480789Sahrens 1481789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1482789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 14832082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14842082Seschrock "dataset is used in a non-global zone")); 14852082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1486789Sahrens } 1487789Sahrens 1488789Sahrens /* 1489789Sahrens * Determine datasets which will be affected by this change, if any. 1490789Sahrens */ 14917366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1492789Sahrens return (-1); 1493789Sahrens 1494789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 14952082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14962082Seschrock "child dataset with inherited mountpoint is used " 14972082Seschrock "in a non-global zone")); 14982082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1499789Sahrens goto error; 1500789Sahrens } 1501789Sahrens 1502789Sahrens if ((ret = changelist_prefix(cl)) != 0) 1503789Sahrens goto error; 1504789Sahrens 15054849Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 15062082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1507789Sahrens } else { 1508789Sahrens 15092169Snd150628 if ((ret = changelist_postfix(cl)) != 0) 1510789Sahrens goto error; 1511789Sahrens 1512789Sahrens /* 1513789Sahrens * Refresh the statistics so the new property is reflected. 1514789Sahrens */ 1515789Sahrens (void) get_stats(zhp); 1516789Sahrens } 1517789Sahrens 1518789Sahrens error: 1519789Sahrens changelist_free(cl); 1520789Sahrens return (ret); 1521789Sahrens } 1522789Sahrens 1523789Sahrens /* 15241356Seschrock * True DSL properties are stored in an nvlist. The following two functions 15251356Seschrock * extract them appropriately. 15261356Seschrock */ 15271356Seschrock static uint64_t 15281356Seschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15291356Seschrock { 15301356Seschrock nvlist_t *nv; 15311356Seschrock uint64_t value; 15321356Seschrock 15332885Sahrens *source = NULL; 15341356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15351356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15365094Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 15375094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15381356Seschrock } else { 15398802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15408802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15411356Seschrock value = zfs_prop_default_numeric(prop); 15421356Seschrock *source = ""; 15431356Seschrock } 15441356Seschrock 15451356Seschrock return (value); 15461356Seschrock } 15471356Seschrock 15481356Seschrock static char * 15491356Seschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15501356Seschrock { 15511356Seschrock nvlist_t *nv; 15521356Seschrock char *value; 15531356Seschrock 15542885Sahrens *source = NULL; 15551356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15561356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15575094Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 15585094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15591356Seschrock } else { 15608802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15618802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15621356Seschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15631356Seschrock value = ""; 15641356Seschrock *source = ""; 15651356Seschrock } 15661356Seschrock 15671356Seschrock return (value); 15681356Seschrock } 15691356Seschrock 157011022STom.Erickson@Sun.COM static boolean_t 157111022STom.Erickson@Sun.COM zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157211022STom.Erickson@Sun.COM { 157311022STom.Erickson@Sun.COM return (zhp->zfs_props == zhp->zfs_recvd_props); 157411022STom.Erickson@Sun.COM } 157511022STom.Erickson@Sun.COM 157611022STom.Erickson@Sun.COM static void 157711022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 157811022STom.Erickson@Sun.COM { 157911022STom.Erickson@Sun.COM *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158011022STom.Erickson@Sun.COM zhp->zfs_props = zhp->zfs_recvd_props; 158111022STom.Erickson@Sun.COM } 158211022STom.Erickson@Sun.COM 158311022STom.Erickson@Sun.COM static void 158411022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158511022STom.Erickson@Sun.COM { 158611022STom.Erickson@Sun.COM zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 158711022STom.Erickson@Sun.COM *cookie = 0; 158811022STom.Erickson@Sun.COM } 158911022STom.Erickson@Sun.COM 15901356Seschrock /* 1591789Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1592789Sahrens * zfs_prop_get_int() are built using this interface. 1593789Sahrens * 1594789Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1595789Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1596789Sahrens * If they differ from the on-disk values, report the current values and mark 1597789Sahrens * the source "temporary". 1598789Sahrens */ 15992082Seschrock static int 16005094Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 16012082Seschrock char **source, uint64_t *val) 1602789Sahrens { 16035147Srm160521 zfs_cmd_t zc = { 0 }; 16045592Stimh nvlist_t *zplprops = NULL; 1605789Sahrens struct mnttab mnt; 16063265Sahrens char *mntopt_on = NULL; 16073265Sahrens char *mntopt_off = NULL; 160811022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1609789Sahrens 1610789Sahrens *source = NULL; 1611789Sahrens 16123265Sahrens switch (prop) { 16133265Sahrens case ZFS_PROP_ATIME: 16143265Sahrens mntopt_on = MNTOPT_ATIME; 16153265Sahrens mntopt_off = MNTOPT_NOATIME; 16163265Sahrens break; 16173265Sahrens 16183265Sahrens case ZFS_PROP_DEVICES: 16193265Sahrens mntopt_on = MNTOPT_DEVICES; 16203265Sahrens mntopt_off = MNTOPT_NODEVICES; 16213265Sahrens break; 16223265Sahrens 16233265Sahrens case ZFS_PROP_EXEC: 16243265Sahrens mntopt_on = MNTOPT_EXEC; 16253265Sahrens mntopt_off = MNTOPT_NOEXEC; 16263265Sahrens break; 16273265Sahrens 16283265Sahrens case ZFS_PROP_READONLY: 16293265Sahrens mntopt_on = MNTOPT_RO; 16303265Sahrens mntopt_off = MNTOPT_RW; 16313265Sahrens break; 16323265Sahrens 16333265Sahrens case ZFS_PROP_SETUID: 16343265Sahrens mntopt_on = MNTOPT_SETUID; 16353265Sahrens mntopt_off = MNTOPT_NOSETUID; 16363265Sahrens break; 16373265Sahrens 16383265Sahrens case ZFS_PROP_XATTR: 16393265Sahrens mntopt_on = MNTOPT_XATTR; 16403265Sahrens mntopt_off = MNTOPT_NOXATTR; 16413265Sahrens break; 16425331Samw 16435331Samw case ZFS_PROP_NBMAND: 16445331Samw mntopt_on = MNTOPT_NBMAND; 16455331Samw mntopt_off = MNTOPT_NONBMAND; 16465331Samw break; 16473265Sahrens } 16483265Sahrens 16492474Seschrock /* 16502474Seschrock * Because looking up the mount options is potentially expensive 16512474Seschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16522474Seschrock * we're looking up a property which requires its presence. 16532474Seschrock */ 16542474Seschrock if (!zhp->zfs_mntcheck && 16553265Sahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 16568228SEric.Taylor@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 16578228SEric.Taylor@Sun.COM struct mnttab entry; 16588228SEric.Taylor@Sun.COM 16598228SEric.Taylor@Sun.COM if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 16608228SEric.Taylor@Sun.COM zhp->zfs_mntopts = zfs_strdup(hdl, 16613265Sahrens entry.mnt_mntopts); 16623265Sahrens if (zhp->zfs_mntopts == NULL) 16633265Sahrens return (-1); 16643265Sahrens } 16652474Seschrock 16662474Seschrock zhp->zfs_mntcheck = B_TRUE; 16672474Seschrock } 16682474Seschrock 1669789Sahrens if (zhp->zfs_mntopts == NULL) 1670789Sahrens mnt.mnt_mntopts = ""; 1671789Sahrens else 1672789Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1673789Sahrens 1674789Sahrens switch (prop) { 1675789Sahrens case ZFS_PROP_ATIME: 16763265Sahrens case ZFS_PROP_DEVICES: 16773265Sahrens case ZFS_PROP_EXEC: 16783265Sahrens case ZFS_PROP_READONLY: 16793265Sahrens case ZFS_PROP_SETUID: 16803265Sahrens case ZFS_PROP_XATTR: 16815331Samw case ZFS_PROP_NBMAND: 16822082Seschrock *val = getprop_uint64(zhp, prop, source); 16832082Seschrock 168411022STom.Erickson@Sun.COM if (received) 168511022STom.Erickson@Sun.COM break; 168611022STom.Erickson@Sun.COM 16873265Sahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 16882082Seschrock *val = B_TRUE; 1689789Sahrens if (src) 16905094Slling *src = ZPROP_SRC_TEMPORARY; 16913265Sahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 16922082Seschrock *val = B_FALSE; 1693789Sahrens if (src) 16945094Slling *src = ZPROP_SRC_TEMPORARY; 1695789Sahrens } 16962082Seschrock break; 1697789Sahrens 16983265Sahrens case ZFS_PROP_CANMOUNT: 169911497SMark.Musante@Sun.COM case ZFS_PROP_VOLSIZE: 1700789Sahrens case ZFS_PROP_QUOTA: 17015378Sck153898 case ZFS_PROP_REFQUOTA: 1702789Sahrens case ZFS_PROP_RESERVATION: 17035378Sck153898 case ZFS_PROP_REFRESERVATION: 17042885Sahrens *val = getprop_uint64(zhp, prop, source); 170511022STom.Erickson@Sun.COM 170611022STom.Erickson@Sun.COM if (*source == NULL) { 170711022STom.Erickson@Sun.COM /* not default, must be local */ 1708789Sahrens *source = zhp->zfs_name; 170911022STom.Erickson@Sun.COM } 17102082Seschrock break; 1711789Sahrens 1712789Sahrens case ZFS_PROP_MOUNTED: 17132082Seschrock *val = (zhp->zfs_mntopts != NULL); 17142082Seschrock break; 1715789Sahrens 17163377Seschrock case ZFS_PROP_NUMCLONES: 17173377Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 17183377Seschrock break; 17193377Seschrock 17205147Srm160521 case ZFS_PROP_VERSION: 17215498Stimh case ZFS_PROP_NORMALIZE: 17225498Stimh case ZFS_PROP_UTF8ONLY: 17235498Stimh case ZFS_PROP_CASE: 17245498Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 17255498Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17265473Srm160521 return (-1); 17275147Srm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 17285498Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 17295498Stimh zcmd_free_nvlists(&zc); 173010204SMatthew.Ahrens@Sun.COM return (-1); 17315147Srm160521 } 17325498Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 17335498Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 17345498Stimh val) != 0) { 17355498Stimh zcmd_free_nvlists(&zc); 173610204SMatthew.Ahrens@Sun.COM return (-1); 17375498Stimh } 17385592Stimh if (zplprops) 17395592Stimh nvlist_free(zplprops); 17405498Stimh zcmd_free_nvlists(&zc); 17415147Srm160521 break; 17425147Srm160521 1743789Sahrens default: 17444577Sahrens switch (zfs_prop_get_type(prop)) { 17454787Sahrens case PROP_TYPE_NUMBER: 17464787Sahrens case PROP_TYPE_INDEX: 17474577Sahrens *val = getprop_uint64(zhp, prop, source); 17487390SMatthew.Ahrens@Sun.COM /* 17499396SMatthew.Ahrens@Sun.COM * If we tried to use a default value for a 17507390SMatthew.Ahrens@Sun.COM * readonly property, it means that it was not 175111080STom.Erickson@Sun.COM * present. 17527390SMatthew.Ahrens@Sun.COM */ 17537390SMatthew.Ahrens@Sun.COM if (zfs_prop_readonly(prop) && 175411080STom.Erickson@Sun.COM *source != NULL && (*source)[0] == '\0') { 175511080STom.Erickson@Sun.COM *source = NULL; 17567390SMatthew.Ahrens@Sun.COM } 17574577Sahrens break; 17584577Sahrens 17594787Sahrens case PROP_TYPE_STRING: 17604577Sahrens default: 17614577Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 17624577Sahrens "cannot get non-numeric property")); 17634577Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 17644577Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 17654577Sahrens } 1766789Sahrens } 1767789Sahrens 1768789Sahrens return (0); 1769789Sahrens } 1770789Sahrens 1771789Sahrens /* 1772789Sahrens * Calculate the source type, given the raw source string. 1773789Sahrens */ 1774789Sahrens static void 17755094Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1776789Sahrens char *statbuf, size_t statlen) 1777789Sahrens { 17785094Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1779789Sahrens return; 1780789Sahrens 1781789Sahrens if (source == NULL) { 17825094Slling *srctype = ZPROP_SRC_NONE; 1783789Sahrens } else if (source[0] == '\0') { 17845094Slling *srctype = ZPROP_SRC_DEFAULT; 178511022STom.Erickson@Sun.COM } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 178611022STom.Erickson@Sun.COM *srctype = ZPROP_SRC_RECEIVED; 1787789Sahrens } else { 1788789Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 17895094Slling *srctype = ZPROP_SRC_LOCAL; 1790789Sahrens } else { 1791789Sahrens (void) strlcpy(statbuf, source, statlen); 17925094Slling *srctype = ZPROP_SRC_INHERITED; 1793789Sahrens } 1794789Sahrens } 1795789Sahrens 1796789Sahrens } 1797789Sahrens 179811022STom.Erickson@Sun.COM int 179911022STom.Erickson@Sun.COM zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180011022STom.Erickson@Sun.COM size_t proplen, boolean_t literal) 180111022STom.Erickson@Sun.COM { 180211022STom.Erickson@Sun.COM zfs_prop_t prop; 180311022STom.Erickson@Sun.COM int err = 0; 180411022STom.Erickson@Sun.COM 180511022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 180611022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 180711022STom.Erickson@Sun.COM return (-1); 180811022STom.Erickson@Sun.COM 180911022STom.Erickson@Sun.COM prop = zfs_name_to_prop(propname); 181011022STom.Erickson@Sun.COM 181111022STom.Erickson@Sun.COM if (prop != ZPROP_INVAL) { 181211022STom.Erickson@Sun.COM uint64_t cookie; 181311022STom.Erickson@Sun.COM if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181411022STom.Erickson@Sun.COM return (-1); 181511022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zhp, &cookie); 181611022STom.Erickson@Sun.COM err = zfs_prop_get(zhp, prop, propbuf, proplen, 181711022STom.Erickson@Sun.COM NULL, NULL, 0, literal); 181811022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zhp, &cookie); 181911022STom.Erickson@Sun.COM } else if (zfs_prop_userquota(propname)) { 182011022STom.Erickson@Sun.COM return (-1); 182111022STom.Erickson@Sun.COM } else { 182211022STom.Erickson@Sun.COM nvlist_t *propval; 182311022STom.Erickson@Sun.COM char *recvdval; 182411022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 182511022STom.Erickson@Sun.COM propname, &propval) != 0) 182611022STom.Erickson@Sun.COM return (-1); 182711022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, ZPROP_VALUE, 182811022STom.Erickson@Sun.COM &recvdval) == 0); 182911022STom.Erickson@Sun.COM (void) strlcpy(propbuf, recvdval, proplen); 183011022STom.Erickson@Sun.COM } 183111022STom.Erickson@Sun.COM 183211022STom.Erickson@Sun.COM return (err == 0 ? 0 : -1); 183311022STom.Erickson@Sun.COM } 183411022STom.Erickson@Sun.COM 1835789Sahrens /* 1836789Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1837789Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1838789Sahrens * human-readable form. 1839789Sahrens * 1840789Sahrens * Returns 0 on success, or -1 on error. 1841789Sahrens */ 1842789Sahrens int 1843789Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 18445094Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1845789Sahrens { 1846789Sahrens char *source = NULL; 1847789Sahrens uint64_t val; 1848789Sahrens char *str; 18492676Seschrock const char *strval; 185011022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1851789Sahrens 1852789Sahrens /* 1853789Sahrens * Check to see if this property applies to our object 1854789Sahrens */ 1855789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1856789Sahrens return (-1); 1857789Sahrens 185811022STom.Erickson@Sun.COM if (received && zfs_prop_readonly(prop)) 185911022STom.Erickson@Sun.COM return (-1); 186011022STom.Erickson@Sun.COM 1861789Sahrens if (src) 18625094Slling *src = ZPROP_SRC_NONE; 1863789Sahrens 1864789Sahrens switch (prop) { 1865789Sahrens case ZFS_PROP_CREATION: 1866789Sahrens /* 1867789Sahrens * 'creation' is a time_t stored in the statistics. We convert 1868789Sahrens * this into a string unless 'literal' is specified. 1869789Sahrens */ 1870789Sahrens { 18712885Sahrens val = getprop_uint64(zhp, prop, &source); 18722885Sahrens time_t time = (time_t)val; 1873789Sahrens struct tm t; 1874789Sahrens 1875789Sahrens if (literal || 1876789Sahrens localtime_r(&time, &t) == NULL || 1877789Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1878789Sahrens &t) == 0) 18792885Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1880789Sahrens } 1881789Sahrens break; 1882789Sahrens 1883789Sahrens case ZFS_PROP_MOUNTPOINT: 1884789Sahrens /* 1885789Sahrens * Getting the precise mountpoint can be tricky. 1886789Sahrens * 1887789Sahrens * - for 'none' or 'legacy', return those values. 1888789Sahrens * - for inherited mountpoints, we want to take everything 1889789Sahrens * after our ancestor and append it to the inherited value. 1890789Sahrens * 1891789Sahrens * If the pool has an alternate root, we want to prepend that 1892789Sahrens * root to any values we return. 1893789Sahrens */ 18946865Srm160521 18951356Seschrock str = getprop_string(zhp, prop, &source); 18961356Seschrock 18976612Sgw25295 if (str[0] == '/') { 18986865Srm160521 char buf[MAXPATHLEN]; 18996865Srm160521 char *root = buf; 190011515STom.Erickson@Sun.COM const char *relpath; 190111515STom.Erickson@Sun.COM 190211515STom.Erickson@Sun.COM /* 190311515STom.Erickson@Sun.COM * If we inherit the mountpoint, even from a dataset 190411515STom.Erickson@Sun.COM * with a received value, the source will be the path of 190511515STom.Erickson@Sun.COM * the dataset we inherit from. If source is 190611515STom.Erickson@Sun.COM * ZPROP_SOURCE_VAL_RECVD, the received value is not 190711515STom.Erickson@Sun.COM * inherited. 190811515STom.Erickson@Sun.COM */ 190911515STom.Erickson@Sun.COM if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 191011515STom.Erickson@Sun.COM relpath = ""; 191111515STom.Erickson@Sun.COM } else { 191211515STom.Erickson@Sun.COM relpath = zhp->zfs_name + strlen(source); 191311515STom.Erickson@Sun.COM if (relpath[0] == '/') 191411515STom.Erickson@Sun.COM relpath++; 191511515STom.Erickson@Sun.COM } 19166612Sgw25295 19176865Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 19186865Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 19196865Srm160521 (strcmp(root, "-") == 0)) 19206865Srm160521 root[0] = '\0'; 19216612Sgw25295 /* 19226612Sgw25295 * Special case an alternate root of '/'. This will 19236612Sgw25295 * avoid having multiple leading slashes in the 19246612Sgw25295 * mountpoint path. 19256612Sgw25295 */ 19266612Sgw25295 if (strcmp(root, "/") == 0) 19276612Sgw25295 root++; 19286612Sgw25295 19296612Sgw25295 /* 19306612Sgw25295 * If the mountpoint is '/' then skip over this 19316612Sgw25295 * if we are obtaining either an alternate root or 19326612Sgw25295 * an inherited mountpoint. 19336612Sgw25295 */ 19346612Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 19356612Sgw25295 relpath[0] != '\0')) 19361356Seschrock str++; 1937789Sahrens 1938789Sahrens if (relpath[0] == '\0') 1939789Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19401356Seschrock root, str); 1941789Sahrens else 1942789Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19431356Seschrock root, str, relpath[0] == '@' ? "" : "/", 1944789Sahrens relpath); 1945789Sahrens } else { 1946789Sahrens /* 'legacy' or 'none' */ 19471356Seschrock (void) strlcpy(propbuf, str, proplen); 1948789Sahrens } 1949789Sahrens 1950789Sahrens break; 1951789Sahrens 1952789Sahrens case ZFS_PROP_ORIGIN: 19532885Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1954789Sahrens proplen); 1955789Sahrens /* 1956789Sahrens * If there is no parent at all, return failure to indicate that 1957789Sahrens * it doesn't apply to this dataset. 1958789Sahrens */ 1959789Sahrens if (propbuf[0] == '\0') 1960789Sahrens return (-1); 1961789Sahrens break; 1962789Sahrens 1963789Sahrens case ZFS_PROP_QUOTA: 19645378Sck153898 case ZFS_PROP_REFQUOTA: 1965789Sahrens case ZFS_PROP_RESERVATION: 19665378Sck153898 case ZFS_PROP_REFRESERVATION: 19675378Sck153898 19682082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19692082Seschrock return (-1); 1970789Sahrens 1971789Sahrens /* 1972789Sahrens * If quota or reservation is 0, we translate this into 'none' 1973789Sahrens * (unless literal is set), and indicate that it's the default 1974789Sahrens * value. Otherwise, we print the number nicely and indicate 1975789Sahrens * that its set locally. 1976789Sahrens */ 1977789Sahrens if (val == 0) { 1978789Sahrens if (literal) 1979789Sahrens (void) strlcpy(propbuf, "0", proplen); 1980789Sahrens else 1981789Sahrens (void) strlcpy(propbuf, "none", proplen); 1982789Sahrens } else { 1983789Sahrens if (literal) 19842856Snd150628 (void) snprintf(propbuf, proplen, "%llu", 19853912Slling (u_longlong_t)val); 1986789Sahrens else 1987789Sahrens zfs_nicenum(val, propbuf, proplen); 1988789Sahrens } 1989789Sahrens break; 1990789Sahrens 1991789Sahrens case ZFS_PROP_COMPRESSRATIO: 19922082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19932082Seschrock return (-1); 199410922SJeff.Bonwick@Sun.COM (void) snprintf(propbuf, proplen, "%llu.%02llux", 199510922SJeff.Bonwick@Sun.COM (u_longlong_t)(val / 100), 199610922SJeff.Bonwick@Sun.COM (u_longlong_t)(val % 100)); 1997789Sahrens break; 1998789Sahrens 1999789Sahrens case ZFS_PROP_TYPE: 2000789Sahrens switch (zhp->zfs_type) { 2001789Sahrens case ZFS_TYPE_FILESYSTEM: 2002789Sahrens str = "filesystem"; 2003789Sahrens break; 2004789Sahrens case ZFS_TYPE_VOLUME: 2005789Sahrens str = "volume"; 2006789Sahrens break; 2007789Sahrens case ZFS_TYPE_SNAPSHOT: 2008789Sahrens str = "snapshot"; 2009789Sahrens break; 2010789Sahrens default: 20112082Seschrock abort(); 2012789Sahrens } 2013789Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2014789Sahrens break; 2015789Sahrens 2016789Sahrens case ZFS_PROP_MOUNTED: 2017789Sahrens /* 2018789Sahrens * The 'mounted' property is a pseudo-property that described 2019789Sahrens * whether the filesystem is currently mounted. Even though 2020789Sahrens * it's a boolean value, the typical values of "on" and "off" 2021789Sahrens * don't make sense, so we translate to "yes" and "no". 2022789Sahrens */ 20232082Seschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 20242082Seschrock src, &source, &val) != 0) 20252082Seschrock return (-1); 20262082Seschrock if (val) 2027789Sahrens (void) strlcpy(propbuf, "yes", proplen); 2028789Sahrens else 2029789Sahrens (void) strlcpy(propbuf, "no", proplen); 2030789Sahrens break; 2031789Sahrens 2032789Sahrens case ZFS_PROP_NAME: 2033789Sahrens /* 2034789Sahrens * The 'name' property is a pseudo-property derived from the 2035789Sahrens * dataset name. It is presented as a real property to simplify 2036789Sahrens * consumers. 2037789Sahrens */ 2038789Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2039789Sahrens break; 2040789Sahrens 204110972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 204210972SRic.Aleshire@Sun.COM { 204310972SRic.Aleshire@Sun.COM m_label_t *new_sl = NULL; 204410972SRic.Aleshire@Sun.COM char *ascii = NULL; /* human readable label */ 204510972SRic.Aleshire@Sun.COM 204610972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, 204710972SRic.Aleshire@Sun.COM getprop_string(zhp, prop, &source), proplen); 204810972SRic.Aleshire@Sun.COM 204910972SRic.Aleshire@Sun.COM if (literal || (strcasecmp(propbuf, 205010972SRic.Aleshire@Sun.COM ZFS_MLSLABEL_DEFAULT) == 0)) 205110972SRic.Aleshire@Sun.COM break; 205210972SRic.Aleshire@Sun.COM 205310972SRic.Aleshire@Sun.COM /* 205410972SRic.Aleshire@Sun.COM * Try to translate the internal hex string to 205510972SRic.Aleshire@Sun.COM * human-readable output. If there are any 205610972SRic.Aleshire@Sun.COM * problems just use the hex string. 205710972SRic.Aleshire@Sun.COM */ 205810972SRic.Aleshire@Sun.COM 205910972SRic.Aleshire@Sun.COM if (str_to_label(propbuf, &new_sl, MAC_LABEL, 206010972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1) { 206110972SRic.Aleshire@Sun.COM m_label_free(new_sl); 206210972SRic.Aleshire@Sun.COM break; 206310972SRic.Aleshire@Sun.COM } 206410972SRic.Aleshire@Sun.COM 206510972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &ascii, M_LABEL, 206610972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 206710972SRic.Aleshire@Sun.COM if (ascii) 206810972SRic.Aleshire@Sun.COM free(ascii); 206910972SRic.Aleshire@Sun.COM m_label_free(new_sl); 207010972SRic.Aleshire@Sun.COM break; 207110972SRic.Aleshire@Sun.COM } 207210972SRic.Aleshire@Sun.COM m_label_free(new_sl); 207310972SRic.Aleshire@Sun.COM 207410972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, ascii, proplen); 207510972SRic.Aleshire@Sun.COM free(ascii); 207610972SRic.Aleshire@Sun.COM } 207710972SRic.Aleshire@Sun.COM break; 207810972SRic.Aleshire@Sun.COM 2079789Sahrens default: 20804577Sahrens switch (zfs_prop_get_type(prop)) { 20814787Sahrens case PROP_TYPE_NUMBER: 20824577Sahrens if (get_numeric_property(zhp, prop, src, 20834577Sahrens &source, &val) != 0) 20844577Sahrens return (-1); 20854577Sahrens if (literal) 20864577Sahrens (void) snprintf(propbuf, proplen, "%llu", 20874577Sahrens (u_longlong_t)val); 20884577Sahrens else 20894577Sahrens zfs_nicenum(val, propbuf, proplen); 20904577Sahrens break; 20914577Sahrens 20924787Sahrens case PROP_TYPE_STRING: 20934577Sahrens (void) strlcpy(propbuf, 20944577Sahrens getprop_string(zhp, prop, &source), proplen); 20954577Sahrens break; 20964577Sahrens 20974787Sahrens case PROP_TYPE_INDEX: 20984861Sahrens if (get_numeric_property(zhp, prop, src, 20994861Sahrens &source, &val) != 0) 21004861Sahrens return (-1); 21014861Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 21024577Sahrens return (-1); 21034577Sahrens (void) strlcpy(propbuf, strval, proplen); 21044577Sahrens break; 21054577Sahrens 21064577Sahrens default: 21074577Sahrens abort(); 21084577Sahrens } 2109789Sahrens } 2110789Sahrens 2111789Sahrens get_source(zhp, src, source, statbuf, statlen); 2112789Sahrens 2113789Sahrens return (0); 2114789Sahrens } 2115789Sahrens 2116789Sahrens /* 2117789Sahrens * Utility function to get the given numeric property. Does no validation that 2118789Sahrens * the given property is the appropriate type; should only be used with 2119789Sahrens * hard-coded property types. 2120789Sahrens */ 2121789Sahrens uint64_t 2122789Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2123789Sahrens { 2124789Sahrens char *source; 21252082Seschrock uint64_t val; 21262082Seschrock 21275367Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 21282082Seschrock 21292082Seschrock return (val); 2130789Sahrens } 2131789Sahrens 21325713Srm160521 int 21335713Srm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21345713Srm160521 { 21355713Srm160521 char buf[64]; 21365713Srm160521 21379396SMatthew.Ahrens@Sun.COM (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21385713Srm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21395713Srm160521 } 21405713Srm160521 2141789Sahrens /* 2142789Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2143789Sahrens */ 2144789Sahrens int 2145789Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 21465094Slling zprop_source_t *src, char *statbuf, size_t statlen) 2147789Sahrens { 2148789Sahrens char *source; 2149789Sahrens 2150789Sahrens /* 2151789Sahrens * Check to see if this property applies to our object 2152789Sahrens */ 21534849Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 21543237Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 21552082Seschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 21562082Seschrock zfs_prop_to_name(prop))); 21574849Sahrens } 2158789Sahrens 2159789Sahrens if (src) 21605094Slling *src = ZPROP_SRC_NONE; 2161789Sahrens 21622082Seschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 21632082Seschrock return (-1); 2164789Sahrens 2165789Sahrens get_source(zhp, src, source, statbuf, statlen); 2166789Sahrens 2167789Sahrens return (0); 2168789Sahrens } 2169789Sahrens 21709396SMatthew.Ahrens@Sun.COM static int 21719396SMatthew.Ahrens@Sun.COM idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 21729396SMatthew.Ahrens@Sun.COM char **domainp, idmap_rid_t *ridp) 21739396SMatthew.Ahrens@Sun.COM { 21749396SMatthew.Ahrens@Sun.COM idmap_handle_t *idmap_hdl = NULL; 21759396SMatthew.Ahrens@Sun.COM idmap_get_handle_t *get_hdl = NULL; 21769396SMatthew.Ahrens@Sun.COM idmap_stat status; 21779396SMatthew.Ahrens@Sun.COM int err = EINVAL; 21789396SMatthew.Ahrens@Sun.COM 21799396SMatthew.Ahrens@Sun.COM if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 21809396SMatthew.Ahrens@Sun.COM goto out; 21819396SMatthew.Ahrens@Sun.COM if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 21829396SMatthew.Ahrens@Sun.COM goto out; 21839396SMatthew.Ahrens@Sun.COM 21849396SMatthew.Ahrens@Sun.COM if (isuser) { 21859396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbyuid(get_hdl, id, 21869396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21879396SMatthew.Ahrens@Sun.COM } else { 21889396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbygid(get_hdl, id, 21899396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21909396SMatthew.Ahrens@Sun.COM } 21919396SMatthew.Ahrens@Sun.COM if (err == IDMAP_SUCCESS && 21929396SMatthew.Ahrens@Sun.COM idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 21939396SMatthew.Ahrens@Sun.COM status == IDMAP_SUCCESS) 21949396SMatthew.Ahrens@Sun.COM err = 0; 21959396SMatthew.Ahrens@Sun.COM else 21969396SMatthew.Ahrens@Sun.COM err = EINVAL; 21979396SMatthew.Ahrens@Sun.COM out: 21989396SMatthew.Ahrens@Sun.COM if (get_hdl) 21999396SMatthew.Ahrens@Sun.COM idmap_get_destroy(get_hdl); 22009396SMatthew.Ahrens@Sun.COM if (idmap_hdl) 22019396SMatthew.Ahrens@Sun.COM (void) idmap_fini(idmap_hdl); 22029396SMatthew.Ahrens@Sun.COM return (err); 22039396SMatthew.Ahrens@Sun.COM } 22049396SMatthew.Ahrens@Sun.COM 22059396SMatthew.Ahrens@Sun.COM /* 22069396SMatthew.Ahrens@Sun.COM * convert the propname into parameters needed by kernel 22079396SMatthew.Ahrens@Sun.COM * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 22089396SMatthew.Ahrens@Sun.COM * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 22099396SMatthew.Ahrens@Sun.COM */ 22109396SMatthew.Ahrens@Sun.COM static int 22119396SMatthew.Ahrens@Sun.COM userquota_propname_decode(const char *propname, boolean_t zoned, 22129396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 22139396SMatthew.Ahrens@Sun.COM { 22149396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t type; 22159396SMatthew.Ahrens@Sun.COM char *cp, *end; 221610160SMatthew.Ahrens@Sun.COM char *numericsid = NULL; 22179396SMatthew.Ahrens@Sun.COM boolean_t isuser; 22189396SMatthew.Ahrens@Sun.COM 22199396SMatthew.Ahrens@Sun.COM domain[0] = '\0'; 22209396SMatthew.Ahrens@Sun.COM 22219396SMatthew.Ahrens@Sun.COM /* Figure out the property type ({user|group}{quota|space}) */ 22229396SMatthew.Ahrens@Sun.COM for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 22239396SMatthew.Ahrens@Sun.COM if (strncmp(propname, zfs_userquota_prop_prefixes[type], 22249396SMatthew.Ahrens@Sun.COM strlen(zfs_userquota_prop_prefixes[type])) == 0) 22259396SMatthew.Ahrens@Sun.COM break; 22269396SMatthew.Ahrens@Sun.COM } 22279396SMatthew.Ahrens@Sun.COM if (type == ZFS_NUM_USERQUOTA_PROPS) 22289396SMatthew.Ahrens@Sun.COM return (EINVAL); 22299396SMatthew.Ahrens@Sun.COM *typep = type; 22309396SMatthew.Ahrens@Sun.COM 22319396SMatthew.Ahrens@Sun.COM isuser = (type == ZFS_PROP_USERQUOTA || 22329396SMatthew.Ahrens@Sun.COM type == ZFS_PROP_USERUSED); 22339396SMatthew.Ahrens@Sun.COM 22349396SMatthew.Ahrens@Sun.COM cp = strchr(propname, '@') + 1; 22359396SMatthew.Ahrens@Sun.COM 22369396SMatthew.Ahrens@Sun.COM if (strchr(cp, '@')) { 22379396SMatthew.Ahrens@Sun.COM /* 22389396SMatthew.Ahrens@Sun.COM * It's a SID name (eg "user@domain") that needs to be 223910160SMatthew.Ahrens@Sun.COM * turned into S-1-domainID-RID. 22409396SMatthew.Ahrens@Sun.COM */ 224110160SMatthew.Ahrens@Sun.COM directory_error_t e; 22429396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22439396SMatthew.Ahrens@Sun.COM return (ENOENT); 224410160SMatthew.Ahrens@Sun.COM if (isuser) { 224510160SMatthew.Ahrens@Sun.COM e = directory_sid_from_user_name(NULL, 224610160SMatthew.Ahrens@Sun.COM cp, &numericsid); 224710160SMatthew.Ahrens@Sun.COM } else { 224810160SMatthew.Ahrens@Sun.COM e = directory_sid_from_group_name(NULL, 224910160SMatthew.Ahrens@Sun.COM cp, &numericsid); 225010160SMatthew.Ahrens@Sun.COM } 225110160SMatthew.Ahrens@Sun.COM if (e != NULL) { 225210160SMatthew.Ahrens@Sun.COM directory_error_free(e); 22539396SMatthew.Ahrens@Sun.COM return (ENOENT); 225410160SMatthew.Ahrens@Sun.COM } 225510160SMatthew.Ahrens@Sun.COM if (numericsid == NULL) 225610160SMatthew.Ahrens@Sun.COM return (ENOENT); 225710160SMatthew.Ahrens@Sun.COM cp = numericsid; 225810160SMatthew.Ahrens@Sun.COM /* will be further decoded below */ 225910160SMatthew.Ahrens@Sun.COM } 226010160SMatthew.Ahrens@Sun.COM 226110160SMatthew.Ahrens@Sun.COM if (strncmp(cp, "S-1-", 4) == 0) { 22629396SMatthew.Ahrens@Sun.COM /* It's a numeric SID (eg "S-1-234-567-89") */ 226310160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, cp, domainlen); 22649396SMatthew.Ahrens@Sun.COM cp = strrchr(domain, '-'); 22659396SMatthew.Ahrens@Sun.COM *cp = '\0'; 22669396SMatthew.Ahrens@Sun.COM cp++; 22679396SMatthew.Ahrens@Sun.COM 22689396SMatthew.Ahrens@Sun.COM errno = 0; 22699396SMatthew.Ahrens@Sun.COM *ridp = strtoull(cp, &end, 10); 227010160SMatthew.Ahrens@Sun.COM if (numericsid) { 227110160SMatthew.Ahrens@Sun.COM free(numericsid); 227210160SMatthew.Ahrens@Sun.COM numericsid = NULL; 227310160SMatthew.Ahrens@Sun.COM } 22749688SMatthew.Ahrens@Sun.COM if (errno != 0 || *end != '\0') 22759396SMatthew.Ahrens@Sun.COM return (EINVAL); 22769396SMatthew.Ahrens@Sun.COM } else if (!isdigit(*cp)) { 22779396SMatthew.Ahrens@Sun.COM /* 22789396SMatthew.Ahrens@Sun.COM * It's a user/group name (eg "user") that needs to be 22799396SMatthew.Ahrens@Sun.COM * turned into a uid/gid 22809396SMatthew.Ahrens@Sun.COM */ 22819396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22829396SMatthew.Ahrens@Sun.COM return (ENOENT); 22839396SMatthew.Ahrens@Sun.COM if (isuser) { 22849396SMatthew.Ahrens@Sun.COM struct passwd *pw; 22859396SMatthew.Ahrens@Sun.COM pw = getpwnam(cp); 22869396SMatthew.Ahrens@Sun.COM if (pw == NULL) 22879396SMatthew.Ahrens@Sun.COM return (ENOENT); 22889396SMatthew.Ahrens@Sun.COM *ridp = pw->pw_uid; 22899396SMatthew.Ahrens@Sun.COM } else { 22909396SMatthew.Ahrens@Sun.COM struct group *gr; 22919396SMatthew.Ahrens@Sun.COM gr = getgrnam(cp); 22929396SMatthew.Ahrens@Sun.COM if (gr == NULL) 22939396SMatthew.Ahrens@Sun.COM return (ENOENT); 22949396SMatthew.Ahrens@Sun.COM *ridp = gr->gr_gid; 22959396SMatthew.Ahrens@Sun.COM } 22969396SMatthew.Ahrens@Sun.COM } else { 22979396SMatthew.Ahrens@Sun.COM /* It's a user/group ID (eg "12345"). */ 22989396SMatthew.Ahrens@Sun.COM uid_t id = strtoul(cp, &end, 10); 22999396SMatthew.Ahrens@Sun.COM idmap_rid_t rid; 23009396SMatthew.Ahrens@Sun.COM char *mapdomain; 23019396SMatthew.Ahrens@Sun.COM 23029396SMatthew.Ahrens@Sun.COM if (*end != '\0') 23039396SMatthew.Ahrens@Sun.COM return (EINVAL); 23049396SMatthew.Ahrens@Sun.COM if (id > MAXUID) { 23059396SMatthew.Ahrens@Sun.COM /* It's an ephemeral ID. */ 23069396SMatthew.Ahrens@Sun.COM if (idmap_id_to_numeric_domain_rid(id, isuser, 23079396SMatthew.Ahrens@Sun.COM &mapdomain, &rid) != 0) 23089396SMatthew.Ahrens@Sun.COM return (ENOENT); 230910160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, mapdomain, domainlen); 23109396SMatthew.Ahrens@Sun.COM *ridp = rid; 23119396SMatthew.Ahrens@Sun.COM } else { 23129396SMatthew.Ahrens@Sun.COM *ridp = id; 23139396SMatthew.Ahrens@Sun.COM } 23149396SMatthew.Ahrens@Sun.COM } 23159396SMatthew.Ahrens@Sun.COM 231610160SMatthew.Ahrens@Sun.COM ASSERT3P(numericsid, ==, NULL); 23179396SMatthew.Ahrens@Sun.COM return (0); 23189396SMatthew.Ahrens@Sun.COM } 23199396SMatthew.Ahrens@Sun.COM 23209469SLin.Ling@Sun.COM static int 23219469SLin.Ling@Sun.COM zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 23229469SLin.Ling@Sun.COM uint64_t *propvalue, zfs_userquota_prop_t *typep) 23239396SMatthew.Ahrens@Sun.COM { 23249396SMatthew.Ahrens@Sun.COM int err; 23259396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 23269396SMatthew.Ahrens@Sun.COM 23279396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 23289396SMatthew.Ahrens@Sun.COM 23299396SMatthew.Ahrens@Sun.COM err = userquota_propname_decode(propname, 23309396SMatthew.Ahrens@Sun.COM zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 23319469SLin.Ling@Sun.COM typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 23329469SLin.Ling@Sun.COM zc.zc_objset_type = *typep; 23339396SMatthew.Ahrens@Sun.COM if (err) 23349396SMatthew.Ahrens@Sun.COM return (err); 23359396SMatthew.Ahrens@Sun.COM 23369396SMatthew.Ahrens@Sun.COM err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 23379396SMatthew.Ahrens@Sun.COM if (err) 23389396SMatthew.Ahrens@Sun.COM return (err); 23399396SMatthew.Ahrens@Sun.COM 23409469SLin.Ling@Sun.COM *propvalue = zc.zc_cookie; 23419469SLin.Ling@Sun.COM return (0); 23429469SLin.Ling@Sun.COM } 23439469SLin.Ling@Sun.COM 23449469SLin.Ling@Sun.COM int 23459469SLin.Ling@Sun.COM zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 23469469SLin.Ling@Sun.COM uint64_t *propvalue) 23479469SLin.Ling@Sun.COM { 23489469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23499469SLin.Ling@Sun.COM 23509469SLin.Ling@Sun.COM return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 23519469SLin.Ling@Sun.COM &type)); 23529469SLin.Ling@Sun.COM } 23539469SLin.Ling@Sun.COM 23549469SLin.Ling@Sun.COM int 23559469SLin.Ling@Sun.COM zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 23569469SLin.Ling@Sun.COM char *propbuf, int proplen, boolean_t literal) 23579469SLin.Ling@Sun.COM { 23589469SLin.Ling@Sun.COM int err; 23599469SLin.Ling@Sun.COM uint64_t propvalue; 23609469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23619469SLin.Ling@Sun.COM 23629469SLin.Ling@Sun.COM err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 23639469SLin.Ling@Sun.COM &type); 23649469SLin.Ling@Sun.COM 23659469SLin.Ling@Sun.COM if (err) 23669469SLin.Ling@Sun.COM return (err); 23679469SLin.Ling@Sun.COM 23689396SMatthew.Ahrens@Sun.COM if (literal) { 23699469SLin.Ling@Sun.COM (void) snprintf(propbuf, proplen, "%llu", propvalue); 23709469SLin.Ling@Sun.COM } else if (propvalue == 0 && 23719396SMatthew.Ahrens@Sun.COM (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 23729396SMatthew.Ahrens@Sun.COM (void) strlcpy(propbuf, "none", proplen); 23739396SMatthew.Ahrens@Sun.COM } else { 23749469SLin.Ling@Sun.COM zfs_nicenum(propvalue, propbuf, proplen); 23759396SMatthew.Ahrens@Sun.COM } 23769396SMatthew.Ahrens@Sun.COM return (0); 23779396SMatthew.Ahrens@Sun.COM } 23789396SMatthew.Ahrens@Sun.COM 2379789Sahrens /* 2380789Sahrens * Returns the name of the given zfs handle. 2381789Sahrens */ 2382789Sahrens const char * 2383789Sahrens zfs_get_name(const zfs_handle_t *zhp) 2384789Sahrens { 2385789Sahrens return (zhp->zfs_name); 2386789Sahrens } 2387789Sahrens 2388789Sahrens /* 2389789Sahrens * Returns the type of the given zfs handle. 2390789Sahrens */ 2391789Sahrens zfs_type_t 2392789Sahrens zfs_get_type(const zfs_handle_t *zhp) 2393789Sahrens { 2394789Sahrens return (zhp->zfs_type); 2395789Sahrens } 2396789Sahrens 23978228SEric.Taylor@Sun.COM static int 23988228SEric.Taylor@Sun.COM zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 23998228SEric.Taylor@Sun.COM { 24008228SEric.Taylor@Sun.COM int rc; 24018228SEric.Taylor@Sun.COM uint64_t orig_cookie; 24028228SEric.Taylor@Sun.COM 24038228SEric.Taylor@Sun.COM orig_cookie = zc->zc_cookie; 24048228SEric.Taylor@Sun.COM top: 24058228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 24068228SEric.Taylor@Sun.COM rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 24078228SEric.Taylor@Sun.COM 24088228SEric.Taylor@Sun.COM if (rc == -1) { 24098228SEric.Taylor@Sun.COM switch (errno) { 24108228SEric.Taylor@Sun.COM case ENOMEM: 24118228SEric.Taylor@Sun.COM /* expand nvlist memory and try again */ 24128228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 24138228SEric.Taylor@Sun.COM zcmd_free_nvlists(zc); 24148228SEric.Taylor@Sun.COM return (-1); 24158228SEric.Taylor@Sun.COM } 24168228SEric.Taylor@Sun.COM zc->zc_cookie = orig_cookie; 24178228SEric.Taylor@Sun.COM goto top; 24188228SEric.Taylor@Sun.COM /* 24198228SEric.Taylor@Sun.COM * An errno value of ESRCH indicates normal completion. 24208228SEric.Taylor@Sun.COM * If ENOENT is returned, then the underlying dataset 24218228SEric.Taylor@Sun.COM * has been removed since we obtained the handle. 24228228SEric.Taylor@Sun.COM */ 24238228SEric.Taylor@Sun.COM case ESRCH: 24248228SEric.Taylor@Sun.COM case ENOENT: 24258228SEric.Taylor@Sun.COM rc = 1; 24268228SEric.Taylor@Sun.COM break; 24278228SEric.Taylor@Sun.COM default: 24288228SEric.Taylor@Sun.COM rc = zfs_standard_error(zhp->zfs_hdl, errno, 24298228SEric.Taylor@Sun.COM dgettext(TEXT_DOMAIN, 24308228SEric.Taylor@Sun.COM "cannot iterate filesystems")); 24318228SEric.Taylor@Sun.COM break; 24328228SEric.Taylor@Sun.COM } 24338228SEric.Taylor@Sun.COM } 24348228SEric.Taylor@Sun.COM return (rc); 24358228SEric.Taylor@Sun.COM } 24368228SEric.Taylor@Sun.COM 2437789Sahrens /* 24381356Seschrock * Iterate over all child filesystems 2439789Sahrens */ 2440789Sahrens int 24411356Seschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2442789Sahrens { 2443789Sahrens zfs_cmd_t zc = { 0 }; 2444789Sahrens zfs_handle_t *nzhp; 2445789Sahrens int ret; 2446789Sahrens 24475367Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24485367Sahrens return (0); 24495367Sahrens 24508228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24518228SEric.Taylor@Sun.COM return (-1); 24528228SEric.Taylor@Sun.COM 24538228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 24548228SEric.Taylor@Sun.COM &zc)) == 0) { 2455789Sahrens /* 2456789Sahrens * Silently ignore errors, as the only plausible explanation is 2457789Sahrens * that the pool has since been removed. 2458789Sahrens */ 24598228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24608228SEric.Taylor@Sun.COM &zc)) == NULL) { 2461789Sahrens continue; 24628228SEric.Taylor@Sun.COM } 24638228SEric.Taylor@Sun.COM 24648228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24658228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2466789Sahrens return (ret); 24678228SEric.Taylor@Sun.COM } 2468789Sahrens } 24698228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 24708228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 24711356Seschrock } 24721356Seschrock 24731356Seschrock /* 24741356Seschrock * Iterate over all snapshots 24751356Seschrock */ 24761356Seschrock int 24771356Seschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24781356Seschrock { 24791356Seschrock zfs_cmd_t zc = { 0 }; 24801356Seschrock zfs_handle_t *nzhp; 24811356Seschrock int ret; 2482789Sahrens 24835367Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24845367Sahrens return (0); 24855367Sahrens 24868228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24878228SEric.Taylor@Sun.COM return (-1); 24888228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 24898228SEric.Taylor@Sun.COM &zc)) == 0) { 24908228SEric.Taylor@Sun.COM 24918228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24928228SEric.Taylor@Sun.COM &zc)) == NULL) { 2493789Sahrens continue; 24948228SEric.Taylor@Sun.COM } 24958228SEric.Taylor@Sun.COM 24968228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24978228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2498789Sahrens return (ret); 24998228SEric.Taylor@Sun.COM } 2500789Sahrens } 25018228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 25028228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 2503789Sahrens } 2504789Sahrens 2505789Sahrens /* 25061356Seschrock * Iterate over all children, snapshots and filesystems 25071356Seschrock */ 25081356Seschrock int 25091356Seschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25101356Seschrock { 25111356Seschrock int ret; 25121356Seschrock 25131356Seschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25141356Seschrock return (ret); 25151356Seschrock 25161356Seschrock return (zfs_iter_snapshots(zhp, func, data)); 25171356Seschrock } 25181356Seschrock 25191356Seschrock /* 252011497SMark.Musante@Sun.COM * Is one dataset name a child dataset of another? 252111497SMark.Musante@Sun.COM * 252211497SMark.Musante@Sun.COM * Needs to handle these cases: 252311497SMark.Musante@Sun.COM * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 252411497SMark.Musante@Sun.COM * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 252511497SMark.Musante@Sun.COM * Descendant? No. No. No. Yes. 252611497SMark.Musante@Sun.COM */ 252711497SMark.Musante@Sun.COM static boolean_t 252811497SMark.Musante@Sun.COM is_descendant(const char *ds1, const char *ds2) 252911497SMark.Musante@Sun.COM { 253011497SMark.Musante@Sun.COM size_t d1len = strlen(ds1); 253111497SMark.Musante@Sun.COM 253211497SMark.Musante@Sun.COM /* ds2 can't be a descendant if it's smaller */ 253311497SMark.Musante@Sun.COM if (strlen(ds2) < d1len) 253411497SMark.Musante@Sun.COM return (B_FALSE); 253511497SMark.Musante@Sun.COM 253611497SMark.Musante@Sun.COM /* otherwise, compare strings and verify that there's a '/' char */ 253711497SMark.Musante@Sun.COM return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 253811497SMark.Musante@Sun.COM } 253911497SMark.Musante@Sun.COM 254011497SMark.Musante@Sun.COM /* 2541789Sahrens * Given a complete name, return just the portion that refers to the parent. 2542789Sahrens * Can return NULL if this is a pool. 2543789Sahrens */ 2544789Sahrens static int 2545789Sahrens parent_name(const char *path, char *buf, size_t buflen) 2546789Sahrens { 2547789Sahrens char *loc; 2548789Sahrens 2549789Sahrens if ((loc = strrchr(path, '/')) == NULL) 2550789Sahrens return (-1); 2551789Sahrens 2552789Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2553789Sahrens buf[loc - path] = '\0'; 2554789Sahrens 2555789Sahrens return (0); 2556789Sahrens } 2557789Sahrens 2558789Sahrens /* 25594490Svb160487 * If accept_ancestor is false, then check to make sure that the given path has 25604490Svb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25614490Svb160487 * closest existing ancestor for the given path. In prefixlen return the 25624490Svb160487 * length of already existing prefix of the given path. We also fetch the 25634490Svb160487 * 'zoned' property, which is used to validate property settings when creating 25644490Svb160487 * new datasets. 2565789Sahrens */ 2566789Sahrens static int 25674490Svb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25684490Svb160487 boolean_t accept_ancestor, int *prefixlen) 2569789Sahrens { 2570789Sahrens zfs_cmd_t zc = { 0 }; 2571789Sahrens char parent[ZFS_MAXNAMELEN]; 2572789Sahrens char *slash; 25731356Seschrock zfs_handle_t *zhp; 25742082Seschrock char errbuf[1024]; 257511497SMark.Musante@Sun.COM uint64_t is_zoned; 25762082Seschrock 25778269SMark.Musante@Sun.COM (void) snprintf(errbuf, sizeof (errbuf), 25788269SMark.Musante@Sun.COM dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2579789Sahrens 2580789Sahrens /* get parent, and check to see if this is just a pool */ 2581789Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 25822082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25832082Seschrock "missing dataset name")); 25842082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2585789Sahrens } 2586789Sahrens 2587789Sahrens /* check to see if the pool exists */ 2588789Sahrens if ((slash = strchr(parent, '/')) == NULL) 2589789Sahrens slash = parent + strlen(parent); 2590789Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2591789Sahrens zc.zc_name[slash - parent] = '\0'; 25922082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2593789Sahrens errno == ENOENT) { 25942082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25952082Seschrock "no such pool '%s'"), zc.zc_name); 25962082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2597789Sahrens } 2598789Sahrens 2599789Sahrens /* check to see if the parent dataset exists */ 26004490Svb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 26014490Svb160487 if (errno == ENOENT && accept_ancestor) { 26024490Svb160487 /* 26034490Svb160487 * Go deeper to find an ancestor, give up on top level. 26044490Svb160487 */ 26054490Svb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 26064490Svb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26074490Svb160487 "no such pool '%s'"), zc.zc_name); 26084490Svb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26094490Svb160487 } 26104490Svb160487 } else if (errno == ENOENT) { 26112082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26122082Seschrock "parent does not exist")); 26132082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26144490Svb160487 } else 26152082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2616789Sahrens } 2617789Sahrens 261811497SMark.Musante@Sun.COM is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 261911497SMark.Musante@Sun.COM if (zoned != NULL) 262011497SMark.Musante@Sun.COM *zoned = is_zoned; 262111497SMark.Musante@Sun.COM 2622789Sahrens /* we are in a non-global zone, but parent is in the global zone */ 262311497SMark.Musante@Sun.COM if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 26242082Seschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26251356Seschrock zfs_close(zhp); 2626789Sahrens return (-1); 2627789Sahrens } 2628789Sahrens 2629789Sahrens /* make sure parent is a filesystem */ 26301356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 26312082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26322082Seschrock "parent is not a filesystem")); 26332082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26341356Seschrock zfs_close(zhp); 2635789Sahrens return (-1); 2636789Sahrens } 2637789Sahrens 26381356Seschrock zfs_close(zhp); 26394490Svb160487 if (prefixlen != NULL) 26404490Svb160487 *prefixlen = strlen(parent); 26414490Svb160487 return (0); 26424490Svb160487 } 26434490Svb160487 26444490Svb160487 /* 26454490Svb160487 * Finds whether the dataset of the given type(s) exists. 26464490Svb160487 */ 26474490Svb160487 boolean_t 26484490Svb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26494490Svb160487 { 26504490Svb160487 zfs_handle_t *zhp; 26514490Svb160487 26525326Sek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26534490Svb160487 return (B_FALSE); 26544490Svb160487 26554490Svb160487 /* 26564490Svb160487 * Try to get stats for the dataset, which will tell us if it exists. 26574490Svb160487 */ 26584490Svb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26594490Svb160487 int ds_type = zhp->zfs_type; 26604490Svb160487 26614490Svb160487 zfs_close(zhp); 26624490Svb160487 if (types & ds_type) 26634490Svb160487 return (B_TRUE); 26644490Svb160487 } 26654490Svb160487 return (B_FALSE); 26664490Svb160487 } 26674490Svb160487 26684490Svb160487 /* 26695367Sahrens * Given a path to 'target', create all the ancestors between 26705367Sahrens * the prefixlen portion of the path, and the target itself. 26715367Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26725367Sahrens */ 26735367Sahrens int 26745367Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26755367Sahrens { 26765367Sahrens zfs_handle_t *h; 26775367Sahrens char *cp; 26785367Sahrens const char *opname; 26795367Sahrens 26805367Sahrens /* make sure prefix exists */ 26815367Sahrens cp = target + prefixlen; 26825367Sahrens if (*cp != '/') { 26835367Sahrens assert(strchr(cp, '/') == NULL); 26845367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26855367Sahrens } else { 26865367Sahrens *cp = '\0'; 26875367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26885367Sahrens *cp = '/'; 26895367Sahrens } 26905367Sahrens if (h == NULL) 26915367Sahrens return (-1); 26925367Sahrens zfs_close(h); 26935367Sahrens 26945367Sahrens /* 26955367Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26965367Sahrens * up to the prefixlen-long one. 26975367Sahrens */ 26985367Sahrens for (cp = target + prefixlen + 1; 26995367Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 27005367Sahrens char *logstr; 27015367Sahrens 27025367Sahrens *cp = '\0'; 27035367Sahrens 27045367Sahrens h = make_dataset_handle(hdl, target); 27055367Sahrens if (h) { 27065367Sahrens /* it already exists, nothing to do here */ 27075367Sahrens zfs_close(h); 27085367Sahrens continue; 27095367Sahrens } 27105367Sahrens 27115367Sahrens logstr = hdl->libzfs_log_str; 27125367Sahrens hdl->libzfs_log_str = NULL; 27135367Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27145367Sahrens NULL) != 0) { 27155367Sahrens hdl->libzfs_log_str = logstr; 27165367Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27175367Sahrens goto ancestorerr; 27185367Sahrens } 27195367Sahrens 27205367Sahrens hdl->libzfs_log_str = logstr; 27215367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27225367Sahrens if (h == NULL) { 27235367Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27245367Sahrens goto ancestorerr; 27255367Sahrens } 27265367Sahrens 27275367Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27285367Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27295367Sahrens goto ancestorerr; 27305367Sahrens } 27315367Sahrens 27325367Sahrens if (zfs_share(h) != 0) { 27335367Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27345367Sahrens goto ancestorerr; 27355367Sahrens } 27365367Sahrens 27375367Sahrens zfs_close(h); 27385367Sahrens } 27395367Sahrens 27405367Sahrens return (0); 27415367Sahrens 27425367Sahrens ancestorerr: 27435367Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27445367Sahrens "failed to %s ancestor '%s'"), opname, target); 27455367Sahrens return (-1); 27465367Sahrens } 27475367Sahrens 27485367Sahrens /* 27494490Svb160487 * Creates non-existing ancestors of the given path. 27504490Svb160487 */ 27514490Svb160487 int 27524490Svb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27534490Svb160487 { 27544490Svb160487 int prefix; 27554490Svb160487 char *path_copy; 27564490Svb160487 int rc; 27574490Svb160487 275811497SMark.Musante@Sun.COM if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27594490Svb160487 return (-1); 27604490Svb160487 27614490Svb160487 if ((path_copy = strdup(path)) != NULL) { 27624490Svb160487 rc = create_parents(hdl, path_copy, prefix); 27634490Svb160487 free(path_copy); 27644490Svb160487 } 27654490Svb160487 if (path_copy == NULL || rc != 0) 27664490Svb160487 return (-1); 27674490Svb160487 2768789Sahrens return (0); 2769789Sahrens } 2770789Sahrens 2771789Sahrens /* 27722676Seschrock * Create a new filesystem or volume. 2773789Sahrens */ 2774789Sahrens int 27752082Seschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 27762676Seschrock nvlist_t *props) 2777789Sahrens { 2778789Sahrens zfs_cmd_t zc = { 0 }; 2779789Sahrens int ret; 2780789Sahrens uint64_t size = 0; 2781789Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 27822082Seschrock char errbuf[1024]; 27832676Seschrock uint64_t zoned; 27842082Seschrock 27852082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 27862082Seschrock "cannot create '%s'"), path); 2787789Sahrens 2788789Sahrens /* validate the path, taking care to note the extended error message */ 27895326Sek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 27902082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2791789Sahrens 2792789Sahrens /* validate parents exist */ 27934490Svb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2794789Sahrens return (-1); 2795789Sahrens 2796789Sahrens /* 2797789Sahrens * The failure modes when creating a dataset of a different type over 2798789Sahrens * one that already exists is a little strange. In particular, if you 2799789Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2800789Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2801789Sahrens * first try to see if the dataset exists. 2802789Sahrens */ 2803789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 28045094Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 28052082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28062082Seschrock "dataset already exists")); 28072082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2808789Sahrens } 2809789Sahrens 2810789Sahrens if (type == ZFS_TYPE_VOLUME) 2811789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2812789Sahrens else 2813789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2814789Sahrens 28157184Stimh if (props && (props = zfs_valid_proplist(hdl, type, props, 28163912Slling zoned, NULL, errbuf)) == 0) 28172676Seschrock return (-1); 28182676Seschrock 2819789Sahrens if (type == ZFS_TYPE_VOLUME) { 28201133Seschrock /* 28211133Seschrock * If we are creating a volume, the size and block size must 28221133Seschrock * satisfy a few restraints. First, the blocksize must be a 28231133Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28241133Seschrock * volsize must be a multiple of the block size, and cannot be 28251133Seschrock * zero. 28261133Seschrock */ 28272676Seschrock if (props == NULL || nvlist_lookup_uint64(props, 28282676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 28292676Seschrock nvlist_free(props); 28302082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28312676Seschrock "missing volume size")); 28322676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2833789Sahrens } 2834789Sahrens 28352676Seschrock if ((ret = nvlist_lookup_uint64(props, 28362676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 28372676Seschrock &blocksize)) != 0) { 28382676Seschrock if (ret == ENOENT) { 28392676Seschrock blocksize = zfs_prop_default_numeric( 28402676Seschrock ZFS_PROP_VOLBLOCKSIZE); 28412676Seschrock } else { 28422676Seschrock nvlist_free(props); 28432676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28442676Seschrock "missing volume block size")); 28452676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28462676Seschrock } 28472676Seschrock } 28482676Seschrock 28492676Seschrock if (size == 0) { 28502676Seschrock nvlist_free(props); 28512082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28522676Seschrock "volume size cannot be zero")); 28532676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28541133Seschrock } 28551133Seschrock 28561133Seschrock if (size % blocksize != 0) { 28572676Seschrock nvlist_free(props); 28582082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28592676Seschrock "volume size must be a multiple of volume block " 28602676Seschrock "size")); 28612676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28621133Seschrock } 2863789Sahrens } 2864789Sahrens 28655094Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 28662676Seschrock return (-1); 28672676Seschrock nvlist_free(props); 28682676Seschrock 2869789Sahrens /* create the dataset */ 28704543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2871789Sahrens 28722676Seschrock zcmd_free_nvlists(&zc); 28732676Seschrock 2874789Sahrens /* check for failure */ 2875789Sahrens if (ret != 0) { 2876789Sahrens char parent[ZFS_MAXNAMELEN]; 2877789Sahrens (void) parent_name(path, parent, sizeof (parent)); 2878789Sahrens 2879789Sahrens switch (errno) { 2880789Sahrens case ENOENT: 28812082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28822082Seschrock "no such parent '%s'"), parent); 28832082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2884789Sahrens 2885789Sahrens case EINVAL: 28862082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28873413Smmusante "parent '%s' is not a filesystem"), parent); 28882082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2889789Sahrens 2890789Sahrens case EDOM: 28912082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28922676Seschrock "volume block size must be power of 2 from " 28932676Seschrock "%u to %uk"), 2894789Sahrens (uint_t)SPA_MINBLOCKSIZE, 2895789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 28962082Seschrock 28972676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28982082Seschrock 28994603Sahrens case ENOTSUP: 29004603Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29014603Sahrens "pool must be upgraded to set this " 29024603Sahrens "property or value")); 29034603Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2904789Sahrens #ifdef _ILP32 2905789Sahrens case EOVERFLOW: 2906789Sahrens /* 2907789Sahrens * This platform can't address a volume this big. 2908789Sahrens */ 29092082Seschrock if (type == ZFS_TYPE_VOLUME) 29102082Seschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 29112082Seschrock errbuf)); 2912789Sahrens #endif 29132082Seschrock /* FALLTHROUGH */ 2914789Sahrens default: 29152082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2916789Sahrens } 2917789Sahrens } 2918789Sahrens 2919789Sahrens return (0); 2920789Sahrens } 2921789Sahrens 2922789Sahrens /* 2923789Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2924789Sahrens * isn't mounted, and that there are no active dependents. 2925789Sahrens */ 2926789Sahrens int 292710242Schris.kirby@sun.com zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2928789Sahrens { 2929789Sahrens zfs_cmd_t zc = { 0 }; 2930789Sahrens 2931789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2932789Sahrens 29332676Seschrock if (ZFS_IS_VOLUME(zhp)) { 2934789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2935789Sahrens } else { 2936789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2937789Sahrens } 2938789Sahrens 293910242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 29404543Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 29413237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 29422082Seschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 29432082Seschrock zhp->zfs_name)); 29442199Sahrens } 2945789Sahrens 2946789Sahrens remove_mountpoint(zhp); 2947789Sahrens 2948789Sahrens return (0); 2949789Sahrens } 2950789Sahrens 29512199Sahrens struct destroydata { 29522199Sahrens char *snapname; 29532199Sahrens boolean_t gotone; 29543265Sahrens boolean_t closezhp; 29552199Sahrens }; 29562199Sahrens 29572199Sahrens static int 295810588SEric.Taylor@Sun.COM zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29592199Sahrens { 29602199Sahrens struct destroydata *dd = arg; 29612199Sahrens zfs_handle_t *szhp; 29622199Sahrens char name[ZFS_MAXNAMELEN]; 29633265Sahrens boolean_t closezhp = dd->closezhp; 296410588SEric.Taylor@Sun.COM int rv = 0; 29652199Sahrens 29662676Seschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 29672676Seschrock (void) strlcat(name, "@", sizeof (name)); 29682676Seschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29692199Sahrens 29702199Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 29712199Sahrens if (szhp) { 29722199Sahrens dd->gotone = B_TRUE; 29732199Sahrens zfs_close(szhp); 29742199Sahrens } 29752199Sahrens 29763265Sahrens dd->closezhp = B_TRUE; 297710588SEric.Taylor@Sun.COM if (!dd->gotone) 297810588SEric.Taylor@Sun.COM rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 29793265Sahrens if (closezhp) 29803265Sahrens zfs_close(zhp); 29813265Sahrens return (rv); 29822199Sahrens } 29832199Sahrens 29842199Sahrens /* 29852199Sahrens * Destroys all snapshots with the given name in zhp & descendants. 29862199Sahrens */ 29872199Sahrens int 298810242Schris.kirby@sun.com zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 29892199Sahrens { 29902199Sahrens zfs_cmd_t zc = { 0 }; 29912199Sahrens int ret; 29922199Sahrens struct destroydata dd = { 0 }; 29932199Sahrens 29942199Sahrens dd.snapname = snapname; 299510588SEric.Taylor@Sun.COM (void) zfs_check_snap_cb(zhp, &dd); 29962199Sahrens 29972199Sahrens if (!dd.gotone) { 29983237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 29992199Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 30002199Sahrens zhp->zfs_name, snapname)); 30012199Sahrens } 30022199Sahrens 30032199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 30042676Seschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 300510242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 30062199Sahrens 30074543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 30082199Sahrens if (ret != 0) { 30092199Sahrens char errbuf[1024]; 30102199Sahrens 30112199Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30122199Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 30132199Sahrens 30142199Sahrens switch (errno) { 30152199Sahrens case EEXIST: 30162199Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30172199Sahrens "snapshot is cloned")); 30182199Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 30192199Sahrens 30202199Sahrens default: 30212199Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 30222199Sahrens errbuf)); 30232199Sahrens } 30242199Sahrens } 30252199Sahrens 30262199Sahrens return (0); 30272199Sahrens } 30282199Sahrens 3029789Sahrens /* 3030789Sahrens * Clones the given dataset. The target must be of the same type as the source. 3031789Sahrens */ 3032789Sahrens int 30332676Seschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3034789Sahrens { 3035789Sahrens zfs_cmd_t zc = { 0 }; 3036789Sahrens char parent[ZFS_MAXNAMELEN]; 3037789Sahrens int ret; 30382082Seschrock char errbuf[1024]; 30392082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 30402676Seschrock zfs_type_t type; 30412676Seschrock uint64_t zoned; 3042789Sahrens 3043789Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3044789Sahrens 30452082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30462082Seschrock "cannot create '%s'"), target); 30472082Seschrock 3048789Sahrens /* validate the target name */ 30495326Sek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 30502082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3051789Sahrens 3052789Sahrens /* validate parents exist */ 30534490Svb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3054789Sahrens return (-1); 3055789Sahrens 3056789Sahrens (void) parent_name(target, parent, sizeof (parent)); 3057789Sahrens 3058789Sahrens /* do the clone */ 30592676Seschrock if (ZFS_IS_VOLUME(zhp)) { 3060789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30612744Snn35248 type = ZFS_TYPE_VOLUME; 30622676Seschrock } else { 3063789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30642744Snn35248 type = ZFS_TYPE_FILESYSTEM; 30652676Seschrock } 30662676Seschrock 30672676Seschrock if (props) { 30687184Stimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30697184Stimh zhp, errbuf)) == NULL) 30702676Seschrock return (-1); 30712676Seschrock 30725094Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 30732676Seschrock nvlist_free(props); 30742676Seschrock return (-1); 30752676Seschrock } 30762676Seschrock 30772676Seschrock nvlist_free(props); 30782676Seschrock } 3079789Sahrens 3080789Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 30812676Seschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 30824543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3083789Sahrens 30842676Seschrock zcmd_free_nvlists(&zc); 30852676Seschrock 3086789Sahrens if (ret != 0) { 3087789Sahrens switch (errno) { 3088789Sahrens 3089789Sahrens case ENOENT: 3090789Sahrens /* 3091789Sahrens * The parent doesn't exist. We should have caught this 3092789Sahrens * above, but there may a race condition that has since 3093789Sahrens * destroyed the parent. 3094789Sahrens * 3095789Sahrens * At this point, we don't know whether it's the source 3096789Sahrens * that doesn't exist anymore, or whether the target 3097789Sahrens * dataset doesn't exist. 3098789Sahrens */ 30992082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 31002082Seschrock "no such parent '%s'"), parent); 31012082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 31022082Seschrock 31032082Seschrock case EXDEV: 31042082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 31052082Seschrock "source and target pools differ")); 31062082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 31072082Seschrock errbuf)); 31082082Seschrock 31092082Seschrock default: 31102082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 31112082Seschrock errbuf)); 31122082Seschrock } 31132082Seschrock } 31142082Seschrock 31152082Seschrock return (ret); 31162082Seschrock } 31172082Seschrock 31182082Seschrock /* 31192082Seschrock * Promotes the given clone fs to be the clone parent. 31202082Seschrock */ 31212082Seschrock int 31222082Seschrock zfs_promote(zfs_handle_t *zhp) 31232082Seschrock { 31242082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 31252082Seschrock zfs_cmd_t zc = { 0 }; 31262082Seschrock char parent[MAXPATHLEN]; 31272082Seschrock int ret; 31282082Seschrock char errbuf[1024]; 31292082Seschrock 31302082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31312082Seschrock "cannot promote '%s'"), zhp->zfs_name); 31322082Seschrock 31332082Seschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 31342082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31352082Seschrock "snapshots can not be promoted")); 31362082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31372082Seschrock } 31382082Seschrock 31395367Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 31402082Seschrock if (parent[0] == '\0') { 31412082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31422082Seschrock "not a cloned filesystem")); 31432082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31442082Seschrock } 314510588SEric.Taylor@Sun.COM 31465367Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 31472676Seschrock sizeof (zc.zc_value)); 31482082Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 31494543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 31502082Seschrock 31512082Seschrock if (ret != 0) { 31522417Sahrens int save_errno = errno; 31532417Sahrens 31542417Sahrens switch (save_errno) { 3155789Sahrens case EEXIST: 315610588SEric.Taylor@Sun.COM /* There is a conflicting snapshot name. */ 31572082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 315810588SEric.Taylor@Sun.COM "conflicting snapshot '%s' from parent '%s'"), 315910588SEric.Taylor@Sun.COM zc.zc_string, parent); 31602082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3161789Sahrens 3162789Sahrens default: 31632417Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3164789Sahrens } 3165789Sahrens } 31662676Seschrock return (ret); 31672199Sahrens } 31682199Sahrens 3169789Sahrens /* 31703504Sahl * Takes a snapshot of the given dataset. 3171789Sahrens */ 3172789Sahrens int 31737265Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 31747265Sahrens nvlist_t *props) 3175789Sahrens { 3176789Sahrens const char *delim; 31777265Sahrens char parent[ZFS_MAXNAMELEN]; 3178789Sahrens zfs_handle_t *zhp; 3179789Sahrens zfs_cmd_t zc = { 0 }; 3180789Sahrens int ret; 31812082Seschrock char errbuf[1024]; 31822082Seschrock 31832082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31842082Seschrock "cannot snapshot '%s'"), path); 31852082Seschrock 31862082Seschrock /* validate the target name */ 31875326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 31882082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3189789Sahrens 31907265Sahrens if (props) { 31917265Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 31927265Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 31937265Sahrens return (-1); 31947265Sahrens 31957265Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 31967265Sahrens nvlist_free(props); 31977265Sahrens return (-1); 31987265Sahrens } 31997265Sahrens 32007265Sahrens nvlist_free(props); 32017265Sahrens } 32027265Sahrens 3203789Sahrens /* make sure the parent exists and is of the appropriate type */ 32042199Sahrens delim = strchr(path, '@'); 3205789Sahrens (void) strncpy(parent, path, delim - path); 3206789Sahrens parent[delim - path] = '\0'; 3207789Sahrens 32082082Seschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3209789Sahrens ZFS_TYPE_VOLUME)) == NULL) { 32107265Sahrens zcmd_free_nvlists(&zc); 3211789Sahrens return (-1); 3212789Sahrens } 3213789Sahrens 32142199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 32152676Seschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 32164543Smarks if (ZFS_IS_VOLUME(zhp)) 32174543Smarks zc.zc_objset_type = DMU_OST_ZVOL; 32184543Smarks else 32194543Smarks zc.zc_objset_type = DMU_OST_ZFS; 32202199Sahrens zc.zc_cookie = recursive; 32214543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 32222199Sahrens 32237265Sahrens zcmd_free_nvlists(&zc); 32247265Sahrens 32252199Sahrens /* 32262199Sahrens * if it was recursive, the one that actually failed will be in 32272199Sahrens * zc.zc_name. 32282199Sahrens */ 322910588SEric.Taylor@Sun.COM if (ret != 0) { 32304543Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 32314543Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 323210588SEric.Taylor@Sun.COM (void) zfs_standard_error(hdl, errno, errbuf); 32332199Sahrens } 3234789Sahrens 3235789Sahrens zfs_close(zhp); 3236789Sahrens 3237789Sahrens return (ret); 3238789Sahrens } 3239789Sahrens 3240789Sahrens /* 32411294Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 32421294Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 32431294Slling * is a dependent and we should just destroy it without checking the transaction 32441294Slling * group. 3245789Sahrens */ 32461294Slling typedef struct rollback_data { 32471294Slling const char *cb_target; /* the snapshot */ 32481294Slling uint64_t cb_create; /* creation time reference */ 32495749Sahrens boolean_t cb_error; 32502082Seschrock boolean_t cb_dependent; 32515749Sahrens boolean_t cb_force; 32521294Slling } rollback_data_t; 32531294Slling 32541294Slling static int 32551294Slling rollback_destroy(zfs_handle_t *zhp, void *data) 32561294Slling { 32571294Slling rollback_data_t *cbp = data; 32581294Slling 32591294Slling if (!cbp->cb_dependent) { 32601294Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 32611294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 32621294Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 32631294Slling cbp->cb_create) { 32644543Smarks char *logstr; 32651294Slling 32662082Seschrock cbp->cb_dependent = B_TRUE; 32675446Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32685446Sahrens rollback_destroy, cbp); 32692082Seschrock cbp->cb_dependent = B_FALSE; 32701294Slling 32714543Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 32724543Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 327310242Schris.kirby@sun.com cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 32744543Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 32751294Slling } 32761294Slling } else { 32775749Sahrens /* We must destroy this clone; first unmount it */ 32785749Sahrens prop_changelist_t *clp; 32795749Sahrens 32807366STim.Haley@Sun.COM clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 32815749Sahrens cbp->cb_force ? MS_FORCE: 0); 32825749Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 32835749Sahrens cbp->cb_error = B_TRUE; 32845749Sahrens zfs_close(zhp); 32855749Sahrens return (0); 32865749Sahrens } 328710242Schris.kirby@sun.com if (zfs_destroy(zhp, B_FALSE) != 0) 32885749Sahrens cbp->cb_error = B_TRUE; 32895749Sahrens else 32905749Sahrens changelist_remove(clp, zhp->zfs_name); 32915751Sahrens (void) changelist_postfix(clp); 32925749Sahrens changelist_free(clp); 32931294Slling } 32941294Slling 32951294Slling zfs_close(zhp); 32961294Slling return (0); 32971294Slling } 32981294Slling 32991294Slling /* 33005446Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33015446Sahrens * data changes since then and making it the active dataset. 33025446Sahrens * 33035446Sahrens * Any snapshots more recent than the target are destroyed, along with 33045446Sahrens * their dependents. 33051294Slling */ 33065446Sahrens int 33075749Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3308789Sahrens { 33095446Sahrens rollback_data_t cb = { 0 }; 33105446Sahrens int err; 3311789Sahrens zfs_cmd_t zc = { 0 }; 33125713Srm160521 boolean_t restore_resv = 0; 33135713Srm160521 uint64_t old_volsize, new_volsize; 33145713Srm160521 zfs_prop_t resv_prop; 3315789Sahrens 3316789Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3317789Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3318789Sahrens 33195446Sahrens /* 33205446Sahrens * Destroy all recent snapshots and its dependends. 33215446Sahrens */ 33225749Sahrens cb.cb_force = force; 33235446Sahrens cb.cb_target = snap->zfs_name; 33245446Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33255446Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33265446Sahrens 33275749Sahrens if (cb.cb_error) 33285749Sahrens return (-1); 33295446Sahrens 33305446Sahrens /* 33315446Sahrens * Now that we have verified that the snapshot is the latest, 33325446Sahrens * rollback to the given snapshot. 33335446Sahrens */ 33345446Sahrens 33355713Srm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33365713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33375713Srm160521 return (-1); 33385713Srm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33395713Srm160521 restore_resv = 33405713Srm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33415713Srm160521 } 3342789Sahrens 3343789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3344789Sahrens 33452676Seschrock if (ZFS_IS_VOLUME(zhp)) 3346789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3347789Sahrens else 3348789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3349789Sahrens 3350789Sahrens /* 33515446Sahrens * We rely on zfs_iter_children() to verify that there are no 33525446Sahrens * newer snapshots for the given dataset. Therefore, we can 33535446Sahrens * simply pass the name on to the ioctl() call. There is still 33545446Sahrens * an unlikely race condition where the user has taken a 33555446Sahrens * snapshot since we verified that this was the most recent. 33565713Srm160521 * 3357789Sahrens */ 33585446Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33593237Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33602082Seschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33612082Seschrock zhp->zfs_name); 33625717Srm160521 return (err); 33635717Srm160521 } 33645713Srm160521 33655713Srm160521 /* 33665713Srm160521 * For volumes, if the pre-rollback volsize matched the pre- 33675713Srm160521 * rollback reservation and the volsize has changed then set 33685713Srm160521 * the reservation property to the post-rollback volsize. 33695713Srm160521 * Make a new handle since the rollback closed the dataset. 33705713Srm160521 */ 33715717Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 33725717Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 33735713Srm160521 if (restore_resv) { 33745713Srm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33755713Srm160521 if (old_volsize != new_volsize) 33765717Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 33775717Srm160521 new_volsize); 33785713Srm160521 } 33795713Srm160521 zfs_close(zhp); 3380789Sahrens } 33815446Sahrens return (err); 33821294Slling } 33831294Slling 33841294Slling /* 3385789Sahrens * Iterate over all dependents for a given dataset. This includes both 3386789Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3387789Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3388789Sahrens * libzfs_graph.c. 3389789Sahrens */ 3390789Sahrens int 33912474Seschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33922474Seschrock zfs_iter_f func, void *data) 3393789Sahrens { 3394789Sahrens char **dependents; 3395789Sahrens size_t count; 3396789Sahrens int i; 3397789Sahrens zfs_handle_t *child; 3398789Sahrens int ret = 0; 3399789Sahrens 34002474Seschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34012474Seschrock &dependents, &count) != 0) 34022474Seschrock return (-1); 34032474Seschrock 3404789Sahrens for (i = 0; i < count; i++) { 34052082Seschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 34062082Seschrock dependents[i])) == NULL) 3407789Sahrens continue; 3408789Sahrens 3409789Sahrens if ((ret = func(child, data)) != 0) 3410789Sahrens break; 3411789Sahrens } 3412789Sahrens 3413789Sahrens for (i = 0; i < count; i++) 3414789Sahrens free(dependents[i]); 3415789Sahrens free(dependents); 3416789Sahrens 3417789Sahrens return (ret); 3418789Sahrens } 3419789Sahrens 3420789Sahrens /* 3421789Sahrens * Renames the given dataset. 3422789Sahrens */ 3423789Sahrens int 34244490Svb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3425789Sahrens { 3426789Sahrens int ret; 3427789Sahrens zfs_cmd_t zc = { 0 }; 3428789Sahrens char *delim; 34294007Smmusante prop_changelist_t *cl = NULL; 34304007Smmusante zfs_handle_t *zhrp = NULL; 34314007Smmusante char *parentname = NULL; 3432789Sahrens char parent[ZFS_MAXNAMELEN]; 34332082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 34342082Seschrock char errbuf[1024]; 3435789Sahrens 3436789Sahrens /* if we have the same exact name, just return success */ 3437789Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3438789Sahrens return (0); 3439789Sahrens 34402082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 34412082Seschrock "cannot rename to '%s'"), target); 34422082Seschrock 3443789Sahrens /* 3444789Sahrens * Make sure the target name is valid 3445789Sahrens */ 3446789Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 34472665Snd150628 if ((strchr(target, '@') == NULL) || 34482665Snd150628 *target == '@') { 34492665Snd150628 /* 34502665Snd150628 * Snapshot target name is abbreviated, 34512665Snd150628 * reconstruct full dataset name 34522665Snd150628 */ 34532665Snd150628 (void) strlcpy(parent, zhp->zfs_name, 34542665Snd150628 sizeof (parent)); 34552665Snd150628 delim = strchr(parent, '@'); 34562665Snd150628 if (strchr(target, '@') == NULL) 34572665Snd150628 *(++delim) = '\0'; 34582665Snd150628 else 34592665Snd150628 *delim = '\0'; 34602665Snd150628 (void) strlcat(parent, target, sizeof (parent)); 34612665Snd150628 target = parent; 34622665Snd150628 } else { 34632665Snd150628 /* 34642665Snd150628 * Make sure we're renaming within the same dataset. 34652665Snd150628 */ 34662665Snd150628 delim = strchr(target, '@'); 34672665Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 34682665Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 34692665Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34702665Snd150628 "snapshots must be part of same " 34712665Snd150628 "dataset")); 34722665Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 34733912Slling errbuf)); 34742665Snd150628 } 3475789Sahrens } 34765326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 34772665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3478789Sahrens } else { 34794007Smmusante if (recursive) { 34804007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34814007Smmusante "recursive rename must be a snapshot")); 34824007Smmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 34834007Smmusante } 34844007Smmusante 34855326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 34862665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 34872676Seschrock 3488789Sahrens /* validate parents */ 348911497SMark.Musante@Sun.COM if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3490789Sahrens return (-1); 3491789Sahrens 3492789Sahrens /* make sure we're in the same pool */ 3493789Sahrens verify((delim = strchr(target, '/')) != NULL); 3494789Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3495789Sahrens zhp->zfs_name[delim - target] != '/') { 34962082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34972082Seschrock "datasets must be within same pool")); 34982082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3499789Sahrens } 35002440Snd150628 35012440Snd150628 /* new name cannot be a child of the current dataset name */ 350211497SMark.Musante@Sun.COM if (is_descendant(zhp->zfs_name, target)) { 35032440Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 350411497SMark.Musante@Sun.COM "New dataset name cannot be a descendant of " 35052440Snd150628 "current dataset name")); 35062440Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 35072440Snd150628 } 3508789Sahrens } 3509789Sahrens 35102082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 35112082Seschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 35122082Seschrock 3513789Sahrens if (getzoneid() == GLOBAL_ZONEID && 3514789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 35152082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35162082Seschrock "dataset is used in a non-global zone")); 35172082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3518789Sahrens } 3519789Sahrens 35204007Smmusante if (recursive) { 35214007Smmusante 35224183Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 35234183Smmusante if (parentname == NULL) { 35244183Smmusante ret = -1; 35254183Smmusante goto error; 35264183Smmusante } 35274007Smmusante delim = strchr(parentname, '@'); 35284007Smmusante *delim = '\0'; 35295094Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 35304007Smmusante if (zhrp == NULL) { 35314183Smmusante ret = -1; 35324183Smmusante goto error; 35334007Smmusante } 35344007Smmusante 35354007Smmusante } else { 35367366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 35374007Smmusante return (-1); 35384007Smmusante 35394007Smmusante if (changelist_haszonedchild(cl)) { 35404007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35414007Smmusante "child dataset with inherited mountpoint is used " 35424007Smmusante "in a non-global zone")); 35434007Smmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 35444007Smmusante goto error; 35454007Smmusante } 35464007Smmusante 35474007Smmusante if ((ret = changelist_prefix(cl)) != 0) 35484007Smmusante goto error; 3549789Sahrens } 3550789Sahrens 35512676Seschrock if (ZFS_IS_VOLUME(zhp)) 3552789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3553789Sahrens else 3554789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3555789Sahrens 35562665Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35572676Seschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 35582665Snd150628 35594007Smmusante zc.zc_cookie = recursive; 35604007Smmusante 35614543Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 35624007Smmusante /* 35634007Smmusante * if it was recursive, the one that actually failed will 35644007Smmusante * be in zc.zc_name 35654007Smmusante */ 35664007Smmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35675367Sahrens "cannot rename '%s'"), zc.zc_name); 35684007Smmusante 35694007Smmusante if (recursive && errno == EEXIST) { 35704007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35714007Smmusante "a child dataset already has a snapshot " 35724007Smmusante "with the new name")); 35734801Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 35744007Smmusante } else { 35754007Smmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 35764007Smmusante } 3577789Sahrens 3578789Sahrens /* 3579789Sahrens * On failure, we still want to remount any filesystems that 3580789Sahrens * were previously mounted, so we don't alter the system state. 3581789Sahrens */ 358210588SEric.Taylor@Sun.COM if (!recursive) 35834007Smmusante (void) changelist_postfix(cl); 3584789Sahrens } else { 358510588SEric.Taylor@Sun.COM if (!recursive) { 35864007Smmusante changelist_rename(cl, zfs_get_name(zhp), target); 35874007Smmusante ret = changelist_postfix(cl); 35884007Smmusante } 3589789Sahrens } 3590789Sahrens 3591789Sahrens error: 35924007Smmusante if (parentname) { 35934007Smmusante free(parentname); 35944007Smmusante } 35954007Smmusante if (zhrp) { 35964007Smmusante zfs_close(zhrp); 35974007Smmusante } 35984007Smmusante if (cl) { 35994007Smmusante changelist_free(cl); 36004007Smmusante } 3601789Sahrens return (ret); 3602789Sahrens } 3603789Sahrens 36042676Seschrock nvlist_t * 36052676Seschrock zfs_get_user_props(zfs_handle_t *zhp) 36062676Seschrock { 36072676Seschrock return (zhp->zfs_user_props); 36082676Seschrock } 36092676Seschrock 361011022STom.Erickson@Sun.COM nvlist_t * 361111022STom.Erickson@Sun.COM zfs_get_recvd_props(zfs_handle_t *zhp) 361211022STom.Erickson@Sun.COM { 361311022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 361411022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 361511022STom.Erickson@Sun.COM return (NULL); 361611022STom.Erickson@Sun.COM return (zhp->zfs_recvd_props); 361711022STom.Erickson@Sun.COM } 361811022STom.Erickson@Sun.COM 36192676Seschrock /* 36203912Slling * This function is used by 'zfs list' to determine the exact set of columns to 36213912Slling * display, and their maximum widths. This does two main things: 36223912Slling * 36233912Slling * - If this is a list of all properties, then expand the list to include 36243912Slling * all native properties, and set a flag so that for each dataset we look 36253912Slling * for new unique user properties and add them to the list. 36263912Slling * 36273912Slling * - For non fixed-width properties, keep track of the maximum width seen 362811022STom.Erickson@Sun.COM * so that we can size the column appropriately. If the user has 362911022STom.Erickson@Sun.COM * requested received property values, we also need to compute the width 363011022STom.Erickson@Sun.COM * of the RECEIVED column. 36313912Slling */ 36323912Slling int 363311022STom.Erickson@Sun.COM zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 36343912Slling { 36353912Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 36365094Slling zprop_list_t *entry; 36375094Slling zprop_list_t **last, **start; 36383912Slling nvlist_t *userprops, *propval; 36393912Slling nvpair_t *elem; 36403912Slling char *strval; 36413912Slling char buf[ZFS_MAXPROPLEN]; 36423912Slling 36435094Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 36443912Slling return (-1); 36452676Seschrock 36462676Seschrock userprops = zfs_get_user_props(zhp); 36472676Seschrock 36482676Seschrock entry = *plp; 36492676Seschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 36502676Seschrock /* 36512676Seschrock * Go through and add any user properties as necessary. We 36522676Seschrock * start by incrementing our list pointer to the first 36532676Seschrock * non-native property. 36542676Seschrock */ 36552676Seschrock start = plp; 36562676Seschrock while (*start != NULL) { 36575094Slling if ((*start)->pl_prop == ZPROP_INVAL) 36582676Seschrock break; 36592676Seschrock start = &(*start)->pl_next; 36602676Seschrock } 36612676Seschrock 36622676Seschrock elem = NULL; 36632676Seschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 36642676Seschrock /* 36652676Seschrock * See if we've already found this property in our list. 36662676Seschrock */ 36672676Seschrock for (last = start; *last != NULL; 36682676Seschrock last = &(*last)->pl_next) { 36692676Seschrock if (strcmp((*last)->pl_user_prop, 36702676Seschrock nvpair_name(elem)) == 0) 36712676Seschrock break; 36722676Seschrock } 36732676Seschrock 36742676Seschrock if (*last == NULL) { 36752676Seschrock if ((entry = zfs_alloc(hdl, 36765094Slling sizeof (zprop_list_t))) == NULL || 36772676Seschrock ((entry->pl_user_prop = zfs_strdup(hdl, 36782676Seschrock nvpair_name(elem)))) == NULL) { 36792676Seschrock free(entry); 36802676Seschrock return (-1); 36812676Seschrock } 36822676Seschrock 36835094Slling entry->pl_prop = ZPROP_INVAL; 36842676Seschrock entry->pl_width = strlen(nvpair_name(elem)); 36852676Seschrock entry->pl_all = B_TRUE; 36862676Seschrock *last = entry; 36872676Seschrock } 36882676Seschrock } 36892676Seschrock } 36902676Seschrock 36912676Seschrock /* 36922676Seschrock * Now go through and check the width of any non-fixed columns 36932676Seschrock */ 36942676Seschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 36952676Seschrock if (entry->pl_fixed) 36962676Seschrock continue; 36972676Seschrock 36985094Slling if (entry->pl_prop != ZPROP_INVAL) { 36992676Seschrock if (zfs_prop_get(zhp, entry->pl_prop, 37002676Seschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 37012676Seschrock if (strlen(buf) > entry->pl_width) 37022676Seschrock entry->pl_width = strlen(buf); 37032676Seschrock } 370411022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 370511022STom.Erickson@Sun.COM zfs_prop_to_name(entry->pl_prop), 370611022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 370711022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 370811022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 370911022STom.Erickson@Sun.COM } else { 371011022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 371111022STom.Erickson@Sun.COM &propval) == 0) { 371211022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, 371311022STom.Erickson@Sun.COM ZPROP_VALUE, &strval) == 0); 371411022STom.Erickson@Sun.COM if (strlen(strval) > entry->pl_width) 371511022STom.Erickson@Sun.COM entry->pl_width = strlen(strval); 371611022STom.Erickson@Sun.COM } 371711022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 371811022STom.Erickson@Sun.COM entry->pl_user_prop, 371911022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 372011022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 372111022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 37222676Seschrock } 37232676Seschrock } 37242676Seschrock 37252676Seschrock return (0); 37262676Seschrock } 37274543Smarks 37284543Smarks int 37294543Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 37308845Samw@Sun.COM char *resource, void *export, void *sharetab, 37318845Samw@Sun.COM int sharemax, zfs_share_op_t operation) 37324543Smarks { 37334543Smarks zfs_cmd_t zc = { 0 }; 37344543Smarks int error; 37354543Smarks 37364543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37374543Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37388845Samw@Sun.COM if (resource) 37398845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 37404543Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 37414543Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 37425331Samw zc.zc_share.z_sharetype = operation; 37434543Smarks zc.zc_share.z_sharemax = sharemax; 37444543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 37454543Smarks return (error); 37464543Smarks } 37478802SSanjeev.Bagewadi@Sun.COM 37488802SSanjeev.Bagewadi@Sun.COM void 37498802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37508802SSanjeev.Bagewadi@Sun.COM { 37518802SSanjeev.Bagewadi@Sun.COM nvpair_t *curr; 37528802SSanjeev.Bagewadi@Sun.COM 37538802SSanjeev.Bagewadi@Sun.COM /* 37548802SSanjeev.Bagewadi@Sun.COM * Keep a reference to the props-table against which we prune the 37558802SSanjeev.Bagewadi@Sun.COM * properties. 37568802SSanjeev.Bagewadi@Sun.COM */ 37578802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table = props; 37588802SSanjeev.Bagewadi@Sun.COM 37598802SSanjeev.Bagewadi@Sun.COM curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37608802SSanjeev.Bagewadi@Sun.COM 37618802SSanjeev.Bagewadi@Sun.COM while (curr) { 37628802SSanjeev.Bagewadi@Sun.COM zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37638802SSanjeev.Bagewadi@Sun.COM nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37648802SSanjeev.Bagewadi@Sun.COM 37659396SMatthew.Ahrens@Sun.COM /* 376610960SEric.Schrock@Sun.COM * User properties will result in ZPROP_INVAL, and since we 376710960SEric.Schrock@Sun.COM * only know how to prune standard ZFS properties, we always 376810960SEric.Schrock@Sun.COM * leave these in the list. This can also happen if we 376910960SEric.Schrock@Sun.COM * encounter an unknown DSL property (when running older 377010960SEric.Schrock@Sun.COM * software, for example). 37719396SMatthew.Ahrens@Sun.COM */ 37729396SMatthew.Ahrens@Sun.COM if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 37738802SSanjeev.Bagewadi@Sun.COM (void) nvlist_remove(zhp->zfs_props, 37748802SSanjeev.Bagewadi@Sun.COM nvpair_name(curr), nvpair_type(curr)); 37758802SSanjeev.Bagewadi@Sun.COM curr = next; 37768802SSanjeev.Bagewadi@Sun.COM } 37778802SSanjeev.Bagewadi@Sun.COM } 37788845Samw@Sun.COM 37798845Samw@Sun.COM static int 37808845Samw@Sun.COM zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 37818845Samw@Sun.COM zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 37828845Samw@Sun.COM { 37838845Samw@Sun.COM zfs_cmd_t zc = { 0 }; 37848845Samw@Sun.COM nvlist_t *nvlist = NULL; 37858845Samw@Sun.COM int error; 37868845Samw@Sun.COM 37878845Samw@Sun.COM (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37888845Samw@Sun.COM (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37898845Samw@Sun.COM zc.zc_cookie = (uint64_t)cmd; 37908845Samw@Sun.COM 37918845Samw@Sun.COM if (cmd == ZFS_SMB_ACL_RENAME) { 37928845Samw@Sun.COM if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 37938845Samw@Sun.COM (void) no_memory(hdl); 37948845Samw@Sun.COM return (NULL); 37958845Samw@Sun.COM } 37968845Samw@Sun.COM } 37978845Samw@Sun.COM 37988845Samw@Sun.COM switch (cmd) { 37998845Samw@Sun.COM case ZFS_SMB_ACL_ADD: 38008845Samw@Sun.COM case ZFS_SMB_ACL_REMOVE: 38018845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 38028845Samw@Sun.COM break; 38038845Samw@Sun.COM case ZFS_SMB_ACL_RENAME: 38048845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 38058845Samw@Sun.COM resource1) != 0) { 38068845Samw@Sun.COM (void) no_memory(hdl); 38078845Samw@Sun.COM return (-1); 38088845Samw@Sun.COM } 38098845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 38108845Samw@Sun.COM resource2) != 0) { 38118845Samw@Sun.COM (void) no_memory(hdl); 38128845Samw@Sun.COM return (-1); 38138845Samw@Sun.COM } 38148845Samw@Sun.COM if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 38158845Samw@Sun.COM nvlist_free(nvlist); 38168845Samw@Sun.COM return (-1); 38178845Samw@Sun.COM } 38188845Samw@Sun.COM break; 38198845Samw@Sun.COM case ZFS_SMB_ACL_PURGE: 38208845Samw@Sun.COM break; 38218845Samw@Sun.COM default: 38228845Samw@Sun.COM return (-1); 38238845Samw@Sun.COM } 38248845Samw@Sun.COM error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 38258845Samw@Sun.COM if (nvlist) 38268845Samw@Sun.COM nvlist_free(nvlist); 38278845Samw@Sun.COM return (error); 38288845Samw@Sun.COM } 38298845Samw@Sun.COM 38308845Samw@Sun.COM int 38318845Samw@Sun.COM zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 38328845Samw@Sun.COM char *path, char *resource) 38338845Samw@Sun.COM { 38348845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 38358845Samw@Sun.COM resource, NULL)); 38368845Samw@Sun.COM } 38378845Samw@Sun.COM 38388845Samw@Sun.COM int 38398845Samw@Sun.COM zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 38408845Samw@Sun.COM char *path, char *resource) 38418845Samw@Sun.COM { 38428845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 38438845Samw@Sun.COM resource, NULL)); 38448845Samw@Sun.COM } 38458845Samw@Sun.COM 38468845Samw@Sun.COM int 38478845Samw@Sun.COM zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 38488845Samw@Sun.COM { 38498845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 38508845Samw@Sun.COM NULL, NULL)); 38518845Samw@Sun.COM } 38528845Samw@Sun.COM 38538845Samw@Sun.COM int 38548845Samw@Sun.COM zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 38558845Samw@Sun.COM char *oldname, char *newname) 38568845Samw@Sun.COM { 38578845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 38588845Samw@Sun.COM oldname, newname)); 38598845Samw@Sun.COM } 38609396SMatthew.Ahrens@Sun.COM 38619396SMatthew.Ahrens@Sun.COM int 38629396SMatthew.Ahrens@Sun.COM zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 38639396SMatthew.Ahrens@Sun.COM zfs_userspace_cb_t func, void *arg) 38649396SMatthew.Ahrens@Sun.COM { 38659396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 38669396SMatthew.Ahrens@Sun.COM int error; 38679396SMatthew.Ahrens@Sun.COM zfs_useracct_t buf[100]; 38689396SMatthew.Ahrens@Sun.COM 38699396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 38709396SMatthew.Ahrens@Sun.COM 38719396SMatthew.Ahrens@Sun.COM zc.zc_objset_type = type; 38729396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst = (uintptr_t)buf; 38739396SMatthew.Ahrens@Sun.COM 38749396SMatthew.Ahrens@Sun.COM /* CONSTCOND */ 38759396SMatthew.Ahrens@Sun.COM while (1) { 38769396SMatthew.Ahrens@Sun.COM zfs_useracct_t *zua = buf; 38779396SMatthew.Ahrens@Sun.COM 38789396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size = sizeof (buf); 38799396SMatthew.Ahrens@Sun.COM error = ioctl(zhp->zfs_hdl->libzfs_fd, 38809396SMatthew.Ahrens@Sun.COM ZFS_IOC_USERSPACE_MANY, &zc); 38819396SMatthew.Ahrens@Sun.COM if (error || zc.zc_nvlist_dst_size == 0) 38829396SMatthew.Ahrens@Sun.COM break; 38839396SMatthew.Ahrens@Sun.COM 38849396SMatthew.Ahrens@Sun.COM while (zc.zc_nvlist_dst_size > 0) { 38859554SMatthew.Ahrens@Sun.COM error = func(arg, zua->zu_domain, zua->zu_rid, 38869554SMatthew.Ahrens@Sun.COM zua->zu_space); 38879554SMatthew.Ahrens@Sun.COM if (error != 0) 38889554SMatthew.Ahrens@Sun.COM return (error); 38899396SMatthew.Ahrens@Sun.COM zua++; 38909396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 38919396SMatthew.Ahrens@Sun.COM } 38929396SMatthew.Ahrens@Sun.COM } 38939396SMatthew.Ahrens@Sun.COM 38949396SMatthew.Ahrens@Sun.COM return (error); 38959396SMatthew.Ahrens@Sun.COM } 389610242Schris.kirby@sun.com 389710242Schris.kirby@sun.com int 389810242Schris.kirby@sun.com zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 389911417SChris.Kirby@sun.com boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 390010242Schris.kirby@sun.com { 390110242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 390210242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 390310242Schris.kirby@sun.com 390410242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 390510242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 390610342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 390710342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 390810342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 390910242Schris.kirby@sun.com zc.zc_cookie = recursive; 391010342Schris.kirby@sun.com zc.zc_temphold = temphold; 391110242Schris.kirby@sun.com 391210242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 391310242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 391410242Schris.kirby@sun.com 391510242Schris.kirby@sun.com /* 391610242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 391710242Schris.kirby@sun.com * zc.zc_name. 391810242Schris.kirby@sun.com */ 391910242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 392010242Schris.kirby@sun.com "cannot hold '%s@%s'"), zc.zc_name, snapname); 392110242Schris.kirby@sun.com switch (errno) { 392210951SChris.Kirby@sun.com case E2BIG: 392310951SChris.Kirby@sun.com /* 392410951SChris.Kirby@sun.com * Temporary tags wind up having the ds object id 392510951SChris.Kirby@sun.com * prepended. So even if we passed the length check 392610951SChris.Kirby@sun.com * above, it's still possible for the tag to wind 392710951SChris.Kirby@sun.com * up being slightly too long. 392810951SChris.Kirby@sun.com */ 392910951SChris.Kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 393010242Schris.kirby@sun.com case ENOTSUP: 393110242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 393210242Schris.kirby@sun.com "pool must be upgraded")); 393310242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 393410242Schris.kirby@sun.com case EINVAL: 393510242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 393610242Schris.kirby@sun.com case EEXIST: 393710242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 393811417SChris.Kirby@sun.com case ENOENT: 393911417SChris.Kirby@sun.com if (enoent_ok) 394011417SChris.Kirby@sun.com return (0); 394111417SChris.Kirby@sun.com /* FALLTHROUGH */ 394210242Schris.kirby@sun.com default: 394310242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 394410242Schris.kirby@sun.com } 394510242Schris.kirby@sun.com } 394610242Schris.kirby@sun.com 394710242Schris.kirby@sun.com return (0); 394810242Schris.kirby@sun.com } 394910242Schris.kirby@sun.com 395010342Schris.kirby@sun.com struct hold_range_arg { 395110342Schris.kirby@sun.com zfs_handle_t *origin; 395210342Schris.kirby@sun.com const char *fromsnap; 395310342Schris.kirby@sun.com const char *tosnap; 395410342Schris.kirby@sun.com char lastsnapheld[ZFS_MAXNAMELEN]; 395510342Schris.kirby@sun.com const char *tag; 395610342Schris.kirby@sun.com boolean_t temphold; 395710342Schris.kirby@sun.com boolean_t seento; 395810342Schris.kirby@sun.com boolean_t seenfrom; 395910342Schris.kirby@sun.com boolean_t holding; 396011546SChris.Kirby@sun.com boolean_t recursive; 396111814SChris.Kirby@sun.com snapfilter_cb_t *filter_cb; 396211814SChris.Kirby@sun.com void *filter_cb_arg; 396310342Schris.kirby@sun.com }; 396410342Schris.kirby@sun.com 396510342Schris.kirby@sun.com static int 396610342Schris.kirby@sun.com zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 396710342Schris.kirby@sun.com { 396810342Schris.kirby@sun.com struct hold_range_arg *hra = arg; 396910342Schris.kirby@sun.com const char *thissnap; 397010342Schris.kirby@sun.com int error; 397110342Schris.kirby@sun.com 397210342Schris.kirby@sun.com thissnap = strchr(zfs_get_name(zhp), '@') + 1; 397310342Schris.kirby@sun.com 397410342Schris.kirby@sun.com if (hra->fromsnap && !hra->seenfrom && 397510342Schris.kirby@sun.com strcmp(hra->fromsnap, thissnap) == 0) 397610342Schris.kirby@sun.com hra->seenfrom = B_TRUE; 397710342Schris.kirby@sun.com 397810342Schris.kirby@sun.com /* snap is older or newer than the desired range, ignore it */ 397910342Schris.kirby@sun.com if (hra->seento || !hra->seenfrom) { 398010342Schris.kirby@sun.com zfs_close(zhp); 398110342Schris.kirby@sun.com return (0); 398210342Schris.kirby@sun.com } 398310342Schris.kirby@sun.com 398411814SChris.Kirby@sun.com if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 398511814SChris.Kirby@sun.com hra->seento = B_TRUE; 398611814SChris.Kirby@sun.com 398711814SChris.Kirby@sun.com if (hra->filter_cb != NULL && 398811814SChris.Kirby@sun.com hra->filter_cb(zhp, hra->filter_cb_arg) == B_FALSE) { 398911814SChris.Kirby@sun.com zfs_close(zhp); 399011814SChris.Kirby@sun.com return (0); 399111814SChris.Kirby@sun.com } 399211814SChris.Kirby@sun.com 399310342Schris.kirby@sun.com if (hra->holding) { 399411417SChris.Kirby@sun.com /* We could be racing with destroy, so ignore ENOENT. */ 399511546SChris.Kirby@sun.com error = zfs_hold(hra->origin, thissnap, hra->tag, 399611546SChris.Kirby@sun.com hra->recursive, hra->temphold, B_TRUE); 399710342Schris.kirby@sun.com if (error == 0) { 399810342Schris.kirby@sun.com (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 399910342Schris.kirby@sun.com sizeof (hra->lastsnapheld)); 400010342Schris.kirby@sun.com } 400110342Schris.kirby@sun.com } else { 400211546SChris.Kirby@sun.com error = zfs_release(hra->origin, thissnap, hra->tag, 400311546SChris.Kirby@sun.com hra->recursive); 400410342Schris.kirby@sun.com } 400510342Schris.kirby@sun.com 400610342Schris.kirby@sun.com zfs_close(zhp); 400710342Schris.kirby@sun.com return (error); 400810342Schris.kirby@sun.com } 400910342Schris.kirby@sun.com 401010342Schris.kirby@sun.com /* 401110342Schris.kirby@sun.com * Add a user hold on the set of snapshots starting with fromsnap up to 401210342Schris.kirby@sun.com * and including tosnap. If we're unable to to acquire a particular hold, 401310342Schris.kirby@sun.com * undo any holds up to that point. 401410342Schris.kirby@sun.com */ 401510342Schris.kirby@sun.com int 401610342Schris.kirby@sun.com zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 401711814SChris.Kirby@sun.com const char *tag, boolean_t recursive, boolean_t temphold, 401811814SChris.Kirby@sun.com snapfilter_cb_t filter_cb, void *cbarg) 401910342Schris.kirby@sun.com { 402010342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 402110342Schris.kirby@sun.com int error; 402210342Schris.kirby@sun.com 402310342Schris.kirby@sun.com arg.origin = zhp; 402410342Schris.kirby@sun.com arg.fromsnap = fromsnap; 402510342Schris.kirby@sun.com arg.tosnap = tosnap; 402610342Schris.kirby@sun.com arg.tag = tag; 402710342Schris.kirby@sun.com arg.temphold = temphold; 402810342Schris.kirby@sun.com arg.holding = B_TRUE; 402911546SChris.Kirby@sun.com arg.recursive = recursive; 403011668SChris.Kirby@sun.com arg.seenfrom = (fromsnap == NULL); 403111814SChris.Kirby@sun.com arg.filter_cb = filter_cb; 403211814SChris.Kirby@sun.com arg.filter_cb_arg = cbarg; 403310342Schris.kirby@sun.com 403410342Schris.kirby@sun.com error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 403510342Schris.kirby@sun.com 403610342Schris.kirby@sun.com /* 403710342Schris.kirby@sun.com * Make sure we either hold the entire range or none. 403810342Schris.kirby@sun.com */ 403910342Schris.kirby@sun.com if (error && arg.lastsnapheld[0] != '\0') { 404010342Schris.kirby@sun.com (void) zfs_release_range(zhp, fromsnap, 404111546SChris.Kirby@sun.com (const char *)arg.lastsnapheld, tag, recursive); 404210342Schris.kirby@sun.com } 404310342Schris.kirby@sun.com return (error); 404410342Schris.kirby@sun.com } 404510342Schris.kirby@sun.com 404610242Schris.kirby@sun.com int 404710242Schris.kirby@sun.com zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 404810242Schris.kirby@sun.com boolean_t recursive) 404910242Schris.kirby@sun.com { 405010242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 405110242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 405210242Schris.kirby@sun.com 405310242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 405410242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 405510342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 405610342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 405710342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 405810242Schris.kirby@sun.com zc.zc_cookie = recursive; 405910242Schris.kirby@sun.com 406010242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 406110242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 406210242Schris.kirby@sun.com 406310242Schris.kirby@sun.com /* 406410242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 406510242Schris.kirby@sun.com * zc.zc_name. 406610242Schris.kirby@sun.com */ 406710242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 406811546SChris.Kirby@sun.com "cannot release '%s' from '%s@%s'"), tag, zc.zc_name, 406911546SChris.Kirby@sun.com snapname); 407010242Schris.kirby@sun.com switch (errno) { 407110242Schris.kirby@sun.com case ESRCH: 407210242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 407310242Schris.kirby@sun.com case ENOTSUP: 407410242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 407510242Schris.kirby@sun.com "pool must be upgraded")); 407610242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 407710242Schris.kirby@sun.com case EINVAL: 407810242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 407910242Schris.kirby@sun.com default: 408010242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 408110242Schris.kirby@sun.com } 408210242Schris.kirby@sun.com } 408310242Schris.kirby@sun.com 408410242Schris.kirby@sun.com return (0); 408510242Schris.kirby@sun.com } 408610342Schris.kirby@sun.com 408710342Schris.kirby@sun.com /* 408810342Schris.kirby@sun.com * Release a user hold from the set of snapshots starting with fromsnap 408910342Schris.kirby@sun.com * up to and including tosnap. 409010342Schris.kirby@sun.com */ 409110342Schris.kirby@sun.com int 409210342Schris.kirby@sun.com zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 409311546SChris.Kirby@sun.com const char *tag, boolean_t recursive) 409410342Schris.kirby@sun.com { 409510342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 409610342Schris.kirby@sun.com 409710342Schris.kirby@sun.com arg.origin = zhp; 409810342Schris.kirby@sun.com arg.fromsnap = fromsnap; 409910342Schris.kirby@sun.com arg.tosnap = tosnap; 410010342Schris.kirby@sun.com arg.tag = tag; 410111546SChris.Kirby@sun.com arg.recursive = recursive; 410211668SChris.Kirby@sun.com arg.seenfrom = (fromsnap == NULL); 410310342Schris.kirby@sun.com 410410342Schris.kirby@sun.com return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 410510342Schris.kirby@sun.com } 410611449SEric.Taylor@Sun.COM 410711449SEric.Taylor@Sun.COM uint64_t 410811449SEric.Taylor@Sun.COM zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 410911449SEric.Taylor@Sun.COM { 411011449SEric.Taylor@Sun.COM uint64_t numdb; 411111449SEric.Taylor@Sun.COM uint64_t nblocks, volblocksize; 411211449SEric.Taylor@Sun.COM int ncopies; 411311449SEric.Taylor@Sun.COM char *strval; 411411449SEric.Taylor@Sun.COM 411511449SEric.Taylor@Sun.COM if (nvlist_lookup_string(props, 411611449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 411711449SEric.Taylor@Sun.COM ncopies = atoi(strval); 411811449SEric.Taylor@Sun.COM else 411911449SEric.Taylor@Sun.COM ncopies = 1; 412011449SEric.Taylor@Sun.COM if (nvlist_lookup_uint64(props, 412111449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 412211449SEric.Taylor@Sun.COM &volblocksize) != 0) 412311449SEric.Taylor@Sun.COM volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 412411449SEric.Taylor@Sun.COM nblocks = volsize/volblocksize; 412511449SEric.Taylor@Sun.COM /* start with metadnode L0-L6 */ 412611449SEric.Taylor@Sun.COM numdb = 7; 412711449SEric.Taylor@Sun.COM /* calculate number of indirects */ 412811449SEric.Taylor@Sun.COM while (nblocks > 1) { 412911449SEric.Taylor@Sun.COM nblocks += DNODES_PER_LEVEL - 1; 413011449SEric.Taylor@Sun.COM nblocks /= DNODES_PER_LEVEL; 413111449SEric.Taylor@Sun.COM numdb += nblocks; 413211449SEric.Taylor@Sun.COM } 413311449SEric.Taylor@Sun.COM numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 413411449SEric.Taylor@Sun.COM volsize *= ncopies; 413511449SEric.Taylor@Sun.COM /* 413611449SEric.Taylor@Sun.COM * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 413711449SEric.Taylor@Sun.COM * compressed, but in practice they compress down to about 413811449SEric.Taylor@Sun.COM * 1100 bytes 413911449SEric.Taylor@Sun.COM */ 414011449SEric.Taylor@Sun.COM numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 414111449SEric.Taylor@Sun.COM volsize += numdb; 414211449SEric.Taylor@Sun.COM return (volsize); 414311449SEric.Taylor@Sun.COM } 4144