libzfs_dataset.c revision 235216
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. 24219089Spjd * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 25223623Smm * Copyright (c) 2011 by Delphix. All rights reserved. 26230438Spjd * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>. 27226706Spjd * All rights reserved. 28235216Smm * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved. 29168404Spjd */ 30168404Spjd 31168404Spjd#include <ctype.h> 32168404Spjd#include <errno.h> 33168404Spjd#include <libintl.h> 34168404Spjd#include <math.h> 35168404Spjd#include <stdio.h> 36168404Spjd#include <stdlib.h> 37168404Spjd#include <strings.h> 38168404Spjd#include <unistd.h> 39185029Spjd#include <stddef.h> 40168404Spjd#include <zone.h> 41168404Spjd#include <fcntl.h> 42168404Spjd#include <sys/mntent.h> 43168404Spjd#include <sys/mount.h> 44185029Spjd#include <priv.h> 45185029Spjd#include <pwd.h> 46185029Spjd#include <grp.h> 47185029Spjd#include <stddef.h> 48209962Smm#include <idmap.h> 49168404Spjd 50219089Spjd#include <sys/dnode.h> 51168404Spjd#include <sys/spa.h> 52168404Spjd#include <sys/zap.h> 53209962Smm#include <sys/misc.h> 54168404Spjd#include <libzfs.h> 55168404Spjd 56168404Spjd#include "zfs_namecheck.h" 57168404Spjd#include "zfs_prop.h" 58168404Spjd#include "libzfs_impl.h" 59185029Spjd#include "zfs_deleg.h" 60168404Spjd 61209962Smmstatic int userquota_propname_decode(const char *propname, boolean_t zoned, 62209962Smm zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 63168676Spjd 64168404Spjd/* 65168404Spjd * Given a single type (not a mask of types), return the type in a human 66168404Spjd * readable form. 67168404Spjd */ 68168404Spjdconst char * 69168404Spjdzfs_type_to_name(zfs_type_t type) 70168404Spjd{ 71168404Spjd switch (type) { 72168404Spjd case ZFS_TYPE_FILESYSTEM: 73168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 74168404Spjd case ZFS_TYPE_SNAPSHOT: 75168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 76168404Spjd case ZFS_TYPE_VOLUME: 77168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 78168404Spjd } 79168404Spjd 80168404Spjd return (NULL); 81168404Spjd} 82168404Spjd 83168404Spjd/* 84168404Spjd * Given a path and mask of ZFS types, return a string describing this dataset. 85168404Spjd * This is used when we fail to open a dataset and we cannot get an exact type. 86168404Spjd * We guess what the type would have been based on the path and the mask of 87168404Spjd * acceptable types. 88168404Spjd */ 89168404Spjdstatic const char * 90168404Spjdpath_to_str(const char *path, int types) 91168404Spjd{ 92168404Spjd /* 93168404Spjd * When given a single type, always report the exact type. 94168404Spjd */ 95168404Spjd if (types == ZFS_TYPE_SNAPSHOT) 96168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 97168404Spjd if (types == ZFS_TYPE_FILESYSTEM) 98168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 99168404Spjd if (types == ZFS_TYPE_VOLUME) 100168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 101168404Spjd 102168404Spjd /* 103168404Spjd * The user is requesting more than one type of dataset. If this is the 104168404Spjd * case, consult the path itself. If we're looking for a snapshot, and 105168404Spjd * a '@' is found, then report it as "snapshot". Otherwise, remove the 106168404Spjd * snapshot attribute and try again. 107168404Spjd */ 108168404Spjd if (types & ZFS_TYPE_SNAPSHOT) { 109168404Spjd if (strchr(path, '@') != NULL) 110168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 111168404Spjd return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 112168404Spjd } 113168404Spjd 114168404Spjd /* 115168404Spjd * The user has requested either filesystems or volumes. 116168404Spjd * We have no way of knowing a priori what type this would be, so always 117168404Spjd * report it as "filesystem" or "volume", our two primitive types. 118168404Spjd */ 119168404Spjd if (types & ZFS_TYPE_FILESYSTEM) 120168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 121168404Spjd 122168404Spjd assert(types & ZFS_TYPE_VOLUME); 123168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 124168404Spjd} 125168404Spjd 126168404Spjd/* 127168404Spjd * Validate a ZFS path. This is used even before trying to open the dataset, to 128209962Smm * provide a more meaningful error message. We call zfs_error_aux() to 129209962Smm * explain exactly why the name was not valid. 130168404Spjd */ 131219089Spjdint 132185029Spjdzfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 133185029Spjd boolean_t modifying) 134168404Spjd{ 135168404Spjd namecheck_err_t why; 136168404Spjd char what; 137168404Spjd 138219089Spjd (void) zfs_prop_get_table(); 139168404Spjd if (dataset_namecheck(path, &why, &what) != 0) { 140168404Spjd if (hdl != NULL) { 141168404Spjd switch (why) { 142168404Spjd case NAME_ERR_TOOLONG: 143168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 144168404Spjd "name is too long")); 145168404Spjd break; 146168404Spjd 147168404Spjd case NAME_ERR_LEADING_SLASH: 148168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 149168404Spjd "leading slash in name")); 150168404Spjd break; 151168404Spjd 152168404Spjd case NAME_ERR_EMPTY_COMPONENT: 153168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 154168404Spjd "empty component in name")); 155168404Spjd break; 156168404Spjd 157168404Spjd case NAME_ERR_TRAILING_SLASH: 158168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 159168404Spjd "trailing slash in name")); 160168404Spjd break; 161168404Spjd 162168404Spjd case NAME_ERR_INVALCHAR: 163168404Spjd zfs_error_aux(hdl, 164168404Spjd dgettext(TEXT_DOMAIN, "invalid character " 165168404Spjd "'%c' in name"), what); 166168404Spjd break; 167168404Spjd 168168404Spjd case NAME_ERR_MULTIPLE_AT: 169168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 170168404Spjd "multiple '@' delimiters in name")); 171168404Spjd break; 172168404Spjd 173168404Spjd case NAME_ERR_NOLETTER: 174168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 175168404Spjd "pool doesn't begin with a letter")); 176168404Spjd break; 177168404Spjd 178168404Spjd case NAME_ERR_RESERVED: 179168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 180168404Spjd "name is reserved")); 181168404Spjd break; 182168404Spjd 183168404Spjd case NAME_ERR_DISKLIKE: 184168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 185168404Spjd "reserved disk name")); 186168404Spjd break; 187168404Spjd } 188168404Spjd } 189168404Spjd 190168404Spjd return (0); 191168404Spjd } 192168404Spjd 193168404Spjd if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 194168404Spjd if (hdl != NULL) 195168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 196168404Spjd "snapshot delimiter '@' in filesystem name")); 197168404Spjd return (0); 198168404Spjd } 199168404Spjd 200168404Spjd if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 201168404Spjd if (hdl != NULL) 202168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 203168404Spjd "missing '@' delimiter in snapshot name")); 204168404Spjd return (0); 205168404Spjd } 206168404Spjd 207185029Spjd if (modifying && strchr(path, '%') != NULL) { 208185029Spjd if (hdl != NULL) 209185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 210185029Spjd "invalid character %c in name"), '%'); 211185029Spjd return (0); 212185029Spjd } 213185029Spjd 214168404Spjd return (-1); 215168404Spjd} 216168404Spjd 217168404Spjdint 218168404Spjdzfs_name_valid(const char *name, zfs_type_t type) 219168404Spjd{ 220185029Spjd if (type == ZFS_TYPE_POOL) 221185029Spjd return (zpool_name_valid(NULL, B_FALSE, name)); 222185029Spjd return (zfs_validate_name(NULL, name, type, B_FALSE)); 223168404Spjd} 224168404Spjd 225168404Spjd/* 226168404Spjd * This function takes the raw DSL properties, and filters out the user-defined 227168404Spjd * properties into a separate nvlist. 228168404Spjd */ 229185029Spjdstatic nvlist_t * 230185029Spjdprocess_user_props(zfs_handle_t *zhp, nvlist_t *props) 231168404Spjd{ 232168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 233168404Spjd nvpair_t *elem; 234168404Spjd nvlist_t *propval; 235185029Spjd nvlist_t *nvl; 236168404Spjd 237185029Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 238185029Spjd (void) no_memory(hdl); 239185029Spjd return (NULL); 240185029Spjd } 241168404Spjd 242168404Spjd elem = NULL; 243185029Spjd while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 244168404Spjd if (!zfs_prop_user(nvpair_name(elem))) 245168404Spjd continue; 246168404Spjd 247168404Spjd verify(nvpair_value_nvlist(elem, &propval) == 0); 248185029Spjd if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 249185029Spjd nvlist_free(nvl); 250185029Spjd (void) no_memory(hdl); 251185029Spjd return (NULL); 252185029Spjd } 253168404Spjd } 254168404Spjd 255185029Spjd return (nvl); 256168404Spjd} 257168404Spjd 258185029Spjdstatic zpool_handle_t * 259185029Spjdzpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 260185029Spjd{ 261185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 262185029Spjd zpool_handle_t *zph; 263185029Spjd 264185029Spjd if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 265185029Spjd if (hdl->libzfs_pool_handles != NULL) 266185029Spjd zph->zpool_next = hdl->libzfs_pool_handles; 267185029Spjd hdl->libzfs_pool_handles = zph; 268185029Spjd } 269185029Spjd return (zph); 270185029Spjd} 271185029Spjd 272185029Spjdstatic zpool_handle_t * 273185029Spjdzpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 274185029Spjd{ 275185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 276185029Spjd zpool_handle_t *zph = hdl->libzfs_pool_handles; 277185029Spjd 278185029Spjd while ((zph != NULL) && 279185029Spjd (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 280185029Spjd zph = zph->zpool_next; 281185029Spjd return (zph); 282185029Spjd} 283185029Spjd 284168404Spjd/* 285185029Spjd * Returns a handle to the pool that contains the provided dataset. 286185029Spjd * If a handle to that pool already exists then that handle is returned. 287185029Spjd * Otherwise, a new handle is created and added to the list of handles. 288185029Spjd */ 289185029Spjdstatic zpool_handle_t * 290185029Spjdzpool_handle(zfs_handle_t *zhp) 291185029Spjd{ 292185029Spjd char *pool_name; 293185029Spjd int len; 294185029Spjd zpool_handle_t *zph; 295185029Spjd 296185029Spjd len = strcspn(zhp->zfs_name, "/@") + 1; 297185029Spjd pool_name = zfs_alloc(zhp->zfs_hdl, len); 298185029Spjd (void) strlcpy(pool_name, zhp->zfs_name, len); 299185029Spjd 300185029Spjd zph = zpool_find_handle(zhp, pool_name, len); 301185029Spjd if (zph == NULL) 302185029Spjd zph = zpool_add_handle(zhp, pool_name); 303185029Spjd 304185029Spjd free(pool_name); 305185029Spjd return (zph); 306185029Spjd} 307185029Spjd 308185029Spjdvoid 309185029Spjdzpool_free_handles(libzfs_handle_t *hdl) 310185029Spjd{ 311185029Spjd zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 312185029Spjd 313185029Spjd while (zph != NULL) { 314185029Spjd next = zph->zpool_next; 315185029Spjd zpool_close(zph); 316185029Spjd zph = next; 317185029Spjd } 318185029Spjd hdl->libzfs_pool_handles = NULL; 319185029Spjd} 320185029Spjd 321185029Spjd/* 322168404Spjd * Utility function to gather stats (objset and zpl) for the given object. 323168404Spjd */ 324219089Spjdstatic int 325209962Smmget_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 326168404Spjd{ 327168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 328168404Spjd 329209962Smm (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 330168404Spjd 331209962Smm while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 332168404Spjd if (errno == ENOMEM) { 333209962Smm if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 334168404Spjd return (-1); 335168404Spjd } 336168404Spjd } else { 337168404Spjd return (-1); 338168404Spjd } 339168404Spjd } 340209962Smm return (0); 341209962Smm} 342168404Spjd 343219089Spjd/* 344219089Spjd * Utility function to get the received properties of the given object. 345219089Spjd */ 346209962Smmstatic int 347219089Spjdget_recvd_props_ioctl(zfs_handle_t *zhp) 348219089Spjd{ 349219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 350219089Spjd nvlist_t *recvdprops; 351219089Spjd zfs_cmd_t zc = { 0 }; 352219089Spjd int err; 353219089Spjd 354219089Spjd if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 355219089Spjd return (-1); 356219089Spjd 357219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 358219089Spjd 359219089Spjd while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 360219089Spjd if (errno == ENOMEM) { 361219089Spjd if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 362219089Spjd return (-1); 363219089Spjd } 364219089Spjd } else { 365219089Spjd zcmd_free_nvlists(&zc); 366219089Spjd return (-1); 367219089Spjd } 368219089Spjd } 369219089Spjd 370219089Spjd err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 371219089Spjd zcmd_free_nvlists(&zc); 372219089Spjd if (err != 0) 373219089Spjd return (-1); 374219089Spjd 375219089Spjd nvlist_free(zhp->zfs_recvd_props); 376219089Spjd zhp->zfs_recvd_props = recvdprops; 377219089Spjd 378219089Spjd return (0); 379219089Spjd} 380219089Spjd 381219089Spjdstatic int 382209962Smmput_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 383209962Smm{ 384209962Smm nvlist_t *allprops, *userprops; 385168404Spjd 386209962Smm zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 387209962Smm 388209962Smm if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 389168404Spjd return (-1); 390168404Spjd } 391168404Spjd 392209962Smm /* 393209962Smm * XXX Why do we store the user props separately, in addition to 394209962Smm * storing them in zfs_props? 395209962Smm */ 396185029Spjd if ((userprops = process_user_props(zhp, allprops)) == NULL) { 397185029Spjd nvlist_free(allprops); 398168404Spjd return (-1); 399185029Spjd } 400168404Spjd 401185029Spjd nvlist_free(zhp->zfs_props); 402185029Spjd nvlist_free(zhp->zfs_user_props); 403185029Spjd 404185029Spjd zhp->zfs_props = allprops; 405185029Spjd zhp->zfs_user_props = userprops; 406185029Spjd 407168404Spjd return (0); 408168404Spjd} 409168404Spjd 410209962Smmstatic int 411209962Smmget_stats(zfs_handle_t *zhp) 412209962Smm{ 413209962Smm int rc = 0; 414209962Smm zfs_cmd_t zc = { 0 }; 415209962Smm 416209962Smm if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 417209962Smm return (-1); 418209962Smm if (get_stats_ioctl(zhp, &zc) != 0) 419209962Smm rc = -1; 420209962Smm else if (put_stats_zhdl(zhp, &zc) != 0) 421209962Smm rc = -1; 422209962Smm zcmd_free_nvlists(&zc); 423209962Smm return (rc); 424209962Smm} 425209962Smm 426168404Spjd/* 427168404Spjd * Refresh the properties currently stored in the handle. 428168404Spjd */ 429168404Spjdvoid 430168404Spjdzfs_refresh_properties(zfs_handle_t *zhp) 431168404Spjd{ 432168404Spjd (void) get_stats(zhp); 433168404Spjd} 434168404Spjd 435168404Spjd/* 436168404Spjd * Makes a handle from the given dataset name. Used by zfs_open() and 437168404Spjd * zfs_iter_* to create child handles on the fly. 438168404Spjd */ 439209962Smmstatic int 440209962Smmmake_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 441168404Spjd{ 442219089Spjd if (put_stats_zhdl(zhp, zc) != 0) 443209962Smm return (-1); 444168404Spjd 445168404Spjd /* 446168404Spjd * We've managed to open the dataset and gather statistics. Determine 447168404Spjd * the high-level type. 448168404Spjd */ 449168404Spjd if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 450168404Spjd zhp->zfs_head_type = ZFS_TYPE_VOLUME; 451168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 452168404Spjd zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 453168404Spjd else 454168404Spjd abort(); 455168404Spjd 456168404Spjd if (zhp->zfs_dmustats.dds_is_snapshot) 457168404Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 458168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 459168404Spjd zhp->zfs_type = ZFS_TYPE_VOLUME; 460168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 461168404Spjd zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 462168404Spjd else 463168404Spjd abort(); /* we should never see any other types */ 464168404Spjd 465219089Spjd if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 466219089Spjd return (-1); 467219089Spjd 468209962Smm return (0); 469209962Smm} 470209962Smm 471209962Smmzfs_handle_t * 472209962Smmmake_dataset_handle(libzfs_handle_t *hdl, const char *path) 473209962Smm{ 474209962Smm zfs_cmd_t zc = { 0 }; 475209962Smm 476209962Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 477209962Smm 478209962Smm if (zhp == NULL) 479209962Smm return (NULL); 480209962Smm 481209962Smm zhp->zfs_hdl = hdl; 482209962Smm (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 483209962Smm if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 484209962Smm free(zhp); 485209962Smm return (NULL); 486209962Smm } 487209962Smm if (get_stats_ioctl(zhp, &zc) == -1) { 488209962Smm zcmd_free_nvlists(&zc); 489209962Smm free(zhp); 490209962Smm return (NULL); 491209962Smm } 492209962Smm if (make_dataset_handle_common(zhp, &zc) == -1) { 493209962Smm free(zhp); 494209962Smm zhp = NULL; 495209962Smm } 496209962Smm zcmd_free_nvlists(&zc); 497168404Spjd return (zhp); 498168404Spjd} 499168404Spjd 500228103Smmzfs_handle_t * 501209962Smmmake_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 502209962Smm{ 503209962Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 504209962Smm 505209962Smm if (zhp == NULL) 506209962Smm return (NULL); 507209962Smm 508209962Smm zhp->zfs_hdl = hdl; 509209962Smm (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 510209962Smm if (make_dataset_handle_common(zhp, zc) == -1) { 511209962Smm free(zhp); 512209962Smm return (NULL); 513209962Smm } 514209962Smm return (zhp); 515209962Smm} 516209962Smm 517228103Smmzfs_handle_t * 518230438Spjdmake_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc) 519230438Spjd{ 520230438Spjd zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 521230438Spjd 522230438Spjd if (zhp == NULL) 523230438Spjd return (NULL); 524230438Spjd 525230438Spjd zhp->zfs_hdl = pzhp->zfs_hdl; 526230438Spjd (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 527230438Spjd zhp->zfs_head_type = pzhp->zfs_type; 528230438Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 529230438Spjd zhp->zpool_hdl = zpool_handle(zhp); 530230438Spjd return (zhp); 531230438Spjd} 532230438Spjd 533230438Spjdzfs_handle_t * 534228103Smmzfs_handle_dup(zfs_handle_t *zhp_orig) 535228103Smm{ 536228103Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 537228103Smm 538228103Smm if (zhp == NULL) 539228103Smm return (NULL); 540228103Smm 541228103Smm zhp->zfs_hdl = zhp_orig->zfs_hdl; 542228103Smm zhp->zpool_hdl = zhp_orig->zpool_hdl; 543228103Smm (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name, 544228103Smm sizeof (zhp->zfs_name)); 545228103Smm zhp->zfs_type = zhp_orig->zfs_type; 546228103Smm zhp->zfs_head_type = zhp_orig->zfs_head_type; 547228103Smm zhp->zfs_dmustats = zhp_orig->zfs_dmustats; 548228103Smm if (zhp_orig->zfs_props != NULL) { 549228103Smm if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) { 550228103Smm (void) no_memory(zhp->zfs_hdl); 551228103Smm zfs_close(zhp); 552228103Smm return (NULL); 553228103Smm } 554228103Smm } 555228103Smm if (zhp_orig->zfs_user_props != NULL) { 556228103Smm if (nvlist_dup(zhp_orig->zfs_user_props, 557228103Smm &zhp->zfs_user_props, 0) != 0) { 558228103Smm (void) no_memory(zhp->zfs_hdl); 559228103Smm zfs_close(zhp); 560228103Smm return (NULL); 561228103Smm } 562228103Smm } 563228103Smm if (zhp_orig->zfs_recvd_props != NULL) { 564228103Smm if (nvlist_dup(zhp_orig->zfs_recvd_props, 565228103Smm &zhp->zfs_recvd_props, 0)) { 566228103Smm (void) no_memory(zhp->zfs_hdl); 567228103Smm zfs_close(zhp); 568228103Smm return (NULL); 569228103Smm } 570228103Smm } 571228103Smm zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck; 572228103Smm if (zhp_orig->zfs_mntopts != NULL) { 573228103Smm zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl, 574228103Smm zhp_orig->zfs_mntopts); 575228103Smm } 576228103Smm zhp->zfs_props_table = zhp_orig->zfs_props_table; 577228103Smm return (zhp); 578228103Smm} 579228103Smm 580168404Spjd/* 581168404Spjd * Opens the given snapshot, filesystem, or volume. The 'types' 582168404Spjd * argument is a mask of acceptable types. The function will print an 583168404Spjd * appropriate error message and return NULL if it can't be opened. 584168404Spjd */ 585168404Spjdzfs_handle_t * 586168404Spjdzfs_open(libzfs_handle_t *hdl, const char *path, int types) 587168404Spjd{ 588168404Spjd zfs_handle_t *zhp; 589168404Spjd char errbuf[1024]; 590168404Spjd 591168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 592168404Spjd dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 593168404Spjd 594168404Spjd /* 595168404Spjd * Validate the name before we even try to open it. 596168404Spjd */ 597185029Spjd if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 598168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 599168404Spjd "invalid dataset name")); 600168404Spjd (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 601168404Spjd return (NULL); 602168404Spjd } 603168404Spjd 604168404Spjd /* 605168404Spjd * Try to get stats for the dataset, which will tell us if it exists. 606168404Spjd */ 607168404Spjd errno = 0; 608168404Spjd if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 609168404Spjd (void) zfs_standard_error(hdl, errno, errbuf); 610168404Spjd return (NULL); 611168404Spjd } 612168404Spjd 613168404Spjd if (!(types & zhp->zfs_type)) { 614168404Spjd (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 615168404Spjd zfs_close(zhp); 616168404Spjd return (NULL); 617168404Spjd } 618168404Spjd 619168404Spjd return (zhp); 620168404Spjd} 621168404Spjd 622168404Spjd/* 623168404Spjd * Release a ZFS handle. Nothing to do but free the associated memory. 624168404Spjd */ 625168404Spjdvoid 626168404Spjdzfs_close(zfs_handle_t *zhp) 627168404Spjd{ 628168404Spjd if (zhp->zfs_mntopts) 629168404Spjd free(zhp->zfs_mntopts); 630168404Spjd nvlist_free(zhp->zfs_props); 631168404Spjd nvlist_free(zhp->zfs_user_props); 632219089Spjd nvlist_free(zhp->zfs_recvd_props); 633168404Spjd free(zhp); 634168404Spjd} 635168404Spjd 636209962Smmtypedef struct mnttab_node { 637209962Smm struct mnttab mtn_mt; 638209962Smm avl_node_t mtn_node; 639209962Smm} mnttab_node_t; 640209962Smm 641209962Smmstatic int 642209962Smmlibzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 643209962Smm{ 644209962Smm const mnttab_node_t *mtn1 = arg1; 645209962Smm const mnttab_node_t *mtn2 = arg2; 646209962Smm int rv; 647209962Smm 648209962Smm rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 649209962Smm 650209962Smm if (rv == 0) 651209962Smm return (0); 652209962Smm return (rv > 0 ? 1 : -1); 653209962Smm} 654209962Smm 655209962Smmvoid 656209962Smmlibzfs_mnttab_init(libzfs_handle_t *hdl) 657209962Smm{ 658209962Smm assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 659209962Smm avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 660209962Smm sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 661209962Smm} 662209962Smm 663209962Smmvoid 664209962Smmlibzfs_mnttab_update(libzfs_handle_t *hdl) 665209962Smm{ 666209962Smm struct mnttab entry; 667209962Smm 668209962Smm rewind(hdl->libzfs_mnttab); 669209962Smm while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 670209962Smm mnttab_node_t *mtn; 671209962Smm 672209962Smm if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 673209962Smm continue; 674209962Smm mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 675209962Smm mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 676209962Smm mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 677209962Smm mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 678209962Smm mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 679209962Smm avl_add(&hdl->libzfs_mnttab_cache, mtn); 680209962Smm } 681209962Smm} 682209962Smm 683209962Smmvoid 684209962Smmlibzfs_mnttab_fini(libzfs_handle_t *hdl) 685209962Smm{ 686209962Smm void *cookie = NULL; 687209962Smm mnttab_node_t *mtn; 688209962Smm 689209962Smm while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 690209962Smm free(mtn->mtn_mt.mnt_special); 691209962Smm free(mtn->mtn_mt.mnt_mountp); 692209962Smm free(mtn->mtn_mt.mnt_fstype); 693209962Smm free(mtn->mtn_mt.mnt_mntopts); 694209962Smm free(mtn); 695209962Smm } 696209962Smm avl_destroy(&hdl->libzfs_mnttab_cache); 697209962Smm} 698209962Smm 699209962Smmvoid 700209962Smmlibzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 701209962Smm{ 702209962Smm hdl->libzfs_mnttab_enable = enable; 703209962Smm} 704209962Smm 705185029Spjdint 706209962Smmlibzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 707209962Smm struct mnttab *entry) 708209962Smm{ 709209962Smm mnttab_node_t find; 710209962Smm mnttab_node_t *mtn; 711209962Smm 712209962Smm if (!hdl->libzfs_mnttab_enable) { 713209962Smm struct mnttab srch = { 0 }; 714209962Smm 715209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 716209962Smm libzfs_mnttab_fini(hdl); 717209962Smm rewind(hdl->libzfs_mnttab); 718209962Smm srch.mnt_special = (char *)fsname; 719209962Smm srch.mnt_fstype = MNTTYPE_ZFS; 720209962Smm if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 721209962Smm return (0); 722209962Smm else 723209962Smm return (ENOENT); 724209962Smm } 725209962Smm 726209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 727209962Smm libzfs_mnttab_update(hdl); 728209962Smm 729209962Smm find.mtn_mt.mnt_special = (char *)fsname; 730209962Smm mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 731209962Smm if (mtn) { 732209962Smm *entry = mtn->mtn_mt; 733209962Smm return (0); 734209962Smm } 735209962Smm return (ENOENT); 736209962Smm} 737209962Smm 738209962Smmvoid 739209962Smmlibzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 740209962Smm const char *mountp, const char *mntopts) 741209962Smm{ 742209962Smm mnttab_node_t *mtn; 743209962Smm 744209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 745209962Smm return; 746209962Smm mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 747209962Smm mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 748209962Smm mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 749209962Smm mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 750209962Smm mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 751209962Smm avl_add(&hdl->libzfs_mnttab_cache, mtn); 752209962Smm} 753209962Smm 754209962Smmvoid 755209962Smmlibzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 756209962Smm{ 757209962Smm mnttab_node_t find; 758209962Smm mnttab_node_t *ret; 759209962Smm 760209962Smm find.mtn_mt.mnt_special = (char *)fsname; 761209962Smm if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 762209962Smm avl_remove(&hdl->libzfs_mnttab_cache, ret); 763209962Smm free(ret->mtn_mt.mnt_special); 764209962Smm free(ret->mtn_mt.mnt_mountp); 765209962Smm free(ret->mtn_mt.mnt_fstype); 766209962Smm free(ret->mtn_mt.mnt_mntopts); 767209962Smm free(ret); 768209962Smm } 769209962Smm} 770209962Smm 771209962Smmint 772185029Spjdzfs_spa_version(zfs_handle_t *zhp, int *spa_version) 773168404Spjd{ 774185029Spjd zpool_handle_t *zpool_handle = zhp->zpool_hdl; 775168404Spjd 776185029Spjd if (zpool_handle == NULL) 777168404Spjd return (-1); 778168404Spjd 779185029Spjd *spa_version = zpool_get_prop_int(zpool_handle, 780185029Spjd ZPOOL_PROP_VERSION, NULL); 781168404Spjd return (0); 782168404Spjd} 783168404Spjd 784168404Spjd/* 785185029Spjd * The choice of reservation property depends on the SPA version. 786168404Spjd */ 787168404Spjdstatic int 788185029Spjdzfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 789168404Spjd{ 790185029Spjd int spa_version; 791168404Spjd 792185029Spjd if (zfs_spa_version(zhp, &spa_version) < 0) 793168404Spjd return (-1); 794168404Spjd 795185029Spjd if (spa_version >= SPA_VERSION_REFRESERVATION) 796185029Spjd *resv_prop = ZFS_PROP_REFRESERVATION; 797185029Spjd else 798185029Spjd *resv_prop = ZFS_PROP_RESERVATION; 799168404Spjd 800168404Spjd return (0); 801168404Spjd} 802168404Spjd 803168404Spjd/* 804168404Spjd * Given an nvlist of properties to set, validates that they are correct, and 805168404Spjd * parses any numeric properties (index, boolean, etc) if they are specified as 806168404Spjd * strings. 807168404Spjd */ 808168404Spjdnvlist_t * 809185029Spjdzfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 810185029Spjd uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 811168404Spjd{ 812168404Spjd nvpair_t *elem; 813168404Spjd uint64_t intval; 814168404Spjd char *strval; 815185029Spjd zfs_prop_t prop; 816168404Spjd nvlist_t *ret; 817185029Spjd int chosen_normal = -1; 818185029Spjd int chosen_utf = -1; 819168404Spjd 820168404Spjd if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 821168404Spjd (void) no_memory(hdl); 822168404Spjd return (NULL); 823168404Spjd } 824168404Spjd 825209962Smm /* 826209962Smm * Make sure this property is valid and applies to this type. 827209962Smm */ 828209962Smm 829168404Spjd elem = NULL; 830168404Spjd while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 831185029Spjd const char *propname = nvpair_name(elem); 832168404Spjd 833209962Smm prop = zfs_name_to_prop(propname); 834209962Smm if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 835185029Spjd /* 836209962Smm * This is a user property: make sure it's a 837185029Spjd * string, and that it's less than ZAP_MAXNAMELEN. 838185029Spjd */ 839185029Spjd if (nvpair_type(elem) != DATA_TYPE_STRING) { 840185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 841185029Spjd "'%s' must be a string"), propname); 842185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 843185029Spjd goto error; 844168404Spjd } 845168404Spjd 846185029Spjd if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 847185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 848185029Spjd "property name '%s' is too long"), 849185029Spjd propname); 850185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 851185029Spjd goto error; 852185029Spjd } 853185029Spjd 854168404Spjd (void) nvpair_value_string(elem, &strval); 855168404Spjd if (nvlist_add_string(ret, propname, strval) != 0) { 856168404Spjd (void) no_memory(hdl); 857168404Spjd goto error; 858168404Spjd } 859168404Spjd continue; 860168404Spjd } 861168404Spjd 862209962Smm /* 863209962Smm * Currently, only user properties can be modified on 864209962Smm * snapshots. 865209962Smm */ 866185029Spjd if (type == ZFS_TYPE_SNAPSHOT) { 867185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 868185029Spjd "this property can not be modified for snapshots")); 869185029Spjd (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 870185029Spjd goto error; 871185029Spjd } 872168404Spjd 873209962Smm if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 874209962Smm zfs_userquota_prop_t uqtype; 875209962Smm char newpropname[128]; 876209962Smm char domain[128]; 877209962Smm uint64_t rid; 878209962Smm uint64_t valary[3]; 879209962Smm 880209962Smm if (userquota_propname_decode(propname, zoned, 881209962Smm &uqtype, domain, sizeof (domain), &rid) != 0) { 882209962Smm zfs_error_aux(hdl, 883209962Smm dgettext(TEXT_DOMAIN, 884209962Smm "'%s' has an invalid user/group name"), 885209962Smm propname); 886209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 887209962Smm goto error; 888209962Smm } 889209962Smm 890209962Smm if (uqtype != ZFS_PROP_USERQUOTA && 891209962Smm uqtype != ZFS_PROP_GROUPQUOTA) { 892209962Smm zfs_error_aux(hdl, 893209962Smm dgettext(TEXT_DOMAIN, "'%s' is readonly"), 894209962Smm propname); 895209962Smm (void) zfs_error(hdl, EZFS_PROPREADONLY, 896209962Smm errbuf); 897209962Smm goto error; 898209962Smm } 899209962Smm 900209962Smm if (nvpair_type(elem) == DATA_TYPE_STRING) { 901209962Smm (void) nvpair_value_string(elem, &strval); 902209962Smm if (strcmp(strval, "none") == 0) { 903209962Smm intval = 0; 904209962Smm } else if (zfs_nicestrtonum(hdl, 905209962Smm strval, &intval) != 0) { 906209962Smm (void) zfs_error(hdl, 907209962Smm EZFS_BADPROP, errbuf); 908209962Smm goto error; 909209962Smm } 910209962Smm } else if (nvpair_type(elem) == 911209962Smm DATA_TYPE_UINT64) { 912209962Smm (void) nvpair_value_uint64(elem, &intval); 913209962Smm if (intval == 0) { 914209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 915209962Smm "use 'none' to disable " 916209962Smm "userquota/groupquota")); 917209962Smm goto error; 918209962Smm } 919209962Smm } else { 920209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 921209962Smm "'%s' must be a number"), propname); 922209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 923209962Smm goto error; 924209962Smm } 925209962Smm 926219089Spjd /* 927219089Spjd * Encode the prop name as 928219089Spjd * userquota@<hex-rid>-domain, to make it easy 929219089Spjd * for the kernel to decode. 930219089Spjd */ 931209962Smm (void) snprintf(newpropname, sizeof (newpropname), 932219089Spjd "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 933219089Spjd (longlong_t)rid, domain); 934209962Smm valary[0] = uqtype; 935209962Smm valary[1] = rid; 936209962Smm valary[2] = intval; 937209962Smm if (nvlist_add_uint64_array(ret, newpropname, 938209962Smm valary, 3) != 0) { 939209962Smm (void) no_memory(hdl); 940209962Smm goto error; 941209962Smm } 942209962Smm continue; 943228103Smm } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) { 944228103Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 945228103Smm "'%s' is readonly"), 946228103Smm propname); 947228103Smm (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 948228103Smm goto error; 949209962Smm } 950209962Smm 951209962Smm if (prop == ZPROP_INVAL) { 952209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 953209962Smm "invalid property '%s'"), propname); 954209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 955209962Smm goto error; 956209962Smm } 957209962Smm 958168404Spjd if (!zfs_prop_valid_for_type(prop, type)) { 959168404Spjd zfs_error_aux(hdl, 960168404Spjd dgettext(TEXT_DOMAIN, "'%s' does not " 961168404Spjd "apply to datasets of this type"), propname); 962168404Spjd (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 963168404Spjd goto error; 964168404Spjd } 965168404Spjd 966168404Spjd if (zfs_prop_readonly(prop) && 967185029Spjd (!zfs_prop_setonce(prop) || zhp != NULL)) { 968168404Spjd zfs_error_aux(hdl, 969168404Spjd dgettext(TEXT_DOMAIN, "'%s' is readonly"), 970168404Spjd propname); 971168404Spjd (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 972168404Spjd goto error; 973168404Spjd } 974168404Spjd 975185029Spjd if (zprop_parse_value(hdl, elem, prop, type, ret, 976185029Spjd &strval, &intval, errbuf) != 0) 977185029Spjd goto error; 978185029Spjd 979168404Spjd /* 980185029Spjd * Perform some additional checks for specific properties. 981168404Spjd */ 982185029Spjd switch (prop) { 983185029Spjd case ZFS_PROP_VERSION: 984185029Spjd { 985185029Spjd int version; 986168404Spjd 987185029Spjd if (zhp == NULL) 988185029Spjd break; 989185029Spjd version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 990185029Spjd if (intval < version) { 991168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 992185029Spjd "Can not downgrade; already at version %u"), 993185029Spjd version); 994168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 995168404Spjd goto error; 996168404Spjd } 997168404Spjd break; 998168404Spjd } 999168404Spjd 1000168404Spjd case ZFS_PROP_RECORDSIZE: 1001168404Spjd case ZFS_PROP_VOLBLOCKSIZE: 1002168404Spjd /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 1003168404Spjd if (intval < SPA_MINBLOCKSIZE || 1004168404Spjd intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 1005168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1006168404Spjd "'%s' must be power of 2 from %u " 1007168404Spjd "to %uk"), propname, 1008168404Spjd (uint_t)SPA_MINBLOCKSIZE, 1009168404Spjd (uint_t)SPA_MAXBLOCKSIZE >> 10); 1010168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1011168404Spjd goto error; 1012168404Spjd } 1013168404Spjd break; 1014168404Spjd 1015219089Spjd case ZFS_PROP_MLSLABEL: 1016219089Spjd { 1017219089Spjd#ifdef sun 1018219089Spjd /* 1019219089Spjd * Verify the mlslabel string and convert to 1020219089Spjd * internal hex label string. 1021219089Spjd */ 1022219089Spjd 1023219089Spjd m_label_t *new_sl; 1024219089Spjd char *hex = NULL; /* internal label string */ 1025219089Spjd 1026219089Spjd /* Default value is already OK. */ 1027219089Spjd if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 1028219089Spjd break; 1029219089Spjd 1030219089Spjd /* Verify the label can be converted to binary form */ 1031219089Spjd if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 1032219089Spjd (str_to_label(strval, &new_sl, MAC_LABEL, 1033219089Spjd L_NO_CORRECTION, NULL) == -1)) { 1034219089Spjd goto badlabel; 1035168404Spjd } 1036168404Spjd 1037219089Spjd /* Now translate to hex internal label string */ 1038219089Spjd if (label_to_str(new_sl, &hex, M_INTERNAL, 1039219089Spjd DEF_NAMES) != 0) { 1040219089Spjd if (hex) 1041219089Spjd free(hex); 1042219089Spjd goto badlabel; 1043219089Spjd } 1044219089Spjd m_label_free(new_sl); 1045219089Spjd 1046219089Spjd /* If string is already in internal form, we're done. */ 1047219089Spjd if (strcmp(strval, hex) == 0) { 1048219089Spjd free(hex); 1049219089Spjd break; 1050219089Spjd } 1051219089Spjd 1052219089Spjd /* Replace the label string with the internal form. */ 1053219089Spjd (void) nvlist_remove(ret, zfs_prop_to_name(prop), 1054219089Spjd DATA_TYPE_STRING); 1055219089Spjd verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 1056219089Spjd hex) == 0); 1057219089Spjd free(hex); 1058219089Spjd 1059168404Spjd break; 1060168404Spjd 1061219089Spjdbadlabel: 1062219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1063219089Spjd "invalid mlslabel '%s'"), strval); 1064219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1065219089Spjd m_label_free(new_sl); /* OK if null */ 1066219089Spjd#else /* !sun */ 1067219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1068219089Spjd "mlslabel is not supported on FreeBSD")); 1069219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1070219089Spjd#endif /* !sun */ 1071219089Spjd goto error; 1072219089Spjd 1073219089Spjd } 1074219089Spjd 1075168404Spjd case ZFS_PROP_MOUNTPOINT: 1076185029Spjd { 1077185029Spjd namecheck_err_t why; 1078185029Spjd 1079168404Spjd if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1080168404Spjd strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1081168404Spjd break; 1082168404Spjd 1083185029Spjd if (mountpoint_namecheck(strval, &why)) { 1084185029Spjd switch (why) { 1085185029Spjd case NAME_ERR_LEADING_SLASH: 1086185029Spjd zfs_error_aux(hdl, 1087185029Spjd dgettext(TEXT_DOMAIN, 1088185029Spjd "'%s' must be an absolute path, " 1089185029Spjd "'none', or 'legacy'"), propname); 1090185029Spjd break; 1091185029Spjd case NAME_ERR_TOOLONG: 1092185029Spjd zfs_error_aux(hdl, 1093185029Spjd dgettext(TEXT_DOMAIN, 1094185029Spjd "component of '%s' is too long"), 1095185029Spjd propname); 1096185029Spjd break; 1097185029Spjd } 1098168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1099168404Spjd goto error; 1100168404Spjd } 1101185029Spjd } 1102185029Spjd 1103168404Spjd /*FALLTHRU*/ 1104168404Spjd 1105185029Spjd case ZFS_PROP_SHARESMB: 1106168404Spjd case ZFS_PROP_SHARENFS: 1107168404Spjd /* 1108185029Spjd * For the mountpoint and sharenfs or sharesmb 1109185029Spjd * properties, check if it can be set in a 1110185029Spjd * global/non-global zone based on 1111168404Spjd * the zoned property value: 1112168404Spjd * 1113168404Spjd * global zone non-global zone 1114168404Spjd * -------------------------------------------------- 1115168404Spjd * zoned=on mountpoint (no) mountpoint (yes) 1116168404Spjd * sharenfs (no) sharenfs (no) 1117185029Spjd * sharesmb (no) sharesmb (no) 1118168404Spjd * 1119168404Spjd * zoned=off mountpoint (yes) N/A 1120168404Spjd * sharenfs (yes) 1121185029Spjd * sharesmb (yes) 1122168404Spjd */ 1123168404Spjd if (zoned) { 1124168404Spjd if (getzoneid() == GLOBAL_ZONEID) { 1125168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1126168404Spjd "'%s' cannot be set on " 1127168404Spjd "dataset in a non-global zone"), 1128168404Spjd propname); 1129168404Spjd (void) zfs_error(hdl, EZFS_ZONED, 1130168404Spjd errbuf); 1131168404Spjd goto error; 1132185029Spjd } else if (prop == ZFS_PROP_SHARENFS || 1133185029Spjd prop == ZFS_PROP_SHARESMB) { 1134168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1135168404Spjd "'%s' cannot be set in " 1136168404Spjd "a non-global zone"), propname); 1137168404Spjd (void) zfs_error(hdl, EZFS_ZONED, 1138168404Spjd errbuf); 1139168404Spjd goto error; 1140168404Spjd } 1141168404Spjd } else if (getzoneid() != GLOBAL_ZONEID) { 1142168404Spjd /* 1143168404Spjd * If zoned property is 'off', this must be in 1144209962Smm * a global zone. If not, something is wrong. 1145168404Spjd */ 1146168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1147168404Spjd "'%s' cannot be set while dataset " 1148168404Spjd "'zoned' property is set"), propname); 1149168404Spjd (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1150168404Spjd goto error; 1151168404Spjd } 1152168404Spjd 1153168404Spjd /* 1154185029Spjd * At this point, it is legitimate to set the 1155185029Spjd * property. Now we want to make sure that the 1156185029Spjd * property value is valid if it is sharenfs. 1157168404Spjd */ 1158185029Spjd if ((prop == ZFS_PROP_SHARENFS || 1159185029Spjd prop == ZFS_PROP_SHARESMB) && 1160185029Spjd strcmp(strval, "on") != 0 && 1161185029Spjd strcmp(strval, "off") != 0) { 1162185029Spjd zfs_share_proto_t proto; 1163168404Spjd 1164185029Spjd if (prop == ZFS_PROP_SHARESMB) 1165185029Spjd proto = PROTO_SMB; 1166185029Spjd else 1167185029Spjd proto = PROTO_NFS; 1168185029Spjd 1169185029Spjd /* 1170185029Spjd * Must be an valid sharing protocol 1171185029Spjd * option string so init the libshare 1172185029Spjd * in order to enable the parser and 1173185029Spjd * then parse the options. We use the 1174185029Spjd * control API since we don't care about 1175185029Spjd * the current configuration and don't 1176185029Spjd * want the overhead of loading it 1177185029Spjd * until we actually do something. 1178185029Spjd */ 1179185029Spjd 1180185029Spjd if (zfs_init_libshare(hdl, 1181185029Spjd SA_INIT_CONTROL_API) != SA_OK) { 1182185029Spjd /* 1183185029Spjd * An error occurred so we can't do 1184185029Spjd * anything 1185185029Spjd */ 1186185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1187185029Spjd "'%s' cannot be set: problem " 1188185029Spjd "in share initialization"), 1189185029Spjd propname); 1190185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1191185029Spjd errbuf); 1192185029Spjd goto error; 1193185029Spjd } 1194185029Spjd 1195185029Spjd if (zfs_parse_options(strval, proto) != SA_OK) { 1196185029Spjd /* 1197185029Spjd * There was an error in parsing so 1198185029Spjd * deal with it by issuing an error 1199185029Spjd * message and leaving after 1200185029Spjd * uninitializing the the libshare 1201185029Spjd * interface. 1202185029Spjd */ 1203185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1204185029Spjd "'%s' cannot be set to invalid " 1205185029Spjd "options"), propname); 1206185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1207185029Spjd errbuf); 1208185029Spjd zfs_uninit_libshare(hdl); 1209185029Spjd goto error; 1210185029Spjd } 1211185029Spjd zfs_uninit_libshare(hdl); 1212168404Spjd } 1213185029Spjd 1214168404Spjd break; 1215185029Spjd case ZFS_PROP_UTF8ONLY: 1216185029Spjd chosen_utf = (int)intval; 1217185029Spjd break; 1218185029Spjd case ZFS_PROP_NORMALIZE: 1219185029Spjd chosen_normal = (int)intval; 1220185029Spjd break; 1221168404Spjd } 1222168404Spjd 1223168404Spjd /* 1224168404Spjd * For changes to existing volumes, we have some additional 1225168404Spjd * checks to enforce. 1226168404Spjd */ 1227168404Spjd if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1228168404Spjd uint64_t volsize = zfs_prop_get_int(zhp, 1229168404Spjd ZFS_PROP_VOLSIZE); 1230168404Spjd uint64_t blocksize = zfs_prop_get_int(zhp, 1231168404Spjd ZFS_PROP_VOLBLOCKSIZE); 1232168404Spjd char buf[64]; 1233168404Spjd 1234168404Spjd switch (prop) { 1235168404Spjd case ZFS_PROP_RESERVATION: 1236185029Spjd case ZFS_PROP_REFRESERVATION: 1237168404Spjd if (intval > volsize) { 1238168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1239168404Spjd "'%s' is greater than current " 1240168404Spjd "volume size"), propname); 1241168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1242168404Spjd errbuf); 1243168404Spjd goto error; 1244168404Spjd } 1245168404Spjd break; 1246168404Spjd 1247168404Spjd case ZFS_PROP_VOLSIZE: 1248168404Spjd if (intval % blocksize != 0) { 1249168404Spjd zfs_nicenum(blocksize, buf, 1250168404Spjd sizeof (buf)); 1251168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1252168404Spjd "'%s' must be a multiple of " 1253168404Spjd "volume block size (%s)"), 1254168404Spjd propname, buf); 1255168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1256168404Spjd errbuf); 1257168404Spjd goto error; 1258168404Spjd } 1259168404Spjd 1260168404Spjd if (intval == 0) { 1261168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1262168404Spjd "'%s' cannot be zero"), 1263168404Spjd propname); 1264168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1265168404Spjd errbuf); 1266168404Spjd goto error; 1267168404Spjd } 1268168404Spjd break; 1269168404Spjd } 1270168404Spjd } 1271168404Spjd } 1272168404Spjd 1273168404Spjd /* 1274185029Spjd * If normalization was chosen, but no UTF8 choice was made, 1275185029Spjd * enforce rejection of non-UTF8 names. 1276185029Spjd * 1277185029Spjd * If normalization was chosen, but rejecting non-UTF8 names 1278185029Spjd * was explicitly not chosen, it is an error. 1279185029Spjd */ 1280185029Spjd if (chosen_normal > 0 && chosen_utf < 0) { 1281185029Spjd if (nvlist_add_uint64(ret, 1282185029Spjd zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1283185029Spjd (void) no_memory(hdl); 1284185029Spjd goto error; 1285185029Spjd } 1286185029Spjd } else if (chosen_normal > 0 && chosen_utf == 0) { 1287185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1288185029Spjd "'%s' must be set 'on' if normalization chosen"), 1289185029Spjd zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1290185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1291185029Spjd goto error; 1292185029Spjd } 1293219089Spjd return (ret); 1294185029Spjd 1295219089Spjderror: 1296219089Spjd nvlist_free(ret); 1297219089Spjd return (NULL); 1298219089Spjd} 1299219089Spjd 1300219089Spjdint 1301219089Spjdzfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) 1302219089Spjd{ 1303219089Spjd uint64_t old_volsize; 1304219089Spjd uint64_t new_volsize; 1305219089Spjd uint64_t old_reservation; 1306219089Spjd uint64_t new_reservation; 1307219089Spjd zfs_prop_t resv_prop; 1308219089Spjd 1309185029Spjd /* 1310168404Spjd * If this is an existing volume, and someone is setting the volsize, 1311168404Spjd * make sure that it matches the reservation, or add it if necessary. 1312168404Spjd */ 1313219089Spjd old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1314219089Spjd if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1315219089Spjd return (-1); 1316219089Spjd old_reservation = zfs_prop_get_int(zhp, resv_prop); 1317219089Spjd if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) != 1318219089Spjd old_reservation) || nvlist_lookup_uint64(nvl, 1319219089Spjd zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) { 1320219089Spjd return (0); 1321219089Spjd } 1322219089Spjd if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1323219089Spjd &new_volsize) != 0) 1324219089Spjd return (-1); 1325219089Spjd new_reservation = zvol_volsize_to_reservation(new_volsize, 1326219089Spjd zhp->zfs_props); 1327219089Spjd if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), 1328219089Spjd new_reservation) != 0) { 1329219089Spjd (void) no_memory(zhp->zfs_hdl); 1330219089Spjd return (-1); 1331219089Spjd } 1332219089Spjd return (1); 1333219089Spjd} 1334168404Spjd 1335219089Spjdvoid 1336219089Spjdzfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 1337219089Spjd char *errbuf) 1338219089Spjd{ 1339219089Spjd switch (err) { 1340185029Spjd 1341219089Spjd case ENOSPC: 1342219089Spjd /* 1343219089Spjd * For quotas and reservations, ENOSPC indicates 1344219089Spjd * something different; setting a quota or reservation 1345219089Spjd * doesn't use any disk space. 1346219089Spjd */ 1347219089Spjd switch (prop) { 1348219089Spjd case ZFS_PROP_QUOTA: 1349219089Spjd case ZFS_PROP_REFQUOTA: 1350219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1351219089Spjd "size is less than current used or " 1352219089Spjd "reserved space")); 1353219089Spjd (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1354219089Spjd break; 1355219089Spjd 1356219089Spjd case ZFS_PROP_RESERVATION: 1357219089Spjd case ZFS_PROP_REFRESERVATION: 1358219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1359219089Spjd "size is greater than available space")); 1360219089Spjd (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1361219089Spjd break; 1362219089Spjd 1363219089Spjd default: 1364219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1365219089Spjd break; 1366168404Spjd } 1367219089Spjd break; 1368219089Spjd 1369219089Spjd case EBUSY: 1370219089Spjd (void) zfs_standard_error(hdl, EBUSY, errbuf); 1371219089Spjd break; 1372219089Spjd 1373219089Spjd case EROFS: 1374219089Spjd (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 1375219089Spjd break; 1376219089Spjd 1377219089Spjd case ENOTSUP: 1378219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1379219089Spjd "pool and or dataset must be upgraded to set this " 1380219089Spjd "property or value")); 1381219089Spjd (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1382219089Spjd break; 1383219089Spjd 1384219089Spjd case ERANGE: 1385219089Spjd if (prop == ZFS_PROP_COMPRESSION) { 1386219089Spjd (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1387219089Spjd "property setting is not allowed on " 1388219089Spjd "bootable datasets")); 1389219089Spjd (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 1390219089Spjd } else { 1391219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1392219089Spjd } 1393219089Spjd break; 1394219089Spjd 1395219089Spjd case EINVAL: 1396219089Spjd if (prop == ZPROP_INVAL) { 1397219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1398219089Spjd } else { 1399219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1400219089Spjd } 1401219089Spjd break; 1402219089Spjd 1403219089Spjd case EOVERFLOW: 1404219089Spjd /* 1405219089Spjd * This platform can't address a volume this big. 1406219089Spjd */ 1407219089Spjd#ifdef _ILP32 1408219089Spjd if (prop == ZFS_PROP_VOLSIZE) { 1409219089Spjd (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1410219089Spjd break; 1411219089Spjd } 1412219089Spjd#endif 1413219089Spjd /* FALLTHROUGH */ 1414219089Spjd default: 1415219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1416168404Spjd } 1417168404Spjd} 1418168404Spjd 1419168404Spjd/* 1420168404Spjd * Given a property name and value, set the property for the given dataset. 1421168404Spjd */ 1422168404Spjdint 1423168404Spjdzfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1424168404Spjd{ 1425168404Spjd zfs_cmd_t zc = { 0 }; 1426168404Spjd int ret = -1; 1427168404Spjd prop_changelist_t *cl = NULL; 1428168404Spjd char errbuf[1024]; 1429168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 1430168404Spjd nvlist_t *nvl = NULL, *realprops; 1431168404Spjd zfs_prop_t prop; 1432185029Spjd boolean_t do_prefix; 1433185029Spjd uint64_t idx; 1434219089Spjd int added_resv; 1435168404Spjd 1436168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 1437168404Spjd dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1438168404Spjd zhp->zfs_name); 1439168404Spjd 1440168404Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1441168404Spjd nvlist_add_string(nvl, propname, propval) != 0) { 1442168404Spjd (void) no_memory(hdl); 1443168404Spjd goto error; 1444168404Spjd } 1445168404Spjd 1446185029Spjd if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1447168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1448168404Spjd goto error; 1449185029Spjd 1450168404Spjd nvlist_free(nvl); 1451168404Spjd nvl = realprops; 1452168404Spjd 1453168404Spjd prop = zfs_name_to_prop(propname); 1454168404Spjd 1455168404Spjd /* We don't support those properties on FreeBSD. */ 1456168404Spjd switch (prop) { 1457197867Strasz case ZFS_PROP_DEVICES: 1458168404Spjd case ZFS_PROP_ISCSIOPTIONS: 1459197867Strasz case ZFS_PROP_XATTR: 1460197867Strasz case ZFS_PROP_VSCAN: 1461197867Strasz case ZFS_PROP_NBMAND: 1462219089Spjd case ZFS_PROP_MLSLABEL: 1463168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 1464168404Spjd "property '%s' not supported on FreeBSD", propname); 1465168404Spjd ret = zfs_error(hdl, EZFS_PERM, errbuf); 1466168404Spjd goto error; 1467168404Spjd } 1468168404Spjd 1469219089Spjd if (prop == ZFS_PROP_VOLSIZE) { 1470219089Spjd if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) 1471219089Spjd goto error; 1472219089Spjd } 1473219089Spjd 1474185029Spjd if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1475168404Spjd goto error; 1476168404Spjd 1477168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 1478168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1479168404Spjd "child dataset with inherited mountpoint is used " 1480168404Spjd "in a non-global zone")); 1481168404Spjd ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1482168404Spjd goto error; 1483168404Spjd } 1484168404Spjd 1485185029Spjd /* 1486185029Spjd * If the dataset's canmount property is being set to noauto, 1487185029Spjd * then we want to prevent unmounting & remounting it. 1488185029Spjd */ 1489185029Spjd do_prefix = !((prop == ZFS_PROP_CANMOUNT) && 1490185029Spjd (zprop_string_to_index(prop, propval, &idx, 1491185029Spjd ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); 1492185029Spjd 1493185029Spjd if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1494168404Spjd goto error; 1495168404Spjd 1496168404Spjd /* 1497168404Spjd * Execute the corresponding ioctl() to set this property. 1498168404Spjd */ 1499168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1500168404Spjd 1501185029Spjd if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1502168404Spjd goto error; 1503168404Spjd 1504185029Spjd ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1505209962Smm 1506168404Spjd if (ret != 0) { 1507219089Spjd zfs_setprop_error(hdl, prop, errno, errbuf); 1508219089Spjd if (added_resv && errno == ENOSPC) { 1509219089Spjd /* clean up the volsize property we tried to set */ 1510219089Spjd uint64_t old_volsize = zfs_prop_get_int(zhp, 1511219089Spjd ZFS_PROP_VOLSIZE); 1512219089Spjd nvlist_free(nvl); 1513219089Spjd zcmd_free_nvlists(&zc); 1514219089Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 1515219089Spjd goto error; 1516219089Spjd if (nvlist_add_uint64(nvl, 1517219089Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1518219089Spjd old_volsize) != 0) 1519219089Spjd goto error; 1520219089Spjd if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1521219089Spjd goto error; 1522219089Spjd (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1523168404Spjd } 1524168404Spjd } else { 1525185029Spjd if (do_prefix) 1526185029Spjd ret = changelist_postfix(cl); 1527185029Spjd 1528168404Spjd /* 1529168404Spjd * Refresh the statistics so the new property value 1530168404Spjd * is reflected. 1531168404Spjd */ 1532185029Spjd if (ret == 0) 1533168404Spjd (void) get_stats(zhp); 1534168404Spjd } 1535168404Spjd 1536168404Spjderror: 1537168404Spjd nvlist_free(nvl); 1538168404Spjd zcmd_free_nvlists(&zc); 1539168404Spjd if (cl) 1540168404Spjd changelist_free(cl); 1541168404Spjd return (ret); 1542168404Spjd} 1543168404Spjd 1544168404Spjd/* 1545219089Spjd * Given a property, inherit the value from the parent dataset, or if received 1546219089Spjd * is TRUE, revert to the received value, if any. 1547168404Spjd */ 1548168404Spjdint 1549219089Spjdzfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1550168404Spjd{ 1551168404Spjd zfs_cmd_t zc = { 0 }; 1552168404Spjd int ret; 1553168404Spjd prop_changelist_t *cl; 1554168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 1555168404Spjd char errbuf[1024]; 1556168404Spjd zfs_prop_t prop; 1557168404Spjd 1558168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 1559168404Spjd "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1560168404Spjd 1561219089Spjd zc.zc_cookie = received; 1562185029Spjd if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1563168404Spjd /* 1564168404Spjd * For user properties, the amount of work we have to do is very 1565168404Spjd * small, so just do it here. 1566168404Spjd */ 1567168404Spjd if (!zfs_prop_user(propname)) { 1568168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1569168404Spjd "invalid property")); 1570168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1571168404Spjd } 1572168404Spjd 1573168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1574168404Spjd (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1575168404Spjd 1576185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1577168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 1578168404Spjd 1579168404Spjd return (0); 1580168404Spjd } 1581168404Spjd 1582168404Spjd /* 1583168404Spjd * Verify that this property is inheritable. 1584168404Spjd */ 1585168404Spjd if (zfs_prop_readonly(prop)) 1586168404Spjd return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1587168404Spjd 1588219089Spjd if (!zfs_prop_inheritable(prop) && !received) 1589168404Spjd return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1590168404Spjd 1591168404Spjd /* 1592168404Spjd * Check to see if the value applies to this type 1593168404Spjd */ 1594168404Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1595168404Spjd return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1596168404Spjd 1597168404Spjd /* 1598219089Spjd * Normalize the name, to get rid of shorthand abbreviations. 1599168404Spjd */ 1600168404Spjd propname = zfs_prop_to_name(prop); 1601168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1602168404Spjd (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1603168404Spjd 1604168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1605168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 1606168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1607168404Spjd "dataset is used in a non-global zone")); 1608168404Spjd return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1609168404Spjd } 1610168404Spjd 1611168404Spjd /* 1612168404Spjd * Determine datasets which will be affected by this change, if any. 1613168404Spjd */ 1614185029Spjd if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1615168404Spjd return (-1); 1616168404Spjd 1617168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 1618168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1619168404Spjd "child dataset with inherited mountpoint is used " 1620168404Spjd "in a non-global zone")); 1621168404Spjd ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1622168404Spjd goto error; 1623168404Spjd } 1624168404Spjd 1625168404Spjd if ((ret = changelist_prefix(cl)) != 0) 1626168404Spjd goto error; 1627168404Spjd 1628185029Spjd if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 1629168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 1630168404Spjd } else { 1631168404Spjd 1632168404Spjd if ((ret = changelist_postfix(cl)) != 0) 1633168404Spjd goto error; 1634168404Spjd 1635168404Spjd /* 1636168404Spjd * Refresh the statistics so the new property is reflected. 1637168404Spjd */ 1638168404Spjd (void) get_stats(zhp); 1639168404Spjd } 1640168404Spjd 1641168404Spjderror: 1642168404Spjd changelist_free(cl); 1643168404Spjd return (ret); 1644168404Spjd} 1645168404Spjd 1646168404Spjd/* 1647168404Spjd * True DSL properties are stored in an nvlist. The following two functions 1648168404Spjd * extract them appropriately. 1649168404Spjd */ 1650168404Spjdstatic uint64_t 1651168404Spjdgetprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 1652168404Spjd{ 1653168404Spjd nvlist_t *nv; 1654168404Spjd uint64_t value; 1655168404Spjd 1656168404Spjd *source = NULL; 1657168404Spjd if (nvlist_lookup_nvlist(zhp->zfs_props, 1658168404Spjd zfs_prop_to_name(prop), &nv) == 0) { 1659185029Spjd verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1660185029Spjd (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 1661168404Spjd } else { 1662205198Sdelphij verify(!zhp->zfs_props_table || 1663205198Sdelphij zhp->zfs_props_table[prop] == B_TRUE); 1664168404Spjd value = zfs_prop_default_numeric(prop); 1665168404Spjd *source = ""; 1666168404Spjd } 1667168404Spjd 1668168404Spjd return (value); 1669168404Spjd} 1670168404Spjd 1671168404Spjdstatic char * 1672168404Spjdgetprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 1673168404Spjd{ 1674168404Spjd nvlist_t *nv; 1675168404Spjd char *value; 1676168404Spjd 1677168404Spjd *source = NULL; 1678168404Spjd if (nvlist_lookup_nvlist(zhp->zfs_props, 1679168404Spjd zfs_prop_to_name(prop), &nv) == 0) { 1680185029Spjd verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1681185029Spjd (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 1682168404Spjd } else { 1683205198Sdelphij verify(!zhp->zfs_props_table || 1684205198Sdelphij zhp->zfs_props_table[prop] == B_TRUE); 1685168404Spjd if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 1686168404Spjd value = ""; 1687168404Spjd *source = ""; 1688168404Spjd } 1689168404Spjd 1690168404Spjd return (value); 1691168404Spjd} 1692168404Spjd 1693219089Spjdstatic boolean_t 1694219089Spjdzfs_is_recvd_props_mode(zfs_handle_t *zhp) 1695219089Spjd{ 1696219089Spjd return (zhp->zfs_props == zhp->zfs_recvd_props); 1697219089Spjd} 1698219089Spjd 1699219089Spjdstatic void 1700219089Spjdzfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 1701219089Spjd{ 1702219089Spjd *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 1703219089Spjd zhp->zfs_props = zhp->zfs_recvd_props; 1704219089Spjd} 1705219089Spjd 1706219089Spjdstatic void 1707219089Spjdzfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 1708219089Spjd{ 1709219089Spjd zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 1710219089Spjd *cookie = 0; 1711219089Spjd} 1712219089Spjd 1713168404Spjd/* 1714168404Spjd * Internal function for getting a numeric property. Both zfs_prop_get() and 1715168404Spjd * zfs_prop_get_int() are built using this interface. 1716168404Spjd * 1717168404Spjd * Certain properties can be overridden using 'mount -o'. In this case, scan 1718168404Spjd * the contents of the /etc/mnttab entry, searching for the appropriate options. 1719168404Spjd * If they differ from the on-disk values, report the current values and mark 1720168404Spjd * the source "temporary". 1721168404Spjd */ 1722168404Spjdstatic int 1723185029Spjdget_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 1724168404Spjd char **source, uint64_t *val) 1725168404Spjd{ 1726185029Spjd zfs_cmd_t zc = { 0 }; 1727185029Spjd nvlist_t *zplprops = NULL; 1728168404Spjd struct mnttab mnt; 1729168404Spjd char *mntopt_on = NULL; 1730168404Spjd char *mntopt_off = NULL; 1731219089Spjd boolean_t received = zfs_is_recvd_props_mode(zhp); 1732168404Spjd 1733168404Spjd *source = NULL; 1734168404Spjd 1735168404Spjd switch (prop) { 1736168404Spjd case ZFS_PROP_ATIME: 1737168404Spjd mntopt_on = MNTOPT_ATIME; 1738168404Spjd mntopt_off = MNTOPT_NOATIME; 1739168404Spjd break; 1740168404Spjd 1741168404Spjd case ZFS_PROP_DEVICES: 1742168404Spjd mntopt_on = MNTOPT_DEVICES; 1743168404Spjd mntopt_off = MNTOPT_NODEVICES; 1744168404Spjd break; 1745168404Spjd 1746168404Spjd case ZFS_PROP_EXEC: 1747168404Spjd mntopt_on = MNTOPT_EXEC; 1748168404Spjd mntopt_off = MNTOPT_NOEXEC; 1749168404Spjd break; 1750168404Spjd 1751168404Spjd case ZFS_PROP_READONLY: 1752168404Spjd mntopt_on = MNTOPT_RO; 1753168404Spjd mntopt_off = MNTOPT_RW; 1754168404Spjd break; 1755168404Spjd 1756168404Spjd case ZFS_PROP_SETUID: 1757168404Spjd mntopt_on = MNTOPT_SETUID; 1758168404Spjd mntopt_off = MNTOPT_NOSETUID; 1759168404Spjd break; 1760168404Spjd 1761168404Spjd case ZFS_PROP_XATTR: 1762168404Spjd mntopt_on = MNTOPT_XATTR; 1763168404Spjd mntopt_off = MNTOPT_NOXATTR; 1764168404Spjd break; 1765185029Spjd 1766185029Spjd case ZFS_PROP_NBMAND: 1767185029Spjd mntopt_on = MNTOPT_NBMAND; 1768185029Spjd mntopt_off = MNTOPT_NONBMAND; 1769185029Spjd break; 1770168404Spjd } 1771168404Spjd 1772168404Spjd /* 1773168404Spjd * Because looking up the mount options is potentially expensive 1774168404Spjd * (iterating over all of /etc/mnttab), we defer its calculation until 1775168404Spjd * we're looking up a property which requires its presence. 1776168404Spjd */ 1777168404Spjd if (!zhp->zfs_mntcheck && 1778168404Spjd (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1779209962Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 1780209962Smm struct mnttab entry; 1781168404Spjd 1782209962Smm if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1783209962Smm zhp->zfs_mntopts = zfs_strdup(hdl, 1784168404Spjd entry.mnt_mntopts); 1785168404Spjd if (zhp->zfs_mntopts == NULL) 1786168404Spjd return (-1); 1787168404Spjd } 1788168404Spjd 1789168404Spjd zhp->zfs_mntcheck = B_TRUE; 1790168404Spjd } 1791168404Spjd 1792168404Spjd if (zhp->zfs_mntopts == NULL) 1793168404Spjd mnt.mnt_mntopts = ""; 1794168404Spjd else 1795168404Spjd mnt.mnt_mntopts = zhp->zfs_mntopts; 1796168404Spjd 1797168404Spjd switch (prop) { 1798168404Spjd case ZFS_PROP_ATIME: 1799168404Spjd case ZFS_PROP_DEVICES: 1800168404Spjd case ZFS_PROP_EXEC: 1801168404Spjd case ZFS_PROP_READONLY: 1802168404Spjd case ZFS_PROP_SETUID: 1803168404Spjd case ZFS_PROP_XATTR: 1804185029Spjd case ZFS_PROP_NBMAND: 1805168404Spjd *val = getprop_uint64(zhp, prop, source); 1806168404Spjd 1807219089Spjd if (received) 1808219089Spjd break; 1809219089Spjd 1810168404Spjd if (hasmntopt(&mnt, mntopt_on) && !*val) { 1811168404Spjd *val = B_TRUE; 1812168404Spjd if (src) 1813185029Spjd *src = ZPROP_SRC_TEMPORARY; 1814168404Spjd } else if (hasmntopt(&mnt, mntopt_off) && *val) { 1815168404Spjd *val = B_FALSE; 1816168404Spjd if (src) 1817185029Spjd *src = ZPROP_SRC_TEMPORARY; 1818168404Spjd } 1819168404Spjd break; 1820168404Spjd 1821168404Spjd case ZFS_PROP_CANMOUNT: 1822219089Spjd case ZFS_PROP_VOLSIZE: 1823168404Spjd case ZFS_PROP_QUOTA: 1824185029Spjd case ZFS_PROP_REFQUOTA: 1825168404Spjd case ZFS_PROP_RESERVATION: 1826185029Spjd case ZFS_PROP_REFRESERVATION: 1827168404Spjd *val = getprop_uint64(zhp, prop, source); 1828219089Spjd 1829219089Spjd if (*source == NULL) { 1830219089Spjd /* not default, must be local */ 1831168404Spjd *source = zhp->zfs_name; 1832219089Spjd } 1833168404Spjd break; 1834168404Spjd 1835168404Spjd case ZFS_PROP_MOUNTED: 1836168404Spjd *val = (zhp->zfs_mntopts != NULL); 1837168404Spjd break; 1838168404Spjd 1839168404Spjd case ZFS_PROP_NUMCLONES: 1840168404Spjd *val = zhp->zfs_dmustats.dds_num_clones; 1841168404Spjd break; 1842168404Spjd 1843185029Spjd case ZFS_PROP_VERSION: 1844185029Spjd case ZFS_PROP_NORMALIZE: 1845185029Spjd case ZFS_PROP_UTF8ONLY: 1846185029Spjd case ZFS_PROP_CASE: 1847185029Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1848185029Spjd zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 1849185029Spjd return (-1); 1850185029Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1851185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1852185029Spjd zcmd_free_nvlists(&zc); 1853219089Spjd return (-1); 1854185029Spjd } 1855185029Spjd if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1856185029Spjd nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1857185029Spjd val) != 0) { 1858185029Spjd zcmd_free_nvlists(&zc); 1859219089Spjd return (-1); 1860185029Spjd } 1861185029Spjd if (zplprops) 1862185029Spjd nvlist_free(zplprops); 1863185029Spjd zcmd_free_nvlists(&zc); 1864185029Spjd break; 1865185029Spjd 1866168404Spjd default: 1867185029Spjd switch (zfs_prop_get_type(prop)) { 1868185029Spjd case PROP_TYPE_NUMBER: 1869185029Spjd case PROP_TYPE_INDEX: 1870185029Spjd *val = getprop_uint64(zhp, prop, source); 1871185029Spjd /* 1872209962Smm * If we tried to use a default value for a 1873185029Spjd * readonly property, it means that it was not 1874219089Spjd * present. 1875185029Spjd */ 1876185029Spjd if (zfs_prop_readonly(prop) && 1877219089Spjd *source != NULL && (*source)[0] == '\0') { 1878219089Spjd *source = NULL; 1879185029Spjd } 1880185029Spjd break; 1881185029Spjd 1882185029Spjd case PROP_TYPE_STRING: 1883185029Spjd default: 1884185029Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1885185029Spjd "cannot get non-numeric property")); 1886185029Spjd return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 1887185029Spjd dgettext(TEXT_DOMAIN, "internal error"))); 1888185029Spjd } 1889168404Spjd } 1890168404Spjd 1891168404Spjd return (0); 1892168404Spjd} 1893168404Spjd 1894168404Spjd/* 1895168404Spjd * Calculate the source type, given the raw source string. 1896168404Spjd */ 1897168404Spjdstatic void 1898185029Spjdget_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 1899168404Spjd char *statbuf, size_t statlen) 1900168404Spjd{ 1901185029Spjd if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 1902168404Spjd return; 1903168404Spjd 1904168404Spjd if (source == NULL) { 1905185029Spjd *srctype = ZPROP_SRC_NONE; 1906168404Spjd } else if (source[0] == '\0') { 1907185029Spjd *srctype = ZPROP_SRC_DEFAULT; 1908219089Spjd } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 1909219089Spjd *srctype = ZPROP_SRC_RECEIVED; 1910168404Spjd } else { 1911168404Spjd if (strcmp(source, zhp->zfs_name) == 0) { 1912185029Spjd *srctype = ZPROP_SRC_LOCAL; 1913168404Spjd } else { 1914168404Spjd (void) strlcpy(statbuf, source, statlen); 1915185029Spjd *srctype = ZPROP_SRC_INHERITED; 1916168404Spjd } 1917168404Spjd } 1918168404Spjd 1919168404Spjd} 1920168404Spjd 1921219089Spjdint 1922219089Spjdzfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 1923219089Spjd size_t proplen, boolean_t literal) 1924219089Spjd{ 1925219089Spjd zfs_prop_t prop; 1926219089Spjd int err = 0; 1927219089Spjd 1928219089Spjd if (zhp->zfs_recvd_props == NULL) 1929219089Spjd if (get_recvd_props_ioctl(zhp) != 0) 1930219089Spjd return (-1); 1931219089Spjd 1932219089Spjd prop = zfs_name_to_prop(propname); 1933219089Spjd 1934219089Spjd if (prop != ZPROP_INVAL) { 1935219089Spjd uint64_t cookie; 1936219089Spjd if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 1937219089Spjd return (-1); 1938219089Spjd zfs_set_recvd_props_mode(zhp, &cookie); 1939219089Spjd err = zfs_prop_get(zhp, prop, propbuf, proplen, 1940219089Spjd NULL, NULL, 0, literal); 1941219089Spjd zfs_unset_recvd_props_mode(zhp, &cookie); 1942219089Spjd } else { 1943219089Spjd nvlist_t *propval; 1944219089Spjd char *recvdval; 1945219089Spjd if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 1946219089Spjd propname, &propval) != 0) 1947219089Spjd return (-1); 1948219089Spjd verify(nvlist_lookup_string(propval, ZPROP_VALUE, 1949219089Spjd &recvdval) == 0); 1950219089Spjd (void) strlcpy(propbuf, recvdval, proplen); 1951219089Spjd } 1952219089Spjd 1953219089Spjd return (err == 0 ? 0 : -1); 1954219089Spjd} 1955219089Spjd 1956228103Smmstatic int 1957228103Smmget_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen) 1958228103Smm{ 1959228103Smm nvlist_t *value; 1960228103Smm nvpair_t *pair; 1961228103Smm 1962228103Smm value = zfs_get_clones_nvl(zhp); 1963228103Smm if (value == NULL) 1964228103Smm return (-1); 1965228103Smm 1966228103Smm propbuf[0] = '\0'; 1967228103Smm for (pair = nvlist_next_nvpair(value, NULL); pair != NULL; 1968228103Smm pair = nvlist_next_nvpair(value, pair)) { 1969228103Smm if (propbuf[0] != '\0') 1970228103Smm (void) strlcat(propbuf, ",", proplen); 1971228103Smm (void) strlcat(propbuf, nvpair_name(pair), proplen); 1972228103Smm } 1973228103Smm 1974228103Smm return (0); 1975228103Smm} 1976228103Smm 1977228103Smmstruct get_clones_arg { 1978228103Smm uint64_t numclones; 1979228103Smm nvlist_t *value; 1980228103Smm const char *origin; 1981228103Smm char buf[ZFS_MAXNAMELEN]; 1982228103Smm}; 1983228103Smm 1984228103Smmint 1985228103Smmget_clones_cb(zfs_handle_t *zhp, void *arg) 1986228103Smm{ 1987228103Smm struct get_clones_arg *gca = arg; 1988228103Smm 1989228103Smm if (gca->numclones == 0) { 1990228103Smm zfs_close(zhp); 1991228103Smm return (0); 1992228103Smm } 1993228103Smm 1994228103Smm if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf), 1995228103Smm NULL, NULL, 0, B_TRUE) != 0) 1996228103Smm goto out; 1997228103Smm if (strcmp(gca->buf, gca->origin) == 0) { 1998228103Smm if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) != 0) { 1999228103Smm zfs_close(zhp); 2000228103Smm return (no_memory(zhp->zfs_hdl)); 2001228103Smm } 2002228103Smm gca->numclones--; 2003228103Smm } 2004228103Smm 2005228103Smmout: 2006228103Smm (void) zfs_iter_children(zhp, get_clones_cb, gca); 2007228103Smm zfs_close(zhp); 2008228103Smm return (0); 2009228103Smm} 2010228103Smm 2011228103Smmnvlist_t * 2012228103Smmzfs_get_clones_nvl(zfs_handle_t *zhp) 2013228103Smm{ 2014228103Smm nvlist_t *nv, *value; 2015228103Smm 2016228103Smm if (nvlist_lookup_nvlist(zhp->zfs_props, 2017228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) { 2018228103Smm struct get_clones_arg gca; 2019228103Smm 2020228103Smm /* 2021228103Smm * if this is a snapshot, then the kernel wasn't able 2022228103Smm * to get the clones. Do it by slowly iterating. 2023228103Smm */ 2024228103Smm if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) 2025228103Smm return (NULL); 2026228103Smm if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0) 2027228103Smm return (NULL); 2028228103Smm if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) { 2029228103Smm nvlist_free(nv); 2030228103Smm return (NULL); 2031228103Smm } 2032228103Smm 2033228103Smm gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES); 2034228103Smm gca.value = value; 2035228103Smm gca.origin = zhp->zfs_name; 2036228103Smm 2037228103Smm if (gca.numclones != 0) { 2038228103Smm zfs_handle_t *root; 2039228103Smm char pool[ZFS_MAXNAMELEN]; 2040228103Smm char *cp = pool; 2041228103Smm 2042228103Smm /* get the pool name */ 2043228103Smm (void) strlcpy(pool, zhp->zfs_name, sizeof (pool)); 2044228103Smm (void) strsep(&cp, "/@"); 2045228103Smm root = zfs_open(zhp->zfs_hdl, pool, 2046228103Smm ZFS_TYPE_FILESYSTEM); 2047228103Smm 2048228103Smm (void) get_clones_cb(root, &gca); 2049228103Smm } 2050228103Smm 2051228103Smm if (gca.numclones != 0 || 2052228103Smm nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 || 2053228103Smm nvlist_add_nvlist(zhp->zfs_props, 2054228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) { 2055228103Smm nvlist_free(nv); 2056228103Smm nvlist_free(value); 2057228103Smm return (NULL); 2058228103Smm } 2059228103Smm nvlist_free(nv); 2060228103Smm nvlist_free(value); 2061228103Smm verify(0 == nvlist_lookup_nvlist(zhp->zfs_props, 2062228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), &nv)); 2063228103Smm } 2064228103Smm 2065228103Smm verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0); 2066228103Smm 2067228103Smm return (value); 2068228103Smm} 2069228103Smm 2070168404Spjd/* 2071168404Spjd * Retrieve a property from the given object. If 'literal' is specified, then 2072168404Spjd * numbers are left as exact values. Otherwise, numbers are converted to a 2073168404Spjd * human-readable form. 2074168404Spjd * 2075168404Spjd * Returns 0 on success, or -1 on error. 2076168404Spjd */ 2077168404Spjdint 2078168404Spjdzfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 2079185029Spjd zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 2080168404Spjd{ 2081168404Spjd char *source = NULL; 2082168404Spjd uint64_t val; 2083168404Spjd char *str; 2084168404Spjd const char *strval; 2085219089Spjd boolean_t received = zfs_is_recvd_props_mode(zhp); 2086168404Spjd 2087168404Spjd /* 2088168404Spjd * Check to see if this property applies to our object 2089168404Spjd */ 2090168404Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 2091168404Spjd return (-1); 2092168404Spjd 2093219089Spjd if (received && zfs_prop_readonly(prop)) 2094219089Spjd return (-1); 2095219089Spjd 2096168404Spjd if (src) 2097185029Spjd *src = ZPROP_SRC_NONE; 2098168404Spjd 2099168404Spjd switch (prop) { 2100168404Spjd case ZFS_PROP_CREATION: 2101168404Spjd /* 2102168404Spjd * 'creation' is a time_t stored in the statistics. We convert 2103168404Spjd * this into a string unless 'literal' is specified. 2104168404Spjd */ 2105168404Spjd { 2106168404Spjd val = getprop_uint64(zhp, prop, &source); 2107168404Spjd time_t time = (time_t)val; 2108168404Spjd struct tm t; 2109168404Spjd 2110168404Spjd if (literal || 2111168404Spjd localtime_r(&time, &t) == NULL || 2112168404Spjd strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 2113168404Spjd &t) == 0) 2114168404Spjd (void) snprintf(propbuf, proplen, "%llu", val); 2115168404Spjd } 2116168404Spjd break; 2117168404Spjd 2118168404Spjd case ZFS_PROP_MOUNTPOINT: 2119168404Spjd /* 2120168404Spjd * Getting the precise mountpoint can be tricky. 2121168404Spjd * 2122168404Spjd * - for 'none' or 'legacy', return those values. 2123168404Spjd * - for inherited mountpoints, we want to take everything 2124168404Spjd * after our ancestor and append it to the inherited value. 2125168404Spjd * 2126168404Spjd * If the pool has an alternate root, we want to prepend that 2127168404Spjd * root to any values we return. 2128168404Spjd */ 2129185029Spjd 2130168404Spjd str = getprop_string(zhp, prop, &source); 2131168404Spjd 2132185029Spjd if (str[0] == '/') { 2133185029Spjd char buf[MAXPATHLEN]; 2134185029Spjd char *root = buf; 2135219089Spjd const char *relpath; 2136168404Spjd 2137219089Spjd /* 2138219089Spjd * If we inherit the mountpoint, even from a dataset 2139219089Spjd * with a received value, the source will be the path of 2140219089Spjd * the dataset we inherit from. If source is 2141219089Spjd * ZPROP_SOURCE_VAL_RECVD, the received value is not 2142219089Spjd * inherited. 2143219089Spjd */ 2144219089Spjd if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 2145219089Spjd relpath = ""; 2146219089Spjd } else { 2147219089Spjd relpath = zhp->zfs_name + strlen(source); 2148219089Spjd if (relpath[0] == '/') 2149219089Spjd relpath++; 2150219089Spjd } 2151185029Spjd 2152185029Spjd if ((zpool_get_prop(zhp->zpool_hdl, 2153185029Spjd ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) || 2154185029Spjd (strcmp(root, "-") == 0)) 2155185029Spjd root[0] = '\0'; 2156185029Spjd /* 2157185029Spjd * Special case an alternate root of '/'. This will 2158185029Spjd * avoid having multiple leading slashes in the 2159185029Spjd * mountpoint path. 2160185029Spjd */ 2161185029Spjd if (strcmp(root, "/") == 0) 2162185029Spjd root++; 2163185029Spjd 2164185029Spjd /* 2165185029Spjd * If the mountpoint is '/' then skip over this 2166185029Spjd * if we are obtaining either an alternate root or 2167185029Spjd * an inherited mountpoint. 2168185029Spjd */ 2169185029Spjd if (str[1] == '\0' && (root[0] != '\0' || 2170185029Spjd relpath[0] != '\0')) 2171168404Spjd str++; 2172168404Spjd 2173168404Spjd if (relpath[0] == '\0') 2174168404Spjd (void) snprintf(propbuf, proplen, "%s%s", 2175168404Spjd root, str); 2176168404Spjd else 2177168404Spjd (void) snprintf(propbuf, proplen, "%s%s%s%s", 2178168404Spjd root, str, relpath[0] == '@' ? "" : "/", 2179168404Spjd relpath); 2180168404Spjd } else { 2181168404Spjd /* 'legacy' or 'none' */ 2182168404Spjd (void) strlcpy(propbuf, str, proplen); 2183168404Spjd } 2184168404Spjd 2185168404Spjd break; 2186168404Spjd 2187168404Spjd case ZFS_PROP_ORIGIN: 2188168404Spjd (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 2189168404Spjd proplen); 2190168404Spjd /* 2191168404Spjd * If there is no parent at all, return failure to indicate that 2192168404Spjd * it doesn't apply to this dataset. 2193168404Spjd */ 2194168404Spjd if (propbuf[0] == '\0') 2195168404Spjd return (-1); 2196168404Spjd break; 2197168404Spjd 2198228103Smm case ZFS_PROP_CLONES: 2199228103Smm if (get_clones_string(zhp, propbuf, proplen) != 0) 2200228103Smm return (-1); 2201228103Smm break; 2202228103Smm 2203168404Spjd case ZFS_PROP_QUOTA: 2204185029Spjd case ZFS_PROP_REFQUOTA: 2205168404Spjd case ZFS_PROP_RESERVATION: 2206185029Spjd case ZFS_PROP_REFRESERVATION: 2207185029Spjd 2208168404Spjd if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2209168404Spjd return (-1); 2210168404Spjd 2211168404Spjd /* 2212168404Spjd * If quota or reservation is 0, we translate this into 'none' 2213168404Spjd * (unless literal is set), and indicate that it's the default 2214168404Spjd * value. Otherwise, we print the number nicely and indicate 2215168404Spjd * that its set locally. 2216168404Spjd */ 2217168404Spjd if (val == 0) { 2218168404Spjd if (literal) 2219168404Spjd (void) strlcpy(propbuf, "0", proplen); 2220168404Spjd else 2221168404Spjd (void) strlcpy(propbuf, "none", proplen); 2222168404Spjd } else { 2223168404Spjd if (literal) 2224168404Spjd (void) snprintf(propbuf, proplen, "%llu", 2225168404Spjd (u_longlong_t)val); 2226168404Spjd else 2227168404Spjd zfs_nicenum(val, propbuf, proplen); 2228168404Spjd } 2229168404Spjd break; 2230168404Spjd 2231223623Smm case ZFS_PROP_REFRATIO: 2232168404Spjd case ZFS_PROP_COMPRESSRATIO: 2233168404Spjd if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2234168404Spjd return (-1); 2235219089Spjd (void) snprintf(propbuf, proplen, "%llu.%02llux", 2236219089Spjd (u_longlong_t)(val / 100), 2237219089Spjd (u_longlong_t)(val % 100)); 2238168404Spjd break; 2239168404Spjd 2240168404Spjd case ZFS_PROP_TYPE: 2241168404Spjd switch (zhp->zfs_type) { 2242168404Spjd case ZFS_TYPE_FILESYSTEM: 2243168404Spjd str = "filesystem"; 2244168404Spjd break; 2245168404Spjd case ZFS_TYPE_VOLUME: 2246168404Spjd str = "volume"; 2247168404Spjd break; 2248168404Spjd case ZFS_TYPE_SNAPSHOT: 2249168404Spjd str = "snapshot"; 2250168404Spjd break; 2251168404Spjd default: 2252168404Spjd abort(); 2253168404Spjd } 2254168404Spjd (void) snprintf(propbuf, proplen, "%s", str); 2255168404Spjd break; 2256168404Spjd 2257168404Spjd case ZFS_PROP_MOUNTED: 2258168404Spjd /* 2259168404Spjd * The 'mounted' property is a pseudo-property that described 2260168404Spjd * whether the filesystem is currently mounted. Even though 2261168404Spjd * it's a boolean value, the typical values of "on" and "off" 2262168404Spjd * don't make sense, so we translate to "yes" and "no". 2263168404Spjd */ 2264168404Spjd if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 2265168404Spjd src, &source, &val) != 0) 2266168404Spjd return (-1); 2267168404Spjd if (val) 2268168404Spjd (void) strlcpy(propbuf, "yes", proplen); 2269168404Spjd else 2270168404Spjd (void) strlcpy(propbuf, "no", proplen); 2271168404Spjd break; 2272168404Spjd 2273168404Spjd case ZFS_PROP_NAME: 2274168404Spjd /* 2275168404Spjd * The 'name' property is a pseudo-property derived from the 2276168404Spjd * dataset name. It is presented as a real property to simplify 2277168404Spjd * consumers. 2278168404Spjd */ 2279168404Spjd (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2280168404Spjd break; 2281168404Spjd 2282219089Spjd case ZFS_PROP_MLSLABEL: 2283219089Spjd { 2284219089Spjd#ifdef sun 2285219089Spjd m_label_t *new_sl = NULL; 2286219089Spjd char *ascii = NULL; /* human readable label */ 2287219089Spjd 2288219089Spjd (void) strlcpy(propbuf, 2289219089Spjd getprop_string(zhp, prop, &source), proplen); 2290219089Spjd 2291219089Spjd if (literal || (strcasecmp(propbuf, 2292219089Spjd ZFS_MLSLABEL_DEFAULT) == 0)) 2293219089Spjd break; 2294219089Spjd 2295219089Spjd /* 2296219089Spjd * Try to translate the internal hex string to 2297219089Spjd * human-readable output. If there are any 2298219089Spjd * problems just use the hex string. 2299219089Spjd */ 2300219089Spjd 2301219089Spjd if (str_to_label(propbuf, &new_sl, MAC_LABEL, 2302219089Spjd L_NO_CORRECTION, NULL) == -1) { 2303219089Spjd m_label_free(new_sl); 2304219089Spjd break; 2305219089Spjd } 2306219089Spjd 2307219089Spjd if (label_to_str(new_sl, &ascii, M_LABEL, 2308219089Spjd DEF_NAMES) != 0) { 2309219089Spjd if (ascii) 2310219089Spjd free(ascii); 2311219089Spjd m_label_free(new_sl); 2312219089Spjd break; 2313219089Spjd } 2314219089Spjd m_label_free(new_sl); 2315219089Spjd 2316219089Spjd (void) strlcpy(propbuf, ascii, proplen); 2317219089Spjd free(ascii); 2318219089Spjd#else /* !sun */ 2319219089Spjd propbuf[0] = '\0'; 2320219089Spjd#endif /* !sun */ 2321219089Spjd } 2322219089Spjd break; 2323219089Spjd 2324168404Spjd default: 2325185029Spjd switch (zfs_prop_get_type(prop)) { 2326185029Spjd case PROP_TYPE_NUMBER: 2327185029Spjd if (get_numeric_property(zhp, prop, src, 2328185029Spjd &source, &val) != 0) 2329185029Spjd return (-1); 2330185029Spjd if (literal) 2331185029Spjd (void) snprintf(propbuf, proplen, "%llu", 2332185029Spjd (u_longlong_t)val); 2333185029Spjd else 2334185029Spjd zfs_nicenum(val, propbuf, proplen); 2335185029Spjd break; 2336185029Spjd 2337185029Spjd case PROP_TYPE_STRING: 2338185029Spjd (void) strlcpy(propbuf, 2339185029Spjd getprop_string(zhp, prop, &source), proplen); 2340185029Spjd break; 2341185029Spjd 2342185029Spjd case PROP_TYPE_INDEX: 2343185029Spjd if (get_numeric_property(zhp, prop, src, 2344185029Spjd &source, &val) != 0) 2345185029Spjd return (-1); 2346185029Spjd if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2347185029Spjd return (-1); 2348185029Spjd (void) strlcpy(propbuf, strval, proplen); 2349185029Spjd break; 2350185029Spjd 2351185029Spjd default: 2352185029Spjd abort(); 2353185029Spjd } 2354168404Spjd } 2355168404Spjd 2356168404Spjd get_source(zhp, src, source, statbuf, statlen); 2357168404Spjd 2358168404Spjd return (0); 2359168404Spjd} 2360168404Spjd 2361168404Spjd/* 2362168404Spjd * Utility function to get the given numeric property. Does no validation that 2363168404Spjd * the given property is the appropriate type; should only be used with 2364168404Spjd * hard-coded property types. 2365168404Spjd */ 2366168404Spjduint64_t 2367168404Spjdzfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2368168404Spjd{ 2369168404Spjd char *source; 2370168404Spjd uint64_t val; 2371168404Spjd 2372185029Spjd (void) get_numeric_property(zhp, prop, NULL, &source, &val); 2373168404Spjd 2374168404Spjd return (val); 2375168404Spjd} 2376168404Spjd 2377185029Spjdint 2378185029Spjdzfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 2379185029Spjd{ 2380185029Spjd char buf[64]; 2381185029Spjd 2382209962Smm (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 2383185029Spjd return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 2384185029Spjd} 2385185029Spjd 2386168404Spjd/* 2387168404Spjd * Similar to zfs_prop_get(), but returns the value as an integer. 2388168404Spjd */ 2389168404Spjdint 2390168404Spjdzfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2391185029Spjd zprop_source_t *src, char *statbuf, size_t statlen) 2392168404Spjd{ 2393168404Spjd char *source; 2394168404Spjd 2395168404Spjd /* 2396168404Spjd * Check to see if this property applies to our object 2397168404Spjd */ 2398185029Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2399168404Spjd return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 2400168404Spjd dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 2401168404Spjd zfs_prop_to_name(prop))); 2402185029Spjd } 2403168404Spjd 2404168404Spjd if (src) 2405185029Spjd *src = ZPROP_SRC_NONE; 2406168404Spjd 2407168404Spjd if (get_numeric_property(zhp, prop, src, &source, value) != 0) 2408168404Spjd return (-1); 2409168404Spjd 2410168404Spjd get_source(zhp, src, source, statbuf, statlen); 2411168404Spjd 2412168404Spjd return (0); 2413168404Spjd} 2414168404Spjd 2415209962Smmstatic int 2416209962Smmidmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 2417209962Smm char **domainp, idmap_rid_t *ridp) 2418209962Smm{ 2419209962Smm#ifdef sun 2420209962Smm idmap_get_handle_t *get_hdl = NULL; 2421209962Smm idmap_stat status; 2422209962Smm int err = EINVAL; 2423209962Smm 2424219089Spjd if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS) 2425209962Smm goto out; 2426209962Smm 2427209962Smm if (isuser) { 2428209962Smm err = idmap_get_sidbyuid(get_hdl, id, 2429209962Smm IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2430209962Smm } else { 2431209962Smm err = idmap_get_sidbygid(get_hdl, id, 2432209962Smm IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2433209962Smm } 2434209962Smm if (err == IDMAP_SUCCESS && 2435209962Smm idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 2436209962Smm status == IDMAP_SUCCESS) 2437209962Smm err = 0; 2438209962Smm else 2439209962Smm err = EINVAL; 2440209962Smmout: 2441209962Smm if (get_hdl) 2442209962Smm idmap_get_destroy(get_hdl); 2443209962Smm return (err); 2444209962Smm#else /* !sun */ 2445209962Smm assert(!"invalid code path"); 2446209962Smm#endif /* !sun */ 2447209962Smm} 2448209962Smm 2449168404Spjd/* 2450209962Smm * convert the propname into parameters needed by kernel 2451209962Smm * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 2452209962Smm * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 2453209962Smm */ 2454209962Smmstatic int 2455209962Smmuserquota_propname_decode(const char *propname, boolean_t zoned, 2456209962Smm zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 2457209962Smm{ 2458209962Smm zfs_userquota_prop_t type; 2459209962Smm char *cp, *end; 2460209962Smm char *numericsid = NULL; 2461209962Smm boolean_t isuser; 2462209962Smm 2463209962Smm domain[0] = '\0'; 2464209962Smm 2465209962Smm /* Figure out the property type ({user|group}{quota|space}) */ 2466209962Smm for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 2467209962Smm if (strncmp(propname, zfs_userquota_prop_prefixes[type], 2468209962Smm strlen(zfs_userquota_prop_prefixes[type])) == 0) 2469209962Smm break; 2470209962Smm } 2471209962Smm if (type == ZFS_NUM_USERQUOTA_PROPS) 2472209962Smm return (EINVAL); 2473209962Smm *typep = type; 2474209962Smm 2475209962Smm isuser = (type == ZFS_PROP_USERQUOTA || 2476209962Smm type == ZFS_PROP_USERUSED); 2477209962Smm 2478209962Smm cp = strchr(propname, '@') + 1; 2479209962Smm 2480209962Smm if (strchr(cp, '@')) { 2481209962Smm#ifdef sun 2482209962Smm /* 2483209962Smm * It's a SID name (eg "user@domain") that needs to be 2484209962Smm * turned into S-1-domainID-RID. 2485209962Smm */ 2486209962Smm directory_error_t e; 2487209962Smm if (zoned && getzoneid() == GLOBAL_ZONEID) 2488209962Smm return (ENOENT); 2489209962Smm if (isuser) { 2490209962Smm e = directory_sid_from_user_name(NULL, 2491209962Smm cp, &numericsid); 2492209962Smm } else { 2493209962Smm e = directory_sid_from_group_name(NULL, 2494209962Smm cp, &numericsid); 2495209962Smm } 2496209962Smm if (e != NULL) { 2497209962Smm directory_error_free(e); 2498209962Smm return (ENOENT); 2499209962Smm } 2500209962Smm if (numericsid == NULL) 2501209962Smm return (ENOENT); 2502209962Smm cp = numericsid; 2503209962Smm /* will be further decoded below */ 2504209962Smm#else /* !sun */ 2505219089Spjd return (ENOENT); 2506209962Smm#endif /* !sun */ 2507209962Smm } 2508209962Smm 2509209962Smm if (strncmp(cp, "S-1-", 4) == 0) { 2510209962Smm /* It's a numeric SID (eg "S-1-234-567-89") */ 2511209962Smm (void) strlcpy(domain, cp, domainlen); 2512209962Smm cp = strrchr(domain, '-'); 2513209962Smm *cp = '\0'; 2514209962Smm cp++; 2515209962Smm 2516209962Smm errno = 0; 2517209962Smm *ridp = strtoull(cp, &end, 10); 2518209962Smm if (numericsid) { 2519209962Smm free(numericsid); 2520209962Smm numericsid = NULL; 2521209962Smm } 2522209962Smm if (errno != 0 || *end != '\0') 2523209962Smm return (EINVAL); 2524209962Smm } else if (!isdigit(*cp)) { 2525209962Smm /* 2526209962Smm * It's a user/group name (eg "user") that needs to be 2527209962Smm * turned into a uid/gid 2528209962Smm */ 2529209962Smm if (zoned && getzoneid() == GLOBAL_ZONEID) 2530209962Smm return (ENOENT); 2531209962Smm if (isuser) { 2532209962Smm struct passwd *pw; 2533209962Smm pw = getpwnam(cp); 2534209962Smm if (pw == NULL) 2535209962Smm return (ENOENT); 2536209962Smm *ridp = pw->pw_uid; 2537209962Smm } else { 2538209962Smm struct group *gr; 2539209962Smm gr = getgrnam(cp); 2540209962Smm if (gr == NULL) 2541209962Smm return (ENOENT); 2542209962Smm *ridp = gr->gr_gid; 2543209962Smm } 2544209962Smm } else { 2545209962Smm /* It's a user/group ID (eg "12345"). */ 2546209962Smm uid_t id = strtoul(cp, &end, 10); 2547209962Smm idmap_rid_t rid; 2548209962Smm char *mapdomain; 2549209962Smm 2550209962Smm if (*end != '\0') 2551209962Smm return (EINVAL); 2552209962Smm if (id > MAXUID) { 2553209962Smm /* It's an ephemeral ID. */ 2554209962Smm if (idmap_id_to_numeric_domain_rid(id, isuser, 2555209962Smm &mapdomain, &rid) != 0) 2556209962Smm return (ENOENT); 2557209962Smm (void) strlcpy(domain, mapdomain, domainlen); 2558209962Smm *ridp = rid; 2559209962Smm } else { 2560209962Smm *ridp = id; 2561209962Smm } 2562209962Smm } 2563209962Smm 2564209962Smm ASSERT3P(numericsid, ==, NULL); 2565209962Smm return (0); 2566209962Smm} 2567209962Smm 2568209962Smmstatic int 2569209962Smmzfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2570209962Smm uint64_t *propvalue, zfs_userquota_prop_t *typep) 2571209962Smm{ 2572209962Smm int err; 2573209962Smm zfs_cmd_t zc = { 0 }; 2574209962Smm 2575228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2576209962Smm 2577209962Smm err = userquota_propname_decode(propname, 2578209962Smm zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2579209962Smm typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2580209962Smm zc.zc_objset_type = *typep; 2581209962Smm if (err) 2582209962Smm return (err); 2583209962Smm 2584209962Smm err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 2585209962Smm if (err) 2586209962Smm return (err); 2587209962Smm 2588209962Smm *propvalue = zc.zc_cookie; 2589209962Smm return (0); 2590209962Smm} 2591209962Smm 2592209962Smmint 2593209962Smmzfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2594209962Smm uint64_t *propvalue) 2595209962Smm{ 2596209962Smm zfs_userquota_prop_t type; 2597209962Smm 2598209962Smm return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2599209962Smm &type)); 2600209962Smm} 2601209962Smm 2602209962Smmint 2603209962Smmzfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2604209962Smm char *propbuf, int proplen, boolean_t literal) 2605209962Smm{ 2606209962Smm int err; 2607209962Smm uint64_t propvalue; 2608209962Smm zfs_userquota_prop_t type; 2609209962Smm 2610209962Smm err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2611209962Smm &type); 2612209962Smm 2613209962Smm if (err) 2614209962Smm return (err); 2615209962Smm 2616209962Smm if (literal) { 2617209962Smm (void) snprintf(propbuf, proplen, "%llu", propvalue); 2618209962Smm } else if (propvalue == 0 && 2619209962Smm (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 2620209962Smm (void) strlcpy(propbuf, "none", proplen); 2621209962Smm } else { 2622209962Smm zfs_nicenum(propvalue, propbuf, proplen); 2623209962Smm } 2624209962Smm return (0); 2625209962Smm} 2626209962Smm 2627228103Smmint 2628228103Smmzfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname, 2629228103Smm uint64_t *propvalue) 2630168404Spjd{ 2631228103Smm int err; 2632228103Smm zfs_cmd_t zc = { 0 }; 2633228103Smm const char *snapname; 2634168404Spjd 2635228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2636168404Spjd 2637228103Smm snapname = strchr(propname, '@') + 1; 2638228103Smm if (strchr(snapname, '@')) { 2639228103Smm (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 2640228103Smm } else { 2641228103Smm /* snapname is the short name, append it to zhp's fsname */ 2642228103Smm char *cp; 2643209962Smm 2644228103Smm (void) strlcpy(zc.zc_value, zhp->zfs_name, 2645228103Smm sizeof (zc.zc_value)); 2646228103Smm cp = strchr(zc.zc_value, '@'); 2647228103Smm if (cp != NULL) 2648228103Smm *cp = '\0'; 2649228103Smm (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value)); 2650228103Smm (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value)); 2651228103Smm } 2652209962Smm 2653228103Smm err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc); 2654228103Smm if (err) 2655228103Smm return (err); 2656228103Smm 2657228103Smm *propvalue = zc.zc_cookie; 2658228103Smm return (0); 2659209962Smm} 2660209962Smm 2661168404Spjdint 2662228103Smmzfs_prop_get_written(zfs_handle_t *zhp, const char *propname, 2663228103Smm char *propbuf, int proplen, boolean_t literal) 2664168404Spjd{ 2665228103Smm int err; 2666228103Smm uint64_t propvalue; 2667168404Spjd 2668228103Smm err = zfs_prop_get_written_int(zhp, propname, &propvalue); 2669185029Spjd 2670228103Smm if (err) 2671228103Smm return (err); 2672209962Smm 2673228103Smm if (literal) { 2674228103Smm (void) snprintf(propbuf, proplen, "%llu", propvalue); 2675228103Smm } else { 2676228103Smm zfs_nicenum(propvalue, propbuf, proplen); 2677168404Spjd } 2678228103Smm return (0); 2679168404Spjd} 2680168404Spjd 2681168404Spjdint 2682228103Smmzfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap, 2683228103Smm uint64_t *usedp) 2684168404Spjd{ 2685228103Smm int err; 2686168404Spjd zfs_cmd_t zc = { 0 }; 2687168404Spjd 2688228103Smm (void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name)); 2689228103Smm (void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value)); 2690185029Spjd 2691228103Smm err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc); 2692228103Smm if (err) 2693228103Smm return (err); 2694168404Spjd 2695228103Smm *usedp = zc.zc_cookie; 2696209962Smm 2697228103Smm return (0); 2698168404Spjd} 2699168404Spjd 2700168404Spjd/* 2701228103Smm * Returns the name of the given zfs handle. 2702168404Spjd */ 2703228103Smmconst char * 2704228103Smmzfs_get_name(const zfs_handle_t *zhp) 2705168404Spjd{ 2706228103Smm return (zhp->zfs_name); 2707228103Smm} 2708168404Spjd 2709228103Smm/* 2710228103Smm * Returns the type of the given zfs handle. 2711228103Smm */ 2712228103Smmzfs_type_t 2713228103Smmzfs_get_type(const zfs_handle_t *zhp) 2714228103Smm{ 2715228103Smm return (zhp->zfs_type); 2716168404Spjd} 2717168404Spjd 2718168404Spjd/* 2719219089Spjd * Is one dataset name a child dataset of another? 2720219089Spjd * 2721219089Spjd * Needs to handle these cases: 2722219089Spjd * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 2723219089Spjd * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 2724219089Spjd * Descendant? No. No. No. Yes. 2725219089Spjd */ 2726219089Spjdstatic boolean_t 2727219089Spjdis_descendant(const char *ds1, const char *ds2) 2728219089Spjd{ 2729219089Spjd size_t d1len = strlen(ds1); 2730219089Spjd 2731219089Spjd /* ds2 can't be a descendant if it's smaller */ 2732219089Spjd if (strlen(ds2) < d1len) 2733219089Spjd return (B_FALSE); 2734219089Spjd 2735219089Spjd /* otherwise, compare strings and verify that there's a '/' char */ 2736219089Spjd return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 2737219089Spjd} 2738219089Spjd 2739219089Spjd/* 2740168404Spjd * Given a complete name, return just the portion that refers to the parent. 2741228103Smm * Will return -1 if there is no parent (path is just the name of the 2742228103Smm * pool). 2743168404Spjd */ 2744168404Spjdstatic int 2745168404Spjdparent_name(const char *path, char *buf, size_t buflen) 2746168404Spjd{ 2747228103Smm char *slashp; 2748168404Spjd 2749228103Smm (void) strlcpy(buf, path, buflen); 2750228103Smm 2751228103Smm if ((slashp = strrchr(buf, '/')) == NULL) 2752168404Spjd return (-1); 2753228103Smm *slashp = '\0'; 2754168404Spjd 2755168404Spjd return (0); 2756168404Spjd} 2757168404Spjd 2758168404Spjd/* 2759185029Spjd * If accept_ancestor is false, then check to make sure that the given path has 2760185029Spjd * a parent, and that it exists. If accept_ancestor is true, then find the 2761185029Spjd * closest existing ancestor for the given path. In prefixlen return the 2762185029Spjd * length of already existing prefix of the given path. We also fetch the 2763185029Spjd * 'zoned' property, which is used to validate property settings when creating 2764185029Spjd * new datasets. 2765168404Spjd */ 2766168404Spjdstatic int 2767185029Spjdcheck_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 2768185029Spjd boolean_t accept_ancestor, int *prefixlen) 2769168404Spjd{ 2770168404Spjd zfs_cmd_t zc = { 0 }; 2771168404Spjd char parent[ZFS_MAXNAMELEN]; 2772168404Spjd char *slash; 2773168404Spjd zfs_handle_t *zhp; 2774168404Spjd char errbuf[1024]; 2775219089Spjd uint64_t is_zoned; 2776168404Spjd 2777209962Smm (void) snprintf(errbuf, sizeof (errbuf), 2778209962Smm dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2779168404Spjd 2780168404Spjd /* get parent, and check to see if this is just a pool */ 2781168404Spjd if (parent_name(path, parent, sizeof (parent)) != 0) { 2782168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2783168404Spjd "missing dataset name")); 2784168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2785168404Spjd } 2786168404Spjd 2787168404Spjd /* check to see if the pool exists */ 2788168404Spjd if ((slash = strchr(parent, '/')) == NULL) 2789168404Spjd slash = parent + strlen(parent); 2790168404Spjd (void) strncpy(zc.zc_name, parent, slash - parent); 2791168404Spjd zc.zc_name[slash - parent] = '\0'; 2792168404Spjd if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2793168404Spjd errno == ENOENT) { 2794168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2795168404Spjd "no such pool '%s'"), zc.zc_name); 2796168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2797168404Spjd } 2798168404Spjd 2799168404Spjd /* check to see if the parent dataset exists */ 2800185029Spjd while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 2801185029Spjd if (errno == ENOENT && accept_ancestor) { 2802185029Spjd /* 2803185029Spjd * Go deeper to find an ancestor, give up on top level. 2804185029Spjd */ 2805185029Spjd if (parent_name(parent, parent, sizeof (parent)) != 0) { 2806185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2807185029Spjd "no such pool '%s'"), zc.zc_name); 2808185029Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2809185029Spjd } 2810185029Spjd } else if (errno == ENOENT) { 2811168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2812168404Spjd "parent does not exist")); 2813168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2814185029Spjd } else 2815168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 2816168404Spjd } 2817168404Spjd 2818219089Spjd is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2819219089Spjd if (zoned != NULL) 2820219089Spjd *zoned = is_zoned; 2821219089Spjd 2822168404Spjd /* we are in a non-global zone, but parent is in the global zone */ 2823219089Spjd if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 2824168404Spjd (void) zfs_standard_error(hdl, EPERM, errbuf); 2825168404Spjd zfs_close(zhp); 2826168404Spjd return (-1); 2827168404Spjd } 2828168404Spjd 2829168404Spjd /* make sure parent is a filesystem */ 2830168404Spjd if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 2831168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2832168404Spjd "parent is not a filesystem")); 2833168404Spjd (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 2834168404Spjd zfs_close(zhp); 2835168404Spjd return (-1); 2836168404Spjd } 2837168404Spjd 2838168404Spjd zfs_close(zhp); 2839185029Spjd if (prefixlen != NULL) 2840185029Spjd *prefixlen = strlen(parent); 2841168404Spjd return (0); 2842168404Spjd} 2843168404Spjd 2844168404Spjd/* 2845185029Spjd * Finds whether the dataset of the given type(s) exists. 2846185029Spjd */ 2847185029Spjdboolean_t 2848185029Spjdzfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 2849185029Spjd{ 2850185029Spjd zfs_handle_t *zhp; 2851185029Spjd 2852185029Spjd if (!zfs_validate_name(hdl, path, types, B_FALSE)) 2853185029Spjd return (B_FALSE); 2854185029Spjd 2855185029Spjd /* 2856185029Spjd * Try to get stats for the dataset, which will tell us if it exists. 2857185029Spjd */ 2858185029Spjd if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 2859185029Spjd int ds_type = zhp->zfs_type; 2860185029Spjd 2861185029Spjd zfs_close(zhp); 2862185029Spjd if (types & ds_type) 2863185029Spjd return (B_TRUE); 2864185029Spjd } 2865185029Spjd return (B_FALSE); 2866185029Spjd} 2867185029Spjd 2868185029Spjd/* 2869185029Spjd * Given a path to 'target', create all the ancestors between 2870185029Spjd * the prefixlen portion of the path, and the target itself. 2871185029Spjd * Fail if the initial prefixlen-ancestor does not already exist. 2872185029Spjd */ 2873185029Spjdint 2874185029Spjdcreate_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 2875185029Spjd{ 2876185029Spjd zfs_handle_t *h; 2877185029Spjd char *cp; 2878185029Spjd const char *opname; 2879185029Spjd 2880185029Spjd /* make sure prefix exists */ 2881185029Spjd cp = target + prefixlen; 2882185029Spjd if (*cp != '/') { 2883185029Spjd assert(strchr(cp, '/') == NULL); 2884185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 2885185029Spjd } else { 2886185029Spjd *cp = '\0'; 2887185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 2888185029Spjd *cp = '/'; 2889185029Spjd } 2890185029Spjd if (h == NULL) 2891185029Spjd return (-1); 2892185029Spjd zfs_close(h); 2893185029Spjd 2894185029Spjd /* 2895185029Spjd * Attempt to create, mount, and share any ancestor filesystems, 2896185029Spjd * up to the prefixlen-long one. 2897185029Spjd */ 2898185029Spjd for (cp = target + prefixlen + 1; 2899185029Spjd cp = strchr(cp, '/'); *cp = '/', cp++) { 2900185029Spjd char *logstr; 2901185029Spjd 2902185029Spjd *cp = '\0'; 2903185029Spjd 2904185029Spjd h = make_dataset_handle(hdl, target); 2905185029Spjd if (h) { 2906185029Spjd /* it already exists, nothing to do here */ 2907185029Spjd zfs_close(h); 2908185029Spjd continue; 2909185029Spjd } 2910185029Spjd 2911185029Spjd logstr = hdl->libzfs_log_str; 2912185029Spjd hdl->libzfs_log_str = NULL; 2913185029Spjd if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 2914185029Spjd NULL) != 0) { 2915185029Spjd hdl->libzfs_log_str = logstr; 2916185029Spjd opname = dgettext(TEXT_DOMAIN, "create"); 2917185029Spjd goto ancestorerr; 2918185029Spjd } 2919185029Spjd 2920185029Spjd hdl->libzfs_log_str = logstr; 2921185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 2922185029Spjd if (h == NULL) { 2923185029Spjd opname = dgettext(TEXT_DOMAIN, "open"); 2924185029Spjd goto ancestorerr; 2925185029Spjd } 2926185029Spjd 2927185029Spjd if (zfs_mount(h, NULL, 0) != 0) { 2928185029Spjd opname = dgettext(TEXT_DOMAIN, "mount"); 2929185029Spjd goto ancestorerr; 2930185029Spjd } 2931185029Spjd 2932185029Spjd if (zfs_share(h) != 0) { 2933185029Spjd opname = dgettext(TEXT_DOMAIN, "share"); 2934185029Spjd goto ancestorerr; 2935185029Spjd } 2936185029Spjd 2937185029Spjd zfs_close(h); 2938185029Spjd } 2939185029Spjd 2940185029Spjd return (0); 2941185029Spjd 2942185029Spjdancestorerr: 2943185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2944185029Spjd "failed to %s ancestor '%s'"), opname, target); 2945185029Spjd return (-1); 2946185029Spjd} 2947185029Spjd 2948185029Spjd/* 2949185029Spjd * Creates non-existing ancestors of the given path. 2950185029Spjd */ 2951185029Spjdint 2952185029Spjdzfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 2953185029Spjd{ 2954185029Spjd int prefix; 2955185029Spjd char *path_copy; 2956185029Spjd int rc; 2957185029Spjd 2958219089Spjd if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 2959185029Spjd return (-1); 2960185029Spjd 2961185029Spjd if ((path_copy = strdup(path)) != NULL) { 2962185029Spjd rc = create_parents(hdl, path_copy, prefix); 2963185029Spjd free(path_copy); 2964185029Spjd } 2965185029Spjd if (path_copy == NULL || rc != 0) 2966185029Spjd return (-1); 2967185029Spjd 2968185029Spjd return (0); 2969185029Spjd} 2970185029Spjd 2971185029Spjd/* 2972168404Spjd * Create a new filesystem or volume. 2973168404Spjd */ 2974168404Spjdint 2975168404Spjdzfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2976168404Spjd nvlist_t *props) 2977168404Spjd{ 2978168404Spjd zfs_cmd_t zc = { 0 }; 2979168404Spjd int ret; 2980168404Spjd uint64_t size = 0; 2981168404Spjd uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 2982168404Spjd char errbuf[1024]; 2983168404Spjd uint64_t zoned; 2984168404Spjd 2985168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 2986168404Spjd "cannot create '%s'"), path); 2987168404Spjd 2988168404Spjd /* validate the path, taking care to note the extended error message */ 2989185029Spjd if (!zfs_validate_name(hdl, path, type, B_TRUE)) 2990168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2991168404Spjd 2992168404Spjd /* validate parents exist */ 2993185029Spjd if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2994168404Spjd return (-1); 2995168404Spjd 2996168404Spjd /* 2997168404Spjd * The failure modes when creating a dataset of a different type over 2998168404Spjd * one that already exists is a little strange. In particular, if you 2999168404Spjd * try to create a dataset on top of an existing dataset, the ioctl() 3000168404Spjd * will return ENOENT, not EEXIST. To prevent this from happening, we 3001168404Spjd * first try to see if the dataset exists. 3002168404Spjd */ 3003168404Spjd (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 3004185029Spjd if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 3005168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3006168404Spjd "dataset already exists")); 3007168404Spjd return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3008168404Spjd } 3009168404Spjd 3010168404Spjd if (type == ZFS_TYPE_VOLUME) 3011168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3012168404Spjd else 3013168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3014168404Spjd 3015185029Spjd if (props && (props = zfs_valid_proplist(hdl, type, props, 3016168404Spjd zoned, NULL, errbuf)) == 0) 3017168404Spjd return (-1); 3018168404Spjd 3019168404Spjd if (type == ZFS_TYPE_VOLUME) { 3020168404Spjd /* 3021168404Spjd * If we are creating a volume, the size and block size must 3022168404Spjd * satisfy a few restraints. First, the blocksize must be a 3023168404Spjd * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 3024168404Spjd * volsize must be a multiple of the block size, and cannot be 3025168404Spjd * zero. 3026168404Spjd */ 3027168404Spjd if (props == NULL || nvlist_lookup_uint64(props, 3028168404Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 3029168404Spjd nvlist_free(props); 3030168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3031168404Spjd "missing volume size")); 3032168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3033168404Spjd } 3034168404Spjd 3035168404Spjd if ((ret = nvlist_lookup_uint64(props, 3036168404Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 3037168404Spjd &blocksize)) != 0) { 3038168404Spjd if (ret == ENOENT) { 3039168404Spjd blocksize = zfs_prop_default_numeric( 3040168404Spjd ZFS_PROP_VOLBLOCKSIZE); 3041168404Spjd } else { 3042168404Spjd nvlist_free(props); 3043168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3044168404Spjd "missing volume block size")); 3045168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3046168404Spjd } 3047168404Spjd } 3048168404Spjd 3049168404Spjd if (size == 0) { 3050168404Spjd nvlist_free(props); 3051168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3052168404Spjd "volume size cannot be zero")); 3053168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3054168404Spjd } 3055168404Spjd 3056168404Spjd if (size % blocksize != 0) { 3057168404Spjd nvlist_free(props); 3058168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3059168404Spjd "volume size must be a multiple of volume block " 3060168404Spjd "size")); 3061168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3062168404Spjd } 3063168404Spjd } 3064168404Spjd 3065185029Spjd if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 3066168404Spjd return (-1); 3067168404Spjd nvlist_free(props); 3068168404Spjd 3069168404Spjd /* create the dataset */ 3070185029Spjd ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 3071168404Spjd 3072168404Spjd zcmd_free_nvlists(&zc); 3073168404Spjd 3074168404Spjd /* check for failure */ 3075168404Spjd if (ret != 0) { 3076168404Spjd char parent[ZFS_MAXNAMELEN]; 3077168404Spjd (void) parent_name(path, parent, sizeof (parent)); 3078168404Spjd 3079168404Spjd switch (errno) { 3080168404Spjd case ENOENT: 3081168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3082168404Spjd "no such parent '%s'"), parent); 3083168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3084168404Spjd 3085168404Spjd case EINVAL: 3086168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3087168404Spjd "parent '%s' is not a filesystem"), parent); 3088168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3089168404Spjd 3090168404Spjd case EDOM: 3091168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3092168404Spjd "volume block size must be power of 2 from " 3093168404Spjd "%u to %uk"), 3094168404Spjd (uint_t)SPA_MINBLOCKSIZE, 3095168404Spjd (uint_t)SPA_MAXBLOCKSIZE >> 10); 3096168404Spjd 3097168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3098168404Spjd 3099185029Spjd case ENOTSUP: 3100185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3101185029Spjd "pool must be upgraded to set this " 3102185029Spjd "property or value")); 3103185029Spjd return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3104168404Spjd#ifdef _ILP32 3105168404Spjd case EOVERFLOW: 3106168404Spjd /* 3107168404Spjd * This platform can't address a volume this big. 3108168404Spjd */ 3109168404Spjd if (type == ZFS_TYPE_VOLUME) 3110168404Spjd return (zfs_error(hdl, EZFS_VOLTOOBIG, 3111168404Spjd errbuf)); 3112168404Spjd#endif 3113168404Spjd /* FALLTHROUGH */ 3114168404Spjd default: 3115168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 3116168404Spjd } 3117168404Spjd } 3118168404Spjd 3119168404Spjd return (0); 3120168404Spjd} 3121168404Spjd 3122168404Spjd/* 3123168404Spjd * Destroys the given dataset. The caller must make sure that the filesystem 3124168404Spjd * isn't mounted, and that there are no active dependents. 3125168404Spjd */ 3126168404Spjdint 3127219089Spjdzfs_destroy(zfs_handle_t *zhp, boolean_t defer) 3128168404Spjd{ 3129168404Spjd zfs_cmd_t zc = { 0 }; 3130168404Spjd 3131168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3132168404Spjd 3133168404Spjd if (ZFS_IS_VOLUME(zhp)) { 3134168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3135168404Spjd } else { 3136168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3137168404Spjd } 3138168404Spjd 3139219089Spjd zc.zc_defer_destroy = defer; 3140185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 3141168404Spjd return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3142168404Spjd dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 3143168404Spjd zhp->zfs_name)); 3144168404Spjd } 3145168404Spjd 3146168404Spjd remove_mountpoint(zhp); 3147168404Spjd 3148168404Spjd return (0); 3149168404Spjd} 3150168404Spjd 3151168404Spjdstruct destroydata { 3152228103Smm nvlist_t *nvl; 3153228103Smm const char *snapname; 3154168404Spjd}; 3155168404Spjd 3156168404Spjdstatic int 3157219089Spjdzfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 3158168404Spjd{ 3159168404Spjd struct destroydata *dd = arg; 3160168404Spjd zfs_handle_t *szhp; 3161168404Spjd char name[ZFS_MAXNAMELEN]; 3162219089Spjd int rv = 0; 3163168404Spjd 3164228103Smm (void) snprintf(name, sizeof (name), 3165228103Smm "%s@%s", zhp->zfs_name, dd->snapname); 3166168404Spjd 3167168404Spjd szhp = make_dataset_handle(zhp->zfs_hdl, name); 3168168404Spjd if (szhp) { 3169228103Smm verify(nvlist_add_boolean(dd->nvl, name) == 0); 3170168404Spjd zfs_close(szhp); 3171168404Spjd } 3172168404Spjd 3173228103Smm rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd); 3174228103Smm zfs_close(zhp); 3175168404Spjd return (rv); 3176168404Spjd} 3177168404Spjd 3178168404Spjd/* 3179168404Spjd * Destroys all snapshots with the given name in zhp & descendants. 3180168404Spjd */ 3181168404Spjdint 3182219089Spjdzfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 3183168404Spjd{ 3184168404Spjd int ret; 3185168404Spjd struct destroydata dd = { 0 }; 3186168404Spjd 3187168404Spjd dd.snapname = snapname; 3188228103Smm verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0); 3189228103Smm (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd); 3190168404Spjd 3191228103Smm if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) { 3192228103Smm ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 3193168404Spjd dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 3194228103Smm zhp->zfs_name, snapname); 3195228103Smm } else { 3196228103Smm ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer); 3197168404Spjd } 3198228103Smm nvlist_free(dd.nvl); 3199228103Smm return (ret); 3200228103Smm} 3201168404Spjd 3202228103Smm/* 3203228103Smm * Destroys all the snapshots named in the nvlist. They must be underneath 3204228103Smm * the zhp (either snapshots of it, or snapshots of its descendants). 3205228103Smm */ 3206228103Smmint 3207228103Smmzfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer) 3208228103Smm{ 3209228103Smm int ret; 3210228103Smm zfs_cmd_t zc = { 0 }; 3211228103Smm 3212168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3213228103Smm if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0) 3214228103Smm return (-1); 3215219089Spjd zc.zc_defer_destroy = defer; 3216168404Spjd 3217228103Smm ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc); 3218168404Spjd if (ret != 0) { 3219168404Spjd char errbuf[1024]; 3220168404Spjd 3221168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3222228103Smm "cannot destroy snapshots in %s"), zc.zc_name); 3223168404Spjd 3224168404Spjd switch (errno) { 3225168404Spjd case EEXIST: 3226168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3227168404Spjd "snapshot is cloned")); 3228168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 3229168404Spjd 3230168404Spjd default: 3231168404Spjd return (zfs_standard_error(zhp->zfs_hdl, errno, 3232168404Spjd errbuf)); 3233168404Spjd } 3234168404Spjd } 3235168404Spjd 3236168404Spjd return (0); 3237168404Spjd} 3238168404Spjd 3239168404Spjd/* 3240168404Spjd * Clones the given dataset. The target must be of the same type as the source. 3241168404Spjd */ 3242168404Spjdint 3243168404Spjdzfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3244168404Spjd{ 3245168404Spjd zfs_cmd_t zc = { 0 }; 3246168404Spjd char parent[ZFS_MAXNAMELEN]; 3247168404Spjd int ret; 3248168404Spjd char errbuf[1024]; 3249168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3250168404Spjd zfs_type_t type; 3251168404Spjd uint64_t zoned; 3252168404Spjd 3253168404Spjd assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3254168404Spjd 3255168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3256168404Spjd "cannot create '%s'"), target); 3257168404Spjd 3258228103Smm /* validate the target/clone name */ 3259185029Spjd if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 3260168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3261168404Spjd 3262168404Spjd /* validate parents exist */ 3263185029Spjd if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3264168404Spjd return (-1); 3265168404Spjd 3266168404Spjd (void) parent_name(target, parent, sizeof (parent)); 3267168404Spjd 3268168404Spjd /* do the clone */ 3269168404Spjd if (ZFS_IS_VOLUME(zhp)) { 3270168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3271168404Spjd type = ZFS_TYPE_VOLUME; 3272168404Spjd } else { 3273168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3274168404Spjd type = ZFS_TYPE_FILESYSTEM; 3275168404Spjd } 3276168404Spjd 3277168404Spjd if (props) { 3278185029Spjd if ((props = zfs_valid_proplist(hdl, type, props, zoned, 3279185029Spjd zhp, errbuf)) == NULL) 3280168404Spjd return (-1); 3281168404Spjd 3282185029Spjd if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3283168404Spjd nvlist_free(props); 3284168404Spjd return (-1); 3285168404Spjd } 3286168404Spjd 3287168404Spjd nvlist_free(props); 3288168404Spjd } 3289168404Spjd 3290168404Spjd (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 3291168404Spjd (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 3292185029Spjd ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3293168404Spjd 3294168404Spjd zcmd_free_nvlists(&zc); 3295168404Spjd 3296168404Spjd if (ret != 0) { 3297168404Spjd switch (errno) { 3298168404Spjd 3299168404Spjd case ENOENT: 3300168404Spjd /* 3301168404Spjd * The parent doesn't exist. We should have caught this 3302168404Spjd * above, but there may a race condition that has since 3303168404Spjd * destroyed the parent. 3304168404Spjd * 3305168404Spjd * At this point, we don't know whether it's the source 3306168404Spjd * that doesn't exist anymore, or whether the target 3307168404Spjd * dataset doesn't exist. 3308168404Spjd */ 3309168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3310168404Spjd "no such parent '%s'"), parent); 3311168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3312168404Spjd 3313168404Spjd case EXDEV: 3314168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3315168404Spjd "source and target pools differ")); 3316168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 3317168404Spjd errbuf)); 3318168404Spjd 3319168404Spjd default: 3320168404Spjd return (zfs_standard_error(zhp->zfs_hdl, errno, 3321168404Spjd errbuf)); 3322168404Spjd } 3323168404Spjd } 3324168404Spjd 3325168404Spjd return (ret); 3326168404Spjd} 3327168404Spjd 3328168404Spjd/* 3329168404Spjd * Promotes the given clone fs to be the clone parent. 3330168404Spjd */ 3331168404Spjdint 3332168404Spjdzfs_promote(zfs_handle_t *zhp) 3333168404Spjd{ 3334168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3335168404Spjd zfs_cmd_t zc = { 0 }; 3336168404Spjd char parent[MAXPATHLEN]; 3337168404Spjd int ret; 3338168404Spjd char errbuf[1024]; 3339168404Spjd 3340168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3341168404Spjd "cannot promote '%s'"), zhp->zfs_name); 3342168404Spjd 3343168404Spjd if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 3344168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3345168404Spjd "snapshots can not be promoted")); 3346168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3347168404Spjd } 3348168404Spjd 3349185029Spjd (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 3350168404Spjd if (parent[0] == '\0') { 3351168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3352168404Spjd "not a cloned filesystem")); 3353168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3354168404Spjd } 3355168404Spjd 3356185029Spjd (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3357168404Spjd sizeof (zc.zc_value)); 3358168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3359185029Spjd ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 3360168404Spjd 3361168404Spjd if (ret != 0) { 3362168404Spjd int save_errno = errno; 3363168404Spjd 3364168404Spjd switch (save_errno) { 3365168404Spjd case EEXIST: 3366219089Spjd /* There is a conflicting snapshot name. */ 3367168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3368219089Spjd "conflicting snapshot '%s' from parent '%s'"), 3369219089Spjd zc.zc_string, parent); 3370168404Spjd return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3371168404Spjd 3372168404Spjd default: 3373168404Spjd return (zfs_standard_error(hdl, save_errno, errbuf)); 3374168404Spjd } 3375168404Spjd } 3376168404Spjd return (ret); 3377168404Spjd} 3378168404Spjd 3379168404Spjd/* 3380168404Spjd * Takes a snapshot of the given dataset. 3381168404Spjd */ 3382168404Spjdint 3383185029Spjdzfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3384185029Spjd nvlist_t *props) 3385168404Spjd{ 3386168404Spjd const char *delim; 3387185029Spjd char parent[ZFS_MAXNAMELEN]; 3388168404Spjd zfs_handle_t *zhp; 3389168404Spjd zfs_cmd_t zc = { 0 }; 3390168404Spjd int ret; 3391168404Spjd char errbuf[1024]; 3392168404Spjd 3393168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3394168404Spjd "cannot snapshot '%s'"), path); 3395168404Spjd 3396168404Spjd /* validate the target name */ 3397185029Spjd if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 3398168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3399168404Spjd 3400185029Spjd if (props) { 3401185029Spjd if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3402185029Spjd props, B_FALSE, NULL, errbuf)) == NULL) 3403185029Spjd return (-1); 3404185029Spjd 3405185029Spjd if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3406185029Spjd nvlist_free(props); 3407185029Spjd return (-1); 3408185029Spjd } 3409185029Spjd 3410185029Spjd nvlist_free(props); 3411185029Spjd } 3412185029Spjd 3413168404Spjd /* make sure the parent exists and is of the appropriate type */ 3414168404Spjd delim = strchr(path, '@'); 3415168404Spjd (void) strncpy(parent, path, delim - path); 3416168404Spjd parent[delim - path] = '\0'; 3417168404Spjd 3418168404Spjd if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3419168404Spjd ZFS_TYPE_VOLUME)) == NULL) { 3420185029Spjd zcmd_free_nvlists(&zc); 3421168404Spjd return (-1); 3422168404Spjd } 3423168404Spjd 3424168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3425168404Spjd (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3426185029Spjd if (ZFS_IS_VOLUME(zhp)) 3427185029Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3428185029Spjd else 3429185029Spjd zc.zc_objset_type = DMU_OST_ZFS; 3430168404Spjd zc.zc_cookie = recursive; 3431185029Spjd ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3432168404Spjd 3433185029Spjd zcmd_free_nvlists(&zc); 3434185029Spjd 3435168404Spjd /* 3436168404Spjd * if it was recursive, the one that actually failed will be in 3437168404Spjd * zc.zc_name. 3438168404Spjd */ 3439219089Spjd if (ret != 0) { 3440185029Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3441185029Spjd "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 3442219089Spjd (void) zfs_standard_error(hdl, errno, errbuf); 3443168404Spjd } 3444168404Spjd 3445168404Spjd zfs_close(zhp); 3446168404Spjd 3447168404Spjd return (ret); 3448168404Spjd} 3449168404Spjd 3450168404Spjd/* 3451168404Spjd * Destroy any more recent snapshots. We invoke this callback on any dependents 3452168404Spjd * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3453168404Spjd * is a dependent and we should just destroy it without checking the transaction 3454168404Spjd * group. 3455168404Spjd */ 3456168404Spjdtypedef struct rollback_data { 3457168404Spjd const char *cb_target; /* the snapshot */ 3458168404Spjd uint64_t cb_create; /* creation time reference */ 3459185029Spjd boolean_t cb_error; 3460168404Spjd boolean_t cb_dependent; 3461185029Spjd boolean_t cb_force; 3462168404Spjd} rollback_data_t; 3463168404Spjd 3464168404Spjdstatic int 3465168404Spjdrollback_destroy(zfs_handle_t *zhp, void *data) 3466168404Spjd{ 3467168404Spjd rollback_data_t *cbp = data; 3468168404Spjd 3469168404Spjd if (!cbp->cb_dependent) { 3470168404Spjd if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3471168404Spjd zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3472168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3473168404Spjd cbp->cb_create) { 3474185029Spjd char *logstr; 3475168404Spjd 3476168404Spjd cbp->cb_dependent = B_TRUE; 3477185029Spjd cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 3478185029Spjd rollback_destroy, cbp); 3479168404Spjd cbp->cb_dependent = B_FALSE; 3480168404Spjd 3481185029Spjd logstr = zhp->zfs_hdl->libzfs_log_str; 3482185029Spjd zhp->zfs_hdl->libzfs_log_str = NULL; 3483219089Spjd cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3484185029Spjd zhp->zfs_hdl->libzfs_log_str = logstr; 3485168404Spjd } 3486168404Spjd } else { 3487185029Spjd /* We must destroy this clone; first unmount it */ 3488185029Spjd prop_changelist_t *clp; 3489185029Spjd 3490185029Spjd clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3491185029Spjd cbp->cb_force ? MS_FORCE: 0); 3492185029Spjd if (clp == NULL || changelist_prefix(clp) != 0) { 3493185029Spjd cbp->cb_error = B_TRUE; 3494185029Spjd zfs_close(zhp); 3495185029Spjd return (0); 3496185029Spjd } 3497219089Spjd if (zfs_destroy(zhp, B_FALSE) != 0) 3498185029Spjd cbp->cb_error = B_TRUE; 3499168404Spjd else 3500185029Spjd changelist_remove(clp, zhp->zfs_name); 3501185029Spjd (void) changelist_postfix(clp); 3502185029Spjd changelist_free(clp); 3503168404Spjd } 3504168404Spjd 3505168404Spjd zfs_close(zhp); 3506168404Spjd return (0); 3507168404Spjd} 3508168404Spjd 3509168404Spjd/* 3510168404Spjd * Given a dataset, rollback to a specific snapshot, discarding any 3511168404Spjd * data changes since then and making it the active dataset. 3512168404Spjd * 3513168404Spjd * Any snapshots more recent than the target are destroyed, along with 3514168404Spjd * their dependents. 3515168404Spjd */ 3516168404Spjdint 3517185029Spjdzfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3518168404Spjd{ 3519168404Spjd rollback_data_t cb = { 0 }; 3520185029Spjd int err; 3521185029Spjd zfs_cmd_t zc = { 0 }; 3522185029Spjd boolean_t restore_resv = 0; 3523185029Spjd uint64_t old_volsize, new_volsize; 3524185029Spjd zfs_prop_t resv_prop; 3525168404Spjd 3526185029Spjd assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3527185029Spjd zhp->zfs_type == ZFS_TYPE_VOLUME); 3528168404Spjd 3529168404Spjd /* 3530168404Spjd * Destroy all recent snapshots and its dependends. 3531168404Spjd */ 3532185029Spjd cb.cb_force = force; 3533168404Spjd cb.cb_target = snap->zfs_name; 3534168404Spjd cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3535168404Spjd (void) zfs_iter_children(zhp, rollback_destroy, &cb); 3536168404Spjd 3537185029Spjd if (cb.cb_error) 3538185029Spjd return (-1); 3539168404Spjd 3540168404Spjd /* 3541168404Spjd * Now that we have verified that the snapshot is the latest, 3542168404Spjd * rollback to the given snapshot. 3543168404Spjd */ 3544168404Spjd 3545185029Spjd if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 3546185029Spjd if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 3547185029Spjd return (-1); 3548185029Spjd old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 3549185029Spjd restore_resv = 3550185029Spjd (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 3551168404Spjd } 3552168404Spjd 3553185029Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3554185029Spjd 3555185029Spjd if (ZFS_IS_VOLUME(zhp)) 3556185029Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3557185029Spjd else 3558185029Spjd zc.zc_objset_type = DMU_OST_ZFS; 3559185029Spjd 3560168404Spjd /* 3561185029Spjd * We rely on zfs_iter_children() to verify that there are no 3562185029Spjd * newer snapshots for the given dataset. Therefore, we can 3563185029Spjd * simply pass the name on to the ioctl() call. There is still 3564185029Spjd * an unlikely race condition where the user has taken a 3565185029Spjd * snapshot since we verified that this was the most recent. 3566185029Spjd * 3567168404Spjd */ 3568185029Spjd if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3569185029Spjd (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3570185029Spjd dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 3571185029Spjd zhp->zfs_name); 3572185029Spjd return (err); 3573185029Spjd } 3574168404Spjd 3575185029Spjd /* 3576185029Spjd * For volumes, if the pre-rollback volsize matched the pre- 3577185029Spjd * rollback reservation and the volsize has changed then set 3578185029Spjd * the reservation property to the post-rollback volsize. 3579185029Spjd * Make a new handle since the rollback closed the dataset. 3580185029Spjd */ 3581185029Spjd if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3582185029Spjd (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3583185029Spjd if (restore_resv) { 3584185029Spjd new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 3585185029Spjd if (old_volsize != new_volsize) 3586185029Spjd err = zfs_prop_set_int(zhp, resv_prop, 3587185029Spjd new_volsize); 3588185029Spjd } 3589185029Spjd zfs_close(zhp); 3590185029Spjd } 3591185029Spjd return (err); 3592168404Spjd} 3593168404Spjd 3594168404Spjd/* 3595168404Spjd * Renames the given dataset. 3596168404Spjd */ 3597168404Spjdint 3598226705Spjdzfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags) 3599168404Spjd{ 3600168404Spjd int ret; 3601168404Spjd zfs_cmd_t zc = { 0 }; 3602168404Spjd char *delim; 3603168676Spjd prop_changelist_t *cl = NULL; 3604168676Spjd zfs_handle_t *zhrp = NULL; 3605168676Spjd char *parentname = NULL; 3606168404Spjd char parent[ZFS_MAXNAMELEN]; 3607226676Spjd char property[ZFS_MAXPROPLEN]; 3608168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3609168404Spjd char errbuf[1024]; 3610168404Spjd 3611168404Spjd /* if we have the same exact name, just return success */ 3612168404Spjd if (strcmp(zhp->zfs_name, target) == 0) 3613168404Spjd return (0); 3614168404Spjd 3615168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3616168404Spjd "cannot rename to '%s'"), target); 3617168404Spjd 3618168404Spjd /* 3619168404Spjd * Make sure the target name is valid 3620168404Spjd */ 3621168404Spjd if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 3622168404Spjd if ((strchr(target, '@') == NULL) || 3623168404Spjd *target == '@') { 3624168404Spjd /* 3625168404Spjd * Snapshot target name is abbreviated, 3626168404Spjd * reconstruct full dataset name 3627168404Spjd */ 3628168404Spjd (void) strlcpy(parent, zhp->zfs_name, 3629168404Spjd sizeof (parent)); 3630168404Spjd delim = strchr(parent, '@'); 3631168404Spjd if (strchr(target, '@') == NULL) 3632168404Spjd *(++delim) = '\0'; 3633168404Spjd else 3634168404Spjd *delim = '\0'; 3635168404Spjd (void) strlcat(parent, target, sizeof (parent)); 3636168404Spjd target = parent; 3637168404Spjd } else { 3638168404Spjd /* 3639168404Spjd * Make sure we're renaming within the same dataset. 3640168404Spjd */ 3641168404Spjd delim = strchr(target, '@'); 3642168404Spjd if (strncmp(zhp->zfs_name, target, delim - target) 3643168404Spjd != 0 || zhp->zfs_name[delim - target] != '@') { 3644168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3645168404Spjd "snapshots must be part of same " 3646168404Spjd "dataset")); 3647168404Spjd return (zfs_error(hdl, EZFS_CROSSTARGET, 3648168404Spjd errbuf)); 3649168404Spjd } 3650168404Spjd } 3651185029Spjd if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 3652168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3653168404Spjd } else { 3654226705Spjd if (flags.recurse) { 3655168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3656168676Spjd "recursive rename must be a snapshot")); 3657168676Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3658168676Spjd } 3659168676Spjd 3660185029Spjd if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 3661168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3662168404Spjd 3663168404Spjd /* validate parents */ 3664219089Spjd if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3665168404Spjd return (-1); 3666168404Spjd 3667168404Spjd /* make sure we're in the same pool */ 3668168404Spjd verify((delim = strchr(target, '/')) != NULL); 3669168404Spjd if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3670168404Spjd zhp->zfs_name[delim - target] != '/') { 3671168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3672168404Spjd "datasets must be within same pool")); 3673168404Spjd return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3674168404Spjd } 3675168404Spjd 3676168404Spjd /* new name cannot be a child of the current dataset name */ 3677219089Spjd if (is_descendant(zhp->zfs_name, target)) { 3678168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3679219089Spjd "New dataset name cannot be a descendant of " 3680168404Spjd "current dataset name")); 3681168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3682168404Spjd } 3683168404Spjd } 3684168404Spjd 3685168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 3686168404Spjd dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 3687168404Spjd 3688168404Spjd if (getzoneid() == GLOBAL_ZONEID && 3689168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 3690168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3691168404Spjd "dataset is used in a non-global zone")); 3692168404Spjd return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3693168404Spjd } 3694168404Spjd 3695226705Spjd /* 3696226705Spjd * Avoid unmounting file systems with mountpoint property set to 3697226705Spjd * 'legacy' or 'none' even if -u option is not given. 3698226705Spjd */ 3699226705Spjd if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM && 3700226705Spjd !flags.recurse && !flags.nounmount && 3701226705Spjd zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property, 3702226705Spjd sizeof (property), NULL, NULL, 0, B_FALSE) == 0 && 3703226705Spjd (strcmp(property, "legacy") == 0 || 3704226705Spjd strcmp(property, "none") == 0)) { 3705226705Spjd flags.nounmount = B_TRUE; 3706226705Spjd } 3707168404Spjd 3708226705Spjd if (flags.recurse) { 3709226705Spjd 3710185029Spjd parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3711185029Spjd if (parentname == NULL) { 3712185029Spjd ret = -1; 3713185029Spjd goto error; 3714185029Spjd } 3715168676Spjd delim = strchr(parentname, '@'); 3716168676Spjd *delim = '\0'; 3717185029Spjd zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3718168676Spjd if (zhrp == NULL) { 3719185029Spjd ret = -1; 3720185029Spjd goto error; 3721168676Spjd } 3722168676Spjd 3723168676Spjd } else { 3724226676Spjd if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 3725235216Smm flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0, 3726235216Smm flags.forceunmount ? MS_FORCE : 0)) == NULL) { 3727168676Spjd return (-1); 3728226676Spjd } 3729168676Spjd 3730168676Spjd if (changelist_haszonedchild(cl)) { 3731168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3732168676Spjd "child dataset with inherited mountpoint is used " 3733168676Spjd "in a non-global zone")); 3734168676Spjd (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3735168676Spjd goto error; 3736168676Spjd } 3737168676Spjd 3738168676Spjd if ((ret = changelist_prefix(cl)) != 0) 3739168676Spjd goto error; 3740168404Spjd } 3741168404Spjd 3742168404Spjd if (ZFS_IS_VOLUME(zhp)) 3743168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3744168404Spjd else 3745168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3746168404Spjd 3747168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3748168404Spjd (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 3749168404Spjd 3750226705Spjd zc.zc_cookie = flags.recurse ? 1 : 0; 3751226705Spjd if (flags.nounmount) 3752226676Spjd zc.zc_cookie |= 2; 3753168676Spjd 3754185029Spjd if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3755168676Spjd /* 3756168676Spjd * if it was recursive, the one that actually failed will 3757168676Spjd * be in zc.zc_name 3758168676Spjd */ 3759168676Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3760185029Spjd "cannot rename '%s'"), zc.zc_name); 3761168404Spjd 3762226705Spjd if (flags.recurse && errno == EEXIST) { 3763168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3764168676Spjd "a child dataset already has a snapshot " 3765168676Spjd "with the new name")); 3766185029Spjd (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3767168676Spjd } else { 3768168676Spjd (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3769168676Spjd } 3770168676Spjd 3771168404Spjd /* 3772168404Spjd * On failure, we still want to remount any filesystems that 3773168404Spjd * were previously mounted, so we don't alter the system state. 3774168404Spjd */ 3775226705Spjd if (!flags.recurse) 3776168676Spjd (void) changelist_postfix(cl); 3777168404Spjd } else { 3778226705Spjd if (!flags.recurse) { 3779168676Spjd changelist_rename(cl, zfs_get_name(zhp), target); 3780168676Spjd ret = changelist_postfix(cl); 3781168676Spjd } 3782168404Spjd } 3783168404Spjd 3784168404Spjderror: 3785168676Spjd if (parentname) { 3786168676Spjd free(parentname); 3787168676Spjd } 3788168676Spjd if (zhrp) { 3789168676Spjd zfs_close(zhrp); 3790168676Spjd } 3791168676Spjd if (cl) { 3792168676Spjd changelist_free(cl); 3793168676Spjd } 3794168404Spjd return (ret); 3795168404Spjd} 3796168404Spjd 3797219089Spjdnvlist_t * 3798219089Spjdzfs_get_user_props(zfs_handle_t *zhp) 3799168404Spjd{ 3800219089Spjd return (zhp->zfs_user_props); 3801168676Spjd} 3802168676Spjd 3803168404Spjdnvlist_t * 3804219089Spjdzfs_get_recvd_props(zfs_handle_t *zhp) 3805168404Spjd{ 3806219089Spjd if (zhp->zfs_recvd_props == NULL) 3807219089Spjd if (get_recvd_props_ioctl(zhp) != 0) 3808219089Spjd return (NULL); 3809219089Spjd return (zhp->zfs_recvd_props); 3810168404Spjd} 3811168404Spjd 3812168404Spjd/* 3813168404Spjd * This function is used by 'zfs list' to determine the exact set of columns to 3814168404Spjd * display, and their maximum widths. This does two main things: 3815168404Spjd * 3816168404Spjd * - If this is a list of all properties, then expand the list to include 3817168404Spjd * all native properties, and set a flag so that for each dataset we look 3818168404Spjd * for new unique user properties and add them to the list. 3819168404Spjd * 3820168404Spjd * - For non fixed-width properties, keep track of the maximum width seen 3821219089Spjd * so that we can size the column appropriately. If the user has 3822219089Spjd * requested received property values, we also need to compute the width 3823219089Spjd * of the RECEIVED column. 3824168404Spjd */ 3825168404Spjdint 3826219089Spjdzfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) 3827168404Spjd{ 3828168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3829185029Spjd zprop_list_t *entry; 3830185029Spjd zprop_list_t **last, **start; 3831168404Spjd nvlist_t *userprops, *propval; 3832168404Spjd nvpair_t *elem; 3833168404Spjd char *strval; 3834168404Spjd char buf[ZFS_MAXPROPLEN]; 3835168404Spjd 3836185029Spjd if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3837168404Spjd return (-1); 3838168404Spjd 3839168404Spjd userprops = zfs_get_user_props(zhp); 3840168404Spjd 3841168404Spjd entry = *plp; 3842168404Spjd if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3843168404Spjd /* 3844168404Spjd * Go through and add any user properties as necessary. We 3845168404Spjd * start by incrementing our list pointer to the first 3846168404Spjd * non-native property. 3847168404Spjd */ 3848168404Spjd start = plp; 3849168404Spjd while (*start != NULL) { 3850185029Spjd if ((*start)->pl_prop == ZPROP_INVAL) 3851168404Spjd break; 3852168404Spjd start = &(*start)->pl_next; 3853168404Spjd } 3854168404Spjd 3855168404Spjd elem = NULL; 3856168404Spjd while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3857168404Spjd /* 3858168404Spjd * See if we've already found this property in our list. 3859168404Spjd */ 3860168404Spjd for (last = start; *last != NULL; 3861168404Spjd last = &(*last)->pl_next) { 3862168404Spjd if (strcmp((*last)->pl_user_prop, 3863168404Spjd nvpair_name(elem)) == 0) 3864168404Spjd break; 3865168404Spjd } 3866168404Spjd 3867168404Spjd if (*last == NULL) { 3868168404Spjd if ((entry = zfs_alloc(hdl, 3869185029Spjd sizeof (zprop_list_t))) == NULL || 3870168404Spjd ((entry->pl_user_prop = zfs_strdup(hdl, 3871168404Spjd nvpair_name(elem)))) == NULL) { 3872168404Spjd free(entry); 3873168404Spjd return (-1); 3874168404Spjd } 3875168404Spjd 3876185029Spjd entry->pl_prop = ZPROP_INVAL; 3877168404Spjd entry->pl_width = strlen(nvpair_name(elem)); 3878168404Spjd entry->pl_all = B_TRUE; 3879168404Spjd *last = entry; 3880168404Spjd } 3881168404Spjd } 3882168404Spjd } 3883168404Spjd 3884168404Spjd /* 3885168404Spjd * Now go through and check the width of any non-fixed columns 3886168404Spjd */ 3887168404Spjd for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3888168404Spjd if (entry->pl_fixed) 3889168404Spjd continue; 3890168404Spjd 3891185029Spjd if (entry->pl_prop != ZPROP_INVAL) { 3892168404Spjd if (zfs_prop_get(zhp, entry->pl_prop, 3893168404Spjd buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3894168404Spjd if (strlen(buf) > entry->pl_width) 3895168404Spjd entry->pl_width = strlen(buf); 3896168404Spjd } 3897219089Spjd if (received && zfs_prop_get_recvd(zhp, 3898219089Spjd zfs_prop_to_name(entry->pl_prop), 3899219089Spjd buf, sizeof (buf), B_FALSE) == 0) 3900219089Spjd if (strlen(buf) > entry->pl_recvd_width) 3901219089Spjd entry->pl_recvd_width = strlen(buf); 3902219089Spjd } else { 3903219089Spjd if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 3904219089Spjd &propval) == 0) { 3905219089Spjd verify(nvlist_lookup_string(propval, 3906219089Spjd ZPROP_VALUE, &strval) == 0); 3907219089Spjd if (strlen(strval) > entry->pl_width) 3908219089Spjd entry->pl_width = strlen(strval); 3909219089Spjd } 3910219089Spjd if (received && zfs_prop_get_recvd(zhp, 3911219089Spjd entry->pl_user_prop, 3912219089Spjd buf, sizeof (buf), B_FALSE) == 0) 3913219089Spjd if (strlen(buf) > entry->pl_recvd_width) 3914219089Spjd entry->pl_recvd_width = strlen(buf); 3915168404Spjd } 3916168404Spjd } 3917168404Spjd 3918168404Spjd return (0); 3919168404Spjd} 3920168404Spjd 3921185029Spjdint 3922185029Spjdzfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 3923209962Smm char *resource, void *export, void *sharetab, 3924209962Smm int sharemax, zfs_share_op_t operation) 3925185029Spjd{ 3926185029Spjd zfs_cmd_t zc = { 0 }; 3927185029Spjd int error; 3928185029Spjd 3929185029Spjd (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3930185029Spjd (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3931209962Smm if (resource) 3932209962Smm (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 3933185029Spjd zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 3934185029Spjd zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 3935185029Spjd zc.zc_share.z_sharetype = operation; 3936185029Spjd zc.zc_share.z_sharemax = sharemax; 3937185029Spjd error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 3938185029Spjd return (error); 3939185029Spjd} 3940185029Spjd 3941205198Sdelphijvoid 3942205198Sdelphijzfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 3943205198Sdelphij{ 3944205198Sdelphij nvpair_t *curr; 3945205198Sdelphij 3946205198Sdelphij /* 3947205198Sdelphij * Keep a reference to the props-table against which we prune the 3948205198Sdelphij * properties. 3949205198Sdelphij */ 3950205198Sdelphij zhp->zfs_props_table = props; 3951205198Sdelphij 3952205198Sdelphij curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 3953205198Sdelphij 3954205198Sdelphij while (curr) { 3955205198Sdelphij zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 3956205198Sdelphij nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 3957205198Sdelphij 3958206199Sdelphij /* 3959219089Spjd * User properties will result in ZPROP_INVAL, and since we 3960219089Spjd * only know how to prune standard ZFS properties, we always 3961219089Spjd * leave these in the list. This can also happen if we 3962219089Spjd * encounter an unknown DSL property (when running older 3963219089Spjd * software, for example). 3964206199Sdelphij */ 3965206199Sdelphij if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 3966205198Sdelphij (void) nvlist_remove(zhp->zfs_props, 3967205198Sdelphij nvpair_name(curr), nvpair_type(curr)); 3968205198Sdelphij curr = next; 3969205198Sdelphij } 3970205198Sdelphij} 3971205198Sdelphij 3972209962Smm#ifdef sun 3973209962Smmstatic int 3974209962Smmzfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 3975209962Smm zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 3976209962Smm{ 3977209962Smm zfs_cmd_t zc = { 0 }; 3978209962Smm nvlist_t *nvlist = NULL; 3979209962Smm int error; 3980209962Smm 3981209962Smm (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3982209962Smm (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 3983209962Smm zc.zc_cookie = (uint64_t)cmd; 3984209962Smm 3985209962Smm if (cmd == ZFS_SMB_ACL_RENAME) { 3986209962Smm if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 3987209962Smm (void) no_memory(hdl); 3988209962Smm return (NULL); 3989209962Smm } 3990209962Smm } 3991209962Smm 3992209962Smm switch (cmd) { 3993209962Smm case ZFS_SMB_ACL_ADD: 3994209962Smm case ZFS_SMB_ACL_REMOVE: 3995209962Smm (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 3996209962Smm break; 3997209962Smm case ZFS_SMB_ACL_RENAME: 3998209962Smm if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 3999209962Smm resource1) != 0) { 4000209962Smm (void) no_memory(hdl); 4001209962Smm return (-1); 4002209962Smm } 4003209962Smm if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 4004209962Smm resource2) != 0) { 4005209962Smm (void) no_memory(hdl); 4006209962Smm return (-1); 4007209962Smm } 4008209962Smm if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 4009209962Smm nvlist_free(nvlist); 4010209962Smm return (-1); 4011209962Smm } 4012209962Smm break; 4013209962Smm case ZFS_SMB_ACL_PURGE: 4014209962Smm break; 4015209962Smm default: 4016209962Smm return (-1); 4017209962Smm } 4018209962Smm error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 4019209962Smm if (nvlist) 4020209962Smm nvlist_free(nvlist); 4021209962Smm return (error); 4022209962Smm} 4023209962Smm 4024209962Smmint 4025209962Smmzfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 4026209962Smm char *path, char *resource) 4027209962Smm{ 4028209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 4029209962Smm resource, NULL)); 4030209962Smm} 4031209962Smm 4032209962Smmint 4033209962Smmzfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 4034209962Smm char *path, char *resource) 4035209962Smm{ 4036209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 4037209962Smm resource, NULL)); 4038209962Smm} 4039209962Smm 4040209962Smmint 4041209962Smmzfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 4042209962Smm{ 4043209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 4044209962Smm NULL, NULL)); 4045209962Smm} 4046209962Smm 4047209962Smmint 4048209962Smmzfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 4049209962Smm char *oldname, char *newname) 4050209962Smm{ 4051209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 4052209962Smm oldname, newname)); 4053209962Smm} 4054209962Smm#endif /* sun */ 4055209962Smm 4056209962Smmint 4057209962Smmzfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 4058209962Smm zfs_userspace_cb_t func, void *arg) 4059209962Smm{ 4060209962Smm zfs_cmd_t zc = { 0 }; 4061209962Smm int error; 4062209962Smm zfs_useracct_t buf[100]; 4063209962Smm 4064228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4065209962Smm 4066209962Smm zc.zc_objset_type = type; 4067209962Smm zc.zc_nvlist_dst = (uintptr_t)buf; 4068209962Smm 4069209962Smm /* CONSTCOND */ 4070209962Smm while (1) { 4071209962Smm zfs_useracct_t *zua = buf; 4072209962Smm 4073209962Smm zc.zc_nvlist_dst_size = sizeof (buf); 4074209962Smm error = ioctl(zhp->zfs_hdl->libzfs_fd, 4075209962Smm ZFS_IOC_USERSPACE_MANY, &zc); 4076209962Smm if (error || zc.zc_nvlist_dst_size == 0) 4077209962Smm break; 4078209962Smm 4079209962Smm while (zc.zc_nvlist_dst_size > 0) { 4080209962Smm error = func(arg, zua->zu_domain, zua->zu_rid, 4081209962Smm zua->zu_space); 4082209962Smm if (error != 0) 4083209962Smm return (error); 4084209962Smm zua++; 4085209962Smm zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 4086209962Smm } 4087209962Smm } 4088209962Smm 4089209962Smm return (error); 4090209962Smm} 4091209962Smm 4092219089Spjdint 4093219089Spjdzfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 4094219089Spjd boolean_t recursive, boolean_t temphold, boolean_t enoent_ok, 4095219089Spjd int cleanup_fd, uint64_t dsobj, uint64_t createtxg) 4096219089Spjd{ 4097219089Spjd zfs_cmd_t zc = { 0 }; 4098219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4099219089Spjd 4100219089Spjd ASSERT(!recursive || dsobj == 0); 4101219089Spjd 4102219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4103219089Spjd (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4104219089Spjd if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 4105219089Spjd >= sizeof (zc.zc_string)) 4106219089Spjd return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 4107219089Spjd zc.zc_cookie = recursive; 4108219089Spjd zc.zc_temphold = temphold; 4109219089Spjd zc.zc_cleanup_fd = cleanup_fd; 4110219089Spjd zc.zc_sendobj = dsobj; 4111219089Spjd zc.zc_createtxg = createtxg; 4112219089Spjd 4113219089Spjd if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) { 4114219089Spjd char errbuf[ZFS_MAXNAMELEN+32]; 4115219089Spjd 4116219089Spjd /* 4117219089Spjd * if it was recursive, the one that actually failed will be in 4118219089Spjd * zc.zc_name. 4119219089Spjd */ 4120219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4121219089Spjd "cannot hold '%s@%s'"), zc.zc_name, snapname); 4122219089Spjd switch (errno) { 4123219089Spjd case E2BIG: 4124219089Spjd /* 4125219089Spjd * Temporary tags wind up having the ds object id 4126219089Spjd * prepended. So even if we passed the length check 4127219089Spjd * above, it's still possible for the tag to wind 4128219089Spjd * up being slightly too long. 4129219089Spjd */ 4130219089Spjd return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf)); 4131219089Spjd case ENOTSUP: 4132219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4133219089Spjd "pool must be upgraded")); 4134219089Spjd return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4135219089Spjd case EINVAL: 4136219089Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4137219089Spjd case EEXIST: 4138219089Spjd return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf)); 4139219089Spjd case ENOENT: 4140219089Spjd if (enoent_ok) 4141219089Spjd return (ENOENT); 4142219089Spjd /* FALLTHROUGH */ 4143219089Spjd default: 4144219089Spjd return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4145219089Spjd } 4146219089Spjd } 4147219089Spjd 4148219089Spjd return (0); 4149219089Spjd} 4150219089Spjd 4151219089Spjdint 4152219089Spjdzfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4153219089Spjd boolean_t recursive) 4154219089Spjd{ 4155219089Spjd zfs_cmd_t zc = { 0 }; 4156219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4157219089Spjd 4158219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4159219089Spjd (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 4160219089Spjd if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string)) 4161219089Spjd >= sizeof (zc.zc_string)) 4162219089Spjd return (zfs_error(hdl, EZFS_TAGTOOLONG, tag)); 4163219089Spjd zc.zc_cookie = recursive; 4164219089Spjd 4165219089Spjd if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) { 4166219089Spjd char errbuf[ZFS_MAXNAMELEN+32]; 4167219089Spjd 4168219089Spjd /* 4169219089Spjd * if it was recursive, the one that actually failed will be in 4170219089Spjd * zc.zc_name. 4171219089Spjd */ 4172219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4173219089Spjd "cannot release '%s' from '%s@%s'"), tag, zc.zc_name, 4174219089Spjd snapname); 4175219089Spjd switch (errno) { 4176219089Spjd case ESRCH: 4177219089Spjd return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf)); 4178219089Spjd case ENOTSUP: 4179219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4180219089Spjd "pool must be upgraded")); 4181219089Spjd return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 4182219089Spjd case EINVAL: 4183219089Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4184219089Spjd default: 4185219089Spjd return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4186219089Spjd } 4187219089Spjd } 4188219089Spjd 4189219089Spjd return (0); 4190219089Spjd} 4191219089Spjd 4192219089Spjdint 4193219089Spjdzfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl) 4194219089Spjd{ 4195219089Spjd zfs_cmd_t zc = { 0 }; 4196219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4197219089Spjd int nvsz = 2048; 4198219089Spjd void *nvbuf; 4199219089Spjd int err = 0; 4200219089Spjd char errbuf[ZFS_MAXNAMELEN+32]; 4201219089Spjd 4202219089Spjd assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 4203219089Spjd zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4204219089Spjd 4205219089Spjdtryagain: 4206219089Spjd 4207219089Spjd nvbuf = malloc(nvsz); 4208219089Spjd if (nvbuf == NULL) { 4209219089Spjd err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno))); 4210219089Spjd goto out; 4211219089Spjd } 4212219089Spjd 4213219089Spjd zc.zc_nvlist_dst_size = nvsz; 4214219089Spjd zc.zc_nvlist_dst = (uintptr_t)nvbuf; 4215219089Spjd 4216219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN); 4217219089Spjd 4218230514Smm if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) { 4219219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4220219089Spjd dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"), 4221219089Spjd zc.zc_name); 4222219089Spjd switch (errno) { 4223219089Spjd case ENOMEM: 4224219089Spjd free(nvbuf); 4225219089Spjd nvsz = zc.zc_nvlist_dst_size; 4226219089Spjd goto tryagain; 4227219089Spjd 4228219089Spjd case ENOTSUP: 4229219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4230219089Spjd "pool must be upgraded")); 4231219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4232219089Spjd break; 4233219089Spjd case EINVAL: 4234219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4235219089Spjd break; 4236219089Spjd case ENOENT: 4237219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4238219089Spjd break; 4239219089Spjd default: 4240219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4241219089Spjd break; 4242219089Spjd } 4243219089Spjd } else { 4244219089Spjd /* success */ 4245219089Spjd int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0); 4246219089Spjd if (rc) { 4247219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext( 4248219089Spjd TEXT_DOMAIN, "cannot get permissions on '%s'"), 4249219089Spjd zc.zc_name); 4250219089Spjd err = zfs_standard_error_fmt(hdl, rc, errbuf); 4251219089Spjd } 4252219089Spjd } 4253219089Spjd 4254219089Spjd free(nvbuf); 4255219089Spjdout: 4256219089Spjd return (err); 4257219089Spjd} 4258219089Spjd 4259219089Spjdint 4260219089Spjdzfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl) 4261219089Spjd{ 4262219089Spjd zfs_cmd_t zc = { 0 }; 4263219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4264219089Spjd char *nvbuf; 4265219089Spjd char errbuf[ZFS_MAXNAMELEN+32]; 4266219089Spjd size_t nvsz; 4267219089Spjd int err; 4268219089Spjd 4269219089Spjd assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 4270219089Spjd zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4271219089Spjd 4272219089Spjd err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); 4273219089Spjd assert(err == 0); 4274219089Spjd 4275219089Spjd nvbuf = malloc(nvsz); 4276219089Spjd 4277219089Spjd err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); 4278219089Spjd assert(err == 0); 4279219089Spjd 4280219089Spjd zc.zc_nvlist_src_size = nvsz; 4281219089Spjd zc.zc_nvlist_src = (uintptr_t)nvbuf; 4282219089Spjd zc.zc_perm_action = un; 4283219089Spjd 4284219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4285219089Spjd 4286219089Spjd if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) { 4287219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4288219089Spjd dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"), 4289219089Spjd zc.zc_name); 4290219089Spjd switch (errno) { 4291219089Spjd case ENOTSUP: 4292219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4293219089Spjd "pool must be upgraded")); 4294219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4295219089Spjd break; 4296219089Spjd case EINVAL: 4297219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4298219089Spjd break; 4299219089Spjd case ENOENT: 4300219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4301219089Spjd break; 4302219089Spjd default: 4303219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4304219089Spjd break; 4305219089Spjd } 4306219089Spjd } 4307219089Spjd 4308219089Spjd free(nvbuf); 4309219089Spjd 4310219089Spjd return (err); 4311219089Spjd} 4312219089Spjd 4313219089Spjdint 4314219089Spjdzfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl) 4315219089Spjd{ 4316219089Spjd zfs_cmd_t zc = { 0 }; 4317219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4318219089Spjd int nvsz = 2048; 4319219089Spjd void *nvbuf; 4320219089Spjd int err = 0; 4321219089Spjd char errbuf[ZFS_MAXNAMELEN+32]; 4322219089Spjd 4323219089Spjd assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 4324219089Spjd 4325219089Spjdtryagain: 4326219089Spjd 4327219089Spjd nvbuf = malloc(nvsz); 4328219089Spjd if (nvbuf == NULL) { 4329219089Spjd err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno))); 4330219089Spjd goto out; 4331219089Spjd } 4332219089Spjd 4333219089Spjd zc.zc_nvlist_dst_size = nvsz; 4334219089Spjd zc.zc_nvlist_dst = (uintptr_t)nvbuf; 4335219089Spjd 4336219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN); 4337219089Spjd 4338219089Spjd if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) != 0) { 4339219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4340219089Spjd dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"), 4341219089Spjd zc.zc_name); 4342219089Spjd switch (errno) { 4343219089Spjd case ENOMEM: 4344219089Spjd free(nvbuf); 4345219089Spjd nvsz = zc.zc_nvlist_dst_size; 4346219089Spjd goto tryagain; 4347219089Spjd 4348219089Spjd case ENOTSUP: 4349219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4350219089Spjd "pool must be upgraded")); 4351219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4352219089Spjd break; 4353219089Spjd case EINVAL: 4354219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4355219089Spjd break; 4356219089Spjd case ENOENT: 4357219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4358219089Spjd break; 4359219089Spjd default: 4360219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4361219089Spjd break; 4362219089Spjd } 4363219089Spjd } else { 4364219089Spjd /* success */ 4365219089Spjd int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0); 4366219089Spjd if (rc) { 4367219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4368219089Spjd dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"), 4369219089Spjd zc.zc_name); 4370219089Spjd err = zfs_standard_error_fmt(hdl, rc, errbuf); 4371219089Spjd } 4372219089Spjd } 4373219089Spjd 4374219089Spjd free(nvbuf); 4375219089Spjdout: 4376219089Spjd return (err); 4377219089Spjd} 4378219089Spjd 4379219089Spjduint64_t 4380219089Spjdzvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4381219089Spjd{ 4382219089Spjd uint64_t numdb; 4383219089Spjd uint64_t nblocks, volblocksize; 4384219089Spjd int ncopies; 4385219089Spjd char *strval; 4386219089Spjd 4387219089Spjd if (nvlist_lookup_string(props, 4388219089Spjd zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4389219089Spjd ncopies = atoi(strval); 4390219089Spjd else 4391219089Spjd ncopies = 1; 4392219089Spjd if (nvlist_lookup_uint64(props, 4393219089Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4394219089Spjd &volblocksize) != 0) 4395219089Spjd volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4396219089Spjd nblocks = volsize/volblocksize; 4397219089Spjd /* start with metadnode L0-L6 */ 4398219089Spjd numdb = 7; 4399219089Spjd /* calculate number of indirects */ 4400219089Spjd while (nblocks > 1) { 4401219089Spjd nblocks += DNODES_PER_LEVEL - 1; 4402219089Spjd nblocks /= DNODES_PER_LEVEL; 4403219089Spjd numdb += nblocks; 4404219089Spjd } 4405219089Spjd numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4406219089Spjd volsize *= ncopies; 4407219089Spjd /* 4408219089Spjd * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4409219089Spjd * compressed, but in practice they compress down to about 4410219089Spjd * 1100 bytes 4411219089Spjd */ 4412219089Spjd numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4413219089Spjd volsize += numdb; 4414219089Spjd return (volsize); 4415219089Spjd} 4416219089Spjd 4417168404Spjd/* 4418168404Spjd * Attach/detach the given filesystem to/from the given jail. 4419168404Spjd */ 4420168404Spjdint 4421168404Spjdzfs_jail(zfs_handle_t *zhp, int jailid, int attach) 4422168404Spjd{ 4423168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4424168404Spjd zfs_cmd_t zc = { 0 }; 4425168404Spjd char errbuf[1024]; 4426224525Smm unsigned long cmd; 4427224525Smm int ret; 4428168404Spjd 4429168404Spjd if (attach) { 4430168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4431168404Spjd dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name); 4432168404Spjd } else { 4433168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4434168404Spjd dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name); 4435168404Spjd } 4436168404Spjd 4437168404Spjd switch (zhp->zfs_type) { 4438168404Spjd case ZFS_TYPE_VOLUME: 4439168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4440168404Spjd "volumes can not be jailed")); 4441168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4442168404Spjd case ZFS_TYPE_SNAPSHOT: 4443168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4444168404Spjd "snapshots can not be jailed")); 4445168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4446168404Spjd } 4447168404Spjd assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4448168404Spjd 4449168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4450168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 4451168404Spjd zc.zc_jailid = jailid; 4452168404Spjd 4453168404Spjd cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL; 4454168404Spjd if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0) 4455168404Spjd zfs_standard_error(hdl, errno, errbuf); 4456168404Spjd 4457168404Spjd return (ret); 4458168404Spjd} 4459