libzfs_dataset.c revision 321577
1168404Spjd/* 2168404Spjd * CDDL HEADER START 3168404Spjd * 4168404Spjd * The contents of this file are subject to the terms of the 5168404Spjd * Common Development and Distribution License (the "License"). 6168404Spjd * You may not use this file except in compliance with the License. 7168404Spjd * 8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9168404Spjd * or http://www.opensolaris.org/os/licensing. 10168404Spjd * See the License for the specific language governing permissions 11168404Spjd * and limitations under the License. 12168404Spjd * 13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each 14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15168404Spjd * If applicable, add the following below this CDDL HEADER, with the 16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18168404Spjd * 19168404Spjd * CDDL HEADER END 20168404Spjd */ 21168404Spjd 22168404Spjd/* 23219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24264835Sdelphij * Copyright (c) 2013, Joyent, Inc. All rights reserved. 25289500Smav * Copyright (c) 2011, 2015 by Delphix. All rights reserved. 26252219Sdelphij * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. 27307046Smav * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. 28307046Smav * Copyright (c) 2013 Martin Matuska. All rights reserved. 29251646Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved. 30296519Smav * Copyright (c) 2014 Integros [integros.com] 31307050Smav * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> 32307106Smav * Copyright 2016 Nexenta Systems, Inc. 33321577Smav * Copyright 2017 RackTop Systems. 34168404Spjd */ 35168404Spjd 36168404Spjd#include <ctype.h> 37168404Spjd#include <errno.h> 38168404Spjd#include <libintl.h> 39168404Spjd#include <math.h> 40168404Spjd#include <stdio.h> 41168404Spjd#include <stdlib.h> 42168404Spjd#include <strings.h> 43168404Spjd#include <unistd.h> 44185029Spjd#include <stddef.h> 45168404Spjd#include <zone.h> 46168404Spjd#include <fcntl.h> 47168404Spjd#include <sys/mntent.h> 48168404Spjd#include <sys/mount.h> 49185029Spjd#include <priv.h> 50185029Spjd#include <pwd.h> 51185029Spjd#include <grp.h> 52185029Spjd#include <stddef.h> 53209962Smm#include <idmap.h> 54168404Spjd 55219089Spjd#include <sys/dnode.h> 56168404Spjd#include <sys/spa.h> 57168404Spjd#include <sys/zap.h> 58209962Smm#include <sys/misc.h> 59168404Spjd#include <libzfs.h> 60168404Spjd 61168404Spjd#include "zfs_namecheck.h" 62168404Spjd#include "zfs_prop.h" 63168404Spjd#include "libzfs_impl.h" 64185029Spjd#include "zfs_deleg.h" 65168404Spjd 66209962Smmstatic int userquota_propname_decode(const char *propname, boolean_t zoned, 67209962Smm zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 68168676Spjd 69168404Spjd/* 70168404Spjd * Given a single type (not a mask of types), return the type in a human 71168404Spjd * readable form. 72168404Spjd */ 73168404Spjdconst char * 74168404Spjdzfs_type_to_name(zfs_type_t type) 75168404Spjd{ 76168404Spjd switch (type) { 77168404Spjd case ZFS_TYPE_FILESYSTEM: 78168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 79168404Spjd case ZFS_TYPE_SNAPSHOT: 80168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 81168404Spjd case ZFS_TYPE_VOLUME: 82168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 83307050Smav case ZFS_TYPE_POOL: 84307050Smav return (dgettext(TEXT_DOMAIN, "pool")); 85307050Smav case ZFS_TYPE_BOOKMARK: 86307050Smav return (dgettext(TEXT_DOMAIN, "bookmark")); 87307050Smav default: 88307050Smav assert(!"unhandled zfs_type_t"); 89168404Spjd } 90168404Spjd 91168404Spjd return (NULL); 92168404Spjd} 93168404Spjd 94168404Spjd/* 95168404Spjd * Validate a ZFS path. This is used even before trying to open the dataset, to 96209962Smm * provide a more meaningful error message. We call zfs_error_aux() to 97209962Smm * explain exactly why the name was not valid. 98168404Spjd */ 99219089Spjdint 100185029Spjdzfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 101185029Spjd boolean_t modifying) 102168404Spjd{ 103168404Spjd namecheck_err_t why; 104168404Spjd char what; 105168404Spjd 106219089Spjd (void) zfs_prop_get_table(); 107321534Smav if (entity_namecheck(path, &why, &what) != 0) { 108168404Spjd if (hdl != NULL) { 109168404Spjd switch (why) { 110168404Spjd case NAME_ERR_TOOLONG: 111168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 112168404Spjd "name is too long")); 113168404Spjd break; 114168404Spjd 115168404Spjd case NAME_ERR_LEADING_SLASH: 116168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 117168404Spjd "leading slash in name")); 118168404Spjd break; 119168404Spjd 120168404Spjd case NAME_ERR_EMPTY_COMPONENT: 121168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 122168404Spjd "empty component in name")); 123168404Spjd break; 124168404Spjd 125168404Spjd case NAME_ERR_TRAILING_SLASH: 126168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 127168404Spjd "trailing slash in name")); 128168404Spjd break; 129168404Spjd 130168404Spjd case NAME_ERR_INVALCHAR: 131168404Spjd zfs_error_aux(hdl, 132168404Spjd dgettext(TEXT_DOMAIN, "invalid character " 133168404Spjd "'%c' in name"), what); 134168404Spjd break; 135168404Spjd 136321534Smav case NAME_ERR_MULTIPLE_DELIMITERS: 137168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 138321534Smav "multiple '@' and/or '#' delimiters in " 139321534Smav "name")); 140168404Spjd break; 141168404Spjd 142168404Spjd case NAME_ERR_NOLETTER: 143168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 144168404Spjd "pool doesn't begin with a letter")); 145168404Spjd break; 146168404Spjd 147168404Spjd case NAME_ERR_RESERVED: 148168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 149168404Spjd "name is reserved")); 150168404Spjd break; 151168404Spjd 152168404Spjd case NAME_ERR_DISKLIKE: 153168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 154168404Spjd "reserved disk name")); 155168404Spjd break; 156307050Smav 157307050Smav default: 158307050Smav zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 159307050Smav "(%d) not defined"), why); 160307050Smav break; 161168404Spjd } 162168404Spjd } 163168404Spjd 164168404Spjd return (0); 165168404Spjd } 166168404Spjd 167168404Spjd if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 168168404Spjd if (hdl != NULL) 169168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 170321534Smav "snapshot delimiter '@' is not expected here")); 171168404Spjd return (0); 172168404Spjd } 173168404Spjd 174168404Spjd if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 175168404Spjd if (hdl != NULL) 176168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 177168404Spjd "missing '@' delimiter in snapshot name")); 178168404Spjd return (0); 179168404Spjd } 180168404Spjd 181321534Smav if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) { 182321534Smav if (hdl != NULL) 183321534Smav zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 184321534Smav "bookmark delimiter '#' is not expected here")); 185321534Smav return (0); 186321534Smav } 187321534Smav 188321534Smav if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) { 189321534Smav if (hdl != NULL) 190321534Smav zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 191321534Smav "missing '#' delimiter in bookmark name")); 192321534Smav return (0); 193321534Smav } 194321534Smav 195185029Spjd if (modifying && strchr(path, '%') != NULL) { 196185029Spjd if (hdl != NULL) 197185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 198185029Spjd "invalid character %c in name"), '%'); 199185029Spjd return (0); 200185029Spjd } 201185029Spjd 202168404Spjd return (-1); 203168404Spjd} 204168404Spjd 205168404Spjdint 206168404Spjdzfs_name_valid(const char *name, zfs_type_t type) 207168404Spjd{ 208185029Spjd if (type == ZFS_TYPE_POOL) 209185029Spjd return (zpool_name_valid(NULL, B_FALSE, name)); 210185029Spjd return (zfs_validate_name(NULL, name, type, B_FALSE)); 211168404Spjd} 212168404Spjd 213168404Spjd/* 214168404Spjd * This function takes the raw DSL properties, and filters out the user-defined 215168404Spjd * properties into a separate nvlist. 216168404Spjd */ 217185029Spjdstatic nvlist_t * 218185029Spjdprocess_user_props(zfs_handle_t *zhp, nvlist_t *props) 219168404Spjd{ 220168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 221168404Spjd nvpair_t *elem; 222168404Spjd nvlist_t *propval; 223185029Spjd nvlist_t *nvl; 224168404Spjd 225185029Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 226185029Spjd (void) no_memory(hdl); 227185029Spjd return (NULL); 228185029Spjd } 229168404Spjd 230168404Spjd elem = NULL; 231185029Spjd while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 232168404Spjd if (!zfs_prop_user(nvpair_name(elem))) 233168404Spjd continue; 234168404Spjd 235168404Spjd verify(nvpair_value_nvlist(elem, &propval) == 0); 236185029Spjd if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 237185029Spjd nvlist_free(nvl); 238185029Spjd (void) no_memory(hdl); 239185029Spjd return (NULL); 240185029Spjd } 241168404Spjd } 242168404Spjd 243185029Spjd return (nvl); 244168404Spjd} 245168404Spjd 246185029Spjdstatic zpool_handle_t * 247185029Spjdzpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 248185029Spjd{ 249185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 250185029Spjd zpool_handle_t *zph; 251185029Spjd 252185029Spjd if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 253185029Spjd if (hdl->libzfs_pool_handles != NULL) 254185029Spjd zph->zpool_next = hdl->libzfs_pool_handles; 255185029Spjd hdl->libzfs_pool_handles = zph; 256185029Spjd } 257185029Spjd return (zph); 258185029Spjd} 259185029Spjd 260185029Spjdstatic zpool_handle_t * 261185029Spjdzpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 262185029Spjd{ 263185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 264185029Spjd zpool_handle_t *zph = hdl->libzfs_pool_handles; 265185029Spjd 266185029Spjd while ((zph != NULL) && 267185029Spjd (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 268185029Spjd zph = zph->zpool_next; 269185029Spjd return (zph); 270185029Spjd} 271185029Spjd 272168404Spjd/* 273185029Spjd * Returns a handle to the pool that contains the provided dataset. 274185029Spjd * If a handle to that pool already exists then that handle is returned. 275185029Spjd * Otherwise, a new handle is created and added to the list of handles. 276185029Spjd */ 277185029Spjdstatic zpool_handle_t * 278185029Spjdzpool_handle(zfs_handle_t *zhp) 279185029Spjd{ 280185029Spjd char *pool_name; 281185029Spjd int len; 282185029Spjd zpool_handle_t *zph; 283185029Spjd 284260183Sdelphij len = strcspn(zhp->zfs_name, "/@#") + 1; 285185029Spjd pool_name = zfs_alloc(zhp->zfs_hdl, len); 286185029Spjd (void) strlcpy(pool_name, zhp->zfs_name, len); 287185029Spjd 288185029Spjd zph = zpool_find_handle(zhp, pool_name, len); 289185029Spjd if (zph == NULL) 290185029Spjd zph = zpool_add_handle(zhp, pool_name); 291185029Spjd 292185029Spjd free(pool_name); 293185029Spjd return (zph); 294185029Spjd} 295185029Spjd 296185029Spjdvoid 297185029Spjdzpool_free_handles(libzfs_handle_t *hdl) 298185029Spjd{ 299185029Spjd zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 300185029Spjd 301185029Spjd while (zph != NULL) { 302185029Spjd next = zph->zpool_next; 303185029Spjd zpool_close(zph); 304185029Spjd zph = next; 305185029Spjd } 306185029Spjd hdl->libzfs_pool_handles = NULL; 307185029Spjd} 308185029Spjd 309185029Spjd/* 310168404Spjd * Utility function to gather stats (objset and zpl) for the given object. 311168404Spjd */ 312219089Spjdstatic int 313209962Smmget_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 314168404Spjd{ 315168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 316168404Spjd 317209962Smm (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 318168404Spjd 319209962Smm while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 320168404Spjd if (errno == ENOMEM) { 321209962Smm if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 322168404Spjd return (-1); 323168404Spjd } 324168404Spjd } else { 325168404Spjd return (-1); 326168404Spjd } 327168404Spjd } 328209962Smm return (0); 329209962Smm} 330168404Spjd 331219089Spjd/* 332219089Spjd * Utility function to get the received properties of the given object. 333219089Spjd */ 334209962Smmstatic int 335219089Spjdget_recvd_props_ioctl(zfs_handle_t *zhp) 336219089Spjd{ 337219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 338219089Spjd nvlist_t *recvdprops; 339219089Spjd zfs_cmd_t zc = { 0 }; 340219089Spjd int err; 341219089Spjd 342219089Spjd if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 343219089Spjd return (-1); 344219089Spjd 345219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 346219089Spjd 347219089Spjd while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 348219089Spjd if (errno == ENOMEM) { 349219089Spjd if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 350219089Spjd return (-1); 351219089Spjd } 352219089Spjd } else { 353219089Spjd zcmd_free_nvlists(&zc); 354219089Spjd return (-1); 355219089Spjd } 356219089Spjd } 357219089Spjd 358219089Spjd err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 359219089Spjd zcmd_free_nvlists(&zc); 360219089Spjd if (err != 0) 361219089Spjd return (-1); 362219089Spjd 363219089Spjd nvlist_free(zhp->zfs_recvd_props); 364219089Spjd zhp->zfs_recvd_props = recvdprops; 365219089Spjd 366219089Spjd return (0); 367219089Spjd} 368219089Spjd 369219089Spjdstatic int 370209962Smmput_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 371209962Smm{ 372209962Smm nvlist_t *allprops, *userprops; 373168404Spjd 374209962Smm zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 375209962Smm 376209962Smm if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 377168404Spjd return (-1); 378168404Spjd } 379168404Spjd 380209962Smm /* 381209962Smm * XXX Why do we store the user props separately, in addition to 382209962Smm * storing them in zfs_props? 383209962Smm */ 384185029Spjd if ((userprops = process_user_props(zhp, allprops)) == NULL) { 385185029Spjd nvlist_free(allprops); 386168404Spjd return (-1); 387185029Spjd } 388168404Spjd 389185029Spjd nvlist_free(zhp->zfs_props); 390185029Spjd nvlist_free(zhp->zfs_user_props); 391185029Spjd 392185029Spjd zhp->zfs_props = allprops; 393185029Spjd zhp->zfs_user_props = userprops; 394185029Spjd 395168404Spjd return (0); 396168404Spjd} 397168404Spjd 398209962Smmstatic int 399209962Smmget_stats(zfs_handle_t *zhp) 400209962Smm{ 401209962Smm int rc = 0; 402209962Smm zfs_cmd_t zc = { 0 }; 403209962Smm 404209962Smm if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 405209962Smm return (-1); 406209962Smm if (get_stats_ioctl(zhp, &zc) != 0) 407209962Smm rc = -1; 408209962Smm else if (put_stats_zhdl(zhp, &zc) != 0) 409209962Smm rc = -1; 410209962Smm zcmd_free_nvlists(&zc); 411209962Smm return (rc); 412209962Smm} 413209962Smm 414168404Spjd/* 415168404Spjd * Refresh the properties currently stored in the handle. 416168404Spjd */ 417168404Spjdvoid 418168404Spjdzfs_refresh_properties(zfs_handle_t *zhp) 419168404Spjd{ 420168404Spjd (void) get_stats(zhp); 421168404Spjd} 422168404Spjd 423168404Spjd/* 424168404Spjd * Makes a handle from the given dataset name. Used by zfs_open() and 425168404Spjd * zfs_iter_* to create child handles on the fly. 426168404Spjd */ 427209962Smmstatic int 428209962Smmmake_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 429168404Spjd{ 430219089Spjd if (put_stats_zhdl(zhp, zc) != 0) 431209962Smm return (-1); 432168404Spjd 433168404Spjd /* 434168404Spjd * We've managed to open the dataset and gather statistics. Determine 435168404Spjd * the high-level type. 436168404Spjd */ 437168404Spjd if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 438168404Spjd zhp->zfs_head_type = ZFS_TYPE_VOLUME; 439168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 440168404Spjd zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 441168404Spjd else 442168404Spjd abort(); 443168404Spjd 444168404Spjd if (zhp->zfs_dmustats.dds_is_snapshot) 445168404Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 446168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 447168404Spjd zhp->zfs_type = ZFS_TYPE_VOLUME; 448168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 449168404Spjd zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 450168404Spjd else 451168404Spjd abort(); /* we should never see any other types */ 452168404Spjd 453219089Spjd if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 454219089Spjd return (-1); 455219089Spjd 456209962Smm return (0); 457209962Smm} 458209962Smm 459209962Smmzfs_handle_t * 460209962Smmmake_dataset_handle(libzfs_handle_t *hdl, const char *path) 461209962Smm{ 462209962Smm zfs_cmd_t zc = { 0 }; 463209962Smm 464209962Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 465209962Smm 466209962Smm if (zhp == NULL) 467209962Smm return (NULL); 468209962Smm 469209962Smm zhp->zfs_hdl = hdl; 470209962Smm (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 471209962Smm if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 472209962Smm free(zhp); 473209962Smm return (NULL); 474209962Smm } 475209962Smm if (get_stats_ioctl(zhp, &zc) == -1) { 476209962Smm zcmd_free_nvlists(&zc); 477209962Smm free(zhp); 478209962Smm return (NULL); 479209962Smm } 480209962Smm if (make_dataset_handle_common(zhp, &zc) == -1) { 481209962Smm free(zhp); 482209962Smm zhp = NULL; 483209962Smm } 484209962Smm zcmd_free_nvlists(&zc); 485168404Spjd return (zhp); 486168404Spjd} 487168404Spjd 488228103Smmzfs_handle_t * 489209962Smmmake_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 490209962Smm{ 491209962Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 492209962Smm 493209962Smm if (zhp == NULL) 494209962Smm return (NULL); 495209962Smm 496209962Smm zhp->zfs_hdl = hdl; 497209962Smm (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 498209962Smm if (make_dataset_handle_common(zhp, zc) == -1) { 499209962Smm free(zhp); 500209962Smm return (NULL); 501209962Smm } 502209962Smm return (zhp); 503209962Smm} 504209962Smm 505228103Smmzfs_handle_t * 506230438Spjdmake_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc) 507230438Spjd{ 508230438Spjd zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 509230438Spjd 510230438Spjd if (zhp == NULL) 511230438Spjd return (NULL); 512230438Spjd 513230438Spjd zhp->zfs_hdl = pzhp->zfs_hdl; 514230438Spjd (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 515230438Spjd zhp->zfs_head_type = pzhp->zfs_type; 516230438Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 517230438Spjd zhp->zpool_hdl = zpool_handle(zhp); 518230438Spjd return (zhp); 519230438Spjd} 520230438Spjd 521230438Spjdzfs_handle_t * 522228103Smmzfs_handle_dup(zfs_handle_t *zhp_orig) 523228103Smm{ 524228103Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 525228103Smm 526228103Smm if (zhp == NULL) 527228103Smm return (NULL); 528228103Smm 529228103Smm zhp->zfs_hdl = zhp_orig->zfs_hdl; 530228103Smm zhp->zpool_hdl = zhp_orig->zpool_hdl; 531228103Smm (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name, 532228103Smm sizeof (zhp->zfs_name)); 533228103Smm zhp->zfs_type = zhp_orig->zfs_type; 534228103Smm zhp->zfs_head_type = zhp_orig->zfs_head_type; 535228103Smm zhp->zfs_dmustats = zhp_orig->zfs_dmustats; 536228103Smm if (zhp_orig->zfs_props != NULL) { 537228103Smm if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) { 538228103Smm (void) no_memory(zhp->zfs_hdl); 539228103Smm zfs_close(zhp); 540228103Smm return (NULL); 541228103Smm } 542228103Smm } 543228103Smm if (zhp_orig->zfs_user_props != NULL) { 544228103Smm if (nvlist_dup(zhp_orig->zfs_user_props, 545228103Smm &zhp->zfs_user_props, 0) != 0) { 546228103Smm (void) no_memory(zhp->zfs_hdl); 547228103Smm zfs_close(zhp); 548228103Smm return (NULL); 549228103Smm } 550228103Smm } 551228103Smm if (zhp_orig->zfs_recvd_props != NULL) { 552228103Smm if (nvlist_dup(zhp_orig->zfs_recvd_props, 553228103Smm &zhp->zfs_recvd_props, 0)) { 554228103Smm (void) no_memory(zhp->zfs_hdl); 555228103Smm zfs_close(zhp); 556228103Smm return (NULL); 557228103Smm } 558228103Smm } 559228103Smm zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck; 560228103Smm if (zhp_orig->zfs_mntopts != NULL) { 561228103Smm zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl, 562228103Smm zhp_orig->zfs_mntopts); 563228103Smm } 564228103Smm zhp->zfs_props_table = zhp_orig->zfs_props_table; 565228103Smm return (zhp); 566228103Smm} 567228103Smm 568260183Sdelphijboolean_t 569260183Sdelphijzfs_bookmark_exists(const char *path) 570260183Sdelphij{ 571260183Sdelphij nvlist_t *bmarks; 572260183Sdelphij nvlist_t *props; 573307108Smav char fsname[ZFS_MAX_DATASET_NAME_LEN]; 574260183Sdelphij char *bmark_name; 575260183Sdelphij char *pound; 576260183Sdelphij int err; 577260183Sdelphij boolean_t rv; 578260183Sdelphij 579260183Sdelphij 580260183Sdelphij (void) strlcpy(fsname, path, sizeof (fsname)); 581260183Sdelphij pound = strchr(fsname, '#'); 582260183Sdelphij if (pound == NULL) 583260183Sdelphij return (B_FALSE); 584260183Sdelphij 585260183Sdelphij *pound = '\0'; 586260183Sdelphij bmark_name = pound + 1; 587260183Sdelphij props = fnvlist_alloc(); 588260183Sdelphij err = lzc_get_bookmarks(fsname, props, &bmarks); 589260183Sdelphij nvlist_free(props); 590260183Sdelphij if (err != 0) { 591260183Sdelphij nvlist_free(bmarks); 592260183Sdelphij return (B_FALSE); 593260183Sdelphij } 594260183Sdelphij 595260183Sdelphij rv = nvlist_exists(bmarks, bmark_name); 596260183Sdelphij nvlist_free(bmarks); 597260183Sdelphij return (rv); 598260183Sdelphij} 599260183Sdelphij 600260183Sdelphijzfs_handle_t * 601260183Sdelphijmake_bookmark_handle(zfs_handle_t *parent, const char *path, 602260183Sdelphij nvlist_t *bmark_props) 603260183Sdelphij{ 604260183Sdelphij zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 605260183Sdelphij 606260183Sdelphij if (zhp == NULL) 607260183Sdelphij return (NULL); 608260183Sdelphij 609260183Sdelphij /* Fill in the name. */ 610260183Sdelphij zhp->zfs_hdl = parent->zfs_hdl; 611260183Sdelphij (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 612260183Sdelphij 613260183Sdelphij /* Set the property lists. */ 614260183Sdelphij if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) { 615260183Sdelphij free(zhp); 616260183Sdelphij return (NULL); 617260183Sdelphij } 618260183Sdelphij 619260183Sdelphij /* Set the types. */ 620260183Sdelphij zhp->zfs_head_type = parent->zfs_head_type; 621260183Sdelphij zhp->zfs_type = ZFS_TYPE_BOOKMARK; 622260183Sdelphij 623260183Sdelphij if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) { 624260183Sdelphij nvlist_free(zhp->zfs_props); 625260183Sdelphij free(zhp); 626260183Sdelphij return (NULL); 627260183Sdelphij } 628260183Sdelphij 629260183Sdelphij return (zhp); 630260183Sdelphij} 631260183Sdelphij 632321534Smavstruct zfs_open_bookmarks_cb_data { 633321534Smav const char *path; 634321534Smav zfs_handle_t *zhp; 635321534Smav}; 636321534Smav 637321534Smavstatic int 638321534Smavzfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data) 639321534Smav{ 640321534Smav struct zfs_open_bookmarks_cb_data *dp = data; 641321534Smav 642321534Smav /* 643321534Smav * Is it the one we are looking for? 644321534Smav */ 645321534Smav if (strcmp(dp->path, zfs_get_name(zhp)) == 0) { 646321534Smav /* 647321534Smav * We found it. Save it and let the caller know we are done. 648321534Smav */ 649321534Smav dp->zhp = zhp; 650321534Smav return (EEXIST); 651321534Smav } 652321534Smav 653321534Smav /* 654321534Smav * Not found. Close the handle and ask for another one. 655321534Smav */ 656321534Smav zfs_close(zhp); 657321534Smav return (0); 658321534Smav} 659321534Smav 660168404Spjd/* 661321534Smav * Opens the given snapshot, bookmark, filesystem, or volume. The 'types' 662168404Spjd * argument is a mask of acceptable types. The function will print an 663168404Spjd * appropriate error message and return NULL if it can't be opened. 664168404Spjd */ 665168404Spjdzfs_handle_t * 666168404Spjdzfs_open(libzfs_handle_t *hdl, const char *path, int types) 667168404Spjd{ 668168404Spjd zfs_handle_t *zhp; 669168404Spjd char errbuf[1024]; 670321534Smav char *bookp; 671168404Spjd 672168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 673168404Spjd dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 674168404Spjd 675168404Spjd /* 676168404Spjd * Validate the name before we even try to open it. 677168404Spjd */ 678321534Smav if (!zfs_validate_name(hdl, path, types, B_FALSE)) { 679168404Spjd (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 680168404Spjd return (NULL); 681168404Spjd } 682168404Spjd 683168404Spjd /* 684321534Smav * Bookmarks needs to be handled separately. 685168404Spjd */ 686321534Smav bookp = strchr(path, '#'); 687321534Smav if (bookp == NULL) { 688321534Smav /* 689321534Smav * Try to get stats for the dataset, which will tell us if it 690321534Smav * exists. 691321534Smav */ 692321534Smav errno = 0; 693321534Smav if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 694321534Smav (void) zfs_standard_error(hdl, errno, errbuf); 695321534Smav return (NULL); 696321534Smav } 697321534Smav } else { 698321534Smav char dsname[ZFS_MAX_DATASET_NAME_LEN]; 699321534Smav zfs_handle_t *pzhp; 700321534Smav struct zfs_open_bookmarks_cb_data cb_data = {path, NULL}; 701321534Smav 702321534Smav /* 703321534Smav * We need to cut out '#' and everything after '#' 704321534Smav * to get the parent dataset name only. 705321534Smav */ 706321534Smav assert(bookp - path < sizeof (dsname)); 707321534Smav (void) strncpy(dsname, path, bookp - path); 708321534Smav dsname[bookp - path] = '\0'; 709321534Smav 710321534Smav /* 711321534Smav * Create handle for the parent dataset. 712321534Smav */ 713321534Smav errno = 0; 714321534Smav if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) { 715321534Smav (void) zfs_standard_error(hdl, errno, errbuf); 716321534Smav return (NULL); 717321534Smav } 718321534Smav 719321534Smav /* 720321534Smav * Iterate bookmarks to find the right one. 721321534Smav */ 722321534Smav errno = 0; 723321534Smav if ((zfs_iter_bookmarks(pzhp, zfs_open_bookmarks_cb, 724321534Smav &cb_data) == 0) && (cb_data.zhp == NULL)) { 725321534Smav (void) zfs_error(hdl, EZFS_NOENT, errbuf); 726321534Smav zfs_close(pzhp); 727321534Smav return (NULL); 728321534Smav } 729321534Smav if (cb_data.zhp == NULL) { 730321534Smav (void) zfs_standard_error(hdl, errno, errbuf); 731321534Smav zfs_close(pzhp); 732321534Smav return (NULL); 733321534Smav } 734321534Smav zhp = cb_data.zhp; 735321534Smav 736321534Smav /* 737321534Smav * Cleanup. 738321534Smav */ 739321534Smav zfs_close(pzhp); 740168404Spjd } 741168404Spjd 742240870Spjd if (zhp == NULL) { 743240870Spjd char *at = strchr(path, '@'); 744240870Spjd 745240870Spjd if (at != NULL) 746240870Spjd *at = '\0'; 747240870Spjd errno = 0; 748240870Spjd if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 749240870Spjd (void) zfs_standard_error(hdl, errno, errbuf); 750240870Spjd return (NULL); 751240870Spjd } 752240870Spjd if (at != NULL) 753240870Spjd *at = '@'; 754240870Spjd (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 755240870Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 756240870Spjd } 757240870Spjd 758168404Spjd if (!(types & zhp->zfs_type)) { 759168404Spjd (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 760168404Spjd zfs_close(zhp); 761168404Spjd return (NULL); 762168404Spjd } 763168404Spjd 764168404Spjd return (zhp); 765168404Spjd} 766168404Spjd 767168404Spjd/* 768168404Spjd * Release a ZFS handle. Nothing to do but free the associated memory. 769168404Spjd */ 770168404Spjdvoid 771168404Spjdzfs_close(zfs_handle_t *zhp) 772168404Spjd{ 773168404Spjd if (zhp->zfs_mntopts) 774168404Spjd free(zhp->zfs_mntopts); 775168404Spjd nvlist_free(zhp->zfs_props); 776168404Spjd nvlist_free(zhp->zfs_user_props); 777219089Spjd nvlist_free(zhp->zfs_recvd_props); 778168404Spjd free(zhp); 779168404Spjd} 780168404Spjd 781209962Smmtypedef struct mnttab_node { 782209962Smm struct mnttab mtn_mt; 783209962Smm avl_node_t mtn_node; 784209962Smm} mnttab_node_t; 785209962Smm 786209962Smmstatic int 787209962Smmlibzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 788209962Smm{ 789209962Smm const mnttab_node_t *mtn1 = arg1; 790209962Smm const mnttab_node_t *mtn2 = arg2; 791209962Smm int rv; 792209962Smm 793209962Smm rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 794209962Smm 795209962Smm if (rv == 0) 796209962Smm return (0); 797209962Smm return (rv > 0 ? 1 : -1); 798209962Smm} 799209962Smm 800209962Smmvoid 801209962Smmlibzfs_mnttab_init(libzfs_handle_t *hdl) 802209962Smm{ 803209962Smm assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 804209962Smm avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 805209962Smm sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 806209962Smm} 807209962Smm 808209962Smmvoid 809209962Smmlibzfs_mnttab_update(libzfs_handle_t *hdl) 810209962Smm{ 811209962Smm struct mnttab entry; 812209962Smm 813209962Smm rewind(hdl->libzfs_mnttab); 814209962Smm while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 815209962Smm mnttab_node_t *mtn; 816209962Smm 817209962Smm if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 818209962Smm continue; 819209962Smm mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 820209962Smm mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 821209962Smm mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 822209962Smm mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 823209962Smm mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 824209962Smm avl_add(&hdl->libzfs_mnttab_cache, mtn); 825209962Smm } 826209962Smm} 827209962Smm 828209962Smmvoid 829209962Smmlibzfs_mnttab_fini(libzfs_handle_t *hdl) 830209962Smm{ 831209962Smm void *cookie = NULL; 832209962Smm mnttab_node_t *mtn; 833209962Smm 834307050Smav while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) 835307050Smav != NULL) { 836209962Smm free(mtn->mtn_mt.mnt_special); 837209962Smm free(mtn->mtn_mt.mnt_mountp); 838209962Smm free(mtn->mtn_mt.mnt_fstype); 839209962Smm free(mtn->mtn_mt.mnt_mntopts); 840209962Smm free(mtn); 841209962Smm } 842209962Smm avl_destroy(&hdl->libzfs_mnttab_cache); 843209962Smm} 844209962Smm 845209962Smmvoid 846209962Smmlibzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 847209962Smm{ 848209962Smm hdl->libzfs_mnttab_enable = enable; 849209962Smm} 850209962Smm 851185029Spjdint 852209962Smmlibzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 853209962Smm struct mnttab *entry) 854209962Smm{ 855209962Smm mnttab_node_t find; 856209962Smm mnttab_node_t *mtn; 857209962Smm 858209962Smm if (!hdl->libzfs_mnttab_enable) { 859209962Smm struct mnttab srch = { 0 }; 860209962Smm 861209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 862209962Smm libzfs_mnttab_fini(hdl); 863209962Smm rewind(hdl->libzfs_mnttab); 864209962Smm srch.mnt_special = (char *)fsname; 865209962Smm srch.mnt_fstype = MNTTYPE_ZFS; 866209962Smm if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 867209962Smm return (0); 868209962Smm else 869209962Smm return (ENOENT); 870209962Smm } 871209962Smm 872209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 873209962Smm libzfs_mnttab_update(hdl); 874209962Smm 875209962Smm find.mtn_mt.mnt_special = (char *)fsname; 876209962Smm mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 877209962Smm if (mtn) { 878209962Smm *entry = mtn->mtn_mt; 879209962Smm return (0); 880209962Smm } 881209962Smm return (ENOENT); 882209962Smm} 883209962Smm 884209962Smmvoid 885209962Smmlibzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 886209962Smm const char *mountp, const char *mntopts) 887209962Smm{ 888209962Smm mnttab_node_t *mtn; 889209962Smm 890209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 891209962Smm return; 892209962Smm mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 893209962Smm mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 894209962Smm mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 895209962Smm mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 896209962Smm mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 897209962Smm avl_add(&hdl->libzfs_mnttab_cache, mtn); 898209962Smm} 899209962Smm 900209962Smmvoid 901209962Smmlibzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 902209962Smm{ 903209962Smm mnttab_node_t find; 904209962Smm mnttab_node_t *ret; 905209962Smm 906209962Smm find.mtn_mt.mnt_special = (char *)fsname; 907307050Smav if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) 908307050Smav != NULL) { 909209962Smm avl_remove(&hdl->libzfs_mnttab_cache, ret); 910209962Smm free(ret->mtn_mt.mnt_special); 911209962Smm free(ret->mtn_mt.mnt_mountp); 912209962Smm free(ret->mtn_mt.mnt_fstype); 913209962Smm free(ret->mtn_mt.mnt_mntopts); 914209962Smm free(ret); 915209962Smm } 916209962Smm} 917209962Smm 918209962Smmint 919185029Spjdzfs_spa_version(zfs_handle_t *zhp, int *spa_version) 920168404Spjd{ 921185029Spjd zpool_handle_t *zpool_handle = zhp->zpool_hdl; 922168404Spjd 923185029Spjd if (zpool_handle == NULL) 924168404Spjd return (-1); 925168404Spjd 926185029Spjd *spa_version = zpool_get_prop_int(zpool_handle, 927185029Spjd ZPOOL_PROP_VERSION, NULL); 928168404Spjd return (0); 929168404Spjd} 930168404Spjd 931168404Spjd/* 932185029Spjd * The choice of reservation property depends on the SPA version. 933168404Spjd */ 934168404Spjdstatic int 935185029Spjdzfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 936168404Spjd{ 937185029Spjd int spa_version; 938168404Spjd 939185029Spjd if (zfs_spa_version(zhp, &spa_version) < 0) 940168404Spjd return (-1); 941168404Spjd 942185029Spjd if (spa_version >= SPA_VERSION_REFRESERVATION) 943185029Spjd *resv_prop = ZFS_PROP_REFRESERVATION; 944185029Spjd else 945185029Spjd *resv_prop = ZFS_PROP_RESERVATION; 946168404Spjd 947168404Spjd return (0); 948168404Spjd} 949168404Spjd 950168404Spjd/* 951168404Spjd * Given an nvlist of properties to set, validates that they are correct, and 952168404Spjd * parses any numeric properties (index, boolean, etc) if they are specified as 953168404Spjd * strings. 954168404Spjd */ 955168404Spjdnvlist_t * 956185029Spjdzfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 957289500Smav uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl, 958289500Smav const char *errbuf) 959168404Spjd{ 960168404Spjd nvpair_t *elem; 961168404Spjd uint64_t intval; 962168404Spjd char *strval; 963185029Spjd zfs_prop_t prop; 964168404Spjd nvlist_t *ret; 965185029Spjd int chosen_normal = -1; 966185029Spjd int chosen_utf = -1; 967168404Spjd 968168404Spjd if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 969168404Spjd (void) no_memory(hdl); 970168404Spjd return (NULL); 971168404Spjd } 972168404Spjd 973209962Smm /* 974209962Smm * Make sure this property is valid and applies to this type. 975209962Smm */ 976209962Smm 977168404Spjd elem = NULL; 978168404Spjd while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 979185029Spjd const char *propname = nvpair_name(elem); 980168404Spjd 981209962Smm prop = zfs_name_to_prop(propname); 982209962Smm if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 983185029Spjd /* 984209962Smm * This is a user property: make sure it's a 985185029Spjd * string, and that it's less than ZAP_MAXNAMELEN. 986185029Spjd */ 987185029Spjd if (nvpair_type(elem) != DATA_TYPE_STRING) { 988185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 989185029Spjd "'%s' must be a string"), propname); 990185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 991185029Spjd goto error; 992168404Spjd } 993168404Spjd 994185029Spjd if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 995185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 996185029Spjd "property name '%s' is too long"), 997185029Spjd propname); 998185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 999185029Spjd goto error; 1000185029Spjd } 1001185029Spjd 1002168404Spjd (void) nvpair_value_string(elem, &strval); 1003168404Spjd if (nvlist_add_string(ret, propname, strval) != 0) { 1004168404Spjd (void) no_memory(hdl); 1005168404Spjd goto error; 1006168404Spjd } 1007168404Spjd continue; 1008168404Spjd } 1009168404Spjd 1010209962Smm /* 1011209962Smm * Currently, only user properties can be modified on 1012209962Smm * snapshots. 1013209962Smm */ 1014185029Spjd if (type == ZFS_TYPE_SNAPSHOT) { 1015185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1016185029Spjd "this property can not be modified for snapshots")); 1017185029Spjd (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 1018185029Spjd goto error; 1019185029Spjd } 1020168404Spjd 1021209962Smm if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 1022209962Smm zfs_userquota_prop_t uqtype; 1023209962Smm char newpropname[128]; 1024209962Smm char domain[128]; 1025209962Smm uint64_t rid; 1026209962Smm uint64_t valary[3]; 1027209962Smm 1028209962Smm if (userquota_propname_decode(propname, zoned, 1029209962Smm &uqtype, domain, sizeof (domain), &rid) != 0) { 1030209962Smm zfs_error_aux(hdl, 1031209962Smm dgettext(TEXT_DOMAIN, 1032209962Smm "'%s' has an invalid user/group name"), 1033209962Smm propname); 1034209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1035209962Smm goto error; 1036209962Smm } 1037209962Smm 1038209962Smm if (uqtype != ZFS_PROP_USERQUOTA && 1039209962Smm uqtype != ZFS_PROP_GROUPQUOTA) { 1040209962Smm zfs_error_aux(hdl, 1041209962Smm dgettext(TEXT_DOMAIN, "'%s' is readonly"), 1042209962Smm propname); 1043209962Smm (void) zfs_error(hdl, EZFS_PROPREADONLY, 1044209962Smm errbuf); 1045209962Smm goto error; 1046209962Smm } 1047209962Smm 1048209962Smm if (nvpair_type(elem) == DATA_TYPE_STRING) { 1049209962Smm (void) nvpair_value_string(elem, &strval); 1050209962Smm if (strcmp(strval, "none") == 0) { 1051209962Smm intval = 0; 1052209962Smm } else if (zfs_nicestrtonum(hdl, 1053209962Smm strval, &intval) != 0) { 1054209962Smm (void) zfs_error(hdl, 1055209962Smm EZFS_BADPROP, errbuf); 1056209962Smm goto error; 1057209962Smm } 1058209962Smm } else if (nvpair_type(elem) == 1059209962Smm DATA_TYPE_UINT64) { 1060209962Smm (void) nvpair_value_uint64(elem, &intval); 1061209962Smm if (intval == 0) { 1062209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1063209962Smm "use 'none' to disable " 1064209962Smm "userquota/groupquota")); 1065209962Smm goto error; 1066209962Smm } 1067209962Smm } else { 1068209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1069209962Smm "'%s' must be a number"), propname); 1070209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1071209962Smm goto error; 1072209962Smm } 1073209962Smm 1074219089Spjd /* 1075219089Spjd * Encode the prop name as 1076219089Spjd * userquota@<hex-rid>-domain, to make it easy 1077219089Spjd * for the kernel to decode. 1078219089Spjd */ 1079209962Smm (void) snprintf(newpropname, sizeof (newpropname), 1080219089Spjd "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 1081219089Spjd (longlong_t)rid, domain); 1082209962Smm valary[0] = uqtype; 1083209962Smm valary[1] = rid; 1084209962Smm valary[2] = intval; 1085209962Smm if (nvlist_add_uint64_array(ret, newpropname, 1086209962Smm valary, 3) != 0) { 1087209962Smm (void) no_memory(hdl); 1088209962Smm goto error; 1089209962Smm } 1090209962Smm continue; 1091228103Smm } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) { 1092228103Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1093228103Smm "'%s' is readonly"), 1094228103Smm propname); 1095228103Smm (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 1096228103Smm goto error; 1097209962Smm } 1098209962Smm 1099209962Smm if (prop == ZPROP_INVAL) { 1100209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1101209962Smm "invalid property '%s'"), propname); 1102209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1103209962Smm goto error; 1104209962Smm } 1105209962Smm 1106168404Spjd if (!zfs_prop_valid_for_type(prop, type)) { 1107168404Spjd zfs_error_aux(hdl, 1108168404Spjd dgettext(TEXT_DOMAIN, "'%s' does not " 1109168404Spjd "apply to datasets of this type"), propname); 1110168404Spjd (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 1111168404Spjd goto error; 1112168404Spjd } 1113168404Spjd 1114168404Spjd if (zfs_prop_readonly(prop) && 1115185029Spjd (!zfs_prop_setonce(prop) || zhp != NULL)) { 1116168404Spjd zfs_error_aux(hdl, 1117168404Spjd dgettext(TEXT_DOMAIN, "'%s' is readonly"), 1118168404Spjd propname); 1119168404Spjd (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 1120168404Spjd goto error; 1121168404Spjd } 1122168404Spjd 1123185029Spjd if (zprop_parse_value(hdl, elem, prop, type, ret, 1124185029Spjd &strval, &intval, errbuf) != 0) 1125185029Spjd goto error; 1126185029Spjd 1127168404Spjd /* 1128185029Spjd * Perform some additional checks for specific properties. 1129168404Spjd */ 1130185029Spjd switch (prop) { 1131185029Spjd case ZFS_PROP_VERSION: 1132185029Spjd { 1133185029Spjd int version; 1134168404Spjd 1135185029Spjd if (zhp == NULL) 1136185029Spjd break; 1137185029Spjd version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 1138185029Spjd if (intval < version) { 1139168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1140185029Spjd "Can not downgrade; already at version %u"), 1141185029Spjd version); 1142168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1143168404Spjd goto error; 1144168404Spjd } 1145168404Spjd break; 1146168404Spjd } 1147168404Spjd 1148274337Sdelphij case ZFS_PROP_VOLBLOCKSIZE: 1149168404Spjd case ZFS_PROP_RECORDSIZE: 1150274337Sdelphij { 1151274337Sdelphij int maxbs = SPA_MAXBLOCKSIZE; 1152289500Smav if (zpool_hdl != NULL) { 1153289500Smav maxbs = zpool_get_prop_int(zpool_hdl, 1154274337Sdelphij ZPOOL_PROP_MAXBLOCKSIZE, NULL); 1155274337Sdelphij } 1156274337Sdelphij /* 1157274337Sdelphij * Volumes are limited to a volblocksize of 128KB, 1158274337Sdelphij * because they typically service workloads with 1159274337Sdelphij * small random writes, which incur a large performance 1160274337Sdelphij * penalty with large blocks. 1161274337Sdelphij */ 1162274337Sdelphij if (prop == ZFS_PROP_VOLBLOCKSIZE) 1163274337Sdelphij maxbs = SPA_OLD_MAXBLOCKSIZE; 1164274337Sdelphij /* 1165274337Sdelphij * The value must be a power of two between 1166274337Sdelphij * SPA_MINBLOCKSIZE and maxbs. 1167274337Sdelphij */ 1168168404Spjd if (intval < SPA_MINBLOCKSIZE || 1169274337Sdelphij intval > maxbs || !ISP2(intval)) { 1170168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1171274337Sdelphij "'%s' must be power of 2 from 512B " 1172274337Sdelphij "to %uKB"), propname, maxbs >> 10); 1173168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1174168404Spjd goto error; 1175168404Spjd } 1176168404Spjd break; 1177274337Sdelphij } 1178219089Spjd case ZFS_PROP_MLSLABEL: 1179219089Spjd { 1180277300Ssmh#ifdef illumos 1181219089Spjd /* 1182219089Spjd * Verify the mlslabel string and convert to 1183219089Spjd * internal hex label string. 1184219089Spjd */ 1185219089Spjd 1186219089Spjd m_label_t *new_sl; 1187219089Spjd char *hex = NULL; /* internal label string */ 1188219089Spjd 1189219089Spjd /* Default value is already OK. */ 1190219089Spjd if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 1191219089Spjd break; 1192219089Spjd 1193219089Spjd /* Verify the label can be converted to binary form */ 1194219089Spjd if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 1195219089Spjd (str_to_label(strval, &new_sl, MAC_LABEL, 1196219089Spjd L_NO_CORRECTION, NULL) == -1)) { 1197219089Spjd goto badlabel; 1198168404Spjd } 1199168404Spjd 1200219089Spjd /* Now translate to hex internal label string */ 1201219089Spjd if (label_to_str(new_sl, &hex, M_INTERNAL, 1202219089Spjd DEF_NAMES) != 0) { 1203219089Spjd if (hex) 1204219089Spjd free(hex); 1205219089Spjd goto badlabel; 1206219089Spjd } 1207219089Spjd m_label_free(new_sl); 1208219089Spjd 1209219089Spjd /* If string is already in internal form, we're done. */ 1210219089Spjd if (strcmp(strval, hex) == 0) { 1211219089Spjd free(hex); 1212219089Spjd break; 1213219089Spjd } 1214219089Spjd 1215219089Spjd /* Replace the label string with the internal form. */ 1216219089Spjd (void) nvlist_remove(ret, zfs_prop_to_name(prop), 1217219089Spjd DATA_TYPE_STRING); 1218219089Spjd verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 1219219089Spjd hex) == 0); 1220219089Spjd free(hex); 1221219089Spjd 1222168404Spjd break; 1223168404Spjd 1224219089Spjdbadlabel: 1225219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1226219089Spjd "invalid mlslabel '%s'"), strval); 1227219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1228219089Spjd m_label_free(new_sl); /* OK if null */ 1229277300Ssmh#else /* !illumos */ 1230219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1231219089Spjd "mlslabel is not supported on FreeBSD")); 1232219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1233277300Ssmh#endif /* illumos */ 1234219089Spjd goto error; 1235219089Spjd 1236219089Spjd } 1237219089Spjd 1238168404Spjd case ZFS_PROP_MOUNTPOINT: 1239185029Spjd { 1240185029Spjd namecheck_err_t why; 1241185029Spjd 1242168404Spjd if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1243168404Spjd strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1244168404Spjd break; 1245168404Spjd 1246185029Spjd if (mountpoint_namecheck(strval, &why)) { 1247185029Spjd switch (why) { 1248185029Spjd case NAME_ERR_LEADING_SLASH: 1249185029Spjd zfs_error_aux(hdl, 1250185029Spjd dgettext(TEXT_DOMAIN, 1251185029Spjd "'%s' must be an absolute path, " 1252185029Spjd "'none', or 'legacy'"), propname); 1253185029Spjd break; 1254185029Spjd case NAME_ERR_TOOLONG: 1255185029Spjd zfs_error_aux(hdl, 1256185029Spjd dgettext(TEXT_DOMAIN, 1257185029Spjd "component of '%s' is too long"), 1258185029Spjd propname); 1259185029Spjd break; 1260307050Smav 1261307050Smav default: 1262307050Smav zfs_error_aux(hdl, 1263307050Smav dgettext(TEXT_DOMAIN, 1264307050Smav "(%d) not defined"), 1265307050Smav why); 1266307050Smav break; 1267185029Spjd } 1268168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1269168404Spjd goto error; 1270168404Spjd } 1271185029Spjd } 1272185029Spjd 1273168404Spjd /*FALLTHRU*/ 1274168404Spjd 1275185029Spjd case ZFS_PROP_SHARESMB: 1276168404Spjd case ZFS_PROP_SHARENFS: 1277168404Spjd /* 1278185029Spjd * For the mountpoint and sharenfs or sharesmb 1279185029Spjd * properties, check if it can be set in a 1280185029Spjd * global/non-global zone based on 1281168404Spjd * the zoned property value: 1282168404Spjd * 1283168404Spjd * global zone non-global zone 1284168404Spjd * -------------------------------------------------- 1285168404Spjd * zoned=on mountpoint (no) mountpoint (yes) 1286168404Spjd * sharenfs (no) sharenfs (no) 1287185029Spjd * sharesmb (no) sharesmb (no) 1288168404Spjd * 1289168404Spjd * zoned=off mountpoint (yes) N/A 1290168404Spjd * sharenfs (yes) 1291185029Spjd * sharesmb (yes) 1292168404Spjd */ 1293168404Spjd if (zoned) { 1294168404Spjd if (getzoneid() == GLOBAL_ZONEID) { 1295168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1296168404Spjd "'%s' cannot be set on " 1297168404Spjd "dataset in a non-global zone"), 1298168404Spjd propname); 1299168404Spjd (void) zfs_error(hdl, EZFS_ZONED, 1300168404Spjd errbuf); 1301168404Spjd goto error; 1302185029Spjd } else if (prop == ZFS_PROP_SHARENFS || 1303185029Spjd prop == ZFS_PROP_SHARESMB) { 1304168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1305168404Spjd "'%s' cannot be set in " 1306168404Spjd "a non-global zone"), propname); 1307168404Spjd (void) zfs_error(hdl, EZFS_ZONED, 1308168404Spjd errbuf); 1309168404Spjd goto error; 1310168404Spjd } 1311168404Spjd } else if (getzoneid() != GLOBAL_ZONEID) { 1312168404Spjd /* 1313168404Spjd * If zoned property is 'off', this must be in 1314209962Smm * a global zone. If not, something is wrong. 1315168404Spjd */ 1316168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1317168404Spjd "'%s' cannot be set while dataset " 1318168404Spjd "'zoned' property is set"), propname); 1319168404Spjd (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1320168404Spjd goto error; 1321168404Spjd } 1322168404Spjd 1323168404Spjd /* 1324185029Spjd * At this point, it is legitimate to set the 1325185029Spjd * property. Now we want to make sure that the 1326185029Spjd * property value is valid if it is sharenfs. 1327168404Spjd */ 1328185029Spjd if ((prop == ZFS_PROP_SHARENFS || 1329185029Spjd prop == ZFS_PROP_SHARESMB) && 1330185029Spjd strcmp(strval, "on") != 0 && 1331185029Spjd strcmp(strval, "off") != 0) { 1332185029Spjd zfs_share_proto_t proto; 1333168404Spjd 1334185029Spjd if (prop == ZFS_PROP_SHARESMB) 1335185029Spjd proto = PROTO_SMB; 1336185029Spjd else 1337185029Spjd proto = PROTO_NFS; 1338185029Spjd 1339185029Spjd /* 1340185029Spjd * Must be an valid sharing protocol 1341185029Spjd * option string so init the libshare 1342185029Spjd * in order to enable the parser and 1343185029Spjd * then parse the options. We use the 1344185029Spjd * control API since we don't care about 1345185029Spjd * the current configuration and don't 1346185029Spjd * want the overhead of loading it 1347185029Spjd * until we actually do something. 1348185029Spjd */ 1349185029Spjd 1350185029Spjd if (zfs_init_libshare(hdl, 1351185029Spjd SA_INIT_CONTROL_API) != SA_OK) { 1352185029Spjd /* 1353185029Spjd * An error occurred so we can't do 1354185029Spjd * anything 1355185029Spjd */ 1356185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1357185029Spjd "'%s' cannot be set: problem " 1358185029Spjd "in share initialization"), 1359185029Spjd propname); 1360185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1361185029Spjd errbuf); 1362185029Spjd goto error; 1363185029Spjd } 1364185029Spjd 1365185029Spjd if (zfs_parse_options(strval, proto) != SA_OK) { 1366185029Spjd /* 1367185029Spjd * There was an error in parsing so 1368185029Spjd * deal with it by issuing an error 1369185029Spjd * message and leaving after 1370185029Spjd * uninitializing the the libshare 1371185029Spjd * interface. 1372185029Spjd */ 1373185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1374185029Spjd "'%s' cannot be set to invalid " 1375185029Spjd "options"), propname); 1376185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1377185029Spjd errbuf); 1378185029Spjd zfs_uninit_libshare(hdl); 1379185029Spjd goto error; 1380185029Spjd } 1381185029Spjd zfs_uninit_libshare(hdl); 1382168404Spjd } 1383185029Spjd 1384168404Spjd break; 1385307050Smav 1386185029Spjd case ZFS_PROP_UTF8ONLY: 1387185029Spjd chosen_utf = (int)intval; 1388185029Spjd break; 1389307050Smav 1390185029Spjd case ZFS_PROP_NORMALIZE: 1391185029Spjd chosen_normal = (int)intval; 1392185029Spjd break; 1393307050Smav 1394307050Smav default: 1395307050Smav break; 1396168404Spjd } 1397168404Spjd 1398168404Spjd /* 1399168404Spjd * For changes to existing volumes, we have some additional 1400168404Spjd * checks to enforce. 1401168404Spjd */ 1402168404Spjd if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1403168404Spjd uint64_t volsize = zfs_prop_get_int(zhp, 1404168404Spjd ZFS_PROP_VOLSIZE); 1405168404Spjd uint64_t blocksize = zfs_prop_get_int(zhp, 1406168404Spjd ZFS_PROP_VOLBLOCKSIZE); 1407168404Spjd char buf[64]; 1408168404Spjd 1409168404Spjd switch (prop) { 1410168404Spjd case ZFS_PROP_RESERVATION: 1411185029Spjd case ZFS_PROP_REFRESERVATION: 1412168404Spjd if (intval > volsize) { 1413168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1414168404Spjd "'%s' is greater than current " 1415168404Spjd "volume size"), propname); 1416168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1417168404Spjd errbuf); 1418168404Spjd goto error; 1419168404Spjd } 1420168404Spjd break; 1421168404Spjd 1422168404Spjd case ZFS_PROP_VOLSIZE: 1423168404Spjd if (intval % blocksize != 0) { 1424168404Spjd zfs_nicenum(blocksize, buf, 1425168404Spjd sizeof (buf)); 1426168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1427168404Spjd "'%s' must be a multiple of " 1428168404Spjd "volume block size (%s)"), 1429168404Spjd propname, buf); 1430168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1431168404Spjd errbuf); 1432168404Spjd goto error; 1433168404Spjd } 1434168404Spjd 1435168404Spjd if (intval == 0) { 1436168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1437168404Spjd "'%s' cannot be zero"), 1438168404Spjd propname); 1439168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1440168404Spjd errbuf); 1441168404Spjd goto error; 1442168404Spjd } 1443168404Spjd break; 1444307050Smav 1445307050Smav default: 1446307050Smav break; 1447168404Spjd } 1448168404Spjd } 1449168404Spjd } 1450168404Spjd 1451168404Spjd /* 1452185029Spjd * If normalization was chosen, but no UTF8 choice was made, 1453185029Spjd * enforce rejection of non-UTF8 names. 1454185029Spjd * 1455185029Spjd * If normalization was chosen, but rejecting non-UTF8 names 1456185029Spjd * was explicitly not chosen, it is an error. 1457185029Spjd */ 1458185029Spjd if (chosen_normal > 0 && chosen_utf < 0) { 1459185029Spjd if (nvlist_add_uint64(ret, 1460185029Spjd zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1461185029Spjd (void) no_memory(hdl); 1462185029Spjd goto error; 1463185029Spjd } 1464185029Spjd } else if (chosen_normal > 0 && chosen_utf == 0) { 1465185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1466185029Spjd "'%s' must be set 'on' if normalization chosen"), 1467185029Spjd zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1468185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1469185029Spjd goto error; 1470185029Spjd } 1471219089Spjd return (ret); 1472185029Spjd 1473219089Spjderror: 1474219089Spjd nvlist_free(ret); 1475219089Spjd return (NULL); 1476219089Spjd} 1477219089Spjd 1478219089Spjdint 1479219089Spjdzfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) 1480219089Spjd{ 1481219089Spjd uint64_t old_volsize; 1482219089Spjd uint64_t new_volsize; 1483219089Spjd uint64_t old_reservation; 1484219089Spjd uint64_t new_reservation; 1485219089Spjd zfs_prop_t resv_prop; 1486289499Smav nvlist_t *props; 1487219089Spjd 1488185029Spjd /* 1489168404Spjd * If this is an existing volume, and someone is setting the volsize, 1490168404Spjd * make sure that it matches the reservation, or add it if necessary. 1491168404Spjd */ 1492219089Spjd old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1493219089Spjd if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1494219089Spjd return (-1); 1495219089Spjd old_reservation = zfs_prop_get_int(zhp, resv_prop); 1496289499Smav 1497289499Smav props = fnvlist_alloc(); 1498289499Smav fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 1499289499Smav zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE)); 1500289499Smav 1501289499Smav if ((zvol_volsize_to_reservation(old_volsize, props) != 1502289499Smav old_reservation) || nvlist_exists(nvl, 1503289499Smav zfs_prop_to_name(resv_prop))) { 1504289499Smav fnvlist_free(props); 1505219089Spjd return (0); 1506219089Spjd } 1507219089Spjd if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1508289499Smav &new_volsize) != 0) { 1509289499Smav fnvlist_free(props); 1510219089Spjd return (-1); 1511289499Smav } 1512289499Smav new_reservation = zvol_volsize_to_reservation(new_volsize, props); 1513289499Smav fnvlist_free(props); 1514289499Smav 1515219089Spjd if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), 1516219089Spjd new_reservation) != 0) { 1517219089Spjd (void) no_memory(zhp->zfs_hdl); 1518219089Spjd return (-1); 1519219089Spjd } 1520219089Spjd return (1); 1521219089Spjd} 1522168404Spjd 1523219089Spjdvoid 1524219089Spjdzfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 1525219089Spjd char *errbuf) 1526219089Spjd{ 1527219089Spjd switch (err) { 1528185029Spjd 1529219089Spjd case ENOSPC: 1530219089Spjd /* 1531219089Spjd * For quotas and reservations, ENOSPC indicates 1532219089Spjd * something different; setting a quota or reservation 1533219089Spjd * doesn't use any disk space. 1534219089Spjd */ 1535219089Spjd switch (prop) { 1536219089Spjd case ZFS_PROP_QUOTA: 1537219089Spjd case ZFS_PROP_REFQUOTA: 1538219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1539219089Spjd "size is less than current used or " 1540219089Spjd "reserved space")); 1541219089Spjd (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1542219089Spjd break; 1543219089Spjd 1544219089Spjd case ZFS_PROP_RESERVATION: 1545219089Spjd case ZFS_PROP_REFRESERVATION: 1546219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1547219089Spjd "size is greater than available space")); 1548219089Spjd (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1549219089Spjd break; 1550219089Spjd 1551219089Spjd default: 1552219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1553219089Spjd break; 1554168404Spjd } 1555219089Spjd break; 1556219089Spjd 1557219089Spjd case EBUSY: 1558219089Spjd (void) zfs_standard_error(hdl, EBUSY, errbuf); 1559219089Spjd break; 1560219089Spjd 1561219089Spjd case EROFS: 1562219089Spjd (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 1563219089Spjd break; 1564219089Spjd 1565271764Swill case E2BIG: 1566271764Swill zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1567271764Swill "property value too long")); 1568271764Swill (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1569271764Swill break; 1570271764Swill 1571219089Spjd case ENOTSUP: 1572219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1573219089Spjd "pool and or dataset must be upgraded to set this " 1574219089Spjd "property or value")); 1575219089Spjd (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1576219089Spjd break; 1577219089Spjd 1578219089Spjd case ERANGE: 1579274337Sdelphij case EDOM: 1580274337Sdelphij if (prop == ZFS_PROP_COMPRESSION || 1581274337Sdelphij prop == ZFS_PROP_RECORDSIZE) { 1582219089Spjd (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1583219089Spjd "property setting is not allowed on " 1584219089Spjd "bootable datasets")); 1585219089Spjd (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 1586289422Smav } else if (prop == ZFS_PROP_CHECKSUM || 1587289422Smav prop == ZFS_PROP_DEDUP) { 1588289422Smav (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1589289422Smav "property setting is not allowed on " 1590289422Smav "root pools")); 1591289422Smav (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 1592219089Spjd } else { 1593219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1594219089Spjd } 1595219089Spjd break; 1596219089Spjd 1597219089Spjd case EINVAL: 1598219089Spjd if (prop == ZPROP_INVAL) { 1599219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1600219089Spjd } else { 1601219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1602219089Spjd } 1603219089Spjd break; 1604219089Spjd 1605219089Spjd case EOVERFLOW: 1606219089Spjd /* 1607219089Spjd * This platform can't address a volume this big. 1608219089Spjd */ 1609219089Spjd#ifdef _ILP32 1610219089Spjd if (prop == ZFS_PROP_VOLSIZE) { 1611219089Spjd (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1612219089Spjd break; 1613219089Spjd } 1614219089Spjd#endif 1615219089Spjd /* FALLTHROUGH */ 1616219089Spjd default: 1617219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1618168404Spjd } 1619168404Spjd} 1620168404Spjd 1621168404Spjd/* 1622168404Spjd * Given a property name and value, set the property for the given dataset. 1623168404Spjd */ 1624168404Spjdint 1625168404Spjdzfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1626168404Spjd{ 1627168404Spjd int ret = -1; 1628168404Spjd char errbuf[1024]; 1629168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 1630289497Smav nvlist_t *nvl = NULL; 1631168404Spjd 1632168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 1633168404Spjd dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1634168404Spjd zhp->zfs_name); 1635168404Spjd 1636168404Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1637168404Spjd nvlist_add_string(nvl, propname, propval) != 0) { 1638168404Spjd (void) no_memory(hdl); 1639168404Spjd goto error; 1640168404Spjd } 1641168404Spjd 1642289497Smav ret = zfs_prop_set_list(zhp, nvl); 1643185029Spjd 1644289497Smaverror: 1645168404Spjd nvlist_free(nvl); 1646289497Smav return (ret); 1647289497Smav} 1648168404Spjd 1649168404Spjd 1650168404Spjd 1651289497Smav/* 1652289497Smav * Given an nvlist of property names and values, set the properties for the 1653289497Smav * given dataset. 1654289497Smav */ 1655289497Smavint 1656289497Smavzfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props) 1657289497Smav{ 1658289497Smav zfs_cmd_t zc = { 0 }; 1659289497Smav int ret = -1; 1660289497Smav prop_changelist_t **cls = NULL; 1661289497Smav int cl_idx; 1662289497Smav char errbuf[1024]; 1663289497Smav libzfs_handle_t *hdl = zhp->zfs_hdl; 1664289497Smav nvlist_t *nvl; 1665289497Smav int nvl_len; 1666307107Smav int added_resv = 0; 1667219089Spjd 1668289497Smav (void) snprintf(errbuf, sizeof (errbuf), 1669289497Smav dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1670289497Smav zhp->zfs_name); 1671168404Spjd 1672289497Smav if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props, 1673289500Smav zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl, 1674289500Smav errbuf)) == NULL) 1675168404Spjd goto error; 1676168404Spjd 1677185029Spjd /* 1678289497Smav * We have to check for any extra properties which need to be added 1679289497Smav * before computing the length of the nvlist. 1680185029Spjd */ 1681289497Smav for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 1682289497Smav elem != NULL; 1683289497Smav elem = nvlist_next_nvpair(nvl, elem)) { 1684289497Smav if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE && 1685289497Smav (added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) { 1686289497Smav goto error; 1687289497Smav } 1688238391Smm } 1689289497Smav /* 1690289497Smav * Check how many properties we're setting and allocate an array to 1691289497Smav * store changelist pointers for postfix(). 1692289497Smav */ 1693289497Smav nvl_len = 0; 1694289497Smav for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 1695289497Smav elem != NULL; 1696289497Smav elem = nvlist_next_nvpair(nvl, elem)) 1697289497Smav nvl_len++; 1698289497Smav if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL) 1699168404Spjd goto error; 1700168404Spjd 1701289497Smav cl_idx = 0; 1702289497Smav for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 1703289497Smav elem != NULL; 1704289497Smav elem = nvlist_next_nvpair(nvl, elem)) { 1705289497Smav 1706289497Smav zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem)); 1707289497Smav 1708289497Smav assert(cl_idx < nvl_len); 1709289497Smav /* 1710289497Smav * We don't want to unmount & remount the dataset when changing 1711310068Savg * its canmount property to 'on' or 'noauto'. We only use 1712310068Savg * the changelist logic to unmount when setting canmount=off. 1713289497Smav */ 1714297521Savg if (prop != ZFS_PROP_CANMOUNT || 1715297521Savg (fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF && 1716321522Smav zfs_is_mounted(zhp, NULL))) { 1717289497Smav cls[cl_idx] = changelist_gather(zhp, prop, 0, 0); 1718289497Smav if (cls[cl_idx] == NULL) 1719289497Smav goto error; 1720289497Smav } 1721289497Smav 1722289497Smav if (prop == ZFS_PROP_MOUNTPOINT && 1723289497Smav changelist_haszonedchild(cls[cl_idx])) { 1724289497Smav zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1725289497Smav "child dataset with inherited mountpoint is used " 1726289497Smav "in a non-global zone")); 1727289497Smav ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1728289497Smav goto error; 1729289497Smav } 1730289497Smav 1731289497Smav /* We don't support those properties on FreeBSD. */ 1732289497Smav switch (prop) { 1733289497Smav case ZFS_PROP_DEVICES: 1734289497Smav case ZFS_PROP_ISCSIOPTIONS: 1735289497Smav case ZFS_PROP_XATTR: 1736289497Smav case ZFS_PROP_VSCAN: 1737289497Smav case ZFS_PROP_NBMAND: 1738289497Smav case ZFS_PROP_MLSLABEL: 1739289497Smav (void) snprintf(errbuf, sizeof (errbuf), 1740289497Smav "property '%s' not supported on FreeBSD", 1741289497Smav nvpair_name(elem)); 1742289497Smav ret = zfs_error(hdl, EZFS_PERM, errbuf); 1743289497Smav goto error; 1744289497Smav } 1745289497Smav 1746289497Smav if (cls[cl_idx] != NULL && 1747289497Smav (ret = changelist_prefix(cls[cl_idx])) != 0) 1748289497Smav goto error; 1749289497Smav 1750289497Smav cl_idx++; 1751289497Smav } 1752289497Smav assert(cl_idx == nvl_len); 1753289497Smav 1754168404Spjd /* 1755289497Smav * Execute the corresponding ioctl() to set this list of properties. 1756168404Spjd */ 1757168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1758168404Spjd 1759289497Smav if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 || 1760289497Smav (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0) 1761168404Spjd goto error; 1762168404Spjd 1763185029Spjd ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1764209962Smm 1765168404Spjd if (ret != 0) { 1766289497Smav /* Get the list of unset properties back and report them. */ 1767289497Smav nvlist_t *errorprops = NULL; 1768289497Smav if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0) 1769289497Smav goto error; 1770289497Smav for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 1771289497Smav elem != NULL; 1772289497Smav elem = nvlist_next_nvpair(nvl, elem)) { 1773289497Smav zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem)); 1774289497Smav zfs_setprop_error(hdl, prop, errno, errbuf); 1775289497Smav } 1776289497Smav nvlist_free(errorprops); 1777289497Smav 1778219089Spjd if (added_resv && errno == ENOSPC) { 1779219089Spjd /* clean up the volsize property we tried to set */ 1780219089Spjd uint64_t old_volsize = zfs_prop_get_int(zhp, 1781219089Spjd ZFS_PROP_VOLSIZE); 1782219089Spjd nvlist_free(nvl); 1783289497Smav nvl = NULL; 1784219089Spjd zcmd_free_nvlists(&zc); 1785289497Smav 1786219089Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 1787219089Spjd goto error; 1788219089Spjd if (nvlist_add_uint64(nvl, 1789219089Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1790219089Spjd old_volsize) != 0) 1791219089Spjd goto error; 1792219089Spjd if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1793219089Spjd goto error; 1794219089Spjd (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1795168404Spjd } 1796168404Spjd } else { 1797289497Smav for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) { 1798289497Smav if (cls[cl_idx] != NULL) { 1799289497Smav int clp_err = changelist_postfix(cls[cl_idx]); 1800289497Smav if (clp_err != 0) 1801289497Smav ret = clp_err; 1802289497Smav } 1803289497Smav } 1804185029Spjd 1805168404Spjd /* 1806168404Spjd * Refresh the statistics so the new property value 1807168404Spjd * is reflected. 1808168404Spjd */ 1809185029Spjd if (ret == 0) 1810168404Spjd (void) get_stats(zhp); 1811168404Spjd } 1812168404Spjd 1813168404Spjderror: 1814168404Spjd nvlist_free(nvl); 1815168404Spjd zcmd_free_nvlists(&zc); 1816289497Smav if (cls != NULL) { 1817289497Smav for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) { 1818289497Smav if (cls[cl_idx] != NULL) 1819289497Smav changelist_free(cls[cl_idx]); 1820289497Smav } 1821289497Smav free(cls); 1822289497Smav } 1823168404Spjd return (ret); 1824168404Spjd} 1825168404Spjd 1826168404Spjd/* 1827219089Spjd * Given a property, inherit the value from the parent dataset, or if received 1828219089Spjd * is TRUE, revert to the received value, if any. 1829168404Spjd */ 1830168404Spjdint 1831219089Spjdzfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1832168404Spjd{ 1833168404Spjd zfs_cmd_t zc = { 0 }; 1834168404Spjd int ret; 1835168404Spjd prop_changelist_t *cl; 1836168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 1837168404Spjd char errbuf[1024]; 1838168404Spjd zfs_prop_t prop; 1839168404Spjd 1840168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 1841168404Spjd "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1842168404Spjd 1843219089Spjd zc.zc_cookie = received; 1844185029Spjd if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1845168404Spjd /* 1846168404Spjd * For user properties, the amount of work we have to do is very 1847168404Spjd * small, so just do it here. 1848168404Spjd */ 1849168404Spjd if (!zfs_prop_user(propname)) { 1850168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1851168404Spjd "invalid property")); 1852168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1853168404Spjd } 1854168404Spjd 1855168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1856168404Spjd (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1857168404Spjd 1858185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1859168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 1860168404Spjd 1861168404Spjd return (0); 1862168404Spjd } 1863168404Spjd 1864168404Spjd /* 1865168404Spjd * Verify that this property is inheritable. 1866168404Spjd */ 1867168404Spjd if (zfs_prop_readonly(prop)) 1868168404Spjd return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1869168404Spjd 1870219089Spjd if (!zfs_prop_inheritable(prop) && !received) 1871168404Spjd return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1872168404Spjd 1873168404Spjd /* 1874168404Spjd * Check to see if the value applies to this type 1875168404Spjd */ 1876168404Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1877168404Spjd return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1878168404Spjd 1879168404Spjd /* 1880219089Spjd * Normalize the name, to get rid of shorthand abbreviations. 1881168404Spjd */ 1882168404Spjd propname = zfs_prop_to_name(prop); 1883168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1884168404Spjd (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1885168404Spjd 1886168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1887168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 1888168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1889168404Spjd "dataset is used in a non-global zone")); 1890168404Spjd return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1891168404Spjd } 1892168404Spjd 1893168404Spjd /* 1894168404Spjd * Determine datasets which will be affected by this change, if any. 1895168404Spjd */ 1896185029Spjd if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1897168404Spjd return (-1); 1898168404Spjd 1899168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 1900168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1901168404Spjd "child dataset with inherited mountpoint is used " 1902168404Spjd "in a non-global zone")); 1903168404Spjd ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1904168404Spjd goto error; 1905168404Spjd } 1906168404Spjd 1907168404Spjd if ((ret = changelist_prefix(cl)) != 0) 1908168404Spjd goto error; 1909168404Spjd 1910185029Spjd if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 1911168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 1912168404Spjd } else { 1913168404Spjd 1914168404Spjd if ((ret = changelist_postfix(cl)) != 0) 1915168404Spjd goto error; 1916168404Spjd 1917168404Spjd /* 1918168404Spjd * Refresh the statistics so the new property is reflected. 1919168404Spjd */ 1920168404Spjd (void) get_stats(zhp); 1921168404Spjd } 1922168404Spjd 1923168404Spjderror: 1924168404Spjd changelist_free(cl); 1925168404Spjd return (ret); 1926168404Spjd} 1927168404Spjd 1928168404Spjd/* 1929168404Spjd * True DSL properties are stored in an nvlist. The following two functions 1930168404Spjd * extract them appropriately. 1931168404Spjd */ 1932168404Spjdstatic uint64_t 1933168404Spjdgetprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 1934168404Spjd{ 1935168404Spjd nvlist_t *nv; 1936168404Spjd uint64_t value; 1937168404Spjd 1938168404Spjd *source = NULL; 1939168404Spjd if (nvlist_lookup_nvlist(zhp->zfs_props, 1940168404Spjd zfs_prop_to_name(prop), &nv) == 0) { 1941185029Spjd verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1942185029Spjd (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 1943168404Spjd } else { 1944205198Sdelphij verify(!zhp->zfs_props_table || 1945205198Sdelphij zhp->zfs_props_table[prop] == B_TRUE); 1946168404Spjd value = zfs_prop_default_numeric(prop); 1947168404Spjd *source = ""; 1948168404Spjd } 1949168404Spjd 1950168404Spjd return (value); 1951168404Spjd} 1952168404Spjd 1953289362Smavstatic const char * 1954168404Spjdgetprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 1955168404Spjd{ 1956168404Spjd nvlist_t *nv; 1957289362Smav const char *value; 1958168404Spjd 1959168404Spjd *source = NULL; 1960168404Spjd if (nvlist_lookup_nvlist(zhp->zfs_props, 1961168404Spjd zfs_prop_to_name(prop), &nv) == 0) { 1962289362Smav value = fnvlist_lookup_string(nv, ZPROP_VALUE); 1963185029Spjd (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 1964168404Spjd } else { 1965205198Sdelphij verify(!zhp->zfs_props_table || 1966205198Sdelphij zhp->zfs_props_table[prop] == B_TRUE); 1967289362Smav value = zfs_prop_default_string(prop); 1968168404Spjd *source = ""; 1969168404Spjd } 1970168404Spjd 1971168404Spjd return (value); 1972168404Spjd} 1973168404Spjd 1974219089Spjdstatic boolean_t 1975219089Spjdzfs_is_recvd_props_mode(zfs_handle_t *zhp) 1976219089Spjd{ 1977219089Spjd return (zhp->zfs_props == zhp->zfs_recvd_props); 1978219089Spjd} 1979219089Spjd 1980219089Spjdstatic void 1981219089Spjdzfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 1982219089Spjd{ 1983219089Spjd *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 1984219089Spjd zhp->zfs_props = zhp->zfs_recvd_props; 1985219089Spjd} 1986219089Spjd 1987219089Spjdstatic void 1988219089Spjdzfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 1989219089Spjd{ 1990219089Spjd zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 1991219089Spjd *cookie = 0; 1992219089Spjd} 1993219089Spjd 1994168404Spjd/* 1995168404Spjd * Internal function for getting a numeric property. Both zfs_prop_get() and 1996168404Spjd * zfs_prop_get_int() are built using this interface. 1997168404Spjd * 1998168404Spjd * Certain properties can be overridden using 'mount -o'. In this case, scan 1999168404Spjd * the contents of the /etc/mnttab entry, searching for the appropriate options. 2000168404Spjd * If they differ from the on-disk values, report the current values and mark 2001168404Spjd * the source "temporary". 2002168404Spjd */ 2003168404Spjdstatic int 2004185029Spjdget_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 2005168404Spjd char **source, uint64_t *val) 2006168404Spjd{ 2007185029Spjd zfs_cmd_t zc = { 0 }; 2008185029Spjd nvlist_t *zplprops = NULL; 2009168404Spjd struct mnttab mnt; 2010168404Spjd char *mntopt_on = NULL; 2011168404Spjd char *mntopt_off = NULL; 2012219089Spjd boolean_t received = zfs_is_recvd_props_mode(zhp); 2013168404Spjd 2014168404Spjd *source = NULL; 2015168404Spjd 2016168404Spjd switch (prop) { 2017168404Spjd case ZFS_PROP_ATIME: 2018168404Spjd mntopt_on = MNTOPT_ATIME; 2019168404Spjd mntopt_off = MNTOPT_NOATIME; 2020168404Spjd break; 2021168404Spjd 2022168404Spjd case ZFS_PROP_DEVICES: 2023168404Spjd mntopt_on = MNTOPT_DEVICES; 2024168404Spjd mntopt_off = MNTOPT_NODEVICES; 2025168404Spjd break; 2026168404Spjd 2027168404Spjd case ZFS_PROP_EXEC: 2028168404Spjd mntopt_on = MNTOPT_EXEC; 2029168404Spjd mntopt_off = MNTOPT_NOEXEC; 2030168404Spjd break; 2031168404Spjd 2032168404Spjd case ZFS_PROP_READONLY: 2033168404Spjd mntopt_on = MNTOPT_RO; 2034168404Spjd mntopt_off = MNTOPT_RW; 2035168404Spjd break; 2036168404Spjd 2037168404Spjd case ZFS_PROP_SETUID: 2038168404Spjd mntopt_on = MNTOPT_SETUID; 2039168404Spjd mntopt_off = MNTOPT_NOSETUID; 2040168404Spjd break; 2041168404Spjd 2042168404Spjd case ZFS_PROP_XATTR: 2043168404Spjd mntopt_on = MNTOPT_XATTR; 2044168404Spjd mntopt_off = MNTOPT_NOXATTR; 2045168404Spjd break; 2046185029Spjd 2047185029Spjd case ZFS_PROP_NBMAND: 2048185029Spjd mntopt_on = MNTOPT_NBMAND; 2049185029Spjd mntopt_off = MNTOPT_NONBMAND; 2050185029Spjd break; 2051307050Smav 2052307050Smav default: 2053307050Smav break; 2054168404Spjd } 2055168404Spjd 2056168404Spjd /* 2057168404Spjd * Because looking up the mount options is potentially expensive 2058168404Spjd * (iterating over all of /etc/mnttab), we defer its calculation until 2059168404Spjd * we're looking up a property which requires its presence. 2060168404Spjd */ 2061168404Spjd if (!zhp->zfs_mntcheck && 2062168404Spjd (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 2063209962Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 2064209962Smm struct mnttab entry; 2065168404Spjd 2066209962Smm if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 2067209962Smm zhp->zfs_mntopts = zfs_strdup(hdl, 2068168404Spjd entry.mnt_mntopts); 2069168404Spjd if (zhp->zfs_mntopts == NULL) 2070168404Spjd return (-1); 2071168404Spjd } 2072168404Spjd 2073168404Spjd zhp->zfs_mntcheck = B_TRUE; 2074168404Spjd } 2075168404Spjd 2076168404Spjd if (zhp->zfs_mntopts == NULL) 2077168404Spjd mnt.mnt_mntopts = ""; 2078168404Spjd else 2079168404Spjd mnt.mnt_mntopts = zhp->zfs_mntopts; 2080168404Spjd 2081168404Spjd switch (prop) { 2082168404Spjd case ZFS_PROP_ATIME: 2083168404Spjd case ZFS_PROP_DEVICES: 2084168404Spjd case ZFS_PROP_EXEC: 2085168404Spjd case ZFS_PROP_READONLY: 2086168404Spjd case ZFS_PROP_SETUID: 2087168404Spjd case ZFS_PROP_XATTR: 2088185029Spjd case ZFS_PROP_NBMAND: 2089168404Spjd *val = getprop_uint64(zhp, prop, source); 2090168404Spjd 2091219089Spjd if (received) 2092219089Spjd break; 2093219089Spjd 2094168404Spjd if (hasmntopt(&mnt, mntopt_on) && !*val) { 2095168404Spjd *val = B_TRUE; 2096168404Spjd if (src) 2097185029Spjd *src = ZPROP_SRC_TEMPORARY; 2098168404Spjd } else if (hasmntopt(&mnt, mntopt_off) && *val) { 2099168404Spjd *val = B_FALSE; 2100168404Spjd if (src) 2101185029Spjd *src = ZPROP_SRC_TEMPORARY; 2102168404Spjd } 2103168404Spjd break; 2104168404Spjd 2105168404Spjd case ZFS_PROP_CANMOUNT: 2106219089Spjd case ZFS_PROP_VOLSIZE: 2107168404Spjd case ZFS_PROP_QUOTA: 2108185029Spjd case ZFS_PROP_REFQUOTA: 2109168404Spjd case ZFS_PROP_RESERVATION: 2110185029Spjd case ZFS_PROP_REFRESERVATION: 2111264835Sdelphij case ZFS_PROP_FILESYSTEM_LIMIT: 2112264835Sdelphij case ZFS_PROP_SNAPSHOT_LIMIT: 2113264835Sdelphij case ZFS_PROP_FILESYSTEM_COUNT: 2114264835Sdelphij case ZFS_PROP_SNAPSHOT_COUNT: 2115168404Spjd *val = getprop_uint64(zhp, prop, source); 2116219089Spjd 2117219089Spjd if (*source == NULL) { 2118219089Spjd /* not default, must be local */ 2119168404Spjd *source = zhp->zfs_name; 2120219089Spjd } 2121168404Spjd break; 2122168404Spjd 2123168404Spjd case ZFS_PROP_MOUNTED: 2124168404Spjd *val = (zhp->zfs_mntopts != NULL); 2125168404Spjd break; 2126168404Spjd 2127168404Spjd case ZFS_PROP_NUMCLONES: 2128168404Spjd *val = zhp->zfs_dmustats.dds_num_clones; 2129168404Spjd break; 2130168404Spjd 2131185029Spjd case ZFS_PROP_VERSION: 2132185029Spjd case ZFS_PROP_NORMALIZE: 2133185029Spjd case ZFS_PROP_UTF8ONLY: 2134185029Spjd case ZFS_PROP_CASE: 2135185029Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 2136185029Spjd zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 2137185029Spjd return (-1); 2138185029Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2139185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 2140185029Spjd zcmd_free_nvlists(&zc); 2141219089Spjd return (-1); 2142185029Spjd } 2143185029Spjd if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 2144185029Spjd nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 2145185029Spjd val) != 0) { 2146185029Spjd zcmd_free_nvlists(&zc); 2147219089Spjd return (-1); 2148185029Spjd } 2149296528Smav nvlist_free(zplprops); 2150185029Spjd zcmd_free_nvlists(&zc); 2151185029Spjd break; 2152185029Spjd 2153253819Sdelphij case ZFS_PROP_INCONSISTENT: 2154253819Sdelphij *val = zhp->zfs_dmustats.dds_inconsistent; 2155253819Sdelphij break; 2156253819Sdelphij 2157168404Spjd default: 2158185029Spjd switch (zfs_prop_get_type(prop)) { 2159185029Spjd case PROP_TYPE_NUMBER: 2160185029Spjd case PROP_TYPE_INDEX: 2161185029Spjd *val = getprop_uint64(zhp, prop, source); 2162185029Spjd /* 2163209962Smm * If we tried to use a default value for a 2164185029Spjd * readonly property, it means that it was not 2165219089Spjd * present. 2166185029Spjd */ 2167185029Spjd if (zfs_prop_readonly(prop) && 2168219089Spjd *source != NULL && (*source)[0] == '\0') { 2169219089Spjd *source = NULL; 2170185029Spjd } 2171185029Spjd break; 2172185029Spjd 2173185029Spjd case PROP_TYPE_STRING: 2174185029Spjd default: 2175185029Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 2176185029Spjd "cannot get non-numeric property")); 2177185029Spjd return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 2178185029Spjd dgettext(TEXT_DOMAIN, "internal error"))); 2179185029Spjd } 2180168404Spjd } 2181168404Spjd 2182168404Spjd return (0); 2183168404Spjd} 2184168404Spjd 2185168404Spjd/* 2186168404Spjd * Calculate the source type, given the raw source string. 2187168404Spjd */ 2188168404Spjdstatic void 2189185029Spjdget_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 2190168404Spjd char *statbuf, size_t statlen) 2191168404Spjd{ 2192185029Spjd if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 2193168404Spjd return; 2194168404Spjd 2195168404Spjd if (source == NULL) { 2196185029Spjd *srctype = ZPROP_SRC_NONE; 2197168404Spjd } else if (source[0] == '\0') { 2198185029Spjd *srctype = ZPROP_SRC_DEFAULT; 2199219089Spjd } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 2200219089Spjd *srctype = ZPROP_SRC_RECEIVED; 2201168404Spjd } else { 2202168404Spjd if (strcmp(source, zhp->zfs_name) == 0) { 2203185029Spjd *srctype = ZPROP_SRC_LOCAL; 2204168404Spjd } else { 2205168404Spjd (void) strlcpy(statbuf, source, statlen); 2206185029Spjd *srctype = ZPROP_SRC_INHERITED; 2207168404Spjd } 2208168404Spjd } 2209168404Spjd 2210168404Spjd} 2211168404Spjd 2212219089Spjdint 2213219089Spjdzfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 2214219089Spjd size_t proplen, boolean_t literal) 2215219089Spjd{ 2216219089Spjd zfs_prop_t prop; 2217219089Spjd int err = 0; 2218219089Spjd 2219219089Spjd if (zhp->zfs_recvd_props == NULL) 2220219089Spjd if (get_recvd_props_ioctl(zhp) != 0) 2221219089Spjd return (-1); 2222219089Spjd 2223219089Spjd prop = zfs_name_to_prop(propname); 2224219089Spjd 2225219089Spjd if (prop != ZPROP_INVAL) { 2226219089Spjd uint64_t cookie; 2227219089Spjd if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 2228219089Spjd return (-1); 2229219089Spjd zfs_set_recvd_props_mode(zhp, &cookie); 2230219089Spjd err = zfs_prop_get(zhp, prop, propbuf, proplen, 2231219089Spjd NULL, NULL, 0, literal); 2232219089Spjd zfs_unset_recvd_props_mode(zhp, &cookie); 2233219089Spjd } else { 2234219089Spjd nvlist_t *propval; 2235219089Spjd char *recvdval; 2236219089Spjd if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 2237219089Spjd propname, &propval) != 0) 2238219089Spjd return (-1); 2239219089Spjd verify(nvlist_lookup_string(propval, ZPROP_VALUE, 2240219089Spjd &recvdval) == 0); 2241219089Spjd (void) strlcpy(propbuf, recvdval, proplen); 2242219089Spjd } 2243219089Spjd 2244219089Spjd return (err == 0 ? 0 : -1); 2245219089Spjd} 2246219089Spjd 2247228103Smmstatic int 2248228103Smmget_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen) 2249228103Smm{ 2250228103Smm nvlist_t *value; 2251228103Smm nvpair_t *pair; 2252228103Smm 2253228103Smm value = zfs_get_clones_nvl(zhp); 2254228103Smm if (value == NULL) 2255228103Smm return (-1); 2256228103Smm 2257228103Smm propbuf[0] = '\0'; 2258228103Smm for (pair = nvlist_next_nvpair(value, NULL); pair != NULL; 2259228103Smm pair = nvlist_next_nvpair(value, pair)) { 2260228103Smm if (propbuf[0] != '\0') 2261228103Smm (void) strlcat(propbuf, ",", proplen); 2262228103Smm (void) strlcat(propbuf, nvpair_name(pair), proplen); 2263228103Smm } 2264228103Smm 2265228103Smm return (0); 2266228103Smm} 2267228103Smm 2268228103Smmstruct get_clones_arg { 2269228103Smm uint64_t numclones; 2270228103Smm nvlist_t *value; 2271228103Smm const char *origin; 2272307108Smav char buf[ZFS_MAX_DATASET_NAME_LEN]; 2273228103Smm}; 2274228103Smm 2275228103Smmint 2276228103Smmget_clones_cb(zfs_handle_t *zhp, void *arg) 2277228103Smm{ 2278228103Smm struct get_clones_arg *gca = arg; 2279228103Smm 2280228103Smm if (gca->numclones == 0) { 2281228103Smm zfs_close(zhp); 2282228103Smm return (0); 2283228103Smm } 2284228103Smm 2285228103Smm if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf), 2286228103Smm NULL, NULL, 0, B_TRUE) != 0) 2287228103Smm goto out; 2288228103Smm if (strcmp(gca->buf, gca->origin) == 0) { 2289248571Smm fnvlist_add_boolean(gca->value, zfs_get_name(zhp)); 2290228103Smm gca->numclones--; 2291228103Smm } 2292228103Smm 2293228103Smmout: 2294228103Smm (void) zfs_iter_children(zhp, get_clones_cb, gca); 2295228103Smm zfs_close(zhp); 2296228103Smm return (0); 2297228103Smm} 2298228103Smm 2299228103Smmnvlist_t * 2300228103Smmzfs_get_clones_nvl(zfs_handle_t *zhp) 2301228103Smm{ 2302228103Smm nvlist_t *nv, *value; 2303228103Smm 2304228103Smm if (nvlist_lookup_nvlist(zhp->zfs_props, 2305228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) { 2306228103Smm struct get_clones_arg gca; 2307228103Smm 2308228103Smm /* 2309228103Smm * if this is a snapshot, then the kernel wasn't able 2310228103Smm * to get the clones. Do it by slowly iterating. 2311228103Smm */ 2312228103Smm if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) 2313228103Smm return (NULL); 2314228103Smm if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0) 2315228103Smm return (NULL); 2316228103Smm if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) { 2317228103Smm nvlist_free(nv); 2318228103Smm return (NULL); 2319228103Smm } 2320228103Smm 2321228103Smm gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES); 2322228103Smm gca.value = value; 2323228103Smm gca.origin = zhp->zfs_name; 2324228103Smm 2325228103Smm if (gca.numclones != 0) { 2326228103Smm zfs_handle_t *root; 2327307108Smav char pool[ZFS_MAX_DATASET_NAME_LEN]; 2328228103Smm char *cp = pool; 2329228103Smm 2330228103Smm /* get the pool name */ 2331228103Smm (void) strlcpy(pool, zhp->zfs_name, sizeof (pool)); 2332228103Smm (void) strsep(&cp, "/@"); 2333228103Smm root = zfs_open(zhp->zfs_hdl, pool, 2334228103Smm ZFS_TYPE_FILESYSTEM); 2335228103Smm 2336228103Smm (void) get_clones_cb(root, &gca); 2337228103Smm } 2338228103Smm 2339228103Smm if (gca.numclones != 0 || 2340228103Smm nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 || 2341228103Smm nvlist_add_nvlist(zhp->zfs_props, 2342228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) { 2343228103Smm nvlist_free(nv); 2344228103Smm nvlist_free(value); 2345228103Smm return (NULL); 2346228103Smm } 2347228103Smm nvlist_free(nv); 2348228103Smm nvlist_free(value); 2349228103Smm verify(0 == nvlist_lookup_nvlist(zhp->zfs_props, 2350228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), &nv)); 2351228103Smm } 2352228103Smm 2353228103Smm verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0); 2354228103Smm 2355228103Smm return (value); 2356228103Smm} 2357228103Smm 2358168404Spjd/* 2359168404Spjd * Retrieve a property from the given object. If 'literal' is specified, then 2360168404Spjd * numbers are left as exact values. Otherwise, numbers are converted to a 2361168404Spjd * human-readable form. 2362168404Spjd * 2363168404Spjd * Returns 0 on success, or -1 on error. 2364168404Spjd */ 2365168404Spjdint 2366168404Spjdzfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 2367185029Spjd zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 2368168404Spjd{ 2369168404Spjd char *source = NULL; 2370168404Spjd uint64_t val; 2371289362Smav const char *str; 2372168404Spjd const char *strval; 2373219089Spjd boolean_t received = zfs_is_recvd_props_mode(zhp); 2374168404Spjd 2375168404Spjd /* 2376168404Spjd * Check to see if this property applies to our object 2377168404Spjd */ 2378168404Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 2379168404Spjd return (-1); 2380168404Spjd 2381219089Spjd if (received && zfs_prop_readonly(prop)) 2382219089Spjd return (-1); 2383219089Spjd 2384168404Spjd if (src) 2385185029Spjd *src = ZPROP_SRC_NONE; 2386168404Spjd 2387168404Spjd switch (prop) { 2388168404Spjd case ZFS_PROP_CREATION: 2389168404Spjd /* 2390168404Spjd * 'creation' is a time_t stored in the statistics. We convert 2391168404Spjd * this into a string unless 'literal' is specified. 2392168404Spjd */ 2393168404Spjd { 2394168404Spjd val = getprop_uint64(zhp, prop, &source); 2395168404Spjd time_t time = (time_t)val; 2396168404Spjd struct tm t; 2397168404Spjd 2398168404Spjd if (literal || 2399168404Spjd localtime_r(&time, &t) == NULL || 2400168404Spjd strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 2401168404Spjd &t) == 0) 2402168404Spjd (void) snprintf(propbuf, proplen, "%llu", val); 2403168404Spjd } 2404168404Spjd break; 2405168404Spjd 2406168404Spjd case ZFS_PROP_MOUNTPOINT: 2407168404Spjd /* 2408168404Spjd * Getting the precise mountpoint can be tricky. 2409168404Spjd * 2410168404Spjd * - for 'none' or 'legacy', return those values. 2411168404Spjd * - for inherited mountpoints, we want to take everything 2412168404Spjd * after our ancestor and append it to the inherited value. 2413168404Spjd * 2414168404Spjd * If the pool has an alternate root, we want to prepend that 2415168404Spjd * root to any values we return. 2416168404Spjd */ 2417185029Spjd 2418168404Spjd str = getprop_string(zhp, prop, &source); 2419168404Spjd 2420185029Spjd if (str[0] == '/') { 2421185029Spjd char buf[MAXPATHLEN]; 2422185029Spjd char *root = buf; 2423219089Spjd const char *relpath; 2424168404Spjd 2425219089Spjd /* 2426219089Spjd * If we inherit the mountpoint, even from a dataset 2427219089Spjd * with a received value, the source will be the path of 2428219089Spjd * the dataset we inherit from. If source is 2429219089Spjd * ZPROP_SOURCE_VAL_RECVD, the received value is not 2430219089Spjd * inherited. 2431219089Spjd */ 2432219089Spjd if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 2433219089Spjd relpath = ""; 2434219089Spjd } else { 2435219089Spjd relpath = zhp->zfs_name + strlen(source); 2436219089Spjd if (relpath[0] == '/') 2437219089Spjd relpath++; 2438219089Spjd } 2439185029Spjd 2440185029Spjd if ((zpool_get_prop(zhp->zpool_hdl, 2441263889Sdelphij ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL, 2442263889Sdelphij B_FALSE)) || (strcmp(root, "-") == 0)) 2443185029Spjd root[0] = '\0'; 2444185029Spjd /* 2445185029Spjd * Special case an alternate root of '/'. This will 2446185029Spjd * avoid having multiple leading slashes in the 2447185029Spjd * mountpoint path. 2448185029Spjd */ 2449185029Spjd if (strcmp(root, "/") == 0) 2450185029Spjd root++; 2451185029Spjd 2452185029Spjd /* 2453185029Spjd * If the mountpoint is '/' then skip over this 2454185029Spjd * if we are obtaining either an alternate root or 2455185029Spjd * an inherited mountpoint. 2456185029Spjd */ 2457185029Spjd if (str[1] == '\0' && (root[0] != '\0' || 2458185029Spjd relpath[0] != '\0')) 2459168404Spjd str++; 2460168404Spjd 2461168404Spjd if (relpath[0] == '\0') 2462168404Spjd (void) snprintf(propbuf, proplen, "%s%s", 2463168404Spjd root, str); 2464168404Spjd else 2465168404Spjd (void) snprintf(propbuf, proplen, "%s%s%s%s", 2466168404Spjd root, str, relpath[0] == '@' ? "" : "/", 2467168404Spjd relpath); 2468168404Spjd } else { 2469168404Spjd /* 'legacy' or 'none' */ 2470168404Spjd (void) strlcpy(propbuf, str, proplen); 2471168404Spjd } 2472168404Spjd 2473168404Spjd break; 2474168404Spjd 2475168404Spjd case ZFS_PROP_ORIGIN: 2476289362Smav str = getprop_string(zhp, prop, &source); 2477289362Smav if (str == NULL) 2478168404Spjd return (-1); 2479289362Smav (void) strlcpy(propbuf, str, proplen); 2480168404Spjd break; 2481168404Spjd 2482228103Smm case ZFS_PROP_CLONES: 2483228103Smm if (get_clones_string(zhp, propbuf, proplen) != 0) 2484228103Smm return (-1); 2485228103Smm break; 2486228103Smm 2487168404Spjd case ZFS_PROP_QUOTA: 2488185029Spjd case ZFS_PROP_REFQUOTA: 2489168404Spjd case ZFS_PROP_RESERVATION: 2490185029Spjd case ZFS_PROP_REFRESERVATION: 2491185029Spjd 2492168404Spjd if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2493168404Spjd return (-1); 2494168404Spjd 2495168404Spjd /* 2496168404Spjd * If quota or reservation is 0, we translate this into 'none' 2497168404Spjd * (unless literal is set), and indicate that it's the default 2498168404Spjd * value. Otherwise, we print the number nicely and indicate 2499168404Spjd * that its set locally. 2500168404Spjd */ 2501168404Spjd if (val == 0) { 2502168404Spjd if (literal) 2503168404Spjd (void) strlcpy(propbuf, "0", proplen); 2504168404Spjd else 2505168404Spjd (void) strlcpy(propbuf, "none", proplen); 2506168404Spjd } else { 2507168404Spjd if (literal) 2508168404Spjd (void) snprintf(propbuf, proplen, "%llu", 2509168404Spjd (u_longlong_t)val); 2510168404Spjd else 2511168404Spjd zfs_nicenum(val, propbuf, proplen); 2512168404Spjd } 2513168404Spjd break; 2514168404Spjd 2515264835Sdelphij case ZFS_PROP_FILESYSTEM_LIMIT: 2516264835Sdelphij case ZFS_PROP_SNAPSHOT_LIMIT: 2517264835Sdelphij case ZFS_PROP_FILESYSTEM_COUNT: 2518264835Sdelphij case ZFS_PROP_SNAPSHOT_COUNT: 2519264835Sdelphij 2520264835Sdelphij if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2521264835Sdelphij return (-1); 2522264835Sdelphij 2523264835Sdelphij /* 2524264835Sdelphij * If limit is UINT64_MAX, we translate this into 'none' (unless 2525264835Sdelphij * literal is set), and indicate that it's the default value. 2526264835Sdelphij * Otherwise, we print the number nicely and indicate that it's 2527264835Sdelphij * set locally. 2528264835Sdelphij */ 2529264835Sdelphij if (literal) { 2530264835Sdelphij (void) snprintf(propbuf, proplen, "%llu", 2531264835Sdelphij (u_longlong_t)val); 2532264835Sdelphij } else if (val == UINT64_MAX) { 2533264835Sdelphij (void) strlcpy(propbuf, "none", proplen); 2534264835Sdelphij } else { 2535264835Sdelphij zfs_nicenum(val, propbuf, proplen); 2536264835Sdelphij } 2537264835Sdelphij break; 2538264835Sdelphij 2539223623Smm case ZFS_PROP_REFRATIO: 2540168404Spjd case ZFS_PROP_COMPRESSRATIO: 2541168404Spjd if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2542168404Spjd return (-1); 2543219089Spjd (void) snprintf(propbuf, proplen, "%llu.%02llux", 2544219089Spjd (u_longlong_t)(val / 100), 2545219089Spjd (u_longlong_t)(val % 100)); 2546168404Spjd break; 2547168404Spjd 2548168404Spjd case ZFS_PROP_TYPE: 2549168404Spjd switch (zhp->zfs_type) { 2550168404Spjd case ZFS_TYPE_FILESYSTEM: 2551168404Spjd str = "filesystem"; 2552168404Spjd break; 2553168404Spjd case ZFS_TYPE_VOLUME: 2554168404Spjd str = "volume"; 2555168404Spjd break; 2556168404Spjd case ZFS_TYPE_SNAPSHOT: 2557168404Spjd str = "snapshot"; 2558168404Spjd break; 2559260183Sdelphij case ZFS_TYPE_BOOKMARK: 2560260183Sdelphij str = "bookmark"; 2561260183Sdelphij break; 2562168404Spjd default: 2563168404Spjd abort(); 2564168404Spjd } 2565168404Spjd (void) snprintf(propbuf, proplen, "%s", str); 2566168404Spjd break; 2567168404Spjd 2568168404Spjd case ZFS_PROP_MOUNTED: 2569168404Spjd /* 2570168404Spjd * The 'mounted' property is a pseudo-property that described 2571168404Spjd * whether the filesystem is currently mounted. Even though 2572168404Spjd * it's a boolean value, the typical values of "on" and "off" 2573168404Spjd * don't make sense, so we translate to "yes" and "no". 2574168404Spjd */ 2575168404Spjd if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 2576168404Spjd src, &source, &val) != 0) 2577168404Spjd return (-1); 2578168404Spjd if (val) 2579168404Spjd (void) strlcpy(propbuf, "yes", proplen); 2580168404Spjd else 2581168404Spjd (void) strlcpy(propbuf, "no", proplen); 2582168404Spjd break; 2583168404Spjd 2584168404Spjd case ZFS_PROP_NAME: 2585168404Spjd /* 2586168404Spjd * The 'name' property is a pseudo-property derived from the 2587168404Spjd * dataset name. It is presented as a real property to simplify 2588168404Spjd * consumers. 2589168404Spjd */ 2590168404Spjd (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2591168404Spjd break; 2592168404Spjd 2593219089Spjd case ZFS_PROP_MLSLABEL: 2594219089Spjd { 2595277300Ssmh#ifdef illumos 2596219089Spjd m_label_t *new_sl = NULL; 2597219089Spjd char *ascii = NULL; /* human readable label */ 2598219089Spjd 2599219089Spjd (void) strlcpy(propbuf, 2600219089Spjd getprop_string(zhp, prop, &source), proplen); 2601219089Spjd 2602219089Spjd if (literal || (strcasecmp(propbuf, 2603219089Spjd ZFS_MLSLABEL_DEFAULT) == 0)) 2604219089Spjd break; 2605219089Spjd 2606219089Spjd /* 2607219089Spjd * Try to translate the internal hex string to 2608219089Spjd * human-readable output. If there are any 2609219089Spjd * problems just use the hex string. 2610219089Spjd */ 2611219089Spjd 2612219089Spjd if (str_to_label(propbuf, &new_sl, MAC_LABEL, 2613219089Spjd L_NO_CORRECTION, NULL) == -1) { 2614219089Spjd m_label_free(new_sl); 2615219089Spjd break; 2616219089Spjd } 2617219089Spjd 2618219089Spjd if (label_to_str(new_sl, &ascii, M_LABEL, 2619219089Spjd DEF_NAMES) != 0) { 2620219089Spjd if (ascii) 2621219089Spjd free(ascii); 2622219089Spjd m_label_free(new_sl); 2623219089Spjd break; 2624219089Spjd } 2625219089Spjd m_label_free(new_sl); 2626219089Spjd 2627219089Spjd (void) strlcpy(propbuf, ascii, proplen); 2628219089Spjd free(ascii); 2629277300Ssmh#else /* !illumos */ 2630219089Spjd propbuf[0] = '\0'; 2631277300Ssmh#endif /* illumos */ 2632219089Spjd } 2633219089Spjd break; 2634219089Spjd 2635236705Smm case ZFS_PROP_GUID: 2636236705Smm /* 2637236705Smm * GUIDs are stored as numbers, but they are identifiers. 2638236705Smm * We don't want them to be pretty printed, because pretty 2639236705Smm * printing mangles the ID into a truncated and useless value. 2640236705Smm */ 2641236705Smm if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2642236705Smm return (-1); 2643236705Smm (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); 2644236705Smm break; 2645236705Smm 2646168404Spjd default: 2647185029Spjd switch (zfs_prop_get_type(prop)) { 2648185029Spjd case PROP_TYPE_NUMBER: 2649185029Spjd if (get_numeric_property(zhp, prop, src, 2650185029Spjd &source, &val) != 0) 2651185029Spjd return (-1); 2652185029Spjd if (literal) 2653185029Spjd (void) snprintf(propbuf, proplen, "%llu", 2654185029Spjd (u_longlong_t)val); 2655185029Spjd else 2656185029Spjd zfs_nicenum(val, propbuf, proplen); 2657185029Spjd break; 2658185029Spjd 2659185029Spjd case PROP_TYPE_STRING: 2660289362Smav str = getprop_string(zhp, prop, &source); 2661289362Smav if (str == NULL) 2662289362Smav return (-1); 2663289362Smav (void) strlcpy(propbuf, str, proplen); 2664185029Spjd break; 2665185029Spjd 2666185029Spjd case PROP_TYPE_INDEX: 2667185029Spjd if (get_numeric_property(zhp, prop, src, 2668185029Spjd &source, &val) != 0) 2669185029Spjd return (-1); 2670185029Spjd if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2671185029Spjd return (-1); 2672185029Spjd (void) strlcpy(propbuf, strval, proplen); 2673185029Spjd break; 2674185029Spjd 2675185029Spjd default: 2676185029Spjd abort(); 2677185029Spjd } 2678168404Spjd } 2679168404Spjd 2680168404Spjd get_source(zhp, src, source, statbuf, statlen); 2681168404Spjd 2682168404Spjd return (0); 2683168404Spjd} 2684168404Spjd 2685168404Spjd/* 2686168404Spjd * Utility function to get the given numeric property. Does no validation that 2687168404Spjd * the given property is the appropriate type; should only be used with 2688168404Spjd * hard-coded property types. 2689168404Spjd */ 2690168404Spjduint64_t 2691168404Spjdzfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2692168404Spjd{ 2693168404Spjd char *source; 2694168404Spjd uint64_t val; 2695168404Spjd 2696185029Spjd (void) get_numeric_property(zhp, prop, NULL, &source, &val); 2697168404Spjd 2698168404Spjd return (val); 2699168404Spjd} 2700168404Spjd 2701185029Spjdint 2702185029Spjdzfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 2703185029Spjd{ 2704185029Spjd char buf[64]; 2705185029Spjd 2706209962Smm (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 2707185029Spjd return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 2708185029Spjd} 2709185029Spjd 2710168404Spjd/* 2711168404Spjd * Similar to zfs_prop_get(), but returns the value as an integer. 2712168404Spjd */ 2713168404Spjdint 2714168404Spjdzfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2715185029Spjd zprop_source_t *src, char *statbuf, size_t statlen) 2716168404Spjd{ 2717168404Spjd char *source; 2718168404Spjd 2719168404Spjd /* 2720168404Spjd * Check to see if this property applies to our object 2721168404Spjd */ 2722185029Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2723168404Spjd return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 2724168404Spjd dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 2725168404Spjd zfs_prop_to_name(prop))); 2726185029Spjd } 2727168404Spjd 2728168404Spjd if (src) 2729185029Spjd *src = ZPROP_SRC_NONE; 2730168404Spjd 2731168404Spjd if (get_numeric_property(zhp, prop, src, &source, value) != 0) 2732168404Spjd return (-1); 2733168404Spjd 2734168404Spjd get_source(zhp, src, source, statbuf, statlen); 2735168404Spjd 2736168404Spjd return (0); 2737168404Spjd} 2738168404Spjd 2739209962Smmstatic int 2740209962Smmidmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 2741209962Smm char **domainp, idmap_rid_t *ridp) 2742209962Smm{ 2743277300Ssmh#ifdef illumos 2744209962Smm idmap_get_handle_t *get_hdl = NULL; 2745209962Smm idmap_stat status; 2746209962Smm int err = EINVAL; 2747209962Smm 2748219089Spjd if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS) 2749209962Smm goto out; 2750209962Smm 2751209962Smm if (isuser) { 2752209962Smm err = idmap_get_sidbyuid(get_hdl, id, 2753209962Smm IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2754209962Smm } else { 2755209962Smm err = idmap_get_sidbygid(get_hdl, id, 2756209962Smm IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2757209962Smm } 2758209962Smm if (err == IDMAP_SUCCESS && 2759209962Smm idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 2760209962Smm status == IDMAP_SUCCESS) 2761209962Smm err = 0; 2762209962Smm else 2763209962Smm err = EINVAL; 2764209962Smmout: 2765209962Smm if (get_hdl) 2766209962Smm idmap_get_destroy(get_hdl); 2767209962Smm return (err); 2768277300Ssmh#else /* !illumos */ 2769209962Smm assert(!"invalid code path"); 2770264852Ssmh return (EINVAL); // silence compiler warning 2771277300Ssmh#endif /* illumos */ 2772209962Smm} 2773209962Smm 2774168404Spjd/* 2775209962Smm * convert the propname into parameters needed by kernel 2776209962Smm * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 2777209962Smm * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 2778209962Smm */ 2779209962Smmstatic int 2780209962Smmuserquota_propname_decode(const char *propname, boolean_t zoned, 2781209962Smm zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 2782209962Smm{ 2783209962Smm zfs_userquota_prop_t type; 2784209962Smm char *cp, *end; 2785209962Smm char *numericsid = NULL; 2786209962Smm boolean_t isuser; 2787209962Smm 2788209962Smm domain[0] = '\0'; 2789275579Sdelphij *ridp = 0; 2790209962Smm /* Figure out the property type ({user|group}{quota|space}) */ 2791209962Smm for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 2792209962Smm if (strncmp(propname, zfs_userquota_prop_prefixes[type], 2793209962Smm strlen(zfs_userquota_prop_prefixes[type])) == 0) 2794209962Smm break; 2795209962Smm } 2796209962Smm if (type == ZFS_NUM_USERQUOTA_PROPS) 2797209962Smm return (EINVAL); 2798209962Smm *typep = type; 2799209962Smm 2800209962Smm isuser = (type == ZFS_PROP_USERQUOTA || 2801209962Smm type == ZFS_PROP_USERUSED); 2802209962Smm 2803209962Smm cp = strchr(propname, '@') + 1; 2804209962Smm 2805209962Smm if (strchr(cp, '@')) { 2806277300Ssmh#ifdef illumos 2807209962Smm /* 2808209962Smm * It's a SID name (eg "user@domain") that needs to be 2809209962Smm * turned into S-1-domainID-RID. 2810209962Smm */ 2811275579Sdelphij int flag = 0; 2812275579Sdelphij idmap_stat stat, map_stat; 2813275579Sdelphij uid_t pid; 2814275579Sdelphij idmap_rid_t rid; 2815275579Sdelphij idmap_get_handle_t *gh = NULL; 2816275579Sdelphij 2817275579Sdelphij stat = idmap_get_create(&gh); 2818275579Sdelphij if (stat != IDMAP_SUCCESS) { 2819275579Sdelphij idmap_get_destroy(gh); 2820275579Sdelphij return (ENOMEM); 2821275579Sdelphij } 2822209962Smm if (zoned && getzoneid() == GLOBAL_ZONEID) 2823209962Smm return (ENOENT); 2824209962Smm if (isuser) { 2825275579Sdelphij stat = idmap_getuidbywinname(cp, NULL, flag, &pid); 2826275579Sdelphij if (stat < 0) 2827275579Sdelphij return (ENOENT); 2828275579Sdelphij stat = idmap_get_sidbyuid(gh, pid, flag, &numericsid, 2829275579Sdelphij &rid, &map_stat); 2830209962Smm } else { 2831275579Sdelphij stat = idmap_getgidbywinname(cp, NULL, flag, &pid); 2832275579Sdelphij if (stat < 0) 2833275579Sdelphij return (ENOENT); 2834275579Sdelphij stat = idmap_get_sidbygid(gh, pid, flag, &numericsid, 2835275579Sdelphij &rid, &map_stat); 2836209962Smm } 2837275579Sdelphij if (stat < 0) { 2838275579Sdelphij idmap_get_destroy(gh); 2839209962Smm return (ENOENT); 2840209962Smm } 2841275579Sdelphij stat = idmap_get_mappings(gh); 2842275579Sdelphij idmap_get_destroy(gh); 2843275579Sdelphij 2844275579Sdelphij if (stat < 0) { 2845275579Sdelphij return (ENOENT); 2846275579Sdelphij } 2847209962Smm if (numericsid == NULL) 2848209962Smm return (ENOENT); 2849209962Smm cp = numericsid; 2850275579Sdelphij *ridp = rid; 2851209962Smm /* will be further decoded below */ 2852277300Ssmh#else /* !illumos */ 2853219089Spjd return (ENOENT); 2854277300Ssmh#endif /* illumos */ 2855209962Smm } 2856209962Smm 2857209962Smm if (strncmp(cp, "S-1-", 4) == 0) { 2858209962Smm /* It's a numeric SID (eg "S-1-234-567-89") */ 2859209962Smm (void) strlcpy(domain, cp, domainlen); 2860209962Smm errno = 0; 2861275579Sdelphij if (*ridp == 0) { 2862275579Sdelphij cp = strrchr(domain, '-'); 2863275579Sdelphij *cp = '\0'; 2864275579Sdelphij cp++; 2865275579Sdelphij *ridp = strtoull(cp, &end, 10); 2866275579Sdelphij } else { 2867275579Sdelphij end = ""; 2868275579Sdelphij } 2869209962Smm if (numericsid) { 2870209962Smm free(numericsid); 2871209962Smm numericsid = NULL; 2872209962Smm } 2873209962Smm if (errno != 0 || *end != '\0') 2874209962Smm return (EINVAL); 2875209962Smm } else if (!isdigit(*cp)) { 2876209962Smm /* 2877209962Smm * It's a user/group name (eg "user") that needs to be 2878209962Smm * turned into a uid/gid 2879209962Smm */ 2880209962Smm if (zoned && getzoneid() == GLOBAL_ZONEID) 2881209962Smm return (ENOENT); 2882209962Smm if (isuser) { 2883209962Smm struct passwd *pw; 2884209962Smm pw = getpwnam(cp); 2885209962Smm if (pw == NULL) 2886209962Smm return (ENOENT); 2887209962Smm *ridp = pw->pw_uid; 2888209962Smm } else { 2889209962Smm struct group *gr; 2890209962Smm gr = getgrnam(cp); 2891209962Smm if (gr == NULL) 2892209962Smm return (ENOENT); 2893209962Smm *ridp = gr->gr_gid; 2894209962Smm } 2895209962Smm } else { 2896209962Smm /* It's a user/group ID (eg "12345"). */ 2897209962Smm uid_t id = strtoul(cp, &end, 10); 2898209962Smm idmap_rid_t rid; 2899209962Smm char *mapdomain; 2900209962Smm 2901209962Smm if (*end != '\0') 2902209962Smm return (EINVAL); 2903209962Smm if (id > MAXUID) { 2904209962Smm /* It's an ephemeral ID. */ 2905209962Smm if (idmap_id_to_numeric_domain_rid(id, isuser, 2906209962Smm &mapdomain, &rid) != 0) 2907209962Smm return (ENOENT); 2908209962Smm (void) strlcpy(domain, mapdomain, domainlen); 2909209962Smm *ridp = rid; 2910209962Smm } else { 2911209962Smm *ridp = id; 2912209962Smm } 2913209962Smm } 2914209962Smm 2915209962Smm ASSERT3P(numericsid, ==, NULL); 2916209962Smm return (0); 2917209962Smm} 2918209962Smm 2919209962Smmstatic int 2920209962Smmzfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2921209962Smm uint64_t *propvalue, zfs_userquota_prop_t *typep) 2922209962Smm{ 2923209962Smm int err; 2924209962Smm zfs_cmd_t zc = { 0 }; 2925209962Smm 2926228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2927209962Smm 2928209962Smm err = userquota_propname_decode(propname, 2929209962Smm zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2930209962Smm typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2931209962Smm zc.zc_objset_type = *typep; 2932209962Smm if (err) 2933209962Smm return (err); 2934209962Smm 2935209962Smm err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 2936209962Smm if (err) 2937209962Smm return (err); 2938209962Smm 2939209962Smm *propvalue = zc.zc_cookie; 2940209962Smm return (0); 2941209962Smm} 2942209962Smm 2943209962Smmint 2944209962Smmzfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2945209962Smm uint64_t *propvalue) 2946209962Smm{ 2947209962Smm zfs_userquota_prop_t type; 2948209962Smm 2949209962Smm return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2950209962Smm &type)); 2951209962Smm} 2952209962Smm 2953209962Smmint 2954209962Smmzfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2955209962Smm char *propbuf, int proplen, boolean_t literal) 2956209962Smm{ 2957209962Smm int err; 2958209962Smm uint64_t propvalue; 2959209962Smm zfs_userquota_prop_t type; 2960209962Smm 2961209962Smm err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2962209962Smm &type); 2963209962Smm 2964209962Smm if (err) 2965209962Smm return (err); 2966209962Smm 2967209962Smm if (literal) { 2968209962Smm (void) snprintf(propbuf, proplen, "%llu", propvalue); 2969209962Smm } else if (propvalue == 0 && 2970209962Smm (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 2971209962Smm (void) strlcpy(propbuf, "none", proplen); 2972209962Smm } else { 2973209962Smm zfs_nicenum(propvalue, propbuf, proplen); 2974209962Smm } 2975209962Smm return (0); 2976209962Smm} 2977209962Smm 2978228103Smmint 2979228103Smmzfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname, 2980228103Smm uint64_t *propvalue) 2981168404Spjd{ 2982228103Smm int err; 2983228103Smm zfs_cmd_t zc = { 0 }; 2984228103Smm const char *snapname; 2985168404Spjd 2986228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2987168404Spjd 2988228103Smm snapname = strchr(propname, '@') + 1; 2989228103Smm if (strchr(snapname, '@')) { 2990228103Smm (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 2991228103Smm } else { 2992228103Smm /* snapname is the short name, append it to zhp's fsname */ 2993228103Smm char *cp; 2994209962Smm 2995228103Smm (void) strlcpy(zc.zc_value, zhp->zfs_name, 2996228103Smm sizeof (zc.zc_value)); 2997228103Smm cp = strchr(zc.zc_value, '@'); 2998228103Smm if (cp != NULL) 2999228103Smm *cp = '\0'; 3000228103Smm (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value)); 3001228103Smm (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value)); 3002228103Smm } 3003209962Smm 3004228103Smm err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc); 3005228103Smm if (err) 3006228103Smm return (err); 3007228103Smm 3008228103Smm *propvalue = zc.zc_cookie; 3009228103Smm return (0); 3010209962Smm} 3011209962Smm 3012168404Spjdint 3013228103Smmzfs_prop_get_written(zfs_handle_t *zhp, const char *propname, 3014228103Smm char *propbuf, int proplen, boolean_t literal) 3015168404Spjd{ 3016228103Smm int err; 3017228103Smm uint64_t propvalue; 3018168404Spjd 3019228103Smm err = zfs_prop_get_written_int(zhp, propname, &propvalue); 3020185029Spjd 3021228103Smm if (err) 3022228103Smm return (err); 3023209962Smm 3024228103Smm if (literal) { 3025228103Smm (void) snprintf(propbuf, proplen, "%llu", propvalue); 3026228103Smm } else { 3027228103Smm zfs_nicenum(propvalue, propbuf, proplen); 3028168404Spjd } 3029228103Smm return (0); 3030168404Spjd} 3031168404Spjd 3032168404Spjd/* 3033228103Smm * Returns the name of the given zfs handle. 3034168404Spjd */ 3035228103Smmconst char * 3036228103Smmzfs_get_name(const zfs_handle_t *zhp) 3037168404Spjd{ 3038228103Smm return (zhp->zfs_name); 3039228103Smm} 3040168404Spjd 3041228103Smm/* 3042307106Smav * Returns the name of the parent pool for the given zfs handle. 3043307106Smav */ 3044307106Smavconst char * 3045307106Smavzfs_get_pool_name(const zfs_handle_t *zhp) 3046307106Smav{ 3047307106Smav return (zhp->zpool_hdl->zpool_name); 3048307106Smav} 3049307106Smav 3050307106Smav/* 3051228103Smm * Returns the type of the given zfs handle. 3052228103Smm */ 3053228103Smmzfs_type_t 3054228103Smmzfs_get_type(const zfs_handle_t *zhp) 3055228103Smm{ 3056228103Smm return (zhp->zfs_type); 3057168404Spjd} 3058168404Spjd 3059168404Spjd/* 3060219089Spjd * Is one dataset name a child dataset of another? 3061219089Spjd * 3062219089Spjd * Needs to handle these cases: 3063219089Spjd * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 3064219089Spjd * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 3065219089Spjd * Descendant? No. No. No. Yes. 3066219089Spjd */ 3067219089Spjdstatic boolean_t 3068219089Spjdis_descendant(const char *ds1, const char *ds2) 3069219089Spjd{ 3070219089Spjd size_t d1len = strlen(ds1); 3071219089Spjd 3072219089Spjd /* ds2 can't be a descendant if it's smaller */ 3073219089Spjd if (strlen(ds2) < d1len) 3074219089Spjd return (B_FALSE); 3075219089Spjd 3076219089Spjd /* otherwise, compare strings and verify that there's a '/' char */ 3077219089Spjd return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 3078219089Spjd} 3079219089Spjd 3080219089Spjd/* 3081168404Spjd * Given a complete name, return just the portion that refers to the parent. 3082228103Smm * Will return -1 if there is no parent (path is just the name of the 3083228103Smm * pool). 3084168404Spjd */ 3085168404Spjdstatic int 3086168404Spjdparent_name(const char *path, char *buf, size_t buflen) 3087168404Spjd{ 3088228103Smm char *slashp; 3089168404Spjd 3090228103Smm (void) strlcpy(buf, path, buflen); 3091228103Smm 3092228103Smm if ((slashp = strrchr(buf, '/')) == NULL) 3093168404Spjd return (-1); 3094228103Smm *slashp = '\0'; 3095168404Spjd 3096168404Spjd return (0); 3097168404Spjd} 3098168404Spjd 3099168404Spjd/* 3100185029Spjd * If accept_ancestor is false, then check to make sure that the given path has 3101185029Spjd * a parent, and that it exists. If accept_ancestor is true, then find the 3102185029Spjd * closest existing ancestor for the given path. In prefixlen return the 3103185029Spjd * length of already existing prefix of the given path. We also fetch the 3104185029Spjd * 'zoned' property, which is used to validate property settings when creating 3105185029Spjd * new datasets. 3106168404Spjd */ 3107168404Spjdstatic int 3108185029Spjdcheck_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 3109185029Spjd boolean_t accept_ancestor, int *prefixlen) 3110168404Spjd{ 3111168404Spjd zfs_cmd_t zc = { 0 }; 3112307108Smav char parent[ZFS_MAX_DATASET_NAME_LEN]; 3113168404Spjd char *slash; 3114168404Spjd zfs_handle_t *zhp; 3115168404Spjd char errbuf[1024]; 3116219089Spjd uint64_t is_zoned; 3117168404Spjd 3118209962Smm (void) snprintf(errbuf, sizeof (errbuf), 3119209962Smm dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 3120168404Spjd 3121168404Spjd /* get parent, and check to see if this is just a pool */ 3122168404Spjd if (parent_name(path, parent, sizeof (parent)) != 0) { 3123168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3124168404Spjd "missing dataset name")); 3125168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3126168404Spjd } 3127168404Spjd 3128168404Spjd /* check to see if the pool exists */ 3129168404Spjd if ((slash = strchr(parent, '/')) == NULL) 3130168404Spjd slash = parent + strlen(parent); 3131168404Spjd (void) strncpy(zc.zc_name, parent, slash - parent); 3132168404Spjd zc.zc_name[slash - parent] = '\0'; 3133168404Spjd if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 3134168404Spjd errno == ENOENT) { 3135168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3136168404Spjd "no such pool '%s'"), zc.zc_name); 3137168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3138168404Spjd } 3139168404Spjd 3140168404Spjd /* check to see if the parent dataset exists */ 3141185029Spjd while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 3142185029Spjd if (errno == ENOENT && accept_ancestor) { 3143185029Spjd /* 3144185029Spjd * Go deeper to find an ancestor, give up on top level. 3145185029Spjd */ 3146185029Spjd if (parent_name(parent, parent, sizeof (parent)) != 0) { 3147185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3148185029Spjd "no such pool '%s'"), zc.zc_name); 3149185029Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3150185029Spjd } 3151185029Spjd } else if (errno == ENOENT) { 3152168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3153168404Spjd "parent does not exist")); 3154168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3155185029Spjd } else 3156168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 3157168404Spjd } 3158168404Spjd 3159219089Spjd is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 3160219089Spjd if (zoned != NULL) 3161219089Spjd *zoned = is_zoned; 3162219089Spjd 3163168404Spjd /* we are in a non-global zone, but parent is in the global zone */ 3164219089Spjd if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 3165168404Spjd (void) zfs_standard_error(hdl, EPERM, errbuf); 3166168404Spjd zfs_close(zhp); 3167168404Spjd return (-1); 3168168404Spjd } 3169168404Spjd 3170168404Spjd /* make sure parent is a filesystem */ 3171168404Spjd if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 3172168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3173168404Spjd "parent is not a filesystem")); 3174168404Spjd (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 3175168404Spjd zfs_close(zhp); 3176168404Spjd return (-1); 3177168404Spjd } 3178168404Spjd 3179168404Spjd zfs_close(zhp); 3180185029Spjd if (prefixlen != NULL) 3181185029Spjd *prefixlen = strlen(parent); 3182168404Spjd return (0); 3183168404Spjd} 3184168404Spjd 3185168404Spjd/* 3186185029Spjd * Finds whether the dataset of the given type(s) exists. 3187185029Spjd */ 3188185029Spjdboolean_t 3189185029Spjdzfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 3190185029Spjd{ 3191185029Spjd zfs_handle_t *zhp; 3192185029Spjd 3193185029Spjd if (!zfs_validate_name(hdl, path, types, B_FALSE)) 3194185029Spjd return (B_FALSE); 3195185029Spjd 3196185029Spjd /* 3197185029Spjd * Try to get stats for the dataset, which will tell us if it exists. 3198185029Spjd */ 3199185029Spjd if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 3200185029Spjd int ds_type = zhp->zfs_type; 3201185029Spjd 3202185029Spjd zfs_close(zhp); 3203185029Spjd if (types & ds_type) 3204185029Spjd return (B_TRUE); 3205185029Spjd } 3206185029Spjd return (B_FALSE); 3207185029Spjd} 3208185029Spjd 3209185029Spjd/* 3210185029Spjd * Given a path to 'target', create all the ancestors between 3211185029Spjd * the prefixlen portion of the path, and the target itself. 3212185029Spjd * Fail if the initial prefixlen-ancestor does not already exist. 3213185029Spjd */ 3214185029Spjdint 3215185029Spjdcreate_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 3216185029Spjd{ 3217185029Spjd zfs_handle_t *h; 3218185029Spjd char *cp; 3219185029Spjd const char *opname; 3220185029Spjd 3221185029Spjd /* make sure prefix exists */ 3222185029Spjd cp = target + prefixlen; 3223185029Spjd if (*cp != '/') { 3224185029Spjd assert(strchr(cp, '/') == NULL); 3225185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 3226185029Spjd } else { 3227185029Spjd *cp = '\0'; 3228185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 3229185029Spjd *cp = '/'; 3230185029Spjd } 3231185029Spjd if (h == NULL) 3232185029Spjd return (-1); 3233185029Spjd zfs_close(h); 3234185029Spjd 3235185029Spjd /* 3236185029Spjd * Attempt to create, mount, and share any ancestor filesystems, 3237185029Spjd * up to the prefixlen-long one. 3238185029Spjd */ 3239185029Spjd for (cp = target + prefixlen + 1; 3240307050Smav (cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) { 3241185029Spjd 3242185029Spjd *cp = '\0'; 3243185029Spjd 3244185029Spjd h = make_dataset_handle(hdl, target); 3245185029Spjd if (h) { 3246185029Spjd /* it already exists, nothing to do here */ 3247185029Spjd zfs_close(h); 3248185029Spjd continue; 3249185029Spjd } 3250185029Spjd 3251185029Spjd if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 3252185029Spjd NULL) != 0) { 3253185029Spjd opname = dgettext(TEXT_DOMAIN, "create"); 3254185029Spjd goto ancestorerr; 3255185029Spjd } 3256185029Spjd 3257185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 3258185029Spjd if (h == NULL) { 3259185029Spjd opname = dgettext(TEXT_DOMAIN, "open"); 3260185029Spjd goto ancestorerr; 3261185029Spjd } 3262185029Spjd 3263185029Spjd if (zfs_mount(h, NULL, 0) != 0) { 3264185029Spjd opname = dgettext(TEXT_DOMAIN, "mount"); 3265185029Spjd goto ancestorerr; 3266185029Spjd } 3267185029Spjd 3268185029Spjd if (zfs_share(h) != 0) { 3269185029Spjd opname = dgettext(TEXT_DOMAIN, "share"); 3270185029Spjd goto ancestorerr; 3271185029Spjd } 3272185029Spjd 3273185029Spjd zfs_close(h); 3274185029Spjd } 3275185029Spjd 3276185029Spjd return (0); 3277185029Spjd 3278185029Spjdancestorerr: 3279185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3280185029Spjd "failed to %s ancestor '%s'"), opname, target); 3281185029Spjd return (-1); 3282185029Spjd} 3283185029Spjd 3284185029Spjd/* 3285185029Spjd * Creates non-existing ancestors of the given path. 3286185029Spjd */ 3287185029Spjdint 3288185029Spjdzfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 3289185029Spjd{ 3290185029Spjd int prefix; 3291185029Spjd char *path_copy; 3292307107Smav int rc = 0; 3293185029Spjd 3294219089Spjd if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 3295185029Spjd return (-1); 3296185029Spjd 3297185029Spjd if ((path_copy = strdup(path)) != NULL) { 3298185029Spjd rc = create_parents(hdl, path_copy, prefix); 3299185029Spjd free(path_copy); 3300185029Spjd } 3301185029Spjd if (path_copy == NULL || rc != 0) 3302185029Spjd return (-1); 3303185029Spjd 3304185029Spjd return (0); 3305185029Spjd} 3306185029Spjd 3307185029Spjd/* 3308168404Spjd * Create a new filesystem or volume. 3309168404Spjd */ 3310168404Spjdint 3311168404Spjdzfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 3312168404Spjd nvlist_t *props) 3313168404Spjd{ 3314168404Spjd int ret; 3315168404Spjd uint64_t size = 0; 3316168404Spjd uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 3317168404Spjd char errbuf[1024]; 3318168404Spjd uint64_t zoned; 3319298472Savg enum lzc_dataset_type ost; 3320321576Smav zpool_handle_t *zpool_handle; 3321168404Spjd 3322168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3323168404Spjd "cannot create '%s'"), path); 3324168404Spjd 3325168404Spjd /* validate the path, taking care to note the extended error message */ 3326185029Spjd if (!zfs_validate_name(hdl, path, type, B_TRUE)) 3327168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3328168404Spjd 3329168404Spjd /* validate parents exist */ 3330185029Spjd if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 3331168404Spjd return (-1); 3332168404Spjd 3333168404Spjd /* 3334168404Spjd * The failure modes when creating a dataset of a different type over 3335168404Spjd * one that already exists is a little strange. In particular, if you 3336168404Spjd * try to create a dataset on top of an existing dataset, the ioctl() 3337168404Spjd * will return ENOENT, not EEXIST. To prevent this from happening, we 3338168404Spjd * first try to see if the dataset exists. 3339168404Spjd */ 3340248571Smm if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) { 3341168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3342168404Spjd "dataset already exists")); 3343168404Spjd return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3344168404Spjd } 3345168404Spjd 3346168404Spjd if (type == ZFS_TYPE_VOLUME) 3347298472Savg ost = LZC_DATSET_TYPE_ZVOL; 3348168404Spjd else 3349298472Savg ost = LZC_DATSET_TYPE_ZFS; 3350168404Spjd 3351289500Smav /* open zpool handle for prop validation */ 3352307108Smav char pool_path[ZFS_MAX_DATASET_NAME_LEN]; 3353289500Smav (void) strlcpy(pool_path, path, sizeof (pool_path)); 3354289500Smav 3355289500Smav /* truncate pool_path at first slash */ 3356289500Smav char *p = strchr(pool_path, '/'); 3357289500Smav if (p != NULL) 3358289500Smav *p = '\0'; 3359289500Smav 3360321576Smav if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL) 3361321576Smav return (-1); 3362289500Smav 3363185029Spjd if (props && (props = zfs_valid_proplist(hdl, type, props, 3364289500Smav zoned, NULL, zpool_handle, errbuf)) == 0) { 3365289500Smav zpool_close(zpool_handle); 3366168404Spjd return (-1); 3367289500Smav } 3368289500Smav zpool_close(zpool_handle); 3369168404Spjd 3370168404Spjd if (type == ZFS_TYPE_VOLUME) { 3371168404Spjd /* 3372168404Spjd * If we are creating a volume, the size and block size must 3373168404Spjd * satisfy a few restraints. First, the blocksize must be a 3374168404Spjd * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 3375168404Spjd * volsize must be a multiple of the block size, and cannot be 3376168404Spjd * zero. 3377168404Spjd */ 3378168404Spjd if (props == NULL || nvlist_lookup_uint64(props, 3379168404Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 3380168404Spjd nvlist_free(props); 3381168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3382168404Spjd "missing volume size")); 3383168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3384168404Spjd } 3385168404Spjd 3386168404Spjd if ((ret = nvlist_lookup_uint64(props, 3387168404Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 3388168404Spjd &blocksize)) != 0) { 3389168404Spjd if (ret == ENOENT) { 3390168404Spjd blocksize = zfs_prop_default_numeric( 3391168404Spjd ZFS_PROP_VOLBLOCKSIZE); 3392168404Spjd } else { 3393168404Spjd nvlist_free(props); 3394168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3395168404Spjd "missing volume block size")); 3396168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3397168404Spjd } 3398168404Spjd } 3399168404Spjd 3400168404Spjd if (size == 0) { 3401168404Spjd nvlist_free(props); 3402168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3403168404Spjd "volume size cannot be zero")); 3404168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3405168404Spjd } 3406168404Spjd 3407168404Spjd if (size % blocksize != 0) { 3408168404Spjd nvlist_free(props); 3409168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3410168404Spjd "volume size must be a multiple of volume block " 3411168404Spjd "size")); 3412168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3413168404Spjd } 3414168404Spjd } 3415168404Spjd 3416248571Smm /* create the dataset */ 3417248571Smm ret = lzc_create(path, ost, props); 3418168404Spjd nvlist_free(props); 3419168404Spjd 3420168404Spjd /* check for failure */ 3421168404Spjd if (ret != 0) { 3422307108Smav char parent[ZFS_MAX_DATASET_NAME_LEN]; 3423168404Spjd (void) parent_name(path, parent, sizeof (parent)); 3424168404Spjd 3425168404Spjd switch (errno) { 3426168404Spjd case ENOENT: 3427168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3428168404Spjd "no such parent '%s'"), parent); 3429168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3430168404Spjd 3431168404Spjd case EINVAL: 3432168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3433168404Spjd "parent '%s' is not a filesystem"), parent); 3434168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3435168404Spjd 3436185029Spjd case ENOTSUP: 3437185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3438185029Spjd "pool must be upgraded to set this " 3439185029Spjd "property or value")); 3440185029Spjd return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3441168404Spjd#ifdef _ILP32 3442168404Spjd case EOVERFLOW: 3443168404Spjd /* 3444168404Spjd * This platform can't address a volume this big. 3445168404Spjd */ 3446168404Spjd if (type == ZFS_TYPE_VOLUME) 3447168404Spjd return (zfs_error(hdl, EZFS_VOLTOOBIG, 3448168404Spjd errbuf)); 3449168404Spjd#endif 3450168404Spjd /* FALLTHROUGH */ 3451168404Spjd default: 3452168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 3453168404Spjd } 3454168404Spjd } 3455168404Spjd 3456168404Spjd return (0); 3457168404Spjd} 3458168404Spjd 3459168404Spjd/* 3460168404Spjd * Destroys the given dataset. The caller must make sure that the filesystem 3461238422Smm * isn't mounted, and that there are no active dependents. If the file system 3462238422Smm * does not exist this function does nothing. 3463168404Spjd */ 3464168404Spjdint 3465219089Spjdzfs_destroy(zfs_handle_t *zhp, boolean_t defer) 3466168404Spjd{ 3467168404Spjd zfs_cmd_t zc = { 0 }; 3468168404Spjd 3469260183Sdelphij if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) { 3470260183Sdelphij nvlist_t *nv = fnvlist_alloc(); 3471260183Sdelphij fnvlist_add_boolean(nv, zhp->zfs_name); 3472260183Sdelphij int error = lzc_destroy_bookmarks(nv, NULL); 3473260183Sdelphij fnvlist_free(nv); 3474260183Sdelphij if (error != 0) { 3475260183Sdelphij return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3476260183Sdelphij dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 3477260183Sdelphij zhp->zfs_name)); 3478260183Sdelphij } 3479260183Sdelphij return (0); 3480260183Sdelphij } 3481260183Sdelphij 3482168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3483168404Spjd 3484168404Spjd if (ZFS_IS_VOLUME(zhp)) { 3485168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3486168404Spjd } else { 3487168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3488168404Spjd } 3489168404Spjd 3490219089Spjd zc.zc_defer_destroy = defer; 3491238422Smm if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 && 3492238422Smm errno != ENOENT) { 3493168404Spjd return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3494168404Spjd dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 3495168404Spjd zhp->zfs_name)); 3496168404Spjd } 3497168404Spjd 3498168404Spjd remove_mountpoint(zhp); 3499168404Spjd 3500168404Spjd return (0); 3501168404Spjd} 3502168404Spjd 3503168404Spjdstruct destroydata { 3504228103Smm nvlist_t *nvl; 3505228103Smm const char *snapname; 3506168404Spjd}; 3507168404Spjd 3508168404Spjdstatic int 3509219089Spjdzfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 3510168404Spjd{ 3511168404Spjd struct destroydata *dd = arg; 3512307108Smav char name[ZFS_MAX_DATASET_NAME_LEN]; 3513219089Spjd int rv = 0; 3514168404Spjd 3515228103Smm (void) snprintf(name, sizeof (name), 3516228103Smm "%s@%s", zhp->zfs_name, dd->snapname); 3517168404Spjd 3518251646Sdelphij if (lzc_exists(name)) 3519228103Smm verify(nvlist_add_boolean(dd->nvl, name) == 0); 3520168404Spjd 3521228103Smm rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd); 3522228103Smm zfs_close(zhp); 3523168404Spjd return (rv); 3524168404Spjd} 3525168404Spjd 3526168404Spjd/* 3527168404Spjd * Destroys all snapshots with the given name in zhp & descendants. 3528168404Spjd */ 3529168404Spjdint 3530219089Spjdzfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 3531168404Spjd{ 3532168404Spjd int ret; 3533168404Spjd struct destroydata dd = { 0 }; 3534168404Spjd 3535168404Spjd dd.snapname = snapname; 3536228103Smm verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0); 3537228103Smm (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd); 3538168404Spjd 3539251646Sdelphij if (nvlist_empty(dd.nvl)) { 3540228103Smm ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 3541168404Spjd dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 3542228103Smm zhp->zfs_name, snapname); 3543228103Smm } else { 3544248571Smm ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer); 3545168404Spjd } 3546228103Smm nvlist_free(dd.nvl); 3547228103Smm return (ret); 3548228103Smm} 3549168404Spjd 3550228103Smm/* 3551248571Smm * Destroys all the snapshots named in the nvlist. 3552228103Smm */ 3553228103Smmint 3554248571Smmzfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer) 3555228103Smm{ 3556228103Smm int ret; 3557307105Smav nvlist_t *errlist = NULL; 3558228103Smm 3559248571Smm ret = lzc_destroy_snaps(snaps, defer, &errlist); 3560168404Spjd 3561307105Smav if (ret == 0) { 3562307105Smav nvlist_free(errlist); 3563248571Smm return (0); 3564307105Smav } 3565248571Smm 3566251646Sdelphij if (nvlist_empty(errlist)) { 3567168404Spjd char errbuf[1024]; 3568248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3569248571Smm dgettext(TEXT_DOMAIN, "cannot destroy snapshots")); 3570168404Spjd 3571248571Smm ret = zfs_standard_error(hdl, ret, errbuf); 3572248571Smm } 3573248571Smm for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL); 3574248571Smm pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) { 3575248571Smm char errbuf[1024]; 3576248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3577248571Smm dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"), 3578248571Smm nvpair_name(pair)); 3579168404Spjd 3580248571Smm switch (fnvpair_value_int32(pair)) { 3581168404Spjd case EEXIST: 3582248571Smm zfs_error_aux(hdl, 3583248571Smm dgettext(TEXT_DOMAIN, "snapshot is cloned")); 3584248571Smm ret = zfs_error(hdl, EZFS_EXISTS, errbuf); 3585248571Smm break; 3586168404Spjd default: 3587248571Smm ret = zfs_standard_error(hdl, errno, errbuf); 3588248571Smm break; 3589168404Spjd } 3590168404Spjd } 3591168404Spjd 3592307105Smav nvlist_free(errlist); 3593248571Smm return (ret); 3594168404Spjd} 3595168404Spjd 3596168404Spjd/* 3597168404Spjd * Clones the given dataset. The target must be of the same type as the source. 3598168404Spjd */ 3599168404Spjdint 3600168404Spjdzfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3601168404Spjd{ 3602307108Smav char parent[ZFS_MAX_DATASET_NAME_LEN]; 3603168404Spjd int ret; 3604168404Spjd char errbuf[1024]; 3605168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3606168404Spjd uint64_t zoned; 3607168404Spjd 3608168404Spjd assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3609168404Spjd 3610168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3611168404Spjd "cannot create '%s'"), target); 3612168404Spjd 3613228103Smm /* validate the target/clone name */ 3614185029Spjd if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 3615168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3616168404Spjd 3617168404Spjd /* validate parents exist */ 3618185029Spjd if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3619168404Spjd return (-1); 3620168404Spjd 3621168404Spjd (void) parent_name(target, parent, sizeof (parent)); 3622168404Spjd 3623168404Spjd /* do the clone */ 3624168404Spjd 3625168404Spjd if (props) { 3626248571Smm zfs_type_t type; 3627248571Smm if (ZFS_IS_VOLUME(zhp)) { 3628248571Smm type = ZFS_TYPE_VOLUME; 3629248571Smm } else { 3630248571Smm type = ZFS_TYPE_FILESYSTEM; 3631248571Smm } 3632185029Spjd if ((props = zfs_valid_proplist(hdl, type, props, zoned, 3633289500Smav zhp, zhp->zpool_hdl, errbuf)) == NULL) 3634168404Spjd return (-1); 3635168404Spjd } 3636168404Spjd 3637248571Smm ret = lzc_clone(target, zhp->zfs_name, props); 3638248571Smm nvlist_free(props); 3639168404Spjd 3640168404Spjd if (ret != 0) { 3641168404Spjd switch (errno) { 3642168404Spjd 3643168404Spjd case ENOENT: 3644168404Spjd /* 3645168404Spjd * The parent doesn't exist. We should have caught this 3646168404Spjd * above, but there may a race condition that has since 3647168404Spjd * destroyed the parent. 3648168404Spjd * 3649168404Spjd * At this point, we don't know whether it's the source 3650168404Spjd * that doesn't exist anymore, or whether the target 3651168404Spjd * dataset doesn't exist. 3652168404Spjd */ 3653168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3654168404Spjd "no such parent '%s'"), parent); 3655168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3656168404Spjd 3657168404Spjd case EXDEV: 3658168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3659168404Spjd "source and target pools differ")); 3660168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 3661168404Spjd errbuf)); 3662168404Spjd 3663168404Spjd default: 3664168404Spjd return (zfs_standard_error(zhp->zfs_hdl, errno, 3665168404Spjd errbuf)); 3666168404Spjd } 3667168404Spjd } 3668168404Spjd 3669168404Spjd return (ret); 3670168404Spjd} 3671168404Spjd 3672168404Spjd/* 3673168404Spjd * Promotes the given clone fs to be the clone parent. 3674168404Spjd */ 3675168404Spjdint 3676168404Spjdzfs_promote(zfs_handle_t *zhp) 3677168404Spjd{ 3678168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3679321577Smav char snapname[ZFS_MAX_DATASET_NAME_LEN]; 3680168404Spjd int ret; 3681168404Spjd char errbuf[1024]; 3682168404Spjd 3683168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3684168404Spjd "cannot promote '%s'"), zhp->zfs_name); 3685168404Spjd 3686168404Spjd if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 3687168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3688168404Spjd "snapshots can not be promoted")); 3689168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3690168404Spjd } 3691168404Spjd 3692321577Smav if (zhp->zfs_dmustats.dds_origin[0] == '\0') { 3693168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3694168404Spjd "not a cloned filesystem")); 3695168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3696168404Spjd } 3697168404Spjd 3698321577Smav ret = lzc_promote(zhp->zfs_name, snapname, sizeof (snapname)); 3699168404Spjd 3700168404Spjd if (ret != 0) { 3701321577Smav switch (ret) { 3702168404Spjd case EEXIST: 3703219089Spjd /* There is a conflicting snapshot name. */ 3704168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3705219089Spjd "conflicting snapshot '%s' from parent '%s'"), 3706321577Smav snapname, zhp->zfs_dmustats.dds_origin); 3707168404Spjd return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3708168404Spjd 3709168404Spjd default: 3710321577Smav return (zfs_standard_error(hdl, ret, errbuf)); 3711168404Spjd } 3712168404Spjd } 3713168404Spjd return (ret); 3714168404Spjd} 3715168404Spjd 3716248571Smmtypedef struct snapdata { 3717248571Smm nvlist_t *sd_nvl; 3718248571Smm const char *sd_snapname; 3719248571Smm} snapdata_t; 3720248571Smm 3721248571Smmstatic int 3722248571Smmzfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 3723248571Smm{ 3724248571Smm snapdata_t *sd = arg; 3725307108Smav char name[ZFS_MAX_DATASET_NAME_LEN]; 3726248571Smm int rv = 0; 3727248571Smm 3728253819Sdelphij if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) { 3729253819Sdelphij (void) snprintf(name, sizeof (name), 3730253819Sdelphij "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 3731248571Smm 3732253819Sdelphij fnvlist_add_boolean(sd->sd_nvl, name); 3733248571Smm 3734253819Sdelphij rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 3735253819Sdelphij } 3736248571Smm zfs_close(zhp); 3737253819Sdelphij 3738248571Smm return (rv); 3739248571Smm} 3740248571Smm 3741168404Spjd/* 3742248571Smm * Creates snapshots. The keys in the snaps nvlist are the snapshots to be 3743248571Smm * created. 3744168404Spjd */ 3745168404Spjdint 3746248571Smmzfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props) 3747168404Spjd{ 3748168404Spjd int ret; 3749168404Spjd char errbuf[1024]; 3750248571Smm nvpair_t *elem; 3751248571Smm nvlist_t *errors; 3752168404Spjd 3753168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3754248571Smm "cannot create snapshots ")); 3755168404Spjd 3756248571Smm elem = NULL; 3757248571Smm while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) { 3758248571Smm const char *snapname = nvpair_name(elem); 3759168404Spjd 3760248571Smm /* validate the target name */ 3761248571Smm if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT, 3762248571Smm B_TRUE)) { 3763248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3764248571Smm dgettext(TEXT_DOMAIN, 3765248571Smm "cannot create snapshot '%s'"), snapname); 3766248571Smm return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3767248571Smm } 3768248571Smm } 3769185029Spjd 3770289500Smav /* 3771289500Smav * get pool handle for prop validation. assumes all snaps are in the 3772289500Smav * same pool, as does lzc_snapshot (below). 3773289500Smav */ 3774307108Smav char pool[ZFS_MAX_DATASET_NAME_LEN]; 3775289500Smav elem = nvlist_next_nvpair(snaps, NULL); 3776289500Smav (void) strlcpy(pool, nvpair_name(elem), sizeof (pool)); 3777289500Smav pool[strcspn(pool, "/@")] = '\0'; 3778289500Smav zpool_handle_t *zpool_hdl = zpool_open(hdl, pool); 3779289500Smav 3780248571Smm if (props != NULL && 3781248571Smm (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3782289500Smav props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) { 3783289500Smav zpool_close(zpool_hdl); 3784248571Smm return (-1); 3785248571Smm } 3786289500Smav zpool_close(zpool_hdl); 3787248571Smm 3788248571Smm ret = lzc_snapshot(snaps, props, &errors); 3789248571Smm 3790248571Smm if (ret != 0) { 3791248571Smm boolean_t printed = B_FALSE; 3792248571Smm for (elem = nvlist_next_nvpair(errors, NULL); 3793248571Smm elem != NULL; 3794248571Smm elem = nvlist_next_nvpair(errors, elem)) { 3795248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3796248571Smm dgettext(TEXT_DOMAIN, 3797248571Smm "cannot create snapshot '%s'"), nvpair_name(elem)); 3798248571Smm (void) zfs_standard_error(hdl, 3799248571Smm fnvpair_value_int32(elem), errbuf); 3800248571Smm printed = B_TRUE; 3801185029Spjd } 3802248571Smm if (!printed) { 3803248571Smm switch (ret) { 3804248571Smm case EXDEV: 3805248571Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3806248571Smm "multiple snapshots of same " 3807248571Smm "fs not allowed")); 3808248571Smm (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3809185029Spjd 3810248571Smm break; 3811248571Smm default: 3812248571Smm (void) zfs_standard_error(hdl, ret, errbuf); 3813248571Smm } 3814248571Smm } 3815185029Spjd } 3816185029Spjd 3817248571Smm nvlist_free(props); 3818248571Smm nvlist_free(errors); 3819248571Smm return (ret); 3820248571Smm} 3821168404Spjd 3822248571Smmint 3823248571Smmzfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3824248571Smm nvlist_t *props) 3825248571Smm{ 3826248571Smm int ret; 3827248571Smm snapdata_t sd = { 0 }; 3828307108Smav char fsname[ZFS_MAX_DATASET_NAME_LEN]; 3829248571Smm char *cp; 3830248571Smm zfs_handle_t *zhp; 3831248571Smm char errbuf[1024]; 3832248571Smm 3833248571Smm (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3834248571Smm "cannot snapshot %s"), path); 3835248571Smm 3836248571Smm if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 3837248571Smm return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3838248571Smm 3839248571Smm (void) strlcpy(fsname, path, sizeof (fsname)); 3840248571Smm cp = strchr(fsname, '@'); 3841248571Smm *cp = '\0'; 3842248571Smm sd.sd_snapname = cp + 1; 3843248571Smm 3844248571Smm if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | 3845168404Spjd ZFS_TYPE_VOLUME)) == NULL) { 3846168404Spjd return (-1); 3847168404Spjd } 3848168404Spjd 3849248571Smm verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0); 3850248571Smm if (recursive) { 3851248571Smm (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd); 3852248571Smm } else { 3853248571Smm fnvlist_add_boolean(sd.sd_nvl, path); 3854168404Spjd } 3855168404Spjd 3856248571Smm ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props); 3857248571Smm nvlist_free(sd.sd_nvl); 3858168404Spjd zfs_close(zhp); 3859168404Spjd return (ret); 3860168404Spjd} 3861168404Spjd 3862168404Spjd/* 3863168404Spjd * Destroy any more recent snapshots. We invoke this callback on any dependents 3864168404Spjd * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3865168404Spjd * is a dependent and we should just destroy it without checking the transaction 3866168404Spjd * group. 3867168404Spjd */ 3868168404Spjdtypedef struct rollback_data { 3869168404Spjd const char *cb_target; /* the snapshot */ 3870168404Spjd uint64_t cb_create; /* creation time reference */ 3871185029Spjd boolean_t cb_error; 3872185029Spjd boolean_t cb_force; 3873168404Spjd} rollback_data_t; 3874168404Spjd 3875168404Spjdstatic int 3876260183Sdelphijrollback_destroy_dependent(zfs_handle_t *zhp, void *data) 3877168404Spjd{ 3878168404Spjd rollback_data_t *cbp = data; 3879260183Sdelphij prop_changelist_t *clp; 3880168404Spjd 3881260183Sdelphij /* We must destroy this clone; first unmount it */ 3882260183Sdelphij clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3883260183Sdelphij cbp->cb_force ? MS_FORCE: 0); 3884260183Sdelphij if (clp == NULL || changelist_prefix(clp) != 0) { 3885260183Sdelphij cbp->cb_error = B_TRUE; 3886260183Sdelphij zfs_close(zhp); 3887260183Sdelphij return (0); 3888260183Sdelphij } 3889260183Sdelphij if (zfs_destroy(zhp, B_FALSE) != 0) 3890260183Sdelphij cbp->cb_error = B_TRUE; 3891260183Sdelphij else 3892260183Sdelphij changelist_remove(clp, zhp->zfs_name); 3893260183Sdelphij (void) changelist_postfix(clp); 3894260183Sdelphij changelist_free(clp); 3895168404Spjd 3896260183Sdelphij zfs_close(zhp); 3897260183Sdelphij return (0); 3898260183Sdelphij} 3899168404Spjd 3900260183Sdelphijstatic int 3901260183Sdelphijrollback_destroy(zfs_handle_t *zhp, void *data) 3902260183Sdelphij{ 3903260183Sdelphij rollback_data_t *cbp = data; 3904185029Spjd 3905260183Sdelphij if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { 3906260183Sdelphij cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 3907260183Sdelphij rollback_destroy_dependent, cbp); 3908260183Sdelphij 3909260183Sdelphij cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3910168404Spjd } 3911168404Spjd 3912168404Spjd zfs_close(zhp); 3913168404Spjd return (0); 3914168404Spjd} 3915168404Spjd 3916168404Spjd/* 3917168404Spjd * Given a dataset, rollback to a specific snapshot, discarding any 3918168404Spjd * data changes since then and making it the active dataset. 3919168404Spjd * 3920260183Sdelphij * Any snapshots and bookmarks more recent than the target are 3921260183Sdelphij * destroyed, along with their dependents (i.e. clones). 3922168404Spjd */ 3923168404Spjdint 3924185029Spjdzfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3925168404Spjd{ 3926168404Spjd rollback_data_t cb = { 0 }; 3927185029Spjd int err; 3928185029Spjd boolean_t restore_resv = 0; 3929307107Smav uint64_t old_volsize = 0, new_volsize; 3930185029Spjd zfs_prop_t resv_prop; 3931168404Spjd 3932185029Spjd assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3933185029Spjd zhp->zfs_type == ZFS_TYPE_VOLUME); 3934168404Spjd 3935168404Spjd /* 3936239774Smm * Destroy all recent snapshots and their dependents. 3937168404Spjd */ 3938185029Spjd cb.cb_force = force; 3939168404Spjd cb.cb_target = snap->zfs_name; 3940168404Spjd cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3941260183Sdelphij (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb); 3942260183Sdelphij (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb); 3943168404Spjd 3944185029Spjd if (cb.cb_error) 3945185029Spjd return (-1); 3946168404Spjd 3947168404Spjd /* 3948168404Spjd * Now that we have verified that the snapshot is the latest, 3949168404Spjd * rollback to the given snapshot. 3950168404Spjd */ 3951168404Spjd 3952185029Spjd if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 3953185029Spjd if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 3954185029Spjd return (-1); 3955185029Spjd old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 3956185029Spjd restore_resv = 3957185029Spjd (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 3958168404Spjd } 3959168404Spjd 3960168404Spjd /* 3961185029Spjd * We rely on zfs_iter_children() to verify that there are no 3962185029Spjd * newer snapshots for the given dataset. Therefore, we can 3963185029Spjd * simply pass the name on to the ioctl() call. There is still 3964185029Spjd * an unlikely race condition where the user has taken a 3965185029Spjd * snapshot since we verified that this was the most recent. 3966168404Spjd */ 3967254587Sdelphij err = lzc_rollback(zhp->zfs_name, NULL, 0); 3968254587Sdelphij if (err != 0) { 3969185029Spjd (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3970185029Spjd dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 3971185029Spjd zhp->zfs_name); 3972185029Spjd return (err); 3973185029Spjd } 3974168404Spjd 3975185029Spjd /* 3976185029Spjd * For volumes, if the pre-rollback volsize matched the pre- 3977185029Spjd * rollback reservation and the volsize has changed then set 3978185029Spjd * the reservation property to the post-rollback volsize. 3979185029Spjd * Make a new handle since the rollback closed the dataset. 3980185029Spjd */ 3981185029Spjd if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3982185029Spjd (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3983185029Spjd if (restore_resv) { 3984185029Spjd new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 3985185029Spjd if (old_volsize != new_volsize) 3986185029Spjd err = zfs_prop_set_int(zhp, resv_prop, 3987185029Spjd new_volsize); 3988185029Spjd } 3989185029Spjd zfs_close(zhp); 3990185029Spjd } 3991185029Spjd return (err); 3992168404Spjd} 3993168404Spjd 3994168404Spjd/* 3995168404Spjd * Renames the given dataset. 3996168404Spjd */ 3997168404Spjdint 3998240870Spjdzfs_rename(zfs_handle_t *zhp, const char *source, const char *target, 3999240870Spjd renameflags_t flags) 4000168404Spjd{ 4001307050Smav int ret = 0; 4002168404Spjd zfs_cmd_t zc = { 0 }; 4003168404Spjd char *delim; 4004168676Spjd prop_changelist_t *cl = NULL; 4005168676Spjd zfs_handle_t *zhrp = NULL; 4006168676Spjd char *parentname = NULL; 4007307108Smav char parent[ZFS_MAX_DATASET_NAME_LEN]; 4008226676Spjd char property[ZFS_MAXPROPLEN]; 4009168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4010168404Spjd char errbuf[1024]; 4011168404Spjd 4012168404Spjd /* if we have the same exact name, just return success */ 4013168404Spjd if (strcmp(zhp->zfs_name, target) == 0) 4014168404Spjd return (0); 4015168404Spjd 4016168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4017168404Spjd "cannot rename to '%s'"), target); 4018168404Spjd 4019240870Spjd if (source != NULL) { 4020240870Spjd /* 4021240870Spjd * This is recursive snapshots rename, put snapshot name 4022240870Spjd * (that might not exist) into zfs_name. 4023240870Spjd */ 4024240870Spjd assert(flags.recurse); 4025240870Spjd 4026240870Spjd (void) strlcat(zhp->zfs_name, "@", sizeof(zhp->zfs_name)); 4027240870Spjd (void) strlcat(zhp->zfs_name, source, sizeof(zhp->zfs_name)); 4028240870Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 4029240870Spjd } 4030240870Spjd 4031168404Spjd /* 4032168404Spjd * Make sure the target name is valid 4033168404Spjd */ 4034168404Spjd if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 4035168404Spjd if ((strchr(target, '@') == NULL) || 4036168404Spjd *target == '@') { 4037168404Spjd /* 4038168404Spjd * Snapshot target name is abbreviated, 4039168404Spjd * reconstruct full dataset name 4040168404Spjd */ 4041168404Spjd (void) strlcpy(parent, zhp->zfs_name, 4042168404Spjd sizeof (parent)); 4043168404Spjd delim = strchr(parent, '@'); 4044168404Spjd if (strchr(target, '@') == NULL) 4045168404Spjd *(++delim) = '\0'; 4046168404Spjd else 4047168404Spjd *delim = '\0'; 4048168404Spjd (void) strlcat(parent, target, sizeof (parent)); 4049168404Spjd target = parent; 4050168404Spjd } else { 4051168404Spjd /* 4052168404Spjd * Make sure we're renaming within the same dataset. 4053168404Spjd */ 4054168404Spjd delim = strchr(target, '@'); 4055168404Spjd if (strncmp(zhp->zfs_name, target, delim - target) 4056168404Spjd != 0 || zhp->zfs_name[delim - target] != '@') { 4057168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4058168404Spjd "snapshots must be part of same " 4059168404Spjd "dataset")); 4060168404Spjd return (zfs_error(hdl, EZFS_CROSSTARGET, 4061168404Spjd errbuf)); 4062168404Spjd } 4063168404Spjd } 4064185029Spjd if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 4065168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4066168404Spjd } else { 4067226705Spjd if (flags.recurse) { 4068168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4069168676Spjd "recursive rename must be a snapshot")); 4070168676Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4071168676Spjd } 4072168676Spjd 4073185029Spjd if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 4074168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4075168404Spjd 4076168404Spjd /* validate parents */ 4077219089Spjd if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 4078168404Spjd return (-1); 4079168404Spjd 4080168404Spjd /* make sure we're in the same pool */ 4081168404Spjd verify((delim = strchr(target, '/')) != NULL); 4082168404Spjd if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 4083168404Spjd zhp->zfs_name[delim - target] != '/') { 4084168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4085168404Spjd "datasets must be within same pool")); 4086168404Spjd return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 4087168404Spjd } 4088168404Spjd 4089168404Spjd /* new name cannot be a child of the current dataset name */ 4090219089Spjd if (is_descendant(zhp->zfs_name, target)) { 4091168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4092219089Spjd "New dataset name cannot be a descendant of " 4093168404Spjd "current dataset name")); 4094168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4095168404Spjd } 4096168404Spjd } 4097168404Spjd 4098168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4099168404Spjd dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 4100168404Spjd 4101168404Spjd if (getzoneid() == GLOBAL_ZONEID && 4102168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 4103168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4104168404Spjd "dataset is used in a non-global zone")); 4105168404Spjd return (zfs_error(hdl, EZFS_ZONED, errbuf)); 4106168404Spjd } 4107168404Spjd 4108226705Spjd /* 4109226705Spjd * Avoid unmounting file systems with mountpoint property set to 4110226705Spjd * 'legacy' or 'none' even if -u option is not given. 4111226705Spjd */ 4112226705Spjd if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM && 4113226705Spjd !flags.recurse && !flags.nounmount && 4114226705Spjd zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property, 4115226705Spjd sizeof (property), NULL, NULL, 0, B_FALSE) == 0 && 4116226705Spjd (strcmp(property, "legacy") == 0 || 4117226705Spjd strcmp(property, "none") == 0)) { 4118226705Spjd flags.nounmount = B_TRUE; 4119226705Spjd } 4120226705Spjd if (flags.recurse) { 4121226705Spjd 4122185029Spjd parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 4123185029Spjd if (parentname == NULL) { 4124185029Spjd ret = -1; 4125185029Spjd goto error; 4126185029Spjd } 4127168676Spjd delim = strchr(parentname, '@'); 4128168676Spjd *delim = '\0'; 4129185029Spjd zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 4130168676Spjd if (zhrp == NULL) { 4131185029Spjd ret = -1; 4132185029Spjd goto error; 4133168676Spjd } 4134268469Sdelphij } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) { 4135226676Spjd if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 4136235216Smm flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0, 4137235216Smm flags.forceunmount ? MS_FORCE : 0)) == NULL) { 4138168676Spjd return (-1); 4139226676Spjd } 4140168676Spjd 4141168676Spjd if (changelist_haszonedchild(cl)) { 4142168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4143168676Spjd "child dataset with inherited mountpoint is used " 4144168676Spjd "in a non-global zone")); 4145168676Spjd (void) zfs_error(hdl, EZFS_ZONED, errbuf); 4146307107Smav ret = -1; 4147168676Spjd goto error; 4148168676Spjd } 4149168676Spjd 4150168676Spjd if ((ret = changelist_prefix(cl)) != 0) 4151168676Spjd goto error; 4152168404Spjd } 4153168404Spjd 4154168404Spjd if (ZFS_IS_VOLUME(zhp)) 4155168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 4156168404Spjd else 4157168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 4158168404Spjd 4159168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4160168404Spjd (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 4161168404Spjd 4162226705Spjd zc.zc_cookie = flags.recurse ? 1 : 0; 4163226705Spjd if (flags.nounmount) 4164226676Spjd zc.zc_cookie |= 2; 4165168676Spjd 4166185029Spjd if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 4167168676Spjd /* 4168168676Spjd * if it was recursive, the one that actually failed will 4169168676Spjd * be in zc.zc_name 4170168676Spjd */ 4171168676Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4172185029Spjd "cannot rename '%s'"), zc.zc_name); 4173168404Spjd 4174226705Spjd if (flags.recurse && errno == EEXIST) { 4175168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4176168676Spjd "a child dataset already has a snapshot " 4177168676Spjd "with the new name")); 4178185029Spjd (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 4179168676Spjd } else { 4180168676Spjd (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 4181168676Spjd } 4182168676Spjd 4183168404Spjd /* 4184168404Spjd * On failure, we still want to remount any filesystems that 4185168404Spjd * were previously mounted, so we don't alter the system state. 4186168404Spjd */ 4187268469Sdelphij if (cl != NULL) 4188168676Spjd (void) changelist_postfix(cl); 4189168404Spjd } else { 4190268469Sdelphij if (cl != NULL) { 4191168676Spjd changelist_rename(cl, zfs_get_name(zhp), target); 4192168676Spjd ret = changelist_postfix(cl); 4193168676Spjd } 4194168404Spjd } 4195168404Spjd 4196168404Spjderror: 4197268469Sdelphij if (parentname != NULL) { 4198168676Spjd free(parentname); 4199168676Spjd } 4200268469Sdelphij if (zhrp != NULL) { 4201168676Spjd zfs_close(zhrp); 4202168676Spjd } 4203268469Sdelphij if (cl != NULL) { 4204168676Spjd changelist_free(cl); 4205168676Spjd } 4206168404Spjd return (ret); 4207168404Spjd} 4208168404Spjd 4209219089Spjdnvlist_t * 4210219089Spjdzfs_get_user_props(zfs_handle_t *zhp) 4211168404Spjd{ 4212219089Spjd return (zhp->zfs_user_props); 4213168676Spjd} 4214168676Spjd 4215168404Spjdnvlist_t * 4216219089Spjdzfs_get_recvd_props(zfs_handle_t *zhp) 4217168404Spjd{ 4218219089Spjd if (zhp->zfs_recvd_props == NULL) 4219219089Spjd if (get_recvd_props_ioctl(zhp) != 0) 4220219089Spjd return (NULL); 4221219089Spjd return (zhp->zfs_recvd_props); 4222168404Spjd} 4223168404Spjd 4224168404Spjd/* 4225168404Spjd * This function is used by 'zfs list' to determine the exact set of columns to 4226168404Spjd * display, and their maximum widths. This does two main things: 4227168404Spjd * 4228168404Spjd * - If this is a list of all properties, then expand the list to include 4229168404Spjd * all native properties, and set a flag so that for each dataset we look 4230168404Spjd * for new unique user properties and add them to the list. 4231168404Spjd * 4232168404Spjd * - For non fixed-width properties, keep track of the maximum width seen 4233219089Spjd * so that we can size the column appropriately. If the user has 4234219089Spjd * requested received property values, we also need to compute the width 4235219089Spjd * of the RECEIVED column. 4236168404Spjd */ 4237168404Spjdint 4238259850Sdelphijzfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received, 4239259850Sdelphij boolean_t literal) 4240168404Spjd{ 4241168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4242185029Spjd zprop_list_t *entry; 4243185029Spjd zprop_list_t **last, **start; 4244168404Spjd nvlist_t *userprops, *propval; 4245168404Spjd nvpair_t *elem; 4246168404Spjd char *strval; 4247168404Spjd char buf[ZFS_MAXPROPLEN]; 4248168404Spjd 4249185029Spjd if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 4250168404Spjd return (-1); 4251168404Spjd 4252168404Spjd userprops = zfs_get_user_props(zhp); 4253168404Spjd 4254168404Spjd entry = *plp; 4255168404Spjd if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 4256168404Spjd /* 4257168404Spjd * Go through and add any user properties as necessary. We 4258168404Spjd * start by incrementing our list pointer to the first 4259168404Spjd * non-native property. 4260168404Spjd */ 4261168404Spjd start = plp; 4262168404Spjd while (*start != NULL) { 4263185029Spjd if ((*start)->pl_prop == ZPROP_INVAL) 4264168404Spjd break; 4265168404Spjd start = &(*start)->pl_next; 4266168404Spjd } 4267168404Spjd 4268168404Spjd elem = NULL; 4269168404Spjd while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 4270168404Spjd /* 4271168404Spjd * See if we've already found this property in our list. 4272168404Spjd */ 4273168404Spjd for (last = start; *last != NULL; 4274168404Spjd last = &(*last)->pl_next) { 4275168404Spjd if (strcmp((*last)->pl_user_prop, 4276168404Spjd nvpair_name(elem)) == 0) 4277168404Spjd break; 4278168404Spjd } 4279168404Spjd 4280168404Spjd if (*last == NULL) { 4281168404Spjd if ((entry = zfs_alloc(hdl, 4282185029Spjd sizeof (zprop_list_t))) == NULL || 4283168404Spjd ((entry->pl_user_prop = zfs_strdup(hdl, 4284168404Spjd nvpair_name(elem)))) == NULL) { 4285168404Spjd free(entry); 4286168404Spjd return (-1); 4287168404Spjd } 4288168404Spjd 4289185029Spjd entry->pl_prop = ZPROP_INVAL; 4290168404Spjd entry->pl_width = strlen(nvpair_name(elem)); 4291168404Spjd entry->pl_all = B_TRUE; 4292168404Spjd *last = entry; 4293168404Spjd } 4294168404Spjd } 4295168404Spjd } 4296168404Spjd 4297168404Spjd /* 4298168404Spjd * Now go through and check the width of any non-fixed columns 4299168404Spjd */ 4300168404Spjd for (entry = *plp; entry != NULL; entry = entry->pl_next) { 4301259850Sdelphij if (entry->pl_fixed && !literal) 4302168404Spjd continue; 4303168404Spjd 4304185029Spjd if (entry->pl_prop != ZPROP_INVAL) { 4305168404Spjd if (zfs_prop_get(zhp, entry->pl_prop, 4306259850Sdelphij buf, sizeof (buf), NULL, NULL, 0, literal) == 0) { 4307168404Spjd if (strlen(buf) > entry->pl_width) 4308168404Spjd entry->pl_width = strlen(buf); 4309168404Spjd } 4310219089Spjd if (received && zfs_prop_get_recvd(zhp, 4311219089Spjd zfs_prop_to_name(entry->pl_prop), 4312259850Sdelphij buf, sizeof (buf), literal) == 0) 4313219089Spjd if (strlen(buf) > entry->pl_recvd_width) 4314219089Spjd entry->pl_recvd_width = strlen(buf); 4315219089Spjd } else { 4316219089Spjd if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 4317219089Spjd &propval) == 0) { 4318219089Spjd verify(nvlist_lookup_string(propval, 4319219089Spjd ZPROP_VALUE, &strval) == 0); 4320219089Spjd if (strlen(strval) > entry->pl_width) 4321219089Spjd entry->pl_width = strlen(strval); 4322219089Spjd } 4323219089Spjd if (received && zfs_prop_get_recvd(zhp, 4324219089Spjd entry->pl_user_prop, 4325259850Sdelphij buf, sizeof (buf), literal) == 0) 4326219089Spjd if (strlen(buf) > entry->pl_recvd_width) 4327219089Spjd entry->pl_recvd_width = strlen(buf); 4328168404Spjd } 4329168404Spjd } 4330168404Spjd 4331168404Spjd return (0); 4332168404Spjd} 4333168404Spjd 4334185029Spjdint 4335185029Spjdzfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 4336209962Smm char *resource, void *export, void *sharetab, 4337209962Smm int sharemax, zfs_share_op_t operation) 4338185029Spjd{ 4339185029Spjd zfs_cmd_t zc = { 0 }; 4340185029Spjd int error; 4341185029Spjd 4342185029Spjd (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4343185029Spjd (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4344209962Smm if (resource) 4345209962Smm (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 4346185029Spjd zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 4347185029Spjd zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 4348185029Spjd zc.zc_share.z_sharetype = operation; 4349185029Spjd zc.zc_share.z_sharemax = sharemax; 4350185029Spjd error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 4351185029Spjd return (error); 4352185029Spjd} 4353185029Spjd 4354205198Sdelphijvoid 4355205198Sdelphijzfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 4356205198Sdelphij{ 4357205198Sdelphij nvpair_t *curr; 4358205198Sdelphij 4359205198Sdelphij /* 4360205198Sdelphij * Keep a reference to the props-table against which we prune the 4361205198Sdelphij * properties. 4362205198Sdelphij */ 4363205198Sdelphij zhp->zfs_props_table = props; 4364205198Sdelphij 4365205198Sdelphij curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 4366205198Sdelphij 4367205198Sdelphij while (curr) { 4368205198Sdelphij zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 4369205198Sdelphij nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 4370205198Sdelphij 4371206199Sdelphij /* 4372219089Spjd * User properties will result in ZPROP_INVAL, and since we 4373219089Spjd * only know how to prune standard ZFS properties, we always 4374219089Spjd * leave these in the list. This can also happen if we 4375219089Spjd * encounter an unknown DSL property (when running older 4376219089Spjd * software, for example). 4377206199Sdelphij */ 4378206199Sdelphij if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 4379205198Sdelphij (void) nvlist_remove(zhp->zfs_props, 4380205198Sdelphij nvpair_name(curr), nvpair_type(curr)); 4381205198Sdelphij curr = next; 4382205198Sdelphij } 4383205198Sdelphij} 4384205198Sdelphij 4385277300Ssmh#ifdef illumos 4386209962Smmstatic int 4387209962Smmzfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 4388209962Smm zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 4389209962Smm{ 4390209962Smm zfs_cmd_t zc = { 0 }; 4391209962Smm nvlist_t *nvlist = NULL; 4392209962Smm int error; 4393209962Smm 4394209962Smm (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4395209962Smm (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4396209962Smm zc.zc_cookie = (uint64_t)cmd; 4397209962Smm 4398209962Smm if (cmd == ZFS_SMB_ACL_RENAME) { 4399209962Smm if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 4400209962Smm (void) no_memory(hdl); 4401289497Smav return (0); 4402209962Smm } 4403209962Smm } 4404209962Smm 4405209962Smm switch (cmd) { 4406209962Smm case ZFS_SMB_ACL_ADD: 4407209962Smm case ZFS_SMB_ACL_REMOVE: 4408209962Smm (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 4409209962Smm break; 4410209962Smm case ZFS_SMB_ACL_RENAME: 4411209962Smm if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 4412209962Smm resource1) != 0) { 4413209962Smm (void) no_memory(hdl); 4414209962Smm return (-1); 4415209962Smm } 4416209962Smm if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 4417209962Smm resource2) != 0) { 4418209962Smm (void) no_memory(hdl); 4419209962Smm return (-1); 4420209962Smm } 4421209962Smm if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 4422209962Smm nvlist_free(nvlist); 4423209962Smm return (-1); 4424209962Smm } 4425209962Smm break; 4426209962Smm case ZFS_SMB_ACL_PURGE: 4427209962Smm break; 4428209962Smm default: 4429209962Smm return (-1); 4430209962Smm } 4431209962Smm error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 4432296528Smav nvlist_free(nvlist); 4433209962Smm return (error); 4434209962Smm} 4435209962Smm 4436209962Smmint 4437209962Smmzfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 4438209962Smm char *path, char *resource) 4439209962Smm{ 4440209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 4441209962Smm resource, NULL)); 4442209962Smm} 4443209962Smm 4444209962Smmint 4445209962Smmzfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 4446209962Smm char *path, char *resource) 4447209962Smm{ 4448209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 4449209962Smm resource, NULL)); 4450209962Smm} 4451209962Smm 4452209962Smmint 4453209962Smmzfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 4454209962Smm{ 4455209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 4456209962Smm NULL, NULL)); 4457209962Smm} 4458209962Smm 4459209962Smmint 4460209962Smmzfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 4461209962Smm char *oldname, char *newname) 4462209962Smm{ 4463209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 4464209962Smm oldname, newname)); 4465209962Smm} 4466277300Ssmh#endif /* illumos */ 4467209962Smm 4468209962Smmint 4469209962Smmzfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 4470209962Smm zfs_userspace_cb_t func, void *arg) 4471209962Smm{ 4472209962Smm zfs_cmd_t zc = { 0 }; 4473209962Smm zfs_useracct_t buf[100]; 4474240415Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 4475240415Smm int ret; 4476209962Smm 4477228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4478209962Smm 4479209962Smm zc.zc_objset_type = type; 4480209962Smm zc.zc_nvlist_dst = (uintptr_t)buf; 4481209962Smm 4482240415Smm for (;;) { 4483209962Smm zfs_useracct_t *zua = buf; 4484209962Smm 4485209962Smm zc.zc_nvlist_dst_size = sizeof (buf); 4486240415Smm if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) { 4487248571Smm char errbuf[1024]; 4488240415Smm 4489240415Smm (void) snprintf(errbuf, sizeof (errbuf), 4490240415Smm dgettext(TEXT_DOMAIN, 4491240415Smm "cannot get used/quota for %s"), zc.zc_name); 4492240415Smm return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4493240415Smm } 4494240415Smm if (zc.zc_nvlist_dst_size == 0) 4495209962Smm break; 4496209962Smm 4497209962Smm while (zc.zc_nvlist_dst_size > 0) { 4498240415Smm if ((ret = func(arg, zua->zu_domain, zua->zu_rid, 4499240415Smm zua->zu_space)) != 0) 4500240415Smm return (ret); 4501209962Smm zua++; 4502209962Smm zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 4503209962Smm } 4504209962Smm } 4505209962Smm 4506240415Smm return (0); 4507209962Smm} 4508209962Smm 4509248571Smmstruct holdarg { 4510248571Smm nvlist_t *nvl; 4511248571Smm const char *snapname; 4512248571Smm const char *tag; 4513248571Smm boolean_t recursive; 4514252219Sdelphij int error; 4515248571Smm}; 4516248571Smm 4517248571Smmstatic int 4518248571Smmzfs_hold_one(zfs_handle_t *zhp, void *arg) 4519248571Smm{ 4520248571Smm struct holdarg *ha = arg; 4521307108Smav char name[ZFS_MAX_DATASET_NAME_LEN]; 4522248571Smm int rv = 0; 4523248571Smm 4524248571Smm (void) snprintf(name, sizeof (name), 4525248571Smm "%s@%s", zhp->zfs_name, ha->snapname); 4526248571Smm 4527251646Sdelphij if (lzc_exists(name)) 4528248571Smm fnvlist_add_string(ha->nvl, name, ha->tag); 4529248571Smm 4530248571Smm if (ha->recursive) 4531248571Smm rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha); 4532248571Smm zfs_close(zhp); 4533248571Smm return (rv); 4534248571Smm} 4535248571Smm 4536219089Spjdint 4537219089Spjdzfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 4538251646Sdelphij boolean_t recursive, int cleanup_fd) 4539219089Spjd{ 4540248571Smm int ret; 4541248571Smm struct holdarg ha; 4542219089Spjd 4543248571Smm ha.nvl = fnvlist_alloc(); 4544248571Smm ha.snapname = snapname; 4545248571Smm ha.tag = tag; 4546248571Smm ha.recursive = recursive; 4547248571Smm (void) zfs_hold_one(zfs_handle_dup(zhp), &ha); 4548249357Smm 4549251646Sdelphij if (nvlist_empty(ha.nvl)) { 4550251646Sdelphij char errbuf[1024]; 4551251646Sdelphij 4552249357Smm fnvlist_free(ha.nvl); 4553249357Smm ret = ENOENT; 4554251646Sdelphij (void) snprintf(errbuf, sizeof (errbuf), 4555251646Sdelphij dgettext(TEXT_DOMAIN, 4556251646Sdelphij "cannot hold snapshot '%s@%s'"), 4557251646Sdelphij zhp->zfs_name, snapname); 4558251646Sdelphij (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf); 4559249357Smm return (ret); 4560249357Smm } 4561249357Smm 4562251646Sdelphij ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl); 4563248571Smm fnvlist_free(ha.nvl); 4564219089Spjd 4565251646Sdelphij return (ret); 4566251646Sdelphij} 4567251646Sdelphij 4568251646Sdelphijint 4569251646Sdelphijzfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds) 4570251646Sdelphij{ 4571251646Sdelphij int ret; 4572251646Sdelphij nvlist_t *errors; 4573251646Sdelphij libzfs_handle_t *hdl = zhp->zfs_hdl; 4574251646Sdelphij char errbuf[1024]; 4575251646Sdelphij nvpair_t *elem; 4576251646Sdelphij 4577251646Sdelphij errors = NULL; 4578251646Sdelphij ret = lzc_hold(holds, cleanup_fd, &errors); 4579251646Sdelphij 4580251646Sdelphij if (ret == 0) { 4581251646Sdelphij /* There may be errors even in the success case. */ 4582251646Sdelphij fnvlist_free(errors); 4583248571Smm return (0); 4584251646Sdelphij } 4585219089Spjd 4586251646Sdelphij if (nvlist_empty(errors)) { 4587248571Smm /* no hold-specific errors */ 4588248571Smm (void) snprintf(errbuf, sizeof (errbuf), 4589248571Smm dgettext(TEXT_DOMAIN, "cannot hold")); 4590248571Smm switch (ret) { 4591248571Smm case ENOTSUP: 4592248571Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4593248571Smm "pool must be upgraded")); 4594248571Smm (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 4595248571Smm break; 4596248571Smm case EINVAL: 4597248571Smm (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 4598248571Smm break; 4599248571Smm default: 4600248571Smm (void) zfs_standard_error(hdl, ret, errbuf); 4601248571Smm } 4602248571Smm } 4603219089Spjd 4604248571Smm for (elem = nvlist_next_nvpair(errors, NULL); 4605248571Smm elem != NULL; 4606248571Smm elem = nvlist_next_nvpair(errors, elem)) { 4607248571Smm (void) snprintf(errbuf, sizeof (errbuf), 4608248571Smm dgettext(TEXT_DOMAIN, 4609248571Smm "cannot hold snapshot '%s'"), nvpair_name(elem)); 4610248571Smm switch (fnvpair_value_int32(elem)) { 4611219089Spjd case E2BIG: 4612219089Spjd /* 4613219089Spjd * Temporary tags wind up having the ds object id 4614219089Spjd * prepended. So even if we passed the length check 4615219089Spjd * above, it's still possible for the tag to wind 4616219089Spjd * up being slightly too long. 4617219089Spjd */ 4618248571Smm (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf); 4619248571Smm break; 4620219089Spjd case EINVAL: 4621248571Smm (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 4622248571Smm break; 4623219089Spjd case EEXIST: 4624248571Smm (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf); 4625248571Smm break; 4626219089Spjd default: 4627248571Smm (void) zfs_standard_error(hdl, 4628248571Smm fnvpair_value_int32(elem), errbuf); 4629219089Spjd } 4630219089Spjd } 4631219089Spjd 4632248571Smm fnvlist_free(errors); 4633248571Smm return (ret); 4634219089Spjd} 4635219089Spjd 4636248571Smmstatic int 4637248571Smmzfs_release_one(zfs_handle_t *zhp, void *arg) 4638248571Smm{ 4639248571Smm struct holdarg *ha = arg; 4640307108Smav char name[ZFS_MAX_DATASET_NAME_LEN]; 4641248571Smm int rv = 0; 4642252219Sdelphij nvlist_t *existing_holds; 4643248571Smm 4644248571Smm (void) snprintf(name, sizeof (name), 4645248571Smm "%s@%s", zhp->zfs_name, ha->snapname); 4646248571Smm 4647252219Sdelphij if (lzc_get_holds(name, &existing_holds) != 0) { 4648252219Sdelphij ha->error = ENOENT; 4649252219Sdelphij } else if (!nvlist_exists(existing_holds, ha->tag)) { 4650252219Sdelphij ha->error = ESRCH; 4651252219Sdelphij } else { 4652252219Sdelphij nvlist_t *torelease = fnvlist_alloc(); 4653252219Sdelphij fnvlist_add_boolean(torelease, ha->tag); 4654252219Sdelphij fnvlist_add_nvlist(ha->nvl, name, torelease); 4655252219Sdelphij fnvlist_free(torelease); 4656248571Smm } 4657248571Smm 4658248571Smm if (ha->recursive) 4659248571Smm rv = zfs_iter_filesystems(zhp, zfs_release_one, ha); 4660248571Smm zfs_close(zhp); 4661248571Smm return (rv); 4662248571Smm} 4663248571Smm 4664219089Spjdint 4665219089Spjdzfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4666219089Spjd boolean_t recursive) 4667219089Spjd{ 4668248571Smm int ret; 4669248571Smm struct holdarg ha; 4670251646Sdelphij nvlist_t *errors = NULL; 4671248571Smm nvpair_t *elem; 4672219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4673249357Smm char errbuf[1024]; 4674219089Spjd 4675248571Smm ha.nvl = fnvlist_alloc(); 4676248571Smm ha.snapname = snapname; 4677248571Smm ha.tag = tag; 4678248571Smm ha.recursive = recursive; 4679252219Sdelphij ha.error = 0; 4680248571Smm (void) zfs_release_one(zfs_handle_dup(zhp), &ha); 4681249357Smm 4682251646Sdelphij if (nvlist_empty(ha.nvl)) { 4683249357Smm fnvlist_free(ha.nvl); 4684252219Sdelphij ret = ha.error; 4685249357Smm (void) snprintf(errbuf, sizeof (errbuf), 4686249357Smm dgettext(TEXT_DOMAIN, 4687249357Smm "cannot release hold from snapshot '%s@%s'"), 4688249357Smm zhp->zfs_name, snapname); 4689252219Sdelphij if (ret == ESRCH) { 4690252219Sdelphij (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf); 4691252219Sdelphij } else { 4692252219Sdelphij (void) zfs_standard_error(hdl, ret, errbuf); 4693252219Sdelphij } 4694249357Smm return (ret); 4695249357Smm } 4696249357Smm 4697248571Smm ret = lzc_release(ha.nvl, &errors); 4698248571Smm fnvlist_free(ha.nvl); 4699219089Spjd 4700251646Sdelphij if (ret == 0) { 4701251646Sdelphij /* There may be errors even in the success case. */ 4702251646Sdelphij fnvlist_free(errors); 4703248571Smm return (0); 4704251646Sdelphij } 4705219089Spjd 4706251646Sdelphij if (nvlist_empty(errors)) { 4707248571Smm /* no hold-specific errors */ 4708219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4709248571Smm "cannot release")); 4710219089Spjd switch (errno) { 4711219089Spjd case ENOTSUP: 4712219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4713219089Spjd "pool must be upgraded")); 4714248571Smm (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 4715248571Smm break; 4716248571Smm default: 4717248571Smm (void) zfs_standard_error_fmt(hdl, errno, errbuf); 4718248571Smm } 4719248571Smm } 4720248571Smm 4721248571Smm for (elem = nvlist_next_nvpair(errors, NULL); 4722248571Smm elem != NULL; 4723248571Smm elem = nvlist_next_nvpair(errors, elem)) { 4724248571Smm (void) snprintf(errbuf, sizeof (errbuf), 4725248571Smm dgettext(TEXT_DOMAIN, 4726248571Smm "cannot release hold from snapshot '%s'"), 4727248571Smm nvpair_name(elem)); 4728248571Smm switch (fnvpair_value_int32(elem)) { 4729248571Smm case ESRCH: 4730248571Smm (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf); 4731248571Smm break; 4732219089Spjd case EINVAL: 4733248571Smm (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 4734248571Smm break; 4735219089Spjd default: 4736248571Smm (void) zfs_standard_error_fmt(hdl, 4737248571Smm fnvpair_value_int32(elem), errbuf); 4738219089Spjd } 4739219089Spjd } 4740219089Spjd 4741248571Smm fnvlist_free(errors); 4742248571Smm return (ret); 4743219089Spjd} 4744219089Spjd 4745219089Spjdint 4746219089Spjdzfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl) 4747219089Spjd{ 4748219089Spjd zfs_cmd_t zc = { 0 }; 4749219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4750219089Spjd int nvsz = 2048; 4751219089Spjd void *nvbuf; 4752219089Spjd int err = 0; 4753248571Smm char errbuf[1024]; 4754219089Spjd 4755219089Spjd assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 4756219089Spjd zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4757219089Spjd 4758219089Spjdtryagain: 4759219089Spjd 4760219089Spjd nvbuf = malloc(nvsz); 4761219089Spjd if (nvbuf == NULL) { 4762219089Spjd err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno))); 4763219089Spjd goto out; 4764219089Spjd } 4765219089Spjd 4766219089Spjd zc.zc_nvlist_dst_size = nvsz; 4767219089Spjd zc.zc_nvlist_dst = (uintptr_t)nvbuf; 4768219089Spjd 4769307108Smav (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4770219089Spjd 4771230514Smm if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) { 4772219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4773219089Spjd dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"), 4774219089Spjd zc.zc_name); 4775219089Spjd switch (errno) { 4776219089Spjd case ENOMEM: 4777219089Spjd free(nvbuf); 4778219089Spjd nvsz = zc.zc_nvlist_dst_size; 4779219089Spjd goto tryagain; 4780219089Spjd 4781219089Spjd case ENOTSUP: 4782219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4783219089Spjd "pool must be upgraded")); 4784219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4785219089Spjd break; 4786219089Spjd case EINVAL: 4787219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4788219089Spjd break; 4789219089Spjd case ENOENT: 4790219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4791219089Spjd break; 4792219089Spjd default: 4793219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4794219089Spjd break; 4795219089Spjd } 4796219089Spjd } else { 4797219089Spjd /* success */ 4798219089Spjd int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0); 4799219089Spjd if (rc) { 4800219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext( 4801219089Spjd TEXT_DOMAIN, "cannot get permissions on '%s'"), 4802219089Spjd zc.zc_name); 4803219089Spjd err = zfs_standard_error_fmt(hdl, rc, errbuf); 4804219089Spjd } 4805219089Spjd } 4806219089Spjd 4807219089Spjd free(nvbuf); 4808219089Spjdout: 4809219089Spjd return (err); 4810219089Spjd} 4811219089Spjd 4812219089Spjdint 4813219089Spjdzfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl) 4814219089Spjd{ 4815219089Spjd zfs_cmd_t zc = { 0 }; 4816219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4817219089Spjd char *nvbuf; 4818248571Smm char errbuf[1024]; 4819219089Spjd size_t nvsz; 4820219089Spjd int err; 4821219089Spjd 4822219089Spjd assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 4823219089Spjd zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4824219089Spjd 4825219089Spjd err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); 4826219089Spjd assert(err == 0); 4827219089Spjd 4828219089Spjd nvbuf = malloc(nvsz); 4829219089Spjd 4830219089Spjd err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); 4831219089Spjd assert(err == 0); 4832219089Spjd 4833219089Spjd zc.zc_nvlist_src_size = nvsz; 4834219089Spjd zc.zc_nvlist_src = (uintptr_t)nvbuf; 4835219089Spjd zc.zc_perm_action = un; 4836219089Spjd 4837219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4838219089Spjd 4839219089Spjd if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) { 4840219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4841219089Spjd dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"), 4842219089Spjd zc.zc_name); 4843219089Spjd switch (errno) { 4844219089Spjd case ENOTSUP: 4845219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4846219089Spjd "pool must be upgraded")); 4847219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4848219089Spjd break; 4849219089Spjd case EINVAL: 4850219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4851219089Spjd break; 4852219089Spjd case ENOENT: 4853219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4854219089Spjd break; 4855219089Spjd default: 4856219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4857219089Spjd break; 4858219089Spjd } 4859219089Spjd } 4860219089Spjd 4861219089Spjd free(nvbuf); 4862219089Spjd 4863219089Spjd return (err); 4864219089Spjd} 4865219089Spjd 4866219089Spjdint 4867219089Spjdzfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl) 4868219089Spjd{ 4869248571Smm int err; 4870248571Smm char errbuf[1024]; 4871219089Spjd 4872248571Smm err = lzc_get_holds(zhp->zfs_name, nvl); 4873219089Spjd 4874248571Smm if (err != 0) { 4875248571Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 4876219089Spjd 4877219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4878219089Spjd dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"), 4879248571Smm zhp->zfs_name); 4880248571Smm switch (err) { 4881219089Spjd case ENOTSUP: 4882219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4883219089Spjd "pool must be upgraded")); 4884219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4885219089Spjd break; 4886219089Spjd case EINVAL: 4887219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4888219089Spjd break; 4889219089Spjd case ENOENT: 4890219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4891219089Spjd break; 4892219089Spjd default: 4893219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4894219089Spjd break; 4895219089Spjd } 4896219089Spjd } 4897219089Spjd 4898219089Spjd return (err); 4899219089Spjd} 4900219089Spjd 4901251629Sdelphij/* 4902251629Sdelphij * Convert the zvol's volume size to an appropriate reservation. 4903251629Sdelphij * Note: If this routine is updated, it is necessary to update the ZFS test 4904251629Sdelphij * suite's shell version in reservation.kshlib. 4905251629Sdelphij */ 4906219089Spjduint64_t 4907219089Spjdzvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4908219089Spjd{ 4909219089Spjd uint64_t numdb; 4910219089Spjd uint64_t nblocks, volblocksize; 4911219089Spjd int ncopies; 4912219089Spjd char *strval; 4913219089Spjd 4914219089Spjd if (nvlist_lookup_string(props, 4915219089Spjd zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4916219089Spjd ncopies = atoi(strval); 4917219089Spjd else 4918219089Spjd ncopies = 1; 4919219089Spjd if (nvlist_lookup_uint64(props, 4920219089Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4921219089Spjd &volblocksize) != 0) 4922219089Spjd volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4923219089Spjd nblocks = volsize/volblocksize; 4924219089Spjd /* start with metadnode L0-L6 */ 4925219089Spjd numdb = 7; 4926219089Spjd /* calculate number of indirects */ 4927219089Spjd while (nblocks > 1) { 4928219089Spjd nblocks += DNODES_PER_LEVEL - 1; 4929219089Spjd nblocks /= DNODES_PER_LEVEL; 4930219089Spjd numdb += nblocks; 4931219089Spjd } 4932219089Spjd numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4933219089Spjd volsize *= ncopies; 4934219089Spjd /* 4935219089Spjd * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4936219089Spjd * compressed, but in practice they compress down to about 4937219089Spjd * 1100 bytes 4938219089Spjd */ 4939219089Spjd numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4940219089Spjd volsize += numdb; 4941219089Spjd return (volsize); 4942219089Spjd} 4943219089Spjd 4944168404Spjd/* 4945168404Spjd * Attach/detach the given filesystem to/from the given jail. 4946168404Spjd */ 4947168404Spjdint 4948168404Spjdzfs_jail(zfs_handle_t *zhp, int jailid, int attach) 4949168404Spjd{ 4950168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4951168404Spjd zfs_cmd_t zc = { 0 }; 4952168404Spjd char errbuf[1024]; 4953224525Smm unsigned long cmd; 4954224525Smm int ret; 4955168404Spjd 4956168404Spjd if (attach) { 4957168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4958168404Spjd dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name); 4959168404Spjd } else { 4960168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4961249547Spjd dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name); 4962168404Spjd } 4963168404Spjd 4964168404Spjd switch (zhp->zfs_type) { 4965168404Spjd case ZFS_TYPE_VOLUME: 4966168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4967168404Spjd "volumes can not be jailed")); 4968168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4969168404Spjd case ZFS_TYPE_SNAPSHOT: 4970168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4971168404Spjd "snapshots can not be jailed")); 4972168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4973168404Spjd } 4974168404Spjd assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4975168404Spjd 4976168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4977168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 4978168404Spjd zc.zc_jailid = jailid; 4979168404Spjd 4980168404Spjd cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL; 4981168404Spjd if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0) 4982168404Spjd zfs_standard_error(hdl, errno, errbuf); 4983168404Spjd 4984168404Spjd return (ret); 4985168404Spjd} 4986