1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 213126Sahl 22789Sahrens /* 2311417SChris.Kirby@sun.com * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #include <ctype.h> 28789Sahrens #include <errno.h> 29789Sahrens #include <libintl.h> 30789Sahrens #include <math.h> 31789Sahrens #include <stdio.h> 32789Sahrens #include <stdlib.h> 33789Sahrens #include <strings.h> 34789Sahrens #include <unistd.h> 355367Sahrens #include <stddef.h> 36789Sahrens #include <zone.h> 372082Seschrock #include <fcntl.h> 38789Sahrens #include <sys/mntent.h> 391294Slling #include <sys/mount.h> 404543Smarks #include <priv.h> 414543Smarks #include <pwd.h> 424543Smarks #include <grp.h> 434543Smarks #include <stddef.h> 444543Smarks #include <ucred.h> 459396SMatthew.Ahrens@Sun.COM #include <idmap.h> 469396SMatthew.Ahrens@Sun.COM #include <aclutils.h> 4710160SMatthew.Ahrens@Sun.COM #include <directory.h> 48789Sahrens 4911449SEric.Taylor@Sun.COM #include <sys/dnode.h> 50789Sahrens #include <sys/spa.h> 512676Seschrock #include <sys/zap.h> 52789Sahrens #include <libzfs.h> 53789Sahrens 54789Sahrens #include "zfs_namecheck.h" 55789Sahrens #include "zfs_prop.h" 56789Sahrens #include "libzfs_impl.h" 574543Smarks #include "zfs_deleg.h" 58789Sahrens 599396SMatthew.Ahrens@Sun.COM static int userquota_propname_decode(const char *propname, boolean_t zoned, 609396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 614007Smmusante 62789Sahrens /* 63789Sahrens * Given a single type (not a mask of types), return the type in a human 64789Sahrens * readable form. 65789Sahrens */ 66789Sahrens const char * 67789Sahrens zfs_type_to_name(zfs_type_t type) 68789Sahrens { 69789Sahrens switch (type) { 70789Sahrens case ZFS_TYPE_FILESYSTEM: 71789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 72789Sahrens case ZFS_TYPE_SNAPSHOT: 73789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 74789Sahrens case ZFS_TYPE_VOLUME: 75789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 76789Sahrens } 77789Sahrens 78789Sahrens return (NULL); 79789Sahrens } 80789Sahrens 81789Sahrens /* 82789Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 83789Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 84789Sahrens * We guess what the type would have been based on the path and the mask of 85789Sahrens * acceptable types. 86789Sahrens */ 87789Sahrens static const char * 88789Sahrens path_to_str(const char *path, int types) 89789Sahrens { 90789Sahrens /* 91789Sahrens * When given a single type, always report the exact type. 92789Sahrens */ 93789Sahrens if (types == ZFS_TYPE_SNAPSHOT) 94789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 95789Sahrens if (types == ZFS_TYPE_FILESYSTEM) 96789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 97789Sahrens if (types == ZFS_TYPE_VOLUME) 98789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 99789Sahrens 100789Sahrens /* 101789Sahrens * The user is requesting more than one type of dataset. If this is the 102789Sahrens * case, consult the path itself. If we're looking for a snapshot, and 103789Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 104789Sahrens * snapshot attribute and try again. 105789Sahrens */ 106789Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 107789Sahrens if (strchr(path, '@') != NULL) 108789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 109789Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 110789Sahrens } 111789Sahrens 112789Sahrens /* 113789Sahrens * The user has requested either filesystems or volumes. 114789Sahrens * We have no way of knowing a priori what type this would be, so always 115789Sahrens * report it as "filesystem" or "volume", our two primitive types. 116789Sahrens */ 117789Sahrens if (types & ZFS_TYPE_FILESYSTEM) 118789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 119789Sahrens 120789Sahrens assert(types & ZFS_TYPE_VOLUME); 121789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 122789Sahrens } 123789Sahrens 124789Sahrens /* 125789Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 1269396SMatthew.Ahrens@Sun.COM * provide a more meaningful error message. We call zfs_error_aux() to 1279396SMatthew.Ahrens@Sun.COM * explain exactly why the name was not valid. 128789Sahrens */ 129789Sahrens static int 1305326Sek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 1315326Sek110237 boolean_t modifying) 132789Sahrens { 133789Sahrens namecheck_err_t why; 134789Sahrens char what; 135789Sahrens 136789Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 1372082Seschrock if (hdl != NULL) { 138789Sahrens switch (why) { 1391003Slling case NAME_ERR_TOOLONG: 1402082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1412082Seschrock "name is too long")); 1421003Slling break; 1431003Slling 144789Sahrens case NAME_ERR_LEADING_SLASH: 1452082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1462082Seschrock "leading slash in name")); 147789Sahrens break; 148789Sahrens 149789Sahrens case NAME_ERR_EMPTY_COMPONENT: 1502082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1512082Seschrock "empty component in name")); 152789Sahrens break; 153789Sahrens 154789Sahrens case NAME_ERR_TRAILING_SLASH: 1552082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1562082Seschrock "trailing slash in name")); 157789Sahrens break; 158789Sahrens 159789Sahrens case NAME_ERR_INVALCHAR: 1602082Seschrock zfs_error_aux(hdl, 161789Sahrens dgettext(TEXT_DOMAIN, "invalid character " 1622082Seschrock "'%c' in name"), what); 163789Sahrens break; 164789Sahrens 165789Sahrens case NAME_ERR_MULTIPLE_AT: 1662082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1672082Seschrock "multiple '@' delimiters in name")); 168789Sahrens break; 1692856Snd150628 1702856Snd150628 case NAME_ERR_NOLETTER: 1712856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1722856Snd150628 "pool doesn't begin with a letter")); 1732856Snd150628 break; 1742856Snd150628 1752856Snd150628 case NAME_ERR_RESERVED: 1762856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1772856Snd150628 "name is reserved")); 1782856Snd150628 break; 1792856Snd150628 1802856Snd150628 case NAME_ERR_DISKLIKE: 1812856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1822856Snd150628 "reserved disk name")); 1832856Snd150628 break; 184789Sahrens } 185789Sahrens } 186789Sahrens 187789Sahrens return (0); 188789Sahrens } 189789Sahrens 190789Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 1912082Seschrock if (hdl != NULL) 1922082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1932082Seschrock "snapshot delimiter '@' in filesystem name")); 194789Sahrens return (0); 195789Sahrens } 196789Sahrens 1972199Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 1982199Sahrens if (hdl != NULL) 1992199Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2003413Smmusante "missing '@' delimiter in snapshot name")); 2012199Sahrens return (0); 2022199Sahrens } 2032199Sahrens 2045326Sek110237 if (modifying && strchr(path, '%') != NULL) { 2055326Sek110237 if (hdl != NULL) 2065326Sek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2075326Sek110237 "invalid character %c in name"), '%'); 2085326Sek110237 return (0); 2095326Sek110237 } 2105326Sek110237 2112082Seschrock return (-1); 212789Sahrens } 213789Sahrens 214789Sahrens int 215789Sahrens zfs_name_valid(const char *name, zfs_type_t type) 216789Sahrens { 2176423Sgw25295 if (type == ZFS_TYPE_POOL) 2186423Sgw25295 return (zpool_name_valid(NULL, B_FALSE, name)); 2195326Sek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 220789Sahrens } 221789Sahrens 222789Sahrens /* 2232676Seschrock * This function takes the raw DSL properties, and filters out the user-defined 2242676Seschrock * properties into a separate nvlist. 2252676Seschrock */ 2264217Seschrock static nvlist_t * 2274217Seschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 2282676Seschrock { 2292676Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2302676Seschrock nvpair_t *elem; 2312676Seschrock nvlist_t *propval; 2324217Seschrock nvlist_t *nvl; 2334217Seschrock 2344217Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 2354217Seschrock (void) no_memory(hdl); 2364217Seschrock return (NULL); 2374217Seschrock } 2382676Seschrock 2392676Seschrock elem = NULL; 2404217Seschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 2412676Seschrock if (!zfs_prop_user(nvpair_name(elem))) 2422676Seschrock continue; 2432676Seschrock 2442676Seschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 2454217Seschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 2464217Seschrock nvlist_free(nvl); 2474217Seschrock (void) no_memory(hdl); 2484217Seschrock return (NULL); 2494217Seschrock } 2502676Seschrock } 2512676Seschrock 2524217Seschrock return (nvl); 2532676Seschrock } 2542676Seschrock 2556865Srm160521 static zpool_handle_t * 2566865Srm160521 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 2576865Srm160521 { 2586865Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 2596865Srm160521 zpool_handle_t *zph; 2606865Srm160521 2616865Srm160521 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 2626865Srm160521 if (hdl->libzfs_pool_handles != NULL) 2636865Srm160521 zph->zpool_next = hdl->libzfs_pool_handles; 2646865Srm160521 hdl->libzfs_pool_handles = zph; 2656865Srm160521 } 2666865Srm160521 return (zph); 2676865Srm160521 } 2686865Srm160521 2696865Srm160521 static zpool_handle_t * 2706865Srm160521 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 2716865Srm160521 { 2726865Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 2736865Srm160521 zpool_handle_t *zph = hdl->libzfs_pool_handles; 2746865Srm160521 2756865Srm160521 while ((zph != NULL) && 2766865Srm160521 (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 2776865Srm160521 zph = zph->zpool_next; 2786865Srm160521 return (zph); 2796865Srm160521 } 2806865Srm160521 2816865Srm160521 /* 2826865Srm160521 * Returns a handle to the pool that contains the provided dataset. 2836865Srm160521 * If a handle to that pool already exists then that handle is returned. 2846865Srm160521 * Otherwise, a new handle is created and added to the list of handles. 2856865Srm160521 */ 2866865Srm160521 static zpool_handle_t * 2876865Srm160521 zpool_handle(zfs_handle_t *zhp) 2886865Srm160521 { 2896865Srm160521 char *pool_name; 2906865Srm160521 int len; 2916865Srm160521 zpool_handle_t *zph; 2926865Srm160521 2936865Srm160521 len = strcspn(zhp->zfs_name, "/@") + 1; 2946865Srm160521 pool_name = zfs_alloc(zhp->zfs_hdl, len); 2956865Srm160521 (void) strlcpy(pool_name, zhp->zfs_name, len); 2966865Srm160521 2976865Srm160521 zph = zpool_find_handle(zhp, pool_name, len); 2986865Srm160521 if (zph == NULL) 2996865Srm160521 zph = zpool_add_handle(zhp, pool_name); 3006865Srm160521 3016865Srm160521 free(pool_name); 3026865Srm160521 return (zph); 3036865Srm160521 } 3046865Srm160521 3056865Srm160521 void 3066865Srm160521 zpool_free_handles(libzfs_handle_t *hdl) 3076865Srm160521 { 3086865Srm160521 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 3096865Srm160521 3106865Srm160521 while (zph != NULL) { 3116865Srm160521 next = zph->zpool_next; 3126865Srm160521 zpool_close(zph); 3136865Srm160521 zph = next; 3146865Srm160521 } 3156865Srm160521 hdl->libzfs_pool_handles = NULL; 3166865Srm160521 } 3176865Srm160521 3182676Seschrock /* 319789Sahrens * Utility function to gather stats (objset and zpl) for the given object. 320789Sahrens */ 321789Sahrens static int 3228228SEric.Taylor@Sun.COM get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 323789Sahrens { 3242676Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3258228SEric.Taylor@Sun.COM 3268228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 3278228SEric.Taylor@Sun.COM 3288228SEric.Taylor@Sun.COM while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3291356Seschrock if (errno == ENOMEM) { 3308228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 3312082Seschrock return (-1); 3322676Seschrock } 3331356Seschrock } else { 3341356Seschrock return (-1); 3351356Seschrock } 3361356Seschrock } 3378228SEric.Taylor@Sun.COM return (0); 3388228SEric.Taylor@Sun.COM } 3398228SEric.Taylor@Sun.COM 34011022STom.Erickson@Sun.COM /* 34111022STom.Erickson@Sun.COM * Utility function to get the received properties of the given object. 34211022STom.Erickson@Sun.COM */ 34311022STom.Erickson@Sun.COM static int 34411022STom.Erickson@Sun.COM get_recvd_props_ioctl(zfs_handle_t *zhp) 34511022STom.Erickson@Sun.COM { 34611022STom.Erickson@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 34711022STom.Erickson@Sun.COM nvlist_t *recvdprops; 34811022STom.Erickson@Sun.COM zfs_cmd_t zc = { 0 }; 34911022STom.Erickson@Sun.COM int err; 35011022STom.Erickson@Sun.COM 35111022STom.Erickson@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 35211022STom.Erickson@Sun.COM return (-1); 35311022STom.Erickson@Sun.COM 35411022STom.Erickson@Sun.COM (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35511022STom.Erickson@Sun.COM 35611022STom.Erickson@Sun.COM while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 35711022STom.Erickson@Sun.COM if (errno == ENOMEM) { 35811022STom.Erickson@Sun.COM if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 35911022STom.Erickson@Sun.COM return (-1); 36011022STom.Erickson@Sun.COM } 36111022STom.Erickson@Sun.COM } else { 36211022STom.Erickson@Sun.COM zcmd_free_nvlists(&zc); 36311022STom.Erickson@Sun.COM return (-1); 36411022STom.Erickson@Sun.COM } 36511022STom.Erickson@Sun.COM } 36611022STom.Erickson@Sun.COM 36711022STom.Erickson@Sun.COM err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 36811022STom.Erickson@Sun.COM zcmd_free_nvlists(&zc); 36911022STom.Erickson@Sun.COM if (err != 0) 37011022STom.Erickson@Sun.COM return (-1); 37111022STom.Erickson@Sun.COM 37211022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 37311022STom.Erickson@Sun.COM zhp->zfs_recvd_props = recvdprops; 37411022STom.Erickson@Sun.COM 37511022STom.Erickson@Sun.COM return (0); 37611022STom.Erickson@Sun.COM } 37711022STom.Erickson@Sun.COM 3788228SEric.Taylor@Sun.COM static int 3798228SEric.Taylor@Sun.COM put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 3808228SEric.Taylor@Sun.COM { 3818228SEric.Taylor@Sun.COM nvlist_t *allprops, *userprops; 3828228SEric.Taylor@Sun.COM 3838228SEric.Taylor@Sun.COM zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 3848228SEric.Taylor@Sun.COM 3858228SEric.Taylor@Sun.COM if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 3862082Seschrock return (-1); 3872082Seschrock } 388789Sahrens 3899396SMatthew.Ahrens@Sun.COM /* 3909396SMatthew.Ahrens@Sun.COM * XXX Why do we store the user props separately, in addition to 3919396SMatthew.Ahrens@Sun.COM * storing them in zfs_props? 3929396SMatthew.Ahrens@Sun.COM */ 3934217Seschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 3944217Seschrock nvlist_free(allprops); 3952676Seschrock return (-1); 3964217Seschrock } 3974217Seschrock 3984217Seschrock nvlist_free(zhp->zfs_props); 3994217Seschrock nvlist_free(zhp->zfs_user_props); 4004217Seschrock 4014217Seschrock zhp->zfs_props = allprops; 4024217Seschrock zhp->zfs_user_props = userprops; 4032082Seschrock 404789Sahrens return (0); 405789Sahrens } 406789Sahrens 4078228SEric.Taylor@Sun.COM static int 4088228SEric.Taylor@Sun.COM get_stats(zfs_handle_t *zhp) 4098228SEric.Taylor@Sun.COM { 4108228SEric.Taylor@Sun.COM int rc = 0; 4118228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4128228SEric.Taylor@Sun.COM 4138228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 4148228SEric.Taylor@Sun.COM return (-1); 4158228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) != 0) 4168228SEric.Taylor@Sun.COM rc = -1; 4178228SEric.Taylor@Sun.COM else if (put_stats_zhdl(zhp, &zc) != 0) 4188228SEric.Taylor@Sun.COM rc = -1; 4198228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4208228SEric.Taylor@Sun.COM return (rc); 4218228SEric.Taylor@Sun.COM } 4228228SEric.Taylor@Sun.COM 423789Sahrens /* 424789Sahrens * Refresh the properties currently stored in the handle. 425789Sahrens */ 426789Sahrens void 427789Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 428789Sahrens { 429789Sahrens (void) get_stats(zhp); 430789Sahrens } 431789Sahrens 432789Sahrens /* 433789Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 434789Sahrens * zfs_iter_* to create child handles on the fly. 435789Sahrens */ 4368228SEric.Taylor@Sun.COM static int 4378228SEric.Taylor@Sun.COM make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 438789Sahrens { 43910298SMatthew.Ahrens@Sun.COM if (put_stats_zhdl(zhp, zc) != 0) 4408228SEric.Taylor@Sun.COM return (-1); 4411758Sahrens 442789Sahrens /* 443789Sahrens * We've managed to open the dataset and gather statistics. Determine 444789Sahrens * the high-level type. 445789Sahrens */ 4462885Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 4472885Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 4482885Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 4492885Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 4502885Sahrens else 4512885Sahrens abort(); 4522885Sahrens 453789Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 454789Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 455789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 456789Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 457789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 458789Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 459789Sahrens else 4602082Seschrock abort(); /* we should never see any other types */ 461789Sahrens 4626865Srm160521 zhp->zpool_hdl = zpool_handle(zhp); 4638228SEric.Taylor@Sun.COM return (0); 4648228SEric.Taylor@Sun.COM } 4658228SEric.Taylor@Sun.COM 4668228SEric.Taylor@Sun.COM zfs_handle_t * 4678228SEric.Taylor@Sun.COM make_dataset_handle(libzfs_handle_t *hdl, const char *path) 4688228SEric.Taylor@Sun.COM { 4698228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4708228SEric.Taylor@Sun.COM 4718228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 4728228SEric.Taylor@Sun.COM 4738228SEric.Taylor@Sun.COM if (zhp == NULL) 4748228SEric.Taylor@Sun.COM return (NULL); 4758228SEric.Taylor@Sun.COM 4768228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 4778228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 4788228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 4798228SEric.Taylor@Sun.COM free(zhp); 4808228SEric.Taylor@Sun.COM return (NULL); 4818228SEric.Taylor@Sun.COM } 4828228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) == -1) { 4838228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4848228SEric.Taylor@Sun.COM free(zhp); 4858228SEric.Taylor@Sun.COM return (NULL); 4868228SEric.Taylor@Sun.COM } 4878228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, &zc) == -1) { 4888228SEric.Taylor@Sun.COM free(zhp); 4898228SEric.Taylor@Sun.COM zhp = NULL; 4908228SEric.Taylor@Sun.COM } 4918228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4928228SEric.Taylor@Sun.COM return (zhp); 4938228SEric.Taylor@Sun.COM } 4948228SEric.Taylor@Sun.COM 4958228SEric.Taylor@Sun.COM static zfs_handle_t * 4968228SEric.Taylor@Sun.COM make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 4978228SEric.Taylor@Sun.COM { 4988228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 4998228SEric.Taylor@Sun.COM 5008228SEric.Taylor@Sun.COM if (zhp == NULL) 5018228SEric.Taylor@Sun.COM return (NULL); 5028228SEric.Taylor@Sun.COM 5038228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 5048228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 5058228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, zc) == -1) { 5068228SEric.Taylor@Sun.COM free(zhp); 5078228SEric.Taylor@Sun.COM return (NULL); 5088228SEric.Taylor@Sun.COM } 509789Sahrens return (zhp); 510789Sahrens } 511789Sahrens 512789Sahrens /* 513789Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 514789Sahrens * argument is a mask of acceptable types. The function will print an 515789Sahrens * appropriate error message and return NULL if it can't be opened. 516789Sahrens */ 517789Sahrens zfs_handle_t * 5182082Seschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 519789Sahrens { 520789Sahrens zfs_handle_t *zhp; 5212082Seschrock char errbuf[1024]; 5222082Seschrock 5232082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 5242082Seschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 525789Sahrens 526789Sahrens /* 5272082Seschrock * Validate the name before we even try to open it. 528789Sahrens */ 5295326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 5302082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5312082Seschrock "invalid dataset name")); 5322082Seschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 533789Sahrens return (NULL); 534789Sahrens } 535789Sahrens 536789Sahrens /* 537789Sahrens * Try to get stats for the dataset, which will tell us if it exists. 538789Sahrens */ 539789Sahrens errno = 0; 5402082Seschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 5413237Slling (void) zfs_standard_error(hdl, errno, errbuf); 542789Sahrens return (NULL); 543789Sahrens } 544789Sahrens 545789Sahrens if (!(types & zhp->zfs_type)) { 5462082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 5472142Seschrock zfs_close(zhp); 548789Sahrens return (NULL); 549789Sahrens } 550789Sahrens 551789Sahrens return (zhp); 552789Sahrens } 553789Sahrens 554789Sahrens /* 555789Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 556789Sahrens */ 557789Sahrens void 558789Sahrens zfs_close(zfs_handle_t *zhp) 559789Sahrens { 560789Sahrens if (zhp->zfs_mntopts) 561789Sahrens free(zhp->zfs_mntopts); 5622676Seschrock nvlist_free(zhp->zfs_props); 5632676Seschrock nvlist_free(zhp->zfs_user_props); 56411022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 565789Sahrens free(zhp); 566789Sahrens } 567789Sahrens 5688228SEric.Taylor@Sun.COM typedef struct mnttab_node { 5698228SEric.Taylor@Sun.COM struct mnttab mtn_mt; 5708228SEric.Taylor@Sun.COM avl_node_t mtn_node; 5718228SEric.Taylor@Sun.COM } mnttab_node_t; 5728228SEric.Taylor@Sun.COM 5738228SEric.Taylor@Sun.COM static int 5748228SEric.Taylor@Sun.COM libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 5758228SEric.Taylor@Sun.COM { 5768228SEric.Taylor@Sun.COM const mnttab_node_t *mtn1 = arg1; 5778228SEric.Taylor@Sun.COM const mnttab_node_t *mtn2 = arg2; 5788228SEric.Taylor@Sun.COM int rv; 5798228SEric.Taylor@Sun.COM 5808228SEric.Taylor@Sun.COM rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 5818228SEric.Taylor@Sun.COM 5828228SEric.Taylor@Sun.COM if (rv == 0) 5838228SEric.Taylor@Sun.COM return (0); 5848228SEric.Taylor@Sun.COM return (rv > 0 ? 1 : -1); 5858228SEric.Taylor@Sun.COM } 5868228SEric.Taylor@Sun.COM 5878228SEric.Taylor@Sun.COM void 5888228SEric.Taylor@Sun.COM libzfs_mnttab_init(libzfs_handle_t *hdl) 5898228SEric.Taylor@Sun.COM { 5908228SEric.Taylor@Sun.COM assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 5918228SEric.Taylor@Sun.COM avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 5928228SEric.Taylor@Sun.COM sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 5938811SEric.Taylor@Sun.COM } 5948811SEric.Taylor@Sun.COM 5958811SEric.Taylor@Sun.COM void 5968811SEric.Taylor@Sun.COM libzfs_mnttab_update(libzfs_handle_t *hdl) 5978811SEric.Taylor@Sun.COM { 5988811SEric.Taylor@Sun.COM struct mnttab entry; 5998228SEric.Taylor@Sun.COM 6008228SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6018228SEric.Taylor@Sun.COM while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 6028228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6038228SEric.Taylor@Sun.COM 6048228SEric.Taylor@Sun.COM if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6058228SEric.Taylor@Sun.COM continue; 6068228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6078228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 6088228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 6098228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 6108228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 6118228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6128228SEric.Taylor@Sun.COM } 6138228SEric.Taylor@Sun.COM } 6148228SEric.Taylor@Sun.COM 6158228SEric.Taylor@Sun.COM void 6168228SEric.Taylor@Sun.COM libzfs_mnttab_fini(libzfs_handle_t *hdl) 6178228SEric.Taylor@Sun.COM { 6188228SEric.Taylor@Sun.COM void *cookie = NULL; 6198228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6208228SEric.Taylor@Sun.COM 6218228SEric.Taylor@Sun.COM while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 6228228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_special); 6238228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mountp); 6248228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_fstype); 6258228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mntopts); 6268228SEric.Taylor@Sun.COM free(mtn); 6278228SEric.Taylor@Sun.COM } 6288228SEric.Taylor@Sun.COM avl_destroy(&hdl->libzfs_mnttab_cache); 6298228SEric.Taylor@Sun.COM } 6308228SEric.Taylor@Sun.COM 6318811SEric.Taylor@Sun.COM void 6328811SEric.Taylor@Sun.COM libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 6338811SEric.Taylor@Sun.COM { 6348811SEric.Taylor@Sun.COM hdl->libzfs_mnttab_enable = enable; 6358811SEric.Taylor@Sun.COM } 6368811SEric.Taylor@Sun.COM 6378228SEric.Taylor@Sun.COM int 6388228SEric.Taylor@Sun.COM libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 6398228SEric.Taylor@Sun.COM struct mnttab *entry) 6408228SEric.Taylor@Sun.COM { 6418228SEric.Taylor@Sun.COM mnttab_node_t find; 6428228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6438228SEric.Taylor@Sun.COM 6448811SEric.Taylor@Sun.COM if (!hdl->libzfs_mnttab_enable) { 6458811SEric.Taylor@Sun.COM struct mnttab srch = { 0 }; 6468811SEric.Taylor@Sun.COM 6478811SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 6488811SEric.Taylor@Sun.COM libzfs_mnttab_fini(hdl); 6498811SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6508811SEric.Taylor@Sun.COM srch.mnt_special = (char *)fsname; 6518811SEric.Taylor@Sun.COM srch.mnt_fstype = MNTTYPE_ZFS; 6528811SEric.Taylor@Sun.COM if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 6538811SEric.Taylor@Sun.COM return (0); 6548811SEric.Taylor@Sun.COM else 6558811SEric.Taylor@Sun.COM return (ENOENT); 6568811SEric.Taylor@Sun.COM } 6578811SEric.Taylor@Sun.COM 6588228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6598811SEric.Taylor@Sun.COM libzfs_mnttab_update(hdl); 6608228SEric.Taylor@Sun.COM 6618228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6628228SEric.Taylor@Sun.COM mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 6638228SEric.Taylor@Sun.COM if (mtn) { 6648228SEric.Taylor@Sun.COM *entry = mtn->mtn_mt; 6658228SEric.Taylor@Sun.COM return (0); 6668228SEric.Taylor@Sun.COM } 6678228SEric.Taylor@Sun.COM return (ENOENT); 6688228SEric.Taylor@Sun.COM } 6698228SEric.Taylor@Sun.COM 6708228SEric.Taylor@Sun.COM void 6718228SEric.Taylor@Sun.COM libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 6728228SEric.Taylor@Sun.COM const char *mountp, const char *mntopts) 6738228SEric.Taylor@Sun.COM { 6748228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6758228SEric.Taylor@Sun.COM 6768228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6778228SEric.Taylor@Sun.COM return; 6788228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6798228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 6808228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 6818228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 6828228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 6838228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6848228SEric.Taylor@Sun.COM } 6858228SEric.Taylor@Sun.COM 6868228SEric.Taylor@Sun.COM void 6878228SEric.Taylor@Sun.COM libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 6888228SEric.Taylor@Sun.COM { 6898228SEric.Taylor@Sun.COM mnttab_node_t find; 6908228SEric.Taylor@Sun.COM mnttab_node_t *ret; 6918228SEric.Taylor@Sun.COM 6928228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6938228SEric.Taylor@Sun.COM if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 6948228SEric.Taylor@Sun.COM avl_remove(&hdl->libzfs_mnttab_cache, ret); 6958228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_special); 6968228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mountp); 6978228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_fstype); 6988228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mntopts); 6998228SEric.Taylor@Sun.COM free(ret); 7008228SEric.Taylor@Sun.COM } 7018228SEric.Taylor@Sun.COM } 7028228SEric.Taylor@Sun.COM 7035713Srm160521 int 7045713Srm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7055713Srm160521 { 7066865Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7076865Srm160521 7085713Srm160521 if (zpool_handle == NULL) 7095713Srm160521 return (-1); 7105713Srm160521 7115713Srm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7125713Srm160521 ZPOOL_PROP_VERSION, NULL); 7135713Srm160521 return (0); 7145713Srm160521 } 7155713Srm160521 7165713Srm160521 /* 7175713Srm160521 * The choice of reservation property depends on the SPA version. 7185713Srm160521 */ 7195713Srm160521 static int 7205713Srm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7215713Srm160521 { 7225713Srm160521 int spa_version; 7235713Srm160521 7245713Srm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7255713Srm160521 return (-1); 7265713Srm160521 7275713Srm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7285713Srm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7295713Srm160521 else 7305713Srm160521 *resv_prop = ZFS_PROP_RESERVATION; 7315713Srm160521 7325713Srm160521 return (0); 7335713Srm160521 } 7345713Srm160521 7353912Slling /* 7362676Seschrock * Given an nvlist of properties to set, validates that they are correct, and 7372676Seschrock * parses any numeric properties (index, boolean, etc) if they are specified as 7382676Seschrock * strings. 739789Sahrens */ 7407184Stimh nvlist_t * 7417184Stimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 7425094Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 743789Sahrens { 7442676Seschrock nvpair_t *elem; 7452676Seschrock uint64_t intval; 7462676Seschrock char *strval; 7475094Slling zfs_prop_t prop; 7482676Seschrock nvlist_t *ret; 7495331Samw int chosen_normal = -1; 7505331Samw int chosen_utf = -1; 7512676Seschrock 7525094Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 7535094Slling (void) no_memory(hdl); 7545094Slling return (NULL); 755789Sahrens } 756789Sahrens 7579396SMatthew.Ahrens@Sun.COM /* 7589396SMatthew.Ahrens@Sun.COM * Make sure this property is valid and applies to this type. 7599396SMatthew.Ahrens@Sun.COM */ 7609396SMatthew.Ahrens@Sun.COM 7612676Seschrock elem = NULL; 7622676Seschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 7635094Slling const char *propname = nvpair_name(elem); 7642676Seschrock 7659396SMatthew.Ahrens@Sun.COM prop = zfs_name_to_prop(propname); 7669396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 7675094Slling /* 7689396SMatthew.Ahrens@Sun.COM * This is a user property: make sure it's a 7695094Slling * string, and that it's less than ZAP_MAXNAMELEN. 7705094Slling */ 7715094Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 7725094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7735094Slling "'%s' must be a string"), propname); 7745094Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7755094Slling goto error; 7765094Slling } 7775094Slling 7785094Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 7795094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7805094Slling "property name '%s' is too long"), 7812676Seschrock propname); 7822676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7832676Seschrock goto error; 7842676Seschrock } 7852676Seschrock 7862676Seschrock (void) nvpair_value_string(elem, &strval); 7872676Seschrock if (nvlist_add_string(ret, propname, strval) != 0) { 7882676Seschrock (void) no_memory(hdl); 7892676Seschrock goto error; 7902676Seschrock } 7912676Seschrock continue; 792789Sahrens } 7932676Seschrock 7949396SMatthew.Ahrens@Sun.COM /* 7959396SMatthew.Ahrens@Sun.COM * Currently, only user properties can be modified on 7969396SMatthew.Ahrens@Sun.COM * snapshots. 7979396SMatthew.Ahrens@Sun.COM */ 7987265Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 7997265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8007265Sahrens "this property can not be modified for snapshots")); 8017265Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8027265Sahrens goto error; 8037265Sahrens } 8047265Sahrens 8059396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 8069396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t uqtype; 8079396SMatthew.Ahrens@Sun.COM char newpropname[128]; 8089396SMatthew.Ahrens@Sun.COM char domain[128]; 8099396SMatthew.Ahrens@Sun.COM uint64_t rid; 8109396SMatthew.Ahrens@Sun.COM uint64_t valary[3]; 8119396SMatthew.Ahrens@Sun.COM 8129396SMatthew.Ahrens@Sun.COM if (userquota_propname_decode(propname, zoned, 8139396SMatthew.Ahrens@Sun.COM &uqtype, domain, sizeof (domain), &rid) != 0) { 8149396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8159396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, 8169396SMatthew.Ahrens@Sun.COM "'%s' has an invalid user/group name"), 8179396SMatthew.Ahrens@Sun.COM propname); 8189396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8199396SMatthew.Ahrens@Sun.COM goto error; 8209396SMatthew.Ahrens@Sun.COM } 8219396SMatthew.Ahrens@Sun.COM 8229396SMatthew.Ahrens@Sun.COM if (uqtype != ZFS_PROP_USERQUOTA && 8239396SMatthew.Ahrens@Sun.COM uqtype != ZFS_PROP_GROUPQUOTA) { 8249396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8259396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8269396SMatthew.Ahrens@Sun.COM propname); 8279396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_PROPREADONLY, 8289396SMatthew.Ahrens@Sun.COM errbuf); 8299396SMatthew.Ahrens@Sun.COM goto error; 8309396SMatthew.Ahrens@Sun.COM } 8319396SMatthew.Ahrens@Sun.COM 8329396SMatthew.Ahrens@Sun.COM if (nvpair_type(elem) == DATA_TYPE_STRING) { 8339396SMatthew.Ahrens@Sun.COM (void) nvpair_value_string(elem, &strval); 8349396SMatthew.Ahrens@Sun.COM if (strcmp(strval, "none") == 0) { 8359396SMatthew.Ahrens@Sun.COM intval = 0; 8369396SMatthew.Ahrens@Sun.COM } else if (zfs_nicestrtonum(hdl, 8379396SMatthew.Ahrens@Sun.COM strval, &intval) != 0) { 8389396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, 8399396SMatthew.Ahrens@Sun.COM EZFS_BADPROP, errbuf); 8409396SMatthew.Ahrens@Sun.COM goto error; 8419396SMatthew.Ahrens@Sun.COM } 8429396SMatthew.Ahrens@Sun.COM } else if (nvpair_type(elem) == 8439396SMatthew.Ahrens@Sun.COM DATA_TYPE_UINT64) { 8449396SMatthew.Ahrens@Sun.COM (void) nvpair_value_uint64(elem, &intval); 8459396SMatthew.Ahrens@Sun.COM if (intval == 0) { 8469396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8479396SMatthew.Ahrens@Sun.COM "use 'none' to disable " 8489396SMatthew.Ahrens@Sun.COM "userquota/groupquota")); 8499396SMatthew.Ahrens@Sun.COM goto error; 8509396SMatthew.Ahrens@Sun.COM } 8519396SMatthew.Ahrens@Sun.COM } else { 8529396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8539396SMatthew.Ahrens@Sun.COM "'%s' must be a number"), propname); 8549396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8559396SMatthew.Ahrens@Sun.COM goto error; 8569396SMatthew.Ahrens@Sun.COM } 8579396SMatthew.Ahrens@Sun.COM 85810969SMatthew.Ahrens@Sun.COM /* 85910969SMatthew.Ahrens@Sun.COM * Encode the prop name as 86010969SMatthew.Ahrens@Sun.COM * userquota@<hex-rid>-domain, to make it easy 86110969SMatthew.Ahrens@Sun.COM * for the kernel to decode. 86210969SMatthew.Ahrens@Sun.COM */ 8639396SMatthew.Ahrens@Sun.COM (void) snprintf(newpropname, sizeof (newpropname), 86410969SMatthew.Ahrens@Sun.COM "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 86510969SMatthew.Ahrens@Sun.COM (longlong_t)rid, domain); 8669396SMatthew.Ahrens@Sun.COM valary[0] = uqtype; 8679396SMatthew.Ahrens@Sun.COM valary[1] = rid; 8689396SMatthew.Ahrens@Sun.COM valary[2] = intval; 8699396SMatthew.Ahrens@Sun.COM if (nvlist_add_uint64_array(ret, newpropname, 8709396SMatthew.Ahrens@Sun.COM valary, 3) != 0) { 8719396SMatthew.Ahrens@Sun.COM (void) no_memory(hdl); 8729396SMatthew.Ahrens@Sun.COM goto error; 8739396SMatthew.Ahrens@Sun.COM } 8749396SMatthew.Ahrens@Sun.COM continue; 8759396SMatthew.Ahrens@Sun.COM } 8769396SMatthew.Ahrens@Sun.COM 8779396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL) { 8789396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8799396SMatthew.Ahrens@Sun.COM "invalid property '%s'"), propname); 8809396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8819396SMatthew.Ahrens@Sun.COM goto error; 8829396SMatthew.Ahrens@Sun.COM } 8839396SMatthew.Ahrens@Sun.COM 8842676Seschrock if (!zfs_prop_valid_for_type(prop, type)) { 8852676Seschrock zfs_error_aux(hdl, 8862676Seschrock dgettext(TEXT_DOMAIN, "'%s' does not " 8872676Seschrock "apply to datasets of this type"), propname); 8882676Seschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8892676Seschrock goto error; 8902676Seschrock } 8912676Seschrock 8922676Seschrock if (zfs_prop_readonly(prop) && 8935331Samw (!zfs_prop_setonce(prop) || zhp != NULL)) { 8942676Seschrock zfs_error_aux(hdl, 8952676Seschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8962676Seschrock propname); 8972676Seschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 8982676Seschrock goto error; 8992676Seschrock } 9002676Seschrock 9015094Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 9025094Slling &strval, &intval, errbuf) != 0) 9032676Seschrock goto error; 9042676Seschrock 9052676Seschrock /* 9062676Seschrock * Perform some additional checks for specific properties. 9072676Seschrock */ 9082676Seschrock switch (prop) { 9094577Sahrens case ZFS_PROP_VERSION: 9104577Sahrens { 9114577Sahrens int version; 9124577Sahrens 9134577Sahrens if (zhp == NULL) 9144577Sahrens break; 9154577Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 9164577Sahrens if (intval < version) { 9174577Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9184577Sahrens "Can not downgrade; already at version %u"), 9194577Sahrens version); 9204577Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9214577Sahrens goto error; 9224577Sahrens } 9234577Sahrens break; 9244577Sahrens } 9254577Sahrens 9262676Seschrock case ZFS_PROP_RECORDSIZE: 9272676Seschrock case ZFS_PROP_VOLBLOCKSIZE: 9282676Seschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 9292676Seschrock if (intval < SPA_MINBLOCKSIZE || 9302676Seschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 9312082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9322676Seschrock "'%s' must be power of 2 from %u " 9332676Seschrock "to %uk"), propname, 9342676Seschrock (uint_t)SPA_MINBLOCKSIZE, 9352676Seschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 9362676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9372676Seschrock goto error; 938789Sahrens } 939789Sahrens break; 940789Sahrens 9413126Sahl case ZFS_PROP_SHAREISCSI: 9423126Sahl if (strcmp(strval, "off") != 0 && 9433126Sahl strcmp(strval, "on") != 0 && 9443126Sahl strcmp(strval, "type=disk") != 0) { 9453126Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9463126Sahl "'%s' must be 'on', 'off', or 'type=disk'"), 9473126Sahl propname); 9483126Sahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9493126Sahl goto error; 9503126Sahl } 9513126Sahl 9523126Sahl break; 9533126Sahl 95410972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 95510972SRic.Aleshire@Sun.COM { 95610972SRic.Aleshire@Sun.COM /* 95710972SRic.Aleshire@Sun.COM * Verify the mlslabel string and convert to 95810972SRic.Aleshire@Sun.COM * internal hex label string. 95910972SRic.Aleshire@Sun.COM */ 96010972SRic.Aleshire@Sun.COM 96110972SRic.Aleshire@Sun.COM m_label_t *new_sl; 96210972SRic.Aleshire@Sun.COM char *hex = NULL; /* internal label string */ 96310972SRic.Aleshire@Sun.COM 96410972SRic.Aleshire@Sun.COM /* Default value is already OK. */ 96510972SRic.Aleshire@Sun.COM if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 96610972SRic.Aleshire@Sun.COM break; 96710972SRic.Aleshire@Sun.COM 96810972SRic.Aleshire@Sun.COM /* Verify the label can be converted to binary form */ 96910972SRic.Aleshire@Sun.COM if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 97010972SRic.Aleshire@Sun.COM (str_to_label(strval, &new_sl, MAC_LABEL, 97110972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1)) { 97210972SRic.Aleshire@Sun.COM goto badlabel; 97310972SRic.Aleshire@Sun.COM } 97410972SRic.Aleshire@Sun.COM 97510972SRic.Aleshire@Sun.COM /* Now translate to hex internal label string */ 97610972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &hex, M_INTERNAL, 97710972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 97810972SRic.Aleshire@Sun.COM if (hex) 97910972SRic.Aleshire@Sun.COM free(hex); 98010972SRic.Aleshire@Sun.COM goto badlabel; 98110972SRic.Aleshire@Sun.COM } 98210972SRic.Aleshire@Sun.COM m_label_free(new_sl); 98310972SRic.Aleshire@Sun.COM 98410972SRic.Aleshire@Sun.COM /* If string is already in internal form, we're done. */ 98510972SRic.Aleshire@Sun.COM if (strcmp(strval, hex) == 0) { 98610972SRic.Aleshire@Sun.COM free(hex); 98710972SRic.Aleshire@Sun.COM break; 98810972SRic.Aleshire@Sun.COM } 98910972SRic.Aleshire@Sun.COM 99010972SRic.Aleshire@Sun.COM /* Replace the label string with the internal form. */ 99110984SRic.Aleshire@Sun.COM (void) nvlist_remove(ret, zfs_prop_to_name(prop), 99210972SRic.Aleshire@Sun.COM DATA_TYPE_STRING); 99310972SRic.Aleshire@Sun.COM verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 99410972SRic.Aleshire@Sun.COM hex) == 0); 99510972SRic.Aleshire@Sun.COM free(hex); 99610972SRic.Aleshire@Sun.COM 99710972SRic.Aleshire@Sun.COM break; 99810972SRic.Aleshire@Sun.COM 99910972SRic.Aleshire@Sun.COM badlabel: 100010972SRic.Aleshire@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 100110972SRic.Aleshire@Sun.COM "invalid mlslabel '%s'"), strval); 100210972SRic.Aleshire@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 100310972SRic.Aleshire@Sun.COM m_label_free(new_sl); /* OK if null */ 100410972SRic.Aleshire@Sun.COM goto error; 100510972SRic.Aleshire@Sun.COM 100610972SRic.Aleshire@Sun.COM } 100710972SRic.Aleshire@Sun.COM 10082676Seschrock case ZFS_PROP_MOUNTPOINT: 10094778Srm160521 { 10104778Srm160521 namecheck_err_t why; 10114778Srm160521 10122676Seschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 10132676Seschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 10142676Seschrock break; 10152676Seschrock 10164778Srm160521 if (mountpoint_namecheck(strval, &why)) { 10174778Srm160521 switch (why) { 10184778Srm160521 case NAME_ERR_LEADING_SLASH: 10194778Srm160521 zfs_error_aux(hdl, 10204778Srm160521 dgettext(TEXT_DOMAIN, 10214778Srm160521 "'%s' must be an absolute path, " 10224778Srm160521 "'none', or 'legacy'"), propname); 10234778Srm160521 break; 10244778Srm160521 case NAME_ERR_TOOLONG: 10254778Srm160521 zfs_error_aux(hdl, 10264778Srm160521 dgettext(TEXT_DOMAIN, 10274778Srm160521 "component of '%s' is too long"), 10284778Srm160521 propname); 10294778Srm160521 break; 10304778Srm160521 } 10312676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10322676Seschrock goto error; 1033789Sahrens } 10344778Srm160521 } 10354778Srm160521 10363126Sahl /*FALLTHRU*/ 10373126Sahl 10385331Samw case ZFS_PROP_SHARESMB: 10393126Sahl case ZFS_PROP_SHARENFS: 10403126Sahl /* 10415331Samw * For the mountpoint and sharenfs or sharesmb 10425331Samw * properties, check if it can be set in a 10435331Samw * global/non-global zone based on 10443126Sahl * the zoned property value: 10453126Sahl * 10463126Sahl * global zone non-global zone 10473126Sahl * -------------------------------------------------- 10483126Sahl * zoned=on mountpoint (no) mountpoint (yes) 10493126Sahl * sharenfs (no) sharenfs (no) 10505331Samw * sharesmb (no) sharesmb (no) 10513126Sahl * 10523126Sahl * zoned=off mountpoint (yes) N/A 10533126Sahl * sharenfs (yes) 10545331Samw * sharesmb (yes) 10553126Sahl */ 10562676Seschrock if (zoned) { 10572676Seschrock if (getzoneid() == GLOBAL_ZONEID) { 10582676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10592676Seschrock "'%s' cannot be set on " 10602676Seschrock "dataset in a non-global zone"), 10612676Seschrock propname); 10622676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10632676Seschrock errbuf); 10642676Seschrock goto error; 10655331Samw } else if (prop == ZFS_PROP_SHARENFS || 10665331Samw prop == ZFS_PROP_SHARESMB) { 10672676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10682676Seschrock "'%s' cannot be set in " 10692676Seschrock "a non-global zone"), propname); 10702676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10712676Seschrock errbuf); 10722676Seschrock goto error; 10732676Seschrock } 10742676Seschrock } else if (getzoneid() != GLOBAL_ZONEID) { 10752676Seschrock /* 10762676Seschrock * If zoned property is 'off', this must be in 10779396SMatthew.Ahrens@Sun.COM * a global zone. If not, something is wrong. 10782676Seschrock */ 10792676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10802676Seschrock "'%s' cannot be set while dataset " 10812676Seschrock "'zoned' property is set"), propname); 10822676Seschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 10832676Seschrock goto error; 10842676Seschrock } 10853126Sahl 10864180Sdougm /* 10874180Sdougm * At this point, it is legitimate to set the 10884180Sdougm * property. Now we want to make sure that the 10894180Sdougm * property value is valid if it is sharenfs. 10904180Sdougm */ 10915331Samw if ((prop == ZFS_PROP_SHARENFS || 10925331Samw prop == ZFS_PROP_SHARESMB) && 10934217Seschrock strcmp(strval, "on") != 0 && 10944217Seschrock strcmp(strval, "off") != 0) { 10955331Samw zfs_share_proto_t proto; 10965331Samw 10975331Samw if (prop == ZFS_PROP_SHARESMB) 10985331Samw proto = PROTO_SMB; 10995331Samw else 11005331Samw proto = PROTO_NFS; 11014180Sdougm 11024180Sdougm /* 11035331Samw * Must be an valid sharing protocol 11045331Samw * option string so init the libshare 11055331Samw * in order to enable the parser and 11065331Samw * then parse the options. We use the 11075331Samw * control API since we don't care about 11085331Samw * the current configuration and don't 11094180Sdougm * want the overhead of loading it 11104180Sdougm * until we actually do something. 11114180Sdougm */ 11124180Sdougm 11134217Seschrock if (zfs_init_libshare(hdl, 11144217Seschrock SA_INIT_CONTROL_API) != SA_OK) { 11154217Seschrock /* 11164217Seschrock * An error occurred so we can't do 11174217Seschrock * anything 11184217Seschrock */ 11194217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11204217Seschrock "'%s' cannot be set: problem " 11214217Seschrock "in share initialization"), 11224217Seschrock propname); 11234217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11244217Seschrock errbuf); 11254217Seschrock goto error; 11264217Seschrock } 11274217Seschrock 11285331Samw if (zfs_parse_options(strval, proto) != SA_OK) { 11294217Seschrock /* 11304217Seschrock * There was an error in parsing so 11314217Seschrock * deal with it by issuing an error 11324217Seschrock * message and leaving after 11334217Seschrock * uninitializing the the libshare 11344217Seschrock * interface. 11354217Seschrock */ 11364217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11374217Seschrock "'%s' cannot be set to invalid " 11384217Seschrock "options"), propname); 11394217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11404217Seschrock errbuf); 11414217Seschrock zfs_uninit_libshare(hdl); 11424217Seschrock goto error; 11434217Seschrock } 11444180Sdougm zfs_uninit_libshare(hdl); 11454180Sdougm } 11464180Sdougm 11473126Sahl break; 11485331Samw case ZFS_PROP_UTF8ONLY: 11495331Samw chosen_utf = (int)intval; 11505331Samw break; 11515331Samw case ZFS_PROP_NORMALIZE: 11525331Samw chosen_normal = (int)intval; 11535331Samw break; 11542676Seschrock } 11552676Seschrock 11562676Seschrock /* 11572676Seschrock * For changes to existing volumes, we have some additional 11582676Seschrock * checks to enforce. 11592676Seschrock */ 11602676Seschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 11612676Seschrock uint64_t volsize = zfs_prop_get_int(zhp, 11622676Seschrock ZFS_PROP_VOLSIZE); 11632676Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 11642676Seschrock ZFS_PROP_VOLBLOCKSIZE); 11652676Seschrock char buf[64]; 11662676Seschrock 11672676Seschrock switch (prop) { 11682676Seschrock case ZFS_PROP_RESERVATION: 11695378Sck153898 case ZFS_PROP_REFRESERVATION: 11702676Seschrock if (intval > volsize) { 11712676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11722676Seschrock "'%s' is greater than current " 11732676Seschrock "volume size"), propname); 11742676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11752676Seschrock errbuf); 11762676Seschrock goto error; 11772676Seschrock } 11782676Seschrock break; 11792676Seschrock 11802676Seschrock case ZFS_PROP_VOLSIZE: 11812676Seschrock if (intval % blocksize != 0) { 11822676Seschrock zfs_nicenum(blocksize, buf, 11832676Seschrock sizeof (buf)); 11842676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11852676Seschrock "'%s' must be a multiple of " 11862676Seschrock "volume block size (%s)"), 11872676Seschrock propname, buf); 11882676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11892676Seschrock errbuf); 11902676Seschrock goto error; 11912676Seschrock } 11922676Seschrock 11932676Seschrock if (intval == 0) { 11942676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11952676Seschrock "'%s' cannot be zero"), 11962676Seschrock propname); 11972676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11982676Seschrock errbuf); 11992676Seschrock goto error; 1200789Sahrens } 12013126Sahl break; 1202789Sahrens } 1203789Sahrens } 1204789Sahrens } 1205789Sahrens 12062676Seschrock /* 12075331Samw * If normalization was chosen, but no UTF8 choice was made, 12085331Samw * enforce rejection of non-UTF8 names. 12095331Samw * 12105331Samw * If normalization was chosen, but rejecting non-UTF8 names 12115331Samw * was explicitly not chosen, it is an error. 12125331Samw */ 12135498Stimh if (chosen_normal > 0 && chosen_utf < 0) { 12145331Samw if (nvlist_add_uint64(ret, 12155331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 12165331Samw (void) no_memory(hdl); 12175331Samw goto error; 12185331Samw } 12195498Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 12205331Samw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12215331Samw "'%s' must be set 'on' if normalization chosen"), 12225331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 12235331Samw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 12245331Samw goto error; 12255331Samw } 12265331Samw 12275331Samw /* 12282676Seschrock * If this is an existing volume, and someone is setting the volsize, 12292676Seschrock * make sure that it matches the reservation, or add it if necessary. 12302676Seschrock */ 12312676Seschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 12322676Seschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 12332676Seschrock &intval) == 0) { 12342676Seschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 12352676Seschrock ZFS_PROP_VOLSIZE); 12365481Sck153898 uint64_t old_reservation; 12372676Seschrock uint64_t new_reservation; 12385481Sck153898 zfs_prop_t resv_prop; 12395713Srm160521 12405713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 12415481Sck153898 goto error; 12425481Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 12432676Seschrock 12442676Seschrock if (old_volsize == old_reservation && 12455481Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 12462676Seschrock &new_reservation) != 0) { 12472676Seschrock if (nvlist_add_uint64(ret, 12485481Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 12492676Seschrock (void) no_memory(hdl); 12502676Seschrock goto error; 12512676Seschrock } 12522676Seschrock } 12532676Seschrock } 12542676Seschrock return (ret); 12552676Seschrock 12562676Seschrock error: 12572676Seschrock nvlist_free(ret); 12582676Seschrock return (NULL); 1259789Sahrens } 1260789Sahrens 126111022STom.Erickson@Sun.COM void 126211022STom.Erickson@Sun.COM zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 126311022STom.Erickson@Sun.COM char *errbuf) 126411022STom.Erickson@Sun.COM { 126511022STom.Erickson@Sun.COM switch (err) { 126611022STom.Erickson@Sun.COM 126711022STom.Erickson@Sun.COM case ENOSPC: 126811022STom.Erickson@Sun.COM /* 126911022STom.Erickson@Sun.COM * For quotas and reservations, ENOSPC indicates 127011022STom.Erickson@Sun.COM * something different; setting a quota or reservation 127111022STom.Erickson@Sun.COM * doesn't use any disk space. 127211022STom.Erickson@Sun.COM */ 127311022STom.Erickson@Sun.COM switch (prop) { 127411022STom.Erickson@Sun.COM case ZFS_PROP_QUOTA: 127511022STom.Erickson@Sun.COM case ZFS_PROP_REFQUOTA: 127611022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127711022STom.Erickson@Sun.COM "size is less than current used or " 127811022STom.Erickson@Sun.COM "reserved space")); 127911022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128011022STom.Erickson@Sun.COM break; 128111022STom.Erickson@Sun.COM 128211022STom.Erickson@Sun.COM case ZFS_PROP_RESERVATION: 128311022STom.Erickson@Sun.COM case ZFS_PROP_REFRESERVATION: 128411022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128511022STom.Erickson@Sun.COM "size is greater than available space")); 128611022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128711022STom.Erickson@Sun.COM break; 128811022STom.Erickson@Sun.COM 128911022STom.Erickson@Sun.COM default: 129011022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 129111022STom.Erickson@Sun.COM break; 129211022STom.Erickson@Sun.COM } 129311022STom.Erickson@Sun.COM break; 129411022STom.Erickson@Sun.COM 129511022STom.Erickson@Sun.COM case EBUSY: 129611022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, EBUSY, errbuf); 129711022STom.Erickson@Sun.COM break; 129811022STom.Erickson@Sun.COM 129911022STom.Erickson@Sun.COM case EROFS: 130011022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 130111022STom.Erickson@Sun.COM break; 130211022STom.Erickson@Sun.COM 130311022STom.Erickson@Sun.COM case ENOTSUP: 130411022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130511022STom.Erickson@Sun.COM "pool and or dataset must be upgraded to set this " 130611022STom.Erickson@Sun.COM "property or value")); 130711022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 130811022STom.Erickson@Sun.COM break; 130911022STom.Erickson@Sun.COM 131011022STom.Erickson@Sun.COM case ERANGE: 131111022STom.Erickson@Sun.COM if (prop == ZFS_PROP_COMPRESSION) { 131211022STom.Erickson@Sun.COM (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 131311022STom.Erickson@Sun.COM "property setting is not allowed on " 131411022STom.Erickson@Sun.COM "bootable datasets")); 131511022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 131611022STom.Erickson@Sun.COM } else { 131711022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 131811022STom.Erickson@Sun.COM } 131911022STom.Erickson@Sun.COM break; 132011022STom.Erickson@Sun.COM 132111022STom.Erickson@Sun.COM case EOVERFLOW: 132211022STom.Erickson@Sun.COM /* 132311022STom.Erickson@Sun.COM * This platform can't address a volume this big. 132411022STom.Erickson@Sun.COM */ 132511022STom.Erickson@Sun.COM #ifdef _ILP32 132611022STom.Erickson@Sun.COM if (prop == ZFS_PROP_VOLSIZE) { 132711022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 132811022STom.Erickson@Sun.COM break; 132911022STom.Erickson@Sun.COM } 133011022STom.Erickson@Sun.COM #endif 133111022STom.Erickson@Sun.COM /* FALLTHROUGH */ 133211022STom.Erickson@Sun.COM default: 133311022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 133411022STom.Erickson@Sun.COM } 133511022STom.Erickson@Sun.COM } 133611022STom.Erickson@Sun.COM 1337789Sahrens /* 1338789Sahrens * Given a property name and value, set the property for the given dataset. 1339789Sahrens */ 1340789Sahrens int 13412676Seschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1342789Sahrens { 1343789Sahrens zfs_cmd_t zc = { 0 }; 13442676Seschrock int ret = -1; 13452676Seschrock prop_changelist_t *cl = NULL; 13462082Seschrock char errbuf[1024]; 13472082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 13482676Seschrock nvlist_t *nvl = NULL, *realprops; 13492676Seschrock zfs_prop_t prop; 13507509SMark.Musante@Sun.COM boolean_t do_prefix; 13517509SMark.Musante@Sun.COM uint64_t idx; 13522082Seschrock 13532082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 13542676Seschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 13552082Seschrock zhp->zfs_name); 13562082Seschrock 13572676Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 13582676Seschrock nvlist_add_string(nvl, propname, propval) != 0) { 13592676Seschrock (void) no_memory(hdl); 13602676Seschrock goto error; 1361789Sahrens } 1362789Sahrens 13637184Stimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 13642676Seschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 13652676Seschrock goto error; 13665094Slling 13672676Seschrock nvlist_free(nvl); 13682676Seschrock nvl = realprops; 13692676Seschrock 13702676Seschrock prop = zfs_name_to_prop(propname); 13712676Seschrock 13727366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 13732676Seschrock goto error; 1374789Sahrens 1375789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 13762082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13772082Seschrock "child dataset with inherited mountpoint is used " 13782082Seschrock "in a non-global zone")); 13792082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1380789Sahrens goto error; 1381789Sahrens } 1382789Sahrens 13837509SMark.Musante@Sun.COM /* 13847509SMark.Musante@Sun.COM * If the dataset's canmount property is being set to noauto, 13857509SMark.Musante@Sun.COM * then we want to prevent unmounting & remounting it. 13867509SMark.Musante@Sun.COM */ 13877509SMark.Musante@Sun.COM do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13887509SMark.Musante@Sun.COM (zprop_string_to_index(prop, propval, &idx, 13897509SMark.Musante@Sun.COM ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 13906168Shs24103 13916168Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 13927509SMark.Musante@Sun.COM goto error; 1393789Sahrens 1394789Sahrens /* 1395789Sahrens * Execute the corresponding ioctl() to set this property. 1396789Sahrens */ 1397789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1398789Sahrens 13995094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 14002676Seschrock goto error; 14012676Seschrock 14024543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 14038845Samw@Sun.COM 1404789Sahrens if (ret != 0) { 140511022STom.Erickson@Sun.COM zfs_setprop_error(hdl, prop, errno, errbuf); 1406789Sahrens } else { 14076168Shs24103 if (do_prefix) 14086168Shs24103 ret = changelist_postfix(cl); 14096168Shs24103 1410789Sahrens /* 1411789Sahrens * Refresh the statistics so the new property value 1412789Sahrens * is reflected. 1413789Sahrens */ 14146168Shs24103 if (ret == 0) 14152676Seschrock (void) get_stats(zhp); 1416789Sahrens } 1417789Sahrens 1418789Sahrens error: 14192676Seschrock nvlist_free(nvl); 14202676Seschrock zcmd_free_nvlists(&zc); 14212676Seschrock if (cl) 14222676Seschrock changelist_free(cl); 1423789Sahrens return (ret); 1424789Sahrens } 1425789Sahrens 1426789Sahrens /* 142711022STom.Erickson@Sun.COM * Given a property, inherit the value from the parent dataset, or if received 142811022STom.Erickson@Sun.COM * is TRUE, revert to the received value, if any. 1429789Sahrens */ 1430789Sahrens int 143111022STom.Erickson@Sun.COM zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1432789Sahrens { 1433789Sahrens zfs_cmd_t zc = { 0 }; 1434789Sahrens int ret; 1435789Sahrens prop_changelist_t *cl; 14362082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 14372082Seschrock char errbuf[1024]; 14382676Seschrock zfs_prop_t prop; 14392082Seschrock 14402082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 14412082Seschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1442789Sahrens 144311022STom.Erickson@Sun.COM zc.zc_cookie = received; 14445094Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 14452676Seschrock /* 14462676Seschrock * For user properties, the amount of work we have to do is very 14472676Seschrock * small, so just do it here. 14482676Seschrock */ 14492676Seschrock if (!zfs_prop_user(propname)) { 14502676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14512676Seschrock "invalid property")); 14522676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 14532676Seschrock } 14542676Seschrock 14552676Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14562676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 14572676Seschrock 14584849Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 14592676Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 14602676Seschrock 14612676Seschrock return (0); 14622676Seschrock } 14632676Seschrock 1464789Sahrens /* 1465789Sahrens * Verify that this property is inheritable. 1466789Sahrens */ 14672082Seschrock if (zfs_prop_readonly(prop)) 14682082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 14692082Seschrock 147011022STom.Erickson@Sun.COM if (!zfs_prop_inheritable(prop) && !received) 14712082Seschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1472789Sahrens 1473789Sahrens /* 1474789Sahrens * Check to see if the value applies to this type 1475789Sahrens */ 14762082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 14772082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1478789Sahrens 14793443Srm160521 /* 14803443Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 14813443Srm160521 */ 14823443Srm160521 propname = zfs_prop_to_name(prop); 1483789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14842676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1485789Sahrens 1486789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1487789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 14882082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14892082Seschrock "dataset is used in a non-global zone")); 14902082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1491789Sahrens } 1492789Sahrens 1493789Sahrens /* 1494789Sahrens * Determine datasets which will be affected by this change, if any. 1495789Sahrens */ 14967366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1497789Sahrens return (-1); 1498789Sahrens 1499789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 15002082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15012082Seschrock "child dataset with inherited mountpoint is used " 15022082Seschrock "in a non-global zone")); 15032082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1504789Sahrens goto error; 1505789Sahrens } 1506789Sahrens 1507789Sahrens if ((ret = changelist_prefix(cl)) != 0) 1508789Sahrens goto error; 1509789Sahrens 15104849Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 15112082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1512789Sahrens } else { 1513789Sahrens 15142169Snd150628 if ((ret = changelist_postfix(cl)) != 0) 1515789Sahrens goto error; 1516789Sahrens 1517789Sahrens /* 1518789Sahrens * Refresh the statistics so the new property is reflected. 1519789Sahrens */ 1520789Sahrens (void) get_stats(zhp); 1521789Sahrens } 1522789Sahrens 1523789Sahrens error: 1524789Sahrens changelist_free(cl); 1525789Sahrens return (ret); 1526789Sahrens } 1527789Sahrens 1528789Sahrens /* 15291356Seschrock * True DSL properties are stored in an nvlist. The following two functions 15301356Seschrock * extract them appropriately. 15311356Seschrock */ 15321356Seschrock static uint64_t 15331356Seschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15341356Seschrock { 15351356Seschrock nvlist_t *nv; 15361356Seschrock uint64_t value; 15371356Seschrock 15382885Sahrens *source = NULL; 15391356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15401356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15415094Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 15425094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15431356Seschrock } else { 15448802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15458802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15461356Seschrock value = zfs_prop_default_numeric(prop); 15471356Seschrock *source = ""; 15481356Seschrock } 15491356Seschrock 15501356Seschrock return (value); 15511356Seschrock } 15521356Seschrock 15531356Seschrock static char * 15541356Seschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15551356Seschrock { 15561356Seschrock nvlist_t *nv; 15571356Seschrock char *value; 15581356Seschrock 15592885Sahrens *source = NULL; 15601356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15611356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15625094Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 15635094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15641356Seschrock } else { 15658802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15668802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15671356Seschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15681356Seschrock value = ""; 15691356Seschrock *source = ""; 15701356Seschrock } 15711356Seschrock 15721356Seschrock return (value); 15731356Seschrock } 15741356Seschrock 157511022STom.Erickson@Sun.COM static boolean_t 157611022STom.Erickson@Sun.COM zfs_is_recvd_props_mode(zfs_handle_t *zhp) 157711022STom.Erickson@Sun.COM { 157811022STom.Erickson@Sun.COM return (zhp->zfs_props == zhp->zfs_recvd_props); 157911022STom.Erickson@Sun.COM } 158011022STom.Erickson@Sun.COM 158111022STom.Erickson@Sun.COM static void 158211022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 158311022STom.Erickson@Sun.COM { 158411022STom.Erickson@Sun.COM *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 158511022STom.Erickson@Sun.COM zhp->zfs_props = zhp->zfs_recvd_props; 158611022STom.Erickson@Sun.COM } 158711022STom.Erickson@Sun.COM 158811022STom.Erickson@Sun.COM static void 158911022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 159011022STom.Erickson@Sun.COM { 159111022STom.Erickson@Sun.COM zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 159211022STom.Erickson@Sun.COM *cookie = 0; 159311022STom.Erickson@Sun.COM } 159411022STom.Erickson@Sun.COM 15951356Seschrock /* 1596789Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1597789Sahrens * zfs_prop_get_int() are built using this interface. 1598789Sahrens * 1599789Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1600789Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1601789Sahrens * If they differ from the on-disk values, report the current values and mark 1602789Sahrens * the source "temporary". 1603789Sahrens */ 16042082Seschrock static int 16055094Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 16062082Seschrock char **source, uint64_t *val) 1607789Sahrens { 16085147Srm160521 zfs_cmd_t zc = { 0 }; 16095592Stimh nvlist_t *zplprops = NULL; 1610789Sahrens struct mnttab mnt; 16113265Sahrens char *mntopt_on = NULL; 16123265Sahrens char *mntopt_off = NULL; 161311022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1614789Sahrens 1615789Sahrens *source = NULL; 1616789Sahrens 16173265Sahrens switch (prop) { 16183265Sahrens case ZFS_PROP_ATIME: 16193265Sahrens mntopt_on = MNTOPT_ATIME; 16203265Sahrens mntopt_off = MNTOPT_NOATIME; 16213265Sahrens break; 16223265Sahrens 16233265Sahrens case ZFS_PROP_DEVICES: 16243265Sahrens mntopt_on = MNTOPT_DEVICES; 16253265Sahrens mntopt_off = MNTOPT_NODEVICES; 16263265Sahrens break; 16273265Sahrens 16283265Sahrens case ZFS_PROP_EXEC: 16293265Sahrens mntopt_on = MNTOPT_EXEC; 16303265Sahrens mntopt_off = MNTOPT_NOEXEC; 16313265Sahrens break; 16323265Sahrens 16333265Sahrens case ZFS_PROP_READONLY: 16343265Sahrens mntopt_on = MNTOPT_RO; 16353265Sahrens mntopt_off = MNTOPT_RW; 16363265Sahrens break; 16373265Sahrens 16383265Sahrens case ZFS_PROP_SETUID: 16393265Sahrens mntopt_on = MNTOPT_SETUID; 16403265Sahrens mntopt_off = MNTOPT_NOSETUID; 16413265Sahrens break; 16423265Sahrens 16433265Sahrens case ZFS_PROP_XATTR: 16443265Sahrens mntopt_on = MNTOPT_XATTR; 16453265Sahrens mntopt_off = MNTOPT_NOXATTR; 16463265Sahrens break; 16475331Samw 16485331Samw case ZFS_PROP_NBMAND: 16495331Samw mntopt_on = MNTOPT_NBMAND; 16505331Samw mntopt_off = MNTOPT_NONBMAND; 16515331Samw break; 16523265Sahrens } 16533265Sahrens 16542474Seschrock /* 16552474Seschrock * Because looking up the mount options is potentially expensive 16562474Seschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16572474Seschrock * we're looking up a property which requires its presence. 16582474Seschrock */ 16592474Seschrock if (!zhp->zfs_mntcheck && 16603265Sahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 16618228SEric.Taylor@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 16628228SEric.Taylor@Sun.COM struct mnttab entry; 16638228SEric.Taylor@Sun.COM 16648228SEric.Taylor@Sun.COM if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 16658228SEric.Taylor@Sun.COM zhp->zfs_mntopts = zfs_strdup(hdl, 16663265Sahrens entry.mnt_mntopts); 16673265Sahrens if (zhp->zfs_mntopts == NULL) 16683265Sahrens return (-1); 16693265Sahrens } 16702474Seschrock 16712474Seschrock zhp->zfs_mntcheck = B_TRUE; 16722474Seschrock } 16732474Seschrock 1674789Sahrens if (zhp->zfs_mntopts == NULL) 1675789Sahrens mnt.mnt_mntopts = ""; 1676789Sahrens else 1677789Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1678789Sahrens 1679789Sahrens switch (prop) { 1680789Sahrens case ZFS_PROP_ATIME: 16813265Sahrens case ZFS_PROP_DEVICES: 16823265Sahrens case ZFS_PROP_EXEC: 16833265Sahrens case ZFS_PROP_READONLY: 16843265Sahrens case ZFS_PROP_SETUID: 16853265Sahrens case ZFS_PROP_XATTR: 16865331Samw case ZFS_PROP_NBMAND: 16872082Seschrock *val = getprop_uint64(zhp, prop, source); 16882082Seschrock 168911022STom.Erickson@Sun.COM if (received) 169011022STom.Erickson@Sun.COM break; 169111022STom.Erickson@Sun.COM 16923265Sahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 16932082Seschrock *val = B_TRUE; 1694789Sahrens if (src) 16955094Slling *src = ZPROP_SRC_TEMPORARY; 16963265Sahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 16972082Seschrock *val = B_FALSE; 1698789Sahrens if (src) 16995094Slling *src = ZPROP_SRC_TEMPORARY; 1700789Sahrens } 17012082Seschrock break; 1702789Sahrens 17033265Sahrens case ZFS_PROP_CANMOUNT: 1704*11497SMark.Musante@Sun.COM case ZFS_PROP_VOLSIZE: 1705789Sahrens case ZFS_PROP_QUOTA: 17065378Sck153898 case ZFS_PROP_REFQUOTA: 1707789Sahrens case ZFS_PROP_RESERVATION: 17085378Sck153898 case ZFS_PROP_REFRESERVATION: 17092885Sahrens *val = getprop_uint64(zhp, prop, source); 171011022STom.Erickson@Sun.COM 171111022STom.Erickson@Sun.COM if (*source == NULL) { 171211022STom.Erickson@Sun.COM /* not default, must be local */ 1713789Sahrens *source = zhp->zfs_name; 171411022STom.Erickson@Sun.COM } 17152082Seschrock break; 1716789Sahrens 1717789Sahrens case ZFS_PROP_MOUNTED: 17182082Seschrock *val = (zhp->zfs_mntopts != NULL); 17192082Seschrock break; 1720789Sahrens 17213377Seschrock case ZFS_PROP_NUMCLONES: 17223377Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 17233377Seschrock break; 17243377Seschrock 17255147Srm160521 case ZFS_PROP_VERSION: 17265498Stimh case ZFS_PROP_NORMALIZE: 17275498Stimh case ZFS_PROP_UTF8ONLY: 17285498Stimh case ZFS_PROP_CASE: 17295498Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 17305498Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17315473Srm160521 return (-1); 17325147Srm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 17335498Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 17345498Stimh zcmd_free_nvlists(&zc); 173510204SMatthew.Ahrens@Sun.COM return (-1); 17365147Srm160521 } 17375498Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 17385498Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 17395498Stimh val) != 0) { 17405498Stimh zcmd_free_nvlists(&zc); 174110204SMatthew.Ahrens@Sun.COM return (-1); 17425498Stimh } 17435592Stimh if (zplprops) 17445592Stimh nvlist_free(zplprops); 17455498Stimh zcmd_free_nvlists(&zc); 17465147Srm160521 break; 17475147Srm160521 1748789Sahrens default: 17494577Sahrens switch (zfs_prop_get_type(prop)) { 17504787Sahrens case PROP_TYPE_NUMBER: 17514787Sahrens case PROP_TYPE_INDEX: 17524577Sahrens *val = getprop_uint64(zhp, prop, source); 17537390SMatthew.Ahrens@Sun.COM /* 17549396SMatthew.Ahrens@Sun.COM * If we tried to use a default value for a 17557390SMatthew.Ahrens@Sun.COM * readonly property, it means that it was not 175611080STom.Erickson@Sun.COM * present. 17577390SMatthew.Ahrens@Sun.COM */ 17587390SMatthew.Ahrens@Sun.COM if (zfs_prop_readonly(prop) && 175911080STom.Erickson@Sun.COM *source != NULL && (*source)[0] == '\0') { 176011080STom.Erickson@Sun.COM *source = NULL; 17617390SMatthew.Ahrens@Sun.COM } 17624577Sahrens break; 17634577Sahrens 17644787Sahrens case PROP_TYPE_STRING: 17654577Sahrens default: 17664577Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 17674577Sahrens "cannot get non-numeric property")); 17684577Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 17694577Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 17704577Sahrens } 1771789Sahrens } 1772789Sahrens 1773789Sahrens return (0); 1774789Sahrens } 1775789Sahrens 1776789Sahrens /* 1777789Sahrens * Calculate the source type, given the raw source string. 1778789Sahrens */ 1779789Sahrens static void 17805094Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1781789Sahrens char *statbuf, size_t statlen) 1782789Sahrens { 17835094Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1784789Sahrens return; 1785789Sahrens 1786789Sahrens if (source == NULL) { 17875094Slling *srctype = ZPROP_SRC_NONE; 1788789Sahrens } else if (source[0] == '\0') { 17895094Slling *srctype = ZPROP_SRC_DEFAULT; 179011022STom.Erickson@Sun.COM } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 179111022STom.Erickson@Sun.COM *srctype = ZPROP_SRC_RECEIVED; 1792789Sahrens } else { 1793789Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 17945094Slling *srctype = ZPROP_SRC_LOCAL; 1795789Sahrens } else { 1796789Sahrens (void) strlcpy(statbuf, source, statlen); 17975094Slling *srctype = ZPROP_SRC_INHERITED; 1798789Sahrens } 1799789Sahrens } 1800789Sahrens 1801789Sahrens } 1802789Sahrens 180311022STom.Erickson@Sun.COM int 180411022STom.Erickson@Sun.COM zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 180511022STom.Erickson@Sun.COM size_t proplen, boolean_t literal) 180611022STom.Erickson@Sun.COM { 180711022STom.Erickson@Sun.COM zfs_prop_t prop; 180811022STom.Erickson@Sun.COM int err = 0; 180911022STom.Erickson@Sun.COM 181011022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 181111022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 181211022STom.Erickson@Sun.COM return (-1); 181311022STom.Erickson@Sun.COM 181411022STom.Erickson@Sun.COM prop = zfs_name_to_prop(propname); 181511022STom.Erickson@Sun.COM 181611022STom.Erickson@Sun.COM if (prop != ZPROP_INVAL) { 181711022STom.Erickson@Sun.COM uint64_t cookie; 181811022STom.Erickson@Sun.COM if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 181911022STom.Erickson@Sun.COM return (-1); 182011022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zhp, &cookie); 182111022STom.Erickson@Sun.COM err = zfs_prop_get(zhp, prop, propbuf, proplen, 182211022STom.Erickson@Sun.COM NULL, NULL, 0, literal); 182311022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zhp, &cookie); 182411022STom.Erickson@Sun.COM } else if (zfs_prop_userquota(propname)) { 182511022STom.Erickson@Sun.COM return (-1); 182611022STom.Erickson@Sun.COM } else { 182711022STom.Erickson@Sun.COM nvlist_t *propval; 182811022STom.Erickson@Sun.COM char *recvdval; 182911022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 183011022STom.Erickson@Sun.COM propname, &propval) != 0) 183111022STom.Erickson@Sun.COM return (-1); 183211022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, ZPROP_VALUE, 183311022STom.Erickson@Sun.COM &recvdval) == 0); 183411022STom.Erickson@Sun.COM (void) strlcpy(propbuf, recvdval, proplen); 183511022STom.Erickson@Sun.COM } 183611022STom.Erickson@Sun.COM 183711022STom.Erickson@Sun.COM return (err == 0 ? 0 : -1); 183811022STom.Erickson@Sun.COM } 183911022STom.Erickson@Sun.COM 1840789Sahrens /* 1841789Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1842789Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1843789Sahrens * human-readable form. 1844789Sahrens * 1845789Sahrens * Returns 0 on success, or -1 on error. 1846789Sahrens */ 1847789Sahrens int 1848789Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 18495094Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1850789Sahrens { 1851789Sahrens char *source = NULL; 1852789Sahrens uint64_t val; 1853789Sahrens char *str; 18542676Seschrock const char *strval; 185511022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1856789Sahrens 1857789Sahrens /* 1858789Sahrens * Check to see if this property applies to our object 1859789Sahrens */ 1860789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1861789Sahrens return (-1); 1862789Sahrens 186311022STom.Erickson@Sun.COM if (received && zfs_prop_readonly(prop)) 186411022STom.Erickson@Sun.COM return (-1); 186511022STom.Erickson@Sun.COM 1866789Sahrens if (src) 18675094Slling *src = ZPROP_SRC_NONE; 1868789Sahrens 1869789Sahrens switch (prop) { 1870789Sahrens case ZFS_PROP_CREATION: 1871789Sahrens /* 1872789Sahrens * 'creation' is a time_t stored in the statistics. We convert 1873789Sahrens * this into a string unless 'literal' is specified. 1874789Sahrens */ 1875789Sahrens { 18762885Sahrens val = getprop_uint64(zhp, prop, &source); 18772885Sahrens time_t time = (time_t)val; 1878789Sahrens struct tm t; 1879789Sahrens 1880789Sahrens if (literal || 1881789Sahrens localtime_r(&time, &t) == NULL || 1882789Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1883789Sahrens &t) == 0) 18842885Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1885789Sahrens } 1886789Sahrens break; 1887789Sahrens 1888789Sahrens case ZFS_PROP_MOUNTPOINT: 1889789Sahrens /* 1890789Sahrens * Getting the precise mountpoint can be tricky. 1891789Sahrens * 1892789Sahrens * - for 'none' or 'legacy', return those values. 1893789Sahrens * - for inherited mountpoints, we want to take everything 1894789Sahrens * after our ancestor and append it to the inherited value. 1895789Sahrens * 1896789Sahrens * If the pool has an alternate root, we want to prepend that 1897789Sahrens * root to any values we return. 1898789Sahrens */ 18996865Srm160521 19001356Seschrock str = getprop_string(zhp, prop, &source); 19011356Seschrock 19026612Sgw25295 if (str[0] == '/') { 19036865Srm160521 char buf[MAXPATHLEN]; 19046865Srm160521 char *root = buf; 19051356Seschrock const char *relpath = zhp->zfs_name + strlen(source); 1906789Sahrens 1907789Sahrens if (relpath[0] == '/') 1908789Sahrens relpath++; 19096612Sgw25295 19106865Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 19116865Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 19126865Srm160521 (strcmp(root, "-") == 0)) 19136865Srm160521 root[0] = '\0'; 19146612Sgw25295 /* 19156612Sgw25295 * Special case an alternate root of '/'. This will 19166612Sgw25295 * avoid having multiple leading slashes in the 19176612Sgw25295 * mountpoint path. 19186612Sgw25295 */ 19196612Sgw25295 if (strcmp(root, "/") == 0) 19206612Sgw25295 root++; 19216612Sgw25295 19226612Sgw25295 /* 19236612Sgw25295 * If the mountpoint is '/' then skip over this 19246612Sgw25295 * if we are obtaining either an alternate root or 19256612Sgw25295 * an inherited mountpoint. 19266612Sgw25295 */ 19276612Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 19286612Sgw25295 relpath[0] != '\0')) 19291356Seschrock str++; 1930789Sahrens 1931789Sahrens if (relpath[0] == '\0') 1932789Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19331356Seschrock root, str); 1934789Sahrens else 1935789Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19361356Seschrock root, str, relpath[0] == '@' ? "" : "/", 1937789Sahrens relpath); 1938789Sahrens } else { 1939789Sahrens /* 'legacy' or 'none' */ 19401356Seschrock (void) strlcpy(propbuf, str, proplen); 1941789Sahrens } 1942789Sahrens 1943789Sahrens break; 1944789Sahrens 1945789Sahrens case ZFS_PROP_ORIGIN: 19462885Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1947789Sahrens proplen); 1948789Sahrens /* 1949789Sahrens * If there is no parent at all, return failure to indicate that 1950789Sahrens * it doesn't apply to this dataset. 1951789Sahrens */ 1952789Sahrens if (propbuf[0] == '\0') 1953789Sahrens return (-1); 1954789Sahrens break; 1955789Sahrens 1956789Sahrens case ZFS_PROP_QUOTA: 19575378Sck153898 case ZFS_PROP_REFQUOTA: 1958789Sahrens case ZFS_PROP_RESERVATION: 19595378Sck153898 case ZFS_PROP_REFRESERVATION: 19605378Sck153898 19612082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19622082Seschrock return (-1); 1963789Sahrens 1964789Sahrens /* 1965789Sahrens * If quota or reservation is 0, we translate this into 'none' 1966789Sahrens * (unless literal is set), and indicate that it's the default 1967789Sahrens * value. Otherwise, we print the number nicely and indicate 1968789Sahrens * that its set locally. 1969789Sahrens */ 1970789Sahrens if (val == 0) { 1971789Sahrens if (literal) 1972789Sahrens (void) strlcpy(propbuf, "0", proplen); 1973789Sahrens else 1974789Sahrens (void) strlcpy(propbuf, "none", proplen); 1975789Sahrens } else { 1976789Sahrens if (literal) 19772856Snd150628 (void) snprintf(propbuf, proplen, "%llu", 19783912Slling (u_longlong_t)val); 1979789Sahrens else 1980789Sahrens zfs_nicenum(val, propbuf, proplen); 1981789Sahrens } 1982789Sahrens break; 1983789Sahrens 1984789Sahrens case ZFS_PROP_COMPRESSRATIO: 19852082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19862082Seschrock return (-1); 198710922SJeff.Bonwick@Sun.COM (void) snprintf(propbuf, proplen, "%llu.%02llux", 198810922SJeff.Bonwick@Sun.COM (u_longlong_t)(val / 100), 198910922SJeff.Bonwick@Sun.COM (u_longlong_t)(val % 100)); 1990789Sahrens break; 1991789Sahrens 1992789Sahrens case ZFS_PROP_TYPE: 1993789Sahrens switch (zhp->zfs_type) { 1994789Sahrens case ZFS_TYPE_FILESYSTEM: 1995789Sahrens str = "filesystem"; 1996789Sahrens break; 1997789Sahrens case ZFS_TYPE_VOLUME: 1998789Sahrens str = "volume"; 1999789Sahrens break; 2000789Sahrens case ZFS_TYPE_SNAPSHOT: 2001789Sahrens str = "snapshot"; 2002789Sahrens break; 2003789Sahrens default: 20042082Seschrock abort(); 2005789Sahrens } 2006789Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2007789Sahrens break; 2008789Sahrens 2009789Sahrens case ZFS_PROP_MOUNTED: 2010789Sahrens /* 2011789Sahrens * The 'mounted' property is a pseudo-property that described 2012789Sahrens * whether the filesystem is currently mounted. Even though 2013789Sahrens * it's a boolean value, the typical values of "on" and "off" 2014789Sahrens * don't make sense, so we translate to "yes" and "no". 2015789Sahrens */ 20162082Seschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 20172082Seschrock src, &source, &val) != 0) 20182082Seschrock return (-1); 20192082Seschrock if (val) 2020789Sahrens (void) strlcpy(propbuf, "yes", proplen); 2021789Sahrens else 2022789Sahrens (void) strlcpy(propbuf, "no", proplen); 2023789Sahrens break; 2024789Sahrens 2025789Sahrens case ZFS_PROP_NAME: 2026789Sahrens /* 2027789Sahrens * The 'name' property is a pseudo-property derived from the 2028789Sahrens * dataset name. It is presented as a real property to simplify 2029789Sahrens * consumers. 2030789Sahrens */ 2031789Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2032789Sahrens break; 2033789Sahrens 203410972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 203510972SRic.Aleshire@Sun.COM { 203610972SRic.Aleshire@Sun.COM m_label_t *new_sl = NULL; 203710972SRic.Aleshire@Sun.COM char *ascii = NULL; /* human readable label */ 203810972SRic.Aleshire@Sun.COM 203910972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, 204010972SRic.Aleshire@Sun.COM getprop_string(zhp, prop, &source), proplen); 204110972SRic.Aleshire@Sun.COM 204210972SRic.Aleshire@Sun.COM if (literal || (strcasecmp(propbuf, 204310972SRic.Aleshire@Sun.COM ZFS_MLSLABEL_DEFAULT) == 0)) 204410972SRic.Aleshire@Sun.COM break; 204510972SRic.Aleshire@Sun.COM 204610972SRic.Aleshire@Sun.COM /* 204710972SRic.Aleshire@Sun.COM * Try to translate the internal hex string to 204810972SRic.Aleshire@Sun.COM * human-readable output. If there are any 204910972SRic.Aleshire@Sun.COM * problems just use the hex string. 205010972SRic.Aleshire@Sun.COM */ 205110972SRic.Aleshire@Sun.COM 205210972SRic.Aleshire@Sun.COM if (str_to_label(propbuf, &new_sl, MAC_LABEL, 205310972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1) { 205410972SRic.Aleshire@Sun.COM m_label_free(new_sl); 205510972SRic.Aleshire@Sun.COM break; 205610972SRic.Aleshire@Sun.COM } 205710972SRic.Aleshire@Sun.COM 205810972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &ascii, M_LABEL, 205910972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 206010972SRic.Aleshire@Sun.COM if (ascii) 206110972SRic.Aleshire@Sun.COM free(ascii); 206210972SRic.Aleshire@Sun.COM m_label_free(new_sl); 206310972SRic.Aleshire@Sun.COM break; 206410972SRic.Aleshire@Sun.COM } 206510972SRic.Aleshire@Sun.COM m_label_free(new_sl); 206610972SRic.Aleshire@Sun.COM 206710972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, ascii, proplen); 206810972SRic.Aleshire@Sun.COM free(ascii); 206910972SRic.Aleshire@Sun.COM } 207010972SRic.Aleshire@Sun.COM break; 207110972SRic.Aleshire@Sun.COM 2072789Sahrens default: 20734577Sahrens switch (zfs_prop_get_type(prop)) { 20744787Sahrens case PROP_TYPE_NUMBER: 20754577Sahrens if (get_numeric_property(zhp, prop, src, 20764577Sahrens &source, &val) != 0) 20774577Sahrens return (-1); 20784577Sahrens if (literal) 20794577Sahrens (void) snprintf(propbuf, proplen, "%llu", 20804577Sahrens (u_longlong_t)val); 20814577Sahrens else 20824577Sahrens zfs_nicenum(val, propbuf, proplen); 20834577Sahrens break; 20844577Sahrens 20854787Sahrens case PROP_TYPE_STRING: 20864577Sahrens (void) strlcpy(propbuf, 20874577Sahrens getprop_string(zhp, prop, &source), proplen); 20884577Sahrens break; 20894577Sahrens 20904787Sahrens case PROP_TYPE_INDEX: 20914861Sahrens if (get_numeric_property(zhp, prop, src, 20924861Sahrens &source, &val) != 0) 20934861Sahrens return (-1); 20944861Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 20954577Sahrens return (-1); 20964577Sahrens (void) strlcpy(propbuf, strval, proplen); 20974577Sahrens break; 20984577Sahrens 20994577Sahrens default: 21004577Sahrens abort(); 21014577Sahrens } 2102789Sahrens } 2103789Sahrens 2104789Sahrens get_source(zhp, src, source, statbuf, statlen); 2105789Sahrens 2106789Sahrens return (0); 2107789Sahrens } 2108789Sahrens 2109789Sahrens /* 2110789Sahrens * Utility function to get the given numeric property. Does no validation that 2111789Sahrens * the given property is the appropriate type; should only be used with 2112789Sahrens * hard-coded property types. 2113789Sahrens */ 2114789Sahrens uint64_t 2115789Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2116789Sahrens { 2117789Sahrens char *source; 21182082Seschrock uint64_t val; 21192082Seschrock 21205367Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 21212082Seschrock 21222082Seschrock return (val); 2123789Sahrens } 2124789Sahrens 21255713Srm160521 int 21265713Srm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21275713Srm160521 { 21285713Srm160521 char buf[64]; 21295713Srm160521 21309396SMatthew.Ahrens@Sun.COM (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21315713Srm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21325713Srm160521 } 21335713Srm160521 2134789Sahrens /* 2135789Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2136789Sahrens */ 2137789Sahrens int 2138789Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 21395094Slling zprop_source_t *src, char *statbuf, size_t statlen) 2140789Sahrens { 2141789Sahrens char *source; 2142789Sahrens 2143789Sahrens /* 2144789Sahrens * Check to see if this property applies to our object 2145789Sahrens */ 21464849Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 21473237Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 21482082Seschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 21492082Seschrock zfs_prop_to_name(prop))); 21504849Sahrens } 2151789Sahrens 2152789Sahrens if (src) 21535094Slling *src = ZPROP_SRC_NONE; 2154789Sahrens 21552082Seschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 21562082Seschrock return (-1); 2157789Sahrens 2158789Sahrens get_source(zhp, src, source, statbuf, statlen); 2159789Sahrens 2160789Sahrens return (0); 2161789Sahrens } 2162789Sahrens 21639396SMatthew.Ahrens@Sun.COM static int 21649396SMatthew.Ahrens@Sun.COM idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 21659396SMatthew.Ahrens@Sun.COM char **domainp, idmap_rid_t *ridp) 21669396SMatthew.Ahrens@Sun.COM { 21679396SMatthew.Ahrens@Sun.COM idmap_handle_t *idmap_hdl = NULL; 21689396SMatthew.Ahrens@Sun.COM idmap_get_handle_t *get_hdl = NULL; 21699396SMatthew.Ahrens@Sun.COM idmap_stat status; 21709396SMatthew.Ahrens@Sun.COM int err = EINVAL; 21719396SMatthew.Ahrens@Sun.COM 21729396SMatthew.Ahrens@Sun.COM if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 21739396SMatthew.Ahrens@Sun.COM goto out; 21749396SMatthew.Ahrens@Sun.COM if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 21759396SMatthew.Ahrens@Sun.COM goto out; 21769396SMatthew.Ahrens@Sun.COM 21779396SMatthew.Ahrens@Sun.COM if (isuser) { 21789396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbyuid(get_hdl, id, 21799396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21809396SMatthew.Ahrens@Sun.COM } else { 21819396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbygid(get_hdl, id, 21829396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 21839396SMatthew.Ahrens@Sun.COM } 21849396SMatthew.Ahrens@Sun.COM if (err == IDMAP_SUCCESS && 21859396SMatthew.Ahrens@Sun.COM idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 21869396SMatthew.Ahrens@Sun.COM status == IDMAP_SUCCESS) 21879396SMatthew.Ahrens@Sun.COM err = 0; 21889396SMatthew.Ahrens@Sun.COM else 21899396SMatthew.Ahrens@Sun.COM err = EINVAL; 21909396SMatthew.Ahrens@Sun.COM out: 21919396SMatthew.Ahrens@Sun.COM if (get_hdl) 21929396SMatthew.Ahrens@Sun.COM idmap_get_destroy(get_hdl); 21939396SMatthew.Ahrens@Sun.COM if (idmap_hdl) 21949396SMatthew.Ahrens@Sun.COM (void) idmap_fini(idmap_hdl); 21959396SMatthew.Ahrens@Sun.COM return (err); 21969396SMatthew.Ahrens@Sun.COM } 21979396SMatthew.Ahrens@Sun.COM 21989396SMatthew.Ahrens@Sun.COM /* 21999396SMatthew.Ahrens@Sun.COM * convert the propname into parameters needed by kernel 22009396SMatthew.Ahrens@Sun.COM * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 22019396SMatthew.Ahrens@Sun.COM * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 22029396SMatthew.Ahrens@Sun.COM */ 22039396SMatthew.Ahrens@Sun.COM static int 22049396SMatthew.Ahrens@Sun.COM userquota_propname_decode(const char *propname, boolean_t zoned, 22059396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 22069396SMatthew.Ahrens@Sun.COM { 22079396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t type; 22089396SMatthew.Ahrens@Sun.COM char *cp, *end; 220910160SMatthew.Ahrens@Sun.COM char *numericsid = NULL; 22109396SMatthew.Ahrens@Sun.COM boolean_t isuser; 22119396SMatthew.Ahrens@Sun.COM 22129396SMatthew.Ahrens@Sun.COM domain[0] = '\0'; 22139396SMatthew.Ahrens@Sun.COM 22149396SMatthew.Ahrens@Sun.COM /* Figure out the property type ({user|group}{quota|space}) */ 22159396SMatthew.Ahrens@Sun.COM for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 22169396SMatthew.Ahrens@Sun.COM if (strncmp(propname, zfs_userquota_prop_prefixes[type], 22179396SMatthew.Ahrens@Sun.COM strlen(zfs_userquota_prop_prefixes[type])) == 0) 22189396SMatthew.Ahrens@Sun.COM break; 22199396SMatthew.Ahrens@Sun.COM } 22209396SMatthew.Ahrens@Sun.COM if (type == ZFS_NUM_USERQUOTA_PROPS) 22219396SMatthew.Ahrens@Sun.COM return (EINVAL); 22229396SMatthew.Ahrens@Sun.COM *typep = type; 22239396SMatthew.Ahrens@Sun.COM 22249396SMatthew.Ahrens@Sun.COM isuser = (type == ZFS_PROP_USERQUOTA || 22259396SMatthew.Ahrens@Sun.COM type == ZFS_PROP_USERUSED); 22269396SMatthew.Ahrens@Sun.COM 22279396SMatthew.Ahrens@Sun.COM cp = strchr(propname, '@') + 1; 22289396SMatthew.Ahrens@Sun.COM 22299396SMatthew.Ahrens@Sun.COM if (strchr(cp, '@')) { 22309396SMatthew.Ahrens@Sun.COM /* 22319396SMatthew.Ahrens@Sun.COM * It's a SID name (eg "user@domain") that needs to be 223210160SMatthew.Ahrens@Sun.COM * turned into S-1-domainID-RID. 22339396SMatthew.Ahrens@Sun.COM */ 223410160SMatthew.Ahrens@Sun.COM directory_error_t e; 22359396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22369396SMatthew.Ahrens@Sun.COM return (ENOENT); 223710160SMatthew.Ahrens@Sun.COM if (isuser) { 223810160SMatthew.Ahrens@Sun.COM e = directory_sid_from_user_name(NULL, 223910160SMatthew.Ahrens@Sun.COM cp, &numericsid); 224010160SMatthew.Ahrens@Sun.COM } else { 224110160SMatthew.Ahrens@Sun.COM e = directory_sid_from_group_name(NULL, 224210160SMatthew.Ahrens@Sun.COM cp, &numericsid); 224310160SMatthew.Ahrens@Sun.COM } 224410160SMatthew.Ahrens@Sun.COM if (e != NULL) { 224510160SMatthew.Ahrens@Sun.COM directory_error_free(e); 22469396SMatthew.Ahrens@Sun.COM return (ENOENT); 224710160SMatthew.Ahrens@Sun.COM } 224810160SMatthew.Ahrens@Sun.COM if (numericsid == NULL) 224910160SMatthew.Ahrens@Sun.COM return (ENOENT); 225010160SMatthew.Ahrens@Sun.COM cp = numericsid; 225110160SMatthew.Ahrens@Sun.COM /* will be further decoded below */ 225210160SMatthew.Ahrens@Sun.COM } 225310160SMatthew.Ahrens@Sun.COM 225410160SMatthew.Ahrens@Sun.COM if (strncmp(cp, "S-1-", 4) == 0) { 22559396SMatthew.Ahrens@Sun.COM /* It's a numeric SID (eg "S-1-234-567-89") */ 225610160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, cp, domainlen); 22579396SMatthew.Ahrens@Sun.COM cp = strrchr(domain, '-'); 22589396SMatthew.Ahrens@Sun.COM *cp = '\0'; 22599396SMatthew.Ahrens@Sun.COM cp++; 22609396SMatthew.Ahrens@Sun.COM 22619396SMatthew.Ahrens@Sun.COM errno = 0; 22629396SMatthew.Ahrens@Sun.COM *ridp = strtoull(cp, &end, 10); 226310160SMatthew.Ahrens@Sun.COM if (numericsid) { 226410160SMatthew.Ahrens@Sun.COM free(numericsid); 226510160SMatthew.Ahrens@Sun.COM numericsid = NULL; 226610160SMatthew.Ahrens@Sun.COM } 22679688SMatthew.Ahrens@Sun.COM if (errno != 0 || *end != '\0') 22689396SMatthew.Ahrens@Sun.COM return (EINVAL); 22699396SMatthew.Ahrens@Sun.COM } else if (!isdigit(*cp)) { 22709396SMatthew.Ahrens@Sun.COM /* 22719396SMatthew.Ahrens@Sun.COM * It's a user/group name (eg "user") that needs to be 22729396SMatthew.Ahrens@Sun.COM * turned into a uid/gid 22739396SMatthew.Ahrens@Sun.COM */ 22749396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22759396SMatthew.Ahrens@Sun.COM return (ENOENT); 22769396SMatthew.Ahrens@Sun.COM if (isuser) { 22779396SMatthew.Ahrens@Sun.COM struct passwd *pw; 22789396SMatthew.Ahrens@Sun.COM pw = getpwnam(cp); 22799396SMatthew.Ahrens@Sun.COM if (pw == NULL) 22809396SMatthew.Ahrens@Sun.COM return (ENOENT); 22819396SMatthew.Ahrens@Sun.COM *ridp = pw->pw_uid; 22829396SMatthew.Ahrens@Sun.COM } else { 22839396SMatthew.Ahrens@Sun.COM struct group *gr; 22849396SMatthew.Ahrens@Sun.COM gr = getgrnam(cp); 22859396SMatthew.Ahrens@Sun.COM if (gr == NULL) 22869396SMatthew.Ahrens@Sun.COM return (ENOENT); 22879396SMatthew.Ahrens@Sun.COM *ridp = gr->gr_gid; 22889396SMatthew.Ahrens@Sun.COM } 22899396SMatthew.Ahrens@Sun.COM } else { 22909396SMatthew.Ahrens@Sun.COM /* It's a user/group ID (eg "12345"). */ 22919396SMatthew.Ahrens@Sun.COM uid_t id = strtoul(cp, &end, 10); 22929396SMatthew.Ahrens@Sun.COM idmap_rid_t rid; 22939396SMatthew.Ahrens@Sun.COM char *mapdomain; 22949396SMatthew.Ahrens@Sun.COM 22959396SMatthew.Ahrens@Sun.COM if (*end != '\0') 22969396SMatthew.Ahrens@Sun.COM return (EINVAL); 22979396SMatthew.Ahrens@Sun.COM if (id > MAXUID) { 22989396SMatthew.Ahrens@Sun.COM /* It's an ephemeral ID. */ 22999396SMatthew.Ahrens@Sun.COM if (idmap_id_to_numeric_domain_rid(id, isuser, 23009396SMatthew.Ahrens@Sun.COM &mapdomain, &rid) != 0) 23019396SMatthew.Ahrens@Sun.COM return (ENOENT); 230210160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, mapdomain, domainlen); 23039396SMatthew.Ahrens@Sun.COM *ridp = rid; 23049396SMatthew.Ahrens@Sun.COM } else { 23059396SMatthew.Ahrens@Sun.COM *ridp = id; 23069396SMatthew.Ahrens@Sun.COM } 23079396SMatthew.Ahrens@Sun.COM } 23089396SMatthew.Ahrens@Sun.COM 230910160SMatthew.Ahrens@Sun.COM ASSERT3P(numericsid, ==, NULL); 23109396SMatthew.Ahrens@Sun.COM return (0); 23119396SMatthew.Ahrens@Sun.COM } 23129396SMatthew.Ahrens@Sun.COM 23139469SLin.Ling@Sun.COM static int 23149469SLin.Ling@Sun.COM zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 23159469SLin.Ling@Sun.COM uint64_t *propvalue, zfs_userquota_prop_t *typep) 23169396SMatthew.Ahrens@Sun.COM { 23179396SMatthew.Ahrens@Sun.COM int err; 23189396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 23199396SMatthew.Ahrens@Sun.COM 23209396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 23219396SMatthew.Ahrens@Sun.COM 23229396SMatthew.Ahrens@Sun.COM err = userquota_propname_decode(propname, 23239396SMatthew.Ahrens@Sun.COM zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 23249469SLin.Ling@Sun.COM typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 23259469SLin.Ling@Sun.COM zc.zc_objset_type = *typep; 23269396SMatthew.Ahrens@Sun.COM if (err) 23279396SMatthew.Ahrens@Sun.COM return (err); 23289396SMatthew.Ahrens@Sun.COM 23299396SMatthew.Ahrens@Sun.COM err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 23309396SMatthew.Ahrens@Sun.COM if (err) 23319396SMatthew.Ahrens@Sun.COM return (err); 23329396SMatthew.Ahrens@Sun.COM 23339469SLin.Ling@Sun.COM *propvalue = zc.zc_cookie; 23349469SLin.Ling@Sun.COM return (0); 23359469SLin.Ling@Sun.COM } 23369469SLin.Ling@Sun.COM 23379469SLin.Ling@Sun.COM int 23389469SLin.Ling@Sun.COM zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 23399469SLin.Ling@Sun.COM uint64_t *propvalue) 23409469SLin.Ling@Sun.COM { 23419469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23429469SLin.Ling@Sun.COM 23439469SLin.Ling@Sun.COM return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 23449469SLin.Ling@Sun.COM &type)); 23459469SLin.Ling@Sun.COM } 23469469SLin.Ling@Sun.COM 23479469SLin.Ling@Sun.COM int 23489469SLin.Ling@Sun.COM zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 23499469SLin.Ling@Sun.COM char *propbuf, int proplen, boolean_t literal) 23509469SLin.Ling@Sun.COM { 23519469SLin.Ling@Sun.COM int err; 23529469SLin.Ling@Sun.COM uint64_t propvalue; 23539469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23549469SLin.Ling@Sun.COM 23559469SLin.Ling@Sun.COM err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 23569469SLin.Ling@Sun.COM &type); 23579469SLin.Ling@Sun.COM 23589469SLin.Ling@Sun.COM if (err) 23599469SLin.Ling@Sun.COM return (err); 23609469SLin.Ling@Sun.COM 23619396SMatthew.Ahrens@Sun.COM if (literal) { 23629469SLin.Ling@Sun.COM (void) snprintf(propbuf, proplen, "%llu", propvalue); 23639469SLin.Ling@Sun.COM } else if (propvalue == 0 && 23649396SMatthew.Ahrens@Sun.COM (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 23659396SMatthew.Ahrens@Sun.COM (void) strlcpy(propbuf, "none", proplen); 23669396SMatthew.Ahrens@Sun.COM } else { 23679469SLin.Ling@Sun.COM zfs_nicenum(propvalue, propbuf, proplen); 23689396SMatthew.Ahrens@Sun.COM } 23699396SMatthew.Ahrens@Sun.COM return (0); 23709396SMatthew.Ahrens@Sun.COM } 23719396SMatthew.Ahrens@Sun.COM 2372789Sahrens /* 2373789Sahrens * Returns the name of the given zfs handle. 2374789Sahrens */ 2375789Sahrens const char * 2376789Sahrens zfs_get_name(const zfs_handle_t *zhp) 2377789Sahrens { 2378789Sahrens return (zhp->zfs_name); 2379789Sahrens } 2380789Sahrens 2381789Sahrens /* 2382789Sahrens * Returns the type of the given zfs handle. 2383789Sahrens */ 2384789Sahrens zfs_type_t 2385789Sahrens zfs_get_type(const zfs_handle_t *zhp) 2386789Sahrens { 2387789Sahrens return (zhp->zfs_type); 2388789Sahrens } 2389789Sahrens 23908228SEric.Taylor@Sun.COM static int 23918228SEric.Taylor@Sun.COM zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 23928228SEric.Taylor@Sun.COM { 23938228SEric.Taylor@Sun.COM int rc; 23948228SEric.Taylor@Sun.COM uint64_t orig_cookie; 23958228SEric.Taylor@Sun.COM 23968228SEric.Taylor@Sun.COM orig_cookie = zc->zc_cookie; 23978228SEric.Taylor@Sun.COM top: 23988228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 23998228SEric.Taylor@Sun.COM rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 24008228SEric.Taylor@Sun.COM 24018228SEric.Taylor@Sun.COM if (rc == -1) { 24028228SEric.Taylor@Sun.COM switch (errno) { 24038228SEric.Taylor@Sun.COM case ENOMEM: 24048228SEric.Taylor@Sun.COM /* expand nvlist memory and try again */ 24058228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 24068228SEric.Taylor@Sun.COM zcmd_free_nvlists(zc); 24078228SEric.Taylor@Sun.COM return (-1); 24088228SEric.Taylor@Sun.COM } 24098228SEric.Taylor@Sun.COM zc->zc_cookie = orig_cookie; 24108228SEric.Taylor@Sun.COM goto top; 24118228SEric.Taylor@Sun.COM /* 24128228SEric.Taylor@Sun.COM * An errno value of ESRCH indicates normal completion. 24138228SEric.Taylor@Sun.COM * If ENOENT is returned, then the underlying dataset 24148228SEric.Taylor@Sun.COM * has been removed since we obtained the handle. 24158228SEric.Taylor@Sun.COM */ 24168228SEric.Taylor@Sun.COM case ESRCH: 24178228SEric.Taylor@Sun.COM case ENOENT: 24188228SEric.Taylor@Sun.COM rc = 1; 24198228SEric.Taylor@Sun.COM break; 24208228SEric.Taylor@Sun.COM default: 24218228SEric.Taylor@Sun.COM rc = zfs_standard_error(zhp->zfs_hdl, errno, 24228228SEric.Taylor@Sun.COM dgettext(TEXT_DOMAIN, 24238228SEric.Taylor@Sun.COM "cannot iterate filesystems")); 24248228SEric.Taylor@Sun.COM break; 24258228SEric.Taylor@Sun.COM } 24268228SEric.Taylor@Sun.COM } 24278228SEric.Taylor@Sun.COM return (rc); 24288228SEric.Taylor@Sun.COM } 24298228SEric.Taylor@Sun.COM 2430789Sahrens /* 24311356Seschrock * Iterate over all child filesystems 2432789Sahrens */ 2433789Sahrens int 24341356Seschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2435789Sahrens { 2436789Sahrens zfs_cmd_t zc = { 0 }; 2437789Sahrens zfs_handle_t *nzhp; 2438789Sahrens int ret; 2439789Sahrens 24405367Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24415367Sahrens return (0); 24425367Sahrens 24438228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24448228SEric.Taylor@Sun.COM return (-1); 24458228SEric.Taylor@Sun.COM 24468228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 24478228SEric.Taylor@Sun.COM &zc)) == 0) { 2448789Sahrens /* 2449789Sahrens * Silently ignore errors, as the only plausible explanation is 2450789Sahrens * that the pool has since been removed. 2451789Sahrens */ 24528228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24538228SEric.Taylor@Sun.COM &zc)) == NULL) { 2454789Sahrens continue; 24558228SEric.Taylor@Sun.COM } 24568228SEric.Taylor@Sun.COM 24578228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24588228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2459789Sahrens return (ret); 24608228SEric.Taylor@Sun.COM } 2461789Sahrens } 24628228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 24638228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 24641356Seschrock } 24651356Seschrock 24661356Seschrock /* 24671356Seschrock * Iterate over all snapshots 24681356Seschrock */ 24691356Seschrock int 24701356Seschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24711356Seschrock { 24721356Seschrock zfs_cmd_t zc = { 0 }; 24731356Seschrock zfs_handle_t *nzhp; 24741356Seschrock int ret; 2475789Sahrens 24765367Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24775367Sahrens return (0); 24785367Sahrens 24798228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24808228SEric.Taylor@Sun.COM return (-1); 24818228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 24828228SEric.Taylor@Sun.COM &zc)) == 0) { 24838228SEric.Taylor@Sun.COM 24848228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24858228SEric.Taylor@Sun.COM &zc)) == NULL) { 2486789Sahrens continue; 24878228SEric.Taylor@Sun.COM } 24888228SEric.Taylor@Sun.COM 24898228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24908228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2491789Sahrens return (ret); 24928228SEric.Taylor@Sun.COM } 2493789Sahrens } 24948228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 24958228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 2496789Sahrens } 2497789Sahrens 2498789Sahrens /* 24991356Seschrock * Iterate over all children, snapshots and filesystems 25001356Seschrock */ 25011356Seschrock int 25021356Seschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25031356Seschrock { 25041356Seschrock int ret; 25051356Seschrock 25061356Seschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25071356Seschrock return (ret); 25081356Seschrock 25091356Seschrock return (zfs_iter_snapshots(zhp, func, data)); 25101356Seschrock } 25111356Seschrock 25121356Seschrock /* 2513*11497SMark.Musante@Sun.COM * Is one dataset name a child dataset of another? 2514*11497SMark.Musante@Sun.COM * 2515*11497SMark.Musante@Sun.COM * Needs to handle these cases: 2516*11497SMark.Musante@Sun.COM * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 2517*11497SMark.Musante@Sun.COM * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 2518*11497SMark.Musante@Sun.COM * Descendant? No. No. No. Yes. 2519*11497SMark.Musante@Sun.COM */ 2520*11497SMark.Musante@Sun.COM static boolean_t 2521*11497SMark.Musante@Sun.COM is_descendant(const char *ds1, const char *ds2) 2522*11497SMark.Musante@Sun.COM { 2523*11497SMark.Musante@Sun.COM size_t d1len = strlen(ds1); 2524*11497SMark.Musante@Sun.COM 2525*11497SMark.Musante@Sun.COM /* ds2 can't be a descendant if it's smaller */ 2526*11497SMark.Musante@Sun.COM if (strlen(ds2) < d1len) 2527*11497SMark.Musante@Sun.COM return (B_FALSE); 2528*11497SMark.Musante@Sun.COM 2529*11497SMark.Musante@Sun.COM /* otherwise, compare strings and verify that there's a '/' char */ 2530*11497SMark.Musante@Sun.COM return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 2531*11497SMark.Musante@Sun.COM } 2532*11497SMark.Musante@Sun.COM 2533*11497SMark.Musante@Sun.COM /* 2534789Sahrens * Given a complete name, return just the portion that refers to the parent. 2535789Sahrens * Can return NULL if this is a pool. 2536789Sahrens */ 2537789Sahrens static int 2538789Sahrens parent_name(const char *path, char *buf, size_t buflen) 2539789Sahrens { 2540789Sahrens char *loc; 2541789Sahrens 2542789Sahrens if ((loc = strrchr(path, '/')) == NULL) 2543789Sahrens return (-1); 2544789Sahrens 2545789Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2546789Sahrens buf[loc - path] = '\0'; 2547789Sahrens 2548789Sahrens return (0); 2549789Sahrens } 2550789Sahrens 2551789Sahrens /* 25524490Svb160487 * If accept_ancestor is false, then check to make sure that the given path has 25534490Svb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25544490Svb160487 * closest existing ancestor for the given path. In prefixlen return the 25554490Svb160487 * length of already existing prefix of the given path. We also fetch the 25564490Svb160487 * 'zoned' property, which is used to validate property settings when creating 25574490Svb160487 * new datasets. 2558789Sahrens */ 2559789Sahrens static int 25604490Svb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25614490Svb160487 boolean_t accept_ancestor, int *prefixlen) 2562789Sahrens { 2563789Sahrens zfs_cmd_t zc = { 0 }; 2564789Sahrens char parent[ZFS_MAXNAMELEN]; 2565789Sahrens char *slash; 25661356Seschrock zfs_handle_t *zhp; 25672082Seschrock char errbuf[1024]; 2568*11497SMark.Musante@Sun.COM uint64_t is_zoned; 25692082Seschrock 25708269SMark.Musante@Sun.COM (void) snprintf(errbuf, sizeof (errbuf), 25718269SMark.Musante@Sun.COM dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2572789Sahrens 2573789Sahrens /* get parent, and check to see if this is just a pool */ 2574789Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 25752082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25762082Seschrock "missing dataset name")); 25772082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2578789Sahrens } 2579789Sahrens 2580789Sahrens /* check to see if the pool exists */ 2581789Sahrens if ((slash = strchr(parent, '/')) == NULL) 2582789Sahrens slash = parent + strlen(parent); 2583789Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2584789Sahrens zc.zc_name[slash - parent] = '\0'; 25852082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2586789Sahrens errno == ENOENT) { 25872082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25882082Seschrock "no such pool '%s'"), zc.zc_name); 25892082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2590789Sahrens } 2591789Sahrens 2592789Sahrens /* check to see if the parent dataset exists */ 25934490Svb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 25944490Svb160487 if (errno == ENOENT && accept_ancestor) { 25954490Svb160487 /* 25964490Svb160487 * Go deeper to find an ancestor, give up on top level. 25974490Svb160487 */ 25984490Svb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 25994490Svb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26004490Svb160487 "no such pool '%s'"), zc.zc_name); 26014490Svb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26024490Svb160487 } 26034490Svb160487 } else if (errno == ENOENT) { 26042082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26052082Seschrock "parent does not exist")); 26062082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26074490Svb160487 } else 26082082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2609789Sahrens } 2610789Sahrens 2611*11497SMark.Musante@Sun.COM is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2612*11497SMark.Musante@Sun.COM if (zoned != NULL) 2613*11497SMark.Musante@Sun.COM *zoned = is_zoned; 2614*11497SMark.Musante@Sun.COM 2615789Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2616*11497SMark.Musante@Sun.COM if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 26172082Seschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26181356Seschrock zfs_close(zhp); 2619789Sahrens return (-1); 2620789Sahrens } 2621789Sahrens 2622789Sahrens /* make sure parent is a filesystem */ 26231356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 26242082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26252082Seschrock "parent is not a filesystem")); 26262082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26271356Seschrock zfs_close(zhp); 2628789Sahrens return (-1); 2629789Sahrens } 2630789Sahrens 26311356Seschrock zfs_close(zhp); 26324490Svb160487 if (prefixlen != NULL) 26334490Svb160487 *prefixlen = strlen(parent); 26344490Svb160487 return (0); 26354490Svb160487 } 26364490Svb160487 26374490Svb160487 /* 26384490Svb160487 * Finds whether the dataset of the given type(s) exists. 26394490Svb160487 */ 26404490Svb160487 boolean_t 26414490Svb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26424490Svb160487 { 26434490Svb160487 zfs_handle_t *zhp; 26444490Svb160487 26455326Sek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26464490Svb160487 return (B_FALSE); 26474490Svb160487 26484490Svb160487 /* 26494490Svb160487 * Try to get stats for the dataset, which will tell us if it exists. 26504490Svb160487 */ 26514490Svb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26524490Svb160487 int ds_type = zhp->zfs_type; 26534490Svb160487 26544490Svb160487 zfs_close(zhp); 26554490Svb160487 if (types & ds_type) 26564490Svb160487 return (B_TRUE); 26574490Svb160487 } 26584490Svb160487 return (B_FALSE); 26594490Svb160487 } 26604490Svb160487 26614490Svb160487 /* 26625367Sahrens * Given a path to 'target', create all the ancestors between 26635367Sahrens * the prefixlen portion of the path, and the target itself. 26645367Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 26655367Sahrens */ 26665367Sahrens int 26675367Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 26685367Sahrens { 26695367Sahrens zfs_handle_t *h; 26705367Sahrens char *cp; 26715367Sahrens const char *opname; 26725367Sahrens 26735367Sahrens /* make sure prefix exists */ 26745367Sahrens cp = target + prefixlen; 26755367Sahrens if (*cp != '/') { 26765367Sahrens assert(strchr(cp, '/') == NULL); 26775367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26785367Sahrens } else { 26795367Sahrens *cp = '\0'; 26805367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26815367Sahrens *cp = '/'; 26825367Sahrens } 26835367Sahrens if (h == NULL) 26845367Sahrens return (-1); 26855367Sahrens zfs_close(h); 26865367Sahrens 26875367Sahrens /* 26885367Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26895367Sahrens * up to the prefixlen-long one. 26905367Sahrens */ 26915367Sahrens for (cp = target + prefixlen + 1; 26925367Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 26935367Sahrens char *logstr; 26945367Sahrens 26955367Sahrens *cp = '\0'; 26965367Sahrens 26975367Sahrens h = make_dataset_handle(hdl, target); 26985367Sahrens if (h) { 26995367Sahrens /* it already exists, nothing to do here */ 27005367Sahrens zfs_close(h); 27015367Sahrens continue; 27025367Sahrens } 27035367Sahrens 27045367Sahrens logstr = hdl->libzfs_log_str; 27055367Sahrens hdl->libzfs_log_str = NULL; 27065367Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27075367Sahrens NULL) != 0) { 27085367Sahrens hdl->libzfs_log_str = logstr; 27095367Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27105367Sahrens goto ancestorerr; 27115367Sahrens } 27125367Sahrens 27135367Sahrens hdl->libzfs_log_str = logstr; 27145367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27155367Sahrens if (h == NULL) { 27165367Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27175367Sahrens goto ancestorerr; 27185367Sahrens } 27195367Sahrens 27205367Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27215367Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27225367Sahrens goto ancestorerr; 27235367Sahrens } 27245367Sahrens 27255367Sahrens if (zfs_share(h) != 0) { 27265367Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27275367Sahrens goto ancestorerr; 27285367Sahrens } 27295367Sahrens 27305367Sahrens zfs_close(h); 27315367Sahrens } 27325367Sahrens 27335367Sahrens return (0); 27345367Sahrens 27355367Sahrens ancestorerr: 27365367Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27375367Sahrens "failed to %s ancestor '%s'"), opname, target); 27385367Sahrens return (-1); 27395367Sahrens } 27405367Sahrens 27415367Sahrens /* 27424490Svb160487 * Creates non-existing ancestors of the given path. 27434490Svb160487 */ 27444490Svb160487 int 27454490Svb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27464490Svb160487 { 27474490Svb160487 int prefix; 27484490Svb160487 char *path_copy; 27494490Svb160487 int rc; 27504490Svb160487 2751*11497SMark.Musante@Sun.COM if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27524490Svb160487 return (-1); 27534490Svb160487 27544490Svb160487 if ((path_copy = strdup(path)) != NULL) { 27554490Svb160487 rc = create_parents(hdl, path_copy, prefix); 27564490Svb160487 free(path_copy); 27574490Svb160487 } 27584490Svb160487 if (path_copy == NULL || rc != 0) 27594490Svb160487 return (-1); 27604490Svb160487 2761789Sahrens return (0); 2762789Sahrens } 2763789Sahrens 2764789Sahrens /* 27652676Seschrock * Create a new filesystem or volume. 2766789Sahrens */ 2767789Sahrens int 27682082Seschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 27692676Seschrock nvlist_t *props) 2770789Sahrens { 2771789Sahrens zfs_cmd_t zc = { 0 }; 2772789Sahrens int ret; 2773789Sahrens uint64_t size = 0; 2774789Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 27752082Seschrock char errbuf[1024]; 27762676Seschrock uint64_t zoned; 27772082Seschrock 27782082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 27792082Seschrock "cannot create '%s'"), path); 2780789Sahrens 2781789Sahrens /* validate the path, taking care to note the extended error message */ 27825326Sek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 27832082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2784789Sahrens 2785789Sahrens /* validate parents exist */ 27864490Svb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2787789Sahrens return (-1); 2788789Sahrens 2789789Sahrens /* 2790789Sahrens * The failure modes when creating a dataset of a different type over 2791789Sahrens * one that already exists is a little strange. In particular, if you 2792789Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2793789Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2794789Sahrens * first try to see if the dataset exists. 2795789Sahrens */ 2796789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 27975094Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 27982082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27992082Seschrock "dataset already exists")); 28002082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2801789Sahrens } 2802789Sahrens 2803789Sahrens if (type == ZFS_TYPE_VOLUME) 2804789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2805789Sahrens else 2806789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2807789Sahrens 28087184Stimh if (props && (props = zfs_valid_proplist(hdl, type, props, 28093912Slling zoned, NULL, errbuf)) == 0) 28102676Seschrock return (-1); 28112676Seschrock 2812789Sahrens if (type == ZFS_TYPE_VOLUME) { 28131133Seschrock /* 28141133Seschrock * If we are creating a volume, the size and block size must 28151133Seschrock * satisfy a few restraints. First, the blocksize must be a 28161133Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28171133Seschrock * volsize must be a multiple of the block size, and cannot be 28181133Seschrock * zero. 28191133Seschrock */ 28202676Seschrock if (props == NULL || nvlist_lookup_uint64(props, 28212676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 28222676Seschrock nvlist_free(props); 28232082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28242676Seschrock "missing volume size")); 28252676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2826789Sahrens } 2827789Sahrens 28282676Seschrock if ((ret = nvlist_lookup_uint64(props, 28292676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 28302676Seschrock &blocksize)) != 0) { 28312676Seschrock if (ret == ENOENT) { 28322676Seschrock blocksize = zfs_prop_default_numeric( 28332676Seschrock ZFS_PROP_VOLBLOCKSIZE); 28342676Seschrock } else { 28352676Seschrock nvlist_free(props); 28362676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28372676Seschrock "missing volume block size")); 28382676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28392676Seschrock } 28402676Seschrock } 28412676Seschrock 28422676Seschrock if (size == 0) { 28432676Seschrock nvlist_free(props); 28442082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28452676Seschrock "volume size cannot be zero")); 28462676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28471133Seschrock } 28481133Seschrock 28491133Seschrock if (size % blocksize != 0) { 28502676Seschrock nvlist_free(props); 28512082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28522676Seschrock "volume size must be a multiple of volume block " 28532676Seschrock "size")); 28542676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28551133Seschrock } 2856789Sahrens } 2857789Sahrens 28585094Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 28592676Seschrock return (-1); 28602676Seschrock nvlist_free(props); 28612676Seschrock 2862789Sahrens /* create the dataset */ 28634543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2864789Sahrens 28652676Seschrock zcmd_free_nvlists(&zc); 28662676Seschrock 2867789Sahrens /* check for failure */ 2868789Sahrens if (ret != 0) { 2869789Sahrens char parent[ZFS_MAXNAMELEN]; 2870789Sahrens (void) parent_name(path, parent, sizeof (parent)); 2871789Sahrens 2872789Sahrens switch (errno) { 2873789Sahrens case ENOENT: 28742082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28752082Seschrock "no such parent '%s'"), parent); 28762082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2877789Sahrens 2878789Sahrens case EINVAL: 28792082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28803413Smmusante "parent '%s' is not a filesystem"), parent); 28812082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2882789Sahrens 2883789Sahrens case EDOM: 28842082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28852676Seschrock "volume block size must be power of 2 from " 28862676Seschrock "%u to %uk"), 2887789Sahrens (uint_t)SPA_MINBLOCKSIZE, 2888789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 28892082Seschrock 28902676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28912082Seschrock 28924603Sahrens case ENOTSUP: 28934603Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28944603Sahrens "pool must be upgraded to set this " 28954603Sahrens "property or value")); 28964603Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2897789Sahrens #ifdef _ILP32 2898789Sahrens case EOVERFLOW: 2899789Sahrens /* 2900789Sahrens * This platform can't address a volume this big. 2901789Sahrens */ 29022082Seschrock if (type == ZFS_TYPE_VOLUME) 29032082Seschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 29042082Seschrock errbuf)); 2905789Sahrens #endif 29062082Seschrock /* FALLTHROUGH */ 2907789Sahrens default: 29082082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2909789Sahrens } 2910789Sahrens } 2911789Sahrens 2912789Sahrens return (0); 2913789Sahrens } 2914789Sahrens 2915789Sahrens /* 2916789Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2917789Sahrens * isn't mounted, and that there are no active dependents. 2918789Sahrens */ 2919789Sahrens int 292010242Schris.kirby@sun.com zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2921789Sahrens { 2922789Sahrens zfs_cmd_t zc = { 0 }; 2923789Sahrens 2924789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2925789Sahrens 29262676Seschrock if (ZFS_IS_VOLUME(zhp)) { 29273126Sahl /* 29284543Smarks * If user doesn't have permissions to unshare volume, then 29294543Smarks * abort the request. This would only happen for a 29304543Smarks * non-privileged user. 29313126Sahl */ 29324543Smarks if (zfs_unshare_iscsi(zhp) != 0) { 29334543Smarks return (-1); 29344543Smarks } 29353126Sahl 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 */ 3491*11497SMark.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 */ 3504*11497SMark.Musante@Sun.COM if (is_descendant(zhp->zfs_name, target)) { 35052440Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3506*11497SMark.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_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 37324543Smarks { 37334543Smarks zfs_cmd_t zc = { 0 }; 37344543Smarks nvlist_t *nvp; 37354543Smarks gid_t gid; 37364543Smarks uid_t uid; 37374543Smarks const gid_t *groups; 37384543Smarks int group_cnt; 37394543Smarks int error; 37404543Smarks 37414543Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 37424543Smarks return (no_memory(hdl)); 37434543Smarks 37444543Smarks uid = ucred_geteuid(cred); 37454543Smarks gid = ucred_getegid(cred); 37464543Smarks group_cnt = ucred_getgroups(cred, &groups); 37474543Smarks 37484543Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 37494543Smarks return (1); 37504543Smarks 37514543Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 37524543Smarks nvlist_free(nvp); 37534543Smarks return (1); 37544543Smarks } 37554543Smarks 37564543Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 37574543Smarks nvlist_free(nvp); 37584543Smarks return (1); 37594543Smarks } 37604543Smarks 37614543Smarks if (nvlist_add_uint32_array(nvp, 37624543Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 37634543Smarks nvlist_free(nvp); 37644543Smarks return (1); 37654543Smarks } 37664543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37674543Smarks 37685094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 37694543Smarks return (-1); 37704543Smarks 37714543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 37724543Smarks nvlist_free(nvp); 37734543Smarks return (error); 37744543Smarks } 37754543Smarks 37764543Smarks int 37774543Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 37788845Samw@Sun.COM char *resource, void *export, void *sharetab, 37798845Samw@Sun.COM int sharemax, zfs_share_op_t operation) 37804543Smarks { 37814543Smarks zfs_cmd_t zc = { 0 }; 37824543Smarks int error; 37834543Smarks 37844543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37854543Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37868845Samw@Sun.COM if (resource) 37878845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 37884543Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 37894543Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 37905331Samw zc.zc_share.z_sharetype = operation; 37914543Smarks zc.zc_share.z_sharemax = sharemax; 37924543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 37934543Smarks return (error); 37944543Smarks } 37958802SSanjeev.Bagewadi@Sun.COM 37968802SSanjeev.Bagewadi@Sun.COM void 37978802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37988802SSanjeev.Bagewadi@Sun.COM { 37998802SSanjeev.Bagewadi@Sun.COM nvpair_t *curr; 38008802SSanjeev.Bagewadi@Sun.COM 38018802SSanjeev.Bagewadi@Sun.COM /* 38028802SSanjeev.Bagewadi@Sun.COM * Keep a reference to the props-table against which we prune the 38038802SSanjeev.Bagewadi@Sun.COM * properties. 38048802SSanjeev.Bagewadi@Sun.COM */ 38058802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table = props; 38068802SSanjeev.Bagewadi@Sun.COM 38078802SSanjeev.Bagewadi@Sun.COM curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 38088802SSanjeev.Bagewadi@Sun.COM 38098802SSanjeev.Bagewadi@Sun.COM while (curr) { 38108802SSanjeev.Bagewadi@Sun.COM zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 38118802SSanjeev.Bagewadi@Sun.COM nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 38128802SSanjeev.Bagewadi@Sun.COM 38139396SMatthew.Ahrens@Sun.COM /* 381410960SEric.Schrock@Sun.COM * User properties will result in ZPROP_INVAL, and since we 381510960SEric.Schrock@Sun.COM * only know how to prune standard ZFS properties, we always 381610960SEric.Schrock@Sun.COM * leave these in the list. This can also happen if we 381710960SEric.Schrock@Sun.COM * encounter an unknown DSL property (when running older 381810960SEric.Schrock@Sun.COM * software, for example). 38199396SMatthew.Ahrens@Sun.COM */ 38209396SMatthew.Ahrens@Sun.COM if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 38218802SSanjeev.Bagewadi@Sun.COM (void) nvlist_remove(zhp->zfs_props, 38228802SSanjeev.Bagewadi@Sun.COM nvpair_name(curr), nvpair_type(curr)); 38238802SSanjeev.Bagewadi@Sun.COM curr = next; 38248802SSanjeev.Bagewadi@Sun.COM } 38258802SSanjeev.Bagewadi@Sun.COM } 38268845Samw@Sun.COM 38278845Samw@Sun.COM static int 38288845Samw@Sun.COM zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 38298845Samw@Sun.COM zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 38308845Samw@Sun.COM { 38318845Samw@Sun.COM zfs_cmd_t zc = { 0 }; 38328845Samw@Sun.COM nvlist_t *nvlist = NULL; 38338845Samw@Sun.COM int error; 38348845Samw@Sun.COM 38358845Samw@Sun.COM (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 38368845Samw@Sun.COM (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 38378845Samw@Sun.COM zc.zc_cookie = (uint64_t)cmd; 38388845Samw@Sun.COM 38398845Samw@Sun.COM if (cmd == ZFS_SMB_ACL_RENAME) { 38408845Samw@Sun.COM if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 38418845Samw@Sun.COM (void) no_memory(hdl); 38428845Samw@Sun.COM return (NULL); 38438845Samw@Sun.COM } 38448845Samw@Sun.COM } 38458845Samw@Sun.COM 38468845Samw@Sun.COM switch (cmd) { 38478845Samw@Sun.COM case ZFS_SMB_ACL_ADD: 38488845Samw@Sun.COM case ZFS_SMB_ACL_REMOVE: 38498845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 38508845Samw@Sun.COM break; 38518845Samw@Sun.COM case ZFS_SMB_ACL_RENAME: 38528845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 38538845Samw@Sun.COM resource1) != 0) { 38548845Samw@Sun.COM (void) no_memory(hdl); 38558845Samw@Sun.COM return (-1); 38568845Samw@Sun.COM } 38578845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 38588845Samw@Sun.COM resource2) != 0) { 38598845Samw@Sun.COM (void) no_memory(hdl); 38608845Samw@Sun.COM return (-1); 38618845Samw@Sun.COM } 38628845Samw@Sun.COM if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 38638845Samw@Sun.COM nvlist_free(nvlist); 38648845Samw@Sun.COM return (-1); 38658845Samw@Sun.COM } 38668845Samw@Sun.COM break; 38678845Samw@Sun.COM case ZFS_SMB_ACL_PURGE: 38688845Samw@Sun.COM break; 38698845Samw@Sun.COM default: 38708845Samw@Sun.COM return (-1); 38718845Samw@Sun.COM } 38728845Samw@Sun.COM error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 38738845Samw@Sun.COM if (nvlist) 38748845Samw@Sun.COM nvlist_free(nvlist); 38758845Samw@Sun.COM return (error); 38768845Samw@Sun.COM } 38778845Samw@Sun.COM 38788845Samw@Sun.COM int 38798845Samw@Sun.COM zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 38808845Samw@Sun.COM char *path, char *resource) 38818845Samw@Sun.COM { 38828845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 38838845Samw@Sun.COM resource, NULL)); 38848845Samw@Sun.COM } 38858845Samw@Sun.COM 38868845Samw@Sun.COM int 38878845Samw@Sun.COM zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 38888845Samw@Sun.COM char *path, char *resource) 38898845Samw@Sun.COM { 38908845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 38918845Samw@Sun.COM resource, NULL)); 38928845Samw@Sun.COM } 38938845Samw@Sun.COM 38948845Samw@Sun.COM int 38958845Samw@Sun.COM zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 38968845Samw@Sun.COM { 38978845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 38988845Samw@Sun.COM NULL, NULL)); 38998845Samw@Sun.COM } 39008845Samw@Sun.COM 39018845Samw@Sun.COM int 39028845Samw@Sun.COM zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 39038845Samw@Sun.COM char *oldname, char *newname) 39048845Samw@Sun.COM { 39058845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 39068845Samw@Sun.COM oldname, newname)); 39078845Samw@Sun.COM } 39089396SMatthew.Ahrens@Sun.COM 39099396SMatthew.Ahrens@Sun.COM int 39109396SMatthew.Ahrens@Sun.COM zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 39119396SMatthew.Ahrens@Sun.COM zfs_userspace_cb_t func, void *arg) 39129396SMatthew.Ahrens@Sun.COM { 39139396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 39149396SMatthew.Ahrens@Sun.COM int error; 39159396SMatthew.Ahrens@Sun.COM zfs_useracct_t buf[100]; 39169396SMatthew.Ahrens@Sun.COM 39179396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 39189396SMatthew.Ahrens@Sun.COM 39199396SMatthew.Ahrens@Sun.COM zc.zc_objset_type = type; 39209396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst = (uintptr_t)buf; 39219396SMatthew.Ahrens@Sun.COM 39229396SMatthew.Ahrens@Sun.COM /* CONSTCOND */ 39239396SMatthew.Ahrens@Sun.COM while (1) { 39249396SMatthew.Ahrens@Sun.COM zfs_useracct_t *zua = buf; 39259396SMatthew.Ahrens@Sun.COM 39269396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size = sizeof (buf); 39279396SMatthew.Ahrens@Sun.COM error = ioctl(zhp->zfs_hdl->libzfs_fd, 39289396SMatthew.Ahrens@Sun.COM ZFS_IOC_USERSPACE_MANY, &zc); 39299396SMatthew.Ahrens@Sun.COM if (error || zc.zc_nvlist_dst_size == 0) 39309396SMatthew.Ahrens@Sun.COM break; 39319396SMatthew.Ahrens@Sun.COM 39329396SMatthew.Ahrens@Sun.COM while (zc.zc_nvlist_dst_size > 0) { 39339554SMatthew.Ahrens@Sun.COM error = func(arg, zua->zu_domain, zua->zu_rid, 39349554SMatthew.Ahrens@Sun.COM zua->zu_space); 39359554SMatthew.Ahrens@Sun.COM if (error != 0) 39369554SMatthew.Ahrens@Sun.COM return (error); 39379396SMatthew.Ahrens@Sun.COM zua++; 39389396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 39399396SMatthew.Ahrens@Sun.COM } 39409396SMatthew.Ahrens@Sun.COM } 39419396SMatthew.Ahrens@Sun.COM 39429396SMatthew.Ahrens@Sun.COM return (error); 39439396SMatthew.Ahrens@Sun.COM } 394410242Schris.kirby@sun.com 394510242Schris.kirby@sun.com int 394610242Schris.kirby@sun.com zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 394711417SChris.Kirby@sun.com boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 394810242Schris.kirby@sun.com { 394910242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 395010242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 395110242Schris.kirby@sun.com 395210242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 395310242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 395410342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 395510342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 395610342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 395710242Schris.kirby@sun.com zc.zc_cookie = recursive; 395810342Schris.kirby@sun.com zc.zc_temphold = temphold; 395910242Schris.kirby@sun.com 396010242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 396110242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 396210242Schris.kirby@sun.com 396310242Schris.kirby@sun.com /* 396410242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 396510242Schris.kirby@sun.com * zc.zc_name. 396610242Schris.kirby@sun.com */ 396710242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 396810242Schris.kirby@sun.com "cannot hold '%s@%s'"), zc.zc_name, snapname); 396910242Schris.kirby@sun.com switch (errno) { 397010951SChris.Kirby@sun.com case E2BIG: 397110951SChris.Kirby@sun.com /* 397210951SChris.Kirby@sun.com * Temporary tags wind up having the ds object id 397310951SChris.Kirby@sun.com * prepended. So even if we passed the length check 397410951SChris.Kirby@sun.com * above, it's still possible for the tag to wind 397510951SChris.Kirby@sun.com * up being slightly too long. 397610951SChris.Kirby@sun.com */ 397710951SChris.Kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 397810242Schris.kirby@sun.com case ENOTSUP: 397910242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 398010242Schris.kirby@sun.com "pool must be upgraded")); 398110242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 398210242Schris.kirby@sun.com case EINVAL: 398310242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 398410242Schris.kirby@sun.com case EEXIST: 398510242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 398611417SChris.Kirby@sun.com case ENOENT: 398711417SChris.Kirby@sun.com if (enoent_ok) 398811417SChris.Kirby@sun.com return (0); 398911417SChris.Kirby@sun.com /* FALLTHROUGH */ 399010242Schris.kirby@sun.com default: 399110242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 399210242Schris.kirby@sun.com } 399310242Schris.kirby@sun.com } 399410242Schris.kirby@sun.com 399510242Schris.kirby@sun.com return (0); 399610242Schris.kirby@sun.com } 399710242Schris.kirby@sun.com 399810342Schris.kirby@sun.com struct hold_range_arg { 399910342Schris.kirby@sun.com zfs_handle_t *origin; 400010342Schris.kirby@sun.com const char *fromsnap; 400110342Schris.kirby@sun.com const char *tosnap; 400210342Schris.kirby@sun.com char lastsnapheld[ZFS_MAXNAMELEN]; 400310342Schris.kirby@sun.com const char *tag; 400410342Schris.kirby@sun.com boolean_t temphold; 400510342Schris.kirby@sun.com boolean_t seento; 400610342Schris.kirby@sun.com boolean_t seenfrom; 400710342Schris.kirby@sun.com boolean_t holding; 400810342Schris.kirby@sun.com }; 400910342Schris.kirby@sun.com 401010342Schris.kirby@sun.com static int 401110342Schris.kirby@sun.com zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 401210342Schris.kirby@sun.com { 401310342Schris.kirby@sun.com struct hold_range_arg *hra = arg; 401410342Schris.kirby@sun.com const char *thissnap; 401510342Schris.kirby@sun.com int error; 401610342Schris.kirby@sun.com 401710342Schris.kirby@sun.com thissnap = strchr(zfs_get_name(zhp), '@') + 1; 401810342Schris.kirby@sun.com 401910342Schris.kirby@sun.com if (hra->fromsnap && !hra->seenfrom && 402010342Schris.kirby@sun.com strcmp(hra->fromsnap, thissnap) == 0) 402110342Schris.kirby@sun.com hra->seenfrom = B_TRUE; 402210342Schris.kirby@sun.com 402310342Schris.kirby@sun.com /* snap is older or newer than the desired range, ignore it */ 402410342Schris.kirby@sun.com if (hra->seento || !hra->seenfrom) { 402510342Schris.kirby@sun.com zfs_close(zhp); 402610342Schris.kirby@sun.com return (0); 402710342Schris.kirby@sun.com } 402810342Schris.kirby@sun.com 402910342Schris.kirby@sun.com if (hra->holding) { 403011417SChris.Kirby@sun.com /* We could be racing with destroy, so ignore ENOENT. */ 403110342Schris.kirby@sun.com error = zfs_hold(hra->origin, thissnap, hra->tag, B_FALSE, 403211417SChris.Kirby@sun.com hra->temphold, B_TRUE); 403310342Schris.kirby@sun.com if (error == 0) { 403410342Schris.kirby@sun.com (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 403510342Schris.kirby@sun.com sizeof (hra->lastsnapheld)); 403610342Schris.kirby@sun.com } 403710342Schris.kirby@sun.com } else { 403810342Schris.kirby@sun.com error = zfs_release(hra->origin, thissnap, hra->tag, B_FALSE); 403910342Schris.kirby@sun.com } 404010342Schris.kirby@sun.com 404110342Schris.kirby@sun.com if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 404210342Schris.kirby@sun.com hra->seento = B_TRUE; 404310342Schris.kirby@sun.com 404410342Schris.kirby@sun.com zfs_close(zhp); 404510342Schris.kirby@sun.com return (error); 404610342Schris.kirby@sun.com } 404710342Schris.kirby@sun.com 404810342Schris.kirby@sun.com /* 404910342Schris.kirby@sun.com * Add a user hold on the set of snapshots starting with fromsnap up to 405010342Schris.kirby@sun.com * and including tosnap. If we're unable to to acquire a particular hold, 405110342Schris.kirby@sun.com * undo any holds up to that point. 405210342Schris.kirby@sun.com */ 405310342Schris.kirby@sun.com int 405410342Schris.kirby@sun.com zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 405510342Schris.kirby@sun.com const char *tag, boolean_t temphold) 405610342Schris.kirby@sun.com { 405710342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 405810342Schris.kirby@sun.com int error; 405910342Schris.kirby@sun.com 406010342Schris.kirby@sun.com arg.origin = zhp; 406110342Schris.kirby@sun.com arg.fromsnap = fromsnap; 406210342Schris.kirby@sun.com arg.tosnap = tosnap; 406310342Schris.kirby@sun.com arg.tag = tag; 406410342Schris.kirby@sun.com arg.temphold = temphold; 406510342Schris.kirby@sun.com arg.holding = B_TRUE; 406610342Schris.kirby@sun.com 406710342Schris.kirby@sun.com error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 406810342Schris.kirby@sun.com 406910342Schris.kirby@sun.com /* 407010342Schris.kirby@sun.com * Make sure we either hold the entire range or none. 407110342Schris.kirby@sun.com */ 407210342Schris.kirby@sun.com if (error && arg.lastsnapheld[0] != '\0') { 407310342Schris.kirby@sun.com (void) zfs_release_range(zhp, fromsnap, 407410342Schris.kirby@sun.com (const char *)arg.lastsnapheld, tag); 407510342Schris.kirby@sun.com } 407610342Schris.kirby@sun.com return (error); 407710342Schris.kirby@sun.com } 407810342Schris.kirby@sun.com 407910242Schris.kirby@sun.com int 408010242Schris.kirby@sun.com zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 408110242Schris.kirby@sun.com boolean_t recursive) 408210242Schris.kirby@sun.com { 408310242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 408410242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 408510242Schris.kirby@sun.com 408610242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 408710242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 408810342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 408910342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 409010342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 409110242Schris.kirby@sun.com zc.zc_cookie = recursive; 409210242Schris.kirby@sun.com 409310242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 409410242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 409510242Schris.kirby@sun.com 409610242Schris.kirby@sun.com /* 409710242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 409810242Schris.kirby@sun.com * zc.zc_name. 409910242Schris.kirby@sun.com */ 410010242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 410110242Schris.kirby@sun.com "cannot release '%s@%s'"), zc.zc_name, snapname); 410210242Schris.kirby@sun.com switch (errno) { 410310242Schris.kirby@sun.com case ESRCH: 410410242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 410510242Schris.kirby@sun.com case ENOTSUP: 410610242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 410710242Schris.kirby@sun.com "pool must be upgraded")); 410810242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 410910242Schris.kirby@sun.com case EINVAL: 411010242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 411110242Schris.kirby@sun.com default: 411210242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 411310242Schris.kirby@sun.com } 411410242Schris.kirby@sun.com } 411510242Schris.kirby@sun.com 411610242Schris.kirby@sun.com return (0); 411710242Schris.kirby@sun.com } 411810342Schris.kirby@sun.com 411910342Schris.kirby@sun.com /* 412010342Schris.kirby@sun.com * Release a user hold from the set of snapshots starting with fromsnap 412110342Schris.kirby@sun.com * up to and including tosnap. 412210342Schris.kirby@sun.com */ 412310342Schris.kirby@sun.com int 412410342Schris.kirby@sun.com zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 412510342Schris.kirby@sun.com const char *tag) 412610342Schris.kirby@sun.com { 412710342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 412810342Schris.kirby@sun.com 412910342Schris.kirby@sun.com arg.origin = zhp; 413010342Schris.kirby@sun.com arg.fromsnap = fromsnap; 413110342Schris.kirby@sun.com arg.tosnap = tosnap; 413210342Schris.kirby@sun.com arg.tag = tag; 413310342Schris.kirby@sun.com 413410342Schris.kirby@sun.com return (zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg)); 413510342Schris.kirby@sun.com } 413611449SEric.Taylor@Sun.COM 413711449SEric.Taylor@Sun.COM uint64_t 413811449SEric.Taylor@Sun.COM zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 413911449SEric.Taylor@Sun.COM { 414011449SEric.Taylor@Sun.COM uint64_t numdb; 414111449SEric.Taylor@Sun.COM uint64_t nblocks, volblocksize; 414211449SEric.Taylor@Sun.COM int ncopies; 414311449SEric.Taylor@Sun.COM char *strval; 414411449SEric.Taylor@Sun.COM 414511449SEric.Taylor@Sun.COM if (nvlist_lookup_string(props, 414611449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 414711449SEric.Taylor@Sun.COM ncopies = atoi(strval); 414811449SEric.Taylor@Sun.COM else 414911449SEric.Taylor@Sun.COM ncopies = 1; 415011449SEric.Taylor@Sun.COM if (nvlist_lookup_uint64(props, 415111449SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 415211449SEric.Taylor@Sun.COM &volblocksize) != 0) 415311449SEric.Taylor@Sun.COM volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 415411449SEric.Taylor@Sun.COM nblocks = volsize/volblocksize; 415511449SEric.Taylor@Sun.COM /* start with metadnode L0-L6 */ 415611449SEric.Taylor@Sun.COM numdb = 7; 415711449SEric.Taylor@Sun.COM /* calculate number of indirects */ 415811449SEric.Taylor@Sun.COM while (nblocks > 1) { 415911449SEric.Taylor@Sun.COM nblocks += DNODES_PER_LEVEL - 1; 416011449SEric.Taylor@Sun.COM nblocks /= DNODES_PER_LEVEL; 416111449SEric.Taylor@Sun.COM numdb += nblocks; 416211449SEric.Taylor@Sun.COM } 416311449SEric.Taylor@Sun.COM numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 416411449SEric.Taylor@Sun.COM volsize *= ncopies; 416511449SEric.Taylor@Sun.COM /* 416611449SEric.Taylor@Sun.COM * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 416711449SEric.Taylor@Sun.COM * compressed, but in practice they compress down to about 416811449SEric.Taylor@Sun.COM * 1100 bytes 416911449SEric.Taylor@Sun.COM */ 417011449SEric.Taylor@Sun.COM numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 417111449SEric.Taylor@Sun.COM volsize += numdb; 417211449SEric.Taylor@Sun.COM return (volsize); 417311449SEric.Taylor@Sun.COM } 4174