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 462*11963SAfshin.Ardakani@Sun.COM if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 463*11963SAfshin.Ardakani@Sun.COM return (-1); 464*11963SAfshin.Ardakani@Sun.COM 4658228SEric.Taylor@Sun.COM return (0); 4668228SEric.Taylor@Sun.COM } 4678228SEric.Taylor@Sun.COM 4688228SEric.Taylor@Sun.COM zfs_handle_t * 4698228SEric.Taylor@Sun.COM make_dataset_handle(libzfs_handle_t *hdl, const char *path) 4708228SEric.Taylor@Sun.COM { 4718228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4728228SEric.Taylor@Sun.COM 4738228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 4748228SEric.Taylor@Sun.COM 4758228SEric.Taylor@Sun.COM if (zhp == NULL) 4768228SEric.Taylor@Sun.COM return (NULL); 4778228SEric.Taylor@Sun.COM 4788228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 4798228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 4808228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 4818228SEric.Taylor@Sun.COM free(zhp); 4828228SEric.Taylor@Sun.COM return (NULL); 4838228SEric.Taylor@Sun.COM } 4848228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) == -1) { 4858228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4868228SEric.Taylor@Sun.COM free(zhp); 4878228SEric.Taylor@Sun.COM return (NULL); 4888228SEric.Taylor@Sun.COM } 4898228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, &zc) == -1) { 4908228SEric.Taylor@Sun.COM free(zhp); 4918228SEric.Taylor@Sun.COM zhp = NULL; 4928228SEric.Taylor@Sun.COM } 4938228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4948228SEric.Taylor@Sun.COM return (zhp); 4958228SEric.Taylor@Sun.COM } 4968228SEric.Taylor@Sun.COM 4978228SEric.Taylor@Sun.COM static zfs_handle_t * 4988228SEric.Taylor@Sun.COM make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 4998228SEric.Taylor@Sun.COM { 5008228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 5018228SEric.Taylor@Sun.COM 5028228SEric.Taylor@Sun.COM if (zhp == NULL) 5038228SEric.Taylor@Sun.COM return (NULL); 5048228SEric.Taylor@Sun.COM 5058228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 5068228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 5078228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, zc) == -1) { 5088228SEric.Taylor@Sun.COM free(zhp); 5098228SEric.Taylor@Sun.COM return (NULL); 5108228SEric.Taylor@Sun.COM } 511789Sahrens return (zhp); 512789Sahrens } 513789Sahrens 514789Sahrens /* 515789Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 516789Sahrens * argument is a mask of acceptable types. The function will print an 517789Sahrens * appropriate error message and return NULL if it can't be opened. 518789Sahrens */ 519789Sahrens zfs_handle_t * 5202082Seschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 521789Sahrens { 522789Sahrens zfs_handle_t *zhp; 5232082Seschrock char errbuf[1024]; 5242082Seschrock 5252082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 5262082Seschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 527789Sahrens 528789Sahrens /* 5292082Seschrock * Validate the name before we even try to open it. 530789Sahrens */ 5315326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 5322082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5332082Seschrock "invalid dataset name")); 5342082Seschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 535789Sahrens return (NULL); 536789Sahrens } 537789Sahrens 538789Sahrens /* 539789Sahrens * Try to get stats for the dataset, which will tell us if it exists. 540789Sahrens */ 541789Sahrens errno = 0; 5422082Seschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 5433237Slling (void) zfs_standard_error(hdl, errno, errbuf); 544789Sahrens return (NULL); 545789Sahrens } 546789Sahrens 547789Sahrens if (!(types & zhp->zfs_type)) { 5482082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 5492142Seschrock zfs_close(zhp); 550789Sahrens return (NULL); 551789Sahrens } 552789Sahrens 553789Sahrens return (zhp); 554789Sahrens } 555789Sahrens 556789Sahrens /* 557789Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 558789Sahrens */ 559789Sahrens void 560789Sahrens zfs_close(zfs_handle_t *zhp) 561789Sahrens { 562789Sahrens if (zhp->zfs_mntopts) 563789Sahrens free(zhp->zfs_mntopts); 5642676Seschrock nvlist_free(zhp->zfs_props); 5652676Seschrock nvlist_free(zhp->zfs_user_props); 56611022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 567789Sahrens free(zhp); 568789Sahrens } 569789Sahrens 5708228SEric.Taylor@Sun.COM typedef struct mnttab_node { 5718228SEric.Taylor@Sun.COM struct mnttab mtn_mt; 5728228SEric.Taylor@Sun.COM avl_node_t mtn_node; 5738228SEric.Taylor@Sun.COM } mnttab_node_t; 5748228SEric.Taylor@Sun.COM 5758228SEric.Taylor@Sun.COM static int 5768228SEric.Taylor@Sun.COM libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 5778228SEric.Taylor@Sun.COM { 5788228SEric.Taylor@Sun.COM const mnttab_node_t *mtn1 = arg1; 5798228SEric.Taylor@Sun.COM const mnttab_node_t *mtn2 = arg2; 5808228SEric.Taylor@Sun.COM int rv; 5818228SEric.Taylor@Sun.COM 5828228SEric.Taylor@Sun.COM rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 5838228SEric.Taylor@Sun.COM 5848228SEric.Taylor@Sun.COM if (rv == 0) 5858228SEric.Taylor@Sun.COM return (0); 5868228SEric.Taylor@Sun.COM return (rv > 0 ? 1 : -1); 5878228SEric.Taylor@Sun.COM } 5888228SEric.Taylor@Sun.COM 5898228SEric.Taylor@Sun.COM void 5908228SEric.Taylor@Sun.COM libzfs_mnttab_init(libzfs_handle_t *hdl) 5918228SEric.Taylor@Sun.COM { 5928228SEric.Taylor@Sun.COM assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 5938228SEric.Taylor@Sun.COM avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 5948228SEric.Taylor@Sun.COM sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 5958811SEric.Taylor@Sun.COM } 5968811SEric.Taylor@Sun.COM 5978811SEric.Taylor@Sun.COM void 5988811SEric.Taylor@Sun.COM libzfs_mnttab_update(libzfs_handle_t *hdl) 5998811SEric.Taylor@Sun.COM { 6008811SEric.Taylor@Sun.COM struct mnttab entry; 6018228SEric.Taylor@Sun.COM 6028228SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6038228SEric.Taylor@Sun.COM while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 6048228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6058228SEric.Taylor@Sun.COM 6068228SEric.Taylor@Sun.COM if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6078228SEric.Taylor@Sun.COM continue; 6088228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6098228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 6108228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 6118228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 6128228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 6138228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6148228SEric.Taylor@Sun.COM } 6158228SEric.Taylor@Sun.COM } 6168228SEric.Taylor@Sun.COM 6178228SEric.Taylor@Sun.COM void 6188228SEric.Taylor@Sun.COM libzfs_mnttab_fini(libzfs_handle_t *hdl) 6198228SEric.Taylor@Sun.COM { 6208228SEric.Taylor@Sun.COM void *cookie = NULL; 6218228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6228228SEric.Taylor@Sun.COM 6238228SEric.Taylor@Sun.COM while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 6248228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_special); 6258228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mountp); 6268228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_fstype); 6278228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mntopts); 6288228SEric.Taylor@Sun.COM free(mtn); 6298228SEric.Taylor@Sun.COM } 6308228SEric.Taylor@Sun.COM avl_destroy(&hdl->libzfs_mnttab_cache); 6318228SEric.Taylor@Sun.COM } 6328228SEric.Taylor@Sun.COM 6338811SEric.Taylor@Sun.COM void 6348811SEric.Taylor@Sun.COM libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 6358811SEric.Taylor@Sun.COM { 6368811SEric.Taylor@Sun.COM hdl->libzfs_mnttab_enable = enable; 6378811SEric.Taylor@Sun.COM } 6388811SEric.Taylor@Sun.COM 6398228SEric.Taylor@Sun.COM int 6408228SEric.Taylor@Sun.COM libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 6418228SEric.Taylor@Sun.COM struct mnttab *entry) 6428228SEric.Taylor@Sun.COM { 6438228SEric.Taylor@Sun.COM mnttab_node_t find; 6448228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6458228SEric.Taylor@Sun.COM 6468811SEric.Taylor@Sun.COM if (!hdl->libzfs_mnttab_enable) { 6478811SEric.Taylor@Sun.COM struct mnttab srch = { 0 }; 6488811SEric.Taylor@Sun.COM 6498811SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 6508811SEric.Taylor@Sun.COM libzfs_mnttab_fini(hdl); 6518811SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6528811SEric.Taylor@Sun.COM srch.mnt_special = (char *)fsname; 6538811SEric.Taylor@Sun.COM srch.mnt_fstype = MNTTYPE_ZFS; 6548811SEric.Taylor@Sun.COM if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 6558811SEric.Taylor@Sun.COM return (0); 6568811SEric.Taylor@Sun.COM else 6578811SEric.Taylor@Sun.COM return (ENOENT); 6588811SEric.Taylor@Sun.COM } 6598811SEric.Taylor@Sun.COM 6608228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6618811SEric.Taylor@Sun.COM libzfs_mnttab_update(hdl); 6628228SEric.Taylor@Sun.COM 6638228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6648228SEric.Taylor@Sun.COM mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 6658228SEric.Taylor@Sun.COM if (mtn) { 6668228SEric.Taylor@Sun.COM *entry = mtn->mtn_mt; 6678228SEric.Taylor@Sun.COM return (0); 6688228SEric.Taylor@Sun.COM } 6698228SEric.Taylor@Sun.COM return (ENOENT); 6708228SEric.Taylor@Sun.COM } 6718228SEric.Taylor@Sun.COM 6728228SEric.Taylor@Sun.COM void 6738228SEric.Taylor@Sun.COM libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 6748228SEric.Taylor@Sun.COM const char *mountp, const char *mntopts) 6758228SEric.Taylor@Sun.COM { 6768228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6778228SEric.Taylor@Sun.COM 6788228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6798228SEric.Taylor@Sun.COM return; 6808228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6818228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 6828228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 6838228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 6848228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 6858228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6868228SEric.Taylor@Sun.COM } 6878228SEric.Taylor@Sun.COM 6888228SEric.Taylor@Sun.COM void 6898228SEric.Taylor@Sun.COM libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 6908228SEric.Taylor@Sun.COM { 6918228SEric.Taylor@Sun.COM mnttab_node_t find; 6928228SEric.Taylor@Sun.COM mnttab_node_t *ret; 6938228SEric.Taylor@Sun.COM 6948228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6958228SEric.Taylor@Sun.COM if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 6968228SEric.Taylor@Sun.COM avl_remove(&hdl->libzfs_mnttab_cache, ret); 6978228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_special); 6988228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mountp); 6998228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_fstype); 7008228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mntopts); 7018228SEric.Taylor@Sun.COM free(ret); 7028228SEric.Taylor@Sun.COM } 7038228SEric.Taylor@Sun.COM } 7048228SEric.Taylor@Sun.COM 7055713Srm160521 int 7065713Srm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7075713Srm160521 { 7086865Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7096865Srm160521 7105713Srm160521 if (zpool_handle == NULL) 7115713Srm160521 return (-1); 7125713Srm160521 7135713Srm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7145713Srm160521 ZPOOL_PROP_VERSION, NULL); 7155713Srm160521 return (0); 7165713Srm160521 } 7175713Srm160521 7185713Srm160521 /* 7195713Srm160521 * The choice of reservation property depends on the SPA version. 7205713Srm160521 */ 7215713Srm160521 static int 7225713Srm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7235713Srm160521 { 7245713Srm160521 int spa_version; 7255713Srm160521 7265713Srm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7275713Srm160521 return (-1); 7285713Srm160521 7295713Srm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7305713Srm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7315713Srm160521 else 7325713Srm160521 *resv_prop = ZFS_PROP_RESERVATION; 7335713Srm160521 7345713Srm160521 return (0); 7355713Srm160521 } 7365713Srm160521 7373912Slling /* 7382676Seschrock * Given an nvlist of properties to set, validates that they are correct, and 7392676Seschrock * parses any numeric properties (index, boolean, etc) if they are specified as 7402676Seschrock * strings. 741789Sahrens */ 7427184Stimh nvlist_t * 7437184Stimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 7445094Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 745789Sahrens { 7462676Seschrock nvpair_t *elem; 7472676Seschrock uint64_t intval; 7482676Seschrock char *strval; 7495094Slling zfs_prop_t prop; 7502676Seschrock nvlist_t *ret; 7515331Samw int chosen_normal = -1; 7525331Samw int chosen_utf = -1; 7532676Seschrock 7545094Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 7555094Slling (void) no_memory(hdl); 7565094Slling return (NULL); 757789Sahrens } 758789Sahrens 7599396SMatthew.Ahrens@Sun.COM /* 7609396SMatthew.Ahrens@Sun.COM * Make sure this property is valid and applies to this type. 7619396SMatthew.Ahrens@Sun.COM */ 7629396SMatthew.Ahrens@Sun.COM 7632676Seschrock elem = NULL; 7642676Seschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 7655094Slling const char *propname = nvpair_name(elem); 7662676Seschrock 7679396SMatthew.Ahrens@Sun.COM prop = zfs_name_to_prop(propname); 7689396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 7695094Slling /* 7709396SMatthew.Ahrens@Sun.COM * This is a user property: make sure it's a 7715094Slling * string, and that it's less than ZAP_MAXNAMELEN. 7725094Slling */ 7735094Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 7745094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7755094Slling "'%s' must be a string"), propname); 7765094Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7775094Slling goto error; 7785094Slling } 7795094Slling 7805094Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 7815094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7825094Slling "property name '%s' is too long"), 7832676Seschrock propname); 7842676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7852676Seschrock goto error; 7862676Seschrock } 7872676Seschrock 7882676Seschrock (void) nvpair_value_string(elem, &strval); 7892676Seschrock if (nvlist_add_string(ret, propname, strval) != 0) { 7902676Seschrock (void) no_memory(hdl); 7912676Seschrock goto error; 7922676Seschrock } 7932676Seschrock continue; 794789Sahrens } 7952676Seschrock 7969396SMatthew.Ahrens@Sun.COM /* 7979396SMatthew.Ahrens@Sun.COM * Currently, only user properties can be modified on 7989396SMatthew.Ahrens@Sun.COM * snapshots. 7999396SMatthew.Ahrens@Sun.COM */ 8007265Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 8017265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8027265Sahrens "this property can not be modified for snapshots")); 8037265Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8047265Sahrens goto error; 8057265Sahrens } 8067265Sahrens 8079396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 8089396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t uqtype; 8099396SMatthew.Ahrens@Sun.COM char newpropname[128]; 8109396SMatthew.Ahrens@Sun.COM char domain[128]; 8119396SMatthew.Ahrens@Sun.COM uint64_t rid; 8129396SMatthew.Ahrens@Sun.COM uint64_t valary[3]; 8139396SMatthew.Ahrens@Sun.COM 8149396SMatthew.Ahrens@Sun.COM if (userquota_propname_decode(propname, zoned, 8159396SMatthew.Ahrens@Sun.COM &uqtype, domain, sizeof (domain), &rid) != 0) { 8169396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8179396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, 8189396SMatthew.Ahrens@Sun.COM "'%s' has an invalid user/group name"), 8199396SMatthew.Ahrens@Sun.COM propname); 8209396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8219396SMatthew.Ahrens@Sun.COM goto error; 8229396SMatthew.Ahrens@Sun.COM } 8239396SMatthew.Ahrens@Sun.COM 8249396SMatthew.Ahrens@Sun.COM if (uqtype != ZFS_PROP_USERQUOTA && 8259396SMatthew.Ahrens@Sun.COM uqtype != ZFS_PROP_GROUPQUOTA) { 8269396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8279396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8289396SMatthew.Ahrens@Sun.COM propname); 8299396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_PROPREADONLY, 8309396SMatthew.Ahrens@Sun.COM errbuf); 8319396SMatthew.Ahrens@Sun.COM goto error; 8329396SMatthew.Ahrens@Sun.COM } 8339396SMatthew.Ahrens@Sun.COM 8349396SMatthew.Ahrens@Sun.COM if (nvpair_type(elem) == DATA_TYPE_STRING) { 8359396SMatthew.Ahrens@Sun.COM (void) nvpair_value_string(elem, &strval); 8369396SMatthew.Ahrens@Sun.COM if (strcmp(strval, "none") == 0) { 8379396SMatthew.Ahrens@Sun.COM intval = 0; 8389396SMatthew.Ahrens@Sun.COM } else if (zfs_nicestrtonum(hdl, 8399396SMatthew.Ahrens@Sun.COM strval, &intval) != 0) { 8409396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, 8419396SMatthew.Ahrens@Sun.COM EZFS_BADPROP, errbuf); 8429396SMatthew.Ahrens@Sun.COM goto error; 8439396SMatthew.Ahrens@Sun.COM } 8449396SMatthew.Ahrens@Sun.COM } else if (nvpair_type(elem) == 8459396SMatthew.Ahrens@Sun.COM DATA_TYPE_UINT64) { 8469396SMatthew.Ahrens@Sun.COM (void) nvpair_value_uint64(elem, &intval); 8479396SMatthew.Ahrens@Sun.COM if (intval == 0) { 8489396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8499396SMatthew.Ahrens@Sun.COM "use 'none' to disable " 8509396SMatthew.Ahrens@Sun.COM "userquota/groupquota")); 8519396SMatthew.Ahrens@Sun.COM goto error; 8529396SMatthew.Ahrens@Sun.COM } 8539396SMatthew.Ahrens@Sun.COM } else { 8549396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8559396SMatthew.Ahrens@Sun.COM "'%s' must be a number"), propname); 8569396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8579396SMatthew.Ahrens@Sun.COM goto error; 8589396SMatthew.Ahrens@Sun.COM } 8599396SMatthew.Ahrens@Sun.COM 86010969SMatthew.Ahrens@Sun.COM /* 86110969SMatthew.Ahrens@Sun.COM * Encode the prop name as 86210969SMatthew.Ahrens@Sun.COM * userquota@<hex-rid>-domain, to make it easy 86310969SMatthew.Ahrens@Sun.COM * for the kernel to decode. 86410969SMatthew.Ahrens@Sun.COM */ 8659396SMatthew.Ahrens@Sun.COM (void) snprintf(newpropname, sizeof (newpropname), 86610969SMatthew.Ahrens@Sun.COM "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 86710969SMatthew.Ahrens@Sun.COM (longlong_t)rid, domain); 8689396SMatthew.Ahrens@Sun.COM valary[0] = uqtype; 8699396SMatthew.Ahrens@Sun.COM valary[1] = rid; 8709396SMatthew.Ahrens@Sun.COM valary[2] = intval; 8719396SMatthew.Ahrens@Sun.COM if (nvlist_add_uint64_array(ret, newpropname, 8729396SMatthew.Ahrens@Sun.COM valary, 3) != 0) { 8739396SMatthew.Ahrens@Sun.COM (void) no_memory(hdl); 8749396SMatthew.Ahrens@Sun.COM goto error; 8759396SMatthew.Ahrens@Sun.COM } 8769396SMatthew.Ahrens@Sun.COM continue; 8779396SMatthew.Ahrens@Sun.COM } 8789396SMatthew.Ahrens@Sun.COM 8799396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL) { 8809396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8819396SMatthew.Ahrens@Sun.COM "invalid property '%s'"), propname); 8829396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8839396SMatthew.Ahrens@Sun.COM goto error; 8849396SMatthew.Ahrens@Sun.COM } 8859396SMatthew.Ahrens@Sun.COM 8862676Seschrock if (!zfs_prop_valid_for_type(prop, type)) { 8872676Seschrock zfs_error_aux(hdl, 8882676Seschrock dgettext(TEXT_DOMAIN, "'%s' does not " 8892676Seschrock "apply to datasets of this type"), propname); 8902676Seschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8912676Seschrock goto error; 8922676Seschrock } 8932676Seschrock 8942676Seschrock if (zfs_prop_readonly(prop) && 8955331Samw (!zfs_prop_setonce(prop) || zhp != NULL)) { 8962676Seschrock zfs_error_aux(hdl, 8972676Seschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8982676Seschrock propname); 8992676Seschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 9002676Seschrock goto error; 9012676Seschrock } 9022676Seschrock 9035094Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 9045094Slling &strval, &intval, errbuf) != 0) 9052676Seschrock goto error; 9062676Seschrock 9072676Seschrock /* 9082676Seschrock * Perform some additional checks for specific properties. 9092676Seschrock */ 9102676Seschrock switch (prop) { 9114577Sahrens case ZFS_PROP_VERSION: 9124577Sahrens { 9134577Sahrens int version; 9144577Sahrens 9154577Sahrens if (zhp == NULL) 9164577Sahrens break; 9174577Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 9184577Sahrens if (intval < version) { 9194577Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9204577Sahrens "Can not downgrade; already at version %u"), 9214577Sahrens version); 9224577Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9234577Sahrens goto error; 9244577Sahrens } 9254577Sahrens break; 9264577Sahrens } 9274577Sahrens 9282676Seschrock case ZFS_PROP_RECORDSIZE: 9292676Seschrock case ZFS_PROP_VOLBLOCKSIZE: 9302676Seschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 9312676Seschrock if (intval < SPA_MINBLOCKSIZE || 9322676Seschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 9332082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9342676Seschrock "'%s' must be power of 2 from %u " 9352676Seschrock "to %uk"), propname, 9362676Seschrock (uint_t)SPA_MINBLOCKSIZE, 9372676Seschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 9382676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9392676Seschrock goto error; 940789Sahrens } 941789Sahrens break; 942789Sahrens 94310972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 94410972SRic.Aleshire@Sun.COM { 94510972SRic.Aleshire@Sun.COM /* 94610972SRic.Aleshire@Sun.COM * Verify the mlslabel string and convert to 94710972SRic.Aleshire@Sun.COM * internal hex label string. 94810972SRic.Aleshire@Sun.COM */ 94910972SRic.Aleshire@Sun.COM 95010972SRic.Aleshire@Sun.COM m_label_t *new_sl; 95110972SRic.Aleshire@Sun.COM char *hex = NULL; /* internal label string */ 95210972SRic.Aleshire@Sun.COM 95310972SRic.Aleshire@Sun.COM /* Default value is already OK. */ 95410972SRic.Aleshire@Sun.COM if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 95510972SRic.Aleshire@Sun.COM break; 95610972SRic.Aleshire@Sun.COM 95710972SRic.Aleshire@Sun.COM /* Verify the label can be converted to binary form */ 95810972SRic.Aleshire@Sun.COM if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 95910972SRic.Aleshire@Sun.COM (str_to_label(strval, &new_sl, MAC_LABEL, 96010972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1)) { 96110972SRic.Aleshire@Sun.COM goto badlabel; 96210972SRic.Aleshire@Sun.COM } 96310972SRic.Aleshire@Sun.COM 96410972SRic.Aleshire@Sun.COM /* Now translate to hex internal label string */ 96510972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &hex, M_INTERNAL, 96610972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 96710972SRic.Aleshire@Sun.COM if (hex) 96810972SRic.Aleshire@Sun.COM free(hex); 96910972SRic.Aleshire@Sun.COM goto badlabel; 97010972SRic.Aleshire@Sun.COM } 97110972SRic.Aleshire@Sun.COM m_label_free(new_sl); 97210972SRic.Aleshire@Sun.COM 97310972SRic.Aleshire@Sun.COM /* If string is already in internal form, we're done. */ 97410972SRic.Aleshire@Sun.COM if (strcmp(strval, hex) == 0) { 97510972SRic.Aleshire@Sun.COM free(hex); 97610972SRic.Aleshire@Sun.COM break; 97710972SRic.Aleshire@Sun.COM } 97810972SRic.Aleshire@Sun.COM 97910972SRic.Aleshire@Sun.COM /* Replace the label string with the internal form. */ 98010984SRic.Aleshire@Sun.COM (void) nvlist_remove(ret, zfs_prop_to_name(prop), 98110972SRic.Aleshire@Sun.COM DATA_TYPE_STRING); 98210972SRic.Aleshire@Sun.COM verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 98310972SRic.Aleshire@Sun.COM hex) == 0); 98410972SRic.Aleshire@Sun.COM free(hex); 98510972SRic.Aleshire@Sun.COM 98610972SRic.Aleshire@Sun.COM break; 98710972SRic.Aleshire@Sun.COM 98810972SRic.Aleshire@Sun.COM badlabel: 98910972SRic.Aleshire@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 99010972SRic.Aleshire@Sun.COM "invalid mlslabel '%s'"), strval); 99110972SRic.Aleshire@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 99210972SRic.Aleshire@Sun.COM m_label_free(new_sl); /* OK if null */ 99310972SRic.Aleshire@Sun.COM goto error; 99410972SRic.Aleshire@Sun.COM 99510972SRic.Aleshire@Sun.COM } 99610972SRic.Aleshire@Sun.COM 9972676Seschrock case ZFS_PROP_MOUNTPOINT: 9984778Srm160521 { 9994778Srm160521 namecheck_err_t why; 10004778Srm160521 10012676Seschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 10022676Seschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 10032676Seschrock break; 10042676Seschrock 10054778Srm160521 if (mountpoint_namecheck(strval, &why)) { 10064778Srm160521 switch (why) { 10074778Srm160521 case NAME_ERR_LEADING_SLASH: 10084778Srm160521 zfs_error_aux(hdl, 10094778Srm160521 dgettext(TEXT_DOMAIN, 10104778Srm160521 "'%s' must be an absolute path, " 10114778Srm160521 "'none', or 'legacy'"), propname); 10124778Srm160521 break; 10134778Srm160521 case NAME_ERR_TOOLONG: 10144778Srm160521 zfs_error_aux(hdl, 10154778Srm160521 dgettext(TEXT_DOMAIN, 10164778Srm160521 "component of '%s' is too long"), 10174778Srm160521 propname); 10184778Srm160521 break; 10194778Srm160521 } 10202676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10212676Seschrock goto error; 1022789Sahrens } 10234778Srm160521 } 10244778Srm160521 10253126Sahl /*FALLTHRU*/ 10263126Sahl 10275331Samw case ZFS_PROP_SHARESMB: 10283126Sahl case ZFS_PROP_SHARENFS: 10293126Sahl /* 10305331Samw * For the mountpoint and sharenfs or sharesmb 10315331Samw * properties, check if it can be set in a 10325331Samw * global/non-global zone based on 10333126Sahl * the zoned property value: 10343126Sahl * 10353126Sahl * global zone non-global zone 10363126Sahl * -------------------------------------------------- 10373126Sahl * zoned=on mountpoint (no) mountpoint (yes) 10383126Sahl * sharenfs (no) sharenfs (no) 10395331Samw * sharesmb (no) sharesmb (no) 10403126Sahl * 10413126Sahl * zoned=off mountpoint (yes) N/A 10423126Sahl * sharenfs (yes) 10435331Samw * sharesmb (yes) 10443126Sahl */ 10452676Seschrock if (zoned) { 10462676Seschrock if (getzoneid() == GLOBAL_ZONEID) { 10472676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10482676Seschrock "'%s' cannot be set on " 10492676Seschrock "dataset in a non-global zone"), 10502676Seschrock propname); 10512676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10522676Seschrock errbuf); 10532676Seschrock goto error; 10545331Samw } else if (prop == ZFS_PROP_SHARENFS || 10555331Samw prop == ZFS_PROP_SHARESMB) { 10562676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10572676Seschrock "'%s' cannot be set in " 10582676Seschrock "a non-global zone"), propname); 10592676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10602676Seschrock errbuf); 10612676Seschrock goto error; 10622676Seschrock } 10632676Seschrock } else if (getzoneid() != GLOBAL_ZONEID) { 10642676Seschrock /* 10652676Seschrock * If zoned property is 'off', this must be in 10669396SMatthew.Ahrens@Sun.COM * a global zone. If not, something is wrong. 10672676Seschrock */ 10682676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10692676Seschrock "'%s' cannot be set while dataset " 10702676Seschrock "'zoned' property is set"), propname); 10712676Seschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 10722676Seschrock goto error; 10732676Seschrock } 10743126Sahl 10754180Sdougm /* 10764180Sdougm * At this point, it is legitimate to set the 10774180Sdougm * property. Now we want to make sure that the 10784180Sdougm * property value is valid if it is sharenfs. 10794180Sdougm */ 10805331Samw if ((prop == ZFS_PROP_SHARENFS || 10815331Samw prop == ZFS_PROP_SHARESMB) && 10824217Seschrock strcmp(strval, "on") != 0 && 10834217Seschrock strcmp(strval, "off") != 0) { 10845331Samw zfs_share_proto_t proto; 10855331Samw 10865331Samw if (prop == ZFS_PROP_SHARESMB) 10875331Samw proto = PROTO_SMB; 10885331Samw else 10895331Samw proto = PROTO_NFS; 10904180Sdougm 10914180Sdougm /* 10925331Samw * Must be an valid sharing protocol 10935331Samw * option string so init the libshare 10945331Samw * in order to enable the parser and 10955331Samw * then parse the options. We use the 10965331Samw * control API since we don't care about 10975331Samw * the current configuration and don't 10984180Sdougm * want the overhead of loading it 10994180Sdougm * until we actually do something. 11004180Sdougm */ 11014180Sdougm 11024217Seschrock if (zfs_init_libshare(hdl, 11034217Seschrock SA_INIT_CONTROL_API) != SA_OK) { 11044217Seschrock /* 11054217Seschrock * An error occurred so we can't do 11064217Seschrock * anything 11074217Seschrock */ 11084217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11094217Seschrock "'%s' cannot be set: problem " 11104217Seschrock "in share initialization"), 11114217Seschrock propname); 11124217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11134217Seschrock errbuf); 11144217Seschrock goto error; 11154217Seschrock } 11164217Seschrock 11175331Samw if (zfs_parse_options(strval, proto) != SA_OK) { 11184217Seschrock /* 11194217Seschrock * There was an error in parsing so 11204217Seschrock * deal with it by issuing an error 11214217Seschrock * message and leaving after 11224217Seschrock * uninitializing the the libshare 11234217Seschrock * interface. 11244217Seschrock */ 11254217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11264217Seschrock "'%s' cannot be set to invalid " 11274217Seschrock "options"), propname); 11284217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11294217Seschrock errbuf); 11304217Seschrock zfs_uninit_libshare(hdl); 11314217Seschrock goto error; 11324217Seschrock } 11334180Sdougm zfs_uninit_libshare(hdl); 11344180Sdougm } 11354180Sdougm 11363126Sahl break; 11375331Samw case ZFS_PROP_UTF8ONLY: 11385331Samw chosen_utf = (int)intval; 11395331Samw break; 11405331Samw case ZFS_PROP_NORMALIZE: 11415331Samw chosen_normal = (int)intval; 11425331Samw break; 11432676Seschrock } 11442676Seschrock 11452676Seschrock /* 11462676Seschrock * For changes to existing volumes, we have some additional 11472676Seschrock * checks to enforce. 11482676Seschrock */ 11492676Seschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 11502676Seschrock uint64_t volsize = zfs_prop_get_int(zhp, 11512676Seschrock ZFS_PROP_VOLSIZE); 11522676Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 11532676Seschrock ZFS_PROP_VOLBLOCKSIZE); 11542676Seschrock char buf[64]; 11552676Seschrock 11562676Seschrock switch (prop) { 11572676Seschrock case ZFS_PROP_RESERVATION: 11585378Sck153898 case ZFS_PROP_REFRESERVATION: 11592676Seschrock if (intval > volsize) { 11602676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11612676Seschrock "'%s' is greater than current " 11622676Seschrock "volume size"), propname); 11632676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11642676Seschrock errbuf); 11652676Seschrock goto error; 11662676Seschrock } 11672676Seschrock break; 11682676Seschrock 11692676Seschrock case ZFS_PROP_VOLSIZE: 11702676Seschrock if (intval % blocksize != 0) { 11712676Seschrock zfs_nicenum(blocksize, buf, 11722676Seschrock sizeof (buf)); 11732676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11742676Seschrock "'%s' must be a multiple of " 11752676Seschrock "volume block size (%s)"), 11762676Seschrock propname, buf); 11772676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11782676Seschrock errbuf); 11792676Seschrock goto error; 11802676Seschrock } 11812676Seschrock 11822676Seschrock if (intval == 0) { 11832676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11842676Seschrock "'%s' cannot be zero"), 11852676Seschrock propname); 11862676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11872676Seschrock errbuf); 11882676Seschrock goto error; 1189789Sahrens } 11903126Sahl break; 1191789Sahrens } 1192789Sahrens } 1193789Sahrens } 1194789Sahrens 11952676Seschrock /* 11965331Samw * If normalization was chosen, but no UTF8 choice was made, 11975331Samw * enforce rejection of non-UTF8 names. 11985331Samw * 11995331Samw * If normalization was chosen, but rejecting non-UTF8 names 12005331Samw * was explicitly not chosen, it is an error. 12015331Samw */ 12025498Stimh if (chosen_normal > 0 && chosen_utf < 0) { 12035331Samw if (nvlist_add_uint64(ret, 12045331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 12055331Samw (void) no_memory(hdl); 12065331Samw goto error; 12075331Samw } 12085498Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 12095331Samw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12105331Samw "'%s' must be set 'on' if normalization chosen"), 12115331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 12125331Samw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 12135331Samw goto error; 12145331Samw } 12155331Samw 12165331Samw /* 12172676Seschrock * If this is an existing volume, and someone is setting the volsize, 12182676Seschrock * make sure that it matches the reservation, or add it if necessary. 12192676Seschrock */ 12202676Seschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 12212676Seschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 12222676Seschrock &intval) == 0) { 12232676Seschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 12242676Seschrock ZFS_PROP_VOLSIZE); 12255481Sck153898 uint64_t old_reservation; 12262676Seschrock uint64_t new_reservation; 12275481Sck153898 zfs_prop_t resv_prop; 12285713Srm160521 12295713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 12305481Sck153898 goto error; 12315481Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 12322676Seschrock 12332676Seschrock if (old_volsize == old_reservation && 12345481Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 12352676Seschrock &new_reservation) != 0) { 12362676Seschrock if (nvlist_add_uint64(ret, 12375481Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 12382676Seschrock (void) no_memory(hdl); 12392676Seschrock goto error; 12402676Seschrock } 12412676Seschrock } 12422676Seschrock } 12432676Seschrock return (ret); 12442676Seschrock 12452676Seschrock error: 12462676Seschrock nvlist_free(ret); 12472676Seschrock return (NULL); 1248789Sahrens } 1249789Sahrens 125011022STom.Erickson@Sun.COM void 125111022STom.Erickson@Sun.COM zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 125211022STom.Erickson@Sun.COM char *errbuf) 125311022STom.Erickson@Sun.COM { 125411022STom.Erickson@Sun.COM switch (err) { 125511022STom.Erickson@Sun.COM 125611022STom.Erickson@Sun.COM case ENOSPC: 125711022STom.Erickson@Sun.COM /* 125811022STom.Erickson@Sun.COM * For quotas and reservations, ENOSPC indicates 125911022STom.Erickson@Sun.COM * something different; setting a quota or reservation 126011022STom.Erickson@Sun.COM * doesn't use any disk space. 126111022STom.Erickson@Sun.COM */ 126211022STom.Erickson@Sun.COM switch (prop) { 126311022STom.Erickson@Sun.COM case ZFS_PROP_QUOTA: 126411022STom.Erickson@Sun.COM case ZFS_PROP_REFQUOTA: 126511022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 126611022STom.Erickson@Sun.COM "size is less than current used or " 126711022STom.Erickson@Sun.COM "reserved space")); 126811022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 126911022STom.Erickson@Sun.COM break; 127011022STom.Erickson@Sun.COM 127111022STom.Erickson@Sun.COM case ZFS_PROP_RESERVATION: 127211022STom.Erickson@Sun.COM case ZFS_PROP_REFRESERVATION: 127311022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127411022STom.Erickson@Sun.COM "size is greater than available space")); 127511022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 127611022STom.Erickson@Sun.COM break; 127711022STom.Erickson@Sun.COM 127811022STom.Erickson@Sun.COM default: 127911022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 128011022STom.Erickson@Sun.COM break; 128111022STom.Erickson@Sun.COM } 128211022STom.Erickson@Sun.COM break; 128311022STom.Erickson@Sun.COM 128411022STom.Erickson@Sun.COM case EBUSY: 128511022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, EBUSY, errbuf); 128611022STom.Erickson@Sun.COM break; 128711022STom.Erickson@Sun.COM 128811022STom.Erickson@Sun.COM case EROFS: 128911022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 129011022STom.Erickson@Sun.COM break; 129111022STom.Erickson@Sun.COM 129211022STom.Erickson@Sun.COM case ENOTSUP: 129311022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 129411022STom.Erickson@Sun.COM "pool and or dataset must be upgraded to set this " 129511022STom.Erickson@Sun.COM "property or value")); 129611022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 129711022STom.Erickson@Sun.COM break; 129811022STom.Erickson@Sun.COM 129911022STom.Erickson@Sun.COM case ERANGE: 130011022STom.Erickson@Sun.COM if (prop == ZFS_PROP_COMPRESSION) { 130111022STom.Erickson@Sun.COM (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130211022STom.Erickson@Sun.COM "property setting is not allowed on " 130311022STom.Erickson@Sun.COM "bootable datasets")); 130411022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 130511022STom.Erickson@Sun.COM } else { 130611022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 130711022STom.Erickson@Sun.COM } 130811022STom.Erickson@Sun.COM break; 130911022STom.Erickson@Sun.COM 131011876SJames.Dunham@Sun.COM case EINVAL: 131111876SJames.Dunham@Sun.COM if (prop == ZPROP_INVAL) { 131211876SJames.Dunham@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 131311876SJames.Dunham@Sun.COM } else { 131411876SJames.Dunham@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 131511876SJames.Dunham@Sun.COM } 131611876SJames.Dunham@Sun.COM break; 131711876SJames.Dunham@Sun.COM 131811022STom.Erickson@Sun.COM case EOVERFLOW: 131911022STom.Erickson@Sun.COM /* 132011022STom.Erickson@Sun.COM * This platform can't address a volume this big. 132111022STom.Erickson@Sun.COM */ 132211022STom.Erickson@Sun.COM #ifdef _ILP32 132311022STom.Erickson@Sun.COM if (prop == ZFS_PROP_VOLSIZE) { 132411022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132511022STom.Erickson@Sun.COM break; 132611022STom.Erickson@Sun.COM } 132711022STom.Erickson@Sun.COM #endif 132811022STom.Erickson@Sun.COM /* FALLTHROUGH */ 132911022STom.Erickson@Sun.COM default: 133011022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 133111022STom.Erickson@Sun.COM } 133211022STom.Erickson@Sun.COM } 133311022STom.Erickson@Sun.COM 1334789Sahrens /* 1335789Sahrens * Given a property name and value, set the property for the given dataset. 1336789Sahrens */ 1337789Sahrens int 13382676Seschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1339789Sahrens { 1340789Sahrens zfs_cmd_t zc = { 0 }; 13412676Seschrock int ret = -1; 13422676Seschrock prop_changelist_t *cl = NULL; 13432082Seschrock char errbuf[1024]; 13442082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 13452676Seschrock nvlist_t *nvl = NULL, *realprops; 13462676Seschrock zfs_prop_t prop; 13477509SMark.Musante@Sun.COM boolean_t do_prefix; 13487509SMark.Musante@Sun.COM uint64_t idx; 13492082Seschrock 13502082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 13512676Seschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 13522082Seschrock zhp->zfs_name); 13532082Seschrock 13542676Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 13552676Seschrock nvlist_add_string(nvl, propname, propval) != 0) { 13562676Seschrock (void) no_memory(hdl); 13572676Seschrock goto error; 1358789Sahrens } 1359789Sahrens 13607184Stimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 13612676Seschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 13622676Seschrock goto error; 13635094Slling 13642676Seschrock nvlist_free(nvl); 13652676Seschrock nvl = realprops; 13662676Seschrock 13672676Seschrock prop = zfs_name_to_prop(propname); 13682676Seschrock 13697366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 13702676Seschrock goto error; 1371789Sahrens 1372789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 13732082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13742082Seschrock "child dataset with inherited mountpoint is used " 13752082Seschrock "in a non-global zone")); 13762082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1377789Sahrens goto error; 1378789Sahrens } 1379789Sahrens 13807509SMark.Musante@Sun.COM /* 13817509SMark.Musante@Sun.COM * If the dataset's canmount property is being set to noauto, 13827509SMark.Musante@Sun.COM * then we want to prevent unmounting & remounting it. 13837509SMark.Musante@Sun.COM */ 13847509SMark.Musante@Sun.COM do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13857509SMark.Musante@Sun.COM (zprop_string_to_index(prop, propval, &idx, 13867509SMark.Musante@Sun.COM ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 13876168Shs24103 13886168Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 13897509SMark.Musante@Sun.COM goto error; 1390789Sahrens 1391789Sahrens /* 1392789Sahrens * Execute the corresponding ioctl() to set this property. 1393789Sahrens */ 1394789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1395789Sahrens 13965094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 13972676Seschrock goto error; 13982676Seschrock 13994543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 14008845Samw@Sun.COM 1401789Sahrens if (ret != 0) { 140211022STom.Erickson@Sun.COM zfs_setprop_error(hdl, prop, errno, errbuf); 1403789Sahrens } else { 14046168Shs24103 if (do_prefix) 14056168Shs24103 ret = changelist_postfix(cl); 14066168Shs24103 1407789Sahrens /* 1408789Sahrens * Refresh the statistics so the new property value 1409789Sahrens * is reflected. 1410789Sahrens */ 14116168Shs24103 if (ret == 0) 14122676Seschrock (void) get_stats(zhp); 1413789Sahrens } 1414789Sahrens 1415789Sahrens error: 14162676Seschrock nvlist_free(nvl); 14172676Seschrock zcmd_free_nvlists(&zc); 14182676Seschrock if (cl) 14192676Seschrock changelist_free(cl); 1420789Sahrens return (ret); 1421789Sahrens } 1422789Sahrens 1423789Sahrens /* 142411022STom.Erickson@Sun.COM * Given a property, inherit the value from the parent dataset, or if received 142511022STom.Erickson@Sun.COM * is TRUE, revert to the received value, if any. 1426789Sahrens */ 1427789Sahrens int 142811022STom.Erickson@Sun.COM zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1429789Sahrens { 1430789Sahrens zfs_cmd_t zc = { 0 }; 1431789Sahrens int ret; 1432789Sahrens prop_changelist_t *cl; 14332082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 14342082Seschrock char errbuf[1024]; 14352676Seschrock zfs_prop_t prop; 14362082Seschrock 14372082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 14382082Seschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1439789Sahrens 144011022STom.Erickson@Sun.COM zc.zc_cookie = received; 14415094Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 14422676Seschrock /* 14432676Seschrock * For user properties, the amount of work we have to do is very 14442676Seschrock * small, so just do it here. 14452676Seschrock */ 14462676Seschrock if (!zfs_prop_user(propname)) { 14472676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14482676Seschrock "invalid property")); 14492676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 14502676Seschrock } 14512676Seschrock 14522676Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14532676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 14542676Seschrock 14554849Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 14562676Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 14572676Seschrock 14582676Seschrock return (0); 14592676Seschrock } 14602676Seschrock 1461789Sahrens /* 1462789Sahrens * Verify that this property is inheritable. 1463789Sahrens */ 14642082Seschrock if (zfs_prop_readonly(prop)) 14652082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 14662082Seschrock 146711022STom.Erickson@Sun.COM if (!zfs_prop_inheritable(prop) && !received) 14682082Seschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1469789Sahrens 1470789Sahrens /* 1471789Sahrens * Check to see if the value applies to this type 1472789Sahrens */ 14732082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 14742082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1475789Sahrens 14763443Srm160521 /* 14773443Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 14783443Srm160521 */ 14793443Srm160521 propname = zfs_prop_to_name(prop); 1480789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14812676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1482789Sahrens 1483789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1484789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 14852082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14862082Seschrock "dataset is used in a non-global zone")); 14872082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1488789Sahrens } 1489789Sahrens 1490789Sahrens /* 1491789Sahrens * Determine datasets which will be affected by this change, if any. 1492789Sahrens */ 14937366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1494789Sahrens return (-1); 1495789Sahrens 1496789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 14972082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14982082Seschrock "child dataset with inherited mountpoint is used " 14992082Seschrock "in a non-global zone")); 15002082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1501789Sahrens goto error; 1502789Sahrens } 1503789Sahrens 1504789Sahrens if ((ret = changelist_prefix(cl)) != 0) 1505789Sahrens goto error; 1506789Sahrens 15074849Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 15082082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1509789Sahrens } else { 1510789Sahrens 15112169Snd150628 if ((ret = changelist_postfix(cl)) != 0) 1512789Sahrens goto error; 1513789Sahrens 1514789Sahrens /* 1515789Sahrens * Refresh the statistics so the new property is reflected. 1516789Sahrens */ 1517789Sahrens (void) get_stats(zhp); 1518789Sahrens } 1519789Sahrens 1520789Sahrens error: 1521789Sahrens changelist_free(cl); 1522789Sahrens return (ret); 1523789Sahrens } 1524789Sahrens 1525789Sahrens /* 15261356Seschrock * True DSL properties are stored in an nvlist. The following two functions 15271356Seschrock * extract them appropriately. 15281356Seschrock */ 15291356Seschrock static uint64_t 15301356Seschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15311356Seschrock { 15321356Seschrock nvlist_t *nv; 15331356Seschrock uint64_t value; 15341356Seschrock 15352885Sahrens *source = NULL; 15361356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15371356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15385094Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 15395094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15401356Seschrock } else { 15418802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15428802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15431356Seschrock value = zfs_prop_default_numeric(prop); 15441356Seschrock *source = ""; 15451356Seschrock } 15461356Seschrock 15471356Seschrock return (value); 15481356Seschrock } 15491356Seschrock 15501356Seschrock static char * 15511356Seschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15521356Seschrock { 15531356Seschrock nvlist_t *nv; 15541356Seschrock char *value; 15551356Seschrock 15562885Sahrens *source = NULL; 15571356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15581356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15595094Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 15605094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15611356Seschrock } else { 15628802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15638802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15641356Seschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15651356Seschrock value = ""; 15661356Seschrock *source = ""; 15671356Seschrock } 15681356Seschrock 15691356Seschrock return (value); 15701356Seschrock } 15711356Seschrock 157211022STom.Erickson@Sun.COM static boolean_t 157311022STom.Erickson@Sun.COM zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157411022STom.Erickson@Sun.COM { 157511022STom.Erickson@Sun.COM return (zhp->zfs_props == zhp->zfs_recvd_props); 157611022STom.Erickson@Sun.COM } 157711022STom.Erickson@Sun.COM 157811022STom.Erickson@Sun.COM static void 157911022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158011022STom.Erickson@Sun.COM { 158111022STom.Erickson@Sun.COM *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158211022STom.Erickson@Sun.COM zhp->zfs_props = zhp->zfs_recvd_props; 158311022STom.Erickson@Sun.COM } 158411022STom.Erickson@Sun.COM 158511022STom.Erickson@Sun.COM static void 158611022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158711022STom.Erickson@Sun.COM { 158811022STom.Erickson@Sun.COM zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 158911022STom.Erickson@Sun.COM *cookie = 0; 159011022STom.Erickson@Sun.COM } 159111022STom.Erickson@Sun.COM 15921356Seschrock /* 1593789Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1594789Sahrens * zfs_prop_get_int() are built using this interface. 1595789Sahrens * 1596789Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1597789Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1598789Sahrens * If they differ from the on-disk values, report the current values and mark 1599789Sahrens * the source "temporary". 1600789Sahrens */ 16012082Seschrock static int 16025094Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 16032082Seschrock char **source, uint64_t *val) 1604789Sahrens { 16055147Srm160521 zfs_cmd_t zc = { 0 }; 16065592Stimh nvlist_t *zplprops = NULL; 1607789Sahrens struct mnttab mnt; 16083265Sahrens char *mntopt_on = NULL; 16093265Sahrens char *mntopt_off = NULL; 161011022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1611789Sahrens 1612789Sahrens *source = NULL; 1613789Sahrens 16143265Sahrens switch (prop) { 16153265Sahrens case ZFS_PROP_ATIME: 16163265Sahrens mntopt_on = MNTOPT_ATIME; 16173265Sahrens mntopt_off = MNTOPT_NOATIME; 16183265Sahrens break; 16193265Sahrens 16203265Sahrens case ZFS_PROP_DEVICES: 16213265Sahrens mntopt_on = MNTOPT_DEVICES; 16223265Sahrens mntopt_off = MNTOPT_NODEVICES; 16233265Sahrens break; 16243265Sahrens 16253265Sahrens case ZFS_PROP_EXEC: 16263265Sahrens mntopt_on = MNTOPT_EXEC; 16273265Sahrens mntopt_off = MNTOPT_NOEXEC; 16283265Sahrens break; 16293265Sahrens 16303265Sahrens case ZFS_PROP_READONLY: 16313265Sahrens mntopt_on = MNTOPT_RO; 16323265Sahrens mntopt_off = MNTOPT_RW; 16333265Sahrens break; 16343265Sahrens 16353265Sahrens case ZFS_PROP_SETUID: 16363265Sahrens mntopt_on = MNTOPT_SETUID; 16373265Sahrens mntopt_off = MNTOPT_NOSETUID; 16383265Sahrens break; 16393265Sahrens 16403265Sahrens case ZFS_PROP_XATTR: 16413265Sahrens mntopt_on = MNTOPT_XATTR; 16423265Sahrens mntopt_off = MNTOPT_NOXATTR; 16433265Sahrens break; 16445331Samw 16455331Samw case ZFS_PROP_NBMAND: 16465331Samw mntopt_on = MNTOPT_NBMAND; 16475331Samw mntopt_off = MNTOPT_NONBMAND; 16485331Samw break; 16493265Sahrens } 16503265Sahrens 16512474Seschrock /* 16522474Seschrock * Because looking up the mount options is potentially expensive 16532474Seschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16542474Seschrock * we're looking up a property which requires its presence. 16552474Seschrock */ 16562474Seschrock if (!zhp->zfs_mntcheck && 16573265Sahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 16588228SEric.Taylor@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 16598228SEric.Taylor@Sun.COM struct mnttab entry; 16608228SEric.Taylor@Sun.COM 16618228SEric.Taylor@Sun.COM if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 16628228SEric.Taylor@Sun.COM zhp->zfs_mntopts = zfs_strdup(hdl, 16633265Sahrens entry.mnt_mntopts); 16643265Sahrens if (zhp->zfs_mntopts == NULL) 16653265Sahrens return (-1); 16663265Sahrens } 16672474Seschrock 16682474Seschrock zhp->zfs_mntcheck = B_TRUE; 16692474Seschrock } 16702474Seschrock 1671789Sahrens if (zhp->zfs_mntopts == NULL) 1672789Sahrens mnt.mnt_mntopts = ""; 1673789Sahrens else 1674789Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1675789Sahrens 1676789Sahrens switch (prop) { 1677789Sahrens case ZFS_PROP_ATIME: 16783265Sahrens case ZFS_PROP_DEVICES: 16793265Sahrens case ZFS_PROP_EXEC: 16803265Sahrens case ZFS_PROP_READONLY: 16813265Sahrens case ZFS_PROP_SETUID: 16823265Sahrens case ZFS_PROP_XATTR: 16835331Samw case ZFS_PROP_NBMAND: 16842082Seschrock *val = getprop_uint64(zhp, prop, source); 16852082Seschrock 168611022STom.Erickson@Sun.COM if (received) 168711022STom.Erickson@Sun.COM break; 168811022STom.Erickson@Sun.COM 16893265Sahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 16902082Seschrock *val = B_TRUE; 1691789Sahrens if (src) 16925094Slling *src = ZPROP_SRC_TEMPORARY; 16933265Sahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 16942082Seschrock *val = B_FALSE; 1695789Sahrens if (src) 16965094Slling *src = ZPROP_SRC_TEMPORARY; 1697789Sahrens } 16982082Seschrock break; 1699789Sahrens 17003265Sahrens case ZFS_PROP_CANMOUNT: 170111497SMark.Musante@Sun.COM case ZFS_PROP_VOLSIZE: 1702789Sahrens case ZFS_PROP_QUOTA: 17035378Sck153898 case ZFS_PROP_REFQUOTA: 1704789Sahrens case ZFS_PROP_RESERVATION: 17055378Sck153898 case ZFS_PROP_REFRESERVATION: 17062885Sahrens *val = getprop_uint64(zhp, prop, source); 170711022STom.Erickson@Sun.COM 170811022STom.Erickson@Sun.COM if (*source == NULL) { 170911022STom.Erickson@Sun.COM /* not default, must be local */ 1710789Sahrens *source = zhp->zfs_name; 171111022STom.Erickson@Sun.COM } 17122082Seschrock break; 1713789Sahrens 1714789Sahrens case ZFS_PROP_MOUNTED: 17152082Seschrock *val = (zhp->zfs_mntopts != NULL); 17162082Seschrock break; 1717789Sahrens 17183377Seschrock case ZFS_PROP_NUMCLONES: 17193377Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 17203377Seschrock break; 17213377Seschrock 17225147Srm160521 case ZFS_PROP_VERSION: 17235498Stimh case ZFS_PROP_NORMALIZE: 17245498Stimh case ZFS_PROP_UTF8ONLY: 17255498Stimh case ZFS_PROP_CASE: 17265498Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 17275498Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17285473Srm160521 return (-1); 17295147Srm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 17305498Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 17315498Stimh zcmd_free_nvlists(&zc); 173210204SMatthew.Ahrens@Sun.COM return (-1); 17335147Srm160521 } 17345498Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 17355498Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 17365498Stimh val) != 0) { 17375498Stimh zcmd_free_nvlists(&zc); 173810204SMatthew.Ahrens@Sun.COM return (-1); 17395498Stimh } 17405592Stimh if (zplprops) 17415592Stimh nvlist_free(zplprops); 17425498Stimh zcmd_free_nvlists(&zc); 17435147Srm160521 break; 17445147Srm160521 1745789Sahrens default: 17464577Sahrens switch (zfs_prop_get_type(prop)) { 17474787Sahrens case PROP_TYPE_NUMBER: 17484787Sahrens case PROP_TYPE_INDEX: 17494577Sahrens *val = getprop_uint64(zhp, prop, source); 17507390SMatthew.Ahrens@Sun.COM /* 17519396SMatthew.Ahrens@Sun.COM * If we tried to use a default value for a 17527390SMatthew.Ahrens@Sun.COM * readonly property, it means that it was not 175311080STom.Erickson@Sun.COM * present. 17547390SMatthew.Ahrens@Sun.COM */ 17557390SMatthew.Ahrens@Sun.COM if (zfs_prop_readonly(prop) && 175611080STom.Erickson@Sun.COM *source != NULL && (*source)[0] == '\0') { 175711080STom.Erickson@Sun.COM *source = NULL; 17587390SMatthew.Ahrens@Sun.COM } 17594577Sahrens break; 17604577Sahrens 17614787Sahrens case PROP_TYPE_STRING: 17624577Sahrens default: 17634577Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 17644577Sahrens "cannot get non-numeric property")); 17654577Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 17664577Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 17674577Sahrens } 1768789Sahrens } 1769789Sahrens 1770789Sahrens return (0); 1771789Sahrens } 1772789Sahrens 1773789Sahrens /* 1774789Sahrens * Calculate the source type, given the raw source string. 1775789Sahrens */ 1776789Sahrens static void 17775094Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1778789Sahrens char *statbuf, size_t statlen) 1779789Sahrens { 17805094Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1781789Sahrens return; 1782789Sahrens 1783789Sahrens if (source == NULL) { 17845094Slling *srctype = ZPROP_SRC_NONE; 1785789Sahrens } else if (source[0] == '\0') { 17865094Slling *srctype = ZPROP_SRC_DEFAULT; 178711022STom.Erickson@Sun.COM } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 178811022STom.Erickson@Sun.COM *srctype = ZPROP_SRC_RECEIVED; 1789789Sahrens } else { 1790789Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 17915094Slling *srctype = ZPROP_SRC_LOCAL; 1792789Sahrens } else { 1793789Sahrens (void) strlcpy(statbuf, source, statlen); 17945094Slling *srctype = ZPROP_SRC_INHERITED; 1795789Sahrens } 1796789Sahrens } 1797789Sahrens 1798789Sahrens } 1799789Sahrens 180011022STom.Erickson@Sun.COM int 180111022STom.Erickson@Sun.COM zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180211022STom.Erickson@Sun.COM size_t proplen, boolean_t literal) 180311022STom.Erickson@Sun.COM { 180411022STom.Erickson@Sun.COM zfs_prop_t prop; 180511022STom.Erickson@Sun.COM int err = 0; 180611022STom.Erickson@Sun.COM 180711022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 180811022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 180911022STom.Erickson@Sun.COM return (-1); 181011022STom.Erickson@Sun.COM 181111022STom.Erickson@Sun.COM prop = zfs_name_to_prop(propname); 181211022STom.Erickson@Sun.COM 181311022STom.Erickson@Sun.COM if (prop != ZPROP_INVAL) { 181411022STom.Erickson@Sun.COM uint64_t cookie; 181511022STom.Erickson@Sun.COM if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181611022STom.Erickson@Sun.COM return (-1); 181711022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zhp, &cookie); 181811022STom.Erickson@Sun.COM err = zfs_prop_get(zhp, prop, propbuf, proplen, 181911022STom.Erickson@Sun.COM NULL, NULL, 0, literal); 182011022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zhp, &cookie); 182111022STom.Erickson@Sun.COM } else if (zfs_prop_userquota(propname)) { 182211022STom.Erickson@Sun.COM return (-1); 182311022STom.Erickson@Sun.COM } else { 182411022STom.Erickson@Sun.COM nvlist_t *propval; 182511022STom.Erickson@Sun.COM char *recvdval; 182611022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 182711022STom.Erickson@Sun.COM propname, &propval) != 0) 182811022STom.Erickson@Sun.COM return (-1); 182911022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, ZPROP_VALUE, 183011022STom.Erickson@Sun.COM &recvdval) == 0); 183111022STom.Erickson@Sun.COM (void) strlcpy(propbuf, recvdval, proplen); 183211022STom.Erickson@Sun.COM } 183311022STom.Erickson@Sun.COM 183411022STom.Erickson@Sun.COM return (err == 0 ? 0 : -1); 183511022STom.Erickson@Sun.COM } 183611022STom.Erickson@Sun.COM 1837789Sahrens /* 1838789Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1839789Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1840789Sahrens * human-readable form. 1841789Sahrens * 1842789Sahrens * Returns 0 on success, or -1 on error. 1843789Sahrens */ 1844789Sahrens int 1845789Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 18465094Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1847789Sahrens { 1848789Sahrens char *source = NULL; 1849789Sahrens uint64_t val; 1850789Sahrens char *str; 18512676Seschrock const char *strval; 185211022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1853789Sahrens 1854789Sahrens /* 1855789Sahrens * Check to see if this property applies to our object 1856789Sahrens */ 1857789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1858789Sahrens return (-1); 1859789Sahrens 186011022STom.Erickson@Sun.COM if (received && zfs_prop_readonly(prop)) 186111022STom.Erickson@Sun.COM return (-1); 186211022STom.Erickson@Sun.COM 1863789Sahrens if (src) 18645094Slling *src = ZPROP_SRC_NONE; 1865789Sahrens 1866789Sahrens switch (prop) { 1867789Sahrens case ZFS_PROP_CREATION: 1868789Sahrens /* 1869789Sahrens * 'creation' is a time_t stored in the statistics. We convert 1870789Sahrens * this into a string unless 'literal' is specified. 1871789Sahrens */ 1872789Sahrens { 18732885Sahrens val = getprop_uint64(zhp, prop, &source); 18742885Sahrens time_t time = (time_t)val; 1875789Sahrens struct tm t; 1876789Sahrens 1877789Sahrens if (literal || 1878789Sahrens localtime_r(&time, &t) == NULL || 1879789Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1880789Sahrens &t) == 0) 18812885Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1882789Sahrens } 1883789Sahrens break; 1884789Sahrens 1885789Sahrens case ZFS_PROP_MOUNTPOINT: 1886789Sahrens /* 1887789Sahrens * Getting the precise mountpoint can be tricky. 1888789Sahrens * 1889789Sahrens * - for 'none' or 'legacy', return those values. 1890789Sahrens * - for inherited mountpoints, we want to take everything 1891789Sahrens * after our ancestor and append it to the inherited value. 1892789Sahrens * 1893789Sahrens * If the pool has an alternate root, we want to prepend that 1894789Sahrens * root to any values we return. 1895789Sahrens */ 18966865Srm160521 18971356Seschrock str = getprop_string(zhp, prop, &source); 18981356Seschrock 18996612Sgw25295 if (str[0] == '/') { 19006865Srm160521 char buf[MAXPATHLEN]; 19016865Srm160521 char *root = buf; 190211515STom.Erickson@Sun.COM const char *relpath; 190311515STom.Erickson@Sun.COM 190411515STom.Erickson@Sun.COM /* 190511515STom.Erickson@Sun.COM * If we inherit the mountpoint, even from a dataset 190611515STom.Erickson@Sun.COM * with a received value, the source will be the path of 190711515STom.Erickson@Sun.COM * the dataset we inherit from. If source is 190811515STom.Erickson@Sun.COM * ZPROP_SOURCE_VAL_RECVD, the received value is not 190911515STom.Erickson@Sun.COM * inherited. 191011515STom.Erickson@Sun.COM */ 191111515STom.Erickson@Sun.COM if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 191211515STom.Erickson@Sun.COM relpath = ""; 191311515STom.Erickson@Sun.COM } else { 191411515STom.Erickson@Sun.COM relpath = zhp->zfs_name + strlen(source); 191511515STom.Erickson@Sun.COM if (relpath[0] == '/') 191611515STom.Erickson@Sun.COM relpath++; 191711515STom.Erickson@Sun.COM } 19186612Sgw25295 19196865Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 19206865Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 19216865Srm160521 (strcmp(root, "-") == 0)) 19226865Srm160521 root[0] = '\0'; 19236612Sgw25295 /* 19246612Sgw25295 * Special case an alternate root of '/'. This will 19256612Sgw25295 * avoid having multiple leading slashes in the 19266612Sgw25295 * mountpoint path. 19276612Sgw25295 */ 19286612Sgw25295 if (strcmp(root, "/") == 0) 19296612Sgw25295 root++; 19306612Sgw25295 19316612Sgw25295 /* 19326612Sgw25295 * If the mountpoint is '/' then skip over this 19336612Sgw25295 * if we are obtaining either an alternate root or 19346612Sgw25295 * an inherited mountpoint. 19356612Sgw25295 */ 19366612Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 19376612Sgw25295 relpath[0] != '\0')) 19381356Seschrock str++; 1939789Sahrens 1940789Sahrens if (relpath[0] == '\0') 1941789Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19421356Seschrock root, str); 1943789Sahrens else 1944789Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19451356Seschrock root, str, relpath[0] == '@' ? "" : "/", 1946789Sahrens relpath); 1947789Sahrens } else { 1948789Sahrens /* 'legacy' or 'none' */ 19491356Seschrock (void) strlcpy(propbuf, str, proplen); 1950789Sahrens } 1951789Sahrens 1952789Sahrens break; 1953789Sahrens 1954789Sahrens case ZFS_PROP_ORIGIN: 19552885Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1956789Sahrens proplen); 1957789Sahrens /* 1958789Sahrens * If there is no parent at all, return failure to indicate that 1959789Sahrens * it doesn't apply to this dataset. 1960789Sahrens */ 1961789Sahrens if (propbuf[0] == '\0') 1962789Sahrens return (-1); 1963789Sahrens break; 1964789Sahrens 1965789Sahrens case ZFS_PROP_QUOTA: 19665378Sck153898 case ZFS_PROP_REFQUOTA: 1967789Sahrens case ZFS_PROP_RESERVATION: 19685378Sck153898 case ZFS_PROP_REFRESERVATION: 19695378Sck153898 19702082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19712082Seschrock return (-1); 1972789Sahrens 1973789Sahrens /* 1974789Sahrens * If quota or reservation is 0, we translate this into 'none' 1975789Sahrens * (unless literal is set), and indicate that it's the default 1976789Sahrens * value. Otherwise, we print the number nicely and indicate 1977789Sahrens * that its set locally. 1978789Sahrens */ 1979789Sahrens if (val == 0) { 1980789Sahrens if (literal) 1981789Sahrens (void) strlcpy(propbuf, "0", proplen); 1982789Sahrens else 1983789Sahrens (void) strlcpy(propbuf, "none", proplen); 1984789Sahrens } else { 1985789Sahrens if (literal) 19862856Snd150628 (void) snprintf(propbuf, proplen, "%llu", 19873912Slling (u_longlong_t)val); 1988789Sahrens else 1989789Sahrens zfs_nicenum(val, propbuf, proplen); 1990789Sahrens } 1991789Sahrens break; 1992789Sahrens 1993789Sahrens case ZFS_PROP_COMPRESSRATIO: 19942082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19952082Seschrock return (-1); 199610922SJeff.Bonwick@Sun.COM (void) snprintf(propbuf, proplen, "%llu.%02llux", 199710922SJeff.Bonwick@Sun.COM (u_longlong_t)(val / 100), 199810922SJeff.Bonwick@Sun.COM (u_longlong_t)(val % 100)); 1999789Sahrens break; 2000789Sahrens 2001789Sahrens case ZFS_PROP_TYPE: 2002789Sahrens switch (zhp->zfs_type) { 2003789Sahrens case ZFS_TYPE_FILESYSTEM: 2004789Sahrens str = "filesystem"; 2005789Sahrens break; 2006789Sahrens case ZFS_TYPE_VOLUME: 2007789Sahrens str = "volume"; 2008789Sahrens break; 2009789Sahrens case ZFS_TYPE_SNAPSHOT: 2010789Sahrens str = "snapshot"; 2011789Sahrens break; 2012789Sahrens default: 20132082Seschrock abort(); 2014789Sahrens } 2015789Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2016789Sahrens break; 2017789Sahrens 2018789Sahrens case ZFS_PROP_MOUNTED: 2019789Sahrens /* 2020789Sahrens * The 'mounted' property is a pseudo-property that described 2021789Sahrens * whether the filesystem is currently mounted. Even though 2022789Sahrens * it's a boolean value, the typical values of "on" and "off" 2023789Sahrens * don't make sense, so we translate to "yes" and "no". 2024789Sahrens */ 20252082Seschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 20262082Seschrock src, &source, &val) != 0) 20272082Seschrock return (-1); 20282082Seschrock if (val) 2029789Sahrens (void) strlcpy(propbuf, "yes", proplen); 2030789Sahrens else 2031789Sahrens (void) strlcpy(propbuf, "no", proplen); 2032789Sahrens break; 2033789Sahrens 2034789Sahrens case ZFS_PROP_NAME: 2035789Sahrens /* 2036789Sahrens * The 'name' property is a pseudo-property derived from the 2037789Sahrens * dataset name. It is presented as a real property to simplify 2038789Sahrens * consumers. 2039789Sahrens */ 2040789Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2041789Sahrens break; 2042789Sahrens 204310972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 204410972SRic.Aleshire@Sun.COM { 204510972SRic.Aleshire@Sun.COM m_label_t *new_sl = NULL; 204610972SRic.Aleshire@Sun.COM char *ascii = NULL; /* human readable label */ 204710972SRic.Aleshire@Sun.COM 204810972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, 204910972SRic.Aleshire@Sun.COM getprop_string(zhp, prop, &source), proplen); 205010972SRic.Aleshire@Sun.COM 205110972SRic.Aleshire@Sun.COM if (literal || (strcasecmp(propbuf, 205210972SRic.Aleshire@Sun.COM ZFS_MLSLABEL_DEFAULT) == 0)) 205310972SRic.Aleshire@Sun.COM break; 205410972SRic.Aleshire@Sun.COM 205510972SRic.Aleshire@Sun.COM /* 205610972SRic.Aleshire@Sun.COM * Try to translate the internal hex string to 205710972SRic.Aleshire@Sun.COM * human-readable output. If there are any 205810972SRic.Aleshire@Sun.COM * problems just use the hex string. 205910972SRic.Aleshire@Sun.COM */ 206010972SRic.Aleshire@Sun.COM 206110972SRic.Aleshire@Sun.COM if (str_to_label(propbuf, &new_sl, MAC_LABEL, 206210972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1) { 206310972SRic.Aleshire@Sun.COM m_label_free(new_sl); 206410972SRic.Aleshire@Sun.COM break; 206510972SRic.Aleshire@Sun.COM } 206610972SRic.Aleshire@Sun.COM 206710972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &ascii, M_LABEL, 206810972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 206910972SRic.Aleshire@Sun.COM if (ascii) 207010972SRic.Aleshire@Sun.COM free(ascii); 207110972SRic.Aleshire@Sun.COM m_label_free(new_sl); 207210972SRic.Aleshire@Sun.COM break; 207310972SRic.Aleshire@Sun.COM } 207410972SRic.Aleshire@Sun.COM m_label_free(new_sl); 207510972SRic.Aleshire@Sun.COM 207610972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, ascii, proplen); 207710972SRic.Aleshire@Sun.COM free(ascii); 207810972SRic.Aleshire@Sun.COM } 207910972SRic.Aleshire@Sun.COM break; 208010972SRic.Aleshire@Sun.COM 2081789Sahrens default: 20824577Sahrens switch (zfs_prop_get_type(prop)) { 20834787Sahrens case PROP_TYPE_NUMBER: 20844577Sahrens if (get_numeric_property(zhp, prop, src, 20854577Sahrens &source, &val) != 0) 20864577Sahrens return (-1); 20874577Sahrens if (literal) 20884577Sahrens (void) snprintf(propbuf, proplen, "%llu", 20894577Sahrens (u_longlong_t)val); 20904577Sahrens else 20914577Sahrens zfs_nicenum(val, propbuf, proplen); 20924577Sahrens break; 20934577Sahrens 20944787Sahrens case PROP_TYPE_STRING: 20954577Sahrens (void) strlcpy(propbuf, 20964577Sahrens getprop_string(zhp, prop, &source), proplen); 20974577Sahrens break; 20984577Sahrens 20994787Sahrens case PROP_TYPE_INDEX: 21004861Sahrens if (get_numeric_property(zhp, prop, src, 21014861Sahrens &source, &val) != 0) 21024861Sahrens return (-1); 21034861Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 21044577Sahrens return (-1); 21054577Sahrens (void) strlcpy(propbuf, strval, proplen); 21064577Sahrens break; 21074577Sahrens 21084577Sahrens default: 21094577Sahrens abort(); 21104577Sahrens } 2111789Sahrens } 2112789Sahrens 2113789Sahrens get_source(zhp, src, source, statbuf, statlen); 2114789Sahrens 2115789Sahrens return (0); 2116789Sahrens } 2117789Sahrens 2118789Sahrens /* 2119789Sahrens * Utility function to get the given numeric property. Does no validation that 2120789Sahrens * the given property is the appropriate type; should only be used with 2121789Sahrens * hard-coded property types. 2122789Sahrens */ 2123789Sahrens uint64_t 2124789Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2125789Sahrens { 2126789Sahrens char *source; 21272082Seschrock uint64_t val; 21282082Seschrock 21295367Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 21302082Seschrock 21312082Seschrock return (val); 2132789Sahrens } 2133789Sahrens 21345713Srm160521 int 21355713Srm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21365713Srm160521 { 21375713Srm160521 char buf[64]; 21385713Srm160521 21399396SMatthew.Ahrens@Sun.COM (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21405713Srm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21415713Srm160521 } 21425713Srm160521 2143789Sahrens /* 2144789Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2145789Sahrens */ 2146789Sahrens int 2147789Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 21485094Slling zprop_source_t *src, char *statbuf, size_t statlen) 2149789Sahrens { 2150789Sahrens char *source; 2151789Sahrens 2152789Sahrens /* 2153789Sahrens * Check to see if this property applies to our object 2154789Sahrens */ 21554849Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 21563237Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 21572082Seschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 21582082Seschrock zfs_prop_to_name(prop))); 21594849Sahrens } 2160789Sahrens 2161789Sahrens if (src) 21625094Slling *src = ZPROP_SRC_NONE; 2163789Sahrens 21642082Seschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 21652082Seschrock return (-1); 2166789Sahrens 2167789Sahrens get_source(zhp, src, source, statbuf, statlen); 2168789Sahrens 2169789Sahrens return (0); 2170789Sahrens } 2171789Sahrens 21729396SMatthew.Ahrens@Sun.COM static int 21739396SMatthew.Ahrens@Sun.COM idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 21749396SMatthew.Ahrens@Sun.COM char **domainp, idmap_rid_t *ridp) 21759396SMatthew.Ahrens@Sun.COM { 21769396SMatthew.Ahrens@Sun.COM idmap_handle_t *idmap_hdl = NULL; 21779396SMatthew.Ahrens@Sun.COM idmap_get_handle_t *get_hdl = NULL; 21789396SMatthew.Ahrens@Sun.COM idmap_stat status; 21799396SMatthew.Ahrens@Sun.COM int err = EINVAL; 21809396SMatthew.Ahrens@Sun.COM 21819396SMatthew.Ahrens@Sun.COM if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 21829396SMatthew.Ahrens@Sun.COM goto out; 21839396SMatthew.Ahrens@Sun.COM if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 21849396SMatthew.Ahrens@Sun.COM goto out; 21859396SMatthew.Ahrens@Sun.COM 21869396SMatthew.Ahrens@Sun.COM if (isuser) { 21879396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbyuid(get_hdl, id, 21889396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21899396SMatthew.Ahrens@Sun.COM } else { 21909396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbygid(get_hdl, id, 21919396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21929396SMatthew.Ahrens@Sun.COM } 21939396SMatthew.Ahrens@Sun.COM if (err == IDMAP_SUCCESS && 21949396SMatthew.Ahrens@Sun.COM idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 21959396SMatthew.Ahrens@Sun.COM status == IDMAP_SUCCESS) 21969396SMatthew.Ahrens@Sun.COM err = 0; 21979396SMatthew.Ahrens@Sun.COM else 21989396SMatthew.Ahrens@Sun.COM err = EINVAL; 21999396SMatthew.Ahrens@Sun.COM out: 22009396SMatthew.Ahrens@Sun.COM if (get_hdl) 22019396SMatthew.Ahrens@Sun.COM idmap_get_destroy(get_hdl); 22029396SMatthew.Ahrens@Sun.COM if (idmap_hdl) 22039396SMatthew.Ahrens@Sun.COM (void) idmap_fini(idmap_hdl); 22049396SMatthew.Ahrens@Sun.COM return (err); 22059396SMatthew.Ahrens@Sun.COM } 22069396SMatthew.Ahrens@Sun.COM 22079396SMatthew.Ahrens@Sun.COM /* 22089396SMatthew.Ahrens@Sun.COM * convert the propname into parameters needed by kernel 22099396SMatthew.Ahrens@Sun.COM * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 22109396SMatthew.Ahrens@Sun.COM * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 22119396SMatthew.Ahrens@Sun.COM */ 22129396SMatthew.Ahrens@Sun.COM static int 22139396SMatthew.Ahrens@Sun.COM userquota_propname_decode(const char *propname, boolean_t zoned, 22149396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 22159396SMatthew.Ahrens@Sun.COM { 22169396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t type; 22179396SMatthew.Ahrens@Sun.COM char *cp, *end; 221810160SMatthew.Ahrens@Sun.COM char *numericsid = NULL; 22199396SMatthew.Ahrens@Sun.COM boolean_t isuser; 22209396SMatthew.Ahrens@Sun.COM 22219396SMatthew.Ahrens@Sun.COM domain[0] = '\0'; 22229396SMatthew.Ahrens@Sun.COM 22239396SMatthew.Ahrens@Sun.COM /* Figure out the property type ({user|group}{quota|space}) */ 22249396SMatthew.Ahrens@Sun.COM for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 22259396SMatthew.Ahrens@Sun.COM if (strncmp(propname, zfs_userquota_prop_prefixes[type], 22269396SMatthew.Ahrens@Sun.COM strlen(zfs_userquota_prop_prefixes[type])) == 0) 22279396SMatthew.Ahrens@Sun.COM break; 22289396SMatthew.Ahrens@Sun.COM } 22299396SMatthew.Ahrens@Sun.COM if (type == ZFS_NUM_USERQUOTA_PROPS) 22309396SMatthew.Ahrens@Sun.COM return (EINVAL); 22319396SMatthew.Ahrens@Sun.COM *typep = type; 22329396SMatthew.Ahrens@Sun.COM 22339396SMatthew.Ahrens@Sun.COM isuser = (type == ZFS_PROP_USERQUOTA || 22349396SMatthew.Ahrens@Sun.COM type == ZFS_PROP_USERUSED); 22359396SMatthew.Ahrens@Sun.COM 22369396SMatthew.Ahrens@Sun.COM cp = strchr(propname, '@') + 1; 22379396SMatthew.Ahrens@Sun.COM 22389396SMatthew.Ahrens@Sun.COM if (strchr(cp, '@')) { 22399396SMatthew.Ahrens@Sun.COM /* 22409396SMatthew.Ahrens@Sun.COM * It's a SID name (eg "user@domain") that needs to be 224110160SMatthew.Ahrens@Sun.COM * turned into S-1-domainID-RID. 22429396SMatthew.Ahrens@Sun.COM */ 224310160SMatthew.Ahrens@Sun.COM directory_error_t e; 22449396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22459396SMatthew.Ahrens@Sun.COM return (ENOENT); 224610160SMatthew.Ahrens@Sun.COM if (isuser) { 224710160SMatthew.Ahrens@Sun.COM e = directory_sid_from_user_name(NULL, 224810160SMatthew.Ahrens@Sun.COM cp, &numericsid); 224910160SMatthew.Ahrens@Sun.COM } else { 225010160SMatthew.Ahrens@Sun.COM e = directory_sid_from_group_name(NULL, 225110160SMatthew.Ahrens@Sun.COM cp, &numericsid); 225210160SMatthew.Ahrens@Sun.COM } 225310160SMatthew.Ahrens@Sun.COM if (e != NULL) { 225410160SMatthew.Ahrens@Sun.COM directory_error_free(e); 22559396SMatthew.Ahrens@Sun.COM return (ENOENT); 225610160SMatthew.Ahrens@Sun.COM } 225710160SMatthew.Ahrens@Sun.COM if (numericsid == NULL) 225810160SMatthew.Ahrens@Sun.COM return (ENOENT); 225910160SMatthew.Ahrens@Sun.COM cp = numericsid; 226010160SMatthew.Ahrens@Sun.COM /* will be further decoded below */ 226110160SMatthew.Ahrens@Sun.COM } 226210160SMatthew.Ahrens@Sun.COM 226310160SMatthew.Ahrens@Sun.COM if (strncmp(cp, "S-1-", 4) == 0) { 22649396SMatthew.Ahrens@Sun.COM /* It's a numeric SID (eg "S-1-234-567-89") */ 226510160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, cp, domainlen); 22669396SMatthew.Ahrens@Sun.COM cp = strrchr(domain, '-'); 22679396SMatthew.Ahrens@Sun.COM *cp = '\0'; 22689396SMatthew.Ahrens@Sun.COM cp++; 22699396SMatthew.Ahrens@Sun.COM 22709396SMatthew.Ahrens@Sun.COM errno = 0; 22719396SMatthew.Ahrens@Sun.COM *ridp = strtoull(cp, &end, 10); 227210160SMatthew.Ahrens@Sun.COM if (numericsid) { 227310160SMatthew.Ahrens@Sun.COM free(numericsid); 227410160SMatthew.Ahrens@Sun.COM numericsid = NULL; 227510160SMatthew.Ahrens@Sun.COM } 22769688SMatthew.Ahrens@Sun.COM if (errno != 0 || *end != '\0') 22779396SMatthew.Ahrens@Sun.COM return (EINVAL); 22789396SMatthew.Ahrens@Sun.COM } else if (!isdigit(*cp)) { 22799396SMatthew.Ahrens@Sun.COM /* 22809396SMatthew.Ahrens@Sun.COM * It's a user/group name (eg "user") that needs to be 22819396SMatthew.Ahrens@Sun.COM * turned into a uid/gid 22829396SMatthew.Ahrens@Sun.COM */ 22839396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22849396SMatthew.Ahrens@Sun.COM return (ENOENT); 22859396SMatthew.Ahrens@Sun.COM if (isuser) { 22869396SMatthew.Ahrens@Sun.COM struct passwd *pw; 22879396SMatthew.Ahrens@Sun.COM pw = getpwnam(cp); 22889396SMatthew.Ahrens@Sun.COM if (pw == NULL) 22899396SMatthew.Ahrens@Sun.COM return (ENOENT); 22909396SMatthew.Ahrens@Sun.COM *ridp = pw->pw_uid; 22919396SMatthew.Ahrens@Sun.COM } else { 22929396SMatthew.Ahrens@Sun.COM struct group *gr; 22939396SMatthew.Ahrens@Sun.COM gr = getgrnam(cp); 22949396SMatthew.Ahrens@Sun.COM if (gr == NULL) 22959396SMatthew.Ahrens@Sun.COM return (ENOENT); 22969396SMatthew.Ahrens@Sun.COM *ridp = gr->gr_gid; 22979396SMatthew.Ahrens@Sun.COM } 22989396SMatthew.Ahrens@Sun.COM } else { 22999396SMatthew.Ahrens@Sun.COM /* It's a user/group ID (eg "12345"). */ 23009396SMatthew.Ahrens@Sun.COM uid_t id = strtoul(cp, &end, 10); 23019396SMatthew.Ahrens@Sun.COM idmap_rid_t rid; 23029396SMatthew.Ahrens@Sun.COM char *mapdomain; 23039396SMatthew.Ahrens@Sun.COM 23049396SMatthew.Ahrens@Sun.COM if (*end != '\0') 23059396SMatthew.Ahrens@Sun.COM return (EINVAL); 23069396SMatthew.Ahrens@Sun.COM if (id > MAXUID) { 23079396SMatthew.Ahrens@Sun.COM /* It's an ephemeral ID. */ 23089396SMatthew.Ahrens@Sun.COM if (idmap_id_to_numeric_domain_rid(id, isuser, 23099396SMatthew.Ahrens@Sun.COM &mapdomain, &rid) != 0) 23109396SMatthew.Ahrens@Sun.COM return (ENOENT); 231110160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, mapdomain, domainlen); 23129396SMatthew.Ahrens@Sun.COM *ridp = rid; 23139396SMatthew.Ahrens@Sun.COM } else { 23149396SMatthew.Ahrens@Sun.COM *ridp = id; 23159396SMatthew.Ahrens@Sun.COM } 23169396SMatthew.Ahrens@Sun.COM } 23179396SMatthew.Ahrens@Sun.COM 231810160SMatthew.Ahrens@Sun.COM ASSERT3P(numericsid, ==, NULL); 23199396SMatthew.Ahrens@Sun.COM return (0); 23209396SMatthew.Ahrens@Sun.COM } 23219396SMatthew.Ahrens@Sun.COM 23229469SLin.Ling@Sun.COM static int 23239469SLin.Ling@Sun.COM zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 23249469SLin.Ling@Sun.COM uint64_t *propvalue, zfs_userquota_prop_t *typep) 23259396SMatthew.Ahrens@Sun.COM { 23269396SMatthew.Ahrens@Sun.COM int err; 23279396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 23289396SMatthew.Ahrens@Sun.COM 23299396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 23309396SMatthew.Ahrens@Sun.COM 23319396SMatthew.Ahrens@Sun.COM err = userquota_propname_decode(propname, 23329396SMatthew.Ahrens@Sun.COM zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 23339469SLin.Ling@Sun.COM typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 23349469SLin.Ling@Sun.COM zc.zc_objset_type = *typep; 23359396SMatthew.Ahrens@Sun.COM if (err) 23369396SMatthew.Ahrens@Sun.COM return (err); 23379396SMatthew.Ahrens@Sun.COM 23389396SMatthew.Ahrens@Sun.COM err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 23399396SMatthew.Ahrens@Sun.COM if (err) 23409396SMatthew.Ahrens@Sun.COM return (err); 23419396SMatthew.Ahrens@Sun.COM 23429469SLin.Ling@Sun.COM *propvalue = zc.zc_cookie; 23439469SLin.Ling@Sun.COM return (0); 23449469SLin.Ling@Sun.COM } 23459469SLin.Ling@Sun.COM 23469469SLin.Ling@Sun.COM int 23479469SLin.Ling@Sun.COM zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 23489469SLin.Ling@Sun.COM uint64_t *propvalue) 23499469SLin.Ling@Sun.COM { 23509469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23519469SLin.Ling@Sun.COM 23529469SLin.Ling@Sun.COM return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 23539469SLin.Ling@Sun.COM &type)); 23549469SLin.Ling@Sun.COM } 23559469SLin.Ling@Sun.COM 23569469SLin.Ling@Sun.COM int 23579469SLin.Ling@Sun.COM zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 23589469SLin.Ling@Sun.COM char *propbuf, int proplen, boolean_t literal) 23599469SLin.Ling@Sun.COM { 23609469SLin.Ling@Sun.COM int err; 23619469SLin.Ling@Sun.COM uint64_t propvalue; 23629469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23639469SLin.Ling@Sun.COM 23649469SLin.Ling@Sun.COM err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 23659469SLin.Ling@Sun.COM &type); 23669469SLin.Ling@Sun.COM 23679469SLin.Ling@Sun.COM if (err) 23689469SLin.Ling@Sun.COM return (err); 23699469SLin.Ling@Sun.COM 23709396SMatthew.Ahrens@Sun.COM if (literal) { 23719469SLin.Ling@Sun.COM (void) snprintf(propbuf, proplen, "%llu", propvalue); 23729469SLin.Ling@Sun.COM } else if (propvalue == 0 && 23739396SMatthew.Ahrens@Sun.COM (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 23749396SMatthew.Ahrens@Sun.COM (void) strlcpy(propbuf, "none", proplen); 23759396SMatthew.Ahrens@Sun.COM } else { 23769469SLin.Ling@Sun.COM zfs_nicenum(propvalue, propbuf, proplen); 23779396SMatthew.Ahrens@Sun.COM } 23789396SMatthew.Ahrens@Sun.COM return (0); 23799396SMatthew.Ahrens@Sun.COM } 23809396SMatthew.Ahrens@Sun.COM 2381789Sahrens /* 2382789Sahrens * Returns the name of the given zfs handle. 2383789Sahrens */ 2384789Sahrens const char * 2385789Sahrens zfs_get_name(const zfs_handle_t *zhp) 2386789Sahrens { 2387789Sahrens return (zhp->zfs_name); 2388789Sahrens } 2389789Sahrens 2390789Sahrens /* 2391789Sahrens * Returns the type of the given zfs handle. 2392789Sahrens */ 2393789Sahrens zfs_type_t 2394789Sahrens zfs_get_type(const zfs_handle_t *zhp) 2395789Sahrens { 2396789Sahrens return (zhp->zfs_type); 2397789Sahrens } 2398789Sahrens 23998228SEric.Taylor@Sun.COM static int 24008228SEric.Taylor@Sun.COM zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 24018228SEric.Taylor@Sun.COM { 24028228SEric.Taylor@Sun.COM int rc; 24038228SEric.Taylor@Sun.COM uint64_t orig_cookie; 24048228SEric.Taylor@Sun.COM 24058228SEric.Taylor@Sun.COM orig_cookie = zc->zc_cookie; 24068228SEric.Taylor@Sun.COM top: 24078228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 24088228SEric.Taylor@Sun.COM rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 24098228SEric.Taylor@Sun.COM 24108228SEric.Taylor@Sun.COM if (rc == -1) { 24118228SEric.Taylor@Sun.COM switch (errno) { 24128228SEric.Taylor@Sun.COM case ENOMEM: 24138228SEric.Taylor@Sun.COM /* expand nvlist memory and try again */ 24148228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 24158228SEric.Taylor@Sun.COM zcmd_free_nvlists(zc); 24168228SEric.Taylor@Sun.COM return (-1); 24178228SEric.Taylor@Sun.COM } 24188228SEric.Taylor@Sun.COM zc->zc_cookie = orig_cookie; 24198228SEric.Taylor@Sun.COM goto top; 24208228SEric.Taylor@Sun.COM /* 24218228SEric.Taylor@Sun.COM * An errno value of ESRCH indicates normal completion. 24228228SEric.Taylor@Sun.COM * If ENOENT is returned, then the underlying dataset 24238228SEric.Taylor@Sun.COM * has been removed since we obtained the handle. 24248228SEric.Taylor@Sun.COM */ 24258228SEric.Taylor@Sun.COM case ESRCH: 24268228SEric.Taylor@Sun.COM case ENOENT: 24278228SEric.Taylor@Sun.COM rc = 1; 24288228SEric.Taylor@Sun.COM break; 24298228SEric.Taylor@Sun.COM default: 24308228SEric.Taylor@Sun.COM rc = zfs_standard_error(zhp->zfs_hdl, errno, 24318228SEric.Taylor@Sun.COM dgettext(TEXT_DOMAIN, 24328228SEric.Taylor@Sun.COM "cannot iterate filesystems")); 24338228SEric.Taylor@Sun.COM break; 24348228SEric.Taylor@Sun.COM } 24358228SEric.Taylor@Sun.COM } 24368228SEric.Taylor@Sun.COM return (rc); 24378228SEric.Taylor@Sun.COM } 24388228SEric.Taylor@Sun.COM 2439789Sahrens /* 24401356Seschrock * Iterate over all child filesystems 2441789Sahrens */ 2442789Sahrens int 24431356Seschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2444789Sahrens { 2445789Sahrens zfs_cmd_t zc = { 0 }; 2446789Sahrens zfs_handle_t *nzhp; 2447789Sahrens int ret; 2448789Sahrens 24495367Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24505367Sahrens return (0); 24515367Sahrens 24528228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24538228SEric.Taylor@Sun.COM return (-1); 24548228SEric.Taylor@Sun.COM 24558228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 24568228SEric.Taylor@Sun.COM &zc)) == 0) { 2457789Sahrens /* 2458789Sahrens * Silently ignore errors, as the only plausible explanation is 2459789Sahrens * that the pool has since been removed. 2460789Sahrens */ 24618228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24628228SEric.Taylor@Sun.COM &zc)) == NULL) { 2463789Sahrens continue; 24648228SEric.Taylor@Sun.COM } 24658228SEric.Taylor@Sun.COM 24668228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24678228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2468789Sahrens return (ret); 24698228SEric.Taylor@Sun.COM } 2470789Sahrens } 24718228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 24728228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 24731356Seschrock } 24741356Seschrock 24751356Seschrock /* 24761356Seschrock * Iterate over all snapshots 24771356Seschrock */ 24781356Seschrock int 24791356Seschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24801356Seschrock { 24811356Seschrock zfs_cmd_t zc = { 0 }; 24821356Seschrock zfs_handle_t *nzhp; 24831356Seschrock int ret; 2484789Sahrens 24855367Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24865367Sahrens return (0); 24875367Sahrens 24888228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24898228SEric.Taylor@Sun.COM return (-1); 24908228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 24918228SEric.Taylor@Sun.COM &zc)) == 0) { 24928228SEric.Taylor@Sun.COM 24938228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24948228SEric.Taylor@Sun.COM &zc)) == NULL) { 2495789Sahrens continue; 24968228SEric.Taylor@Sun.COM } 24978228SEric.Taylor@Sun.COM 24988228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24998228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2500789Sahrens return (ret); 25018228SEric.Taylor@Sun.COM } 2502789Sahrens } 25038228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 25048228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 2505789Sahrens } 2506789Sahrens 2507789Sahrens /* 25081356Seschrock * Iterate over all children, snapshots and filesystems 25091356Seschrock */ 25101356Seschrock int 25111356Seschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25121356Seschrock { 25131356Seschrock int ret; 25141356Seschrock 25151356Seschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25161356Seschrock return (ret); 25171356Seschrock 25181356Seschrock return (zfs_iter_snapshots(zhp, func, data)); 25191356Seschrock } 25201356Seschrock 25211356Seschrock /* 252211497SMark.Musante@Sun.COM * Is one dataset name a child dataset of another? 252311497SMark.Musante@Sun.COM * 252411497SMark.Musante@Sun.COM * Needs to handle these cases: 252511497SMark.Musante@Sun.COM * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 252611497SMark.Musante@Sun.COM * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 252711497SMark.Musante@Sun.COM * Descendant? No. No. No. Yes. 252811497SMark.Musante@Sun.COM */ 252911497SMark.Musante@Sun.COM static boolean_t 253011497SMark.Musante@Sun.COM is_descendant(const char *ds1, const char *ds2) 253111497SMark.Musante@Sun.COM { 253211497SMark.Musante@Sun.COM size_t d1len = strlen(ds1); 253311497SMark.Musante@Sun.COM 253411497SMark.Musante@Sun.COM /* ds2 can't be a descendant if it's smaller */ 253511497SMark.Musante@Sun.COM if (strlen(ds2) < d1len) 253611497SMark.Musante@Sun.COM return (B_FALSE); 253711497SMark.Musante@Sun.COM 253811497SMark.Musante@Sun.COM /* otherwise, compare strings and verify that there's a '/' char */ 253911497SMark.Musante@Sun.COM return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 254011497SMark.Musante@Sun.COM } 254111497SMark.Musante@Sun.COM 254211497SMark.Musante@Sun.COM /* 2543789Sahrens * Given a complete name, return just the portion that refers to the parent. 2544789Sahrens * Can return NULL if this is a pool. 2545789Sahrens */ 2546789Sahrens static int 2547789Sahrens parent_name(const char *path, char *buf, size_t buflen) 2548789Sahrens { 2549789Sahrens char *loc; 2550789Sahrens 2551789Sahrens if ((loc = strrchr(path, '/')) == NULL) 2552789Sahrens return (-1); 2553789Sahrens 2554789Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2555789Sahrens buf[loc - path] = '\0'; 2556789Sahrens 2557789Sahrens return (0); 2558789Sahrens } 2559789Sahrens 2560789Sahrens /* 25614490Svb160487 * If accept_ancestor is false, then check to make sure that the given path has 25624490Svb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25634490Svb160487 * closest existing ancestor for the given path. In prefixlen return the 25644490Svb160487 * length of already existing prefix of the given path. We also fetch the 25654490Svb160487 * 'zoned' property, which is used to validate property settings when creating 25664490Svb160487 * new datasets. 2567789Sahrens */ 2568789Sahrens static int 25694490Svb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25704490Svb160487 boolean_t accept_ancestor, int *prefixlen) 2571789Sahrens { 2572789Sahrens zfs_cmd_t zc = { 0 }; 2573789Sahrens char parent[ZFS_MAXNAMELEN]; 2574789Sahrens char *slash; 25751356Seschrock zfs_handle_t *zhp; 25762082Seschrock char errbuf[1024]; 257711497SMark.Musante@Sun.COM uint64_t is_zoned; 25782082Seschrock 25798269SMark.Musante@Sun.COM (void) snprintf(errbuf, sizeof (errbuf), 25808269SMark.Musante@Sun.COM dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2581789Sahrens 2582789Sahrens /* get parent, and check to see if this is just a pool */ 2583789Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 25842082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25852082Seschrock "missing dataset name")); 25862082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2587789Sahrens } 2588789Sahrens 2589789Sahrens /* check to see if the pool exists */ 2590789Sahrens if ((slash = strchr(parent, '/')) == NULL) 2591789Sahrens slash = parent + strlen(parent); 2592789Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2593789Sahrens zc.zc_name[slash - parent] = '\0'; 25942082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2595789Sahrens errno == ENOENT) { 25962082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25972082Seschrock "no such pool '%s'"), zc.zc_name); 25982082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2599789Sahrens } 2600789Sahrens 2601789Sahrens /* check to see if the parent dataset exists */ 26024490Svb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 26034490Svb160487 if (errno == ENOENT && accept_ancestor) { 26044490Svb160487 /* 26054490Svb160487 * Go deeper to find an ancestor, give up on top level. 26064490Svb160487 */ 26074490Svb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 26084490Svb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26094490Svb160487 "no such pool '%s'"), zc.zc_name); 26104490Svb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26114490Svb160487 } 26124490Svb160487 } else if (errno == ENOENT) { 26132082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26142082Seschrock "parent does not exist")); 26152082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26164490Svb160487 } else 26172082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2618789Sahrens } 2619789Sahrens 262011497SMark.Musante@Sun.COM is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 262111497SMark.Musante@Sun.COM if (zoned != NULL) 262211497SMark.Musante@Sun.COM *zoned = is_zoned; 262311497SMark.Musante@Sun.COM 2624789Sahrens /* we are in a non-global zone, but parent is in the global zone */ 262511497SMark.Musante@Sun.COM if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 26262082Seschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26271356Seschrock zfs_close(zhp); 2628789Sahrens return (-1); 2629789Sahrens } 2630789Sahrens 2631789Sahrens /* make sure parent is a filesystem */ 26321356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 26332082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26342082Seschrock "parent is not a filesystem")); 26352082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26361356Seschrock zfs_close(zhp); 2637789Sahrens return (-1); 2638789Sahrens } 2639789Sahrens 26401356Seschrock zfs_close(zhp); 26414490Svb160487 if (prefixlen != NULL) 26424490Svb160487 *prefixlen = strlen(parent); 26434490Svb160487 return (0); 26444490Svb160487 } 26454490Svb160487 26464490Svb160487 /* 26474490Svb160487 * Finds whether the dataset of the given type(s) exists. 26484490Svb160487 */ 26494490Svb160487 boolean_t 26504490Svb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26514490Svb160487 { 26524490Svb160487 zfs_handle_t *zhp; 26534490Svb160487 26545326Sek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26554490Svb160487 return (B_FALSE); 26564490Svb160487 26574490Svb160487 /* 26584490Svb160487 * Try to get stats for the dataset, which will tell us if it exists. 26594490Svb160487 */ 26604490Svb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26614490Svb160487 int ds_type = zhp->zfs_type; 26624490Svb160487 26634490Svb160487 zfs_close(zhp); 26644490Svb160487 if (types & ds_type) 26654490Svb160487 return (B_TRUE); 26664490Svb160487 } 26674490Svb160487 return (B_FALSE); 26684490Svb160487 } 26694490Svb160487 26704490Svb160487 /* 26715367Sahrens * Given a path to 'target', create all the ancestors between 26725367Sahrens * the prefixlen portion of the path, and the target itself. 26735367Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26745367Sahrens */ 26755367Sahrens int 26765367Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26775367Sahrens { 26785367Sahrens zfs_handle_t *h; 26795367Sahrens char *cp; 26805367Sahrens const char *opname; 26815367Sahrens 26825367Sahrens /* make sure prefix exists */ 26835367Sahrens cp = target + prefixlen; 26845367Sahrens if (*cp != '/') { 26855367Sahrens assert(strchr(cp, '/') == NULL); 26865367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26875367Sahrens } else { 26885367Sahrens *cp = '\0'; 26895367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26905367Sahrens *cp = '/'; 26915367Sahrens } 26925367Sahrens if (h == NULL) 26935367Sahrens return (-1); 26945367Sahrens zfs_close(h); 26955367Sahrens 26965367Sahrens /* 26975367Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26985367Sahrens * up to the prefixlen-long one. 26995367Sahrens */ 27005367Sahrens for (cp = target + prefixlen + 1; 27015367Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 27025367Sahrens char *logstr; 27035367Sahrens 27045367Sahrens *cp = '\0'; 27055367Sahrens 27065367Sahrens h = make_dataset_handle(hdl, target); 27075367Sahrens if (h) { 27085367Sahrens /* it already exists, nothing to do here */ 27095367Sahrens zfs_close(h); 27105367Sahrens continue; 27115367Sahrens } 27125367Sahrens 27135367Sahrens logstr = hdl->libzfs_log_str; 27145367Sahrens hdl->libzfs_log_str = NULL; 27155367Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27165367Sahrens NULL) != 0) { 27175367Sahrens hdl->libzfs_log_str = logstr; 27185367Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27195367Sahrens goto ancestorerr; 27205367Sahrens } 27215367Sahrens 27225367Sahrens hdl->libzfs_log_str = logstr; 27235367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27245367Sahrens if (h == NULL) { 27255367Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27265367Sahrens goto ancestorerr; 27275367Sahrens } 27285367Sahrens 27295367Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27305367Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27315367Sahrens goto ancestorerr; 27325367Sahrens } 27335367Sahrens 27345367Sahrens if (zfs_share(h) != 0) { 27355367Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27365367Sahrens goto ancestorerr; 27375367Sahrens } 27385367Sahrens 27395367Sahrens zfs_close(h); 27405367Sahrens } 27415367Sahrens 27425367Sahrens return (0); 27435367Sahrens 27445367Sahrens ancestorerr: 27455367Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27465367Sahrens "failed to %s ancestor '%s'"), opname, target); 27475367Sahrens return (-1); 27485367Sahrens } 27495367Sahrens 27505367Sahrens /* 27514490Svb160487 * Creates non-existing ancestors of the given path. 27524490Svb160487 */ 27534490Svb160487 int 27544490Svb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27554490Svb160487 { 27564490Svb160487 int prefix; 27574490Svb160487 char *path_copy; 27584490Svb160487 int rc; 27594490Svb160487 276011497SMark.Musante@Sun.COM if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27614490Svb160487 return (-1); 27624490Svb160487 27634490Svb160487 if ((path_copy = strdup(path)) != NULL) { 27644490Svb160487 rc = create_parents(hdl, path_copy, prefix); 27654490Svb160487 free(path_copy); 27664490Svb160487 } 27674490Svb160487 if (path_copy == NULL || rc != 0) 27684490Svb160487 return (-1); 27694490Svb160487 2770789Sahrens return (0); 2771789Sahrens } 2772789Sahrens 2773789Sahrens /* 27742676Seschrock * Create a new filesystem or volume. 2775789Sahrens */ 2776789Sahrens int 27772082Seschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 27782676Seschrock nvlist_t *props) 2779789Sahrens { 2780789Sahrens zfs_cmd_t zc = { 0 }; 2781789Sahrens int ret; 2782789Sahrens uint64_t size = 0; 2783789Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 27842082Seschrock char errbuf[1024]; 27852676Seschrock uint64_t zoned; 27862082Seschrock 27872082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 27882082Seschrock "cannot create '%s'"), path); 2789789Sahrens 2790789Sahrens /* validate the path, taking care to note the extended error message */ 27915326Sek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 27922082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2793789Sahrens 2794789Sahrens /* validate parents exist */ 27954490Svb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2796789Sahrens return (-1); 2797789Sahrens 2798789Sahrens /* 2799789Sahrens * The failure modes when creating a dataset of a different type over 2800789Sahrens * one that already exists is a little strange. In particular, if you 2801789Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2802789Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2803789Sahrens * first try to see if the dataset exists. 2804789Sahrens */ 2805789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 28065094Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 28072082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28082082Seschrock "dataset already exists")); 28092082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2810789Sahrens } 2811789Sahrens 2812789Sahrens if (type == ZFS_TYPE_VOLUME) 2813789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2814789Sahrens else 2815789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2816789Sahrens 28177184Stimh if (props && (props = zfs_valid_proplist(hdl, type, props, 28183912Slling zoned, NULL, errbuf)) == 0) 28192676Seschrock return (-1); 28202676Seschrock 2821789Sahrens if (type == ZFS_TYPE_VOLUME) { 28221133Seschrock /* 28231133Seschrock * If we are creating a volume, the size and block size must 28241133Seschrock * satisfy a few restraints. First, the blocksize must be a 28251133Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28261133Seschrock * volsize must be a multiple of the block size, and cannot be 28271133Seschrock * zero. 28281133Seschrock */ 28292676Seschrock if (props == NULL || nvlist_lookup_uint64(props, 28302676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 28312676Seschrock nvlist_free(props); 28322082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28332676Seschrock "missing volume size")); 28342676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2835789Sahrens } 2836789Sahrens 28372676Seschrock if ((ret = nvlist_lookup_uint64(props, 28382676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 28392676Seschrock &blocksize)) != 0) { 28402676Seschrock if (ret == ENOENT) { 28412676Seschrock blocksize = zfs_prop_default_numeric( 28422676Seschrock ZFS_PROP_VOLBLOCKSIZE); 28432676Seschrock } else { 28442676Seschrock nvlist_free(props); 28452676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28462676Seschrock "missing volume block size")); 28472676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28482676Seschrock } 28492676Seschrock } 28502676Seschrock 28512676Seschrock if (size == 0) { 28522676Seschrock nvlist_free(props); 28532082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28542676Seschrock "volume size cannot be zero")); 28552676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28561133Seschrock } 28571133Seschrock 28581133Seschrock if (size % blocksize != 0) { 28592676Seschrock nvlist_free(props); 28602082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28612676Seschrock "volume size must be a multiple of volume block " 28622676Seschrock "size")); 28632676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28641133Seschrock } 2865789Sahrens } 2866789Sahrens 28675094Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 28682676Seschrock return (-1); 28692676Seschrock nvlist_free(props); 28702676Seschrock 2871789Sahrens /* create the dataset */ 28724543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2873789Sahrens 28742676Seschrock zcmd_free_nvlists(&zc); 28752676Seschrock 2876789Sahrens /* check for failure */ 2877789Sahrens if (ret != 0) { 2878789Sahrens char parent[ZFS_MAXNAMELEN]; 2879789Sahrens (void) parent_name(path, parent, sizeof (parent)); 2880789Sahrens 2881789Sahrens switch (errno) { 2882789Sahrens case ENOENT: 28832082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28842082Seschrock "no such parent '%s'"), parent); 28852082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2886789Sahrens 2887789Sahrens case EINVAL: 28882082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28893413Smmusante "parent '%s' is not a filesystem"), parent); 28902082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2891789Sahrens 2892789Sahrens case EDOM: 28932082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28942676Seschrock "volume block size must be power of 2 from " 28952676Seschrock "%u to %uk"), 2896789Sahrens (uint_t)SPA_MINBLOCKSIZE, 2897789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 28982082Seschrock 28992676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 29002082Seschrock 29014603Sahrens case ENOTSUP: 29024603Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29034603Sahrens "pool must be upgraded to set this " 29044603Sahrens "property or value")); 29054603Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2906789Sahrens #ifdef _ILP32 2907789Sahrens case EOVERFLOW: 2908789Sahrens /* 2909789Sahrens * This platform can't address a volume this big. 2910789Sahrens */ 29112082Seschrock if (type == ZFS_TYPE_VOLUME) 29122082Seschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 29132082Seschrock errbuf)); 2914789Sahrens #endif 29152082Seschrock /* FALLTHROUGH */ 2916789Sahrens default: 29172082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2918789Sahrens } 2919789Sahrens } 2920789Sahrens 2921789Sahrens return (0); 2922789Sahrens } 2923789Sahrens 2924789Sahrens /* 2925789Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2926789Sahrens * isn't mounted, and that there are no active dependents. 2927789Sahrens */ 2928789Sahrens int 292910242Schris.kirby@sun.com zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2930789Sahrens { 2931789Sahrens zfs_cmd_t zc = { 0 }; 2932789Sahrens 2933789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2934789Sahrens 29352676Seschrock if (ZFS_IS_VOLUME(zhp)) { 2936789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2937789Sahrens } else { 2938789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2939789Sahrens } 2940789Sahrens 294110242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 29424543Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 29433237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 29442082Seschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 29452082Seschrock zhp->zfs_name)); 29462199Sahrens } 2947789Sahrens 2948789Sahrens remove_mountpoint(zhp); 2949789Sahrens 2950789Sahrens return (0); 2951789Sahrens } 2952789Sahrens 29532199Sahrens struct destroydata { 29542199Sahrens char *snapname; 29552199Sahrens boolean_t gotone; 29563265Sahrens boolean_t closezhp; 29572199Sahrens }; 29582199Sahrens 29592199Sahrens static int 296010588SEric.Taylor@Sun.COM zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29612199Sahrens { 29622199Sahrens struct destroydata *dd = arg; 29632199Sahrens zfs_handle_t *szhp; 29642199Sahrens char name[ZFS_MAXNAMELEN]; 29653265Sahrens boolean_t closezhp = dd->closezhp; 296610588SEric.Taylor@Sun.COM int rv = 0; 29672199Sahrens 29682676Seschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 29692676Seschrock (void) strlcat(name, "@", sizeof (name)); 29702676Seschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29712199Sahrens 29722199Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 29732199Sahrens if (szhp) { 29742199Sahrens dd->gotone = B_TRUE; 29752199Sahrens zfs_close(szhp); 29762199Sahrens } 29772199Sahrens 29783265Sahrens dd->closezhp = B_TRUE; 297910588SEric.Taylor@Sun.COM if (!dd->gotone) 298010588SEric.Taylor@Sun.COM rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 29813265Sahrens if (closezhp) 29823265Sahrens zfs_close(zhp); 29833265Sahrens return (rv); 29842199Sahrens } 29852199Sahrens 29862199Sahrens /* 29872199Sahrens * Destroys all snapshots with the given name in zhp & descendants. 29882199Sahrens */ 29892199Sahrens int 299010242Schris.kirby@sun.com zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 29912199Sahrens { 29922199Sahrens zfs_cmd_t zc = { 0 }; 29932199Sahrens int ret; 29942199Sahrens struct destroydata dd = { 0 }; 29952199Sahrens 29962199Sahrens dd.snapname = snapname; 299710588SEric.Taylor@Sun.COM (void) zfs_check_snap_cb(zhp, &dd); 29982199Sahrens 29992199Sahrens if (!dd.gotone) { 30003237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 30012199Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 30022199Sahrens zhp->zfs_name, snapname)); 30032199Sahrens } 30042199Sahrens 30052199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 30062676Seschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 300710242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 30082199Sahrens 30094543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 30102199Sahrens if (ret != 0) { 30112199Sahrens char errbuf[1024]; 30122199Sahrens 30132199Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30142199Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 30152199Sahrens 30162199Sahrens switch (errno) { 30172199Sahrens case EEXIST: 30182199Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30192199Sahrens "snapshot is cloned")); 30202199Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 30212199Sahrens 30222199Sahrens default: 30232199Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 30242199Sahrens errbuf)); 30252199Sahrens } 30262199Sahrens } 30272199Sahrens 30282199Sahrens return (0); 30292199Sahrens } 30302199Sahrens 3031789Sahrens /* 3032789Sahrens * Clones the given dataset. The target must be of the same type as the source. 3033789Sahrens */ 3034789Sahrens int 30352676Seschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3036789Sahrens { 3037789Sahrens zfs_cmd_t zc = { 0 }; 3038789Sahrens char parent[ZFS_MAXNAMELEN]; 3039789Sahrens int ret; 30402082Seschrock char errbuf[1024]; 30412082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 30422676Seschrock zfs_type_t type; 30432676Seschrock uint64_t zoned; 3044789Sahrens 3045789Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3046789Sahrens 30472082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30482082Seschrock "cannot create '%s'"), target); 30492082Seschrock 3050789Sahrens /* validate the target name */ 30515326Sek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 30522082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3053789Sahrens 3054789Sahrens /* validate parents exist */ 30554490Svb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3056789Sahrens return (-1); 3057789Sahrens 3058789Sahrens (void) parent_name(target, parent, sizeof (parent)); 3059789Sahrens 3060789Sahrens /* do the clone */ 30612676Seschrock if (ZFS_IS_VOLUME(zhp)) { 3062789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30632744Snn35248 type = ZFS_TYPE_VOLUME; 30642676Seschrock } else { 3065789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30662744Snn35248 type = ZFS_TYPE_FILESYSTEM; 30672676Seschrock } 30682676Seschrock 30692676Seschrock if (props) { 30707184Stimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30717184Stimh zhp, errbuf)) == NULL) 30722676Seschrock return (-1); 30732676Seschrock 30745094Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 30752676Seschrock nvlist_free(props); 30762676Seschrock return (-1); 30772676Seschrock } 30782676Seschrock 30792676Seschrock nvlist_free(props); 30802676Seschrock } 3081789Sahrens 3082789Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 30832676Seschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 30844543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3085789Sahrens 30862676Seschrock zcmd_free_nvlists(&zc); 30872676Seschrock 3088789Sahrens if (ret != 0) { 3089789Sahrens switch (errno) { 3090789Sahrens 3091789Sahrens case ENOENT: 3092789Sahrens /* 3093789Sahrens * The parent doesn't exist. We should have caught this 3094789Sahrens * above, but there may a race condition that has since 3095789Sahrens * destroyed the parent. 3096789Sahrens * 3097789Sahrens * At this point, we don't know whether it's the source 3098789Sahrens * that doesn't exist anymore, or whether the target 3099789Sahrens * dataset doesn't exist. 3100789Sahrens */ 31012082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 31022082Seschrock "no such parent '%s'"), parent); 31032082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 31042082Seschrock 31052082Seschrock case EXDEV: 31062082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 31072082Seschrock "source and target pools differ")); 31082082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 31092082Seschrock errbuf)); 31102082Seschrock 31112082Seschrock default: 31122082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 31132082Seschrock errbuf)); 31142082Seschrock } 31152082Seschrock } 31162082Seschrock 31172082Seschrock return (ret); 31182082Seschrock } 31192082Seschrock 31202082Seschrock /* 31212082Seschrock * Promotes the given clone fs to be the clone parent. 31222082Seschrock */ 31232082Seschrock int 31242082Seschrock zfs_promote(zfs_handle_t *zhp) 31252082Seschrock { 31262082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 31272082Seschrock zfs_cmd_t zc = { 0 }; 31282082Seschrock char parent[MAXPATHLEN]; 31292082Seschrock int ret; 31302082Seschrock char errbuf[1024]; 31312082Seschrock 31322082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31332082Seschrock "cannot promote '%s'"), zhp->zfs_name); 31342082Seschrock 31352082Seschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 31362082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31372082Seschrock "snapshots can not be promoted")); 31382082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31392082Seschrock } 31402082Seschrock 31415367Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 31422082Seschrock if (parent[0] == '\0') { 31432082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31442082Seschrock "not a cloned filesystem")); 31452082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31462082Seschrock } 314710588SEric.Taylor@Sun.COM 31485367Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 31492676Seschrock sizeof (zc.zc_value)); 31502082Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 31514543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 31522082Seschrock 31532082Seschrock if (ret != 0) { 31542417Sahrens int save_errno = errno; 31552417Sahrens 31562417Sahrens switch (save_errno) { 3157789Sahrens case EEXIST: 315810588SEric.Taylor@Sun.COM /* There is a conflicting snapshot name. */ 31592082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 316010588SEric.Taylor@Sun.COM "conflicting snapshot '%s' from parent '%s'"), 316110588SEric.Taylor@Sun.COM zc.zc_string, parent); 31622082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3163789Sahrens 3164789Sahrens default: 31652417Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3166789Sahrens } 3167789Sahrens } 31682676Seschrock return (ret); 31692199Sahrens } 31702199Sahrens 3171789Sahrens /* 31723504Sahl * Takes a snapshot of the given dataset. 3173789Sahrens */ 3174789Sahrens int 31757265Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 31767265Sahrens nvlist_t *props) 3177789Sahrens { 3178789Sahrens const char *delim; 31797265Sahrens char parent[ZFS_MAXNAMELEN]; 3180789Sahrens zfs_handle_t *zhp; 3181789Sahrens zfs_cmd_t zc = { 0 }; 3182789Sahrens int ret; 31832082Seschrock char errbuf[1024]; 31842082Seschrock 31852082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31862082Seschrock "cannot snapshot '%s'"), path); 31872082Seschrock 31882082Seschrock /* validate the target name */ 31895326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 31902082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3191789Sahrens 31927265Sahrens if (props) { 31937265Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 31947265Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 31957265Sahrens return (-1); 31967265Sahrens 31977265Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 31987265Sahrens nvlist_free(props); 31997265Sahrens return (-1); 32007265Sahrens } 32017265Sahrens 32027265Sahrens nvlist_free(props); 32037265Sahrens } 32047265Sahrens 3205789Sahrens /* make sure the parent exists and is of the appropriate type */ 32062199Sahrens delim = strchr(path, '@'); 3207789Sahrens (void) strncpy(parent, path, delim - path); 3208789Sahrens parent[delim - path] = '\0'; 3209789Sahrens 32102082Seschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3211789Sahrens ZFS_TYPE_VOLUME)) == NULL) { 32127265Sahrens zcmd_free_nvlists(&zc); 3213789Sahrens return (-1); 3214789Sahrens } 3215789Sahrens 32162199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 32172676Seschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 32184543Smarks if (ZFS_IS_VOLUME(zhp)) 32194543Smarks zc.zc_objset_type = DMU_OST_ZVOL; 32204543Smarks else 32214543Smarks zc.zc_objset_type = DMU_OST_ZFS; 32222199Sahrens zc.zc_cookie = recursive; 32234543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 32242199Sahrens 32257265Sahrens zcmd_free_nvlists(&zc); 32267265Sahrens 32272199Sahrens /* 32282199Sahrens * if it was recursive, the one that actually failed will be in 32292199Sahrens * zc.zc_name. 32302199Sahrens */ 323110588SEric.Taylor@Sun.COM if (ret != 0) { 32324543Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 32334543Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 323410588SEric.Taylor@Sun.COM (void) zfs_standard_error(hdl, errno, errbuf); 32352199Sahrens } 3236789Sahrens 3237789Sahrens zfs_close(zhp); 3238789Sahrens 3239789Sahrens return (ret); 3240789Sahrens } 3241789Sahrens 3242789Sahrens /* 32431294Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 32441294Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 32451294Slling * is a dependent and we should just destroy it without checking the transaction 32461294Slling * group. 3247789Sahrens */ 32481294Slling typedef struct rollback_data { 32491294Slling const char *cb_target; /* the snapshot */ 32501294Slling uint64_t cb_create; /* creation time reference */ 32515749Sahrens boolean_t cb_error; 32522082Seschrock boolean_t cb_dependent; 32535749Sahrens boolean_t cb_force; 32541294Slling } rollback_data_t; 32551294Slling 32561294Slling static int 32571294Slling rollback_destroy(zfs_handle_t *zhp, void *data) 32581294Slling { 32591294Slling rollback_data_t *cbp = data; 32601294Slling 32611294Slling if (!cbp->cb_dependent) { 32621294Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 32631294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 32641294Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 32651294Slling cbp->cb_create) { 32664543Smarks char *logstr; 32671294Slling 32682082Seschrock cbp->cb_dependent = B_TRUE; 32695446Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32705446Sahrens rollback_destroy, cbp); 32712082Seschrock cbp->cb_dependent = B_FALSE; 32721294Slling 32734543Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 32744543Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 327510242Schris.kirby@sun.com cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 32764543Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 32771294Slling } 32781294Slling } else { 32795749Sahrens /* We must destroy this clone; first unmount it */ 32805749Sahrens prop_changelist_t *clp; 32815749Sahrens 32827366STim.Haley@Sun.COM clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 32835749Sahrens cbp->cb_force ? MS_FORCE: 0); 32845749Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 32855749Sahrens cbp->cb_error = B_TRUE; 32865749Sahrens zfs_close(zhp); 32875749Sahrens return (0); 32885749Sahrens } 328910242Schris.kirby@sun.com if (zfs_destroy(zhp, B_FALSE) != 0) 32905749Sahrens cbp->cb_error = B_TRUE; 32915749Sahrens else 32925749Sahrens changelist_remove(clp, zhp->zfs_name); 32935751Sahrens (void) changelist_postfix(clp); 32945749Sahrens changelist_free(clp); 32951294Slling } 32961294Slling 32971294Slling zfs_close(zhp); 32981294Slling return (0); 32991294Slling } 33001294Slling 33011294Slling /* 33025446Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33035446Sahrens * data changes since then and making it the active dataset. 33045446Sahrens * 33055446Sahrens * Any snapshots more recent than the target are destroyed, along with 33065446Sahrens * their dependents. 33071294Slling */ 33085446Sahrens int 33095749Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3310789Sahrens { 33115446Sahrens rollback_data_t cb = { 0 }; 33125446Sahrens int err; 3313789Sahrens zfs_cmd_t zc = { 0 }; 33145713Srm160521 boolean_t restore_resv = 0; 33155713Srm160521 uint64_t old_volsize, new_volsize; 33165713Srm160521 zfs_prop_t resv_prop; 3317789Sahrens 3318789Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3319789Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3320789Sahrens 33215446Sahrens /* 33225446Sahrens * Destroy all recent snapshots and its dependends. 33235446Sahrens */ 33245749Sahrens cb.cb_force = force; 33255446Sahrens cb.cb_target = snap->zfs_name; 33265446Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33275446Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33285446Sahrens 33295749Sahrens if (cb.cb_error) 33305749Sahrens return (-1); 33315446Sahrens 33325446Sahrens /* 33335446Sahrens * Now that we have verified that the snapshot is the latest, 33345446Sahrens * rollback to the given snapshot. 33355446Sahrens */ 33365446Sahrens 33375713Srm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33385713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33395713Srm160521 return (-1); 33405713Srm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33415713Srm160521 restore_resv = 33425713Srm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33435713Srm160521 } 3344789Sahrens 3345789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3346789Sahrens 33472676Seschrock if (ZFS_IS_VOLUME(zhp)) 3348789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3349789Sahrens else 3350789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3351789Sahrens 3352789Sahrens /* 33535446Sahrens * We rely on zfs_iter_children() to verify that there are no 33545446Sahrens * newer snapshots for the given dataset. Therefore, we can 33555446Sahrens * simply pass the name on to the ioctl() call. There is still 33565446Sahrens * an unlikely race condition where the user has taken a 33575446Sahrens * snapshot since we verified that this was the most recent. 33585713Srm160521 * 3359789Sahrens */ 33605446Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33613237Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33622082Seschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33632082Seschrock zhp->zfs_name); 33645717Srm160521 return (err); 33655717Srm160521 } 33665713Srm160521 33675713Srm160521 /* 33685713Srm160521 * For volumes, if the pre-rollback volsize matched the pre- 33695713Srm160521 * rollback reservation and the volsize has changed then set 33705713Srm160521 * the reservation property to the post-rollback volsize. 33715713Srm160521 * Make a new handle since the rollback closed the dataset. 33725713Srm160521 */ 33735717Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 33745717Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 33755713Srm160521 if (restore_resv) { 33765713Srm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33775713Srm160521 if (old_volsize != new_volsize) 33785717Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 33795717Srm160521 new_volsize); 33805713Srm160521 } 33815713Srm160521 zfs_close(zhp); 3382789Sahrens } 33835446Sahrens return (err); 33841294Slling } 33851294Slling 33861294Slling /* 3387789Sahrens * Iterate over all dependents for a given dataset. This includes both 3388789Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3389789Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3390789Sahrens * libzfs_graph.c. 3391789Sahrens */ 3392789Sahrens int 33932474Seschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 33942474Seschrock zfs_iter_f func, void *data) 3395789Sahrens { 3396789Sahrens char **dependents; 3397789Sahrens size_t count; 3398789Sahrens int i; 3399789Sahrens zfs_handle_t *child; 3400789Sahrens int ret = 0; 3401789Sahrens 34022474Seschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34032474Seschrock &dependents, &count) != 0) 34042474Seschrock return (-1); 34052474Seschrock 3406789Sahrens for (i = 0; i < count; i++) { 34072082Seschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 34082082Seschrock dependents[i])) == NULL) 3409789Sahrens continue; 3410789Sahrens 3411789Sahrens if ((ret = func(child, data)) != 0) 3412789Sahrens break; 3413789Sahrens } 3414789Sahrens 3415789Sahrens for (i = 0; i < count; i++) 3416789Sahrens free(dependents[i]); 3417789Sahrens free(dependents); 3418789Sahrens 3419789Sahrens return (ret); 3420789Sahrens } 3421789Sahrens 3422789Sahrens /* 3423789Sahrens * Renames the given dataset. 3424789Sahrens */ 3425789Sahrens int 34264490Svb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3427789Sahrens { 3428789Sahrens int ret; 3429789Sahrens zfs_cmd_t zc = { 0 }; 3430789Sahrens char *delim; 34314007Smmusante prop_changelist_t *cl = NULL; 34324007Smmusante zfs_handle_t *zhrp = NULL; 34334007Smmusante char *parentname = NULL; 3434789Sahrens char parent[ZFS_MAXNAMELEN]; 34352082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 34362082Seschrock char errbuf[1024]; 3437789Sahrens 3438789Sahrens /* if we have the same exact name, just return success */ 3439789Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3440789Sahrens return (0); 3441789Sahrens 34422082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 34432082Seschrock "cannot rename to '%s'"), target); 34442082Seschrock 3445789Sahrens /* 3446789Sahrens * Make sure the target name is valid 3447789Sahrens */ 3448789Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 34492665Snd150628 if ((strchr(target, '@') == NULL) || 34502665Snd150628 *target == '@') { 34512665Snd150628 /* 34522665Snd150628 * Snapshot target name is abbreviated, 34532665Snd150628 * reconstruct full dataset name 34542665Snd150628 */ 34552665Snd150628 (void) strlcpy(parent, zhp->zfs_name, 34562665Snd150628 sizeof (parent)); 34572665Snd150628 delim = strchr(parent, '@'); 34582665Snd150628 if (strchr(target, '@') == NULL) 34592665Snd150628 *(++delim) = '\0'; 34602665Snd150628 else 34612665Snd150628 *delim = '\0'; 34622665Snd150628 (void) strlcat(parent, target, sizeof (parent)); 34632665Snd150628 target = parent; 34642665Snd150628 } else { 34652665Snd150628 /* 34662665Snd150628 * Make sure we're renaming within the same dataset. 34672665Snd150628 */ 34682665Snd150628 delim = strchr(target, '@'); 34692665Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 34702665Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 34712665Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34722665Snd150628 "snapshots must be part of same " 34732665Snd150628 "dataset")); 34742665Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 34753912Slling errbuf)); 34762665Snd150628 } 3477789Sahrens } 34785326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 34792665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3480789Sahrens } else { 34814007Smmusante if (recursive) { 34824007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34834007Smmusante "recursive rename must be a snapshot")); 34844007Smmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 34854007Smmusante } 34864007Smmusante 34875326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 34882665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 34892676Seschrock 3490789Sahrens /* validate parents */ 349111497SMark.Musante@Sun.COM if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3492789Sahrens return (-1); 3493789Sahrens 3494789Sahrens /* make sure we're in the same pool */ 3495789Sahrens verify((delim = strchr(target, '/')) != NULL); 3496789Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3497789Sahrens zhp->zfs_name[delim - target] != '/') { 34982082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34992082Seschrock "datasets must be within same pool")); 35002082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3501789Sahrens } 35022440Snd150628 35032440Snd150628 /* new name cannot be a child of the current dataset name */ 350411497SMark.Musante@Sun.COM if (is_descendant(zhp->zfs_name, target)) { 35052440Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 350611497SMark.Musante@Sun.COM "New dataset name cannot be a descendant of " 35072440Snd150628 "current dataset name")); 35082440Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 35092440Snd150628 } 3510789Sahrens } 3511789Sahrens 35122082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 35132082Seschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 35142082Seschrock 3515789Sahrens if (getzoneid() == GLOBAL_ZONEID && 3516789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 35172082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35182082Seschrock "dataset is used in a non-global zone")); 35192082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3520789Sahrens } 3521789Sahrens 35224007Smmusante if (recursive) { 35234007Smmusante 35244183Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 35254183Smmusante if (parentname == NULL) { 35264183Smmusante ret = -1; 35274183Smmusante goto error; 35284183Smmusante } 35294007Smmusante delim = strchr(parentname, '@'); 35304007Smmusante *delim = '\0'; 35315094Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 35324007Smmusante if (zhrp == NULL) { 35334183Smmusante ret = -1; 35344183Smmusante goto error; 35354007Smmusante } 35364007Smmusante 35374007Smmusante } else { 35387366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 35394007Smmusante return (-1); 35404007Smmusante 35414007Smmusante if (changelist_haszonedchild(cl)) { 35424007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35434007Smmusante "child dataset with inherited mountpoint is used " 35444007Smmusante "in a non-global zone")); 35454007Smmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 35464007Smmusante goto error; 35474007Smmusante } 35484007Smmusante 35494007Smmusante if ((ret = changelist_prefix(cl)) != 0) 35504007Smmusante goto error; 3551789Sahrens } 3552789Sahrens 35532676Seschrock if (ZFS_IS_VOLUME(zhp)) 3554789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3555789Sahrens else 3556789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3557789Sahrens 35582665Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35592676Seschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 35602665Snd150628 35614007Smmusante zc.zc_cookie = recursive; 35624007Smmusante 35634543Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 35644007Smmusante /* 35654007Smmusante * if it was recursive, the one that actually failed will 35664007Smmusante * be in zc.zc_name 35674007Smmusante */ 35684007Smmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35695367Sahrens "cannot rename '%s'"), zc.zc_name); 35704007Smmusante 35714007Smmusante if (recursive && errno == EEXIST) { 35724007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35734007Smmusante "a child dataset already has a snapshot " 35744007Smmusante "with the new name")); 35754801Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 35764007Smmusante } else { 35774007Smmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 35784007Smmusante } 3579789Sahrens 3580789Sahrens /* 3581789Sahrens * On failure, we still want to remount any filesystems that 3582789Sahrens * were previously mounted, so we don't alter the system state. 3583789Sahrens */ 358410588SEric.Taylor@Sun.COM if (!recursive) 35854007Smmusante (void) changelist_postfix(cl); 3586789Sahrens } else { 358710588SEric.Taylor@Sun.COM if (!recursive) { 35884007Smmusante changelist_rename(cl, zfs_get_name(zhp), target); 35894007Smmusante ret = changelist_postfix(cl); 35904007Smmusante } 3591789Sahrens } 3592789Sahrens 3593789Sahrens error: 35944007Smmusante if (parentname) { 35954007Smmusante free(parentname); 35964007Smmusante } 35974007Smmusante if (zhrp) { 35984007Smmusante zfs_close(zhrp); 35994007Smmusante } 36004007Smmusante if (cl) { 36014007Smmusante changelist_free(cl); 36024007Smmusante } 3603789Sahrens return (ret); 3604789Sahrens } 3605789Sahrens 36062676Seschrock nvlist_t * 36072676Seschrock zfs_get_user_props(zfs_handle_t *zhp) 36082676Seschrock { 36092676Seschrock return (zhp->zfs_user_props); 36102676Seschrock } 36112676Seschrock 361211022STom.Erickson@Sun.COM nvlist_t * 361311022STom.Erickson@Sun.COM zfs_get_recvd_props(zfs_handle_t *zhp) 361411022STom.Erickson@Sun.COM { 361511022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 361611022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 361711022STom.Erickson@Sun.COM return (NULL); 361811022STom.Erickson@Sun.COM return (zhp->zfs_recvd_props); 361911022STom.Erickson@Sun.COM } 362011022STom.Erickson@Sun.COM 36212676Seschrock /* 36223912Slling * This function is used by 'zfs list' to determine the exact set of columns to 36233912Slling * display, and their maximum widths. This does two main things: 36243912Slling * 36253912Slling * - If this is a list of all properties, then expand the list to include 36263912Slling * all native properties, and set a flag so that for each dataset we look 36273912Slling * for new unique user properties and add them to the list. 36283912Slling * 36293912Slling * - For non fixed-width properties, keep track of the maximum width seen 363011022STom.Erickson@Sun.COM * so that we can size the column appropriately. If the user has 363111022STom.Erickson@Sun.COM * requested received property values, we also need to compute the width 363211022STom.Erickson@Sun.COM * of the RECEIVED column. 36333912Slling */ 36343912Slling int 363511022STom.Erickson@Sun.COM zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 36363912Slling { 36373912Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 36385094Slling zprop_list_t *entry; 36395094Slling zprop_list_t **last, **start; 36403912Slling nvlist_t *userprops, *propval; 36413912Slling nvpair_t *elem; 36423912Slling char *strval; 36433912Slling char buf[ZFS_MAXPROPLEN]; 36443912Slling 36455094Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 36463912Slling return (-1); 36472676Seschrock 36482676Seschrock userprops = zfs_get_user_props(zhp); 36492676Seschrock 36502676Seschrock entry = *plp; 36512676Seschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 36522676Seschrock /* 36532676Seschrock * Go through and add any user properties as necessary. We 36542676Seschrock * start by incrementing our list pointer to the first 36552676Seschrock * non-native property. 36562676Seschrock */ 36572676Seschrock start = plp; 36582676Seschrock while (*start != NULL) { 36595094Slling if ((*start)->pl_prop == ZPROP_INVAL) 36602676Seschrock break; 36612676Seschrock start = &(*start)->pl_next; 36622676Seschrock } 36632676Seschrock 36642676Seschrock elem = NULL; 36652676Seschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 36662676Seschrock /* 36672676Seschrock * See if we've already found this property in our list. 36682676Seschrock */ 36692676Seschrock for (last = start; *last != NULL; 36702676Seschrock last = &(*last)->pl_next) { 36712676Seschrock if (strcmp((*last)->pl_user_prop, 36722676Seschrock nvpair_name(elem)) == 0) 36732676Seschrock break; 36742676Seschrock } 36752676Seschrock 36762676Seschrock if (*last == NULL) { 36772676Seschrock if ((entry = zfs_alloc(hdl, 36785094Slling sizeof (zprop_list_t))) == NULL || 36792676Seschrock ((entry->pl_user_prop = zfs_strdup(hdl, 36802676Seschrock nvpair_name(elem)))) == NULL) { 36812676Seschrock free(entry); 36822676Seschrock return (-1); 36832676Seschrock } 36842676Seschrock 36855094Slling entry->pl_prop = ZPROP_INVAL; 36862676Seschrock entry->pl_width = strlen(nvpair_name(elem)); 36872676Seschrock entry->pl_all = B_TRUE; 36882676Seschrock *last = entry; 36892676Seschrock } 36902676Seschrock } 36912676Seschrock } 36922676Seschrock 36932676Seschrock /* 36942676Seschrock * Now go through and check the width of any non-fixed columns 36952676Seschrock */ 36962676Seschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 36972676Seschrock if (entry->pl_fixed) 36982676Seschrock continue; 36992676Seschrock 37005094Slling if (entry->pl_prop != ZPROP_INVAL) { 37012676Seschrock if (zfs_prop_get(zhp, entry->pl_prop, 37022676Seschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 37032676Seschrock if (strlen(buf) > entry->pl_width) 37042676Seschrock entry->pl_width = strlen(buf); 37052676Seschrock } 370611022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 370711022STom.Erickson@Sun.COM zfs_prop_to_name(entry->pl_prop), 370811022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 370911022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 371011022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 371111022STom.Erickson@Sun.COM } else { 371211022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 371311022STom.Erickson@Sun.COM &propval) == 0) { 371411022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, 371511022STom.Erickson@Sun.COM ZPROP_VALUE, &strval) == 0); 371611022STom.Erickson@Sun.COM if (strlen(strval) > entry->pl_width) 371711022STom.Erickson@Sun.COM entry->pl_width = strlen(strval); 371811022STom.Erickson@Sun.COM } 371911022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 372011022STom.Erickson@Sun.COM entry->pl_user_prop, 372111022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 372211022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 372311022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 37242676Seschrock } 37252676Seschrock } 37262676Seschrock 37272676Seschrock return (0); 37282676Seschrock } 37294543Smarks 37304543Smarks int 37314543Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 37328845Samw@Sun.COM char *resource, void *export, void *sharetab, 37338845Samw@Sun.COM int sharemax, zfs_share_op_t operation) 37344543Smarks { 37354543Smarks zfs_cmd_t zc = { 0 }; 37364543Smarks int error; 37374543Smarks 37384543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37394543Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37408845Samw@Sun.COM if (resource) 37418845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 37424543Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 37434543Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 37445331Samw zc.zc_share.z_sharetype = operation; 37454543Smarks zc.zc_share.z_sharemax = sharemax; 37464543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 37474543Smarks return (error); 37484543Smarks } 37498802SSanjeev.Bagewadi@Sun.COM 37508802SSanjeev.Bagewadi@Sun.COM void 37518802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37528802SSanjeev.Bagewadi@Sun.COM { 37538802SSanjeev.Bagewadi@Sun.COM nvpair_t *curr; 37548802SSanjeev.Bagewadi@Sun.COM 37558802SSanjeev.Bagewadi@Sun.COM /* 37568802SSanjeev.Bagewadi@Sun.COM * Keep a reference to the props-table against which we prune the 37578802SSanjeev.Bagewadi@Sun.COM * properties. 37588802SSanjeev.Bagewadi@Sun.COM */ 37598802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table = props; 37608802SSanjeev.Bagewadi@Sun.COM 37618802SSanjeev.Bagewadi@Sun.COM curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37628802SSanjeev.Bagewadi@Sun.COM 37638802SSanjeev.Bagewadi@Sun.COM while (curr) { 37648802SSanjeev.Bagewadi@Sun.COM zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37658802SSanjeev.Bagewadi@Sun.COM nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37668802SSanjeev.Bagewadi@Sun.COM 37679396SMatthew.Ahrens@Sun.COM /* 376810960SEric.Schrock@Sun.COM * User properties will result in ZPROP_INVAL, and since we 376910960SEric.Schrock@Sun.COM * only know how to prune standard ZFS properties, we always 377010960SEric.Schrock@Sun.COM * leave these in the list. This can also happen if we 377110960SEric.Schrock@Sun.COM * encounter an unknown DSL property (when running older 377210960SEric.Schrock@Sun.COM * software, for example). 37739396SMatthew.Ahrens@Sun.COM */ 37749396SMatthew.Ahrens@Sun.COM if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 37758802SSanjeev.Bagewadi@Sun.COM (void) nvlist_remove(zhp->zfs_props, 37768802SSanjeev.Bagewadi@Sun.COM nvpair_name(curr), nvpair_type(curr)); 37778802SSanjeev.Bagewadi@Sun.COM curr = next; 37788802SSanjeev.Bagewadi@Sun.COM } 37798802SSanjeev.Bagewadi@Sun.COM } 37808845Samw@Sun.COM 37818845Samw@Sun.COM static int 37828845Samw@Sun.COM zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 37838845Samw@Sun.COM zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 37848845Samw@Sun.COM { 37858845Samw@Sun.COM zfs_cmd_t zc = { 0 }; 37868845Samw@Sun.COM nvlist_t *nvlist = NULL; 37878845Samw@Sun.COM int error; 37888845Samw@Sun.COM 37898845Samw@Sun.COM (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37908845Samw@Sun.COM (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37918845Samw@Sun.COM zc.zc_cookie = (uint64_t)cmd; 37928845Samw@Sun.COM 37938845Samw@Sun.COM if (cmd == ZFS_SMB_ACL_RENAME) { 37948845Samw@Sun.COM if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 37958845Samw@Sun.COM (void) no_memory(hdl); 37968845Samw@Sun.COM return (NULL); 37978845Samw@Sun.COM } 37988845Samw@Sun.COM } 37998845Samw@Sun.COM 38008845Samw@Sun.COM switch (cmd) { 38018845Samw@Sun.COM case ZFS_SMB_ACL_ADD: 38028845Samw@Sun.COM case ZFS_SMB_ACL_REMOVE: 38038845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 38048845Samw@Sun.COM break; 38058845Samw@Sun.COM case ZFS_SMB_ACL_RENAME: 38068845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 38078845Samw@Sun.COM resource1) != 0) { 38088845Samw@Sun.COM (void) no_memory(hdl); 38098845Samw@Sun.COM return (-1); 38108845Samw@Sun.COM } 38118845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 38128845Samw@Sun.COM resource2) != 0) { 38138845Samw@Sun.COM (void) no_memory(hdl); 38148845Samw@Sun.COM return (-1); 38158845Samw@Sun.COM } 38168845Samw@Sun.COM if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 38178845Samw@Sun.COM nvlist_free(nvlist); 38188845Samw@Sun.COM return (-1); 38198845Samw@Sun.COM } 38208845Samw@Sun.COM break; 38218845Samw@Sun.COM case ZFS_SMB_ACL_PURGE: 38228845Samw@Sun.COM break; 38238845Samw@Sun.COM default: 38248845Samw@Sun.COM return (-1); 38258845Samw@Sun.COM } 38268845Samw@Sun.COM error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 38278845Samw@Sun.COM if (nvlist) 38288845Samw@Sun.COM nvlist_free(nvlist); 38298845Samw@Sun.COM return (error); 38308845Samw@Sun.COM } 38318845Samw@Sun.COM 38328845Samw@Sun.COM int 38338845Samw@Sun.COM zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 38348845Samw@Sun.COM char *path, char *resource) 38358845Samw@Sun.COM { 38368845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 38378845Samw@Sun.COM resource, NULL)); 38388845Samw@Sun.COM } 38398845Samw@Sun.COM 38408845Samw@Sun.COM int 38418845Samw@Sun.COM zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 38428845Samw@Sun.COM char *path, char *resource) 38438845Samw@Sun.COM { 38448845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 38458845Samw@Sun.COM resource, NULL)); 38468845Samw@Sun.COM } 38478845Samw@Sun.COM 38488845Samw@Sun.COM int 38498845Samw@Sun.COM zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 38508845Samw@Sun.COM { 38518845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 38528845Samw@Sun.COM NULL, NULL)); 38538845Samw@Sun.COM } 38548845Samw@Sun.COM 38558845Samw@Sun.COM int 38568845Samw@Sun.COM zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 38578845Samw@Sun.COM char *oldname, char *newname) 38588845Samw@Sun.COM { 38598845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 38608845Samw@Sun.COM oldname, newname)); 38618845Samw@Sun.COM } 38629396SMatthew.Ahrens@Sun.COM 38639396SMatthew.Ahrens@Sun.COM int 38649396SMatthew.Ahrens@Sun.COM zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 38659396SMatthew.Ahrens@Sun.COM zfs_userspace_cb_t func, void *arg) 38669396SMatthew.Ahrens@Sun.COM { 38679396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 38689396SMatthew.Ahrens@Sun.COM int error; 38699396SMatthew.Ahrens@Sun.COM zfs_useracct_t buf[100]; 38709396SMatthew.Ahrens@Sun.COM 38719396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 38729396SMatthew.Ahrens@Sun.COM 38739396SMatthew.Ahrens@Sun.COM zc.zc_objset_type = type; 38749396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst = (uintptr_t)buf; 38759396SMatthew.Ahrens@Sun.COM 38769396SMatthew.Ahrens@Sun.COM /* CONSTCOND */ 38779396SMatthew.Ahrens@Sun.COM while (1) { 38789396SMatthew.Ahrens@Sun.COM zfs_useracct_t *zua = buf; 38799396SMatthew.Ahrens@Sun.COM 38809396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size = sizeof (buf); 38819396SMatthew.Ahrens@Sun.COM error = ioctl(zhp->zfs_hdl->libzfs_fd, 38829396SMatthew.Ahrens@Sun.COM ZFS_IOC_USERSPACE_MANY, &zc); 38839396SMatthew.Ahrens@Sun.COM if (error || zc.zc_nvlist_dst_size == 0) 38849396SMatthew.Ahrens@Sun.COM break; 38859396SMatthew.Ahrens@Sun.COM 38869396SMatthew.Ahrens@Sun.COM while (zc.zc_nvlist_dst_size > 0) { 38879554SMatthew.Ahrens@Sun.COM error = func(arg, zua->zu_domain, zua->zu_rid, 38889554SMatthew.Ahrens@Sun.COM zua->zu_space); 38899554SMatthew.Ahrens@Sun.COM if (error != 0) 38909554SMatthew.Ahrens@Sun.COM return (error); 38919396SMatthew.Ahrens@Sun.COM zua++; 38929396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 38939396SMatthew.Ahrens@Sun.COM } 38949396SMatthew.Ahrens@Sun.COM } 38959396SMatthew.Ahrens@Sun.COM 38969396SMatthew.Ahrens@Sun.COM return (error); 38979396SMatthew.Ahrens@Sun.COM } 389810242Schris.kirby@sun.com 389910242Schris.kirby@sun.com int 390010242Schris.kirby@sun.com zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 390111417SChris.Kirby@sun.com boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 390210242Schris.kirby@sun.com { 390310242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 390410242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 390510242Schris.kirby@sun.com 390610242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 390710242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 390810342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 390910342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 391010342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 391110242Schris.kirby@sun.com zc.zc_cookie = recursive; 391210342Schris.kirby@sun.com zc.zc_temphold = temphold; 391310242Schris.kirby@sun.com 391410242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 391510242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 391610242Schris.kirby@sun.com 391710242Schris.kirby@sun.com /* 391810242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 391910242Schris.kirby@sun.com * zc.zc_name. 392010242Schris.kirby@sun.com */ 392110242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 392210242Schris.kirby@sun.com "cannot hold '%s@%s'"), zc.zc_name, snapname); 392310242Schris.kirby@sun.com switch (errno) { 392410951SChris.Kirby@sun.com case E2BIG: 392510951SChris.Kirby@sun.com /* 392610951SChris.Kirby@sun.com * Temporary tags wind up having the ds object id 392710951SChris.Kirby@sun.com * prepended. So even if we passed the length check 392810951SChris.Kirby@sun.com * above, it's still possible for the tag to wind 392910951SChris.Kirby@sun.com * up being slightly too long. 393010951SChris.Kirby@sun.com */ 393110951SChris.Kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 393210242Schris.kirby@sun.com case ENOTSUP: 393310242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 393410242Schris.kirby@sun.com "pool must be upgraded")); 393510242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 393610242Schris.kirby@sun.com case EINVAL: 393710242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 393810242Schris.kirby@sun.com case EEXIST: 393910242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 394011417SChris.Kirby@sun.com case ENOENT: 394111417SChris.Kirby@sun.com if (enoent_ok) 394211417SChris.Kirby@sun.com return (0); 394311417SChris.Kirby@sun.com /* FALLTHROUGH */ 394410242Schris.kirby@sun.com default: 394510242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 394610242Schris.kirby@sun.com } 394710242Schris.kirby@sun.com } 394810242Schris.kirby@sun.com 394910242Schris.kirby@sun.com return (0); 395010242Schris.kirby@sun.com } 395110242Schris.kirby@sun.com 395210342Schris.kirby@sun.com struct hold_range_arg { 395310342Schris.kirby@sun.com zfs_handle_t *origin; 395410342Schris.kirby@sun.com const char *fromsnap; 395510342Schris.kirby@sun.com const char *tosnap; 395610342Schris.kirby@sun.com char lastsnapheld[ZFS_MAXNAMELEN]; 395710342Schris.kirby@sun.com const char *tag; 395810342Schris.kirby@sun.com boolean_t temphold; 395910342Schris.kirby@sun.com boolean_t seento; 396010342Schris.kirby@sun.com boolean_t seenfrom; 396110342Schris.kirby@sun.com boolean_t holding; 396211546SChris.Kirby@sun.com boolean_t recursive; 396311814SChris.Kirby@sun.com snapfilter_cb_t *filter_cb; 396411814SChris.Kirby@sun.com void *filter_cb_arg; 396510342Schris.kirby@sun.com }; 396610342Schris.kirby@sun.com 396710342Schris.kirby@sun.com static int 396810342Schris.kirby@sun.com zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 396910342Schris.kirby@sun.com { 397010342Schris.kirby@sun.com struct hold_range_arg *hra = arg; 397110342Schris.kirby@sun.com const char *thissnap; 397210342Schris.kirby@sun.com int error; 397310342Schris.kirby@sun.com 397410342Schris.kirby@sun.com thissnap = strchr(zfs_get_name(zhp), '@') + 1; 397510342Schris.kirby@sun.com 397610342Schris.kirby@sun.com if (hra->fromsnap && !hra->seenfrom && 397710342Schris.kirby@sun.com strcmp(hra->fromsnap, thissnap) == 0) 397810342Schris.kirby@sun.com hra->seenfrom = B_TRUE; 397910342Schris.kirby@sun.com 398010342Schris.kirby@sun.com /* snap is older or newer than the desired range, ignore it */ 398110342Schris.kirby@sun.com if (hra->seento || !hra->seenfrom) { 398210342Schris.kirby@sun.com zfs_close(zhp); 398310342Schris.kirby@sun.com return (0); 398410342Schris.kirby@sun.com } 398510342Schris.kirby@sun.com 398611814SChris.Kirby@sun.com if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 398711814SChris.Kirby@sun.com hra->seento = B_TRUE; 398811814SChris.Kirby@sun.com 398911814SChris.Kirby@sun.com if (hra->filter_cb != NULL && 399011814SChris.Kirby@sun.com hra->filter_cb(zhp, hra->filter_cb_arg) == B_FALSE) { 399111814SChris.Kirby@sun.com zfs_close(zhp); 399211814SChris.Kirby@sun.com return (0); 399311814SChris.Kirby@sun.com } 399411814SChris.Kirby@sun.com 399510342Schris.kirby@sun.com if (hra->holding) { 399611417SChris.Kirby@sun.com /* We could be racing with destroy, so ignore ENOENT. */ 399711546SChris.Kirby@sun.com error = zfs_hold(hra->origin, thissnap, hra->tag, 399811546SChris.Kirby@sun.com hra->recursive, hra->temphold, B_TRUE); 399910342Schris.kirby@sun.com if (error == 0) { 400010342Schris.kirby@sun.com (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 400110342Schris.kirby@sun.com sizeof (hra->lastsnapheld)); 400210342Schris.kirby@sun.com } 400310342Schris.kirby@sun.com } else { 400411546SChris.Kirby@sun.com error = zfs_release(hra->origin, thissnap, hra->tag, 400511546SChris.Kirby@sun.com hra->recursive); 400610342Schris.kirby@sun.com } 400710342Schris.kirby@sun.com 400810342Schris.kirby@sun.com zfs_close(zhp); 400910342Schris.kirby@sun.com return (error); 401010342Schris.kirby@sun.com } 401110342Schris.kirby@sun.com 401210342Schris.kirby@sun.com /* 401310342Schris.kirby@sun.com * Add a user hold on the set of snapshots starting with fromsnap up to 401410342Schris.kirby@sun.com * and including tosnap. If we're unable to to acquire a particular hold, 401510342Schris.kirby@sun.com * undo any holds up to that point. 401610342Schris.kirby@sun.com */ 401710342Schris.kirby@sun.com int 401810342Schris.kirby@sun.com zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 401911814SChris.Kirby@sun.com const char *tag, boolean_t recursive, boolean_t temphold, 402011814SChris.Kirby@sun.com snapfilter_cb_t filter_cb, void *cbarg) 402110342Schris.kirby@sun.com { 402210342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 402310342Schris.kirby@sun.com int error; 402410342Schris.kirby@sun.com 402510342Schris.kirby@sun.com arg.origin = zhp; 402610342Schris.kirby@sun.com arg.fromsnap = fromsnap; 402710342Schris.kirby@sun.com arg.tosnap = tosnap; 402810342Schris.kirby@sun.com arg.tag = tag; 402910342Schris.kirby@sun.com arg.temphold = temphold; 403010342Schris.kirby@sun.com arg.holding = B_TRUE; 403111546SChris.Kirby@sun.com arg.recursive = recursive; 403211668SChris.Kirby@sun.com arg.seenfrom = (fromsnap == NULL); 403311814SChris.Kirby@sun.com arg.filter_cb = filter_cb; 403411814SChris.Kirby@sun.com arg.filter_cb_arg = cbarg; 403510342Schris.kirby@sun.com 403610342Schris.kirby@sun.com error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 403710342Schris.kirby@sun.com 403810342Schris.kirby@sun.com /* 403910342Schris.kirby@sun.com * Make sure we either hold the entire range or none. 404010342Schris.kirby@sun.com */ 404110342Schris.kirby@sun.com if (error && arg.lastsnapheld[0] != '\0') { 404210342Schris.kirby@sun.com (void) zfs_release_range(zhp, fromsnap, 404311546SChris.Kirby@sun.com (const char *)arg.lastsnapheld, tag, recursive); 404410342Schris.kirby@sun.com } 404510342Schris.kirby@sun.com return (error); 404610342Schris.kirby@sun.com } 404710342Schris.kirby@sun.com 404810242Schris.kirby@sun.com int 404910242Schris.kirby@sun.com zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 405010242Schris.kirby@sun.com boolean_t recursive) 405110242Schris.kirby@sun.com { 405210242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 405310242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 405410242Schris.kirby@sun.com 405510242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 405610242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 405710342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 405810342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 405910342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 406010242Schris.kirby@sun.com zc.zc_cookie = recursive; 406110242Schris.kirby@sun.com 406210242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 406310242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 406410242Schris.kirby@sun.com 406510242Schris.kirby@sun.com /* 406610242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 406710242Schris.kirby@sun.com * zc.zc_name. 406810242Schris.kirby@sun.com */ 406910242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 407011546SChris.Kirby@sun.com "cannot release '%s' from '%s@%s'"), tag, zc.zc_name, 407111546SChris.Kirby@sun.com snapname); 407210242Schris.kirby@sun.com switch (errno) { 407310242Schris.kirby@sun.com case ESRCH: 407410242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 407510242Schris.kirby@sun.com case ENOTSUP: 407610242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 407710242Schris.kirby@sun.com "pool must be upgraded")); 407810242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 407910242Schris.kirby@sun.com case EINVAL: 408010242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 408110242Schris.kirby@sun.com default: 408210242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 408310242Schris.kirby@sun.com } 408410242Schris.kirby@sun.com } 408510242Schris.kirby@sun.com 408610242Schris.kirby@sun.com return (0); 408710242Schris.kirby@sun.com } 408810342Schris.kirby@sun.com 408910342Schris.kirby@sun.com /* 409010342Schris.kirby@sun.com * Release a user hold from the set of snapshots starting with fromsnap 409110342Schris.kirby@sun.com * up to and including tosnap. 409210342Schris.kirby@sun.com */ 409310342Schris.kirby@sun.com int 409410342Schris.kirby@sun.com zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 409511546SChris.Kirby@sun.com const char *tag, boolean_t recursive) 409610342Schris.kirby@sun.com { 409710342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 409810342Schris.kirby@sun.com 409910342Schris.kirby@sun.com arg.origin = zhp; 410010342Schris.kirby@sun.com arg.fromsnap = fromsnap; 410110342Schris.kirby@sun.com arg.tosnap = tosnap; 410210342Schris.kirby@sun.com arg.tag = tag; 410311546SChris.Kirby@sun.com arg.recursive = recursive; 410411668SChris.Kirby@sun.com arg.seenfrom = (fromsnap == NULL); 410510342Schris.kirby@sun.com 410610342Schris.kirby@sun.com return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 410710342Schris.kirby@sun.com } 410811449SEric.Taylor@Sun.COM 410911449SEric.Taylor@Sun.COM uint64_t 411011449SEric.Taylor@Sun.COM zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 411111449SEric.Taylor@Sun.COM { 411211449SEric.Taylor@Sun.COM uint64_t numdb; 411311449SEric.Taylor@Sun.COM uint64_t nblocks, volblocksize; 411411449SEric.Taylor@Sun.COM int ncopies; 411511449SEric.Taylor@Sun.COM char *strval; 411611449SEric.Taylor@Sun.COM 411711449SEric.Taylor@Sun.COM if (nvlist_lookup_string(props, 411811449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 411911449SEric.Taylor@Sun.COM ncopies = atoi(strval); 412011449SEric.Taylor@Sun.COM else 412111449SEric.Taylor@Sun.COM ncopies = 1; 412211449SEric.Taylor@Sun.COM if (nvlist_lookup_uint64(props, 412311449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 412411449SEric.Taylor@Sun.COM &volblocksize) != 0) 412511449SEric.Taylor@Sun.COM volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 412611449SEric.Taylor@Sun.COM nblocks = volsize/volblocksize; 412711449SEric.Taylor@Sun.COM /* start with metadnode L0-L6 */ 412811449SEric.Taylor@Sun.COM numdb = 7; 412911449SEric.Taylor@Sun.COM /* calculate number of indirects */ 413011449SEric.Taylor@Sun.COM while (nblocks > 1) { 413111449SEric.Taylor@Sun.COM nblocks += DNODES_PER_LEVEL - 1; 413211449SEric.Taylor@Sun.COM nblocks /= DNODES_PER_LEVEL; 413311449SEric.Taylor@Sun.COM numdb += nblocks; 413411449SEric.Taylor@Sun.COM } 413511449SEric.Taylor@Sun.COM numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 413611449SEric.Taylor@Sun.COM volsize *= ncopies; 413711449SEric.Taylor@Sun.COM /* 413811449SEric.Taylor@Sun.COM * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 413911449SEric.Taylor@Sun.COM * compressed, but in practice they compress down to about 414011449SEric.Taylor@Sun.COM * 1100 bytes 414111449SEric.Taylor@Sun.COM */ 414211449SEric.Taylor@Sun.COM numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 414311449SEric.Taylor@Sun.COM volsize += numdb; 414411449SEric.Taylor@Sun.COM return (volsize); 414511449SEric.Taylor@Sun.COM } 4146