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 /* 23*12496SEric.Taylor@Sun.COM * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24789Sahrens */ 25789Sahrens 26789Sahrens #include <ctype.h> 27789Sahrens #include <errno.h> 28789Sahrens #include <libintl.h> 29789Sahrens #include <math.h> 30789Sahrens #include <stdio.h> 31789Sahrens #include <stdlib.h> 32789Sahrens #include <strings.h> 33789Sahrens #include <unistd.h> 345367Sahrens #include <stddef.h> 35789Sahrens #include <zone.h> 362082Seschrock #include <fcntl.h> 37789Sahrens #include <sys/mntent.h> 381294Slling #include <sys/mount.h> 394543Smarks #include <priv.h> 404543Smarks #include <pwd.h> 414543Smarks #include <grp.h> 424543Smarks #include <stddef.h> 434543Smarks #include <ucred.h> 449396SMatthew.Ahrens@Sun.COM #include <idmap.h> 459396SMatthew.Ahrens@Sun.COM #include <aclutils.h> 4610160SMatthew.Ahrens@Sun.COM #include <directory.h> 47789Sahrens 4811449SEric.Taylor@Sun.COM #include <sys/dnode.h> 49789Sahrens #include <sys/spa.h> 502676Seschrock #include <sys/zap.h> 51789Sahrens #include <libzfs.h> 52789Sahrens 53789Sahrens #include "zfs_namecheck.h" 54789Sahrens #include "zfs_prop.h" 55789Sahrens #include "libzfs_impl.h" 564543Smarks #include "zfs_deleg.h" 57789Sahrens 589396SMatthew.Ahrens@Sun.COM static int userquota_propname_decode(const char *propname, boolean_t zoned, 599396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 604007Smmusante 61789Sahrens /* 62789Sahrens * Given a single type (not a mask of types), return the type in a human 63789Sahrens * readable form. 64789Sahrens */ 65789Sahrens const char * 66789Sahrens zfs_type_to_name(zfs_type_t type) 67789Sahrens { 68789Sahrens switch (type) { 69789Sahrens case ZFS_TYPE_FILESYSTEM: 70789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 71789Sahrens case ZFS_TYPE_SNAPSHOT: 72789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 73789Sahrens case ZFS_TYPE_VOLUME: 74789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 75789Sahrens } 76789Sahrens 77789Sahrens return (NULL); 78789Sahrens } 79789Sahrens 80789Sahrens /* 81789Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 82789Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 83789Sahrens * We guess what the type would have been based on the path and the mask of 84789Sahrens * acceptable types. 85789Sahrens */ 86789Sahrens static const char * 87789Sahrens path_to_str(const char *path, int types) 88789Sahrens { 89789Sahrens /* 90789Sahrens * When given a single type, always report the exact type. 91789Sahrens */ 92789Sahrens if (types == ZFS_TYPE_SNAPSHOT) 93789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 94789Sahrens if (types == ZFS_TYPE_FILESYSTEM) 95789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 96789Sahrens if (types == ZFS_TYPE_VOLUME) 97789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 98789Sahrens 99789Sahrens /* 100789Sahrens * The user is requesting more than one type of dataset. If this is the 101789Sahrens * case, consult the path itself. If we're looking for a snapshot, and 102789Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 103789Sahrens * snapshot attribute and try again. 104789Sahrens */ 105789Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 106789Sahrens if (strchr(path, '@') != NULL) 107789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 108789Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 109789Sahrens } 110789Sahrens 111789Sahrens /* 112789Sahrens * The user has requested either filesystems or volumes. 113789Sahrens * We have no way of knowing a priori what type this would be, so always 114789Sahrens * report it as "filesystem" or "volume", our two primitive types. 115789Sahrens */ 116789Sahrens if (types & ZFS_TYPE_FILESYSTEM) 117789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 118789Sahrens 119789Sahrens assert(types & ZFS_TYPE_VOLUME); 120789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 121789Sahrens } 122789Sahrens 123789Sahrens /* 124789Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 1259396SMatthew.Ahrens@Sun.COM * provide a more meaningful error message. We call zfs_error_aux() to 1269396SMatthew.Ahrens@Sun.COM * explain exactly why the name was not valid. 127789Sahrens */ 128789Sahrens static int 1295326Sek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 1305326Sek110237 boolean_t modifying) 131789Sahrens { 132789Sahrens namecheck_err_t why; 133789Sahrens char what; 134789Sahrens 135789Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 1362082Seschrock if (hdl != NULL) { 137789Sahrens switch (why) { 1381003Slling case NAME_ERR_TOOLONG: 1392082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1402082Seschrock "name is too long")); 1411003Slling break; 1421003Slling 143789Sahrens case NAME_ERR_LEADING_SLASH: 1442082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1452082Seschrock "leading slash in name")); 146789Sahrens break; 147789Sahrens 148789Sahrens case NAME_ERR_EMPTY_COMPONENT: 1492082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1502082Seschrock "empty component in name")); 151789Sahrens break; 152789Sahrens 153789Sahrens case NAME_ERR_TRAILING_SLASH: 1542082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1552082Seschrock "trailing slash in name")); 156789Sahrens break; 157789Sahrens 158789Sahrens case NAME_ERR_INVALCHAR: 1592082Seschrock zfs_error_aux(hdl, 160789Sahrens dgettext(TEXT_DOMAIN, "invalid character " 1612082Seschrock "'%c' in name"), what); 162789Sahrens break; 163789Sahrens 164789Sahrens case NAME_ERR_MULTIPLE_AT: 1652082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1662082Seschrock "multiple '@' delimiters in name")); 167789Sahrens break; 1682856Snd150628 1692856Snd150628 case NAME_ERR_NOLETTER: 1702856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1712856Snd150628 "pool doesn't begin with a letter")); 1722856Snd150628 break; 1732856Snd150628 1742856Snd150628 case NAME_ERR_RESERVED: 1752856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1762856Snd150628 "name is reserved")); 1772856Snd150628 break; 1782856Snd150628 1792856Snd150628 case NAME_ERR_DISKLIKE: 1802856Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1812856Snd150628 "reserved disk name")); 1822856Snd150628 break; 183789Sahrens } 184789Sahrens } 185789Sahrens 186789Sahrens return (0); 187789Sahrens } 188789Sahrens 189789Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 1902082Seschrock if (hdl != NULL) 1912082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1922082Seschrock "snapshot delimiter '@' in filesystem name")); 193789Sahrens return (0); 194789Sahrens } 195789Sahrens 1962199Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 1972199Sahrens if (hdl != NULL) 1982199Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1993413Smmusante "missing '@' delimiter in snapshot name")); 2002199Sahrens return (0); 2012199Sahrens } 2022199Sahrens 2035326Sek110237 if (modifying && strchr(path, '%') != NULL) { 2045326Sek110237 if (hdl != NULL) 2055326Sek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2065326Sek110237 "invalid character %c in name"), '%'); 2075326Sek110237 return (0); 2085326Sek110237 } 2095326Sek110237 2102082Seschrock return (-1); 211789Sahrens } 212789Sahrens 213789Sahrens int 214789Sahrens zfs_name_valid(const char *name, zfs_type_t type) 215789Sahrens { 2166423Sgw25295 if (type == ZFS_TYPE_POOL) 2176423Sgw25295 return (zpool_name_valid(NULL, B_FALSE, name)); 2185326Sek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 219789Sahrens } 220789Sahrens 221789Sahrens /* 2222676Seschrock * This function takes the raw DSL properties, and filters out the user-defined 2232676Seschrock * properties into a separate nvlist. 2242676Seschrock */ 2254217Seschrock static nvlist_t * 2264217Seschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 2272676Seschrock { 2282676Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2292676Seschrock nvpair_t *elem; 2302676Seschrock nvlist_t *propval; 2314217Seschrock nvlist_t *nvl; 2324217Seschrock 2334217Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 2344217Seschrock (void) no_memory(hdl); 2354217Seschrock return (NULL); 2364217Seschrock } 2372676Seschrock 2382676Seschrock elem = NULL; 2394217Seschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 2402676Seschrock if (!zfs_prop_user(nvpair_name(elem))) 2412676Seschrock continue; 2422676Seschrock 2432676Seschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 2444217Seschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 2454217Seschrock nvlist_free(nvl); 2464217Seschrock (void) no_memory(hdl); 2474217Seschrock return (NULL); 2484217Seschrock } 2492676Seschrock } 2502676Seschrock 2514217Seschrock return (nvl); 2522676Seschrock } 2532676Seschrock 2546865Srm160521 static zpool_handle_t * 2556865Srm160521 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 2566865Srm160521 { 2576865Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 2586865Srm160521 zpool_handle_t *zph; 2596865Srm160521 2606865Srm160521 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 2616865Srm160521 if (hdl->libzfs_pool_handles != NULL) 2626865Srm160521 zph->zpool_next = hdl->libzfs_pool_handles; 2636865Srm160521 hdl->libzfs_pool_handles = zph; 2646865Srm160521 } 2656865Srm160521 return (zph); 2666865Srm160521 } 2676865Srm160521 2686865Srm160521 static zpool_handle_t * 2696865Srm160521 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 2706865Srm160521 { 2716865Srm160521 libzfs_handle_t *hdl = zhp->zfs_hdl; 2726865Srm160521 zpool_handle_t *zph = hdl->libzfs_pool_handles; 2736865Srm160521 2746865Srm160521 while ((zph != NULL) && 2756865Srm160521 (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 2766865Srm160521 zph = zph->zpool_next; 2776865Srm160521 return (zph); 2786865Srm160521 } 2796865Srm160521 2806865Srm160521 /* 2816865Srm160521 * Returns a handle to the pool that contains the provided dataset. 2826865Srm160521 * If a handle to that pool already exists then that handle is returned. 2836865Srm160521 * Otherwise, a new handle is created and added to the list of handles. 2846865Srm160521 */ 2856865Srm160521 static zpool_handle_t * 2866865Srm160521 zpool_handle(zfs_handle_t *zhp) 2876865Srm160521 { 2886865Srm160521 char *pool_name; 2896865Srm160521 int len; 2906865Srm160521 zpool_handle_t *zph; 2916865Srm160521 2926865Srm160521 len = strcspn(zhp->zfs_name, "/@") + 1; 2936865Srm160521 pool_name = zfs_alloc(zhp->zfs_hdl, len); 2946865Srm160521 (void) strlcpy(pool_name, zhp->zfs_name, len); 2956865Srm160521 2966865Srm160521 zph = zpool_find_handle(zhp, pool_name, len); 2976865Srm160521 if (zph == NULL) 2986865Srm160521 zph = zpool_add_handle(zhp, pool_name); 2996865Srm160521 3006865Srm160521 free(pool_name); 3016865Srm160521 return (zph); 3026865Srm160521 } 3036865Srm160521 3046865Srm160521 void 3056865Srm160521 zpool_free_handles(libzfs_handle_t *hdl) 3066865Srm160521 { 3076865Srm160521 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 3086865Srm160521 3096865Srm160521 while (zph != NULL) { 3106865Srm160521 next = zph->zpool_next; 3116865Srm160521 zpool_close(zph); 3126865Srm160521 zph = next; 3136865Srm160521 } 3146865Srm160521 hdl->libzfs_pool_handles = NULL; 3156865Srm160521 } 3166865Srm160521 3172676Seschrock /* 318789Sahrens * Utility function to gather stats (objset and zpl) for the given object. 319789Sahrens */ 320789Sahrens static int 3218228SEric.Taylor@Sun.COM get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 322789Sahrens { 3232676Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3248228SEric.Taylor@Sun.COM 3258228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 3268228SEric.Taylor@Sun.COM 3278228SEric.Taylor@Sun.COM while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3281356Seschrock if (errno == ENOMEM) { 3298228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 3302082Seschrock return (-1); 3312676Seschrock } 3321356Seschrock } else { 3331356Seschrock return (-1); 3341356Seschrock } 3351356Seschrock } 3368228SEric.Taylor@Sun.COM return (0); 3378228SEric.Taylor@Sun.COM } 3388228SEric.Taylor@Sun.COM 33911022STom.Erickson@Sun.COM /* 34011022STom.Erickson@Sun.COM * Utility function to get the received properties of the given object. 34111022STom.Erickson@Sun.COM */ 34211022STom.Erickson@Sun.COM static int 34311022STom.Erickson@Sun.COM get_recvd_props_ioctl(zfs_handle_t *zhp) 34411022STom.Erickson@Sun.COM { 34511022STom.Erickson@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 34611022STom.Erickson@Sun.COM nvlist_t *recvdprops; 34711022STom.Erickson@Sun.COM zfs_cmd_t zc = { 0 }; 34811022STom.Erickson@Sun.COM int err; 34911022STom.Erickson@Sun.COM 35011022STom.Erickson@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 35111022STom.Erickson@Sun.COM return (-1); 35211022STom.Erickson@Sun.COM 35311022STom.Erickson@Sun.COM (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35411022STom.Erickson@Sun.COM 35511022STom.Erickson@Sun.COM while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 35611022STom.Erickson@Sun.COM if (errno == ENOMEM) { 35711022STom.Erickson@Sun.COM if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 35811022STom.Erickson@Sun.COM return (-1); 35911022STom.Erickson@Sun.COM } 36011022STom.Erickson@Sun.COM } else { 36111022STom.Erickson@Sun.COM zcmd_free_nvlists(&zc); 36211022STom.Erickson@Sun.COM return (-1); 36311022STom.Erickson@Sun.COM } 36411022STom.Erickson@Sun.COM } 36511022STom.Erickson@Sun.COM 36611022STom.Erickson@Sun.COM err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 36711022STom.Erickson@Sun.COM zcmd_free_nvlists(&zc); 36811022STom.Erickson@Sun.COM if (err != 0) 36911022STom.Erickson@Sun.COM return (-1); 37011022STom.Erickson@Sun.COM 37111022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 37211022STom.Erickson@Sun.COM zhp->zfs_recvd_props = recvdprops; 37311022STom.Erickson@Sun.COM 37411022STom.Erickson@Sun.COM return (0); 37511022STom.Erickson@Sun.COM } 37611022STom.Erickson@Sun.COM 3778228SEric.Taylor@Sun.COM static int 3788228SEric.Taylor@Sun.COM put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 3798228SEric.Taylor@Sun.COM { 3808228SEric.Taylor@Sun.COM nvlist_t *allprops, *userprops; 3818228SEric.Taylor@Sun.COM 3828228SEric.Taylor@Sun.COM zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 3838228SEric.Taylor@Sun.COM 3848228SEric.Taylor@Sun.COM if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 3852082Seschrock return (-1); 3862082Seschrock } 387789Sahrens 3889396SMatthew.Ahrens@Sun.COM /* 3899396SMatthew.Ahrens@Sun.COM * XXX Why do we store the user props separately, in addition to 3909396SMatthew.Ahrens@Sun.COM * storing them in zfs_props? 3919396SMatthew.Ahrens@Sun.COM */ 3924217Seschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 3934217Seschrock nvlist_free(allprops); 3942676Seschrock return (-1); 3954217Seschrock } 3964217Seschrock 3974217Seschrock nvlist_free(zhp->zfs_props); 3984217Seschrock nvlist_free(zhp->zfs_user_props); 3994217Seschrock 4004217Seschrock zhp->zfs_props = allprops; 4014217Seschrock zhp->zfs_user_props = userprops; 4022082Seschrock 403789Sahrens return (0); 404789Sahrens } 405789Sahrens 4068228SEric.Taylor@Sun.COM static int 4078228SEric.Taylor@Sun.COM get_stats(zfs_handle_t *zhp) 4088228SEric.Taylor@Sun.COM { 4098228SEric.Taylor@Sun.COM int rc = 0; 4108228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4118228SEric.Taylor@Sun.COM 4128228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 4138228SEric.Taylor@Sun.COM return (-1); 4148228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) != 0) 4158228SEric.Taylor@Sun.COM rc = -1; 4168228SEric.Taylor@Sun.COM else if (put_stats_zhdl(zhp, &zc) != 0) 4178228SEric.Taylor@Sun.COM rc = -1; 4188228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4198228SEric.Taylor@Sun.COM return (rc); 4208228SEric.Taylor@Sun.COM } 4218228SEric.Taylor@Sun.COM 422789Sahrens /* 423789Sahrens * Refresh the properties currently stored in the handle. 424789Sahrens */ 425789Sahrens void 426789Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 427789Sahrens { 428789Sahrens (void) get_stats(zhp); 429789Sahrens } 430789Sahrens 431789Sahrens /* 432789Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 433789Sahrens * zfs_iter_* to create child handles on the fly. 434789Sahrens */ 4358228SEric.Taylor@Sun.COM static int 4368228SEric.Taylor@Sun.COM make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 437789Sahrens { 43810298SMatthew.Ahrens@Sun.COM if (put_stats_zhdl(zhp, zc) != 0) 4398228SEric.Taylor@Sun.COM return (-1); 4401758Sahrens 441789Sahrens /* 442789Sahrens * We've managed to open the dataset and gather statistics. Determine 443789Sahrens * the high-level type. 444789Sahrens */ 4452885Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 4462885Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 4472885Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 4482885Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 4492885Sahrens else 4502885Sahrens abort(); 4512885Sahrens 452789Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 453789Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 454789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 455789Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 456789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 457789Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 458789Sahrens else 4592082Seschrock abort(); /* we should never see any other types */ 460789Sahrens 46111963SAfshin.Ardakani@Sun.COM if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 46211963SAfshin.Ardakani@Sun.COM return (-1); 46311963SAfshin.Ardakani@Sun.COM 4648228SEric.Taylor@Sun.COM return (0); 4658228SEric.Taylor@Sun.COM } 4668228SEric.Taylor@Sun.COM 4678228SEric.Taylor@Sun.COM zfs_handle_t * 4688228SEric.Taylor@Sun.COM make_dataset_handle(libzfs_handle_t *hdl, const char *path) 4698228SEric.Taylor@Sun.COM { 4708228SEric.Taylor@Sun.COM zfs_cmd_t zc = { 0 }; 4718228SEric.Taylor@Sun.COM 4728228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 4738228SEric.Taylor@Sun.COM 4748228SEric.Taylor@Sun.COM if (zhp == NULL) 4758228SEric.Taylor@Sun.COM return (NULL); 4768228SEric.Taylor@Sun.COM 4778228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 4788228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 4798228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 4808228SEric.Taylor@Sun.COM free(zhp); 4818228SEric.Taylor@Sun.COM return (NULL); 4828228SEric.Taylor@Sun.COM } 4838228SEric.Taylor@Sun.COM if (get_stats_ioctl(zhp, &zc) == -1) { 4848228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4858228SEric.Taylor@Sun.COM free(zhp); 4868228SEric.Taylor@Sun.COM return (NULL); 4878228SEric.Taylor@Sun.COM } 4888228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, &zc) == -1) { 4898228SEric.Taylor@Sun.COM free(zhp); 4908228SEric.Taylor@Sun.COM zhp = NULL; 4918228SEric.Taylor@Sun.COM } 4928228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 4938228SEric.Taylor@Sun.COM return (zhp); 4948228SEric.Taylor@Sun.COM } 4958228SEric.Taylor@Sun.COM 4968228SEric.Taylor@Sun.COM static zfs_handle_t * 4978228SEric.Taylor@Sun.COM make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 4988228SEric.Taylor@Sun.COM { 4998228SEric.Taylor@Sun.COM zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 5008228SEric.Taylor@Sun.COM 5018228SEric.Taylor@Sun.COM if (zhp == NULL) 5028228SEric.Taylor@Sun.COM return (NULL); 5038228SEric.Taylor@Sun.COM 5048228SEric.Taylor@Sun.COM zhp->zfs_hdl = hdl; 5058228SEric.Taylor@Sun.COM (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 5068228SEric.Taylor@Sun.COM if (make_dataset_handle_common(zhp, zc) == -1) { 5078228SEric.Taylor@Sun.COM free(zhp); 5088228SEric.Taylor@Sun.COM return (NULL); 5098228SEric.Taylor@Sun.COM } 510789Sahrens return (zhp); 511789Sahrens } 512789Sahrens 513789Sahrens /* 514789Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 515789Sahrens * argument is a mask of acceptable types. The function will print an 516789Sahrens * appropriate error message and return NULL if it can't be opened. 517789Sahrens */ 518789Sahrens zfs_handle_t * 5192082Seschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 520789Sahrens { 521789Sahrens zfs_handle_t *zhp; 5222082Seschrock char errbuf[1024]; 5232082Seschrock 5242082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 5252082Seschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 526789Sahrens 527789Sahrens /* 5282082Seschrock * Validate the name before we even try to open it. 529789Sahrens */ 5305326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 5312082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5322082Seschrock "invalid dataset name")); 5332082Seschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 534789Sahrens return (NULL); 535789Sahrens } 536789Sahrens 537789Sahrens /* 538789Sahrens * Try to get stats for the dataset, which will tell us if it exists. 539789Sahrens */ 540789Sahrens errno = 0; 5412082Seschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 5423237Slling (void) zfs_standard_error(hdl, errno, errbuf); 543789Sahrens return (NULL); 544789Sahrens } 545789Sahrens 546789Sahrens if (!(types & zhp->zfs_type)) { 5472082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 5482142Seschrock zfs_close(zhp); 549789Sahrens return (NULL); 550789Sahrens } 551789Sahrens 552789Sahrens return (zhp); 553789Sahrens } 554789Sahrens 555789Sahrens /* 556789Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 557789Sahrens */ 558789Sahrens void 559789Sahrens zfs_close(zfs_handle_t *zhp) 560789Sahrens { 561789Sahrens if (zhp->zfs_mntopts) 562789Sahrens free(zhp->zfs_mntopts); 5632676Seschrock nvlist_free(zhp->zfs_props); 5642676Seschrock nvlist_free(zhp->zfs_user_props); 56511022STom.Erickson@Sun.COM nvlist_free(zhp->zfs_recvd_props); 566789Sahrens free(zhp); 567789Sahrens } 568789Sahrens 5698228SEric.Taylor@Sun.COM typedef struct mnttab_node { 5708228SEric.Taylor@Sun.COM struct mnttab mtn_mt; 5718228SEric.Taylor@Sun.COM avl_node_t mtn_node; 5728228SEric.Taylor@Sun.COM } mnttab_node_t; 5738228SEric.Taylor@Sun.COM 5748228SEric.Taylor@Sun.COM static int 5758228SEric.Taylor@Sun.COM libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 5768228SEric.Taylor@Sun.COM { 5778228SEric.Taylor@Sun.COM const mnttab_node_t *mtn1 = arg1; 5788228SEric.Taylor@Sun.COM const mnttab_node_t *mtn2 = arg2; 5798228SEric.Taylor@Sun.COM int rv; 5808228SEric.Taylor@Sun.COM 5818228SEric.Taylor@Sun.COM rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 5828228SEric.Taylor@Sun.COM 5838228SEric.Taylor@Sun.COM if (rv == 0) 5848228SEric.Taylor@Sun.COM return (0); 5858228SEric.Taylor@Sun.COM return (rv > 0 ? 1 : -1); 5868228SEric.Taylor@Sun.COM } 5878228SEric.Taylor@Sun.COM 5888228SEric.Taylor@Sun.COM void 5898228SEric.Taylor@Sun.COM libzfs_mnttab_init(libzfs_handle_t *hdl) 5908228SEric.Taylor@Sun.COM { 5918228SEric.Taylor@Sun.COM assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 5928228SEric.Taylor@Sun.COM avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 5938228SEric.Taylor@Sun.COM sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 5948811SEric.Taylor@Sun.COM } 5958811SEric.Taylor@Sun.COM 5968811SEric.Taylor@Sun.COM void 5978811SEric.Taylor@Sun.COM libzfs_mnttab_update(libzfs_handle_t *hdl) 5988811SEric.Taylor@Sun.COM { 5998811SEric.Taylor@Sun.COM struct mnttab entry; 6008228SEric.Taylor@Sun.COM 6018228SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6028228SEric.Taylor@Sun.COM while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 6038228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6048228SEric.Taylor@Sun.COM 6058228SEric.Taylor@Sun.COM if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6068228SEric.Taylor@Sun.COM continue; 6078228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6088228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 6098228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 6108228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 6118228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 6128228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6138228SEric.Taylor@Sun.COM } 6148228SEric.Taylor@Sun.COM } 6158228SEric.Taylor@Sun.COM 6168228SEric.Taylor@Sun.COM void 6178228SEric.Taylor@Sun.COM libzfs_mnttab_fini(libzfs_handle_t *hdl) 6188228SEric.Taylor@Sun.COM { 6198228SEric.Taylor@Sun.COM void *cookie = NULL; 6208228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6218228SEric.Taylor@Sun.COM 6228228SEric.Taylor@Sun.COM while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 6238228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_special); 6248228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mountp); 6258228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_fstype); 6268228SEric.Taylor@Sun.COM free(mtn->mtn_mt.mnt_mntopts); 6278228SEric.Taylor@Sun.COM free(mtn); 6288228SEric.Taylor@Sun.COM } 6298228SEric.Taylor@Sun.COM avl_destroy(&hdl->libzfs_mnttab_cache); 6308228SEric.Taylor@Sun.COM } 6318228SEric.Taylor@Sun.COM 6328811SEric.Taylor@Sun.COM void 6338811SEric.Taylor@Sun.COM libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 6348811SEric.Taylor@Sun.COM { 6358811SEric.Taylor@Sun.COM hdl->libzfs_mnttab_enable = enable; 6368811SEric.Taylor@Sun.COM } 6378811SEric.Taylor@Sun.COM 6388228SEric.Taylor@Sun.COM int 6398228SEric.Taylor@Sun.COM libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 6408228SEric.Taylor@Sun.COM struct mnttab *entry) 6418228SEric.Taylor@Sun.COM { 6428228SEric.Taylor@Sun.COM mnttab_node_t find; 6438228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6448228SEric.Taylor@Sun.COM 6458811SEric.Taylor@Sun.COM if (!hdl->libzfs_mnttab_enable) { 6468811SEric.Taylor@Sun.COM struct mnttab srch = { 0 }; 6478811SEric.Taylor@Sun.COM 6488811SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 6498811SEric.Taylor@Sun.COM libzfs_mnttab_fini(hdl); 6508811SEric.Taylor@Sun.COM rewind(hdl->libzfs_mnttab); 6518811SEric.Taylor@Sun.COM srch.mnt_special = (char *)fsname; 6528811SEric.Taylor@Sun.COM srch.mnt_fstype = MNTTYPE_ZFS; 6538811SEric.Taylor@Sun.COM if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 6548811SEric.Taylor@Sun.COM return (0); 6558811SEric.Taylor@Sun.COM else 6568811SEric.Taylor@Sun.COM return (ENOENT); 6578811SEric.Taylor@Sun.COM } 6588811SEric.Taylor@Sun.COM 6598228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6608811SEric.Taylor@Sun.COM libzfs_mnttab_update(hdl); 6618228SEric.Taylor@Sun.COM 6628228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6638228SEric.Taylor@Sun.COM mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 6648228SEric.Taylor@Sun.COM if (mtn) { 6658228SEric.Taylor@Sun.COM *entry = mtn->mtn_mt; 6668228SEric.Taylor@Sun.COM return (0); 6678228SEric.Taylor@Sun.COM } 6688228SEric.Taylor@Sun.COM return (ENOENT); 6698228SEric.Taylor@Sun.COM } 6708228SEric.Taylor@Sun.COM 6718228SEric.Taylor@Sun.COM void 6728228SEric.Taylor@Sun.COM libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 6738228SEric.Taylor@Sun.COM const char *mountp, const char *mntopts) 6748228SEric.Taylor@Sun.COM { 6758228SEric.Taylor@Sun.COM mnttab_node_t *mtn; 6768228SEric.Taylor@Sun.COM 6778228SEric.Taylor@Sun.COM if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 6788228SEric.Taylor@Sun.COM return; 6798228SEric.Taylor@Sun.COM mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 6808228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 6818228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 6828228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 6838228SEric.Taylor@Sun.COM mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 6848228SEric.Taylor@Sun.COM avl_add(&hdl->libzfs_mnttab_cache, mtn); 6858228SEric.Taylor@Sun.COM } 6868228SEric.Taylor@Sun.COM 6878228SEric.Taylor@Sun.COM void 6888228SEric.Taylor@Sun.COM libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 6898228SEric.Taylor@Sun.COM { 6908228SEric.Taylor@Sun.COM mnttab_node_t find; 6918228SEric.Taylor@Sun.COM mnttab_node_t *ret; 6928228SEric.Taylor@Sun.COM 6938228SEric.Taylor@Sun.COM find.mtn_mt.mnt_special = (char *)fsname; 6948228SEric.Taylor@Sun.COM if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 6958228SEric.Taylor@Sun.COM avl_remove(&hdl->libzfs_mnttab_cache, ret); 6968228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_special); 6978228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mountp); 6988228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_fstype); 6998228SEric.Taylor@Sun.COM free(ret->mtn_mt.mnt_mntopts); 7008228SEric.Taylor@Sun.COM free(ret); 7018228SEric.Taylor@Sun.COM } 7028228SEric.Taylor@Sun.COM } 7038228SEric.Taylor@Sun.COM 7045713Srm160521 int 7055713Srm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 7065713Srm160521 { 7076865Srm160521 zpool_handle_t *zpool_handle = zhp->zpool_hdl; 7086865Srm160521 7095713Srm160521 if (zpool_handle == NULL) 7105713Srm160521 return (-1); 7115713Srm160521 7125713Srm160521 *spa_version = zpool_get_prop_int(zpool_handle, 7135713Srm160521 ZPOOL_PROP_VERSION, NULL); 7145713Srm160521 return (0); 7155713Srm160521 } 7165713Srm160521 7175713Srm160521 /* 7185713Srm160521 * The choice of reservation property depends on the SPA version. 7195713Srm160521 */ 7205713Srm160521 static int 7215713Srm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 7225713Srm160521 { 7235713Srm160521 int spa_version; 7245713Srm160521 7255713Srm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 7265713Srm160521 return (-1); 7275713Srm160521 7285713Srm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 7295713Srm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 7305713Srm160521 else 7315713Srm160521 *resv_prop = ZFS_PROP_RESERVATION; 7325713Srm160521 7335713Srm160521 return (0); 7345713Srm160521 } 7355713Srm160521 7363912Slling /* 7372676Seschrock * Given an nvlist of properties to set, validates that they are correct, and 7382676Seschrock * parses any numeric properties (index, boolean, etc) if they are specified as 7392676Seschrock * strings. 740789Sahrens */ 7417184Stimh nvlist_t * 7427184Stimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 7435094Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 744789Sahrens { 7452676Seschrock nvpair_t *elem; 7462676Seschrock uint64_t intval; 7472676Seschrock char *strval; 7485094Slling zfs_prop_t prop; 7492676Seschrock nvlist_t *ret; 7505331Samw int chosen_normal = -1; 7515331Samw int chosen_utf = -1; 7522676Seschrock 7535094Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 7545094Slling (void) no_memory(hdl); 7555094Slling return (NULL); 756789Sahrens } 757789Sahrens 7589396SMatthew.Ahrens@Sun.COM /* 7599396SMatthew.Ahrens@Sun.COM * Make sure this property is valid and applies to this type. 7609396SMatthew.Ahrens@Sun.COM */ 7619396SMatthew.Ahrens@Sun.COM 7622676Seschrock elem = NULL; 7632676Seschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 7645094Slling const char *propname = nvpair_name(elem); 7652676Seschrock 7669396SMatthew.Ahrens@Sun.COM prop = zfs_name_to_prop(propname); 7679396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 7685094Slling /* 7699396SMatthew.Ahrens@Sun.COM * This is a user property: make sure it's a 7705094Slling * string, and that it's less than ZAP_MAXNAMELEN. 7715094Slling */ 7725094Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 7735094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7745094Slling "'%s' must be a string"), propname); 7755094Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7765094Slling goto error; 7775094Slling } 7785094Slling 7795094Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 7805094Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7815094Slling "property name '%s' is too long"), 7822676Seschrock propname); 7832676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 7842676Seschrock goto error; 7852676Seschrock } 7862676Seschrock 7872676Seschrock (void) nvpair_value_string(elem, &strval); 7882676Seschrock if (nvlist_add_string(ret, propname, strval) != 0) { 7892676Seschrock (void) no_memory(hdl); 7902676Seschrock goto error; 7912676Seschrock } 7922676Seschrock continue; 793789Sahrens } 7942676Seschrock 7959396SMatthew.Ahrens@Sun.COM /* 7969396SMatthew.Ahrens@Sun.COM * Currently, only user properties can be modified on 7979396SMatthew.Ahrens@Sun.COM * snapshots. 7989396SMatthew.Ahrens@Sun.COM */ 7997265Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 8007265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8017265Sahrens "this property can not be modified for snapshots")); 8027265Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8037265Sahrens goto error; 8047265Sahrens } 8057265Sahrens 8069396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 8079396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t uqtype; 8089396SMatthew.Ahrens@Sun.COM char newpropname[128]; 8099396SMatthew.Ahrens@Sun.COM char domain[128]; 8109396SMatthew.Ahrens@Sun.COM uint64_t rid; 8119396SMatthew.Ahrens@Sun.COM uint64_t valary[3]; 8129396SMatthew.Ahrens@Sun.COM 8139396SMatthew.Ahrens@Sun.COM if (userquota_propname_decode(propname, zoned, 8149396SMatthew.Ahrens@Sun.COM &uqtype, domain, sizeof (domain), &rid) != 0) { 8159396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8169396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, 8179396SMatthew.Ahrens@Sun.COM "'%s' has an invalid user/group name"), 8189396SMatthew.Ahrens@Sun.COM propname); 8199396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8209396SMatthew.Ahrens@Sun.COM goto error; 8219396SMatthew.Ahrens@Sun.COM } 8229396SMatthew.Ahrens@Sun.COM 8239396SMatthew.Ahrens@Sun.COM if (uqtype != ZFS_PROP_USERQUOTA && 8249396SMatthew.Ahrens@Sun.COM uqtype != ZFS_PROP_GROUPQUOTA) { 8259396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, 8269396SMatthew.Ahrens@Sun.COM dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8279396SMatthew.Ahrens@Sun.COM propname); 8289396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_PROPREADONLY, 8299396SMatthew.Ahrens@Sun.COM errbuf); 8309396SMatthew.Ahrens@Sun.COM goto error; 8319396SMatthew.Ahrens@Sun.COM } 8329396SMatthew.Ahrens@Sun.COM 8339396SMatthew.Ahrens@Sun.COM if (nvpair_type(elem) == DATA_TYPE_STRING) { 8349396SMatthew.Ahrens@Sun.COM (void) nvpair_value_string(elem, &strval); 8359396SMatthew.Ahrens@Sun.COM if (strcmp(strval, "none") == 0) { 8369396SMatthew.Ahrens@Sun.COM intval = 0; 8379396SMatthew.Ahrens@Sun.COM } else if (zfs_nicestrtonum(hdl, 8389396SMatthew.Ahrens@Sun.COM strval, &intval) != 0) { 8399396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, 8409396SMatthew.Ahrens@Sun.COM EZFS_BADPROP, errbuf); 8419396SMatthew.Ahrens@Sun.COM goto error; 8429396SMatthew.Ahrens@Sun.COM } 8439396SMatthew.Ahrens@Sun.COM } else if (nvpair_type(elem) == 8449396SMatthew.Ahrens@Sun.COM DATA_TYPE_UINT64) { 8459396SMatthew.Ahrens@Sun.COM (void) nvpair_value_uint64(elem, &intval); 8469396SMatthew.Ahrens@Sun.COM if (intval == 0) { 8479396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8489396SMatthew.Ahrens@Sun.COM "use 'none' to disable " 8499396SMatthew.Ahrens@Sun.COM "userquota/groupquota")); 8509396SMatthew.Ahrens@Sun.COM goto error; 8519396SMatthew.Ahrens@Sun.COM } 8529396SMatthew.Ahrens@Sun.COM } else { 8539396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8549396SMatthew.Ahrens@Sun.COM "'%s' must be a number"), propname); 8559396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8569396SMatthew.Ahrens@Sun.COM goto error; 8579396SMatthew.Ahrens@Sun.COM } 8589396SMatthew.Ahrens@Sun.COM 85910969SMatthew.Ahrens@Sun.COM /* 86010969SMatthew.Ahrens@Sun.COM * Encode the prop name as 86110969SMatthew.Ahrens@Sun.COM * userquota@<hex-rid>-domain, to make it easy 86210969SMatthew.Ahrens@Sun.COM * for the kernel to decode. 86310969SMatthew.Ahrens@Sun.COM */ 8649396SMatthew.Ahrens@Sun.COM (void) snprintf(newpropname, sizeof (newpropname), 86510969SMatthew.Ahrens@Sun.COM "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 86610969SMatthew.Ahrens@Sun.COM (longlong_t)rid, domain); 8679396SMatthew.Ahrens@Sun.COM valary[0] = uqtype; 8689396SMatthew.Ahrens@Sun.COM valary[1] = rid; 8699396SMatthew.Ahrens@Sun.COM valary[2] = intval; 8709396SMatthew.Ahrens@Sun.COM if (nvlist_add_uint64_array(ret, newpropname, 8719396SMatthew.Ahrens@Sun.COM valary, 3) != 0) { 8729396SMatthew.Ahrens@Sun.COM (void) no_memory(hdl); 8739396SMatthew.Ahrens@Sun.COM goto error; 8749396SMatthew.Ahrens@Sun.COM } 8759396SMatthew.Ahrens@Sun.COM continue; 8769396SMatthew.Ahrens@Sun.COM } 8779396SMatthew.Ahrens@Sun.COM 8789396SMatthew.Ahrens@Sun.COM if (prop == ZPROP_INVAL) { 8799396SMatthew.Ahrens@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8809396SMatthew.Ahrens@Sun.COM "invalid property '%s'"), propname); 8819396SMatthew.Ahrens@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 8829396SMatthew.Ahrens@Sun.COM goto error; 8839396SMatthew.Ahrens@Sun.COM } 8849396SMatthew.Ahrens@Sun.COM 8852676Seschrock if (!zfs_prop_valid_for_type(prop, type)) { 8862676Seschrock zfs_error_aux(hdl, 8872676Seschrock dgettext(TEXT_DOMAIN, "'%s' does not " 8882676Seschrock "apply to datasets of this type"), propname); 8892676Seschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 8902676Seschrock goto error; 8912676Seschrock } 8922676Seschrock 8932676Seschrock if (zfs_prop_readonly(prop) && 8945331Samw (!zfs_prop_setonce(prop) || zhp != NULL)) { 8952676Seschrock zfs_error_aux(hdl, 8962676Seschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 8972676Seschrock propname); 8982676Seschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 8992676Seschrock goto error; 9002676Seschrock } 9012676Seschrock 9025094Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 9035094Slling &strval, &intval, errbuf) != 0) 9042676Seschrock goto error; 9052676Seschrock 9062676Seschrock /* 9072676Seschrock * Perform some additional checks for specific properties. 9082676Seschrock */ 9092676Seschrock switch (prop) { 9104577Sahrens case ZFS_PROP_VERSION: 9114577Sahrens { 9124577Sahrens int version; 9134577Sahrens 9144577Sahrens if (zhp == NULL) 9154577Sahrens break; 9164577Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 9174577Sahrens if (intval < version) { 9184577Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9194577Sahrens "Can not downgrade; already at version %u"), 9204577Sahrens version); 9214577Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9224577Sahrens goto error; 9234577Sahrens } 9244577Sahrens break; 9254577Sahrens } 9264577Sahrens 9272676Seschrock case ZFS_PROP_RECORDSIZE: 9282676Seschrock case ZFS_PROP_VOLBLOCKSIZE: 9292676Seschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 9302676Seschrock if (intval < SPA_MINBLOCKSIZE || 9312676Seschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 9322082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9332676Seschrock "'%s' must be power of 2 from %u " 9342676Seschrock "to %uk"), propname, 9352676Seschrock (uint_t)SPA_MINBLOCKSIZE, 9362676Seschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 9372676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 9382676Seschrock goto error; 939789Sahrens } 940789Sahrens break; 941789Sahrens 94210972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 94310972SRic.Aleshire@Sun.COM { 94410972SRic.Aleshire@Sun.COM /* 94510972SRic.Aleshire@Sun.COM * Verify the mlslabel string and convert to 94610972SRic.Aleshire@Sun.COM * internal hex label string. 94710972SRic.Aleshire@Sun.COM */ 94810972SRic.Aleshire@Sun.COM 94910972SRic.Aleshire@Sun.COM m_label_t *new_sl; 95010972SRic.Aleshire@Sun.COM char *hex = NULL; /* internal label string */ 95110972SRic.Aleshire@Sun.COM 95210972SRic.Aleshire@Sun.COM /* Default value is already OK. */ 95310972SRic.Aleshire@Sun.COM if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 95410972SRic.Aleshire@Sun.COM break; 95510972SRic.Aleshire@Sun.COM 95610972SRic.Aleshire@Sun.COM /* Verify the label can be converted to binary form */ 95710972SRic.Aleshire@Sun.COM if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 95810972SRic.Aleshire@Sun.COM (str_to_label(strval, &new_sl, MAC_LABEL, 95910972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1)) { 96010972SRic.Aleshire@Sun.COM goto badlabel; 96110972SRic.Aleshire@Sun.COM } 96210972SRic.Aleshire@Sun.COM 96310972SRic.Aleshire@Sun.COM /* Now translate to hex internal label string */ 96410972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &hex, M_INTERNAL, 96510972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 96610972SRic.Aleshire@Sun.COM if (hex) 96710972SRic.Aleshire@Sun.COM free(hex); 96810972SRic.Aleshire@Sun.COM goto badlabel; 96910972SRic.Aleshire@Sun.COM } 97010972SRic.Aleshire@Sun.COM m_label_free(new_sl); 97110972SRic.Aleshire@Sun.COM 97210972SRic.Aleshire@Sun.COM /* If string is already in internal form, we're done. */ 97310972SRic.Aleshire@Sun.COM if (strcmp(strval, hex) == 0) { 97410972SRic.Aleshire@Sun.COM free(hex); 97510972SRic.Aleshire@Sun.COM break; 97610972SRic.Aleshire@Sun.COM } 97710972SRic.Aleshire@Sun.COM 97810972SRic.Aleshire@Sun.COM /* Replace the label string with the internal form. */ 97910984SRic.Aleshire@Sun.COM (void) nvlist_remove(ret, zfs_prop_to_name(prop), 98010972SRic.Aleshire@Sun.COM DATA_TYPE_STRING); 98110972SRic.Aleshire@Sun.COM verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 98210972SRic.Aleshire@Sun.COM hex) == 0); 98310972SRic.Aleshire@Sun.COM free(hex); 98410972SRic.Aleshire@Sun.COM 98510972SRic.Aleshire@Sun.COM break; 98610972SRic.Aleshire@Sun.COM 98710972SRic.Aleshire@Sun.COM badlabel: 98810972SRic.Aleshire@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 98910972SRic.Aleshire@Sun.COM "invalid mlslabel '%s'"), strval); 99010972SRic.Aleshire@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 99110972SRic.Aleshire@Sun.COM m_label_free(new_sl); /* OK if null */ 99210972SRic.Aleshire@Sun.COM goto error; 99310972SRic.Aleshire@Sun.COM 99410972SRic.Aleshire@Sun.COM } 99510972SRic.Aleshire@Sun.COM 9962676Seschrock case ZFS_PROP_MOUNTPOINT: 9974778Srm160521 { 9984778Srm160521 namecheck_err_t why; 9994778Srm160521 10002676Seschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 10012676Seschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 10022676Seschrock break; 10032676Seschrock 10044778Srm160521 if (mountpoint_namecheck(strval, &why)) { 10054778Srm160521 switch (why) { 10064778Srm160521 case NAME_ERR_LEADING_SLASH: 10074778Srm160521 zfs_error_aux(hdl, 10084778Srm160521 dgettext(TEXT_DOMAIN, 10094778Srm160521 "'%s' must be an absolute path, " 10104778Srm160521 "'none', or 'legacy'"), propname); 10114778Srm160521 break; 10124778Srm160521 case NAME_ERR_TOOLONG: 10134778Srm160521 zfs_error_aux(hdl, 10144778Srm160521 dgettext(TEXT_DOMAIN, 10154778Srm160521 "component of '%s' is too long"), 10164778Srm160521 propname); 10174778Srm160521 break; 10184778Srm160521 } 10192676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 10202676Seschrock goto error; 1021789Sahrens } 10224778Srm160521 } 10234778Srm160521 10243126Sahl /*FALLTHRU*/ 10253126Sahl 10265331Samw case ZFS_PROP_SHARESMB: 10273126Sahl case ZFS_PROP_SHARENFS: 10283126Sahl /* 10295331Samw * For the mountpoint and sharenfs or sharesmb 10305331Samw * properties, check if it can be set in a 10315331Samw * global/non-global zone based on 10323126Sahl * the zoned property value: 10333126Sahl * 10343126Sahl * global zone non-global zone 10353126Sahl * -------------------------------------------------- 10363126Sahl * zoned=on mountpoint (no) mountpoint (yes) 10373126Sahl * sharenfs (no) sharenfs (no) 10385331Samw * sharesmb (no) sharesmb (no) 10393126Sahl * 10403126Sahl * zoned=off mountpoint (yes) N/A 10413126Sahl * sharenfs (yes) 10425331Samw * sharesmb (yes) 10433126Sahl */ 10442676Seschrock if (zoned) { 10452676Seschrock if (getzoneid() == GLOBAL_ZONEID) { 10462676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10472676Seschrock "'%s' cannot be set on " 10482676Seschrock "dataset in a non-global zone"), 10492676Seschrock propname); 10502676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10512676Seschrock errbuf); 10522676Seschrock goto error; 10535331Samw } else if (prop == ZFS_PROP_SHARENFS || 10545331Samw prop == ZFS_PROP_SHARESMB) { 10552676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10562676Seschrock "'%s' cannot be set in " 10572676Seschrock "a non-global zone"), propname); 10582676Seschrock (void) zfs_error(hdl, EZFS_ZONED, 10592676Seschrock errbuf); 10602676Seschrock goto error; 10612676Seschrock } 10622676Seschrock } else if (getzoneid() != GLOBAL_ZONEID) { 10632676Seschrock /* 10642676Seschrock * If zoned property is 'off', this must be in 10659396SMatthew.Ahrens@Sun.COM * a global zone. If not, something is wrong. 10662676Seschrock */ 10672676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 10682676Seschrock "'%s' cannot be set while dataset " 10692676Seschrock "'zoned' property is set"), propname); 10702676Seschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 10712676Seschrock goto error; 10722676Seschrock } 10733126Sahl 10744180Sdougm /* 10754180Sdougm * At this point, it is legitimate to set the 10764180Sdougm * property. Now we want to make sure that the 10774180Sdougm * property value is valid if it is sharenfs. 10784180Sdougm */ 10795331Samw if ((prop == ZFS_PROP_SHARENFS || 10805331Samw prop == ZFS_PROP_SHARESMB) && 10814217Seschrock strcmp(strval, "on") != 0 && 10824217Seschrock strcmp(strval, "off") != 0) { 10835331Samw zfs_share_proto_t proto; 10845331Samw 10855331Samw if (prop == ZFS_PROP_SHARESMB) 10865331Samw proto = PROTO_SMB; 10875331Samw else 10885331Samw proto = PROTO_NFS; 10894180Sdougm 10904180Sdougm /* 10915331Samw * Must be an valid sharing protocol 10925331Samw * option string so init the libshare 10935331Samw * in order to enable the parser and 10945331Samw * then parse the options. We use the 10955331Samw * control API since we don't care about 10965331Samw * the current configuration and don't 10974180Sdougm * want the overhead of loading it 10984180Sdougm * until we actually do something. 10994180Sdougm */ 11004180Sdougm 11014217Seschrock if (zfs_init_libshare(hdl, 11024217Seschrock SA_INIT_CONTROL_API) != SA_OK) { 11034217Seschrock /* 11044217Seschrock * An error occurred so we can't do 11054217Seschrock * anything 11064217Seschrock */ 11074217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11084217Seschrock "'%s' cannot be set: problem " 11094217Seschrock "in share initialization"), 11104217Seschrock propname); 11114217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11124217Seschrock errbuf); 11134217Seschrock goto error; 11144217Seschrock } 11154217Seschrock 11165331Samw if (zfs_parse_options(strval, proto) != SA_OK) { 11174217Seschrock /* 11184217Seschrock * There was an error in parsing so 11194217Seschrock * deal with it by issuing an error 11204217Seschrock * message and leaving after 11214217Seschrock * uninitializing the the libshare 11224217Seschrock * interface. 11234217Seschrock */ 11244217Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11254217Seschrock "'%s' cannot be set to invalid " 11264217Seschrock "options"), propname); 11274217Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11284217Seschrock errbuf); 11294217Seschrock zfs_uninit_libshare(hdl); 11304217Seschrock goto error; 11314217Seschrock } 11324180Sdougm zfs_uninit_libshare(hdl); 11334180Sdougm } 11344180Sdougm 11353126Sahl break; 11365331Samw case ZFS_PROP_UTF8ONLY: 11375331Samw chosen_utf = (int)intval; 11385331Samw break; 11395331Samw case ZFS_PROP_NORMALIZE: 11405331Samw chosen_normal = (int)intval; 11415331Samw break; 11422676Seschrock } 11432676Seschrock 11442676Seschrock /* 11452676Seschrock * For changes to existing volumes, we have some additional 11462676Seschrock * checks to enforce. 11472676Seschrock */ 11482676Seschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 11492676Seschrock uint64_t volsize = zfs_prop_get_int(zhp, 11502676Seschrock ZFS_PROP_VOLSIZE); 11512676Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 11522676Seschrock ZFS_PROP_VOLBLOCKSIZE); 11532676Seschrock char buf[64]; 11542676Seschrock 11552676Seschrock switch (prop) { 11562676Seschrock case ZFS_PROP_RESERVATION: 11575378Sck153898 case ZFS_PROP_REFRESERVATION: 11582676Seschrock if (intval > volsize) { 11592676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11602676Seschrock "'%s' is greater than current " 11612676Seschrock "volume size"), propname); 11622676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11632676Seschrock errbuf); 11642676Seschrock goto error; 11652676Seschrock } 11662676Seschrock break; 11672676Seschrock 11682676Seschrock case ZFS_PROP_VOLSIZE: 11692676Seschrock if (intval % blocksize != 0) { 11702676Seschrock zfs_nicenum(blocksize, buf, 11712676Seschrock sizeof (buf)); 11722676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11732676Seschrock "'%s' must be a multiple of " 11742676Seschrock "volume block size (%s)"), 11752676Seschrock propname, buf); 11762676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11772676Seschrock errbuf); 11782676Seschrock goto error; 11792676Seschrock } 11802676Seschrock 11812676Seschrock if (intval == 0) { 11822676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11832676Seschrock "'%s' cannot be zero"), 11842676Seschrock propname); 11852676Seschrock (void) zfs_error(hdl, EZFS_BADPROP, 11862676Seschrock errbuf); 11872676Seschrock goto error; 1188789Sahrens } 11893126Sahl break; 1190789Sahrens } 1191789Sahrens } 1192789Sahrens } 1193789Sahrens 11942676Seschrock /* 11955331Samw * If normalization was chosen, but no UTF8 choice was made, 11965331Samw * enforce rejection of non-UTF8 names. 11975331Samw * 11985331Samw * If normalization was chosen, but rejecting non-UTF8 names 11995331Samw * was explicitly not chosen, it is an error. 12005331Samw */ 12015498Stimh if (chosen_normal > 0 && chosen_utf < 0) { 12025331Samw if (nvlist_add_uint64(ret, 12035331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 12045331Samw (void) no_memory(hdl); 12055331Samw goto error; 12065331Samw } 12075498Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 12085331Samw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12095331Samw "'%s' must be set 'on' if normalization chosen"), 12105331Samw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 12115331Samw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 12125331Samw goto error; 12135331Samw } 1214*12496SEric.Taylor@Sun.COM return (ret); 1215*12496SEric.Taylor@Sun.COM 1216*12496SEric.Taylor@Sun.COM error: 1217*12496SEric.Taylor@Sun.COM nvlist_free(ret); 1218*12496SEric.Taylor@Sun.COM return (NULL); 1219*12496SEric.Taylor@Sun.COM } 1220*12496SEric.Taylor@Sun.COM 1221*12496SEric.Taylor@Sun.COM int 1222*12496SEric.Taylor@Sun.COM zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) 1223*12496SEric.Taylor@Sun.COM { 1224*12496SEric.Taylor@Sun.COM uint64_t old_volsize; 1225*12496SEric.Taylor@Sun.COM uint64_t new_volsize; 1226*12496SEric.Taylor@Sun.COM uint64_t old_reservation; 1227*12496SEric.Taylor@Sun.COM uint64_t new_reservation; 1228*12496SEric.Taylor@Sun.COM zfs_prop_t resv_prop; 12295331Samw 12305331Samw /* 12312676Seschrock * If this is an existing volume, and someone is setting the volsize, 12322676Seschrock * make sure that it matches the reservation, or add it if necessary. 12332676Seschrock */ 1234*12496SEric.Taylor@Sun.COM old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1235*12496SEric.Taylor@Sun.COM if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1236*12496SEric.Taylor@Sun.COM return (-1); 1237*12496SEric.Taylor@Sun.COM old_reservation = zfs_prop_get_int(zhp, resv_prop); 1238*12496SEric.Taylor@Sun.COM if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) != 1239*12496SEric.Taylor@Sun.COM old_reservation) || nvlist_lookup_uint64(nvl, 1240*12496SEric.Taylor@Sun.COM zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) { 1241*12496SEric.Taylor@Sun.COM return (0); 12422676Seschrock } 1243*12496SEric.Taylor@Sun.COM if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1244*12496SEric.Taylor@Sun.COM &new_volsize) != 0) 1245*12496SEric.Taylor@Sun.COM return (-1); 1246*12496SEric.Taylor@Sun.COM new_reservation = zvol_volsize_to_reservation(new_volsize, 1247*12496SEric.Taylor@Sun.COM zhp->zfs_props); 1248*12496SEric.Taylor@Sun.COM if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), 1249*12496SEric.Taylor@Sun.COM new_reservation) != 0) { 1250*12496SEric.Taylor@Sun.COM (void) no_memory(zhp->zfs_hdl); 1251*12496SEric.Taylor@Sun.COM return (-1); 1252*12496SEric.Taylor@Sun.COM } 1253*12496SEric.Taylor@Sun.COM return (1); 1254789Sahrens } 1255789Sahrens 125611022STom.Erickson@Sun.COM void 125711022STom.Erickson@Sun.COM zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 125811022STom.Erickson@Sun.COM char *errbuf) 125911022STom.Erickson@Sun.COM { 126011022STom.Erickson@Sun.COM switch (err) { 126111022STom.Erickson@Sun.COM 126211022STom.Erickson@Sun.COM case ENOSPC: 126311022STom.Erickson@Sun.COM /* 126411022STom.Erickson@Sun.COM * For quotas and reservations, ENOSPC indicates 126511022STom.Erickson@Sun.COM * something different; setting a quota or reservation 126611022STom.Erickson@Sun.COM * doesn't use any disk space. 126711022STom.Erickson@Sun.COM */ 126811022STom.Erickson@Sun.COM switch (prop) { 126911022STom.Erickson@Sun.COM case ZFS_PROP_QUOTA: 127011022STom.Erickson@Sun.COM case ZFS_PROP_REFQUOTA: 127111022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127211022STom.Erickson@Sun.COM "size is less than current used or " 127311022STom.Erickson@Sun.COM "reserved space")); 127411022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 127511022STom.Erickson@Sun.COM break; 127611022STom.Erickson@Sun.COM 127711022STom.Erickson@Sun.COM case ZFS_PROP_RESERVATION: 127811022STom.Erickson@Sun.COM case ZFS_PROP_REFRESERVATION: 127911022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 128011022STom.Erickson@Sun.COM "size is greater than available space")); 128111022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 128211022STom.Erickson@Sun.COM break; 128311022STom.Erickson@Sun.COM 128411022STom.Erickson@Sun.COM default: 128511022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 128611022STom.Erickson@Sun.COM break; 128711022STom.Erickson@Sun.COM } 128811022STom.Erickson@Sun.COM break; 128911022STom.Erickson@Sun.COM 129011022STom.Erickson@Sun.COM case EBUSY: 129111022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, EBUSY, errbuf); 129211022STom.Erickson@Sun.COM break; 129311022STom.Erickson@Sun.COM 129411022STom.Erickson@Sun.COM case EROFS: 129511022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 129611022STom.Erickson@Sun.COM break; 129711022STom.Erickson@Sun.COM 129811022STom.Erickson@Sun.COM case ENOTSUP: 129911022STom.Erickson@Sun.COM zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130011022STom.Erickson@Sun.COM "pool and or dataset must be upgraded to set this " 130111022STom.Erickson@Sun.COM "property or value")); 130211022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 130311022STom.Erickson@Sun.COM break; 130411022STom.Erickson@Sun.COM 130511022STom.Erickson@Sun.COM case ERANGE: 130611022STom.Erickson@Sun.COM if (prop == ZFS_PROP_COMPRESSION) { 130711022STom.Erickson@Sun.COM (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 130811022STom.Erickson@Sun.COM "property setting is not allowed on " 130911022STom.Erickson@Sun.COM "bootable datasets")); 131011022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 131111022STom.Erickson@Sun.COM } else { 131211022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 131311022STom.Erickson@Sun.COM } 131411022STom.Erickson@Sun.COM break; 131511022STom.Erickson@Sun.COM 131611876SJames.Dunham@Sun.COM case EINVAL: 131711876SJames.Dunham@Sun.COM if (prop == ZPROP_INVAL) { 131811876SJames.Dunham@Sun.COM (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 131911876SJames.Dunham@Sun.COM } else { 132011876SJames.Dunham@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 132111876SJames.Dunham@Sun.COM } 132211876SJames.Dunham@Sun.COM break; 132311876SJames.Dunham@Sun.COM 132411022STom.Erickson@Sun.COM case EOVERFLOW: 132511022STom.Erickson@Sun.COM /* 132611022STom.Erickson@Sun.COM * This platform can't address a volume this big. 132711022STom.Erickson@Sun.COM */ 132811022STom.Erickson@Sun.COM #ifdef _ILP32 132911022STom.Erickson@Sun.COM if (prop == ZFS_PROP_VOLSIZE) { 133011022STom.Erickson@Sun.COM (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 133111022STom.Erickson@Sun.COM break; 133211022STom.Erickson@Sun.COM } 133311022STom.Erickson@Sun.COM #endif 133411022STom.Erickson@Sun.COM /* FALLTHROUGH */ 133511022STom.Erickson@Sun.COM default: 133611022STom.Erickson@Sun.COM (void) zfs_standard_error(hdl, err, errbuf); 133711022STom.Erickson@Sun.COM } 133811022STom.Erickson@Sun.COM } 133911022STom.Erickson@Sun.COM 1340789Sahrens /* 1341789Sahrens * Given a property name and value, set the property for the given dataset. 1342789Sahrens */ 1343789Sahrens int 13442676Seschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1345789Sahrens { 1346789Sahrens zfs_cmd_t zc = { 0 }; 13472676Seschrock int ret = -1; 13482676Seschrock prop_changelist_t *cl = NULL; 13492082Seschrock char errbuf[1024]; 13502082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 13512676Seschrock nvlist_t *nvl = NULL, *realprops; 13522676Seschrock zfs_prop_t prop; 13537509SMark.Musante@Sun.COM boolean_t do_prefix; 13547509SMark.Musante@Sun.COM uint64_t idx; 1355*12496SEric.Taylor@Sun.COM int added_resv; 13562082Seschrock 13572082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 13582676Seschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 13592082Seschrock zhp->zfs_name); 13602082Seschrock 13612676Seschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 13622676Seschrock nvlist_add_string(nvl, propname, propval) != 0) { 13632676Seschrock (void) no_memory(hdl); 13642676Seschrock goto error; 1365789Sahrens } 1366789Sahrens 13677184Stimh if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 13682676Seschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 13692676Seschrock goto error; 13705094Slling 13712676Seschrock nvlist_free(nvl); 13722676Seschrock nvl = realprops; 13732676Seschrock 13742676Seschrock prop = zfs_name_to_prop(propname); 13752676Seschrock 1376*12496SEric.Taylor@Sun.COM if (prop == ZFS_PROP_VOLSIZE) { 1377*12496SEric.Taylor@Sun.COM if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) 1378*12496SEric.Taylor@Sun.COM goto error; 1379*12496SEric.Taylor@Sun.COM } 1380*12496SEric.Taylor@Sun.COM 13817366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 13822676Seschrock goto error; 1383789Sahrens 1384789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 13852082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 13862082Seschrock "child dataset with inherited mountpoint is used " 13872082Seschrock "in a non-global zone")); 13882082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1389789Sahrens goto error; 1390789Sahrens } 1391789Sahrens 13927509SMark.Musante@Sun.COM /* 13937509SMark.Musante@Sun.COM * If the dataset's canmount property is being set to noauto, 13947509SMark.Musante@Sun.COM * then we want to prevent unmounting & remounting it. 13957509SMark.Musante@Sun.COM */ 13967509SMark.Musante@Sun.COM do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 13977509SMark.Musante@Sun.COM (zprop_string_to_index(prop, propval, &idx, 13987509SMark.Musante@Sun.COM ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 13996168Shs24103 14006168Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 14017509SMark.Musante@Sun.COM goto error; 1402789Sahrens 1403789Sahrens /* 1404789Sahrens * Execute the corresponding ioctl() to set this property. 1405789Sahrens */ 1406789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1407789Sahrens 14085094Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 14092676Seschrock goto error; 14102676Seschrock 14114543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 14128845Samw@Sun.COM 1413789Sahrens if (ret != 0) { 141411022STom.Erickson@Sun.COM zfs_setprop_error(hdl, prop, errno, errbuf); 1415*12496SEric.Taylor@Sun.COM if (added_resv && errno == ENOSPC) { 1416*12496SEric.Taylor@Sun.COM /* clean up the volsize property we tried to set */ 1417*12496SEric.Taylor@Sun.COM uint64_t old_volsize = zfs_prop_get_int(zhp, 1418*12496SEric.Taylor@Sun.COM ZFS_PROP_VOLSIZE); 1419*12496SEric.Taylor@Sun.COM nvlist_free(nvl); 1420*12496SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 1421*12496SEric.Taylor@Sun.COM if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 1422*12496SEric.Taylor@Sun.COM goto error; 1423*12496SEric.Taylor@Sun.COM if (nvlist_add_uint64(nvl, 1424*12496SEric.Taylor@Sun.COM zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1425*12496SEric.Taylor@Sun.COM old_volsize) != 0) 1426*12496SEric.Taylor@Sun.COM goto error; 1427*12496SEric.Taylor@Sun.COM if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1428*12496SEric.Taylor@Sun.COM goto error; 1429*12496SEric.Taylor@Sun.COM (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1430*12496SEric.Taylor@Sun.COM } 1431789Sahrens } else { 14326168Shs24103 if (do_prefix) 14336168Shs24103 ret = changelist_postfix(cl); 14346168Shs24103 1435789Sahrens /* 1436789Sahrens * Refresh the statistics so the new property value 1437789Sahrens * is reflected. 1438789Sahrens */ 14396168Shs24103 if (ret == 0) 14402676Seschrock (void) get_stats(zhp); 1441789Sahrens } 1442789Sahrens 1443789Sahrens error: 14442676Seschrock nvlist_free(nvl); 14452676Seschrock zcmd_free_nvlists(&zc); 14462676Seschrock if (cl) 14472676Seschrock changelist_free(cl); 1448789Sahrens return (ret); 1449789Sahrens } 1450789Sahrens 1451789Sahrens /* 145211022STom.Erickson@Sun.COM * Given a property, inherit the value from the parent dataset, or if received 145311022STom.Erickson@Sun.COM * is TRUE, revert to the received value, if any. 1454789Sahrens */ 1455789Sahrens int 145611022STom.Erickson@Sun.COM zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1457789Sahrens { 1458789Sahrens zfs_cmd_t zc = { 0 }; 1459789Sahrens int ret; 1460789Sahrens prop_changelist_t *cl; 14612082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 14622082Seschrock char errbuf[1024]; 14632676Seschrock zfs_prop_t prop; 14642082Seschrock 14652082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 14662082Seschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1467789Sahrens 146811022STom.Erickson@Sun.COM zc.zc_cookie = received; 14695094Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 14702676Seschrock /* 14712676Seschrock * For user properties, the amount of work we have to do is very 14722676Seschrock * small, so just do it here. 14732676Seschrock */ 14742676Seschrock if (!zfs_prop_user(propname)) { 14752676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14762676Seschrock "invalid property")); 14772676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 14782676Seschrock } 14792676Seschrock 14802676Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 14812676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 14822676Seschrock 14834849Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 14842676Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 14852676Seschrock 14862676Seschrock return (0); 14872676Seschrock } 14882676Seschrock 1489789Sahrens /* 1490789Sahrens * Verify that this property is inheritable. 1491789Sahrens */ 14922082Seschrock if (zfs_prop_readonly(prop)) 14932082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 14942082Seschrock 149511022STom.Erickson@Sun.COM if (!zfs_prop_inheritable(prop) && !received) 14962082Seschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1497789Sahrens 1498789Sahrens /* 1499789Sahrens * Check to see if the value applies to this type 1500789Sahrens */ 15012082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 15022082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1503789Sahrens 15043443Srm160521 /* 1505*12496SEric.Taylor@Sun.COM * Normalize the name, to get rid of shorthand abbreviations. 15063443Srm160521 */ 15073443Srm160521 propname = zfs_prop_to_name(prop); 1508789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 15092676Seschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1510789Sahrens 1511789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1512789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 15132082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15142082Seschrock "dataset is used in a non-global zone")); 15152082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1516789Sahrens } 1517789Sahrens 1518789Sahrens /* 1519789Sahrens * Determine datasets which will be affected by this change, if any. 1520789Sahrens */ 15217366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1522789Sahrens return (-1); 1523789Sahrens 1524789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 15252082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15262082Seschrock "child dataset with inherited mountpoint is used " 15272082Seschrock "in a non-global zone")); 15282082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1529789Sahrens goto error; 1530789Sahrens } 1531789Sahrens 1532789Sahrens if ((ret = changelist_prefix(cl)) != 0) 1533789Sahrens goto error; 1534789Sahrens 15354849Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 15362082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1537789Sahrens } else { 1538789Sahrens 15392169Snd150628 if ((ret = changelist_postfix(cl)) != 0) 1540789Sahrens goto error; 1541789Sahrens 1542789Sahrens /* 1543789Sahrens * Refresh the statistics so the new property is reflected. 1544789Sahrens */ 1545789Sahrens (void) get_stats(zhp); 1546789Sahrens } 1547789Sahrens 1548789Sahrens error: 1549789Sahrens changelist_free(cl); 1550789Sahrens return (ret); 1551789Sahrens } 1552789Sahrens 1553789Sahrens /* 15541356Seschrock * True DSL properties are stored in an nvlist. The following two functions 15551356Seschrock * extract them appropriately. 15561356Seschrock */ 15571356Seschrock static uint64_t 15581356Seschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15591356Seschrock { 15601356Seschrock nvlist_t *nv; 15611356Seschrock uint64_t value; 15621356Seschrock 15632885Sahrens *source = NULL; 15641356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15651356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15665094Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 15675094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15681356Seschrock } else { 15698802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15708802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15711356Seschrock value = zfs_prop_default_numeric(prop); 15721356Seschrock *source = ""; 15731356Seschrock } 15741356Seschrock 15751356Seschrock return (value); 15761356Seschrock } 15771356Seschrock 15781356Seschrock static char * 15791356Seschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 15801356Seschrock { 15811356Seschrock nvlist_t *nv; 15821356Seschrock char *value; 15831356Seschrock 15842885Sahrens *source = NULL; 15851356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 15861356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 15875094Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 15885094Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 15891356Seschrock } else { 15908802SSanjeev.Bagewadi@Sun.COM verify(!zhp->zfs_props_table || 15918802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table[prop] == B_TRUE); 15921356Seschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 15931356Seschrock value = ""; 15941356Seschrock *source = ""; 15951356Seschrock } 15961356Seschrock 15971356Seschrock return (value); 15981356Seschrock } 15991356Seschrock 160011022STom.Erickson@Sun.COM static boolean_t 160111022STom.Erickson@Sun.COM zfs_is_recvd_props_mode(zfs_handle_t *zhp) 160211022STom.Erickson@Sun.COM { 160311022STom.Erickson@Sun.COM return (zhp->zfs_props == zhp->zfs_recvd_props); 160411022STom.Erickson@Sun.COM } 160511022STom.Erickson@Sun.COM 160611022STom.Erickson@Sun.COM static void 160711022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 160811022STom.Erickson@Sun.COM { 160911022STom.Erickson@Sun.COM *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 161011022STom.Erickson@Sun.COM zhp->zfs_props = zhp->zfs_recvd_props; 161111022STom.Erickson@Sun.COM } 161211022STom.Erickson@Sun.COM 161311022STom.Erickson@Sun.COM static void 161411022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 161511022STom.Erickson@Sun.COM { 161611022STom.Erickson@Sun.COM zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 161711022STom.Erickson@Sun.COM *cookie = 0; 161811022STom.Erickson@Sun.COM } 161911022STom.Erickson@Sun.COM 16201356Seschrock /* 1621789Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1622789Sahrens * zfs_prop_get_int() are built using this interface. 1623789Sahrens * 1624789Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1625789Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1626789Sahrens * If they differ from the on-disk values, report the current values and mark 1627789Sahrens * the source "temporary". 1628789Sahrens */ 16292082Seschrock static int 16305094Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 16312082Seschrock char **source, uint64_t *val) 1632789Sahrens { 16335147Srm160521 zfs_cmd_t zc = { 0 }; 16345592Stimh nvlist_t *zplprops = NULL; 1635789Sahrens struct mnttab mnt; 16363265Sahrens char *mntopt_on = NULL; 16373265Sahrens char *mntopt_off = NULL; 163811022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1639789Sahrens 1640789Sahrens *source = NULL; 1641789Sahrens 16423265Sahrens switch (prop) { 16433265Sahrens case ZFS_PROP_ATIME: 16443265Sahrens mntopt_on = MNTOPT_ATIME; 16453265Sahrens mntopt_off = MNTOPT_NOATIME; 16463265Sahrens break; 16473265Sahrens 16483265Sahrens case ZFS_PROP_DEVICES: 16493265Sahrens mntopt_on = MNTOPT_DEVICES; 16503265Sahrens mntopt_off = MNTOPT_NODEVICES; 16513265Sahrens break; 16523265Sahrens 16533265Sahrens case ZFS_PROP_EXEC: 16543265Sahrens mntopt_on = MNTOPT_EXEC; 16553265Sahrens mntopt_off = MNTOPT_NOEXEC; 16563265Sahrens break; 16573265Sahrens 16583265Sahrens case ZFS_PROP_READONLY: 16593265Sahrens mntopt_on = MNTOPT_RO; 16603265Sahrens mntopt_off = MNTOPT_RW; 16613265Sahrens break; 16623265Sahrens 16633265Sahrens case ZFS_PROP_SETUID: 16643265Sahrens mntopt_on = MNTOPT_SETUID; 16653265Sahrens mntopt_off = MNTOPT_NOSETUID; 16663265Sahrens break; 16673265Sahrens 16683265Sahrens case ZFS_PROP_XATTR: 16693265Sahrens mntopt_on = MNTOPT_XATTR; 16703265Sahrens mntopt_off = MNTOPT_NOXATTR; 16713265Sahrens break; 16725331Samw 16735331Samw case ZFS_PROP_NBMAND: 16745331Samw mntopt_on = MNTOPT_NBMAND; 16755331Samw mntopt_off = MNTOPT_NONBMAND; 16765331Samw break; 16773265Sahrens } 16783265Sahrens 16792474Seschrock /* 16802474Seschrock * Because looking up the mount options is potentially expensive 16812474Seschrock * (iterating over all of /etc/mnttab), we defer its calculation until 16822474Seschrock * we're looking up a property which requires its presence. 16832474Seschrock */ 16842474Seschrock if (!zhp->zfs_mntcheck && 16853265Sahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 16868228SEric.Taylor@Sun.COM libzfs_handle_t *hdl = zhp->zfs_hdl; 16878228SEric.Taylor@Sun.COM struct mnttab entry; 16888228SEric.Taylor@Sun.COM 16898228SEric.Taylor@Sun.COM if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 16908228SEric.Taylor@Sun.COM zhp->zfs_mntopts = zfs_strdup(hdl, 16913265Sahrens entry.mnt_mntopts); 16923265Sahrens if (zhp->zfs_mntopts == NULL) 16933265Sahrens return (-1); 16943265Sahrens } 16952474Seschrock 16962474Seschrock zhp->zfs_mntcheck = B_TRUE; 16972474Seschrock } 16982474Seschrock 1699789Sahrens if (zhp->zfs_mntopts == NULL) 1700789Sahrens mnt.mnt_mntopts = ""; 1701789Sahrens else 1702789Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1703789Sahrens 1704789Sahrens switch (prop) { 1705789Sahrens case ZFS_PROP_ATIME: 17063265Sahrens case ZFS_PROP_DEVICES: 17073265Sahrens case ZFS_PROP_EXEC: 17083265Sahrens case ZFS_PROP_READONLY: 17093265Sahrens case ZFS_PROP_SETUID: 17103265Sahrens case ZFS_PROP_XATTR: 17115331Samw case ZFS_PROP_NBMAND: 17122082Seschrock *val = getprop_uint64(zhp, prop, source); 17132082Seschrock 171411022STom.Erickson@Sun.COM if (received) 171511022STom.Erickson@Sun.COM break; 171611022STom.Erickson@Sun.COM 17173265Sahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 17182082Seschrock *val = B_TRUE; 1719789Sahrens if (src) 17205094Slling *src = ZPROP_SRC_TEMPORARY; 17213265Sahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 17222082Seschrock *val = B_FALSE; 1723789Sahrens if (src) 17245094Slling *src = ZPROP_SRC_TEMPORARY; 1725789Sahrens } 17262082Seschrock break; 1727789Sahrens 17283265Sahrens case ZFS_PROP_CANMOUNT: 172911497SMark.Musante@Sun.COM case ZFS_PROP_VOLSIZE: 1730789Sahrens case ZFS_PROP_QUOTA: 17315378Sck153898 case ZFS_PROP_REFQUOTA: 1732789Sahrens case ZFS_PROP_RESERVATION: 17335378Sck153898 case ZFS_PROP_REFRESERVATION: 17342885Sahrens *val = getprop_uint64(zhp, prop, source); 173511022STom.Erickson@Sun.COM 173611022STom.Erickson@Sun.COM if (*source == NULL) { 173711022STom.Erickson@Sun.COM /* not default, must be local */ 1738789Sahrens *source = zhp->zfs_name; 173911022STom.Erickson@Sun.COM } 17402082Seschrock break; 1741789Sahrens 1742789Sahrens case ZFS_PROP_MOUNTED: 17432082Seschrock *val = (zhp->zfs_mntopts != NULL); 17442082Seschrock break; 1745789Sahrens 17463377Seschrock case ZFS_PROP_NUMCLONES: 17473377Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 17483377Seschrock break; 17493377Seschrock 17505147Srm160521 case ZFS_PROP_VERSION: 17515498Stimh case ZFS_PROP_NORMALIZE: 17525498Stimh case ZFS_PROP_UTF8ONLY: 17535498Stimh case ZFS_PROP_CASE: 17545498Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 17555498Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 17565473Srm160521 return (-1); 17575147Srm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 17585498Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 17595498Stimh zcmd_free_nvlists(&zc); 176010204SMatthew.Ahrens@Sun.COM return (-1); 17615147Srm160521 } 17625498Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 17635498Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 17645498Stimh val) != 0) { 17655498Stimh zcmd_free_nvlists(&zc); 176610204SMatthew.Ahrens@Sun.COM return (-1); 17675498Stimh } 17685592Stimh if (zplprops) 17695592Stimh nvlist_free(zplprops); 17705498Stimh zcmd_free_nvlists(&zc); 17715147Srm160521 break; 17725147Srm160521 1773789Sahrens default: 17744577Sahrens switch (zfs_prop_get_type(prop)) { 17754787Sahrens case PROP_TYPE_NUMBER: 17764787Sahrens case PROP_TYPE_INDEX: 17774577Sahrens *val = getprop_uint64(zhp, prop, source); 17787390SMatthew.Ahrens@Sun.COM /* 17799396SMatthew.Ahrens@Sun.COM * If we tried to use a default value for a 17807390SMatthew.Ahrens@Sun.COM * readonly property, it means that it was not 178111080STom.Erickson@Sun.COM * present. 17827390SMatthew.Ahrens@Sun.COM */ 17837390SMatthew.Ahrens@Sun.COM if (zfs_prop_readonly(prop) && 178411080STom.Erickson@Sun.COM *source != NULL && (*source)[0] == '\0') { 178511080STom.Erickson@Sun.COM *source = NULL; 17867390SMatthew.Ahrens@Sun.COM } 17874577Sahrens break; 17884577Sahrens 17894787Sahrens case PROP_TYPE_STRING: 17904577Sahrens default: 17914577Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 17924577Sahrens "cannot get non-numeric property")); 17934577Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 17944577Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 17954577Sahrens } 1796789Sahrens } 1797789Sahrens 1798789Sahrens return (0); 1799789Sahrens } 1800789Sahrens 1801789Sahrens /* 1802789Sahrens * Calculate the source type, given the raw source string. 1803789Sahrens */ 1804789Sahrens static void 18055094Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1806789Sahrens char *statbuf, size_t statlen) 1807789Sahrens { 18085094Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1809789Sahrens return; 1810789Sahrens 1811789Sahrens if (source == NULL) { 18125094Slling *srctype = ZPROP_SRC_NONE; 1813789Sahrens } else if (source[0] == '\0') { 18145094Slling *srctype = ZPROP_SRC_DEFAULT; 181511022STom.Erickson@Sun.COM } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 181611022STom.Erickson@Sun.COM *srctype = ZPROP_SRC_RECEIVED; 1817789Sahrens } else { 1818789Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 18195094Slling *srctype = ZPROP_SRC_LOCAL; 1820789Sahrens } else { 1821789Sahrens (void) strlcpy(statbuf, source, statlen); 18225094Slling *srctype = ZPROP_SRC_INHERITED; 1823789Sahrens } 1824789Sahrens } 1825789Sahrens 1826789Sahrens } 1827789Sahrens 182811022STom.Erickson@Sun.COM int 182911022STom.Erickson@Sun.COM zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 183011022STom.Erickson@Sun.COM size_t proplen, boolean_t literal) 183111022STom.Erickson@Sun.COM { 183211022STom.Erickson@Sun.COM zfs_prop_t prop; 183311022STom.Erickson@Sun.COM int err = 0; 183411022STom.Erickson@Sun.COM 183511022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 183611022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 183711022STom.Erickson@Sun.COM return (-1); 183811022STom.Erickson@Sun.COM 183911022STom.Erickson@Sun.COM prop = zfs_name_to_prop(propname); 184011022STom.Erickson@Sun.COM 184111022STom.Erickson@Sun.COM if (prop != ZPROP_INVAL) { 184211022STom.Erickson@Sun.COM uint64_t cookie; 184311022STom.Erickson@Sun.COM if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 184411022STom.Erickson@Sun.COM return (-1); 184511022STom.Erickson@Sun.COM zfs_set_recvd_props_mode(zhp, &cookie); 184611022STom.Erickson@Sun.COM err = zfs_prop_get(zhp, prop, propbuf, proplen, 184711022STom.Erickson@Sun.COM NULL, NULL, 0, literal); 184811022STom.Erickson@Sun.COM zfs_unset_recvd_props_mode(zhp, &cookie); 184911022STom.Erickson@Sun.COM } else if (zfs_prop_userquota(propname)) { 185011022STom.Erickson@Sun.COM return (-1); 185111022STom.Erickson@Sun.COM } else { 185211022STom.Erickson@Sun.COM nvlist_t *propval; 185311022STom.Erickson@Sun.COM char *recvdval; 185411022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 185511022STom.Erickson@Sun.COM propname, &propval) != 0) 185611022STom.Erickson@Sun.COM return (-1); 185711022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, ZPROP_VALUE, 185811022STom.Erickson@Sun.COM &recvdval) == 0); 185911022STom.Erickson@Sun.COM (void) strlcpy(propbuf, recvdval, proplen); 186011022STom.Erickson@Sun.COM } 186111022STom.Erickson@Sun.COM 186211022STom.Erickson@Sun.COM return (err == 0 ? 0 : -1); 186311022STom.Erickson@Sun.COM } 186411022STom.Erickson@Sun.COM 1865789Sahrens /* 1866789Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1867789Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1868789Sahrens * human-readable form. 1869789Sahrens * 1870789Sahrens * Returns 0 on success, or -1 on error. 1871789Sahrens */ 1872789Sahrens int 1873789Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 18745094Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1875789Sahrens { 1876789Sahrens char *source = NULL; 1877789Sahrens uint64_t val; 1878789Sahrens char *str; 18792676Seschrock const char *strval; 188011022STom.Erickson@Sun.COM boolean_t received = zfs_is_recvd_props_mode(zhp); 1881789Sahrens 1882789Sahrens /* 1883789Sahrens * Check to see if this property applies to our object 1884789Sahrens */ 1885789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1886789Sahrens return (-1); 1887789Sahrens 188811022STom.Erickson@Sun.COM if (received && zfs_prop_readonly(prop)) 188911022STom.Erickson@Sun.COM return (-1); 189011022STom.Erickson@Sun.COM 1891789Sahrens if (src) 18925094Slling *src = ZPROP_SRC_NONE; 1893789Sahrens 1894789Sahrens switch (prop) { 1895789Sahrens case ZFS_PROP_CREATION: 1896789Sahrens /* 1897789Sahrens * 'creation' is a time_t stored in the statistics. We convert 1898789Sahrens * this into a string unless 'literal' is specified. 1899789Sahrens */ 1900789Sahrens { 19012885Sahrens val = getprop_uint64(zhp, prop, &source); 19022885Sahrens time_t time = (time_t)val; 1903789Sahrens struct tm t; 1904789Sahrens 1905789Sahrens if (literal || 1906789Sahrens localtime_r(&time, &t) == NULL || 1907789Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1908789Sahrens &t) == 0) 19092885Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1910789Sahrens } 1911789Sahrens break; 1912789Sahrens 1913789Sahrens case ZFS_PROP_MOUNTPOINT: 1914789Sahrens /* 1915789Sahrens * Getting the precise mountpoint can be tricky. 1916789Sahrens * 1917789Sahrens * - for 'none' or 'legacy', return those values. 1918789Sahrens * - for inherited mountpoints, we want to take everything 1919789Sahrens * after our ancestor and append it to the inherited value. 1920789Sahrens * 1921789Sahrens * If the pool has an alternate root, we want to prepend that 1922789Sahrens * root to any values we return. 1923789Sahrens */ 19246865Srm160521 19251356Seschrock str = getprop_string(zhp, prop, &source); 19261356Seschrock 19276612Sgw25295 if (str[0] == '/') { 19286865Srm160521 char buf[MAXPATHLEN]; 19296865Srm160521 char *root = buf; 193011515STom.Erickson@Sun.COM const char *relpath; 193111515STom.Erickson@Sun.COM 193211515STom.Erickson@Sun.COM /* 193311515STom.Erickson@Sun.COM * If we inherit the mountpoint, even from a dataset 193411515STom.Erickson@Sun.COM * with a received value, the source will be the path of 193511515STom.Erickson@Sun.COM * the dataset we inherit from. If source is 193611515STom.Erickson@Sun.COM * ZPROP_SOURCE_VAL_RECVD, the received value is not 193711515STom.Erickson@Sun.COM * inherited. 193811515STom.Erickson@Sun.COM */ 193911515STom.Erickson@Sun.COM if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 194011515STom.Erickson@Sun.COM relpath = ""; 194111515STom.Erickson@Sun.COM } else { 194211515STom.Erickson@Sun.COM relpath = zhp->zfs_name + strlen(source); 194311515STom.Erickson@Sun.COM if (relpath[0] == '/') 194411515STom.Erickson@Sun.COM relpath++; 194511515STom.Erickson@Sun.COM } 19466612Sgw25295 19476865Srm160521 if ((zpool_get_prop(zhp->zpool_hdl, 19486865Srm160521 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 19496865Srm160521 (strcmp(root, "-") == 0)) 19506865Srm160521 root[0] = '\0'; 19516612Sgw25295 /* 19526612Sgw25295 * Special case an alternate root of '/'. This will 19536612Sgw25295 * avoid having multiple leading slashes in the 19546612Sgw25295 * mountpoint path. 19556612Sgw25295 */ 19566612Sgw25295 if (strcmp(root, "/") == 0) 19576612Sgw25295 root++; 19586612Sgw25295 19596612Sgw25295 /* 19606612Sgw25295 * If the mountpoint is '/' then skip over this 19616612Sgw25295 * if we are obtaining either an alternate root or 19626612Sgw25295 * an inherited mountpoint. 19636612Sgw25295 */ 19646612Sgw25295 if (str[1] == '\0' && (root[0] != '\0' || 19656612Sgw25295 relpath[0] != '\0')) 19661356Seschrock str++; 1967789Sahrens 1968789Sahrens if (relpath[0] == '\0') 1969789Sahrens (void) snprintf(propbuf, proplen, "%s%s", 19701356Seschrock root, str); 1971789Sahrens else 1972789Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 19731356Seschrock root, str, relpath[0] == '@' ? "" : "/", 1974789Sahrens relpath); 1975789Sahrens } else { 1976789Sahrens /* 'legacy' or 'none' */ 19771356Seschrock (void) strlcpy(propbuf, str, proplen); 1978789Sahrens } 1979789Sahrens 1980789Sahrens break; 1981789Sahrens 1982789Sahrens case ZFS_PROP_ORIGIN: 19832885Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 1984789Sahrens proplen); 1985789Sahrens /* 1986789Sahrens * If there is no parent at all, return failure to indicate that 1987789Sahrens * it doesn't apply to this dataset. 1988789Sahrens */ 1989789Sahrens if (propbuf[0] == '\0') 1990789Sahrens return (-1); 1991789Sahrens break; 1992789Sahrens 1993789Sahrens case ZFS_PROP_QUOTA: 19945378Sck153898 case ZFS_PROP_REFQUOTA: 1995789Sahrens case ZFS_PROP_RESERVATION: 19965378Sck153898 case ZFS_PROP_REFRESERVATION: 19975378Sck153898 19982082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 19992082Seschrock return (-1); 2000789Sahrens 2001789Sahrens /* 2002789Sahrens * If quota or reservation is 0, we translate this into 'none' 2003789Sahrens * (unless literal is set), and indicate that it's the default 2004789Sahrens * value. Otherwise, we print the number nicely and indicate 2005789Sahrens * that its set locally. 2006789Sahrens */ 2007789Sahrens if (val == 0) { 2008789Sahrens if (literal) 2009789Sahrens (void) strlcpy(propbuf, "0", proplen); 2010789Sahrens else 2011789Sahrens (void) strlcpy(propbuf, "none", proplen); 2012789Sahrens } else { 2013789Sahrens if (literal) 20142856Snd150628 (void) snprintf(propbuf, proplen, "%llu", 20153912Slling (u_longlong_t)val); 2016789Sahrens else 2017789Sahrens zfs_nicenum(val, propbuf, proplen); 2018789Sahrens } 2019789Sahrens break; 2020789Sahrens 2021789Sahrens case ZFS_PROP_COMPRESSRATIO: 20222082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 20232082Seschrock return (-1); 202410922SJeff.Bonwick@Sun.COM (void) snprintf(propbuf, proplen, "%llu.%02llux", 202510922SJeff.Bonwick@Sun.COM (u_longlong_t)(val / 100), 202610922SJeff.Bonwick@Sun.COM (u_longlong_t)(val % 100)); 2027789Sahrens break; 2028789Sahrens 2029789Sahrens case ZFS_PROP_TYPE: 2030789Sahrens switch (zhp->zfs_type) { 2031789Sahrens case ZFS_TYPE_FILESYSTEM: 2032789Sahrens str = "filesystem"; 2033789Sahrens break; 2034789Sahrens case ZFS_TYPE_VOLUME: 2035789Sahrens str = "volume"; 2036789Sahrens break; 2037789Sahrens case ZFS_TYPE_SNAPSHOT: 2038789Sahrens str = "snapshot"; 2039789Sahrens break; 2040789Sahrens default: 20412082Seschrock abort(); 2042789Sahrens } 2043789Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2044789Sahrens break; 2045789Sahrens 2046789Sahrens case ZFS_PROP_MOUNTED: 2047789Sahrens /* 2048789Sahrens * The 'mounted' property is a pseudo-property that described 2049789Sahrens * whether the filesystem is currently mounted. Even though 2050789Sahrens * it's a boolean value, the typical values of "on" and "off" 2051789Sahrens * don't make sense, so we translate to "yes" and "no". 2052789Sahrens */ 20532082Seschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 20542082Seschrock src, &source, &val) != 0) 20552082Seschrock return (-1); 20562082Seschrock if (val) 2057789Sahrens (void) strlcpy(propbuf, "yes", proplen); 2058789Sahrens else 2059789Sahrens (void) strlcpy(propbuf, "no", proplen); 2060789Sahrens break; 2061789Sahrens 2062789Sahrens case ZFS_PROP_NAME: 2063789Sahrens /* 2064789Sahrens * The 'name' property is a pseudo-property derived from the 2065789Sahrens * dataset name. It is presented as a real property to simplify 2066789Sahrens * consumers. 2067789Sahrens */ 2068789Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2069789Sahrens break; 2070789Sahrens 207110972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL: 207210972SRic.Aleshire@Sun.COM { 207310972SRic.Aleshire@Sun.COM m_label_t *new_sl = NULL; 207410972SRic.Aleshire@Sun.COM char *ascii = NULL; /* human readable label */ 207510972SRic.Aleshire@Sun.COM 207610972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, 207710972SRic.Aleshire@Sun.COM getprop_string(zhp, prop, &source), proplen); 207810972SRic.Aleshire@Sun.COM 207910972SRic.Aleshire@Sun.COM if (literal || (strcasecmp(propbuf, 208010972SRic.Aleshire@Sun.COM ZFS_MLSLABEL_DEFAULT) == 0)) 208110972SRic.Aleshire@Sun.COM break; 208210972SRic.Aleshire@Sun.COM 208310972SRic.Aleshire@Sun.COM /* 208410972SRic.Aleshire@Sun.COM * Try to translate the internal hex string to 208510972SRic.Aleshire@Sun.COM * human-readable output. If there are any 208610972SRic.Aleshire@Sun.COM * problems just use the hex string. 208710972SRic.Aleshire@Sun.COM */ 208810972SRic.Aleshire@Sun.COM 208910972SRic.Aleshire@Sun.COM if (str_to_label(propbuf, &new_sl, MAC_LABEL, 209010972SRic.Aleshire@Sun.COM L_NO_CORRECTION, NULL) == -1) { 209110972SRic.Aleshire@Sun.COM m_label_free(new_sl); 209210972SRic.Aleshire@Sun.COM break; 209310972SRic.Aleshire@Sun.COM } 209410972SRic.Aleshire@Sun.COM 209510972SRic.Aleshire@Sun.COM if (label_to_str(new_sl, &ascii, M_LABEL, 209610972SRic.Aleshire@Sun.COM DEF_NAMES) != 0) { 209710972SRic.Aleshire@Sun.COM if (ascii) 209810972SRic.Aleshire@Sun.COM free(ascii); 209910972SRic.Aleshire@Sun.COM m_label_free(new_sl); 210010972SRic.Aleshire@Sun.COM break; 210110972SRic.Aleshire@Sun.COM } 210210972SRic.Aleshire@Sun.COM m_label_free(new_sl); 210310972SRic.Aleshire@Sun.COM 210410972SRic.Aleshire@Sun.COM (void) strlcpy(propbuf, ascii, proplen); 210510972SRic.Aleshire@Sun.COM free(ascii); 210610972SRic.Aleshire@Sun.COM } 210710972SRic.Aleshire@Sun.COM break; 210810972SRic.Aleshire@Sun.COM 2109789Sahrens default: 21104577Sahrens switch (zfs_prop_get_type(prop)) { 21114787Sahrens case PROP_TYPE_NUMBER: 21124577Sahrens if (get_numeric_property(zhp, prop, src, 21134577Sahrens &source, &val) != 0) 21144577Sahrens return (-1); 21154577Sahrens if (literal) 21164577Sahrens (void) snprintf(propbuf, proplen, "%llu", 21174577Sahrens (u_longlong_t)val); 21184577Sahrens else 21194577Sahrens zfs_nicenum(val, propbuf, proplen); 21204577Sahrens break; 21214577Sahrens 21224787Sahrens case PROP_TYPE_STRING: 21234577Sahrens (void) strlcpy(propbuf, 21244577Sahrens getprop_string(zhp, prop, &source), proplen); 21254577Sahrens break; 21264577Sahrens 21274787Sahrens case PROP_TYPE_INDEX: 21284861Sahrens if (get_numeric_property(zhp, prop, src, 21294861Sahrens &source, &val) != 0) 21304861Sahrens return (-1); 21314861Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 21324577Sahrens return (-1); 21334577Sahrens (void) strlcpy(propbuf, strval, proplen); 21344577Sahrens break; 21354577Sahrens 21364577Sahrens default: 21374577Sahrens abort(); 21384577Sahrens } 2139789Sahrens } 2140789Sahrens 2141789Sahrens get_source(zhp, src, source, statbuf, statlen); 2142789Sahrens 2143789Sahrens return (0); 2144789Sahrens } 2145789Sahrens 2146789Sahrens /* 2147789Sahrens * Utility function to get the given numeric property. Does no validation that 2148789Sahrens * the given property is the appropriate type; should only be used with 2149789Sahrens * hard-coded property types. 2150789Sahrens */ 2151789Sahrens uint64_t 2152789Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2153789Sahrens { 2154789Sahrens char *source; 21552082Seschrock uint64_t val; 21562082Seschrock 21575367Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 21582082Seschrock 21592082Seschrock return (val); 2160789Sahrens } 2161789Sahrens 21625713Srm160521 int 21635713Srm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 21645713Srm160521 { 21655713Srm160521 char buf[64]; 21665713Srm160521 21679396SMatthew.Ahrens@Sun.COM (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 21685713Srm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 21695713Srm160521 } 21705713Srm160521 2171789Sahrens /* 2172789Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2173789Sahrens */ 2174789Sahrens int 2175789Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 21765094Slling zprop_source_t *src, char *statbuf, size_t statlen) 2177789Sahrens { 2178789Sahrens char *source; 2179789Sahrens 2180789Sahrens /* 2181789Sahrens * Check to see if this property applies to our object 2182789Sahrens */ 21834849Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 21843237Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 21852082Seschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 21862082Seschrock zfs_prop_to_name(prop))); 21874849Sahrens } 2188789Sahrens 2189789Sahrens if (src) 21905094Slling *src = ZPROP_SRC_NONE; 2191789Sahrens 21922082Seschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 21932082Seschrock return (-1); 2194789Sahrens 2195789Sahrens get_source(zhp, src, source, statbuf, statlen); 2196789Sahrens 2197789Sahrens return (0); 2198789Sahrens } 2199789Sahrens 22009396SMatthew.Ahrens@Sun.COM static int 22019396SMatthew.Ahrens@Sun.COM idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 22029396SMatthew.Ahrens@Sun.COM char **domainp, idmap_rid_t *ridp) 22039396SMatthew.Ahrens@Sun.COM { 22049396SMatthew.Ahrens@Sun.COM idmap_handle_t *idmap_hdl = NULL; 22059396SMatthew.Ahrens@Sun.COM idmap_get_handle_t *get_hdl = NULL; 22069396SMatthew.Ahrens@Sun.COM idmap_stat status; 22079396SMatthew.Ahrens@Sun.COM int err = EINVAL; 22089396SMatthew.Ahrens@Sun.COM 22099396SMatthew.Ahrens@Sun.COM if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) 22109396SMatthew.Ahrens@Sun.COM goto out; 22119396SMatthew.Ahrens@Sun.COM if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) 22129396SMatthew.Ahrens@Sun.COM goto out; 22139396SMatthew.Ahrens@Sun.COM 22149396SMatthew.Ahrens@Sun.COM if (isuser) { 22159396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbyuid(get_hdl, id, 22169396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 22179396SMatthew.Ahrens@Sun.COM } else { 22189396SMatthew.Ahrens@Sun.COM err = idmap_get_sidbygid(get_hdl, id, 22199396SMatthew.Ahrens@Sun.COM IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 22209396SMatthew.Ahrens@Sun.COM } 22219396SMatthew.Ahrens@Sun.COM if (err == IDMAP_SUCCESS && 22229396SMatthew.Ahrens@Sun.COM idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 22239396SMatthew.Ahrens@Sun.COM status == IDMAP_SUCCESS) 22249396SMatthew.Ahrens@Sun.COM err = 0; 22259396SMatthew.Ahrens@Sun.COM else 22269396SMatthew.Ahrens@Sun.COM err = EINVAL; 22279396SMatthew.Ahrens@Sun.COM out: 22289396SMatthew.Ahrens@Sun.COM if (get_hdl) 22299396SMatthew.Ahrens@Sun.COM idmap_get_destroy(get_hdl); 22309396SMatthew.Ahrens@Sun.COM if (idmap_hdl) 22319396SMatthew.Ahrens@Sun.COM (void) idmap_fini(idmap_hdl); 22329396SMatthew.Ahrens@Sun.COM return (err); 22339396SMatthew.Ahrens@Sun.COM } 22349396SMatthew.Ahrens@Sun.COM 22359396SMatthew.Ahrens@Sun.COM /* 22369396SMatthew.Ahrens@Sun.COM * convert the propname into parameters needed by kernel 22379396SMatthew.Ahrens@Sun.COM * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 22389396SMatthew.Ahrens@Sun.COM * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 22399396SMatthew.Ahrens@Sun.COM */ 22409396SMatthew.Ahrens@Sun.COM static int 22419396SMatthew.Ahrens@Sun.COM userquota_propname_decode(const char *propname, boolean_t zoned, 22429396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 22439396SMatthew.Ahrens@Sun.COM { 22449396SMatthew.Ahrens@Sun.COM zfs_userquota_prop_t type; 22459396SMatthew.Ahrens@Sun.COM char *cp, *end; 224610160SMatthew.Ahrens@Sun.COM char *numericsid = NULL; 22479396SMatthew.Ahrens@Sun.COM boolean_t isuser; 22489396SMatthew.Ahrens@Sun.COM 22499396SMatthew.Ahrens@Sun.COM domain[0] = '\0'; 22509396SMatthew.Ahrens@Sun.COM 22519396SMatthew.Ahrens@Sun.COM /* Figure out the property type ({user|group}{quota|space}) */ 22529396SMatthew.Ahrens@Sun.COM for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 22539396SMatthew.Ahrens@Sun.COM if (strncmp(propname, zfs_userquota_prop_prefixes[type], 22549396SMatthew.Ahrens@Sun.COM strlen(zfs_userquota_prop_prefixes[type])) == 0) 22559396SMatthew.Ahrens@Sun.COM break; 22569396SMatthew.Ahrens@Sun.COM } 22579396SMatthew.Ahrens@Sun.COM if (type == ZFS_NUM_USERQUOTA_PROPS) 22589396SMatthew.Ahrens@Sun.COM return (EINVAL); 22599396SMatthew.Ahrens@Sun.COM *typep = type; 22609396SMatthew.Ahrens@Sun.COM 22619396SMatthew.Ahrens@Sun.COM isuser = (type == ZFS_PROP_USERQUOTA || 22629396SMatthew.Ahrens@Sun.COM type == ZFS_PROP_USERUSED); 22639396SMatthew.Ahrens@Sun.COM 22649396SMatthew.Ahrens@Sun.COM cp = strchr(propname, '@') + 1; 22659396SMatthew.Ahrens@Sun.COM 22669396SMatthew.Ahrens@Sun.COM if (strchr(cp, '@')) { 22679396SMatthew.Ahrens@Sun.COM /* 22689396SMatthew.Ahrens@Sun.COM * It's a SID name (eg "user@domain") that needs to be 226910160SMatthew.Ahrens@Sun.COM * turned into S-1-domainID-RID. 22709396SMatthew.Ahrens@Sun.COM */ 227110160SMatthew.Ahrens@Sun.COM directory_error_t e; 22729396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 22739396SMatthew.Ahrens@Sun.COM return (ENOENT); 227410160SMatthew.Ahrens@Sun.COM if (isuser) { 227510160SMatthew.Ahrens@Sun.COM e = directory_sid_from_user_name(NULL, 227610160SMatthew.Ahrens@Sun.COM cp, &numericsid); 227710160SMatthew.Ahrens@Sun.COM } else { 227810160SMatthew.Ahrens@Sun.COM e = directory_sid_from_group_name(NULL, 227910160SMatthew.Ahrens@Sun.COM cp, &numericsid); 228010160SMatthew.Ahrens@Sun.COM } 228110160SMatthew.Ahrens@Sun.COM if (e != NULL) { 228210160SMatthew.Ahrens@Sun.COM directory_error_free(e); 22839396SMatthew.Ahrens@Sun.COM return (ENOENT); 228410160SMatthew.Ahrens@Sun.COM } 228510160SMatthew.Ahrens@Sun.COM if (numericsid == NULL) 228610160SMatthew.Ahrens@Sun.COM return (ENOENT); 228710160SMatthew.Ahrens@Sun.COM cp = numericsid; 228810160SMatthew.Ahrens@Sun.COM /* will be further decoded below */ 228910160SMatthew.Ahrens@Sun.COM } 229010160SMatthew.Ahrens@Sun.COM 229110160SMatthew.Ahrens@Sun.COM if (strncmp(cp, "S-1-", 4) == 0) { 22929396SMatthew.Ahrens@Sun.COM /* It's a numeric SID (eg "S-1-234-567-89") */ 229310160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, cp, domainlen); 22949396SMatthew.Ahrens@Sun.COM cp = strrchr(domain, '-'); 22959396SMatthew.Ahrens@Sun.COM *cp = '\0'; 22969396SMatthew.Ahrens@Sun.COM cp++; 22979396SMatthew.Ahrens@Sun.COM 22989396SMatthew.Ahrens@Sun.COM errno = 0; 22999396SMatthew.Ahrens@Sun.COM *ridp = strtoull(cp, &end, 10); 230010160SMatthew.Ahrens@Sun.COM if (numericsid) { 230110160SMatthew.Ahrens@Sun.COM free(numericsid); 230210160SMatthew.Ahrens@Sun.COM numericsid = NULL; 230310160SMatthew.Ahrens@Sun.COM } 23049688SMatthew.Ahrens@Sun.COM if (errno != 0 || *end != '\0') 23059396SMatthew.Ahrens@Sun.COM return (EINVAL); 23069396SMatthew.Ahrens@Sun.COM } else if (!isdigit(*cp)) { 23079396SMatthew.Ahrens@Sun.COM /* 23089396SMatthew.Ahrens@Sun.COM * It's a user/group name (eg "user") that needs to be 23099396SMatthew.Ahrens@Sun.COM * turned into a uid/gid 23109396SMatthew.Ahrens@Sun.COM */ 23119396SMatthew.Ahrens@Sun.COM if (zoned && getzoneid() == GLOBAL_ZONEID) 23129396SMatthew.Ahrens@Sun.COM return (ENOENT); 23139396SMatthew.Ahrens@Sun.COM if (isuser) { 23149396SMatthew.Ahrens@Sun.COM struct passwd *pw; 23159396SMatthew.Ahrens@Sun.COM pw = getpwnam(cp); 23169396SMatthew.Ahrens@Sun.COM if (pw == NULL) 23179396SMatthew.Ahrens@Sun.COM return (ENOENT); 23189396SMatthew.Ahrens@Sun.COM *ridp = pw->pw_uid; 23199396SMatthew.Ahrens@Sun.COM } else { 23209396SMatthew.Ahrens@Sun.COM struct group *gr; 23219396SMatthew.Ahrens@Sun.COM gr = getgrnam(cp); 23229396SMatthew.Ahrens@Sun.COM if (gr == NULL) 23239396SMatthew.Ahrens@Sun.COM return (ENOENT); 23249396SMatthew.Ahrens@Sun.COM *ridp = gr->gr_gid; 23259396SMatthew.Ahrens@Sun.COM } 23269396SMatthew.Ahrens@Sun.COM } else { 23279396SMatthew.Ahrens@Sun.COM /* It's a user/group ID (eg "12345"). */ 23289396SMatthew.Ahrens@Sun.COM uid_t id = strtoul(cp, &end, 10); 23299396SMatthew.Ahrens@Sun.COM idmap_rid_t rid; 23309396SMatthew.Ahrens@Sun.COM char *mapdomain; 23319396SMatthew.Ahrens@Sun.COM 23329396SMatthew.Ahrens@Sun.COM if (*end != '\0') 23339396SMatthew.Ahrens@Sun.COM return (EINVAL); 23349396SMatthew.Ahrens@Sun.COM if (id > MAXUID) { 23359396SMatthew.Ahrens@Sun.COM /* It's an ephemeral ID. */ 23369396SMatthew.Ahrens@Sun.COM if (idmap_id_to_numeric_domain_rid(id, isuser, 23379396SMatthew.Ahrens@Sun.COM &mapdomain, &rid) != 0) 23389396SMatthew.Ahrens@Sun.COM return (ENOENT); 233910160SMatthew.Ahrens@Sun.COM (void) strlcpy(domain, mapdomain, domainlen); 23409396SMatthew.Ahrens@Sun.COM *ridp = rid; 23419396SMatthew.Ahrens@Sun.COM } else { 23429396SMatthew.Ahrens@Sun.COM *ridp = id; 23439396SMatthew.Ahrens@Sun.COM } 23449396SMatthew.Ahrens@Sun.COM } 23459396SMatthew.Ahrens@Sun.COM 234610160SMatthew.Ahrens@Sun.COM ASSERT3P(numericsid, ==, NULL); 23479396SMatthew.Ahrens@Sun.COM return (0); 23489396SMatthew.Ahrens@Sun.COM } 23499396SMatthew.Ahrens@Sun.COM 23509469SLin.Ling@Sun.COM static int 23519469SLin.Ling@Sun.COM zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 23529469SLin.Ling@Sun.COM uint64_t *propvalue, zfs_userquota_prop_t *typep) 23539396SMatthew.Ahrens@Sun.COM { 23549396SMatthew.Ahrens@Sun.COM int err; 23559396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 23569396SMatthew.Ahrens@Sun.COM 23579396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 23589396SMatthew.Ahrens@Sun.COM 23599396SMatthew.Ahrens@Sun.COM err = userquota_propname_decode(propname, 23609396SMatthew.Ahrens@Sun.COM zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 23619469SLin.Ling@Sun.COM typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 23629469SLin.Ling@Sun.COM zc.zc_objset_type = *typep; 23639396SMatthew.Ahrens@Sun.COM if (err) 23649396SMatthew.Ahrens@Sun.COM return (err); 23659396SMatthew.Ahrens@Sun.COM 23669396SMatthew.Ahrens@Sun.COM err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 23679396SMatthew.Ahrens@Sun.COM if (err) 23689396SMatthew.Ahrens@Sun.COM return (err); 23699396SMatthew.Ahrens@Sun.COM 23709469SLin.Ling@Sun.COM *propvalue = zc.zc_cookie; 23719469SLin.Ling@Sun.COM return (0); 23729469SLin.Ling@Sun.COM } 23739469SLin.Ling@Sun.COM 23749469SLin.Ling@Sun.COM int 23759469SLin.Ling@Sun.COM zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 23769469SLin.Ling@Sun.COM uint64_t *propvalue) 23779469SLin.Ling@Sun.COM { 23789469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23799469SLin.Ling@Sun.COM 23809469SLin.Ling@Sun.COM return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 23819469SLin.Ling@Sun.COM &type)); 23829469SLin.Ling@Sun.COM } 23839469SLin.Ling@Sun.COM 23849469SLin.Ling@Sun.COM int 23859469SLin.Ling@Sun.COM zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 23869469SLin.Ling@Sun.COM char *propbuf, int proplen, boolean_t literal) 23879469SLin.Ling@Sun.COM { 23889469SLin.Ling@Sun.COM int err; 23899469SLin.Ling@Sun.COM uint64_t propvalue; 23909469SLin.Ling@Sun.COM zfs_userquota_prop_t type; 23919469SLin.Ling@Sun.COM 23929469SLin.Ling@Sun.COM err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 23939469SLin.Ling@Sun.COM &type); 23949469SLin.Ling@Sun.COM 23959469SLin.Ling@Sun.COM if (err) 23969469SLin.Ling@Sun.COM return (err); 23979469SLin.Ling@Sun.COM 23989396SMatthew.Ahrens@Sun.COM if (literal) { 23999469SLin.Ling@Sun.COM (void) snprintf(propbuf, proplen, "%llu", propvalue); 24009469SLin.Ling@Sun.COM } else if (propvalue == 0 && 24019396SMatthew.Ahrens@Sun.COM (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 24029396SMatthew.Ahrens@Sun.COM (void) strlcpy(propbuf, "none", proplen); 24039396SMatthew.Ahrens@Sun.COM } else { 24049469SLin.Ling@Sun.COM zfs_nicenum(propvalue, propbuf, proplen); 24059396SMatthew.Ahrens@Sun.COM } 24069396SMatthew.Ahrens@Sun.COM return (0); 24079396SMatthew.Ahrens@Sun.COM } 24089396SMatthew.Ahrens@Sun.COM 2409789Sahrens /* 2410789Sahrens * Returns the name of the given zfs handle. 2411789Sahrens */ 2412789Sahrens const char * 2413789Sahrens zfs_get_name(const zfs_handle_t *zhp) 2414789Sahrens { 2415789Sahrens return (zhp->zfs_name); 2416789Sahrens } 2417789Sahrens 2418789Sahrens /* 2419789Sahrens * Returns the type of the given zfs handle. 2420789Sahrens */ 2421789Sahrens zfs_type_t 2422789Sahrens zfs_get_type(const zfs_handle_t *zhp) 2423789Sahrens { 2424789Sahrens return (zhp->zfs_type); 2425789Sahrens } 2426789Sahrens 24278228SEric.Taylor@Sun.COM static int 24288228SEric.Taylor@Sun.COM zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 24298228SEric.Taylor@Sun.COM { 24308228SEric.Taylor@Sun.COM int rc; 24318228SEric.Taylor@Sun.COM uint64_t orig_cookie; 24328228SEric.Taylor@Sun.COM 24338228SEric.Taylor@Sun.COM orig_cookie = zc->zc_cookie; 24348228SEric.Taylor@Sun.COM top: 24358228SEric.Taylor@Sun.COM (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 24368228SEric.Taylor@Sun.COM rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 24378228SEric.Taylor@Sun.COM 24388228SEric.Taylor@Sun.COM if (rc == -1) { 24398228SEric.Taylor@Sun.COM switch (errno) { 24408228SEric.Taylor@Sun.COM case ENOMEM: 24418228SEric.Taylor@Sun.COM /* expand nvlist memory and try again */ 24428228SEric.Taylor@Sun.COM if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 24438228SEric.Taylor@Sun.COM zcmd_free_nvlists(zc); 24448228SEric.Taylor@Sun.COM return (-1); 24458228SEric.Taylor@Sun.COM } 24468228SEric.Taylor@Sun.COM zc->zc_cookie = orig_cookie; 24478228SEric.Taylor@Sun.COM goto top; 24488228SEric.Taylor@Sun.COM /* 24498228SEric.Taylor@Sun.COM * An errno value of ESRCH indicates normal completion. 24508228SEric.Taylor@Sun.COM * If ENOENT is returned, then the underlying dataset 24518228SEric.Taylor@Sun.COM * has been removed since we obtained the handle. 24528228SEric.Taylor@Sun.COM */ 24538228SEric.Taylor@Sun.COM case ESRCH: 24548228SEric.Taylor@Sun.COM case ENOENT: 24558228SEric.Taylor@Sun.COM rc = 1; 24568228SEric.Taylor@Sun.COM break; 24578228SEric.Taylor@Sun.COM default: 24588228SEric.Taylor@Sun.COM rc = zfs_standard_error(zhp->zfs_hdl, errno, 24598228SEric.Taylor@Sun.COM dgettext(TEXT_DOMAIN, 24608228SEric.Taylor@Sun.COM "cannot iterate filesystems")); 24618228SEric.Taylor@Sun.COM break; 24628228SEric.Taylor@Sun.COM } 24638228SEric.Taylor@Sun.COM } 24648228SEric.Taylor@Sun.COM return (rc); 24658228SEric.Taylor@Sun.COM } 24668228SEric.Taylor@Sun.COM 2467789Sahrens /* 24681356Seschrock * Iterate over all child filesystems 2469789Sahrens */ 2470789Sahrens int 24711356Seschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2472789Sahrens { 2473789Sahrens zfs_cmd_t zc = { 0 }; 2474789Sahrens zfs_handle_t *nzhp; 2475789Sahrens int ret; 2476789Sahrens 24775367Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 24785367Sahrens return (0); 24795367Sahrens 24808228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 24818228SEric.Taylor@Sun.COM return (-1); 24828228SEric.Taylor@Sun.COM 24838228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 24848228SEric.Taylor@Sun.COM &zc)) == 0) { 2485789Sahrens /* 2486789Sahrens * Silently ignore errors, as the only plausible explanation is 2487789Sahrens * that the pool has since been removed. 2488789Sahrens */ 24898228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 24908228SEric.Taylor@Sun.COM &zc)) == NULL) { 2491789Sahrens continue; 24928228SEric.Taylor@Sun.COM } 24938228SEric.Taylor@Sun.COM 24948228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 24958228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2496789Sahrens return (ret); 24978228SEric.Taylor@Sun.COM } 2498789Sahrens } 24998228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 25008228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 25011356Seschrock } 25021356Seschrock 25031356Seschrock /* 25041356Seschrock * Iterate over all snapshots 25051356Seschrock */ 25061356Seschrock int 25071356Seschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25081356Seschrock { 25091356Seschrock zfs_cmd_t zc = { 0 }; 25101356Seschrock zfs_handle_t *nzhp; 25111356Seschrock int ret; 2512789Sahrens 25135367Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 25145367Sahrens return (0); 25155367Sahrens 25168228SEric.Taylor@Sun.COM if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 25178228SEric.Taylor@Sun.COM return (-1); 25188228SEric.Taylor@Sun.COM while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 25198228SEric.Taylor@Sun.COM &zc)) == 0) { 25208228SEric.Taylor@Sun.COM 25218228SEric.Taylor@Sun.COM if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 25228228SEric.Taylor@Sun.COM &zc)) == NULL) { 2523789Sahrens continue; 25248228SEric.Taylor@Sun.COM } 25258228SEric.Taylor@Sun.COM 25268228SEric.Taylor@Sun.COM if ((ret = func(nzhp, data)) != 0) { 25278228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 2528789Sahrens return (ret); 25298228SEric.Taylor@Sun.COM } 2530789Sahrens } 25318228SEric.Taylor@Sun.COM zcmd_free_nvlists(&zc); 25328228SEric.Taylor@Sun.COM return ((ret < 0) ? ret : 0); 2533789Sahrens } 2534789Sahrens 2535789Sahrens /* 25361356Seschrock * Iterate over all children, snapshots and filesystems 25371356Seschrock */ 25381356Seschrock int 25391356Seschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25401356Seschrock { 25411356Seschrock int ret; 25421356Seschrock 25431356Seschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25441356Seschrock return (ret); 25451356Seschrock 25461356Seschrock return (zfs_iter_snapshots(zhp, func, data)); 25471356Seschrock } 25481356Seschrock 25491356Seschrock /* 255011497SMark.Musante@Sun.COM * Is one dataset name a child dataset of another? 255111497SMark.Musante@Sun.COM * 255211497SMark.Musante@Sun.COM * Needs to handle these cases: 255311497SMark.Musante@Sun.COM * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 255411497SMark.Musante@Sun.COM * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 255511497SMark.Musante@Sun.COM * Descendant? No. No. No. Yes. 255611497SMark.Musante@Sun.COM */ 255711497SMark.Musante@Sun.COM static boolean_t 255811497SMark.Musante@Sun.COM is_descendant(const char *ds1, const char *ds2) 255911497SMark.Musante@Sun.COM { 256011497SMark.Musante@Sun.COM size_t d1len = strlen(ds1); 256111497SMark.Musante@Sun.COM 256211497SMark.Musante@Sun.COM /* ds2 can't be a descendant if it's smaller */ 256311497SMark.Musante@Sun.COM if (strlen(ds2) < d1len) 256411497SMark.Musante@Sun.COM return (B_FALSE); 256511497SMark.Musante@Sun.COM 256611497SMark.Musante@Sun.COM /* otherwise, compare strings and verify that there's a '/' char */ 256711497SMark.Musante@Sun.COM return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 256811497SMark.Musante@Sun.COM } 256911497SMark.Musante@Sun.COM 257011497SMark.Musante@Sun.COM /* 2571789Sahrens * Given a complete name, return just the portion that refers to the parent. 2572789Sahrens * Can return NULL if this is a pool. 2573789Sahrens */ 2574789Sahrens static int 2575789Sahrens parent_name(const char *path, char *buf, size_t buflen) 2576789Sahrens { 2577789Sahrens char *loc; 2578789Sahrens 2579789Sahrens if ((loc = strrchr(path, '/')) == NULL) 2580789Sahrens return (-1); 2581789Sahrens 2582789Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2583789Sahrens buf[loc - path] = '\0'; 2584789Sahrens 2585789Sahrens return (0); 2586789Sahrens } 2587789Sahrens 2588789Sahrens /* 25894490Svb160487 * If accept_ancestor is false, then check to make sure that the given path has 25904490Svb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 25914490Svb160487 * closest existing ancestor for the given path. In prefixlen return the 25924490Svb160487 * length of already existing prefix of the given path. We also fetch the 25934490Svb160487 * 'zoned' property, which is used to validate property settings when creating 25944490Svb160487 * new datasets. 2595789Sahrens */ 2596789Sahrens static int 25974490Svb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 25984490Svb160487 boolean_t accept_ancestor, int *prefixlen) 2599789Sahrens { 2600789Sahrens zfs_cmd_t zc = { 0 }; 2601789Sahrens char parent[ZFS_MAXNAMELEN]; 2602789Sahrens char *slash; 26031356Seschrock zfs_handle_t *zhp; 26042082Seschrock char errbuf[1024]; 260511497SMark.Musante@Sun.COM uint64_t is_zoned; 26062082Seschrock 26078269SMark.Musante@Sun.COM (void) snprintf(errbuf, sizeof (errbuf), 26088269SMark.Musante@Sun.COM dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2609789Sahrens 2610789Sahrens /* get parent, and check to see if this is just a pool */ 2611789Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 26122082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26132082Seschrock "missing dataset name")); 26142082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2615789Sahrens } 2616789Sahrens 2617789Sahrens /* check to see if the pool exists */ 2618789Sahrens if ((slash = strchr(parent, '/')) == NULL) 2619789Sahrens slash = parent + strlen(parent); 2620789Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2621789Sahrens zc.zc_name[slash - parent] = '\0'; 26222082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2623789Sahrens errno == ENOENT) { 26242082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26252082Seschrock "no such pool '%s'"), zc.zc_name); 26262082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2627789Sahrens } 2628789Sahrens 2629789Sahrens /* check to see if the parent dataset exists */ 26304490Svb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 26314490Svb160487 if (errno == ENOENT && accept_ancestor) { 26324490Svb160487 /* 26334490Svb160487 * Go deeper to find an ancestor, give up on top level. 26344490Svb160487 */ 26354490Svb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 26364490Svb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26374490Svb160487 "no such pool '%s'"), zc.zc_name); 26384490Svb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26394490Svb160487 } 26404490Svb160487 } else if (errno == ENOENT) { 26412082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26422082Seschrock "parent does not exist")); 26432082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26444490Svb160487 } else 26452082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2646789Sahrens } 2647789Sahrens 264811497SMark.Musante@Sun.COM is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 264911497SMark.Musante@Sun.COM if (zoned != NULL) 265011497SMark.Musante@Sun.COM *zoned = is_zoned; 265111497SMark.Musante@Sun.COM 2652789Sahrens /* we are in a non-global zone, but parent is in the global zone */ 265311497SMark.Musante@Sun.COM if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 26542082Seschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26551356Seschrock zfs_close(zhp); 2656789Sahrens return (-1); 2657789Sahrens } 2658789Sahrens 2659789Sahrens /* make sure parent is a filesystem */ 26601356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 26612082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26622082Seschrock "parent is not a filesystem")); 26632082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26641356Seschrock zfs_close(zhp); 2665789Sahrens return (-1); 2666789Sahrens } 2667789Sahrens 26681356Seschrock zfs_close(zhp); 26694490Svb160487 if (prefixlen != NULL) 26704490Svb160487 *prefixlen = strlen(parent); 26714490Svb160487 return (0); 26724490Svb160487 } 26734490Svb160487 26744490Svb160487 /* 26754490Svb160487 * Finds whether the dataset of the given type(s) exists. 26764490Svb160487 */ 26774490Svb160487 boolean_t 26784490Svb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26794490Svb160487 { 26804490Svb160487 zfs_handle_t *zhp; 26814490Svb160487 26825326Sek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26834490Svb160487 return (B_FALSE); 26844490Svb160487 26854490Svb160487 /* 26864490Svb160487 * Try to get stats for the dataset, which will tell us if it exists. 26874490Svb160487 */ 26884490Svb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 26894490Svb160487 int ds_type = zhp->zfs_type; 26904490Svb160487 26914490Svb160487 zfs_close(zhp); 26924490Svb160487 if (types & ds_type) 26934490Svb160487 return (B_TRUE); 26944490Svb160487 } 26954490Svb160487 return (B_FALSE); 26964490Svb160487 } 26974490Svb160487 26984490Svb160487 /* 26995367Sahrens * Given a path to 'target', create all the ancestors between 27005367Sahrens * the prefixlen portion of the path, and the target itself. 27015367Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 27025367Sahrens */ 27035367Sahrens int 27045367Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 27055367Sahrens { 27065367Sahrens zfs_handle_t *h; 27075367Sahrens char *cp; 27085367Sahrens const char *opname; 27095367Sahrens 27105367Sahrens /* make sure prefix exists */ 27115367Sahrens cp = target + prefixlen; 27125367Sahrens if (*cp != '/') { 27135367Sahrens assert(strchr(cp, '/') == NULL); 27145367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27155367Sahrens } else { 27165367Sahrens *cp = '\0'; 27175367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27185367Sahrens *cp = '/'; 27195367Sahrens } 27205367Sahrens if (h == NULL) 27215367Sahrens return (-1); 27225367Sahrens zfs_close(h); 27235367Sahrens 27245367Sahrens /* 27255367Sahrens * Attempt to create, mount, and share any ancestor filesystems, 27265367Sahrens * up to the prefixlen-long one. 27275367Sahrens */ 27285367Sahrens for (cp = target + prefixlen + 1; 27295367Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 27305367Sahrens char *logstr; 27315367Sahrens 27325367Sahrens *cp = '\0'; 27335367Sahrens 27345367Sahrens h = make_dataset_handle(hdl, target); 27355367Sahrens if (h) { 27365367Sahrens /* it already exists, nothing to do here */ 27375367Sahrens zfs_close(h); 27385367Sahrens continue; 27395367Sahrens } 27405367Sahrens 27415367Sahrens logstr = hdl->libzfs_log_str; 27425367Sahrens hdl->libzfs_log_str = NULL; 27435367Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27445367Sahrens NULL) != 0) { 27455367Sahrens hdl->libzfs_log_str = logstr; 27465367Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27475367Sahrens goto ancestorerr; 27485367Sahrens } 27495367Sahrens 27505367Sahrens hdl->libzfs_log_str = logstr; 27515367Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27525367Sahrens if (h == NULL) { 27535367Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27545367Sahrens goto ancestorerr; 27555367Sahrens } 27565367Sahrens 27575367Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27585367Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27595367Sahrens goto ancestorerr; 27605367Sahrens } 27615367Sahrens 27625367Sahrens if (zfs_share(h) != 0) { 27635367Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27645367Sahrens goto ancestorerr; 27655367Sahrens } 27665367Sahrens 27675367Sahrens zfs_close(h); 27685367Sahrens } 27695367Sahrens 27705367Sahrens return (0); 27715367Sahrens 27725367Sahrens ancestorerr: 27735367Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27745367Sahrens "failed to %s ancestor '%s'"), opname, target); 27755367Sahrens return (-1); 27765367Sahrens } 27775367Sahrens 27785367Sahrens /* 27794490Svb160487 * Creates non-existing ancestors of the given path. 27804490Svb160487 */ 27814490Svb160487 int 27824490Svb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27834490Svb160487 { 27844490Svb160487 int prefix; 27854490Svb160487 char *path_copy; 27864490Svb160487 int rc; 27874490Svb160487 278811497SMark.Musante@Sun.COM if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 27894490Svb160487 return (-1); 27904490Svb160487 27914490Svb160487 if ((path_copy = strdup(path)) != NULL) { 27924490Svb160487 rc = create_parents(hdl, path_copy, prefix); 27934490Svb160487 free(path_copy); 27944490Svb160487 } 27954490Svb160487 if (path_copy == NULL || rc != 0) 27964490Svb160487 return (-1); 27974490Svb160487 2798789Sahrens return (0); 2799789Sahrens } 2800789Sahrens 2801789Sahrens /* 28022676Seschrock * Create a new filesystem or volume. 2803789Sahrens */ 2804789Sahrens int 28052082Seschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 28062676Seschrock nvlist_t *props) 2807789Sahrens { 2808789Sahrens zfs_cmd_t zc = { 0 }; 2809789Sahrens int ret; 2810789Sahrens uint64_t size = 0; 2811789Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 28122082Seschrock char errbuf[1024]; 28132676Seschrock uint64_t zoned; 28142082Seschrock 28152082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 28162082Seschrock "cannot create '%s'"), path); 2817789Sahrens 2818789Sahrens /* validate the path, taking care to note the extended error message */ 28195326Sek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 28202082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2821789Sahrens 2822789Sahrens /* validate parents exist */ 28234490Svb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2824789Sahrens return (-1); 2825789Sahrens 2826789Sahrens /* 2827789Sahrens * The failure modes when creating a dataset of a different type over 2828789Sahrens * one that already exists is a little strange. In particular, if you 2829789Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2830789Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2831789Sahrens * first try to see if the dataset exists. 2832789Sahrens */ 2833789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 28345094Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 28352082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28362082Seschrock "dataset already exists")); 28372082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2838789Sahrens } 2839789Sahrens 2840789Sahrens if (type == ZFS_TYPE_VOLUME) 2841789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2842789Sahrens else 2843789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2844789Sahrens 28457184Stimh if (props && (props = zfs_valid_proplist(hdl, type, props, 28463912Slling zoned, NULL, errbuf)) == 0) 28472676Seschrock return (-1); 28482676Seschrock 2849789Sahrens if (type == ZFS_TYPE_VOLUME) { 28501133Seschrock /* 28511133Seschrock * If we are creating a volume, the size and block size must 28521133Seschrock * satisfy a few restraints. First, the blocksize must be a 28531133Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28541133Seschrock * volsize must be a multiple of the block size, and cannot be 28551133Seschrock * zero. 28561133Seschrock */ 28572676Seschrock if (props == NULL || nvlist_lookup_uint64(props, 28582676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 28592676Seschrock nvlist_free(props); 28602082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28612676Seschrock "missing volume size")); 28622676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2863789Sahrens } 2864789Sahrens 28652676Seschrock if ((ret = nvlist_lookup_uint64(props, 28662676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 28672676Seschrock &blocksize)) != 0) { 28682676Seschrock if (ret == ENOENT) { 28692676Seschrock blocksize = zfs_prop_default_numeric( 28702676Seschrock ZFS_PROP_VOLBLOCKSIZE); 28712676Seschrock } else { 28722676Seschrock nvlist_free(props); 28732676Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28742676Seschrock "missing volume block size")); 28752676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28762676Seschrock } 28772676Seschrock } 28782676Seschrock 28792676Seschrock if (size == 0) { 28802676Seschrock nvlist_free(props); 28812082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28822676Seschrock "volume size cannot be zero")); 28832676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28841133Seschrock } 28851133Seschrock 28861133Seschrock if (size % blocksize != 0) { 28872676Seschrock nvlist_free(props); 28882082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28892676Seschrock "volume size must be a multiple of volume block " 28902676Seschrock "size")); 28912676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28921133Seschrock } 2893789Sahrens } 2894789Sahrens 28955094Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 28962676Seschrock return (-1); 28972676Seschrock nvlist_free(props); 28982676Seschrock 2899789Sahrens /* create the dataset */ 29004543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2901789Sahrens 29022676Seschrock zcmd_free_nvlists(&zc); 29032676Seschrock 2904789Sahrens /* check for failure */ 2905789Sahrens if (ret != 0) { 2906789Sahrens char parent[ZFS_MAXNAMELEN]; 2907789Sahrens (void) parent_name(path, parent, sizeof (parent)); 2908789Sahrens 2909789Sahrens switch (errno) { 2910789Sahrens case ENOENT: 29112082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29122082Seschrock "no such parent '%s'"), parent); 29132082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2914789Sahrens 2915789Sahrens case EINVAL: 29162082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29173413Smmusante "parent '%s' is not a filesystem"), parent); 29182082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2919789Sahrens 2920789Sahrens case EDOM: 29212082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29222676Seschrock "volume block size must be power of 2 from " 29232676Seschrock "%u to %uk"), 2924789Sahrens (uint_t)SPA_MINBLOCKSIZE, 2925789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 29262082Seschrock 29272676Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 29282082Seschrock 29294603Sahrens case ENOTSUP: 29304603Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29314603Sahrens "pool must be upgraded to set this " 29324603Sahrens "property or value")); 29334603Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 2934789Sahrens #ifdef _ILP32 2935789Sahrens case EOVERFLOW: 2936789Sahrens /* 2937789Sahrens * This platform can't address a volume this big. 2938789Sahrens */ 29392082Seschrock if (type == ZFS_TYPE_VOLUME) 29402082Seschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 29412082Seschrock errbuf)); 2942789Sahrens #endif 29432082Seschrock /* FALLTHROUGH */ 2944789Sahrens default: 29452082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2946789Sahrens } 2947789Sahrens } 2948789Sahrens 2949789Sahrens return (0); 2950789Sahrens } 2951789Sahrens 2952789Sahrens /* 2953789Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2954789Sahrens * isn't mounted, and that there are no active dependents. 2955789Sahrens */ 2956789Sahrens int 295710242Schris.kirby@sun.com zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 2958789Sahrens { 2959789Sahrens zfs_cmd_t zc = { 0 }; 2960789Sahrens 2961789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2962789Sahrens 29632676Seschrock if (ZFS_IS_VOLUME(zhp)) { 2964789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2965789Sahrens } else { 2966789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2967789Sahrens } 2968789Sahrens 296910242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 29704543Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 29713237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 29722082Seschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 29732082Seschrock zhp->zfs_name)); 29742199Sahrens } 2975789Sahrens 2976789Sahrens remove_mountpoint(zhp); 2977789Sahrens 2978789Sahrens return (0); 2979789Sahrens } 2980789Sahrens 29812199Sahrens struct destroydata { 29822199Sahrens char *snapname; 29832199Sahrens boolean_t gotone; 29843265Sahrens boolean_t closezhp; 29852199Sahrens }; 29862199Sahrens 29872199Sahrens static int 298810588SEric.Taylor@Sun.COM zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 29892199Sahrens { 29902199Sahrens struct destroydata *dd = arg; 29912199Sahrens zfs_handle_t *szhp; 29922199Sahrens char name[ZFS_MAXNAMELEN]; 29933265Sahrens boolean_t closezhp = dd->closezhp; 299410588SEric.Taylor@Sun.COM int rv = 0; 29952199Sahrens 29962676Seschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 29972676Seschrock (void) strlcat(name, "@", sizeof (name)); 29982676Seschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29992199Sahrens 30002199Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 30012199Sahrens if (szhp) { 30022199Sahrens dd->gotone = B_TRUE; 30032199Sahrens zfs_close(szhp); 30042199Sahrens } 30052199Sahrens 30063265Sahrens dd->closezhp = B_TRUE; 300710588SEric.Taylor@Sun.COM if (!dd->gotone) 300810588SEric.Taylor@Sun.COM rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); 30093265Sahrens if (closezhp) 30103265Sahrens zfs_close(zhp); 30113265Sahrens return (rv); 30122199Sahrens } 30132199Sahrens 30142199Sahrens /* 30152199Sahrens * Destroys all snapshots with the given name in zhp & descendants. 30162199Sahrens */ 30172199Sahrens int 301810242Schris.kirby@sun.com zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 30192199Sahrens { 30202199Sahrens zfs_cmd_t zc = { 0 }; 30212199Sahrens int ret; 30222199Sahrens struct destroydata dd = { 0 }; 30232199Sahrens 30242199Sahrens dd.snapname = snapname; 302510588SEric.Taylor@Sun.COM (void) zfs_check_snap_cb(zhp, &dd); 30262199Sahrens 30272199Sahrens if (!dd.gotone) { 30283237Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 30292199Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 30302199Sahrens zhp->zfs_name, snapname)); 30312199Sahrens } 30322199Sahrens 30332199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 30342676Seschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 303510242Schris.kirby@sun.com zc.zc_defer_destroy = defer; 30362199Sahrens 30374543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 30382199Sahrens if (ret != 0) { 30392199Sahrens char errbuf[1024]; 30402199Sahrens 30412199Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30422199Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 30432199Sahrens 30442199Sahrens switch (errno) { 30452199Sahrens case EEXIST: 30462199Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30472199Sahrens "snapshot is cloned")); 30482199Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 30492199Sahrens 30502199Sahrens default: 30512199Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 30522199Sahrens errbuf)); 30532199Sahrens } 30542199Sahrens } 30552199Sahrens 30562199Sahrens return (0); 30572199Sahrens } 30582199Sahrens 3059789Sahrens /* 3060789Sahrens * Clones the given dataset. The target must be of the same type as the source. 3061789Sahrens */ 3062789Sahrens int 30632676Seschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3064789Sahrens { 3065789Sahrens zfs_cmd_t zc = { 0 }; 3066789Sahrens char parent[ZFS_MAXNAMELEN]; 3067789Sahrens int ret; 30682082Seschrock char errbuf[1024]; 30692082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 30702676Seschrock zfs_type_t type; 30712676Seschrock uint64_t zoned; 3072789Sahrens 3073789Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3074789Sahrens 30752082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30762082Seschrock "cannot create '%s'"), target); 30772082Seschrock 3078789Sahrens /* validate the target name */ 30795326Sek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 30802082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3081789Sahrens 3082789Sahrens /* validate parents exist */ 30834490Svb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3084789Sahrens return (-1); 3085789Sahrens 3086789Sahrens (void) parent_name(target, parent, sizeof (parent)); 3087789Sahrens 3088789Sahrens /* do the clone */ 30892676Seschrock if (ZFS_IS_VOLUME(zhp)) { 3090789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30912744Snn35248 type = ZFS_TYPE_VOLUME; 30922676Seschrock } else { 3093789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30942744Snn35248 type = ZFS_TYPE_FILESYSTEM; 30952676Seschrock } 30962676Seschrock 30972676Seschrock if (props) { 30987184Stimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 30997184Stimh zhp, errbuf)) == NULL) 31002676Seschrock return (-1); 31012676Seschrock 31025094Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 31032676Seschrock nvlist_free(props); 31042676Seschrock return (-1); 31052676Seschrock } 31062676Seschrock 31072676Seschrock nvlist_free(props); 31082676Seschrock } 3109789Sahrens 3110789Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 31112676Seschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 31124543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3113789Sahrens 31142676Seschrock zcmd_free_nvlists(&zc); 31152676Seschrock 3116789Sahrens if (ret != 0) { 3117789Sahrens switch (errno) { 3118789Sahrens 3119789Sahrens case ENOENT: 3120789Sahrens /* 3121789Sahrens * The parent doesn't exist. We should have caught this 3122789Sahrens * above, but there may a race condition that has since 3123789Sahrens * destroyed the parent. 3124789Sahrens * 3125789Sahrens * At this point, we don't know whether it's the source 3126789Sahrens * that doesn't exist anymore, or whether the target 3127789Sahrens * dataset doesn't exist. 3128789Sahrens */ 31292082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 31302082Seschrock "no such parent '%s'"), parent); 31312082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 31322082Seschrock 31332082Seschrock case EXDEV: 31342082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 31352082Seschrock "source and target pools differ")); 31362082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 31372082Seschrock errbuf)); 31382082Seschrock 31392082Seschrock default: 31402082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 31412082Seschrock errbuf)); 31422082Seschrock } 31432082Seschrock } 31442082Seschrock 31452082Seschrock return (ret); 31462082Seschrock } 31472082Seschrock 31482082Seschrock /* 31492082Seschrock * Promotes the given clone fs to be the clone parent. 31502082Seschrock */ 31512082Seschrock int 31522082Seschrock zfs_promote(zfs_handle_t *zhp) 31532082Seschrock { 31542082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 31552082Seschrock zfs_cmd_t zc = { 0 }; 31562082Seschrock char parent[MAXPATHLEN]; 31572082Seschrock int ret; 31582082Seschrock char errbuf[1024]; 31592082Seschrock 31602082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31612082Seschrock "cannot promote '%s'"), zhp->zfs_name); 31622082Seschrock 31632082Seschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 31642082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31652082Seschrock "snapshots can not be promoted")); 31662082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31672082Seschrock } 31682082Seschrock 31695367Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 31702082Seschrock if (parent[0] == '\0') { 31712082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31722082Seschrock "not a cloned filesystem")); 31732082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 31742082Seschrock } 317510588SEric.Taylor@Sun.COM 31765367Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 31772676Seschrock sizeof (zc.zc_value)); 31782082Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 31794543Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 31802082Seschrock 31812082Seschrock if (ret != 0) { 31822417Sahrens int save_errno = errno; 31832417Sahrens 31842417Sahrens switch (save_errno) { 3185789Sahrens case EEXIST: 318610588SEric.Taylor@Sun.COM /* There is a conflicting snapshot name. */ 31872082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 318810588SEric.Taylor@Sun.COM "conflicting snapshot '%s' from parent '%s'"), 318910588SEric.Taylor@Sun.COM zc.zc_string, parent); 31902082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3191789Sahrens 3192789Sahrens default: 31932417Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3194789Sahrens } 3195789Sahrens } 31962676Seschrock return (ret); 31972199Sahrens } 31982199Sahrens 3199789Sahrens /* 32003504Sahl * Takes a snapshot of the given dataset. 3201789Sahrens */ 3202789Sahrens int 32037265Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 32047265Sahrens nvlist_t *props) 3205789Sahrens { 3206789Sahrens const char *delim; 32077265Sahrens char parent[ZFS_MAXNAMELEN]; 3208789Sahrens zfs_handle_t *zhp; 3209789Sahrens zfs_cmd_t zc = { 0 }; 3210789Sahrens int ret; 32112082Seschrock char errbuf[1024]; 32122082Seschrock 32132082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 32142082Seschrock "cannot snapshot '%s'"), path); 32152082Seschrock 32162082Seschrock /* validate the target name */ 32175326Sek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 32182082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3219789Sahrens 32207265Sahrens if (props) { 32217265Sahrens if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 32227265Sahrens props, B_FALSE, NULL, errbuf)) == NULL) 32237265Sahrens return (-1); 32247265Sahrens 32257265Sahrens if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 32267265Sahrens nvlist_free(props); 32277265Sahrens return (-1); 32287265Sahrens } 32297265Sahrens 32307265Sahrens nvlist_free(props); 32317265Sahrens } 32327265Sahrens 3233789Sahrens /* make sure the parent exists and is of the appropriate type */ 32342199Sahrens delim = strchr(path, '@'); 3235789Sahrens (void) strncpy(parent, path, delim - path); 3236789Sahrens parent[delim - path] = '\0'; 3237789Sahrens 32382082Seschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3239789Sahrens ZFS_TYPE_VOLUME)) == NULL) { 32407265Sahrens zcmd_free_nvlists(&zc); 3241789Sahrens return (-1); 3242789Sahrens } 3243789Sahrens 32442199Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 32452676Seschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 32464543Smarks if (ZFS_IS_VOLUME(zhp)) 32474543Smarks zc.zc_objset_type = DMU_OST_ZVOL; 32484543Smarks else 32494543Smarks zc.zc_objset_type = DMU_OST_ZFS; 32502199Sahrens zc.zc_cookie = recursive; 32514543Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 32522199Sahrens 32537265Sahrens zcmd_free_nvlists(&zc); 32547265Sahrens 32552199Sahrens /* 32562199Sahrens * if it was recursive, the one that actually failed will be in 32572199Sahrens * zc.zc_name. 32582199Sahrens */ 325910588SEric.Taylor@Sun.COM if (ret != 0) { 32604543Smarks (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 32614543Smarks "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 326210588SEric.Taylor@Sun.COM (void) zfs_standard_error(hdl, errno, errbuf); 32632199Sahrens } 3264789Sahrens 3265789Sahrens zfs_close(zhp); 3266789Sahrens 3267789Sahrens return (ret); 3268789Sahrens } 3269789Sahrens 3270789Sahrens /* 32711294Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 32721294Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 32731294Slling * is a dependent and we should just destroy it without checking the transaction 32741294Slling * group. 3275789Sahrens */ 32761294Slling typedef struct rollback_data { 32771294Slling const char *cb_target; /* the snapshot */ 32781294Slling uint64_t cb_create; /* creation time reference */ 32795749Sahrens boolean_t cb_error; 32802082Seschrock boolean_t cb_dependent; 32815749Sahrens boolean_t cb_force; 32821294Slling } rollback_data_t; 32831294Slling 32841294Slling static int 32851294Slling rollback_destroy(zfs_handle_t *zhp, void *data) 32861294Slling { 32871294Slling rollback_data_t *cbp = data; 32881294Slling 32891294Slling if (!cbp->cb_dependent) { 32901294Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 32911294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 32921294Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 32931294Slling cbp->cb_create) { 32944543Smarks char *logstr; 32951294Slling 32962082Seschrock cbp->cb_dependent = B_TRUE; 32975446Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 32985446Sahrens rollback_destroy, cbp); 32992082Seschrock cbp->cb_dependent = B_FALSE; 33001294Slling 33014543Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 33024543Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 330310242Schris.kirby@sun.com cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 33044543Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 33051294Slling } 33061294Slling } else { 33075749Sahrens /* We must destroy this clone; first unmount it */ 33085749Sahrens prop_changelist_t *clp; 33095749Sahrens 33107366STim.Haley@Sun.COM clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 33115749Sahrens cbp->cb_force ? MS_FORCE: 0); 33125749Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 33135749Sahrens cbp->cb_error = B_TRUE; 33145749Sahrens zfs_close(zhp); 33155749Sahrens return (0); 33165749Sahrens } 331710242Schris.kirby@sun.com if (zfs_destroy(zhp, B_FALSE) != 0) 33185749Sahrens cbp->cb_error = B_TRUE; 33195749Sahrens else 33205749Sahrens changelist_remove(clp, zhp->zfs_name); 33215751Sahrens (void) changelist_postfix(clp); 33225749Sahrens changelist_free(clp); 33231294Slling } 33241294Slling 33251294Slling zfs_close(zhp); 33261294Slling return (0); 33271294Slling } 33281294Slling 33291294Slling /* 33305446Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33315446Sahrens * data changes since then and making it the active dataset. 33325446Sahrens * 33335446Sahrens * Any snapshots more recent than the target are destroyed, along with 33345446Sahrens * their dependents. 33351294Slling */ 33365446Sahrens int 33375749Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3338789Sahrens { 33395446Sahrens rollback_data_t cb = { 0 }; 33405446Sahrens int err; 3341789Sahrens zfs_cmd_t zc = { 0 }; 33425713Srm160521 boolean_t restore_resv = 0; 33435713Srm160521 uint64_t old_volsize, new_volsize; 33445713Srm160521 zfs_prop_t resv_prop; 3345789Sahrens 3346789Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3347789Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3348789Sahrens 33495446Sahrens /* 33505446Sahrens * Destroy all recent snapshots and its dependends. 33515446Sahrens */ 33525749Sahrens cb.cb_force = force; 33535446Sahrens cb.cb_target = snap->zfs_name; 33545446Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 33555446Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 33565446Sahrens 33575749Sahrens if (cb.cb_error) 33585749Sahrens return (-1); 33595446Sahrens 33605446Sahrens /* 33615446Sahrens * Now that we have verified that the snapshot is the latest, 33625446Sahrens * rollback to the given snapshot. 33635446Sahrens */ 33645446Sahrens 33655713Srm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33665713Srm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 33675713Srm160521 return (-1); 33685713Srm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 33695713Srm160521 restore_resv = 33705713Srm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 33715713Srm160521 } 3372789Sahrens 3373789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3374789Sahrens 33752676Seschrock if (ZFS_IS_VOLUME(zhp)) 3376789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3377789Sahrens else 3378789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3379789Sahrens 3380789Sahrens /* 33815446Sahrens * We rely on zfs_iter_children() to verify that there are no 33825446Sahrens * newer snapshots for the given dataset. Therefore, we can 33835446Sahrens * simply pass the name on to the ioctl() call. There is still 33845446Sahrens * an unlikely race condition where the user has taken a 33855446Sahrens * snapshot since we verified that this was the most recent. 33865713Srm160521 * 3387789Sahrens */ 33885446Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 33893237Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 33902082Seschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 33912082Seschrock zhp->zfs_name); 33925717Srm160521 return (err); 33935717Srm160521 } 33945713Srm160521 33955713Srm160521 /* 33965713Srm160521 * For volumes, if the pre-rollback volsize matched the pre- 33975713Srm160521 * rollback reservation and the volsize has changed then set 33985713Srm160521 * the reservation property to the post-rollback volsize. 33995713Srm160521 * Make a new handle since the rollback closed the dataset. 34005713Srm160521 */ 34015717Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 34025717Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 34035713Srm160521 if (restore_resv) { 34045713Srm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 34055713Srm160521 if (old_volsize != new_volsize) 34065717Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 34075717Srm160521 new_volsize); 34085713Srm160521 } 34095713Srm160521 zfs_close(zhp); 3410789Sahrens } 34115446Sahrens return (err); 34121294Slling } 34131294Slling 34141294Slling /* 3415789Sahrens * Iterate over all dependents for a given dataset. This includes both 3416789Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3417789Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3418789Sahrens * libzfs_graph.c. 3419789Sahrens */ 3420789Sahrens int 34212474Seschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 34222474Seschrock zfs_iter_f func, void *data) 3423789Sahrens { 3424789Sahrens char **dependents; 3425789Sahrens size_t count; 3426789Sahrens int i; 3427789Sahrens zfs_handle_t *child; 3428789Sahrens int ret = 0; 3429789Sahrens 34302474Seschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34312474Seschrock &dependents, &count) != 0) 34322474Seschrock return (-1); 34332474Seschrock 3434789Sahrens for (i = 0; i < count; i++) { 34352082Seschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 34362082Seschrock dependents[i])) == NULL) 3437789Sahrens continue; 3438789Sahrens 3439789Sahrens if ((ret = func(child, data)) != 0) 3440789Sahrens break; 3441789Sahrens } 3442789Sahrens 3443789Sahrens for (i = 0; i < count; i++) 3444789Sahrens free(dependents[i]); 3445789Sahrens free(dependents); 3446789Sahrens 3447789Sahrens return (ret); 3448789Sahrens } 3449789Sahrens 3450789Sahrens /* 3451789Sahrens * Renames the given dataset. 3452789Sahrens */ 3453789Sahrens int 34544490Svb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3455789Sahrens { 3456789Sahrens int ret; 3457789Sahrens zfs_cmd_t zc = { 0 }; 3458789Sahrens char *delim; 34594007Smmusante prop_changelist_t *cl = NULL; 34604007Smmusante zfs_handle_t *zhrp = NULL; 34614007Smmusante char *parentname = NULL; 3462789Sahrens char parent[ZFS_MAXNAMELEN]; 34632082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 34642082Seschrock char errbuf[1024]; 3465789Sahrens 3466789Sahrens /* if we have the same exact name, just return success */ 3467789Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3468789Sahrens return (0); 3469789Sahrens 34702082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 34712082Seschrock "cannot rename to '%s'"), target); 34722082Seschrock 3473789Sahrens /* 3474789Sahrens * Make sure the target name is valid 3475789Sahrens */ 3476789Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 34772665Snd150628 if ((strchr(target, '@') == NULL) || 34782665Snd150628 *target == '@') { 34792665Snd150628 /* 34802665Snd150628 * Snapshot target name is abbreviated, 34812665Snd150628 * reconstruct full dataset name 34822665Snd150628 */ 34832665Snd150628 (void) strlcpy(parent, zhp->zfs_name, 34842665Snd150628 sizeof (parent)); 34852665Snd150628 delim = strchr(parent, '@'); 34862665Snd150628 if (strchr(target, '@') == NULL) 34872665Snd150628 *(++delim) = '\0'; 34882665Snd150628 else 34892665Snd150628 *delim = '\0'; 34902665Snd150628 (void) strlcat(parent, target, sizeof (parent)); 34912665Snd150628 target = parent; 34922665Snd150628 } else { 34932665Snd150628 /* 34942665Snd150628 * Make sure we're renaming within the same dataset. 34952665Snd150628 */ 34962665Snd150628 delim = strchr(target, '@'); 34972665Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 34982665Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 34992665Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35002665Snd150628 "snapshots must be part of same " 35012665Snd150628 "dataset")); 35022665Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 35033912Slling errbuf)); 35042665Snd150628 } 3505789Sahrens } 35065326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 35072665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3508789Sahrens } else { 35094007Smmusante if (recursive) { 35104007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35114007Smmusante "recursive rename must be a snapshot")); 35124007Smmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 35134007Smmusante } 35144007Smmusante 35155326Sek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 35162665Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 35172676Seschrock 3518789Sahrens /* validate parents */ 351911497SMark.Musante@Sun.COM if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3520789Sahrens return (-1); 3521789Sahrens 3522789Sahrens /* make sure we're in the same pool */ 3523789Sahrens verify((delim = strchr(target, '/')) != NULL); 3524789Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3525789Sahrens zhp->zfs_name[delim - target] != '/') { 35262082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35272082Seschrock "datasets must be within same pool")); 35282082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3529789Sahrens } 35302440Snd150628 35312440Snd150628 /* new name cannot be a child of the current dataset name */ 353211497SMark.Musante@Sun.COM if (is_descendant(zhp->zfs_name, target)) { 35332440Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 353411497SMark.Musante@Sun.COM "New dataset name cannot be a descendant of " 35352440Snd150628 "current dataset name")); 35362440Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 35372440Snd150628 } 3538789Sahrens } 3539789Sahrens 35402082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 35412082Seschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 35422082Seschrock 3543789Sahrens if (getzoneid() == GLOBAL_ZONEID && 3544789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 35452082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35462082Seschrock "dataset is used in a non-global zone")); 35472082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3548789Sahrens } 3549789Sahrens 35504007Smmusante if (recursive) { 35514007Smmusante 35524183Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 35534183Smmusante if (parentname == NULL) { 35544183Smmusante ret = -1; 35554183Smmusante goto error; 35564183Smmusante } 35574007Smmusante delim = strchr(parentname, '@'); 35584007Smmusante *delim = '\0'; 35595094Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 35604007Smmusante if (zhrp == NULL) { 35614183Smmusante ret = -1; 35624183Smmusante goto error; 35634007Smmusante } 35644007Smmusante 35654007Smmusante } else { 35667366STim.Haley@Sun.COM if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) 35674007Smmusante return (-1); 35684007Smmusante 35694007Smmusante if (changelist_haszonedchild(cl)) { 35704007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35714007Smmusante "child dataset with inherited mountpoint is used " 35724007Smmusante "in a non-global zone")); 35734007Smmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 35744007Smmusante goto error; 35754007Smmusante } 35764007Smmusante 35774007Smmusante if ((ret = changelist_prefix(cl)) != 0) 35784007Smmusante goto error; 3579789Sahrens } 3580789Sahrens 35812676Seschrock if (ZFS_IS_VOLUME(zhp)) 3582789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3583789Sahrens else 3584789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3585789Sahrens 35862665Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 35872676Seschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 35882665Snd150628 35894007Smmusante zc.zc_cookie = recursive; 35904007Smmusante 35914543Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 35924007Smmusante /* 35934007Smmusante * if it was recursive, the one that actually failed will 35944007Smmusante * be in zc.zc_name 35954007Smmusante */ 35964007Smmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35975367Sahrens "cannot rename '%s'"), zc.zc_name); 35984007Smmusante 35994007Smmusante if (recursive && errno == EEXIST) { 36004007Smmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 36014007Smmusante "a child dataset already has a snapshot " 36024007Smmusante "with the new name")); 36034801Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 36044007Smmusante } else { 36054007Smmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 36064007Smmusante } 3607789Sahrens 3608789Sahrens /* 3609789Sahrens * On failure, we still want to remount any filesystems that 3610789Sahrens * were previously mounted, so we don't alter the system state. 3611789Sahrens */ 361210588SEric.Taylor@Sun.COM if (!recursive) 36134007Smmusante (void) changelist_postfix(cl); 3614789Sahrens } else { 361510588SEric.Taylor@Sun.COM if (!recursive) { 36164007Smmusante changelist_rename(cl, zfs_get_name(zhp), target); 36174007Smmusante ret = changelist_postfix(cl); 36184007Smmusante } 3619789Sahrens } 3620789Sahrens 3621789Sahrens error: 36224007Smmusante if (parentname) { 36234007Smmusante free(parentname); 36244007Smmusante } 36254007Smmusante if (zhrp) { 36264007Smmusante zfs_close(zhrp); 36274007Smmusante } 36284007Smmusante if (cl) { 36294007Smmusante changelist_free(cl); 36304007Smmusante } 3631789Sahrens return (ret); 3632789Sahrens } 3633789Sahrens 36342676Seschrock nvlist_t * 36352676Seschrock zfs_get_user_props(zfs_handle_t *zhp) 36362676Seschrock { 36372676Seschrock return (zhp->zfs_user_props); 36382676Seschrock } 36392676Seschrock 364011022STom.Erickson@Sun.COM nvlist_t * 364111022STom.Erickson@Sun.COM zfs_get_recvd_props(zfs_handle_t *zhp) 364211022STom.Erickson@Sun.COM { 364311022STom.Erickson@Sun.COM if (zhp->zfs_recvd_props == NULL) 364411022STom.Erickson@Sun.COM if (get_recvd_props_ioctl(zhp) != 0) 364511022STom.Erickson@Sun.COM return (NULL); 364611022STom.Erickson@Sun.COM return (zhp->zfs_recvd_props); 364711022STom.Erickson@Sun.COM } 364811022STom.Erickson@Sun.COM 36492676Seschrock /* 36503912Slling * This function is used by 'zfs list' to determine the exact set of columns to 36513912Slling * display, and their maximum widths. This does two main things: 36523912Slling * 36533912Slling * - If this is a list of all properties, then expand the list to include 36543912Slling * all native properties, and set a flag so that for each dataset we look 36553912Slling * for new unique user properties and add them to the list. 36563912Slling * 36573912Slling * - For non fixed-width properties, keep track of the maximum width seen 365811022STom.Erickson@Sun.COM * so that we can size the column appropriately. If the user has 365911022STom.Erickson@Sun.COM * requested received property values, we also need to compute the width 366011022STom.Erickson@Sun.COM * of the RECEIVED column. 36613912Slling */ 36623912Slling int 366311022STom.Erickson@Sun.COM zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 36643912Slling { 36653912Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 36665094Slling zprop_list_t *entry; 36675094Slling zprop_list_t **last, **start; 36683912Slling nvlist_t *userprops, *propval; 36693912Slling nvpair_t *elem; 36703912Slling char *strval; 36713912Slling char buf[ZFS_MAXPROPLEN]; 36723912Slling 36735094Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 36743912Slling return (-1); 36752676Seschrock 36762676Seschrock userprops = zfs_get_user_props(zhp); 36772676Seschrock 36782676Seschrock entry = *plp; 36792676Seschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 36802676Seschrock /* 36812676Seschrock * Go through and add any user properties as necessary. We 36822676Seschrock * start by incrementing our list pointer to the first 36832676Seschrock * non-native property. 36842676Seschrock */ 36852676Seschrock start = plp; 36862676Seschrock while (*start != NULL) { 36875094Slling if ((*start)->pl_prop == ZPROP_INVAL) 36882676Seschrock break; 36892676Seschrock start = &(*start)->pl_next; 36902676Seschrock } 36912676Seschrock 36922676Seschrock elem = NULL; 36932676Seschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 36942676Seschrock /* 36952676Seschrock * See if we've already found this property in our list. 36962676Seschrock */ 36972676Seschrock for (last = start; *last != NULL; 36982676Seschrock last = &(*last)->pl_next) { 36992676Seschrock if (strcmp((*last)->pl_user_prop, 37002676Seschrock nvpair_name(elem)) == 0) 37012676Seschrock break; 37022676Seschrock } 37032676Seschrock 37042676Seschrock if (*last == NULL) { 37052676Seschrock if ((entry = zfs_alloc(hdl, 37065094Slling sizeof (zprop_list_t))) == NULL || 37072676Seschrock ((entry->pl_user_prop = zfs_strdup(hdl, 37082676Seschrock nvpair_name(elem)))) == NULL) { 37092676Seschrock free(entry); 37102676Seschrock return (-1); 37112676Seschrock } 37122676Seschrock 37135094Slling entry->pl_prop = ZPROP_INVAL; 37142676Seschrock entry->pl_width = strlen(nvpair_name(elem)); 37152676Seschrock entry->pl_all = B_TRUE; 37162676Seschrock *last = entry; 37172676Seschrock } 37182676Seschrock } 37192676Seschrock } 37202676Seschrock 37212676Seschrock /* 37222676Seschrock * Now go through and check the width of any non-fixed columns 37232676Seschrock */ 37242676Seschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 37252676Seschrock if (entry->pl_fixed) 37262676Seschrock continue; 37272676Seschrock 37285094Slling if (entry->pl_prop != ZPROP_INVAL) { 37292676Seschrock if (zfs_prop_get(zhp, entry->pl_prop, 37302676Seschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 37312676Seschrock if (strlen(buf) > entry->pl_width) 37322676Seschrock entry->pl_width = strlen(buf); 37332676Seschrock } 373411022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 373511022STom.Erickson@Sun.COM zfs_prop_to_name(entry->pl_prop), 373611022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 373711022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 373811022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 373911022STom.Erickson@Sun.COM } else { 374011022STom.Erickson@Sun.COM if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 374111022STom.Erickson@Sun.COM &propval) == 0) { 374211022STom.Erickson@Sun.COM verify(nvlist_lookup_string(propval, 374311022STom.Erickson@Sun.COM ZPROP_VALUE, &strval) == 0); 374411022STom.Erickson@Sun.COM if (strlen(strval) > entry->pl_width) 374511022STom.Erickson@Sun.COM entry->pl_width = strlen(strval); 374611022STom.Erickson@Sun.COM } 374711022STom.Erickson@Sun.COM if (received && zfs_prop_get_recvd(zhp, 374811022STom.Erickson@Sun.COM entry->pl_user_prop, 374911022STom.Erickson@Sun.COM buf, sizeof (buf), B_FALSE) == 0) 375011022STom.Erickson@Sun.COM if (strlen(buf) > entry->pl_recvd_width) 375111022STom.Erickson@Sun.COM entry->pl_recvd_width = strlen(buf); 37522676Seschrock } 37532676Seschrock } 37542676Seschrock 37552676Seschrock return (0); 37562676Seschrock } 37574543Smarks 37584543Smarks int 37594543Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 37608845Samw@Sun.COM char *resource, void *export, void *sharetab, 37618845Samw@Sun.COM int sharemax, zfs_share_op_t operation) 37624543Smarks { 37634543Smarks zfs_cmd_t zc = { 0 }; 37644543Smarks int error; 37654543Smarks 37664543Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 37674543Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 37688845Samw@Sun.COM if (resource) 37698845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 37704543Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 37714543Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 37725331Samw zc.zc_share.z_sharetype = operation; 37734543Smarks zc.zc_share.z_sharemax = sharemax; 37744543Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 37754543Smarks return (error); 37764543Smarks } 37778802SSanjeev.Bagewadi@Sun.COM 37788802SSanjeev.Bagewadi@Sun.COM void 37798802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 37808802SSanjeev.Bagewadi@Sun.COM { 37818802SSanjeev.Bagewadi@Sun.COM nvpair_t *curr; 37828802SSanjeev.Bagewadi@Sun.COM 37838802SSanjeev.Bagewadi@Sun.COM /* 37848802SSanjeev.Bagewadi@Sun.COM * Keep a reference to the props-table against which we prune the 37858802SSanjeev.Bagewadi@Sun.COM * properties. 37868802SSanjeev.Bagewadi@Sun.COM */ 37878802SSanjeev.Bagewadi@Sun.COM zhp->zfs_props_table = props; 37888802SSanjeev.Bagewadi@Sun.COM 37898802SSanjeev.Bagewadi@Sun.COM curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 37908802SSanjeev.Bagewadi@Sun.COM 37918802SSanjeev.Bagewadi@Sun.COM while (curr) { 37928802SSanjeev.Bagewadi@Sun.COM zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 37938802SSanjeev.Bagewadi@Sun.COM nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 37948802SSanjeev.Bagewadi@Sun.COM 37959396SMatthew.Ahrens@Sun.COM /* 379610960SEric.Schrock@Sun.COM * User properties will result in ZPROP_INVAL, and since we 379710960SEric.Schrock@Sun.COM * only know how to prune standard ZFS properties, we always 379810960SEric.Schrock@Sun.COM * leave these in the list. This can also happen if we 379910960SEric.Schrock@Sun.COM * encounter an unknown DSL property (when running older 380010960SEric.Schrock@Sun.COM * software, for example). 38019396SMatthew.Ahrens@Sun.COM */ 38029396SMatthew.Ahrens@Sun.COM if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 38038802SSanjeev.Bagewadi@Sun.COM (void) nvlist_remove(zhp->zfs_props, 38048802SSanjeev.Bagewadi@Sun.COM nvpair_name(curr), nvpair_type(curr)); 38058802SSanjeev.Bagewadi@Sun.COM curr = next; 38068802SSanjeev.Bagewadi@Sun.COM } 38078802SSanjeev.Bagewadi@Sun.COM } 38088845Samw@Sun.COM 38098845Samw@Sun.COM static int 38108845Samw@Sun.COM zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 38118845Samw@Sun.COM zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 38128845Samw@Sun.COM { 38138845Samw@Sun.COM zfs_cmd_t zc = { 0 }; 38148845Samw@Sun.COM nvlist_t *nvlist = NULL; 38158845Samw@Sun.COM int error; 38168845Samw@Sun.COM 38178845Samw@Sun.COM (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 38188845Samw@Sun.COM (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 38198845Samw@Sun.COM zc.zc_cookie = (uint64_t)cmd; 38208845Samw@Sun.COM 38218845Samw@Sun.COM if (cmd == ZFS_SMB_ACL_RENAME) { 38228845Samw@Sun.COM if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 38238845Samw@Sun.COM (void) no_memory(hdl); 38248845Samw@Sun.COM return (NULL); 38258845Samw@Sun.COM } 38268845Samw@Sun.COM } 38278845Samw@Sun.COM 38288845Samw@Sun.COM switch (cmd) { 38298845Samw@Sun.COM case ZFS_SMB_ACL_ADD: 38308845Samw@Sun.COM case ZFS_SMB_ACL_REMOVE: 38318845Samw@Sun.COM (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 38328845Samw@Sun.COM break; 38338845Samw@Sun.COM case ZFS_SMB_ACL_RENAME: 38348845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 38358845Samw@Sun.COM resource1) != 0) { 38368845Samw@Sun.COM (void) no_memory(hdl); 38378845Samw@Sun.COM return (-1); 38388845Samw@Sun.COM } 38398845Samw@Sun.COM if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 38408845Samw@Sun.COM resource2) != 0) { 38418845Samw@Sun.COM (void) no_memory(hdl); 38428845Samw@Sun.COM return (-1); 38438845Samw@Sun.COM } 38448845Samw@Sun.COM if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 38458845Samw@Sun.COM nvlist_free(nvlist); 38468845Samw@Sun.COM return (-1); 38478845Samw@Sun.COM } 38488845Samw@Sun.COM break; 38498845Samw@Sun.COM case ZFS_SMB_ACL_PURGE: 38508845Samw@Sun.COM break; 38518845Samw@Sun.COM default: 38528845Samw@Sun.COM return (-1); 38538845Samw@Sun.COM } 38548845Samw@Sun.COM error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 38558845Samw@Sun.COM if (nvlist) 38568845Samw@Sun.COM nvlist_free(nvlist); 38578845Samw@Sun.COM return (error); 38588845Samw@Sun.COM } 38598845Samw@Sun.COM 38608845Samw@Sun.COM int 38618845Samw@Sun.COM zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 38628845Samw@Sun.COM char *path, char *resource) 38638845Samw@Sun.COM { 38648845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 38658845Samw@Sun.COM resource, NULL)); 38668845Samw@Sun.COM } 38678845Samw@Sun.COM 38688845Samw@Sun.COM int 38698845Samw@Sun.COM zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 38708845Samw@Sun.COM char *path, char *resource) 38718845Samw@Sun.COM { 38728845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 38738845Samw@Sun.COM resource, NULL)); 38748845Samw@Sun.COM } 38758845Samw@Sun.COM 38768845Samw@Sun.COM int 38778845Samw@Sun.COM zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 38788845Samw@Sun.COM { 38798845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 38808845Samw@Sun.COM NULL, NULL)); 38818845Samw@Sun.COM } 38828845Samw@Sun.COM 38838845Samw@Sun.COM int 38848845Samw@Sun.COM zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 38858845Samw@Sun.COM char *oldname, char *newname) 38868845Samw@Sun.COM { 38878845Samw@Sun.COM return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 38888845Samw@Sun.COM oldname, newname)); 38898845Samw@Sun.COM } 38909396SMatthew.Ahrens@Sun.COM 38919396SMatthew.Ahrens@Sun.COM int 38929396SMatthew.Ahrens@Sun.COM zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 38939396SMatthew.Ahrens@Sun.COM zfs_userspace_cb_t func, void *arg) 38949396SMatthew.Ahrens@Sun.COM { 38959396SMatthew.Ahrens@Sun.COM zfs_cmd_t zc = { 0 }; 38969396SMatthew.Ahrens@Sun.COM int error; 38979396SMatthew.Ahrens@Sun.COM zfs_useracct_t buf[100]; 38989396SMatthew.Ahrens@Sun.COM 38999396SMatthew.Ahrens@Sun.COM (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 39009396SMatthew.Ahrens@Sun.COM 39019396SMatthew.Ahrens@Sun.COM zc.zc_objset_type = type; 39029396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst = (uintptr_t)buf; 39039396SMatthew.Ahrens@Sun.COM 39049396SMatthew.Ahrens@Sun.COM /* CONSTCOND */ 39059396SMatthew.Ahrens@Sun.COM while (1) { 39069396SMatthew.Ahrens@Sun.COM zfs_useracct_t *zua = buf; 39079396SMatthew.Ahrens@Sun.COM 39089396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size = sizeof (buf); 39099396SMatthew.Ahrens@Sun.COM error = ioctl(zhp->zfs_hdl->libzfs_fd, 39109396SMatthew.Ahrens@Sun.COM ZFS_IOC_USERSPACE_MANY, &zc); 39119396SMatthew.Ahrens@Sun.COM if (error || zc.zc_nvlist_dst_size == 0) 39129396SMatthew.Ahrens@Sun.COM break; 39139396SMatthew.Ahrens@Sun.COM 39149396SMatthew.Ahrens@Sun.COM while (zc.zc_nvlist_dst_size > 0) { 39159554SMatthew.Ahrens@Sun.COM error = func(arg, zua->zu_domain, zua->zu_rid, 39169554SMatthew.Ahrens@Sun.COM zua->zu_space); 39179554SMatthew.Ahrens@Sun.COM if (error != 0) 39189554SMatthew.Ahrens@Sun.COM return (error); 39199396SMatthew.Ahrens@Sun.COM zua++; 39209396SMatthew.Ahrens@Sun.COM zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 39219396SMatthew.Ahrens@Sun.COM } 39229396SMatthew.Ahrens@Sun.COM } 39239396SMatthew.Ahrens@Sun.COM 39249396SMatthew.Ahrens@Sun.COM return (error); 39259396SMatthew.Ahrens@Sun.COM } 392610242Schris.kirby@sun.com 392710242Schris.kirby@sun.com int 392810242Schris.kirby@sun.com zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 392911417SChris.Kirby@sun.com boolean_t recursive, boolean_t temphold, boolean_t enoent_ok) 393010242Schris.kirby@sun.com { 393110242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 393210242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 393310242Schris.kirby@sun.com 393410242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 393510242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 393610342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 393710342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 393810342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 393910242Schris.kirby@sun.com zc.zc_cookie = recursive; 394010342Schris.kirby@sun.com zc.zc_temphold = temphold; 394110242Schris.kirby@sun.com 394210242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 394310242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 394410242Schris.kirby@sun.com 394510242Schris.kirby@sun.com /* 394610242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 394710242Schris.kirby@sun.com * zc.zc_name. 394810242Schris.kirby@sun.com */ 394910242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 395010242Schris.kirby@sun.com "cannot hold '%s@%s'"), zc.zc_name, snapname); 395110242Schris.kirby@sun.com switch (errno) { 395210951SChris.Kirby@sun.com case E2BIG: 395310951SChris.Kirby@sun.com /* 395410951SChris.Kirby@sun.com * Temporary tags wind up having the ds object id 395510951SChris.Kirby@sun.com * prepended. So even if we passed the length check 395610951SChris.Kirby@sun.com * above, it's still possible for the tag to wind 395710951SChris.Kirby@sun.com * up being slightly too long. 395810951SChris.Kirby@sun.com */ 395910951SChris.Kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 396010242Schris.kirby@sun.com case ENOTSUP: 396110242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 396210242Schris.kirby@sun.com "pool must be upgraded")); 396310242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 396410242Schris.kirby@sun.com case EINVAL: 396510242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 396610242Schris.kirby@sun.com case EEXIST: 396710242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 396811417SChris.Kirby@sun.com case ENOENT: 396911417SChris.Kirby@sun.com if (enoent_ok) 397011417SChris.Kirby@sun.com return (0); 397111417SChris.Kirby@sun.com /* FALLTHROUGH */ 397210242Schris.kirby@sun.com default: 397310242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 397410242Schris.kirby@sun.com } 397510242Schris.kirby@sun.com } 397610242Schris.kirby@sun.com 397710242Schris.kirby@sun.com return (0); 397810242Schris.kirby@sun.com } 397910242Schris.kirby@sun.com 398010342Schris.kirby@sun.com struct hold_range_arg { 398110342Schris.kirby@sun.com zfs_handle_t *origin; 398210342Schris.kirby@sun.com const char *fromsnap; 398310342Schris.kirby@sun.com const char *tosnap; 398410342Schris.kirby@sun.com char lastsnapheld[ZFS_MAXNAMELEN]; 398510342Schris.kirby@sun.com const char *tag; 398610342Schris.kirby@sun.com boolean_t temphold; 398710342Schris.kirby@sun.com boolean_t seento; 398810342Schris.kirby@sun.com boolean_t seenfrom; 398910342Schris.kirby@sun.com boolean_t holding; 399011546SChris.Kirby@sun.com boolean_t recursive; 399111814SChris.Kirby@sun.com snapfilter_cb_t *filter_cb; 399211814SChris.Kirby@sun.com void *filter_cb_arg; 399310342Schris.kirby@sun.com }; 399410342Schris.kirby@sun.com 399510342Schris.kirby@sun.com static int 399610342Schris.kirby@sun.com zfs_hold_range_one(zfs_handle_t *zhp, void *arg) 399710342Schris.kirby@sun.com { 399810342Schris.kirby@sun.com struct hold_range_arg *hra = arg; 399910342Schris.kirby@sun.com const char *thissnap; 400010342Schris.kirby@sun.com int error; 400110342Schris.kirby@sun.com 400210342Schris.kirby@sun.com thissnap = strchr(zfs_get_name(zhp), '@') + 1; 400310342Schris.kirby@sun.com 400410342Schris.kirby@sun.com if (hra->fromsnap && !hra->seenfrom && 400510342Schris.kirby@sun.com strcmp(hra->fromsnap, thissnap) == 0) 400610342Schris.kirby@sun.com hra->seenfrom = B_TRUE; 400710342Schris.kirby@sun.com 400810342Schris.kirby@sun.com /* snap is older or newer than the desired range, ignore it */ 400910342Schris.kirby@sun.com if (hra->seento || !hra->seenfrom) { 401010342Schris.kirby@sun.com zfs_close(zhp); 401110342Schris.kirby@sun.com return (0); 401210342Schris.kirby@sun.com } 401310342Schris.kirby@sun.com 401411814SChris.Kirby@sun.com if (!hra->seento && strcmp(hra->tosnap, thissnap) == 0) 401511814SChris.Kirby@sun.com hra->seento = B_TRUE; 401611814SChris.Kirby@sun.com 401711814SChris.Kirby@sun.com if (hra->filter_cb != NULL && 401811814SChris.Kirby@sun.com hra->filter_cb(zhp, hra->filter_cb_arg) == B_FALSE) { 401911814SChris.Kirby@sun.com zfs_close(zhp); 402011814SChris.Kirby@sun.com return (0); 402111814SChris.Kirby@sun.com } 402211814SChris.Kirby@sun.com 402310342Schris.kirby@sun.com if (hra->holding) { 402411417SChris.Kirby@sun.com /* We could be racing with destroy, so ignore ENOENT. */ 402511546SChris.Kirby@sun.com error = zfs_hold(hra->origin, thissnap, hra->tag, 402611546SChris.Kirby@sun.com hra->recursive, hra->temphold, B_TRUE); 402710342Schris.kirby@sun.com if (error == 0) { 402810342Schris.kirby@sun.com (void) strlcpy(hra->lastsnapheld, zfs_get_name(zhp), 402910342Schris.kirby@sun.com sizeof (hra->lastsnapheld)); 403010342Schris.kirby@sun.com } 403110342Schris.kirby@sun.com } else { 403211546SChris.Kirby@sun.com error = zfs_release(hra->origin, thissnap, hra->tag, 403311546SChris.Kirby@sun.com hra->recursive); 403410342Schris.kirby@sun.com } 403510342Schris.kirby@sun.com 403610342Schris.kirby@sun.com zfs_close(zhp); 403710342Schris.kirby@sun.com return (error); 403810342Schris.kirby@sun.com } 403910342Schris.kirby@sun.com 404010342Schris.kirby@sun.com /* 404110342Schris.kirby@sun.com * Add a user hold on the set of snapshots starting with fromsnap up to 404210342Schris.kirby@sun.com * and including tosnap. If we're unable to to acquire a particular hold, 404310342Schris.kirby@sun.com * undo any holds up to that point. 404410342Schris.kirby@sun.com */ 404510342Schris.kirby@sun.com int 404610342Schris.kirby@sun.com zfs_hold_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 404711814SChris.Kirby@sun.com const char *tag, boolean_t recursive, boolean_t temphold, 404811814SChris.Kirby@sun.com snapfilter_cb_t filter_cb, void *cbarg) 404910342Schris.kirby@sun.com { 405010342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 405110342Schris.kirby@sun.com int error; 405210342Schris.kirby@sun.com 405310342Schris.kirby@sun.com arg.origin = zhp; 405410342Schris.kirby@sun.com arg.fromsnap = fromsnap; 405510342Schris.kirby@sun.com arg.tosnap = tosnap; 405610342Schris.kirby@sun.com arg.tag = tag; 405710342Schris.kirby@sun.com arg.temphold = temphold; 405810342Schris.kirby@sun.com arg.holding = B_TRUE; 405911546SChris.Kirby@sun.com arg.recursive = recursive; 406011668SChris.Kirby@sun.com arg.seenfrom = (fromsnap == NULL); 406111814SChris.Kirby@sun.com arg.filter_cb = filter_cb; 406211814SChris.Kirby@sun.com arg.filter_cb_arg = cbarg; 406310342Schris.kirby@sun.com 406410342Schris.kirby@sun.com error = zfs_iter_snapshots_sorted(zhp, zfs_hold_range_one, &arg); 406510342Schris.kirby@sun.com 406610342Schris.kirby@sun.com /* 406710342Schris.kirby@sun.com * Make sure we either hold the entire range or none. 406810342Schris.kirby@sun.com */ 406910342Schris.kirby@sun.com if (error && arg.lastsnapheld[0] != '\0') { 407010342Schris.kirby@sun.com (void) zfs_release_range(zhp, fromsnap, 407111546SChris.Kirby@sun.com (const char *)arg.lastsnapheld, tag, recursive); 407210342Schris.kirby@sun.com } 407310342Schris.kirby@sun.com return (error); 407410342Schris.kirby@sun.com } 407510342Schris.kirby@sun.com 407610242Schris.kirby@sun.com int 407710242Schris.kirby@sun.com zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 407810242Schris.kirby@sun.com boolean_t recursive) 407910242Schris.kirby@sun.com { 408010242Schris.kirby@sun.com zfs_cmd_t zc = { 0 }; 408110242Schris.kirby@sun.com libzfs_handle_t *hdl = zhp->zfs_hdl; 408210242Schris.kirby@sun.com 408310242Schris.kirby@sun.com (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 408410242Schris.kirby@sun.com (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 408510342Schris.kirby@sun.com if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 408610342Schris.kirby@sun.com >= sizeof (zc.zc_string)) 408710342Schris.kirby@sun.com return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 408810242Schris.kirby@sun.com zc.zc_cookie = recursive; 408910242Schris.kirby@sun.com 409010242Schris.kirby@sun.com if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 409110242Schris.kirby@sun.com char errbuf[ZFS_MAXNAMELEN+32]; 409210242Schris.kirby@sun.com 409310242Schris.kirby@sun.com /* 409410242Schris.kirby@sun.com * if it was recursive, the one that actually failed will be in 409510242Schris.kirby@sun.com * zc.zc_name. 409610242Schris.kirby@sun.com */ 409710242Schris.kirby@sun.com (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 409811546SChris.Kirby@sun.com "cannot release '%s' from '%s@%s'"), tag, zc.zc_name, 409911546SChris.Kirby@sun.com snapname); 410010242Schris.kirby@sun.com switch (errno) { 410110242Schris.kirby@sun.com case ESRCH: 410210242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 410310242Schris.kirby@sun.com case ENOTSUP: 410410242Schris.kirby@sun.com zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 410510242Schris.kirby@sun.com "pool must be upgraded")); 410610242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 410710242Schris.kirby@sun.com case EINVAL: 410810242Schris.kirby@sun.com return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 410910242Schris.kirby@sun.com default: 411010242Schris.kirby@sun.com return (zfs_standard_error_fmt(hdl, errno, errbuf)); 411110242Schris.kirby@sun.com } 411210242Schris.kirby@sun.com } 411310242Schris.kirby@sun.com 411410242Schris.kirby@sun.com return (0); 411510242Schris.kirby@sun.com } 411610342Schris.kirby@sun.com 411710342Schris.kirby@sun.com /* 411810342Schris.kirby@sun.com * Release a user hold from the set of snapshots starting with fromsnap 411910342Schris.kirby@sun.com * up to and including tosnap. 412010342Schris.kirby@sun.com */ 412110342Schris.kirby@sun.com int 412210342Schris.kirby@sun.com zfs_release_range(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 412311546SChris.Kirby@sun.com const char *tag, boolean_t recursive) 412410342Schris.kirby@sun.com { 412510342Schris.kirby@sun.com struct hold_range_arg arg = { 0 }; 412610342Schris.kirby@sun.com 412710342Schris.kirby@sun.com arg.origin = zhp; 412810342Schris.kirby@sun.com arg.fromsnap = fromsnap; 412910342Schris.kirby@sun.com arg.tosnap = tosnap; 413010342Schris.kirby@sun.com arg.tag = tag; 413111546SChris.Kirby@sun.com arg.recursive = recursive; 413211668SChris.Kirby@sun.com arg.seenfrom = (fromsnap == NULL); 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