libzfs_dataset.c revision 271764
1168404Spjd/* 2168404Spjd * CDDL HEADER START 3168404Spjd * 4168404Spjd * The contents of this file are subject to the terms of the 5168404Spjd * Common Development and Distribution License (the "License"). 6168404Spjd * You may not use this file except in compliance with the License. 7168404Spjd * 8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9168404Spjd * or http://www.opensolaris.org/os/licensing. 10168404Spjd * See the License for the specific language governing permissions 11168404Spjd * and limitations under the License. 12168404Spjd * 13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each 14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15168404Spjd * If applicable, add the following below this CDDL HEADER, with the 16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18168404Spjd * 19168404Spjd * CDDL HEADER END 20168404Spjd */ 21168404Spjd 22168404Spjd/* 23219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24264835Sdelphij * Copyright (c) 2013, Joyent, Inc. All rights reserved. 25268469Sdelphij * Copyright (c) 2011, 2014 by Delphix. All rights reserved. 26252219Sdelphij * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. 27230438Spjd * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>. 28226706Spjd * All rights reserved. 29235216Smm * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved. 30251646Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved. 31259850Sdelphij * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 32168404Spjd */ 33168404Spjd 34168404Spjd#include <ctype.h> 35168404Spjd#include <errno.h> 36168404Spjd#include <libintl.h> 37168404Spjd#include <math.h> 38168404Spjd#include <stdio.h> 39168404Spjd#include <stdlib.h> 40168404Spjd#include <strings.h> 41168404Spjd#include <unistd.h> 42185029Spjd#include <stddef.h> 43168404Spjd#include <zone.h> 44168404Spjd#include <fcntl.h> 45168404Spjd#include <sys/mntent.h> 46168404Spjd#include <sys/mount.h> 47185029Spjd#include <priv.h> 48185029Spjd#include <pwd.h> 49185029Spjd#include <grp.h> 50185029Spjd#include <stddef.h> 51209962Smm#include <idmap.h> 52168404Spjd 53219089Spjd#include <sys/dnode.h> 54168404Spjd#include <sys/spa.h> 55168404Spjd#include <sys/zap.h> 56209962Smm#include <sys/misc.h> 57168404Spjd#include <libzfs.h> 58168404Spjd 59168404Spjd#include "zfs_namecheck.h" 60168404Spjd#include "zfs_prop.h" 61168404Spjd#include "libzfs_impl.h" 62185029Spjd#include "zfs_deleg.h" 63168404Spjd 64209962Smmstatic int userquota_propname_decode(const char *propname, boolean_t zoned, 65209962Smm zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 66168676Spjd 67168404Spjd/* 68168404Spjd * Given a single type (not a mask of types), return the type in a human 69168404Spjd * readable form. 70168404Spjd */ 71168404Spjdconst char * 72168404Spjdzfs_type_to_name(zfs_type_t type) 73168404Spjd{ 74168404Spjd switch (type) { 75168404Spjd case ZFS_TYPE_FILESYSTEM: 76168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 77168404Spjd case ZFS_TYPE_SNAPSHOT: 78168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 79168404Spjd case ZFS_TYPE_VOLUME: 80168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 81168404Spjd } 82168404Spjd 83168404Spjd return (NULL); 84168404Spjd} 85168404Spjd 86168404Spjd/* 87168404Spjd * Given a path and mask of ZFS types, return a string describing this dataset. 88168404Spjd * This is used when we fail to open a dataset and we cannot get an exact type. 89168404Spjd * We guess what the type would have been based on the path and the mask of 90168404Spjd * acceptable types. 91168404Spjd */ 92168404Spjdstatic const char * 93168404Spjdpath_to_str(const char *path, int types) 94168404Spjd{ 95168404Spjd /* 96168404Spjd * When given a single type, always report the exact type. 97168404Spjd */ 98168404Spjd if (types == ZFS_TYPE_SNAPSHOT) 99168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 100168404Spjd if (types == ZFS_TYPE_FILESYSTEM) 101168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 102168404Spjd if (types == ZFS_TYPE_VOLUME) 103168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 104168404Spjd 105168404Spjd /* 106168404Spjd * The user is requesting more than one type of dataset. If this is the 107168404Spjd * case, consult the path itself. If we're looking for a snapshot, and 108168404Spjd * a '@' is found, then report it as "snapshot". Otherwise, remove the 109168404Spjd * snapshot attribute and try again. 110168404Spjd */ 111168404Spjd if (types & ZFS_TYPE_SNAPSHOT) { 112168404Spjd if (strchr(path, '@') != NULL) 113168404Spjd return (dgettext(TEXT_DOMAIN, "snapshot")); 114168404Spjd return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 115168404Spjd } 116168404Spjd 117168404Spjd /* 118168404Spjd * The user has requested either filesystems or volumes. 119168404Spjd * We have no way of knowing a priori what type this would be, so always 120168404Spjd * report it as "filesystem" or "volume", our two primitive types. 121168404Spjd */ 122168404Spjd if (types & ZFS_TYPE_FILESYSTEM) 123168404Spjd return (dgettext(TEXT_DOMAIN, "filesystem")); 124168404Spjd 125168404Spjd assert(types & ZFS_TYPE_VOLUME); 126168404Spjd return (dgettext(TEXT_DOMAIN, "volume")); 127168404Spjd} 128168404Spjd 129168404Spjd/* 130168404Spjd * Validate a ZFS path. This is used even before trying to open the dataset, to 131209962Smm * provide a more meaningful error message. We call zfs_error_aux() to 132209962Smm * explain exactly why the name was not valid. 133168404Spjd */ 134219089Spjdint 135185029Spjdzfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 136185029Spjd boolean_t modifying) 137168404Spjd{ 138168404Spjd namecheck_err_t why; 139168404Spjd char what; 140168404Spjd 141219089Spjd (void) zfs_prop_get_table(); 142168404Spjd if (dataset_namecheck(path, &why, &what) != 0) { 143168404Spjd if (hdl != NULL) { 144168404Spjd switch (why) { 145168404Spjd case NAME_ERR_TOOLONG: 146168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 147168404Spjd "name is too long")); 148168404Spjd break; 149168404Spjd 150168404Spjd case NAME_ERR_LEADING_SLASH: 151168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 152168404Spjd "leading slash in name")); 153168404Spjd break; 154168404Spjd 155168404Spjd case NAME_ERR_EMPTY_COMPONENT: 156168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 157168404Spjd "empty component in name")); 158168404Spjd break; 159168404Spjd 160168404Spjd case NAME_ERR_TRAILING_SLASH: 161168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 162168404Spjd "trailing slash in name")); 163168404Spjd break; 164168404Spjd 165168404Spjd case NAME_ERR_INVALCHAR: 166168404Spjd zfs_error_aux(hdl, 167168404Spjd dgettext(TEXT_DOMAIN, "invalid character " 168168404Spjd "'%c' in name"), what); 169168404Spjd break; 170168404Spjd 171168404Spjd case NAME_ERR_MULTIPLE_AT: 172168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 173168404Spjd "multiple '@' delimiters in name")); 174168404Spjd break; 175168404Spjd 176168404Spjd case NAME_ERR_NOLETTER: 177168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 178168404Spjd "pool doesn't begin with a letter")); 179168404Spjd break; 180168404Spjd 181168404Spjd case NAME_ERR_RESERVED: 182168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 183168404Spjd "name is reserved")); 184168404Spjd break; 185168404Spjd 186168404Spjd case NAME_ERR_DISKLIKE: 187168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 188168404Spjd "reserved disk name")); 189168404Spjd break; 190168404Spjd } 191168404Spjd } 192168404Spjd 193168404Spjd return (0); 194168404Spjd } 195168404Spjd 196168404Spjd if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 197168404Spjd if (hdl != NULL) 198168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 199168404Spjd "snapshot delimiter '@' in filesystem name")); 200168404Spjd return (0); 201168404Spjd } 202168404Spjd 203168404Spjd if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 204168404Spjd if (hdl != NULL) 205168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 206168404Spjd "missing '@' delimiter in snapshot name")); 207168404Spjd return (0); 208168404Spjd } 209168404Spjd 210185029Spjd if (modifying && strchr(path, '%') != NULL) { 211185029Spjd if (hdl != NULL) 212185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 213185029Spjd "invalid character %c in name"), '%'); 214185029Spjd return (0); 215185029Spjd } 216185029Spjd 217168404Spjd return (-1); 218168404Spjd} 219168404Spjd 220168404Spjdint 221168404Spjdzfs_name_valid(const char *name, zfs_type_t type) 222168404Spjd{ 223185029Spjd if (type == ZFS_TYPE_POOL) 224185029Spjd return (zpool_name_valid(NULL, B_FALSE, name)); 225185029Spjd return (zfs_validate_name(NULL, name, type, B_FALSE)); 226168404Spjd} 227168404Spjd 228168404Spjd/* 229168404Spjd * This function takes the raw DSL properties, and filters out the user-defined 230168404Spjd * properties into a separate nvlist. 231168404Spjd */ 232185029Spjdstatic nvlist_t * 233185029Spjdprocess_user_props(zfs_handle_t *zhp, nvlist_t *props) 234168404Spjd{ 235168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 236168404Spjd nvpair_t *elem; 237168404Spjd nvlist_t *propval; 238185029Spjd nvlist_t *nvl; 239168404Spjd 240185029Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 241185029Spjd (void) no_memory(hdl); 242185029Spjd return (NULL); 243185029Spjd } 244168404Spjd 245168404Spjd elem = NULL; 246185029Spjd while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 247168404Spjd if (!zfs_prop_user(nvpair_name(elem))) 248168404Spjd continue; 249168404Spjd 250168404Spjd verify(nvpair_value_nvlist(elem, &propval) == 0); 251185029Spjd if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 252185029Spjd nvlist_free(nvl); 253185029Spjd (void) no_memory(hdl); 254185029Spjd return (NULL); 255185029Spjd } 256168404Spjd } 257168404Spjd 258185029Spjd return (nvl); 259168404Spjd} 260168404Spjd 261185029Spjdstatic zpool_handle_t * 262185029Spjdzpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 263185029Spjd{ 264185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 265185029Spjd zpool_handle_t *zph; 266185029Spjd 267185029Spjd if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 268185029Spjd if (hdl->libzfs_pool_handles != NULL) 269185029Spjd zph->zpool_next = hdl->libzfs_pool_handles; 270185029Spjd hdl->libzfs_pool_handles = zph; 271185029Spjd } 272185029Spjd return (zph); 273185029Spjd} 274185029Spjd 275185029Spjdstatic zpool_handle_t * 276185029Spjdzpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 277185029Spjd{ 278185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 279185029Spjd zpool_handle_t *zph = hdl->libzfs_pool_handles; 280185029Spjd 281185029Spjd while ((zph != NULL) && 282185029Spjd (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 283185029Spjd zph = zph->zpool_next; 284185029Spjd return (zph); 285185029Spjd} 286185029Spjd 287168404Spjd/* 288185029Spjd * Returns a handle to the pool that contains the provided dataset. 289185029Spjd * If a handle to that pool already exists then that handle is returned. 290185029Spjd * Otherwise, a new handle is created and added to the list of handles. 291185029Spjd */ 292185029Spjdstatic zpool_handle_t * 293185029Spjdzpool_handle(zfs_handle_t *zhp) 294185029Spjd{ 295185029Spjd char *pool_name; 296185029Spjd int len; 297185029Spjd zpool_handle_t *zph; 298185029Spjd 299260183Sdelphij len = strcspn(zhp->zfs_name, "/@#") + 1; 300185029Spjd pool_name = zfs_alloc(zhp->zfs_hdl, len); 301185029Spjd (void) strlcpy(pool_name, zhp->zfs_name, len); 302185029Spjd 303185029Spjd zph = zpool_find_handle(zhp, pool_name, len); 304185029Spjd if (zph == NULL) 305185029Spjd zph = zpool_add_handle(zhp, pool_name); 306185029Spjd 307185029Spjd free(pool_name); 308185029Spjd return (zph); 309185029Spjd} 310185029Spjd 311185029Spjdvoid 312185029Spjdzpool_free_handles(libzfs_handle_t *hdl) 313185029Spjd{ 314185029Spjd zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 315185029Spjd 316185029Spjd while (zph != NULL) { 317185029Spjd next = zph->zpool_next; 318185029Spjd zpool_close(zph); 319185029Spjd zph = next; 320185029Spjd } 321185029Spjd hdl->libzfs_pool_handles = NULL; 322185029Spjd} 323185029Spjd 324185029Spjd/* 325168404Spjd * Utility function to gather stats (objset and zpl) for the given object. 326168404Spjd */ 327219089Spjdstatic int 328209962Smmget_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 329168404Spjd{ 330168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 331168404Spjd 332209962Smm (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 333168404Spjd 334209962Smm while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 335168404Spjd if (errno == ENOMEM) { 336209962Smm if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 337168404Spjd return (-1); 338168404Spjd } 339168404Spjd } else { 340168404Spjd return (-1); 341168404Spjd } 342168404Spjd } 343209962Smm return (0); 344209962Smm} 345168404Spjd 346219089Spjd/* 347219089Spjd * Utility function to get the received properties of the given object. 348219089Spjd */ 349209962Smmstatic int 350219089Spjdget_recvd_props_ioctl(zfs_handle_t *zhp) 351219089Spjd{ 352219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 353219089Spjd nvlist_t *recvdprops; 354219089Spjd zfs_cmd_t zc = { 0 }; 355219089Spjd int err; 356219089Spjd 357219089Spjd if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 358219089Spjd return (-1); 359219089Spjd 360219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 361219089Spjd 362219089Spjd while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 363219089Spjd if (errno == ENOMEM) { 364219089Spjd if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 365219089Spjd return (-1); 366219089Spjd } 367219089Spjd } else { 368219089Spjd zcmd_free_nvlists(&zc); 369219089Spjd return (-1); 370219089Spjd } 371219089Spjd } 372219089Spjd 373219089Spjd err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 374219089Spjd zcmd_free_nvlists(&zc); 375219089Spjd if (err != 0) 376219089Spjd return (-1); 377219089Spjd 378219089Spjd nvlist_free(zhp->zfs_recvd_props); 379219089Spjd zhp->zfs_recvd_props = recvdprops; 380219089Spjd 381219089Spjd return (0); 382219089Spjd} 383219089Spjd 384219089Spjdstatic int 385209962Smmput_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 386209962Smm{ 387209962Smm nvlist_t *allprops, *userprops; 388168404Spjd 389209962Smm zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 390209962Smm 391209962Smm if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 392168404Spjd return (-1); 393168404Spjd } 394168404Spjd 395209962Smm /* 396209962Smm * XXX Why do we store the user props separately, in addition to 397209962Smm * storing them in zfs_props? 398209962Smm */ 399185029Spjd if ((userprops = process_user_props(zhp, allprops)) == NULL) { 400185029Spjd nvlist_free(allprops); 401168404Spjd return (-1); 402185029Spjd } 403168404Spjd 404185029Spjd nvlist_free(zhp->zfs_props); 405185029Spjd nvlist_free(zhp->zfs_user_props); 406185029Spjd 407185029Spjd zhp->zfs_props = allprops; 408185029Spjd zhp->zfs_user_props = userprops; 409185029Spjd 410168404Spjd return (0); 411168404Spjd} 412168404Spjd 413209962Smmstatic int 414209962Smmget_stats(zfs_handle_t *zhp) 415209962Smm{ 416209962Smm int rc = 0; 417209962Smm zfs_cmd_t zc = { 0 }; 418209962Smm 419209962Smm if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 420209962Smm return (-1); 421209962Smm if (get_stats_ioctl(zhp, &zc) != 0) 422209962Smm rc = -1; 423209962Smm else if (put_stats_zhdl(zhp, &zc) != 0) 424209962Smm rc = -1; 425209962Smm zcmd_free_nvlists(&zc); 426209962Smm return (rc); 427209962Smm} 428209962Smm 429168404Spjd/* 430168404Spjd * Refresh the properties currently stored in the handle. 431168404Spjd */ 432168404Spjdvoid 433168404Spjdzfs_refresh_properties(zfs_handle_t *zhp) 434168404Spjd{ 435168404Spjd (void) get_stats(zhp); 436168404Spjd} 437168404Spjd 438168404Spjd/* 439168404Spjd * Makes a handle from the given dataset name. Used by zfs_open() and 440168404Spjd * zfs_iter_* to create child handles on the fly. 441168404Spjd */ 442209962Smmstatic int 443209962Smmmake_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 444168404Spjd{ 445219089Spjd if (put_stats_zhdl(zhp, zc) != 0) 446209962Smm return (-1); 447168404Spjd 448168404Spjd /* 449168404Spjd * We've managed to open the dataset and gather statistics. Determine 450168404Spjd * the high-level type. 451168404Spjd */ 452168404Spjd if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 453168404Spjd zhp->zfs_head_type = ZFS_TYPE_VOLUME; 454168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 455168404Spjd zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 456168404Spjd else 457168404Spjd abort(); 458168404Spjd 459168404Spjd if (zhp->zfs_dmustats.dds_is_snapshot) 460168404Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 461168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 462168404Spjd zhp->zfs_type = ZFS_TYPE_VOLUME; 463168404Spjd else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 464168404Spjd zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 465168404Spjd else 466168404Spjd abort(); /* we should never see any other types */ 467168404Spjd 468219089Spjd if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 469219089Spjd return (-1); 470219089Spjd 471209962Smm return (0); 472209962Smm} 473209962Smm 474209962Smmzfs_handle_t * 475209962Smmmake_dataset_handle(libzfs_handle_t *hdl, const char *path) 476209962Smm{ 477209962Smm zfs_cmd_t zc = { 0 }; 478209962Smm 479209962Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 480209962Smm 481209962Smm if (zhp == NULL) 482209962Smm return (NULL); 483209962Smm 484209962Smm zhp->zfs_hdl = hdl; 485209962Smm (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 486209962Smm if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 487209962Smm free(zhp); 488209962Smm return (NULL); 489209962Smm } 490209962Smm if (get_stats_ioctl(zhp, &zc) == -1) { 491209962Smm zcmd_free_nvlists(&zc); 492209962Smm free(zhp); 493209962Smm return (NULL); 494209962Smm } 495209962Smm if (make_dataset_handle_common(zhp, &zc) == -1) { 496209962Smm free(zhp); 497209962Smm zhp = NULL; 498209962Smm } 499209962Smm zcmd_free_nvlists(&zc); 500168404Spjd return (zhp); 501168404Spjd} 502168404Spjd 503228103Smmzfs_handle_t * 504209962Smmmake_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 505209962Smm{ 506209962Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 507209962Smm 508209962Smm if (zhp == NULL) 509209962Smm return (NULL); 510209962Smm 511209962Smm zhp->zfs_hdl = hdl; 512209962Smm (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 513209962Smm if (make_dataset_handle_common(zhp, zc) == -1) { 514209962Smm free(zhp); 515209962Smm return (NULL); 516209962Smm } 517209962Smm return (zhp); 518209962Smm} 519209962Smm 520228103Smmzfs_handle_t * 521230438Spjdmake_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc) 522230438Spjd{ 523230438Spjd zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 524230438Spjd 525230438Spjd if (zhp == NULL) 526230438Spjd return (NULL); 527230438Spjd 528230438Spjd zhp->zfs_hdl = pzhp->zfs_hdl; 529230438Spjd (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 530230438Spjd zhp->zfs_head_type = pzhp->zfs_type; 531230438Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 532230438Spjd zhp->zpool_hdl = zpool_handle(zhp); 533230438Spjd return (zhp); 534230438Spjd} 535230438Spjd 536230438Spjdzfs_handle_t * 537228103Smmzfs_handle_dup(zfs_handle_t *zhp_orig) 538228103Smm{ 539228103Smm zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 540228103Smm 541228103Smm if (zhp == NULL) 542228103Smm return (NULL); 543228103Smm 544228103Smm zhp->zfs_hdl = zhp_orig->zfs_hdl; 545228103Smm zhp->zpool_hdl = zhp_orig->zpool_hdl; 546228103Smm (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name, 547228103Smm sizeof (zhp->zfs_name)); 548228103Smm zhp->zfs_type = zhp_orig->zfs_type; 549228103Smm zhp->zfs_head_type = zhp_orig->zfs_head_type; 550228103Smm zhp->zfs_dmustats = zhp_orig->zfs_dmustats; 551228103Smm if (zhp_orig->zfs_props != NULL) { 552228103Smm if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) { 553228103Smm (void) no_memory(zhp->zfs_hdl); 554228103Smm zfs_close(zhp); 555228103Smm return (NULL); 556228103Smm } 557228103Smm } 558228103Smm if (zhp_orig->zfs_user_props != NULL) { 559228103Smm if (nvlist_dup(zhp_orig->zfs_user_props, 560228103Smm &zhp->zfs_user_props, 0) != 0) { 561228103Smm (void) no_memory(zhp->zfs_hdl); 562228103Smm zfs_close(zhp); 563228103Smm return (NULL); 564228103Smm } 565228103Smm } 566228103Smm if (zhp_orig->zfs_recvd_props != NULL) { 567228103Smm if (nvlist_dup(zhp_orig->zfs_recvd_props, 568228103Smm &zhp->zfs_recvd_props, 0)) { 569228103Smm (void) no_memory(zhp->zfs_hdl); 570228103Smm zfs_close(zhp); 571228103Smm return (NULL); 572228103Smm } 573228103Smm } 574228103Smm zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck; 575228103Smm if (zhp_orig->zfs_mntopts != NULL) { 576228103Smm zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl, 577228103Smm zhp_orig->zfs_mntopts); 578228103Smm } 579228103Smm zhp->zfs_props_table = zhp_orig->zfs_props_table; 580228103Smm return (zhp); 581228103Smm} 582228103Smm 583260183Sdelphijboolean_t 584260183Sdelphijzfs_bookmark_exists(const char *path) 585260183Sdelphij{ 586260183Sdelphij nvlist_t *bmarks; 587260183Sdelphij nvlist_t *props; 588260183Sdelphij char fsname[ZFS_MAXNAMELEN]; 589260183Sdelphij char *bmark_name; 590260183Sdelphij char *pound; 591260183Sdelphij int err; 592260183Sdelphij boolean_t rv; 593260183Sdelphij 594260183Sdelphij 595260183Sdelphij (void) strlcpy(fsname, path, sizeof (fsname)); 596260183Sdelphij pound = strchr(fsname, '#'); 597260183Sdelphij if (pound == NULL) 598260183Sdelphij return (B_FALSE); 599260183Sdelphij 600260183Sdelphij *pound = '\0'; 601260183Sdelphij bmark_name = pound + 1; 602260183Sdelphij props = fnvlist_alloc(); 603260183Sdelphij err = lzc_get_bookmarks(fsname, props, &bmarks); 604260183Sdelphij nvlist_free(props); 605260183Sdelphij if (err != 0) { 606260183Sdelphij nvlist_free(bmarks); 607260183Sdelphij return (B_FALSE); 608260183Sdelphij } 609260183Sdelphij 610260183Sdelphij rv = nvlist_exists(bmarks, bmark_name); 611260183Sdelphij nvlist_free(bmarks); 612260183Sdelphij return (rv); 613260183Sdelphij} 614260183Sdelphij 615260183Sdelphijzfs_handle_t * 616260183Sdelphijmake_bookmark_handle(zfs_handle_t *parent, const char *path, 617260183Sdelphij nvlist_t *bmark_props) 618260183Sdelphij{ 619260183Sdelphij zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 620260183Sdelphij 621260183Sdelphij if (zhp == NULL) 622260183Sdelphij return (NULL); 623260183Sdelphij 624260183Sdelphij /* Fill in the name. */ 625260183Sdelphij zhp->zfs_hdl = parent->zfs_hdl; 626260183Sdelphij (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 627260183Sdelphij 628260183Sdelphij /* Set the property lists. */ 629260183Sdelphij if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) { 630260183Sdelphij free(zhp); 631260183Sdelphij return (NULL); 632260183Sdelphij } 633260183Sdelphij 634260183Sdelphij /* Set the types. */ 635260183Sdelphij zhp->zfs_head_type = parent->zfs_head_type; 636260183Sdelphij zhp->zfs_type = ZFS_TYPE_BOOKMARK; 637260183Sdelphij 638260183Sdelphij if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) { 639260183Sdelphij nvlist_free(zhp->zfs_props); 640260183Sdelphij free(zhp); 641260183Sdelphij return (NULL); 642260183Sdelphij } 643260183Sdelphij 644260183Sdelphij return (zhp); 645260183Sdelphij} 646260183Sdelphij 647168404Spjd/* 648168404Spjd * Opens the given snapshot, filesystem, or volume. The 'types' 649168404Spjd * argument is a mask of acceptable types. The function will print an 650168404Spjd * appropriate error message and return NULL if it can't be opened. 651168404Spjd */ 652168404Spjdzfs_handle_t * 653168404Spjdzfs_open(libzfs_handle_t *hdl, const char *path, int types) 654168404Spjd{ 655168404Spjd zfs_handle_t *zhp; 656168404Spjd char errbuf[1024]; 657168404Spjd 658168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 659168404Spjd dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 660168404Spjd 661168404Spjd /* 662168404Spjd * Validate the name before we even try to open it. 663168404Spjd */ 664185029Spjd if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 665168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 666168404Spjd "invalid dataset name")); 667168404Spjd (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 668168404Spjd return (NULL); 669168404Spjd } 670168404Spjd 671168404Spjd /* 672168404Spjd * Try to get stats for the dataset, which will tell us if it exists. 673168404Spjd */ 674168404Spjd errno = 0; 675168404Spjd if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 676168404Spjd (void) zfs_standard_error(hdl, errno, errbuf); 677168404Spjd return (NULL); 678168404Spjd } 679168404Spjd 680240870Spjd if (zhp == NULL) { 681240870Spjd char *at = strchr(path, '@'); 682240870Spjd 683240870Spjd if (at != NULL) 684240870Spjd *at = '\0'; 685240870Spjd errno = 0; 686240870Spjd if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 687240870Spjd (void) zfs_standard_error(hdl, errno, errbuf); 688240870Spjd return (NULL); 689240870Spjd } 690240870Spjd if (at != NULL) 691240870Spjd *at = '@'; 692240870Spjd (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 693240870Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 694240870Spjd } 695240870Spjd 696168404Spjd if (!(types & zhp->zfs_type)) { 697168404Spjd (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 698168404Spjd zfs_close(zhp); 699168404Spjd return (NULL); 700168404Spjd } 701168404Spjd 702168404Spjd return (zhp); 703168404Spjd} 704168404Spjd 705168404Spjd/* 706168404Spjd * Release a ZFS handle. Nothing to do but free the associated memory. 707168404Spjd */ 708168404Spjdvoid 709168404Spjdzfs_close(zfs_handle_t *zhp) 710168404Spjd{ 711168404Spjd if (zhp->zfs_mntopts) 712168404Spjd free(zhp->zfs_mntopts); 713168404Spjd nvlist_free(zhp->zfs_props); 714168404Spjd nvlist_free(zhp->zfs_user_props); 715219089Spjd nvlist_free(zhp->zfs_recvd_props); 716168404Spjd free(zhp); 717168404Spjd} 718168404Spjd 719209962Smmtypedef struct mnttab_node { 720209962Smm struct mnttab mtn_mt; 721209962Smm avl_node_t mtn_node; 722209962Smm} mnttab_node_t; 723209962Smm 724209962Smmstatic int 725209962Smmlibzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 726209962Smm{ 727209962Smm const mnttab_node_t *mtn1 = arg1; 728209962Smm const mnttab_node_t *mtn2 = arg2; 729209962Smm int rv; 730209962Smm 731209962Smm rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 732209962Smm 733209962Smm if (rv == 0) 734209962Smm return (0); 735209962Smm return (rv > 0 ? 1 : -1); 736209962Smm} 737209962Smm 738209962Smmvoid 739209962Smmlibzfs_mnttab_init(libzfs_handle_t *hdl) 740209962Smm{ 741209962Smm assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 742209962Smm avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 743209962Smm sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 744209962Smm} 745209962Smm 746209962Smmvoid 747209962Smmlibzfs_mnttab_update(libzfs_handle_t *hdl) 748209962Smm{ 749209962Smm struct mnttab entry; 750209962Smm 751209962Smm rewind(hdl->libzfs_mnttab); 752209962Smm while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 753209962Smm mnttab_node_t *mtn; 754209962Smm 755209962Smm if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 756209962Smm continue; 757209962Smm mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 758209962Smm mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 759209962Smm mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 760209962Smm mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 761209962Smm mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 762209962Smm avl_add(&hdl->libzfs_mnttab_cache, mtn); 763209962Smm } 764209962Smm} 765209962Smm 766209962Smmvoid 767209962Smmlibzfs_mnttab_fini(libzfs_handle_t *hdl) 768209962Smm{ 769209962Smm void *cookie = NULL; 770209962Smm mnttab_node_t *mtn; 771209962Smm 772209962Smm while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) { 773209962Smm free(mtn->mtn_mt.mnt_special); 774209962Smm free(mtn->mtn_mt.mnt_mountp); 775209962Smm free(mtn->mtn_mt.mnt_fstype); 776209962Smm free(mtn->mtn_mt.mnt_mntopts); 777209962Smm free(mtn); 778209962Smm } 779209962Smm avl_destroy(&hdl->libzfs_mnttab_cache); 780209962Smm} 781209962Smm 782209962Smmvoid 783209962Smmlibzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 784209962Smm{ 785209962Smm hdl->libzfs_mnttab_enable = enable; 786209962Smm} 787209962Smm 788185029Spjdint 789209962Smmlibzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 790209962Smm struct mnttab *entry) 791209962Smm{ 792209962Smm mnttab_node_t find; 793209962Smm mnttab_node_t *mtn; 794209962Smm 795209962Smm if (!hdl->libzfs_mnttab_enable) { 796209962Smm struct mnttab srch = { 0 }; 797209962Smm 798209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 799209962Smm libzfs_mnttab_fini(hdl); 800209962Smm rewind(hdl->libzfs_mnttab); 801209962Smm srch.mnt_special = (char *)fsname; 802209962Smm srch.mnt_fstype = MNTTYPE_ZFS; 803209962Smm if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 804209962Smm return (0); 805209962Smm else 806209962Smm return (ENOENT); 807209962Smm } 808209962Smm 809209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 810209962Smm libzfs_mnttab_update(hdl); 811209962Smm 812209962Smm find.mtn_mt.mnt_special = (char *)fsname; 813209962Smm mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 814209962Smm if (mtn) { 815209962Smm *entry = mtn->mtn_mt; 816209962Smm return (0); 817209962Smm } 818209962Smm return (ENOENT); 819209962Smm} 820209962Smm 821209962Smmvoid 822209962Smmlibzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 823209962Smm const char *mountp, const char *mntopts) 824209962Smm{ 825209962Smm mnttab_node_t *mtn; 826209962Smm 827209962Smm if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 828209962Smm return; 829209962Smm mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 830209962Smm mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 831209962Smm mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 832209962Smm mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 833209962Smm mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 834209962Smm avl_add(&hdl->libzfs_mnttab_cache, mtn); 835209962Smm} 836209962Smm 837209962Smmvoid 838209962Smmlibzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 839209962Smm{ 840209962Smm mnttab_node_t find; 841209962Smm mnttab_node_t *ret; 842209962Smm 843209962Smm find.mtn_mt.mnt_special = (char *)fsname; 844209962Smm if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) { 845209962Smm avl_remove(&hdl->libzfs_mnttab_cache, ret); 846209962Smm free(ret->mtn_mt.mnt_special); 847209962Smm free(ret->mtn_mt.mnt_mountp); 848209962Smm free(ret->mtn_mt.mnt_fstype); 849209962Smm free(ret->mtn_mt.mnt_mntopts); 850209962Smm free(ret); 851209962Smm } 852209962Smm} 853209962Smm 854209962Smmint 855185029Spjdzfs_spa_version(zfs_handle_t *zhp, int *spa_version) 856168404Spjd{ 857185029Spjd zpool_handle_t *zpool_handle = zhp->zpool_hdl; 858168404Spjd 859185029Spjd if (zpool_handle == NULL) 860168404Spjd return (-1); 861168404Spjd 862185029Spjd *spa_version = zpool_get_prop_int(zpool_handle, 863185029Spjd ZPOOL_PROP_VERSION, NULL); 864168404Spjd return (0); 865168404Spjd} 866168404Spjd 867168404Spjd/* 868185029Spjd * The choice of reservation property depends on the SPA version. 869168404Spjd */ 870168404Spjdstatic int 871185029Spjdzfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 872168404Spjd{ 873185029Spjd int spa_version; 874168404Spjd 875185029Spjd if (zfs_spa_version(zhp, &spa_version) < 0) 876168404Spjd return (-1); 877168404Spjd 878185029Spjd if (spa_version >= SPA_VERSION_REFRESERVATION) 879185029Spjd *resv_prop = ZFS_PROP_REFRESERVATION; 880185029Spjd else 881185029Spjd *resv_prop = ZFS_PROP_RESERVATION; 882168404Spjd 883168404Spjd return (0); 884168404Spjd} 885168404Spjd 886168404Spjd/* 887168404Spjd * Given an nvlist of properties to set, validates that they are correct, and 888168404Spjd * parses any numeric properties (index, boolean, etc) if they are specified as 889168404Spjd * strings. 890168404Spjd */ 891168404Spjdnvlist_t * 892185029Spjdzfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 893185029Spjd uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 894168404Spjd{ 895168404Spjd nvpair_t *elem; 896168404Spjd uint64_t intval; 897168404Spjd char *strval; 898185029Spjd zfs_prop_t prop; 899168404Spjd nvlist_t *ret; 900185029Spjd int chosen_normal = -1; 901185029Spjd int chosen_utf = -1; 902168404Spjd 903168404Spjd if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 904168404Spjd (void) no_memory(hdl); 905168404Spjd return (NULL); 906168404Spjd } 907168404Spjd 908209962Smm /* 909209962Smm * Make sure this property is valid and applies to this type. 910209962Smm */ 911209962Smm 912168404Spjd elem = NULL; 913168404Spjd while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 914185029Spjd const char *propname = nvpair_name(elem); 915168404Spjd 916209962Smm prop = zfs_name_to_prop(propname); 917209962Smm if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 918185029Spjd /* 919209962Smm * This is a user property: make sure it's a 920185029Spjd * string, and that it's less than ZAP_MAXNAMELEN. 921185029Spjd */ 922185029Spjd if (nvpair_type(elem) != DATA_TYPE_STRING) { 923185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 924185029Spjd "'%s' must be a string"), propname); 925185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 926185029Spjd goto error; 927168404Spjd } 928168404Spjd 929185029Spjd if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 930185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 931185029Spjd "property name '%s' is too long"), 932185029Spjd propname); 933185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 934185029Spjd goto error; 935185029Spjd } 936185029Spjd 937168404Spjd (void) nvpair_value_string(elem, &strval); 938168404Spjd if (nvlist_add_string(ret, propname, strval) != 0) { 939168404Spjd (void) no_memory(hdl); 940168404Spjd goto error; 941168404Spjd } 942168404Spjd continue; 943168404Spjd } 944168404Spjd 945209962Smm /* 946209962Smm * Currently, only user properties can be modified on 947209962Smm * snapshots. 948209962Smm */ 949185029Spjd if (type == ZFS_TYPE_SNAPSHOT) { 950185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 951185029Spjd "this property can not be modified for snapshots")); 952185029Spjd (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 953185029Spjd goto error; 954185029Spjd } 955168404Spjd 956209962Smm if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 957209962Smm zfs_userquota_prop_t uqtype; 958209962Smm char newpropname[128]; 959209962Smm char domain[128]; 960209962Smm uint64_t rid; 961209962Smm uint64_t valary[3]; 962209962Smm 963209962Smm if (userquota_propname_decode(propname, zoned, 964209962Smm &uqtype, domain, sizeof (domain), &rid) != 0) { 965209962Smm zfs_error_aux(hdl, 966209962Smm dgettext(TEXT_DOMAIN, 967209962Smm "'%s' has an invalid user/group name"), 968209962Smm propname); 969209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 970209962Smm goto error; 971209962Smm } 972209962Smm 973209962Smm if (uqtype != ZFS_PROP_USERQUOTA && 974209962Smm uqtype != ZFS_PROP_GROUPQUOTA) { 975209962Smm zfs_error_aux(hdl, 976209962Smm dgettext(TEXT_DOMAIN, "'%s' is readonly"), 977209962Smm propname); 978209962Smm (void) zfs_error(hdl, EZFS_PROPREADONLY, 979209962Smm errbuf); 980209962Smm goto error; 981209962Smm } 982209962Smm 983209962Smm if (nvpair_type(elem) == DATA_TYPE_STRING) { 984209962Smm (void) nvpair_value_string(elem, &strval); 985209962Smm if (strcmp(strval, "none") == 0) { 986209962Smm intval = 0; 987209962Smm } else if (zfs_nicestrtonum(hdl, 988209962Smm strval, &intval) != 0) { 989209962Smm (void) zfs_error(hdl, 990209962Smm EZFS_BADPROP, errbuf); 991209962Smm goto error; 992209962Smm } 993209962Smm } else if (nvpair_type(elem) == 994209962Smm DATA_TYPE_UINT64) { 995209962Smm (void) nvpair_value_uint64(elem, &intval); 996209962Smm if (intval == 0) { 997209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 998209962Smm "use 'none' to disable " 999209962Smm "userquota/groupquota")); 1000209962Smm goto error; 1001209962Smm } 1002209962Smm } else { 1003209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1004209962Smm "'%s' must be a number"), propname); 1005209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1006209962Smm goto error; 1007209962Smm } 1008209962Smm 1009219089Spjd /* 1010219089Spjd * Encode the prop name as 1011219089Spjd * userquota@<hex-rid>-domain, to make it easy 1012219089Spjd * for the kernel to decode. 1013219089Spjd */ 1014209962Smm (void) snprintf(newpropname, sizeof (newpropname), 1015219089Spjd "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 1016219089Spjd (longlong_t)rid, domain); 1017209962Smm valary[0] = uqtype; 1018209962Smm valary[1] = rid; 1019209962Smm valary[2] = intval; 1020209962Smm if (nvlist_add_uint64_array(ret, newpropname, 1021209962Smm valary, 3) != 0) { 1022209962Smm (void) no_memory(hdl); 1023209962Smm goto error; 1024209962Smm } 1025209962Smm continue; 1026228103Smm } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) { 1027228103Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1028228103Smm "'%s' is readonly"), 1029228103Smm propname); 1030228103Smm (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 1031228103Smm goto error; 1032209962Smm } 1033209962Smm 1034209962Smm if (prop == ZPROP_INVAL) { 1035209962Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1036209962Smm "invalid property '%s'"), propname); 1037209962Smm (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1038209962Smm goto error; 1039209962Smm } 1040209962Smm 1041168404Spjd if (!zfs_prop_valid_for_type(prop, type)) { 1042168404Spjd zfs_error_aux(hdl, 1043168404Spjd dgettext(TEXT_DOMAIN, "'%s' does not " 1044168404Spjd "apply to datasets of this type"), propname); 1045168404Spjd (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 1046168404Spjd goto error; 1047168404Spjd } 1048168404Spjd 1049168404Spjd if (zfs_prop_readonly(prop) && 1050185029Spjd (!zfs_prop_setonce(prop) || zhp != NULL)) { 1051168404Spjd zfs_error_aux(hdl, 1052168404Spjd dgettext(TEXT_DOMAIN, "'%s' is readonly"), 1053168404Spjd propname); 1054168404Spjd (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 1055168404Spjd goto error; 1056168404Spjd } 1057168404Spjd 1058185029Spjd if (zprop_parse_value(hdl, elem, prop, type, ret, 1059185029Spjd &strval, &intval, errbuf) != 0) 1060185029Spjd goto error; 1061185029Spjd 1062168404Spjd /* 1063185029Spjd * Perform some additional checks for specific properties. 1064168404Spjd */ 1065185029Spjd switch (prop) { 1066185029Spjd case ZFS_PROP_VERSION: 1067185029Spjd { 1068185029Spjd int version; 1069168404Spjd 1070185029Spjd if (zhp == NULL) 1071185029Spjd break; 1072185029Spjd version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 1073185029Spjd if (intval < version) { 1074168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1075185029Spjd "Can not downgrade; already at version %u"), 1076185029Spjd version); 1077168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1078168404Spjd goto error; 1079168404Spjd } 1080168404Spjd break; 1081168404Spjd } 1082168404Spjd 1083168404Spjd case ZFS_PROP_RECORDSIZE: 1084168404Spjd case ZFS_PROP_VOLBLOCKSIZE: 1085168404Spjd /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 1086168404Spjd if (intval < SPA_MINBLOCKSIZE || 1087168404Spjd intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 1088168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1089168404Spjd "'%s' must be power of 2 from %u " 1090168404Spjd "to %uk"), propname, 1091168404Spjd (uint_t)SPA_MINBLOCKSIZE, 1092168404Spjd (uint_t)SPA_MAXBLOCKSIZE >> 10); 1093168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1094168404Spjd goto error; 1095168404Spjd } 1096168404Spjd break; 1097168404Spjd 1098219089Spjd case ZFS_PROP_MLSLABEL: 1099219089Spjd { 1100219089Spjd#ifdef sun 1101219089Spjd /* 1102219089Spjd * Verify the mlslabel string and convert to 1103219089Spjd * internal hex label string. 1104219089Spjd */ 1105219089Spjd 1106219089Spjd m_label_t *new_sl; 1107219089Spjd char *hex = NULL; /* internal label string */ 1108219089Spjd 1109219089Spjd /* Default value is already OK. */ 1110219089Spjd if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 1111219089Spjd break; 1112219089Spjd 1113219089Spjd /* Verify the label can be converted to binary form */ 1114219089Spjd if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 1115219089Spjd (str_to_label(strval, &new_sl, MAC_LABEL, 1116219089Spjd L_NO_CORRECTION, NULL) == -1)) { 1117219089Spjd goto badlabel; 1118168404Spjd } 1119168404Spjd 1120219089Spjd /* Now translate to hex internal label string */ 1121219089Spjd if (label_to_str(new_sl, &hex, M_INTERNAL, 1122219089Spjd DEF_NAMES) != 0) { 1123219089Spjd if (hex) 1124219089Spjd free(hex); 1125219089Spjd goto badlabel; 1126219089Spjd } 1127219089Spjd m_label_free(new_sl); 1128219089Spjd 1129219089Spjd /* If string is already in internal form, we're done. */ 1130219089Spjd if (strcmp(strval, hex) == 0) { 1131219089Spjd free(hex); 1132219089Spjd break; 1133219089Spjd } 1134219089Spjd 1135219089Spjd /* Replace the label string with the internal form. */ 1136219089Spjd (void) nvlist_remove(ret, zfs_prop_to_name(prop), 1137219089Spjd DATA_TYPE_STRING); 1138219089Spjd verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 1139219089Spjd hex) == 0); 1140219089Spjd free(hex); 1141219089Spjd 1142168404Spjd break; 1143168404Spjd 1144219089Spjdbadlabel: 1145219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1146219089Spjd "invalid mlslabel '%s'"), strval); 1147219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1148219089Spjd m_label_free(new_sl); /* OK if null */ 1149219089Spjd#else /* !sun */ 1150219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1151219089Spjd "mlslabel is not supported on FreeBSD")); 1152219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1153219089Spjd#endif /* !sun */ 1154219089Spjd goto error; 1155219089Spjd 1156219089Spjd } 1157219089Spjd 1158168404Spjd case ZFS_PROP_MOUNTPOINT: 1159185029Spjd { 1160185029Spjd namecheck_err_t why; 1161185029Spjd 1162168404Spjd if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1163168404Spjd strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1164168404Spjd break; 1165168404Spjd 1166185029Spjd if (mountpoint_namecheck(strval, &why)) { 1167185029Spjd switch (why) { 1168185029Spjd case NAME_ERR_LEADING_SLASH: 1169185029Spjd zfs_error_aux(hdl, 1170185029Spjd dgettext(TEXT_DOMAIN, 1171185029Spjd "'%s' must be an absolute path, " 1172185029Spjd "'none', or 'legacy'"), propname); 1173185029Spjd break; 1174185029Spjd case NAME_ERR_TOOLONG: 1175185029Spjd zfs_error_aux(hdl, 1176185029Spjd dgettext(TEXT_DOMAIN, 1177185029Spjd "component of '%s' is too long"), 1178185029Spjd propname); 1179185029Spjd break; 1180185029Spjd } 1181168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1182168404Spjd goto error; 1183168404Spjd } 1184185029Spjd } 1185185029Spjd 1186168404Spjd /*FALLTHRU*/ 1187168404Spjd 1188185029Spjd case ZFS_PROP_SHARESMB: 1189168404Spjd case ZFS_PROP_SHARENFS: 1190168404Spjd /* 1191185029Spjd * For the mountpoint and sharenfs or sharesmb 1192185029Spjd * properties, check if it can be set in a 1193185029Spjd * global/non-global zone based on 1194168404Spjd * the zoned property value: 1195168404Spjd * 1196168404Spjd * global zone non-global zone 1197168404Spjd * -------------------------------------------------- 1198168404Spjd * zoned=on mountpoint (no) mountpoint (yes) 1199168404Spjd * sharenfs (no) sharenfs (no) 1200185029Spjd * sharesmb (no) sharesmb (no) 1201168404Spjd * 1202168404Spjd * zoned=off mountpoint (yes) N/A 1203168404Spjd * sharenfs (yes) 1204185029Spjd * sharesmb (yes) 1205168404Spjd */ 1206168404Spjd if (zoned) { 1207168404Spjd if (getzoneid() == GLOBAL_ZONEID) { 1208168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1209168404Spjd "'%s' cannot be set on " 1210168404Spjd "dataset in a non-global zone"), 1211168404Spjd propname); 1212168404Spjd (void) zfs_error(hdl, EZFS_ZONED, 1213168404Spjd errbuf); 1214168404Spjd goto error; 1215185029Spjd } else if (prop == ZFS_PROP_SHARENFS || 1216185029Spjd prop == ZFS_PROP_SHARESMB) { 1217168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1218168404Spjd "'%s' cannot be set in " 1219168404Spjd "a non-global zone"), propname); 1220168404Spjd (void) zfs_error(hdl, EZFS_ZONED, 1221168404Spjd errbuf); 1222168404Spjd goto error; 1223168404Spjd } 1224168404Spjd } else if (getzoneid() != GLOBAL_ZONEID) { 1225168404Spjd /* 1226168404Spjd * If zoned property is 'off', this must be in 1227209962Smm * a global zone. If not, something is wrong. 1228168404Spjd */ 1229168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1230168404Spjd "'%s' cannot be set while dataset " 1231168404Spjd "'zoned' property is set"), propname); 1232168404Spjd (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1233168404Spjd goto error; 1234168404Spjd } 1235168404Spjd 1236168404Spjd /* 1237185029Spjd * At this point, it is legitimate to set the 1238185029Spjd * property. Now we want to make sure that the 1239185029Spjd * property value is valid if it is sharenfs. 1240168404Spjd */ 1241185029Spjd if ((prop == ZFS_PROP_SHARENFS || 1242185029Spjd prop == ZFS_PROP_SHARESMB) && 1243185029Spjd strcmp(strval, "on") != 0 && 1244185029Spjd strcmp(strval, "off") != 0) { 1245185029Spjd zfs_share_proto_t proto; 1246168404Spjd 1247185029Spjd if (prop == ZFS_PROP_SHARESMB) 1248185029Spjd proto = PROTO_SMB; 1249185029Spjd else 1250185029Spjd proto = PROTO_NFS; 1251185029Spjd 1252185029Spjd /* 1253185029Spjd * Must be an valid sharing protocol 1254185029Spjd * option string so init the libshare 1255185029Spjd * in order to enable the parser and 1256185029Spjd * then parse the options. We use the 1257185029Spjd * control API since we don't care about 1258185029Spjd * the current configuration and don't 1259185029Spjd * want the overhead of loading it 1260185029Spjd * until we actually do something. 1261185029Spjd */ 1262185029Spjd 1263185029Spjd if (zfs_init_libshare(hdl, 1264185029Spjd SA_INIT_CONTROL_API) != SA_OK) { 1265185029Spjd /* 1266185029Spjd * An error occurred so we can't do 1267185029Spjd * anything 1268185029Spjd */ 1269185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1270185029Spjd "'%s' cannot be set: problem " 1271185029Spjd "in share initialization"), 1272185029Spjd propname); 1273185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1274185029Spjd errbuf); 1275185029Spjd goto error; 1276185029Spjd } 1277185029Spjd 1278185029Spjd if (zfs_parse_options(strval, proto) != SA_OK) { 1279185029Spjd /* 1280185029Spjd * There was an error in parsing so 1281185029Spjd * deal with it by issuing an error 1282185029Spjd * message and leaving after 1283185029Spjd * uninitializing the the libshare 1284185029Spjd * interface. 1285185029Spjd */ 1286185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1287185029Spjd "'%s' cannot be set to invalid " 1288185029Spjd "options"), propname); 1289185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1290185029Spjd errbuf); 1291185029Spjd zfs_uninit_libshare(hdl); 1292185029Spjd goto error; 1293185029Spjd } 1294185029Spjd zfs_uninit_libshare(hdl); 1295168404Spjd } 1296185029Spjd 1297168404Spjd break; 1298185029Spjd case ZFS_PROP_UTF8ONLY: 1299185029Spjd chosen_utf = (int)intval; 1300185029Spjd break; 1301185029Spjd case ZFS_PROP_NORMALIZE: 1302185029Spjd chosen_normal = (int)intval; 1303185029Spjd break; 1304168404Spjd } 1305168404Spjd 1306168404Spjd /* 1307168404Spjd * For changes to existing volumes, we have some additional 1308168404Spjd * checks to enforce. 1309168404Spjd */ 1310168404Spjd if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1311168404Spjd uint64_t volsize = zfs_prop_get_int(zhp, 1312168404Spjd ZFS_PROP_VOLSIZE); 1313168404Spjd uint64_t blocksize = zfs_prop_get_int(zhp, 1314168404Spjd ZFS_PROP_VOLBLOCKSIZE); 1315168404Spjd char buf[64]; 1316168404Spjd 1317168404Spjd switch (prop) { 1318168404Spjd case ZFS_PROP_RESERVATION: 1319185029Spjd case ZFS_PROP_REFRESERVATION: 1320168404Spjd if (intval > volsize) { 1321168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1322168404Spjd "'%s' is greater than current " 1323168404Spjd "volume size"), propname); 1324168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1325168404Spjd errbuf); 1326168404Spjd goto error; 1327168404Spjd } 1328168404Spjd break; 1329168404Spjd 1330168404Spjd case ZFS_PROP_VOLSIZE: 1331168404Spjd if (intval % blocksize != 0) { 1332168404Spjd zfs_nicenum(blocksize, buf, 1333168404Spjd sizeof (buf)); 1334168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1335168404Spjd "'%s' must be a multiple of " 1336168404Spjd "volume block size (%s)"), 1337168404Spjd propname, buf); 1338168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1339168404Spjd errbuf); 1340168404Spjd goto error; 1341168404Spjd } 1342168404Spjd 1343168404Spjd if (intval == 0) { 1344168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1345168404Spjd "'%s' cannot be zero"), 1346168404Spjd propname); 1347168404Spjd (void) zfs_error(hdl, EZFS_BADPROP, 1348168404Spjd errbuf); 1349168404Spjd goto error; 1350168404Spjd } 1351168404Spjd break; 1352168404Spjd } 1353168404Spjd } 1354168404Spjd } 1355168404Spjd 1356168404Spjd /* 1357185029Spjd * If normalization was chosen, but no UTF8 choice was made, 1358185029Spjd * enforce rejection of non-UTF8 names. 1359185029Spjd * 1360185029Spjd * If normalization was chosen, but rejecting non-UTF8 names 1361185029Spjd * was explicitly not chosen, it is an error. 1362185029Spjd */ 1363185029Spjd if (chosen_normal > 0 && chosen_utf < 0) { 1364185029Spjd if (nvlist_add_uint64(ret, 1365185029Spjd zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1366185029Spjd (void) no_memory(hdl); 1367185029Spjd goto error; 1368185029Spjd } 1369185029Spjd } else if (chosen_normal > 0 && chosen_utf == 0) { 1370185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1371185029Spjd "'%s' must be set 'on' if normalization chosen"), 1372185029Spjd zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1373185029Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1374185029Spjd goto error; 1375185029Spjd } 1376219089Spjd return (ret); 1377185029Spjd 1378219089Spjderror: 1379219089Spjd nvlist_free(ret); 1380219089Spjd return (NULL); 1381219089Spjd} 1382219089Spjd 1383219089Spjdint 1384219089Spjdzfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) 1385219089Spjd{ 1386219089Spjd uint64_t old_volsize; 1387219089Spjd uint64_t new_volsize; 1388219089Spjd uint64_t old_reservation; 1389219089Spjd uint64_t new_reservation; 1390219089Spjd zfs_prop_t resv_prop; 1391219089Spjd 1392185029Spjd /* 1393168404Spjd * If this is an existing volume, and someone is setting the volsize, 1394168404Spjd * make sure that it matches the reservation, or add it if necessary. 1395168404Spjd */ 1396219089Spjd old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1397219089Spjd if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 1398219089Spjd return (-1); 1399219089Spjd old_reservation = zfs_prop_get_int(zhp, resv_prop); 1400219089Spjd if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) != 1401219089Spjd old_reservation) || nvlist_lookup_uint64(nvl, 1402219089Spjd zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) { 1403219089Spjd return (0); 1404219089Spjd } 1405219089Spjd if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1406219089Spjd &new_volsize) != 0) 1407219089Spjd return (-1); 1408219089Spjd new_reservation = zvol_volsize_to_reservation(new_volsize, 1409219089Spjd zhp->zfs_props); 1410219089Spjd if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), 1411219089Spjd new_reservation) != 0) { 1412219089Spjd (void) no_memory(zhp->zfs_hdl); 1413219089Spjd return (-1); 1414219089Spjd } 1415219089Spjd return (1); 1416219089Spjd} 1417168404Spjd 1418219089Spjdvoid 1419219089Spjdzfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 1420219089Spjd char *errbuf) 1421219089Spjd{ 1422219089Spjd switch (err) { 1423185029Spjd 1424219089Spjd case ENOSPC: 1425219089Spjd /* 1426219089Spjd * For quotas and reservations, ENOSPC indicates 1427219089Spjd * something different; setting a quota or reservation 1428219089Spjd * doesn't use any disk space. 1429219089Spjd */ 1430219089Spjd switch (prop) { 1431219089Spjd case ZFS_PROP_QUOTA: 1432219089Spjd case ZFS_PROP_REFQUOTA: 1433219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1434219089Spjd "size is less than current used or " 1435219089Spjd "reserved space")); 1436219089Spjd (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1437219089Spjd break; 1438219089Spjd 1439219089Spjd case ZFS_PROP_RESERVATION: 1440219089Spjd case ZFS_PROP_REFRESERVATION: 1441219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1442219089Spjd "size is greater than available space")); 1443219089Spjd (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1444219089Spjd break; 1445219089Spjd 1446219089Spjd default: 1447219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1448219089Spjd break; 1449168404Spjd } 1450219089Spjd break; 1451219089Spjd 1452219089Spjd case EBUSY: 1453219089Spjd (void) zfs_standard_error(hdl, EBUSY, errbuf); 1454219089Spjd break; 1455219089Spjd 1456219089Spjd case EROFS: 1457219089Spjd (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 1458219089Spjd break; 1459219089Spjd 1460271764Swill case E2BIG: 1461271764Swill zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1462271764Swill "property value too long")); 1463271764Swill (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1464271764Swill break; 1465271764Swill 1466219089Spjd case ENOTSUP: 1467219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1468219089Spjd "pool and or dataset must be upgraded to set this " 1469219089Spjd "property or value")); 1470219089Spjd (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1471219089Spjd break; 1472219089Spjd 1473219089Spjd case ERANGE: 1474219089Spjd if (prop == ZFS_PROP_COMPRESSION) { 1475219089Spjd (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1476219089Spjd "property setting is not allowed on " 1477219089Spjd "bootable datasets")); 1478219089Spjd (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 1479219089Spjd } else { 1480219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1481219089Spjd } 1482219089Spjd break; 1483219089Spjd 1484219089Spjd case EINVAL: 1485219089Spjd if (prop == ZPROP_INVAL) { 1486219089Spjd (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1487219089Spjd } else { 1488219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1489219089Spjd } 1490219089Spjd break; 1491219089Spjd 1492219089Spjd case EOVERFLOW: 1493219089Spjd /* 1494219089Spjd * This platform can't address a volume this big. 1495219089Spjd */ 1496219089Spjd#ifdef _ILP32 1497219089Spjd if (prop == ZFS_PROP_VOLSIZE) { 1498219089Spjd (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1499219089Spjd break; 1500219089Spjd } 1501219089Spjd#endif 1502219089Spjd /* FALLTHROUGH */ 1503219089Spjd default: 1504219089Spjd (void) zfs_standard_error(hdl, err, errbuf); 1505168404Spjd } 1506168404Spjd} 1507168404Spjd 1508168404Spjd/* 1509168404Spjd * Given a property name and value, set the property for the given dataset. 1510168404Spjd */ 1511168404Spjdint 1512168404Spjdzfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1513168404Spjd{ 1514168404Spjd zfs_cmd_t zc = { 0 }; 1515168404Spjd int ret = -1; 1516168404Spjd prop_changelist_t *cl = NULL; 1517168404Spjd char errbuf[1024]; 1518168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 1519168404Spjd nvlist_t *nvl = NULL, *realprops; 1520168404Spjd zfs_prop_t prop; 1521241655Smm boolean_t do_prefix = B_TRUE; 1522219089Spjd int added_resv; 1523168404Spjd 1524168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 1525168404Spjd dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1526168404Spjd zhp->zfs_name); 1527168404Spjd 1528168404Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1529168404Spjd nvlist_add_string(nvl, propname, propval) != 0) { 1530168404Spjd (void) no_memory(hdl); 1531168404Spjd goto error; 1532168404Spjd } 1533168404Spjd 1534185029Spjd if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, 1535168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1536168404Spjd goto error; 1537185029Spjd 1538168404Spjd nvlist_free(nvl); 1539168404Spjd nvl = realprops; 1540168404Spjd 1541168404Spjd prop = zfs_name_to_prop(propname); 1542168404Spjd 1543168404Spjd /* We don't support those properties on FreeBSD. */ 1544168404Spjd switch (prop) { 1545197867Strasz case ZFS_PROP_DEVICES: 1546168404Spjd case ZFS_PROP_ISCSIOPTIONS: 1547197867Strasz case ZFS_PROP_XATTR: 1548197867Strasz case ZFS_PROP_VSCAN: 1549197867Strasz case ZFS_PROP_NBMAND: 1550219089Spjd case ZFS_PROP_MLSLABEL: 1551168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 1552168404Spjd "property '%s' not supported on FreeBSD", propname); 1553168404Spjd ret = zfs_error(hdl, EZFS_PERM, errbuf); 1554168404Spjd goto error; 1555168404Spjd } 1556168404Spjd 1557219089Spjd if (prop == ZFS_PROP_VOLSIZE) { 1558219089Spjd if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) 1559219089Spjd goto error; 1560219089Spjd } 1561219089Spjd 1562185029Spjd if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1563168404Spjd goto error; 1564168404Spjd 1565168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 1566168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1567168404Spjd "child dataset with inherited mountpoint is used " 1568168404Spjd "in a non-global zone")); 1569168404Spjd ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1570168404Spjd goto error; 1571168404Spjd } 1572168404Spjd 1573185029Spjd /* 1574238391Smm * We don't want to unmount & remount the dataset when changing 1575238391Smm * its canmount property to 'on' or 'noauto'. We only use 1576238391Smm * the changelist logic to unmount when setting canmount=off. 1577185029Spjd */ 1578238391Smm if (prop == ZFS_PROP_CANMOUNT) { 1579238391Smm uint64_t idx; 1580238391Smm int err = zprop_string_to_index(prop, propval, &idx, 1581238391Smm ZFS_TYPE_DATASET); 1582238391Smm if (err == 0 && idx != ZFS_CANMOUNT_OFF) 1583238391Smm do_prefix = B_FALSE; 1584238391Smm } 1585185029Spjd 1586185029Spjd if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1587168404Spjd goto error; 1588168404Spjd 1589168404Spjd /* 1590168404Spjd * Execute the corresponding ioctl() to set this property. 1591168404Spjd */ 1592168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1593168404Spjd 1594185029Spjd if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1595168404Spjd goto error; 1596168404Spjd 1597185029Spjd ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1598209962Smm 1599168404Spjd if (ret != 0) { 1600219089Spjd zfs_setprop_error(hdl, prop, errno, errbuf); 1601219089Spjd if (added_resv && errno == ENOSPC) { 1602219089Spjd /* clean up the volsize property we tried to set */ 1603219089Spjd uint64_t old_volsize = zfs_prop_get_int(zhp, 1604219089Spjd ZFS_PROP_VOLSIZE); 1605219089Spjd nvlist_free(nvl); 1606219089Spjd zcmd_free_nvlists(&zc); 1607219089Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 1608219089Spjd goto error; 1609219089Spjd if (nvlist_add_uint64(nvl, 1610219089Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1611219089Spjd old_volsize) != 0) 1612219089Spjd goto error; 1613219089Spjd if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1614219089Spjd goto error; 1615219089Spjd (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1616168404Spjd } 1617168404Spjd } else { 1618185029Spjd if (do_prefix) 1619185029Spjd ret = changelist_postfix(cl); 1620185029Spjd 1621168404Spjd /* 1622168404Spjd * Refresh the statistics so the new property value 1623168404Spjd * is reflected. 1624168404Spjd */ 1625185029Spjd if (ret == 0) 1626168404Spjd (void) get_stats(zhp); 1627168404Spjd } 1628168404Spjd 1629168404Spjderror: 1630168404Spjd nvlist_free(nvl); 1631168404Spjd zcmd_free_nvlists(&zc); 1632168404Spjd if (cl) 1633168404Spjd changelist_free(cl); 1634168404Spjd return (ret); 1635168404Spjd} 1636168404Spjd 1637168404Spjd/* 1638219089Spjd * Given a property, inherit the value from the parent dataset, or if received 1639219089Spjd * is TRUE, revert to the received value, if any. 1640168404Spjd */ 1641168404Spjdint 1642219089Spjdzfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1643168404Spjd{ 1644168404Spjd zfs_cmd_t zc = { 0 }; 1645168404Spjd int ret; 1646168404Spjd prop_changelist_t *cl; 1647168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 1648168404Spjd char errbuf[1024]; 1649168404Spjd zfs_prop_t prop; 1650168404Spjd 1651168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 1652168404Spjd "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1653168404Spjd 1654219089Spjd zc.zc_cookie = received; 1655185029Spjd if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1656168404Spjd /* 1657168404Spjd * For user properties, the amount of work we have to do is very 1658168404Spjd * small, so just do it here. 1659168404Spjd */ 1660168404Spjd if (!zfs_prop_user(propname)) { 1661168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1662168404Spjd "invalid property")); 1663168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1664168404Spjd } 1665168404Spjd 1666168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1667168404Spjd (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1668168404Spjd 1669185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1670168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 1671168404Spjd 1672168404Spjd return (0); 1673168404Spjd } 1674168404Spjd 1675168404Spjd /* 1676168404Spjd * Verify that this property is inheritable. 1677168404Spjd */ 1678168404Spjd if (zfs_prop_readonly(prop)) 1679168404Spjd return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1680168404Spjd 1681219089Spjd if (!zfs_prop_inheritable(prop) && !received) 1682168404Spjd return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1683168404Spjd 1684168404Spjd /* 1685168404Spjd * Check to see if the value applies to this type 1686168404Spjd */ 1687168404Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1688168404Spjd return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1689168404Spjd 1690168404Spjd /* 1691219089Spjd * Normalize the name, to get rid of shorthand abbreviations. 1692168404Spjd */ 1693168404Spjd propname = zfs_prop_to_name(prop); 1694168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1695168404Spjd (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1696168404Spjd 1697168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1698168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 1699168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1700168404Spjd "dataset is used in a non-global zone")); 1701168404Spjd return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1702168404Spjd } 1703168404Spjd 1704168404Spjd /* 1705168404Spjd * Determine datasets which will be affected by this change, if any. 1706168404Spjd */ 1707185029Spjd if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1708168404Spjd return (-1); 1709168404Spjd 1710168404Spjd if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 1711168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1712168404Spjd "child dataset with inherited mountpoint is used " 1713168404Spjd "in a non-global zone")); 1714168404Spjd ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1715168404Spjd goto error; 1716168404Spjd } 1717168404Spjd 1718168404Spjd if ((ret = changelist_prefix(cl)) != 0) 1719168404Spjd goto error; 1720168404Spjd 1721185029Spjd if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 1722168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 1723168404Spjd } else { 1724168404Spjd 1725168404Spjd if ((ret = changelist_postfix(cl)) != 0) 1726168404Spjd goto error; 1727168404Spjd 1728168404Spjd /* 1729168404Spjd * Refresh the statistics so the new property is reflected. 1730168404Spjd */ 1731168404Spjd (void) get_stats(zhp); 1732168404Spjd } 1733168404Spjd 1734168404Spjderror: 1735168404Spjd changelist_free(cl); 1736168404Spjd return (ret); 1737168404Spjd} 1738168404Spjd 1739168404Spjd/* 1740168404Spjd * True DSL properties are stored in an nvlist. The following two functions 1741168404Spjd * extract them appropriately. 1742168404Spjd */ 1743168404Spjdstatic uint64_t 1744168404Spjdgetprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 1745168404Spjd{ 1746168404Spjd nvlist_t *nv; 1747168404Spjd uint64_t value; 1748168404Spjd 1749168404Spjd *source = NULL; 1750168404Spjd if (nvlist_lookup_nvlist(zhp->zfs_props, 1751168404Spjd zfs_prop_to_name(prop), &nv) == 0) { 1752185029Spjd verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1753185029Spjd (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 1754168404Spjd } else { 1755205198Sdelphij verify(!zhp->zfs_props_table || 1756205198Sdelphij zhp->zfs_props_table[prop] == B_TRUE); 1757168404Spjd value = zfs_prop_default_numeric(prop); 1758168404Spjd *source = ""; 1759168404Spjd } 1760168404Spjd 1761168404Spjd return (value); 1762168404Spjd} 1763168404Spjd 1764168404Spjdstatic char * 1765168404Spjdgetprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 1766168404Spjd{ 1767168404Spjd nvlist_t *nv; 1768168404Spjd char *value; 1769168404Spjd 1770168404Spjd *source = NULL; 1771168404Spjd if (nvlist_lookup_nvlist(zhp->zfs_props, 1772168404Spjd zfs_prop_to_name(prop), &nv) == 0) { 1773185029Spjd verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1774185029Spjd (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 1775168404Spjd } else { 1776205198Sdelphij verify(!zhp->zfs_props_table || 1777205198Sdelphij zhp->zfs_props_table[prop] == B_TRUE); 1778168404Spjd if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 1779168404Spjd value = ""; 1780168404Spjd *source = ""; 1781168404Spjd } 1782168404Spjd 1783168404Spjd return (value); 1784168404Spjd} 1785168404Spjd 1786219089Spjdstatic boolean_t 1787219089Spjdzfs_is_recvd_props_mode(zfs_handle_t *zhp) 1788219089Spjd{ 1789219089Spjd return (zhp->zfs_props == zhp->zfs_recvd_props); 1790219089Spjd} 1791219089Spjd 1792219089Spjdstatic void 1793219089Spjdzfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 1794219089Spjd{ 1795219089Spjd *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 1796219089Spjd zhp->zfs_props = zhp->zfs_recvd_props; 1797219089Spjd} 1798219089Spjd 1799219089Spjdstatic void 1800219089Spjdzfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 1801219089Spjd{ 1802219089Spjd zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 1803219089Spjd *cookie = 0; 1804219089Spjd} 1805219089Spjd 1806168404Spjd/* 1807168404Spjd * Internal function for getting a numeric property. Both zfs_prop_get() and 1808168404Spjd * zfs_prop_get_int() are built using this interface. 1809168404Spjd * 1810168404Spjd * Certain properties can be overridden using 'mount -o'. In this case, scan 1811168404Spjd * the contents of the /etc/mnttab entry, searching for the appropriate options. 1812168404Spjd * If they differ from the on-disk values, report the current values and mark 1813168404Spjd * the source "temporary". 1814168404Spjd */ 1815168404Spjdstatic int 1816185029Spjdget_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 1817168404Spjd char **source, uint64_t *val) 1818168404Spjd{ 1819185029Spjd zfs_cmd_t zc = { 0 }; 1820185029Spjd nvlist_t *zplprops = NULL; 1821168404Spjd struct mnttab mnt; 1822168404Spjd char *mntopt_on = NULL; 1823168404Spjd char *mntopt_off = NULL; 1824219089Spjd boolean_t received = zfs_is_recvd_props_mode(zhp); 1825168404Spjd 1826168404Spjd *source = NULL; 1827168404Spjd 1828168404Spjd switch (prop) { 1829168404Spjd case ZFS_PROP_ATIME: 1830168404Spjd mntopt_on = MNTOPT_ATIME; 1831168404Spjd mntopt_off = MNTOPT_NOATIME; 1832168404Spjd break; 1833168404Spjd 1834168404Spjd case ZFS_PROP_DEVICES: 1835168404Spjd mntopt_on = MNTOPT_DEVICES; 1836168404Spjd mntopt_off = MNTOPT_NODEVICES; 1837168404Spjd break; 1838168404Spjd 1839168404Spjd case ZFS_PROP_EXEC: 1840168404Spjd mntopt_on = MNTOPT_EXEC; 1841168404Spjd mntopt_off = MNTOPT_NOEXEC; 1842168404Spjd break; 1843168404Spjd 1844168404Spjd case ZFS_PROP_READONLY: 1845168404Spjd mntopt_on = MNTOPT_RO; 1846168404Spjd mntopt_off = MNTOPT_RW; 1847168404Spjd break; 1848168404Spjd 1849168404Spjd case ZFS_PROP_SETUID: 1850168404Spjd mntopt_on = MNTOPT_SETUID; 1851168404Spjd mntopt_off = MNTOPT_NOSETUID; 1852168404Spjd break; 1853168404Spjd 1854168404Spjd case ZFS_PROP_XATTR: 1855168404Spjd mntopt_on = MNTOPT_XATTR; 1856168404Spjd mntopt_off = MNTOPT_NOXATTR; 1857168404Spjd break; 1858185029Spjd 1859185029Spjd case ZFS_PROP_NBMAND: 1860185029Spjd mntopt_on = MNTOPT_NBMAND; 1861185029Spjd mntopt_off = MNTOPT_NONBMAND; 1862185029Spjd break; 1863168404Spjd } 1864168404Spjd 1865168404Spjd /* 1866168404Spjd * Because looking up the mount options is potentially expensive 1867168404Spjd * (iterating over all of /etc/mnttab), we defer its calculation until 1868168404Spjd * we're looking up a property which requires its presence. 1869168404Spjd */ 1870168404Spjd if (!zhp->zfs_mntcheck && 1871168404Spjd (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 1872209962Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 1873209962Smm struct mnttab entry; 1874168404Spjd 1875209962Smm if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 1876209962Smm zhp->zfs_mntopts = zfs_strdup(hdl, 1877168404Spjd entry.mnt_mntopts); 1878168404Spjd if (zhp->zfs_mntopts == NULL) 1879168404Spjd return (-1); 1880168404Spjd } 1881168404Spjd 1882168404Spjd zhp->zfs_mntcheck = B_TRUE; 1883168404Spjd } 1884168404Spjd 1885168404Spjd if (zhp->zfs_mntopts == NULL) 1886168404Spjd mnt.mnt_mntopts = ""; 1887168404Spjd else 1888168404Spjd mnt.mnt_mntopts = zhp->zfs_mntopts; 1889168404Spjd 1890168404Spjd switch (prop) { 1891168404Spjd case ZFS_PROP_ATIME: 1892168404Spjd case ZFS_PROP_DEVICES: 1893168404Spjd case ZFS_PROP_EXEC: 1894168404Spjd case ZFS_PROP_READONLY: 1895168404Spjd case ZFS_PROP_SETUID: 1896168404Spjd case ZFS_PROP_XATTR: 1897185029Spjd case ZFS_PROP_NBMAND: 1898168404Spjd *val = getprop_uint64(zhp, prop, source); 1899168404Spjd 1900219089Spjd if (received) 1901219089Spjd break; 1902219089Spjd 1903168404Spjd if (hasmntopt(&mnt, mntopt_on) && !*val) { 1904168404Spjd *val = B_TRUE; 1905168404Spjd if (src) 1906185029Spjd *src = ZPROP_SRC_TEMPORARY; 1907168404Spjd } else if (hasmntopt(&mnt, mntopt_off) && *val) { 1908168404Spjd *val = B_FALSE; 1909168404Spjd if (src) 1910185029Spjd *src = ZPROP_SRC_TEMPORARY; 1911168404Spjd } 1912168404Spjd break; 1913168404Spjd 1914168404Spjd case ZFS_PROP_CANMOUNT: 1915219089Spjd case ZFS_PROP_VOLSIZE: 1916168404Spjd case ZFS_PROP_QUOTA: 1917185029Spjd case ZFS_PROP_REFQUOTA: 1918168404Spjd case ZFS_PROP_RESERVATION: 1919185029Spjd case ZFS_PROP_REFRESERVATION: 1920264835Sdelphij case ZFS_PROP_FILESYSTEM_LIMIT: 1921264835Sdelphij case ZFS_PROP_SNAPSHOT_LIMIT: 1922264835Sdelphij case ZFS_PROP_FILESYSTEM_COUNT: 1923264835Sdelphij case ZFS_PROP_SNAPSHOT_COUNT: 1924168404Spjd *val = getprop_uint64(zhp, prop, source); 1925219089Spjd 1926219089Spjd if (*source == NULL) { 1927219089Spjd /* not default, must be local */ 1928168404Spjd *source = zhp->zfs_name; 1929219089Spjd } 1930168404Spjd break; 1931168404Spjd 1932168404Spjd case ZFS_PROP_MOUNTED: 1933168404Spjd *val = (zhp->zfs_mntopts != NULL); 1934168404Spjd break; 1935168404Spjd 1936168404Spjd case ZFS_PROP_NUMCLONES: 1937168404Spjd *val = zhp->zfs_dmustats.dds_num_clones; 1938168404Spjd break; 1939168404Spjd 1940185029Spjd case ZFS_PROP_VERSION: 1941185029Spjd case ZFS_PROP_NORMALIZE: 1942185029Spjd case ZFS_PROP_UTF8ONLY: 1943185029Spjd case ZFS_PROP_CASE: 1944185029Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 1945185029Spjd zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 1946185029Spjd return (-1); 1947185029Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1948185029Spjd if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 1949185029Spjd zcmd_free_nvlists(&zc); 1950219089Spjd return (-1); 1951185029Spjd } 1952185029Spjd if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 1953185029Spjd nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 1954185029Spjd val) != 0) { 1955185029Spjd zcmd_free_nvlists(&zc); 1956219089Spjd return (-1); 1957185029Spjd } 1958185029Spjd if (zplprops) 1959185029Spjd nvlist_free(zplprops); 1960185029Spjd zcmd_free_nvlists(&zc); 1961185029Spjd break; 1962185029Spjd 1963253819Sdelphij case ZFS_PROP_INCONSISTENT: 1964253819Sdelphij *val = zhp->zfs_dmustats.dds_inconsistent; 1965253819Sdelphij break; 1966253819Sdelphij 1967168404Spjd default: 1968185029Spjd switch (zfs_prop_get_type(prop)) { 1969185029Spjd case PROP_TYPE_NUMBER: 1970185029Spjd case PROP_TYPE_INDEX: 1971185029Spjd *val = getprop_uint64(zhp, prop, source); 1972185029Spjd /* 1973209962Smm * If we tried to use a default value for a 1974185029Spjd * readonly property, it means that it was not 1975219089Spjd * present. 1976185029Spjd */ 1977185029Spjd if (zfs_prop_readonly(prop) && 1978219089Spjd *source != NULL && (*source)[0] == '\0') { 1979219089Spjd *source = NULL; 1980185029Spjd } 1981185029Spjd break; 1982185029Spjd 1983185029Spjd case PROP_TYPE_STRING: 1984185029Spjd default: 1985185029Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 1986185029Spjd "cannot get non-numeric property")); 1987185029Spjd return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 1988185029Spjd dgettext(TEXT_DOMAIN, "internal error"))); 1989185029Spjd } 1990168404Spjd } 1991168404Spjd 1992168404Spjd return (0); 1993168404Spjd} 1994168404Spjd 1995168404Spjd/* 1996168404Spjd * Calculate the source type, given the raw source string. 1997168404Spjd */ 1998168404Spjdstatic void 1999185029Spjdget_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 2000168404Spjd char *statbuf, size_t statlen) 2001168404Spjd{ 2002185029Spjd if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 2003168404Spjd return; 2004168404Spjd 2005168404Spjd if (source == NULL) { 2006185029Spjd *srctype = ZPROP_SRC_NONE; 2007168404Spjd } else if (source[0] == '\0') { 2008185029Spjd *srctype = ZPROP_SRC_DEFAULT; 2009219089Spjd } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 2010219089Spjd *srctype = ZPROP_SRC_RECEIVED; 2011168404Spjd } else { 2012168404Spjd if (strcmp(source, zhp->zfs_name) == 0) { 2013185029Spjd *srctype = ZPROP_SRC_LOCAL; 2014168404Spjd } else { 2015168404Spjd (void) strlcpy(statbuf, source, statlen); 2016185029Spjd *srctype = ZPROP_SRC_INHERITED; 2017168404Spjd } 2018168404Spjd } 2019168404Spjd 2020168404Spjd} 2021168404Spjd 2022219089Spjdint 2023219089Spjdzfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 2024219089Spjd size_t proplen, boolean_t literal) 2025219089Spjd{ 2026219089Spjd zfs_prop_t prop; 2027219089Spjd int err = 0; 2028219089Spjd 2029219089Spjd if (zhp->zfs_recvd_props == NULL) 2030219089Spjd if (get_recvd_props_ioctl(zhp) != 0) 2031219089Spjd return (-1); 2032219089Spjd 2033219089Spjd prop = zfs_name_to_prop(propname); 2034219089Spjd 2035219089Spjd if (prop != ZPROP_INVAL) { 2036219089Spjd uint64_t cookie; 2037219089Spjd if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 2038219089Spjd return (-1); 2039219089Spjd zfs_set_recvd_props_mode(zhp, &cookie); 2040219089Spjd err = zfs_prop_get(zhp, prop, propbuf, proplen, 2041219089Spjd NULL, NULL, 0, literal); 2042219089Spjd zfs_unset_recvd_props_mode(zhp, &cookie); 2043219089Spjd } else { 2044219089Spjd nvlist_t *propval; 2045219089Spjd char *recvdval; 2046219089Spjd if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 2047219089Spjd propname, &propval) != 0) 2048219089Spjd return (-1); 2049219089Spjd verify(nvlist_lookup_string(propval, ZPROP_VALUE, 2050219089Spjd &recvdval) == 0); 2051219089Spjd (void) strlcpy(propbuf, recvdval, proplen); 2052219089Spjd } 2053219089Spjd 2054219089Spjd return (err == 0 ? 0 : -1); 2055219089Spjd} 2056219089Spjd 2057228103Smmstatic int 2058228103Smmget_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen) 2059228103Smm{ 2060228103Smm nvlist_t *value; 2061228103Smm nvpair_t *pair; 2062228103Smm 2063228103Smm value = zfs_get_clones_nvl(zhp); 2064228103Smm if (value == NULL) 2065228103Smm return (-1); 2066228103Smm 2067228103Smm propbuf[0] = '\0'; 2068228103Smm for (pair = nvlist_next_nvpair(value, NULL); pair != NULL; 2069228103Smm pair = nvlist_next_nvpair(value, pair)) { 2070228103Smm if (propbuf[0] != '\0') 2071228103Smm (void) strlcat(propbuf, ",", proplen); 2072228103Smm (void) strlcat(propbuf, nvpair_name(pair), proplen); 2073228103Smm } 2074228103Smm 2075228103Smm return (0); 2076228103Smm} 2077228103Smm 2078228103Smmstruct get_clones_arg { 2079228103Smm uint64_t numclones; 2080228103Smm nvlist_t *value; 2081228103Smm const char *origin; 2082228103Smm char buf[ZFS_MAXNAMELEN]; 2083228103Smm}; 2084228103Smm 2085228103Smmint 2086228103Smmget_clones_cb(zfs_handle_t *zhp, void *arg) 2087228103Smm{ 2088228103Smm struct get_clones_arg *gca = arg; 2089228103Smm 2090228103Smm if (gca->numclones == 0) { 2091228103Smm zfs_close(zhp); 2092228103Smm return (0); 2093228103Smm } 2094228103Smm 2095228103Smm if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf), 2096228103Smm NULL, NULL, 0, B_TRUE) != 0) 2097228103Smm goto out; 2098228103Smm if (strcmp(gca->buf, gca->origin) == 0) { 2099248571Smm fnvlist_add_boolean(gca->value, zfs_get_name(zhp)); 2100228103Smm gca->numclones--; 2101228103Smm } 2102228103Smm 2103228103Smmout: 2104228103Smm (void) zfs_iter_children(zhp, get_clones_cb, gca); 2105228103Smm zfs_close(zhp); 2106228103Smm return (0); 2107228103Smm} 2108228103Smm 2109228103Smmnvlist_t * 2110228103Smmzfs_get_clones_nvl(zfs_handle_t *zhp) 2111228103Smm{ 2112228103Smm nvlist_t *nv, *value; 2113228103Smm 2114228103Smm if (nvlist_lookup_nvlist(zhp->zfs_props, 2115228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) { 2116228103Smm struct get_clones_arg gca; 2117228103Smm 2118228103Smm /* 2119228103Smm * if this is a snapshot, then the kernel wasn't able 2120228103Smm * to get the clones. Do it by slowly iterating. 2121228103Smm */ 2122228103Smm if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) 2123228103Smm return (NULL); 2124228103Smm if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0) 2125228103Smm return (NULL); 2126228103Smm if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) { 2127228103Smm nvlist_free(nv); 2128228103Smm return (NULL); 2129228103Smm } 2130228103Smm 2131228103Smm gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES); 2132228103Smm gca.value = value; 2133228103Smm gca.origin = zhp->zfs_name; 2134228103Smm 2135228103Smm if (gca.numclones != 0) { 2136228103Smm zfs_handle_t *root; 2137228103Smm char pool[ZFS_MAXNAMELEN]; 2138228103Smm char *cp = pool; 2139228103Smm 2140228103Smm /* get the pool name */ 2141228103Smm (void) strlcpy(pool, zhp->zfs_name, sizeof (pool)); 2142228103Smm (void) strsep(&cp, "/@"); 2143228103Smm root = zfs_open(zhp->zfs_hdl, pool, 2144228103Smm ZFS_TYPE_FILESYSTEM); 2145228103Smm 2146228103Smm (void) get_clones_cb(root, &gca); 2147228103Smm } 2148228103Smm 2149228103Smm if (gca.numclones != 0 || 2150228103Smm nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 || 2151228103Smm nvlist_add_nvlist(zhp->zfs_props, 2152228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) { 2153228103Smm nvlist_free(nv); 2154228103Smm nvlist_free(value); 2155228103Smm return (NULL); 2156228103Smm } 2157228103Smm nvlist_free(nv); 2158228103Smm nvlist_free(value); 2159228103Smm verify(0 == nvlist_lookup_nvlist(zhp->zfs_props, 2160228103Smm zfs_prop_to_name(ZFS_PROP_CLONES), &nv)); 2161228103Smm } 2162228103Smm 2163228103Smm verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0); 2164228103Smm 2165228103Smm return (value); 2166228103Smm} 2167228103Smm 2168168404Spjd/* 2169168404Spjd * Retrieve a property from the given object. If 'literal' is specified, then 2170168404Spjd * numbers are left as exact values. Otherwise, numbers are converted to a 2171168404Spjd * human-readable form. 2172168404Spjd * 2173168404Spjd * Returns 0 on success, or -1 on error. 2174168404Spjd */ 2175168404Spjdint 2176168404Spjdzfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 2177185029Spjd zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 2178168404Spjd{ 2179168404Spjd char *source = NULL; 2180168404Spjd uint64_t val; 2181168404Spjd char *str; 2182168404Spjd const char *strval; 2183219089Spjd boolean_t received = zfs_is_recvd_props_mode(zhp); 2184168404Spjd 2185168404Spjd /* 2186168404Spjd * Check to see if this property applies to our object 2187168404Spjd */ 2188168404Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 2189168404Spjd return (-1); 2190168404Spjd 2191219089Spjd if (received && zfs_prop_readonly(prop)) 2192219089Spjd return (-1); 2193219089Spjd 2194168404Spjd if (src) 2195185029Spjd *src = ZPROP_SRC_NONE; 2196168404Spjd 2197168404Spjd switch (prop) { 2198168404Spjd case ZFS_PROP_CREATION: 2199168404Spjd /* 2200168404Spjd * 'creation' is a time_t stored in the statistics. We convert 2201168404Spjd * this into a string unless 'literal' is specified. 2202168404Spjd */ 2203168404Spjd { 2204168404Spjd val = getprop_uint64(zhp, prop, &source); 2205168404Spjd time_t time = (time_t)val; 2206168404Spjd struct tm t; 2207168404Spjd 2208168404Spjd if (literal || 2209168404Spjd localtime_r(&time, &t) == NULL || 2210168404Spjd strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 2211168404Spjd &t) == 0) 2212168404Spjd (void) snprintf(propbuf, proplen, "%llu", val); 2213168404Spjd } 2214168404Spjd break; 2215168404Spjd 2216168404Spjd case ZFS_PROP_MOUNTPOINT: 2217168404Spjd /* 2218168404Spjd * Getting the precise mountpoint can be tricky. 2219168404Spjd * 2220168404Spjd * - for 'none' or 'legacy', return those values. 2221168404Spjd * - for inherited mountpoints, we want to take everything 2222168404Spjd * after our ancestor and append it to the inherited value. 2223168404Spjd * 2224168404Spjd * If the pool has an alternate root, we want to prepend that 2225168404Spjd * root to any values we return. 2226168404Spjd */ 2227185029Spjd 2228168404Spjd str = getprop_string(zhp, prop, &source); 2229168404Spjd 2230185029Spjd if (str[0] == '/') { 2231185029Spjd char buf[MAXPATHLEN]; 2232185029Spjd char *root = buf; 2233219089Spjd const char *relpath; 2234168404Spjd 2235219089Spjd /* 2236219089Spjd * If we inherit the mountpoint, even from a dataset 2237219089Spjd * with a received value, the source will be the path of 2238219089Spjd * the dataset we inherit from. If source is 2239219089Spjd * ZPROP_SOURCE_VAL_RECVD, the received value is not 2240219089Spjd * inherited. 2241219089Spjd */ 2242219089Spjd if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 2243219089Spjd relpath = ""; 2244219089Spjd } else { 2245219089Spjd relpath = zhp->zfs_name + strlen(source); 2246219089Spjd if (relpath[0] == '/') 2247219089Spjd relpath++; 2248219089Spjd } 2249185029Spjd 2250185029Spjd if ((zpool_get_prop(zhp->zpool_hdl, 2251263889Sdelphij ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL, 2252263889Sdelphij B_FALSE)) || (strcmp(root, "-") == 0)) 2253185029Spjd root[0] = '\0'; 2254185029Spjd /* 2255185029Spjd * Special case an alternate root of '/'. This will 2256185029Spjd * avoid having multiple leading slashes in the 2257185029Spjd * mountpoint path. 2258185029Spjd */ 2259185029Spjd if (strcmp(root, "/") == 0) 2260185029Spjd root++; 2261185029Spjd 2262185029Spjd /* 2263185029Spjd * If the mountpoint is '/' then skip over this 2264185029Spjd * if we are obtaining either an alternate root or 2265185029Spjd * an inherited mountpoint. 2266185029Spjd */ 2267185029Spjd if (str[1] == '\0' && (root[0] != '\0' || 2268185029Spjd relpath[0] != '\0')) 2269168404Spjd str++; 2270168404Spjd 2271168404Spjd if (relpath[0] == '\0') 2272168404Spjd (void) snprintf(propbuf, proplen, "%s%s", 2273168404Spjd root, str); 2274168404Spjd else 2275168404Spjd (void) snprintf(propbuf, proplen, "%s%s%s%s", 2276168404Spjd root, str, relpath[0] == '@' ? "" : "/", 2277168404Spjd relpath); 2278168404Spjd } else { 2279168404Spjd /* 'legacy' or 'none' */ 2280168404Spjd (void) strlcpy(propbuf, str, proplen); 2281168404Spjd } 2282168404Spjd 2283168404Spjd break; 2284168404Spjd 2285168404Spjd case ZFS_PROP_ORIGIN: 2286168404Spjd (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 2287168404Spjd proplen); 2288168404Spjd /* 2289168404Spjd * If there is no parent at all, return failure to indicate that 2290168404Spjd * it doesn't apply to this dataset. 2291168404Spjd */ 2292168404Spjd if (propbuf[0] == '\0') 2293168404Spjd return (-1); 2294168404Spjd break; 2295168404Spjd 2296228103Smm case ZFS_PROP_CLONES: 2297228103Smm if (get_clones_string(zhp, propbuf, proplen) != 0) 2298228103Smm return (-1); 2299228103Smm break; 2300228103Smm 2301168404Spjd case ZFS_PROP_QUOTA: 2302185029Spjd case ZFS_PROP_REFQUOTA: 2303168404Spjd case ZFS_PROP_RESERVATION: 2304185029Spjd case ZFS_PROP_REFRESERVATION: 2305185029Spjd 2306168404Spjd if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2307168404Spjd return (-1); 2308168404Spjd 2309168404Spjd /* 2310168404Spjd * If quota or reservation is 0, we translate this into 'none' 2311168404Spjd * (unless literal is set), and indicate that it's the default 2312168404Spjd * value. Otherwise, we print the number nicely and indicate 2313168404Spjd * that its set locally. 2314168404Spjd */ 2315168404Spjd if (val == 0) { 2316168404Spjd if (literal) 2317168404Spjd (void) strlcpy(propbuf, "0", proplen); 2318168404Spjd else 2319168404Spjd (void) strlcpy(propbuf, "none", proplen); 2320168404Spjd } else { 2321168404Spjd if (literal) 2322168404Spjd (void) snprintf(propbuf, proplen, "%llu", 2323168404Spjd (u_longlong_t)val); 2324168404Spjd else 2325168404Spjd zfs_nicenum(val, propbuf, proplen); 2326168404Spjd } 2327168404Spjd break; 2328168404Spjd 2329264835Sdelphij case ZFS_PROP_FILESYSTEM_LIMIT: 2330264835Sdelphij case ZFS_PROP_SNAPSHOT_LIMIT: 2331264835Sdelphij case ZFS_PROP_FILESYSTEM_COUNT: 2332264835Sdelphij case ZFS_PROP_SNAPSHOT_COUNT: 2333264835Sdelphij 2334264835Sdelphij if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2335264835Sdelphij return (-1); 2336264835Sdelphij 2337264835Sdelphij /* 2338264835Sdelphij * If limit is UINT64_MAX, we translate this into 'none' (unless 2339264835Sdelphij * literal is set), and indicate that it's the default value. 2340264835Sdelphij * Otherwise, we print the number nicely and indicate that it's 2341264835Sdelphij * set locally. 2342264835Sdelphij */ 2343264835Sdelphij if (literal) { 2344264835Sdelphij (void) snprintf(propbuf, proplen, "%llu", 2345264835Sdelphij (u_longlong_t)val); 2346264835Sdelphij } else if (val == UINT64_MAX) { 2347264835Sdelphij (void) strlcpy(propbuf, "none", proplen); 2348264835Sdelphij } else { 2349264835Sdelphij zfs_nicenum(val, propbuf, proplen); 2350264835Sdelphij } 2351264835Sdelphij break; 2352264835Sdelphij 2353223623Smm case ZFS_PROP_REFRATIO: 2354168404Spjd case ZFS_PROP_COMPRESSRATIO: 2355168404Spjd if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2356168404Spjd return (-1); 2357219089Spjd (void) snprintf(propbuf, proplen, "%llu.%02llux", 2358219089Spjd (u_longlong_t)(val / 100), 2359219089Spjd (u_longlong_t)(val % 100)); 2360168404Spjd break; 2361168404Spjd 2362168404Spjd case ZFS_PROP_TYPE: 2363168404Spjd switch (zhp->zfs_type) { 2364168404Spjd case ZFS_TYPE_FILESYSTEM: 2365168404Spjd str = "filesystem"; 2366168404Spjd break; 2367168404Spjd case ZFS_TYPE_VOLUME: 2368168404Spjd str = "volume"; 2369168404Spjd break; 2370168404Spjd case ZFS_TYPE_SNAPSHOT: 2371168404Spjd str = "snapshot"; 2372168404Spjd break; 2373260183Sdelphij case ZFS_TYPE_BOOKMARK: 2374260183Sdelphij str = "bookmark"; 2375260183Sdelphij break; 2376168404Spjd default: 2377168404Spjd abort(); 2378168404Spjd } 2379168404Spjd (void) snprintf(propbuf, proplen, "%s", str); 2380168404Spjd break; 2381168404Spjd 2382168404Spjd case ZFS_PROP_MOUNTED: 2383168404Spjd /* 2384168404Spjd * The 'mounted' property is a pseudo-property that described 2385168404Spjd * whether the filesystem is currently mounted. Even though 2386168404Spjd * it's a boolean value, the typical values of "on" and "off" 2387168404Spjd * don't make sense, so we translate to "yes" and "no". 2388168404Spjd */ 2389168404Spjd if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 2390168404Spjd src, &source, &val) != 0) 2391168404Spjd return (-1); 2392168404Spjd if (val) 2393168404Spjd (void) strlcpy(propbuf, "yes", proplen); 2394168404Spjd else 2395168404Spjd (void) strlcpy(propbuf, "no", proplen); 2396168404Spjd break; 2397168404Spjd 2398168404Spjd case ZFS_PROP_NAME: 2399168404Spjd /* 2400168404Spjd * The 'name' property is a pseudo-property derived from the 2401168404Spjd * dataset name. It is presented as a real property to simplify 2402168404Spjd * consumers. 2403168404Spjd */ 2404168404Spjd (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2405168404Spjd break; 2406168404Spjd 2407219089Spjd case ZFS_PROP_MLSLABEL: 2408219089Spjd { 2409219089Spjd#ifdef sun 2410219089Spjd m_label_t *new_sl = NULL; 2411219089Spjd char *ascii = NULL; /* human readable label */ 2412219089Spjd 2413219089Spjd (void) strlcpy(propbuf, 2414219089Spjd getprop_string(zhp, prop, &source), proplen); 2415219089Spjd 2416219089Spjd if (literal || (strcasecmp(propbuf, 2417219089Spjd ZFS_MLSLABEL_DEFAULT) == 0)) 2418219089Spjd break; 2419219089Spjd 2420219089Spjd /* 2421219089Spjd * Try to translate the internal hex string to 2422219089Spjd * human-readable output. If there are any 2423219089Spjd * problems just use the hex string. 2424219089Spjd */ 2425219089Spjd 2426219089Spjd if (str_to_label(propbuf, &new_sl, MAC_LABEL, 2427219089Spjd L_NO_CORRECTION, NULL) == -1) { 2428219089Spjd m_label_free(new_sl); 2429219089Spjd break; 2430219089Spjd } 2431219089Spjd 2432219089Spjd if (label_to_str(new_sl, &ascii, M_LABEL, 2433219089Spjd DEF_NAMES) != 0) { 2434219089Spjd if (ascii) 2435219089Spjd free(ascii); 2436219089Spjd m_label_free(new_sl); 2437219089Spjd break; 2438219089Spjd } 2439219089Spjd m_label_free(new_sl); 2440219089Spjd 2441219089Spjd (void) strlcpy(propbuf, ascii, proplen); 2442219089Spjd free(ascii); 2443219089Spjd#else /* !sun */ 2444219089Spjd propbuf[0] = '\0'; 2445219089Spjd#endif /* !sun */ 2446219089Spjd } 2447219089Spjd break; 2448219089Spjd 2449236705Smm case ZFS_PROP_GUID: 2450236705Smm /* 2451236705Smm * GUIDs are stored as numbers, but they are identifiers. 2452236705Smm * We don't want them to be pretty printed, because pretty 2453236705Smm * printing mangles the ID into a truncated and useless value. 2454236705Smm */ 2455236705Smm if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2456236705Smm return (-1); 2457236705Smm (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); 2458236705Smm break; 2459236705Smm 2460168404Spjd default: 2461185029Spjd switch (zfs_prop_get_type(prop)) { 2462185029Spjd case PROP_TYPE_NUMBER: 2463185029Spjd if (get_numeric_property(zhp, prop, src, 2464185029Spjd &source, &val) != 0) 2465185029Spjd return (-1); 2466185029Spjd if (literal) 2467185029Spjd (void) snprintf(propbuf, proplen, "%llu", 2468185029Spjd (u_longlong_t)val); 2469185029Spjd else 2470185029Spjd zfs_nicenum(val, propbuf, proplen); 2471185029Spjd break; 2472185029Spjd 2473185029Spjd case PROP_TYPE_STRING: 2474185029Spjd (void) strlcpy(propbuf, 2475185029Spjd getprop_string(zhp, prop, &source), proplen); 2476185029Spjd break; 2477185029Spjd 2478185029Spjd case PROP_TYPE_INDEX: 2479185029Spjd if (get_numeric_property(zhp, prop, src, 2480185029Spjd &source, &val) != 0) 2481185029Spjd return (-1); 2482185029Spjd if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2483185029Spjd return (-1); 2484185029Spjd (void) strlcpy(propbuf, strval, proplen); 2485185029Spjd break; 2486185029Spjd 2487185029Spjd default: 2488185029Spjd abort(); 2489185029Spjd } 2490168404Spjd } 2491168404Spjd 2492168404Spjd get_source(zhp, src, source, statbuf, statlen); 2493168404Spjd 2494168404Spjd return (0); 2495168404Spjd} 2496168404Spjd 2497168404Spjd/* 2498168404Spjd * Utility function to get the given numeric property. Does no validation that 2499168404Spjd * the given property is the appropriate type; should only be used with 2500168404Spjd * hard-coded property types. 2501168404Spjd */ 2502168404Spjduint64_t 2503168404Spjdzfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2504168404Spjd{ 2505168404Spjd char *source; 2506168404Spjd uint64_t val; 2507168404Spjd 2508185029Spjd (void) get_numeric_property(zhp, prop, NULL, &source, &val); 2509168404Spjd 2510168404Spjd return (val); 2511168404Spjd} 2512168404Spjd 2513185029Spjdint 2514185029Spjdzfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 2515185029Spjd{ 2516185029Spjd char buf[64]; 2517185029Spjd 2518209962Smm (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 2519185029Spjd return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 2520185029Spjd} 2521185029Spjd 2522168404Spjd/* 2523168404Spjd * Similar to zfs_prop_get(), but returns the value as an integer. 2524168404Spjd */ 2525168404Spjdint 2526168404Spjdzfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2527185029Spjd zprop_source_t *src, char *statbuf, size_t statlen) 2528168404Spjd{ 2529168404Spjd char *source; 2530168404Spjd 2531168404Spjd /* 2532168404Spjd * Check to see if this property applies to our object 2533168404Spjd */ 2534185029Spjd if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2535168404Spjd return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 2536168404Spjd dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 2537168404Spjd zfs_prop_to_name(prop))); 2538185029Spjd } 2539168404Spjd 2540168404Spjd if (src) 2541185029Spjd *src = ZPROP_SRC_NONE; 2542168404Spjd 2543168404Spjd if (get_numeric_property(zhp, prop, src, &source, value) != 0) 2544168404Spjd return (-1); 2545168404Spjd 2546168404Spjd get_source(zhp, src, source, statbuf, statlen); 2547168404Spjd 2548168404Spjd return (0); 2549168404Spjd} 2550168404Spjd 2551209962Smmstatic int 2552209962Smmidmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 2553209962Smm char **domainp, idmap_rid_t *ridp) 2554209962Smm{ 2555209962Smm#ifdef sun 2556209962Smm idmap_get_handle_t *get_hdl = NULL; 2557209962Smm idmap_stat status; 2558209962Smm int err = EINVAL; 2559209962Smm 2560219089Spjd if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS) 2561209962Smm goto out; 2562209962Smm 2563209962Smm if (isuser) { 2564209962Smm err = idmap_get_sidbyuid(get_hdl, id, 2565209962Smm IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2566209962Smm } else { 2567209962Smm err = idmap_get_sidbygid(get_hdl, id, 2568209962Smm IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 2569209962Smm } 2570209962Smm if (err == IDMAP_SUCCESS && 2571209962Smm idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 2572209962Smm status == IDMAP_SUCCESS) 2573209962Smm err = 0; 2574209962Smm else 2575209962Smm err = EINVAL; 2576209962Smmout: 2577209962Smm if (get_hdl) 2578209962Smm idmap_get_destroy(get_hdl); 2579209962Smm return (err); 2580209962Smm#else /* !sun */ 2581209962Smm assert(!"invalid code path"); 2582264852Ssmh return (EINVAL); // silence compiler warning 2583209962Smm#endif /* !sun */ 2584209962Smm} 2585209962Smm 2586168404Spjd/* 2587209962Smm * convert the propname into parameters needed by kernel 2588209962Smm * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 2589209962Smm * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 2590209962Smm */ 2591209962Smmstatic int 2592209962Smmuserquota_propname_decode(const char *propname, boolean_t zoned, 2593209962Smm zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 2594209962Smm{ 2595209962Smm zfs_userquota_prop_t type; 2596209962Smm char *cp, *end; 2597209962Smm char *numericsid = NULL; 2598209962Smm boolean_t isuser; 2599209962Smm 2600209962Smm domain[0] = '\0'; 2601209962Smm 2602209962Smm /* Figure out the property type ({user|group}{quota|space}) */ 2603209962Smm for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 2604209962Smm if (strncmp(propname, zfs_userquota_prop_prefixes[type], 2605209962Smm strlen(zfs_userquota_prop_prefixes[type])) == 0) 2606209962Smm break; 2607209962Smm } 2608209962Smm if (type == ZFS_NUM_USERQUOTA_PROPS) 2609209962Smm return (EINVAL); 2610209962Smm *typep = type; 2611209962Smm 2612209962Smm isuser = (type == ZFS_PROP_USERQUOTA || 2613209962Smm type == ZFS_PROP_USERUSED); 2614209962Smm 2615209962Smm cp = strchr(propname, '@') + 1; 2616209962Smm 2617209962Smm if (strchr(cp, '@')) { 2618209962Smm#ifdef sun 2619209962Smm /* 2620209962Smm * It's a SID name (eg "user@domain") that needs to be 2621209962Smm * turned into S-1-domainID-RID. 2622209962Smm */ 2623209962Smm directory_error_t e; 2624209962Smm if (zoned && getzoneid() == GLOBAL_ZONEID) 2625209962Smm return (ENOENT); 2626209962Smm if (isuser) { 2627209962Smm e = directory_sid_from_user_name(NULL, 2628209962Smm cp, &numericsid); 2629209962Smm } else { 2630209962Smm e = directory_sid_from_group_name(NULL, 2631209962Smm cp, &numericsid); 2632209962Smm } 2633209962Smm if (e != NULL) { 2634209962Smm directory_error_free(e); 2635209962Smm return (ENOENT); 2636209962Smm } 2637209962Smm if (numericsid == NULL) 2638209962Smm return (ENOENT); 2639209962Smm cp = numericsid; 2640209962Smm /* will be further decoded below */ 2641209962Smm#else /* !sun */ 2642219089Spjd return (ENOENT); 2643209962Smm#endif /* !sun */ 2644209962Smm } 2645209962Smm 2646209962Smm if (strncmp(cp, "S-1-", 4) == 0) { 2647209962Smm /* It's a numeric SID (eg "S-1-234-567-89") */ 2648209962Smm (void) strlcpy(domain, cp, domainlen); 2649209962Smm cp = strrchr(domain, '-'); 2650209962Smm *cp = '\0'; 2651209962Smm cp++; 2652209962Smm 2653209962Smm errno = 0; 2654209962Smm *ridp = strtoull(cp, &end, 10); 2655209962Smm if (numericsid) { 2656209962Smm free(numericsid); 2657209962Smm numericsid = NULL; 2658209962Smm } 2659209962Smm if (errno != 0 || *end != '\0') 2660209962Smm return (EINVAL); 2661209962Smm } else if (!isdigit(*cp)) { 2662209962Smm /* 2663209962Smm * It's a user/group name (eg "user") that needs to be 2664209962Smm * turned into a uid/gid 2665209962Smm */ 2666209962Smm if (zoned && getzoneid() == GLOBAL_ZONEID) 2667209962Smm return (ENOENT); 2668209962Smm if (isuser) { 2669209962Smm struct passwd *pw; 2670209962Smm pw = getpwnam(cp); 2671209962Smm if (pw == NULL) 2672209962Smm return (ENOENT); 2673209962Smm *ridp = pw->pw_uid; 2674209962Smm } else { 2675209962Smm struct group *gr; 2676209962Smm gr = getgrnam(cp); 2677209962Smm if (gr == NULL) 2678209962Smm return (ENOENT); 2679209962Smm *ridp = gr->gr_gid; 2680209962Smm } 2681209962Smm } else { 2682209962Smm /* It's a user/group ID (eg "12345"). */ 2683209962Smm uid_t id = strtoul(cp, &end, 10); 2684209962Smm idmap_rid_t rid; 2685209962Smm char *mapdomain; 2686209962Smm 2687209962Smm if (*end != '\0') 2688209962Smm return (EINVAL); 2689209962Smm if (id > MAXUID) { 2690209962Smm /* It's an ephemeral ID. */ 2691209962Smm if (idmap_id_to_numeric_domain_rid(id, isuser, 2692209962Smm &mapdomain, &rid) != 0) 2693209962Smm return (ENOENT); 2694209962Smm (void) strlcpy(domain, mapdomain, domainlen); 2695209962Smm *ridp = rid; 2696209962Smm } else { 2697209962Smm *ridp = id; 2698209962Smm } 2699209962Smm } 2700209962Smm 2701209962Smm ASSERT3P(numericsid, ==, NULL); 2702209962Smm return (0); 2703209962Smm} 2704209962Smm 2705209962Smmstatic int 2706209962Smmzfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 2707209962Smm uint64_t *propvalue, zfs_userquota_prop_t *typep) 2708209962Smm{ 2709209962Smm int err; 2710209962Smm zfs_cmd_t zc = { 0 }; 2711209962Smm 2712228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2713209962Smm 2714209962Smm err = userquota_propname_decode(propname, 2715209962Smm zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 2716209962Smm typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 2717209962Smm zc.zc_objset_type = *typep; 2718209962Smm if (err) 2719209962Smm return (err); 2720209962Smm 2721209962Smm err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 2722209962Smm if (err) 2723209962Smm return (err); 2724209962Smm 2725209962Smm *propvalue = zc.zc_cookie; 2726209962Smm return (0); 2727209962Smm} 2728209962Smm 2729209962Smmint 2730209962Smmzfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 2731209962Smm uint64_t *propvalue) 2732209962Smm{ 2733209962Smm zfs_userquota_prop_t type; 2734209962Smm 2735209962Smm return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 2736209962Smm &type)); 2737209962Smm} 2738209962Smm 2739209962Smmint 2740209962Smmzfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 2741209962Smm char *propbuf, int proplen, boolean_t literal) 2742209962Smm{ 2743209962Smm int err; 2744209962Smm uint64_t propvalue; 2745209962Smm zfs_userquota_prop_t type; 2746209962Smm 2747209962Smm err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 2748209962Smm &type); 2749209962Smm 2750209962Smm if (err) 2751209962Smm return (err); 2752209962Smm 2753209962Smm if (literal) { 2754209962Smm (void) snprintf(propbuf, proplen, "%llu", propvalue); 2755209962Smm } else if (propvalue == 0 && 2756209962Smm (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 2757209962Smm (void) strlcpy(propbuf, "none", proplen); 2758209962Smm } else { 2759209962Smm zfs_nicenum(propvalue, propbuf, proplen); 2760209962Smm } 2761209962Smm return (0); 2762209962Smm} 2763209962Smm 2764228103Smmint 2765228103Smmzfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname, 2766228103Smm uint64_t *propvalue) 2767168404Spjd{ 2768228103Smm int err; 2769228103Smm zfs_cmd_t zc = { 0 }; 2770228103Smm const char *snapname; 2771168404Spjd 2772228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2773168404Spjd 2774228103Smm snapname = strchr(propname, '@') + 1; 2775228103Smm if (strchr(snapname, '@')) { 2776228103Smm (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 2777228103Smm } else { 2778228103Smm /* snapname is the short name, append it to zhp's fsname */ 2779228103Smm char *cp; 2780209962Smm 2781228103Smm (void) strlcpy(zc.zc_value, zhp->zfs_name, 2782228103Smm sizeof (zc.zc_value)); 2783228103Smm cp = strchr(zc.zc_value, '@'); 2784228103Smm if (cp != NULL) 2785228103Smm *cp = '\0'; 2786228103Smm (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value)); 2787228103Smm (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value)); 2788228103Smm } 2789209962Smm 2790228103Smm err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc); 2791228103Smm if (err) 2792228103Smm return (err); 2793228103Smm 2794228103Smm *propvalue = zc.zc_cookie; 2795228103Smm return (0); 2796209962Smm} 2797209962Smm 2798168404Spjdint 2799228103Smmzfs_prop_get_written(zfs_handle_t *zhp, const char *propname, 2800228103Smm char *propbuf, int proplen, boolean_t literal) 2801168404Spjd{ 2802228103Smm int err; 2803228103Smm uint64_t propvalue; 2804168404Spjd 2805228103Smm err = zfs_prop_get_written_int(zhp, propname, &propvalue); 2806185029Spjd 2807228103Smm if (err) 2808228103Smm return (err); 2809209962Smm 2810228103Smm if (literal) { 2811228103Smm (void) snprintf(propbuf, proplen, "%llu", propvalue); 2812228103Smm } else { 2813228103Smm zfs_nicenum(propvalue, propbuf, proplen); 2814168404Spjd } 2815228103Smm return (0); 2816168404Spjd} 2817168404Spjd 2818168404Spjd/* 2819228103Smm * Returns the name of the given zfs handle. 2820168404Spjd */ 2821228103Smmconst char * 2822228103Smmzfs_get_name(const zfs_handle_t *zhp) 2823168404Spjd{ 2824228103Smm return (zhp->zfs_name); 2825228103Smm} 2826168404Spjd 2827228103Smm/* 2828228103Smm * Returns the type of the given zfs handle. 2829228103Smm */ 2830228103Smmzfs_type_t 2831228103Smmzfs_get_type(const zfs_handle_t *zhp) 2832228103Smm{ 2833228103Smm return (zhp->zfs_type); 2834168404Spjd} 2835168404Spjd 2836168404Spjd/* 2837219089Spjd * Is one dataset name a child dataset of another? 2838219089Spjd * 2839219089Spjd * Needs to handle these cases: 2840219089Spjd * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 2841219089Spjd * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 2842219089Spjd * Descendant? No. No. No. Yes. 2843219089Spjd */ 2844219089Spjdstatic boolean_t 2845219089Spjdis_descendant(const char *ds1, const char *ds2) 2846219089Spjd{ 2847219089Spjd size_t d1len = strlen(ds1); 2848219089Spjd 2849219089Spjd /* ds2 can't be a descendant if it's smaller */ 2850219089Spjd if (strlen(ds2) < d1len) 2851219089Spjd return (B_FALSE); 2852219089Spjd 2853219089Spjd /* otherwise, compare strings and verify that there's a '/' char */ 2854219089Spjd return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 2855219089Spjd} 2856219089Spjd 2857219089Spjd/* 2858168404Spjd * Given a complete name, return just the portion that refers to the parent. 2859228103Smm * Will return -1 if there is no parent (path is just the name of the 2860228103Smm * pool). 2861168404Spjd */ 2862168404Spjdstatic int 2863168404Spjdparent_name(const char *path, char *buf, size_t buflen) 2864168404Spjd{ 2865228103Smm char *slashp; 2866168404Spjd 2867228103Smm (void) strlcpy(buf, path, buflen); 2868228103Smm 2869228103Smm if ((slashp = strrchr(buf, '/')) == NULL) 2870168404Spjd return (-1); 2871228103Smm *slashp = '\0'; 2872168404Spjd 2873168404Spjd return (0); 2874168404Spjd} 2875168404Spjd 2876168404Spjd/* 2877185029Spjd * If accept_ancestor is false, then check to make sure that the given path has 2878185029Spjd * a parent, and that it exists. If accept_ancestor is true, then find the 2879185029Spjd * closest existing ancestor for the given path. In prefixlen return the 2880185029Spjd * length of already existing prefix of the given path. We also fetch the 2881185029Spjd * 'zoned' property, which is used to validate property settings when creating 2882185029Spjd * new datasets. 2883168404Spjd */ 2884168404Spjdstatic int 2885185029Spjdcheck_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 2886185029Spjd boolean_t accept_ancestor, int *prefixlen) 2887168404Spjd{ 2888168404Spjd zfs_cmd_t zc = { 0 }; 2889168404Spjd char parent[ZFS_MAXNAMELEN]; 2890168404Spjd char *slash; 2891168404Spjd zfs_handle_t *zhp; 2892168404Spjd char errbuf[1024]; 2893219089Spjd uint64_t is_zoned; 2894168404Spjd 2895209962Smm (void) snprintf(errbuf, sizeof (errbuf), 2896209962Smm dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 2897168404Spjd 2898168404Spjd /* get parent, and check to see if this is just a pool */ 2899168404Spjd if (parent_name(path, parent, sizeof (parent)) != 0) { 2900168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2901168404Spjd "missing dataset name")); 2902168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2903168404Spjd } 2904168404Spjd 2905168404Spjd /* check to see if the pool exists */ 2906168404Spjd if ((slash = strchr(parent, '/')) == NULL) 2907168404Spjd slash = parent + strlen(parent); 2908168404Spjd (void) strncpy(zc.zc_name, parent, slash - parent); 2909168404Spjd zc.zc_name[slash - parent] = '\0'; 2910168404Spjd if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2911168404Spjd errno == ENOENT) { 2912168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2913168404Spjd "no such pool '%s'"), zc.zc_name); 2914168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2915168404Spjd } 2916168404Spjd 2917168404Spjd /* check to see if the parent dataset exists */ 2918185029Spjd while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 2919185029Spjd if (errno == ENOENT && accept_ancestor) { 2920185029Spjd /* 2921185029Spjd * Go deeper to find an ancestor, give up on top level. 2922185029Spjd */ 2923185029Spjd if (parent_name(parent, parent, sizeof (parent)) != 0) { 2924185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2925185029Spjd "no such pool '%s'"), zc.zc_name); 2926185029Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2927185029Spjd } 2928185029Spjd } else if (errno == ENOENT) { 2929168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2930168404Spjd "parent does not exist")); 2931168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2932185029Spjd } else 2933168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 2934168404Spjd } 2935168404Spjd 2936219089Spjd is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2937219089Spjd if (zoned != NULL) 2938219089Spjd *zoned = is_zoned; 2939219089Spjd 2940168404Spjd /* we are in a non-global zone, but parent is in the global zone */ 2941219089Spjd if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 2942168404Spjd (void) zfs_standard_error(hdl, EPERM, errbuf); 2943168404Spjd zfs_close(zhp); 2944168404Spjd return (-1); 2945168404Spjd } 2946168404Spjd 2947168404Spjd /* make sure parent is a filesystem */ 2948168404Spjd if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 2949168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2950168404Spjd "parent is not a filesystem")); 2951168404Spjd (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 2952168404Spjd zfs_close(zhp); 2953168404Spjd return (-1); 2954168404Spjd } 2955168404Spjd 2956168404Spjd zfs_close(zhp); 2957185029Spjd if (prefixlen != NULL) 2958185029Spjd *prefixlen = strlen(parent); 2959168404Spjd return (0); 2960168404Spjd} 2961168404Spjd 2962168404Spjd/* 2963185029Spjd * Finds whether the dataset of the given type(s) exists. 2964185029Spjd */ 2965185029Spjdboolean_t 2966185029Spjdzfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 2967185029Spjd{ 2968185029Spjd zfs_handle_t *zhp; 2969185029Spjd 2970185029Spjd if (!zfs_validate_name(hdl, path, types, B_FALSE)) 2971185029Spjd return (B_FALSE); 2972185029Spjd 2973185029Spjd /* 2974185029Spjd * Try to get stats for the dataset, which will tell us if it exists. 2975185029Spjd */ 2976185029Spjd if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 2977185029Spjd int ds_type = zhp->zfs_type; 2978185029Spjd 2979185029Spjd zfs_close(zhp); 2980185029Spjd if (types & ds_type) 2981185029Spjd return (B_TRUE); 2982185029Spjd } 2983185029Spjd return (B_FALSE); 2984185029Spjd} 2985185029Spjd 2986185029Spjd/* 2987185029Spjd * Given a path to 'target', create all the ancestors between 2988185029Spjd * the prefixlen portion of the path, and the target itself. 2989185029Spjd * Fail if the initial prefixlen-ancestor does not already exist. 2990185029Spjd */ 2991185029Spjdint 2992185029Spjdcreate_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 2993185029Spjd{ 2994185029Spjd zfs_handle_t *h; 2995185029Spjd char *cp; 2996185029Spjd const char *opname; 2997185029Spjd 2998185029Spjd /* make sure prefix exists */ 2999185029Spjd cp = target + prefixlen; 3000185029Spjd if (*cp != '/') { 3001185029Spjd assert(strchr(cp, '/') == NULL); 3002185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 3003185029Spjd } else { 3004185029Spjd *cp = '\0'; 3005185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 3006185029Spjd *cp = '/'; 3007185029Spjd } 3008185029Spjd if (h == NULL) 3009185029Spjd return (-1); 3010185029Spjd zfs_close(h); 3011185029Spjd 3012185029Spjd /* 3013185029Spjd * Attempt to create, mount, and share any ancestor filesystems, 3014185029Spjd * up to the prefixlen-long one. 3015185029Spjd */ 3016185029Spjd for (cp = target + prefixlen + 1; 3017185029Spjd cp = strchr(cp, '/'); *cp = '/', cp++) { 3018185029Spjd 3019185029Spjd *cp = '\0'; 3020185029Spjd 3021185029Spjd h = make_dataset_handle(hdl, target); 3022185029Spjd if (h) { 3023185029Spjd /* it already exists, nothing to do here */ 3024185029Spjd zfs_close(h); 3025185029Spjd continue; 3026185029Spjd } 3027185029Spjd 3028185029Spjd if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 3029185029Spjd NULL) != 0) { 3030185029Spjd opname = dgettext(TEXT_DOMAIN, "create"); 3031185029Spjd goto ancestorerr; 3032185029Spjd } 3033185029Spjd 3034185029Spjd h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 3035185029Spjd if (h == NULL) { 3036185029Spjd opname = dgettext(TEXT_DOMAIN, "open"); 3037185029Spjd goto ancestorerr; 3038185029Spjd } 3039185029Spjd 3040185029Spjd if (zfs_mount(h, NULL, 0) != 0) { 3041185029Spjd opname = dgettext(TEXT_DOMAIN, "mount"); 3042185029Spjd goto ancestorerr; 3043185029Spjd } 3044185029Spjd 3045185029Spjd if (zfs_share(h) != 0) { 3046185029Spjd opname = dgettext(TEXT_DOMAIN, "share"); 3047185029Spjd goto ancestorerr; 3048185029Spjd } 3049185029Spjd 3050185029Spjd zfs_close(h); 3051185029Spjd } 3052185029Spjd 3053185029Spjd return (0); 3054185029Spjd 3055185029Spjdancestorerr: 3056185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3057185029Spjd "failed to %s ancestor '%s'"), opname, target); 3058185029Spjd return (-1); 3059185029Spjd} 3060185029Spjd 3061185029Spjd/* 3062185029Spjd * Creates non-existing ancestors of the given path. 3063185029Spjd */ 3064185029Spjdint 3065185029Spjdzfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 3066185029Spjd{ 3067185029Spjd int prefix; 3068185029Spjd char *path_copy; 3069185029Spjd int rc; 3070185029Spjd 3071219089Spjd if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 3072185029Spjd return (-1); 3073185029Spjd 3074185029Spjd if ((path_copy = strdup(path)) != NULL) { 3075185029Spjd rc = create_parents(hdl, path_copy, prefix); 3076185029Spjd free(path_copy); 3077185029Spjd } 3078185029Spjd if (path_copy == NULL || rc != 0) 3079185029Spjd return (-1); 3080185029Spjd 3081185029Spjd return (0); 3082185029Spjd} 3083185029Spjd 3084185029Spjd/* 3085168404Spjd * Create a new filesystem or volume. 3086168404Spjd */ 3087168404Spjdint 3088168404Spjdzfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 3089168404Spjd nvlist_t *props) 3090168404Spjd{ 3091168404Spjd int ret; 3092168404Spjd uint64_t size = 0; 3093168404Spjd uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 3094168404Spjd char errbuf[1024]; 3095168404Spjd uint64_t zoned; 3096248571Smm dmu_objset_type_t ost; 3097168404Spjd 3098168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3099168404Spjd "cannot create '%s'"), path); 3100168404Spjd 3101168404Spjd /* validate the path, taking care to note the extended error message */ 3102185029Spjd if (!zfs_validate_name(hdl, path, type, B_TRUE)) 3103168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3104168404Spjd 3105168404Spjd /* validate parents exist */ 3106185029Spjd if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 3107168404Spjd return (-1); 3108168404Spjd 3109168404Spjd /* 3110168404Spjd * The failure modes when creating a dataset of a different type over 3111168404Spjd * one that already exists is a little strange. In particular, if you 3112168404Spjd * try to create a dataset on top of an existing dataset, the ioctl() 3113168404Spjd * will return ENOENT, not EEXIST. To prevent this from happening, we 3114168404Spjd * first try to see if the dataset exists. 3115168404Spjd */ 3116248571Smm if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) { 3117168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3118168404Spjd "dataset already exists")); 3119168404Spjd return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3120168404Spjd } 3121168404Spjd 3122168404Spjd if (type == ZFS_TYPE_VOLUME) 3123248571Smm ost = DMU_OST_ZVOL; 3124168404Spjd else 3125248571Smm ost = DMU_OST_ZFS; 3126168404Spjd 3127185029Spjd if (props && (props = zfs_valid_proplist(hdl, type, props, 3128168404Spjd zoned, NULL, errbuf)) == 0) 3129168404Spjd return (-1); 3130168404Spjd 3131168404Spjd if (type == ZFS_TYPE_VOLUME) { 3132168404Spjd /* 3133168404Spjd * If we are creating a volume, the size and block size must 3134168404Spjd * satisfy a few restraints. First, the blocksize must be a 3135168404Spjd * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 3136168404Spjd * volsize must be a multiple of the block size, and cannot be 3137168404Spjd * zero. 3138168404Spjd */ 3139168404Spjd if (props == NULL || nvlist_lookup_uint64(props, 3140168404Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 3141168404Spjd nvlist_free(props); 3142168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3143168404Spjd "missing volume size")); 3144168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3145168404Spjd } 3146168404Spjd 3147168404Spjd if ((ret = nvlist_lookup_uint64(props, 3148168404Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 3149168404Spjd &blocksize)) != 0) { 3150168404Spjd if (ret == ENOENT) { 3151168404Spjd blocksize = zfs_prop_default_numeric( 3152168404Spjd ZFS_PROP_VOLBLOCKSIZE); 3153168404Spjd } else { 3154168404Spjd nvlist_free(props); 3155168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3156168404Spjd "missing volume block size")); 3157168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3158168404Spjd } 3159168404Spjd } 3160168404Spjd 3161168404Spjd if (size == 0) { 3162168404Spjd nvlist_free(props); 3163168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3164168404Spjd "volume size cannot be zero")); 3165168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3166168404Spjd } 3167168404Spjd 3168168404Spjd if (size % blocksize != 0) { 3169168404Spjd nvlist_free(props); 3170168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3171168404Spjd "volume size must be a multiple of volume block " 3172168404Spjd "size")); 3173168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3174168404Spjd } 3175168404Spjd } 3176168404Spjd 3177248571Smm /* create the dataset */ 3178248571Smm ret = lzc_create(path, ost, props); 3179168404Spjd nvlist_free(props); 3180168404Spjd 3181168404Spjd /* check for failure */ 3182168404Spjd if (ret != 0) { 3183168404Spjd char parent[ZFS_MAXNAMELEN]; 3184168404Spjd (void) parent_name(path, parent, sizeof (parent)); 3185168404Spjd 3186168404Spjd switch (errno) { 3187168404Spjd case ENOENT: 3188168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3189168404Spjd "no such parent '%s'"), parent); 3190168404Spjd return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3191168404Spjd 3192168404Spjd case EINVAL: 3193168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3194168404Spjd "parent '%s' is not a filesystem"), parent); 3195168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3196168404Spjd 3197168404Spjd case EDOM: 3198168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3199168404Spjd "volume block size must be power of 2 from " 3200168404Spjd "%u to %uk"), 3201168404Spjd (uint_t)SPA_MINBLOCKSIZE, 3202168404Spjd (uint_t)SPA_MAXBLOCKSIZE >> 10); 3203168404Spjd 3204168404Spjd return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3205168404Spjd 3206185029Spjd case ENOTSUP: 3207185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3208185029Spjd "pool must be upgraded to set this " 3209185029Spjd "property or value")); 3210185029Spjd return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 3211168404Spjd#ifdef _ILP32 3212168404Spjd case EOVERFLOW: 3213168404Spjd /* 3214168404Spjd * This platform can't address a volume this big. 3215168404Spjd */ 3216168404Spjd if (type == ZFS_TYPE_VOLUME) 3217168404Spjd return (zfs_error(hdl, EZFS_VOLTOOBIG, 3218168404Spjd errbuf)); 3219168404Spjd#endif 3220168404Spjd /* FALLTHROUGH */ 3221168404Spjd default: 3222168404Spjd return (zfs_standard_error(hdl, errno, errbuf)); 3223168404Spjd } 3224168404Spjd } 3225168404Spjd 3226168404Spjd return (0); 3227168404Spjd} 3228168404Spjd 3229168404Spjd/* 3230168404Spjd * Destroys the given dataset. The caller must make sure that the filesystem 3231238422Smm * isn't mounted, and that there are no active dependents. If the file system 3232238422Smm * does not exist this function does nothing. 3233168404Spjd */ 3234168404Spjdint 3235219089Spjdzfs_destroy(zfs_handle_t *zhp, boolean_t defer) 3236168404Spjd{ 3237168404Spjd zfs_cmd_t zc = { 0 }; 3238168404Spjd 3239260183Sdelphij if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) { 3240260183Sdelphij nvlist_t *nv = fnvlist_alloc(); 3241260183Sdelphij fnvlist_add_boolean(nv, zhp->zfs_name); 3242260183Sdelphij int error = lzc_destroy_bookmarks(nv, NULL); 3243260183Sdelphij fnvlist_free(nv); 3244260183Sdelphij if (error != 0) { 3245260183Sdelphij return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3246260183Sdelphij dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 3247260183Sdelphij zhp->zfs_name)); 3248260183Sdelphij } 3249260183Sdelphij return (0); 3250260183Sdelphij } 3251260183Sdelphij 3252168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3253168404Spjd 3254168404Spjd if (ZFS_IS_VOLUME(zhp)) { 3255168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3256168404Spjd } else { 3257168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3258168404Spjd } 3259168404Spjd 3260219089Spjd zc.zc_defer_destroy = defer; 3261238422Smm if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 && 3262238422Smm errno != ENOENT) { 3263168404Spjd return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3264168404Spjd dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 3265168404Spjd zhp->zfs_name)); 3266168404Spjd } 3267168404Spjd 3268168404Spjd remove_mountpoint(zhp); 3269168404Spjd 3270168404Spjd return (0); 3271168404Spjd} 3272168404Spjd 3273168404Spjdstruct destroydata { 3274228103Smm nvlist_t *nvl; 3275228103Smm const char *snapname; 3276168404Spjd}; 3277168404Spjd 3278168404Spjdstatic int 3279219089Spjdzfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 3280168404Spjd{ 3281168404Spjd struct destroydata *dd = arg; 3282168404Spjd char name[ZFS_MAXNAMELEN]; 3283219089Spjd int rv = 0; 3284168404Spjd 3285228103Smm (void) snprintf(name, sizeof (name), 3286228103Smm "%s@%s", zhp->zfs_name, dd->snapname); 3287168404Spjd 3288251646Sdelphij if (lzc_exists(name)) 3289228103Smm verify(nvlist_add_boolean(dd->nvl, name) == 0); 3290168404Spjd 3291228103Smm rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd); 3292228103Smm zfs_close(zhp); 3293168404Spjd return (rv); 3294168404Spjd} 3295168404Spjd 3296168404Spjd/* 3297168404Spjd * Destroys all snapshots with the given name in zhp & descendants. 3298168404Spjd */ 3299168404Spjdint 3300219089Spjdzfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 3301168404Spjd{ 3302168404Spjd int ret; 3303168404Spjd struct destroydata dd = { 0 }; 3304168404Spjd 3305168404Spjd dd.snapname = snapname; 3306228103Smm verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0); 3307228103Smm (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd); 3308168404Spjd 3309251646Sdelphij if (nvlist_empty(dd.nvl)) { 3310228103Smm ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 3311168404Spjd dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 3312228103Smm zhp->zfs_name, snapname); 3313228103Smm } else { 3314248571Smm ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer); 3315168404Spjd } 3316228103Smm nvlist_free(dd.nvl); 3317228103Smm return (ret); 3318228103Smm} 3319168404Spjd 3320228103Smm/* 3321248571Smm * Destroys all the snapshots named in the nvlist. 3322228103Smm */ 3323228103Smmint 3324248571Smmzfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer) 3325228103Smm{ 3326228103Smm int ret; 3327248571Smm nvlist_t *errlist; 3328228103Smm 3329248571Smm ret = lzc_destroy_snaps(snaps, defer, &errlist); 3330168404Spjd 3331248571Smm if (ret == 0) 3332248571Smm return (0); 3333248571Smm 3334251646Sdelphij if (nvlist_empty(errlist)) { 3335168404Spjd char errbuf[1024]; 3336248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3337248571Smm dgettext(TEXT_DOMAIN, "cannot destroy snapshots")); 3338168404Spjd 3339248571Smm ret = zfs_standard_error(hdl, ret, errbuf); 3340248571Smm } 3341248571Smm for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL); 3342248571Smm pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) { 3343248571Smm char errbuf[1024]; 3344248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3345248571Smm dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"), 3346248571Smm nvpair_name(pair)); 3347168404Spjd 3348248571Smm switch (fnvpair_value_int32(pair)) { 3349168404Spjd case EEXIST: 3350248571Smm zfs_error_aux(hdl, 3351248571Smm dgettext(TEXT_DOMAIN, "snapshot is cloned")); 3352248571Smm ret = zfs_error(hdl, EZFS_EXISTS, errbuf); 3353248571Smm break; 3354168404Spjd default: 3355248571Smm ret = zfs_standard_error(hdl, errno, errbuf); 3356248571Smm break; 3357168404Spjd } 3358168404Spjd } 3359168404Spjd 3360248571Smm return (ret); 3361168404Spjd} 3362168404Spjd 3363168404Spjd/* 3364168404Spjd * Clones the given dataset. The target must be of the same type as the source. 3365168404Spjd */ 3366168404Spjdint 3367168404Spjdzfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3368168404Spjd{ 3369168404Spjd char parent[ZFS_MAXNAMELEN]; 3370168404Spjd int ret; 3371168404Spjd char errbuf[1024]; 3372168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3373168404Spjd uint64_t zoned; 3374168404Spjd 3375168404Spjd assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3376168404Spjd 3377168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3378168404Spjd "cannot create '%s'"), target); 3379168404Spjd 3380228103Smm /* validate the target/clone name */ 3381185029Spjd if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 3382168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3383168404Spjd 3384168404Spjd /* validate parents exist */ 3385185029Spjd if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3386168404Spjd return (-1); 3387168404Spjd 3388168404Spjd (void) parent_name(target, parent, sizeof (parent)); 3389168404Spjd 3390168404Spjd /* do the clone */ 3391168404Spjd 3392168404Spjd if (props) { 3393248571Smm zfs_type_t type; 3394248571Smm if (ZFS_IS_VOLUME(zhp)) { 3395248571Smm type = ZFS_TYPE_VOLUME; 3396248571Smm } else { 3397248571Smm type = ZFS_TYPE_FILESYSTEM; 3398248571Smm } 3399185029Spjd if ((props = zfs_valid_proplist(hdl, type, props, zoned, 3400185029Spjd zhp, errbuf)) == NULL) 3401168404Spjd return (-1); 3402168404Spjd } 3403168404Spjd 3404248571Smm ret = lzc_clone(target, zhp->zfs_name, props); 3405248571Smm nvlist_free(props); 3406168404Spjd 3407168404Spjd if (ret != 0) { 3408168404Spjd switch (errno) { 3409168404Spjd 3410168404Spjd case ENOENT: 3411168404Spjd /* 3412168404Spjd * The parent doesn't exist. We should have caught this 3413168404Spjd * above, but there may a race condition that has since 3414168404Spjd * destroyed the parent. 3415168404Spjd * 3416168404Spjd * At this point, we don't know whether it's the source 3417168404Spjd * that doesn't exist anymore, or whether the target 3418168404Spjd * dataset doesn't exist. 3419168404Spjd */ 3420168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3421168404Spjd "no such parent '%s'"), parent); 3422168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3423168404Spjd 3424168404Spjd case EXDEV: 3425168404Spjd zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 3426168404Spjd "source and target pools differ")); 3427168404Spjd return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 3428168404Spjd errbuf)); 3429168404Spjd 3430168404Spjd default: 3431168404Spjd return (zfs_standard_error(zhp->zfs_hdl, errno, 3432168404Spjd errbuf)); 3433168404Spjd } 3434168404Spjd } 3435168404Spjd 3436168404Spjd return (ret); 3437168404Spjd} 3438168404Spjd 3439168404Spjd/* 3440168404Spjd * Promotes the given clone fs to be the clone parent. 3441168404Spjd */ 3442168404Spjdint 3443168404Spjdzfs_promote(zfs_handle_t *zhp) 3444168404Spjd{ 3445168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3446168404Spjd zfs_cmd_t zc = { 0 }; 3447168404Spjd char parent[MAXPATHLEN]; 3448168404Spjd int ret; 3449168404Spjd char errbuf[1024]; 3450168404Spjd 3451168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3452168404Spjd "cannot promote '%s'"), zhp->zfs_name); 3453168404Spjd 3454168404Spjd if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 3455168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3456168404Spjd "snapshots can not be promoted")); 3457168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3458168404Spjd } 3459168404Spjd 3460185029Spjd (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 3461168404Spjd if (parent[0] == '\0') { 3462168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3463168404Spjd "not a cloned filesystem")); 3464168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3465168404Spjd } 3466168404Spjd 3467185029Spjd (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3468168404Spjd sizeof (zc.zc_value)); 3469168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3470185029Spjd ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 3471168404Spjd 3472168404Spjd if (ret != 0) { 3473168404Spjd int save_errno = errno; 3474168404Spjd 3475168404Spjd switch (save_errno) { 3476168404Spjd case EEXIST: 3477219089Spjd /* There is a conflicting snapshot name. */ 3478168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3479219089Spjd "conflicting snapshot '%s' from parent '%s'"), 3480219089Spjd zc.zc_string, parent); 3481168404Spjd return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3482168404Spjd 3483168404Spjd default: 3484168404Spjd return (zfs_standard_error(hdl, save_errno, errbuf)); 3485168404Spjd } 3486168404Spjd } 3487168404Spjd return (ret); 3488168404Spjd} 3489168404Spjd 3490248571Smmtypedef struct snapdata { 3491248571Smm nvlist_t *sd_nvl; 3492248571Smm const char *sd_snapname; 3493248571Smm} snapdata_t; 3494248571Smm 3495248571Smmstatic int 3496248571Smmzfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 3497248571Smm{ 3498248571Smm snapdata_t *sd = arg; 3499248571Smm char name[ZFS_MAXNAMELEN]; 3500248571Smm int rv = 0; 3501248571Smm 3502253819Sdelphij if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) { 3503253819Sdelphij (void) snprintf(name, sizeof (name), 3504253819Sdelphij "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 3505248571Smm 3506253819Sdelphij fnvlist_add_boolean(sd->sd_nvl, name); 3507248571Smm 3508253819Sdelphij rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 3509253819Sdelphij } 3510248571Smm zfs_close(zhp); 3511253819Sdelphij 3512248571Smm return (rv); 3513248571Smm} 3514248571Smm 3515168404Spjd/* 3516248571Smm * Creates snapshots. The keys in the snaps nvlist are the snapshots to be 3517248571Smm * created. 3518168404Spjd */ 3519168404Spjdint 3520248571Smmzfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props) 3521168404Spjd{ 3522168404Spjd int ret; 3523168404Spjd char errbuf[1024]; 3524248571Smm nvpair_t *elem; 3525248571Smm nvlist_t *errors; 3526168404Spjd 3527168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3528248571Smm "cannot create snapshots ")); 3529168404Spjd 3530248571Smm elem = NULL; 3531248571Smm while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) { 3532248571Smm const char *snapname = nvpair_name(elem); 3533168404Spjd 3534248571Smm /* validate the target name */ 3535248571Smm if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT, 3536248571Smm B_TRUE)) { 3537248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3538248571Smm dgettext(TEXT_DOMAIN, 3539248571Smm "cannot create snapshot '%s'"), snapname); 3540248571Smm return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3541248571Smm } 3542248571Smm } 3543185029Spjd 3544248571Smm if (props != NULL && 3545248571Smm (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3546248571Smm props, B_FALSE, NULL, errbuf)) == NULL) { 3547248571Smm return (-1); 3548248571Smm } 3549248571Smm 3550248571Smm ret = lzc_snapshot(snaps, props, &errors); 3551248571Smm 3552248571Smm if (ret != 0) { 3553248571Smm boolean_t printed = B_FALSE; 3554248571Smm for (elem = nvlist_next_nvpair(errors, NULL); 3555248571Smm elem != NULL; 3556248571Smm elem = nvlist_next_nvpair(errors, elem)) { 3557248571Smm (void) snprintf(errbuf, sizeof (errbuf), 3558248571Smm dgettext(TEXT_DOMAIN, 3559248571Smm "cannot create snapshot '%s'"), nvpair_name(elem)); 3560248571Smm (void) zfs_standard_error(hdl, 3561248571Smm fnvpair_value_int32(elem), errbuf); 3562248571Smm printed = B_TRUE; 3563185029Spjd } 3564248571Smm if (!printed) { 3565248571Smm switch (ret) { 3566248571Smm case EXDEV: 3567248571Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3568248571Smm "multiple snapshots of same " 3569248571Smm "fs not allowed")); 3570248571Smm (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3571185029Spjd 3572248571Smm break; 3573248571Smm default: 3574248571Smm (void) zfs_standard_error(hdl, ret, errbuf); 3575248571Smm } 3576248571Smm } 3577185029Spjd } 3578185029Spjd 3579248571Smm nvlist_free(props); 3580248571Smm nvlist_free(errors); 3581248571Smm return (ret); 3582248571Smm} 3583168404Spjd 3584248571Smmint 3585248571Smmzfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 3586248571Smm nvlist_t *props) 3587248571Smm{ 3588248571Smm int ret; 3589248571Smm snapdata_t sd = { 0 }; 3590248571Smm char fsname[ZFS_MAXNAMELEN]; 3591248571Smm char *cp; 3592248571Smm zfs_handle_t *zhp; 3593248571Smm char errbuf[1024]; 3594248571Smm 3595248571Smm (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3596248571Smm "cannot snapshot %s"), path); 3597248571Smm 3598248571Smm if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 3599248571Smm return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3600248571Smm 3601248571Smm (void) strlcpy(fsname, path, sizeof (fsname)); 3602248571Smm cp = strchr(fsname, '@'); 3603248571Smm *cp = '\0'; 3604248571Smm sd.sd_snapname = cp + 1; 3605248571Smm 3606248571Smm if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | 3607168404Spjd ZFS_TYPE_VOLUME)) == NULL) { 3608168404Spjd return (-1); 3609168404Spjd } 3610168404Spjd 3611248571Smm verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0); 3612248571Smm if (recursive) { 3613248571Smm (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd); 3614248571Smm } else { 3615248571Smm fnvlist_add_boolean(sd.sd_nvl, path); 3616168404Spjd } 3617168404Spjd 3618248571Smm ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props); 3619248571Smm nvlist_free(sd.sd_nvl); 3620168404Spjd zfs_close(zhp); 3621168404Spjd return (ret); 3622168404Spjd} 3623168404Spjd 3624168404Spjd/* 3625168404Spjd * Destroy any more recent snapshots. We invoke this callback on any dependents 3626168404Spjd * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3627168404Spjd * is a dependent and we should just destroy it without checking the transaction 3628168404Spjd * group. 3629168404Spjd */ 3630168404Spjdtypedef struct rollback_data { 3631168404Spjd const char *cb_target; /* the snapshot */ 3632168404Spjd uint64_t cb_create; /* creation time reference */ 3633185029Spjd boolean_t cb_error; 3634185029Spjd boolean_t cb_force; 3635168404Spjd} rollback_data_t; 3636168404Spjd 3637168404Spjdstatic int 3638260183Sdelphijrollback_destroy_dependent(zfs_handle_t *zhp, void *data) 3639168404Spjd{ 3640168404Spjd rollback_data_t *cbp = data; 3641260183Sdelphij prop_changelist_t *clp; 3642168404Spjd 3643260183Sdelphij /* We must destroy this clone; first unmount it */ 3644260183Sdelphij clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 3645260183Sdelphij cbp->cb_force ? MS_FORCE: 0); 3646260183Sdelphij if (clp == NULL || changelist_prefix(clp) != 0) { 3647260183Sdelphij cbp->cb_error = B_TRUE; 3648260183Sdelphij zfs_close(zhp); 3649260183Sdelphij return (0); 3650260183Sdelphij } 3651260183Sdelphij if (zfs_destroy(zhp, B_FALSE) != 0) 3652260183Sdelphij cbp->cb_error = B_TRUE; 3653260183Sdelphij else 3654260183Sdelphij changelist_remove(clp, zhp->zfs_name); 3655260183Sdelphij (void) changelist_postfix(clp); 3656260183Sdelphij changelist_free(clp); 3657168404Spjd 3658260183Sdelphij zfs_close(zhp); 3659260183Sdelphij return (0); 3660260183Sdelphij} 3661168404Spjd 3662260183Sdelphijstatic int 3663260183Sdelphijrollback_destroy(zfs_handle_t *zhp, void *data) 3664260183Sdelphij{ 3665260183Sdelphij rollback_data_t *cbp = data; 3666185029Spjd 3667260183Sdelphij if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { 3668260183Sdelphij cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 3669260183Sdelphij rollback_destroy_dependent, cbp); 3670260183Sdelphij 3671260183Sdelphij cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 3672168404Spjd } 3673168404Spjd 3674168404Spjd zfs_close(zhp); 3675168404Spjd return (0); 3676168404Spjd} 3677168404Spjd 3678168404Spjd/* 3679168404Spjd * Given a dataset, rollback to a specific snapshot, discarding any 3680168404Spjd * data changes since then and making it the active dataset. 3681168404Spjd * 3682260183Sdelphij * Any snapshots and bookmarks more recent than the target are 3683260183Sdelphij * destroyed, along with their dependents (i.e. clones). 3684168404Spjd */ 3685168404Spjdint 3686185029Spjdzfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3687168404Spjd{ 3688168404Spjd rollback_data_t cb = { 0 }; 3689185029Spjd int err; 3690185029Spjd boolean_t restore_resv = 0; 3691185029Spjd uint64_t old_volsize, new_volsize; 3692185029Spjd zfs_prop_t resv_prop; 3693168404Spjd 3694185029Spjd assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3695185029Spjd zhp->zfs_type == ZFS_TYPE_VOLUME); 3696168404Spjd 3697168404Spjd /* 3698239774Smm * Destroy all recent snapshots and their dependents. 3699168404Spjd */ 3700185029Spjd cb.cb_force = force; 3701168404Spjd cb.cb_target = snap->zfs_name; 3702168404Spjd cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3703260183Sdelphij (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb); 3704260183Sdelphij (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb); 3705168404Spjd 3706185029Spjd if (cb.cb_error) 3707185029Spjd return (-1); 3708168404Spjd 3709168404Spjd /* 3710168404Spjd * Now that we have verified that the snapshot is the latest, 3711168404Spjd * rollback to the given snapshot. 3712168404Spjd */ 3713168404Spjd 3714185029Spjd if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 3715185029Spjd if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 3716185029Spjd return (-1); 3717185029Spjd old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 3718185029Spjd restore_resv = 3719185029Spjd (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 3720168404Spjd } 3721168404Spjd 3722168404Spjd /* 3723185029Spjd * We rely on zfs_iter_children() to verify that there are no 3724185029Spjd * newer snapshots for the given dataset. Therefore, we can 3725185029Spjd * simply pass the name on to the ioctl() call. There is still 3726185029Spjd * an unlikely race condition where the user has taken a 3727185029Spjd * snapshot since we verified that this was the most recent. 3728168404Spjd */ 3729254587Sdelphij err = lzc_rollback(zhp->zfs_name, NULL, 0); 3730254587Sdelphij if (err != 0) { 3731185029Spjd (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 3732185029Spjd dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 3733185029Spjd zhp->zfs_name); 3734185029Spjd return (err); 3735185029Spjd } 3736168404Spjd 3737185029Spjd /* 3738185029Spjd * For volumes, if the pre-rollback volsize matched the pre- 3739185029Spjd * rollback reservation and the volsize has changed then set 3740185029Spjd * the reservation property to the post-rollback volsize. 3741185029Spjd * Make a new handle since the rollback closed the dataset. 3742185029Spjd */ 3743185029Spjd if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3744185029Spjd (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3745185029Spjd if (restore_resv) { 3746185029Spjd new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 3747185029Spjd if (old_volsize != new_volsize) 3748185029Spjd err = zfs_prop_set_int(zhp, resv_prop, 3749185029Spjd new_volsize); 3750185029Spjd } 3751185029Spjd zfs_close(zhp); 3752185029Spjd } 3753185029Spjd return (err); 3754168404Spjd} 3755168404Spjd 3756168404Spjd/* 3757168404Spjd * Renames the given dataset. 3758168404Spjd */ 3759168404Spjdint 3760240870Spjdzfs_rename(zfs_handle_t *zhp, const char *source, const char *target, 3761240870Spjd renameflags_t flags) 3762168404Spjd{ 3763168404Spjd int ret; 3764168404Spjd zfs_cmd_t zc = { 0 }; 3765168404Spjd char *delim; 3766168676Spjd prop_changelist_t *cl = NULL; 3767168676Spjd zfs_handle_t *zhrp = NULL; 3768168676Spjd char *parentname = NULL; 3769168404Spjd char parent[ZFS_MAXNAMELEN]; 3770226676Spjd char property[ZFS_MAXPROPLEN]; 3771168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 3772168404Spjd char errbuf[1024]; 3773168404Spjd 3774168404Spjd /* if we have the same exact name, just return success */ 3775168404Spjd if (strcmp(zhp->zfs_name, target) == 0) 3776168404Spjd return (0); 3777168404Spjd 3778168404Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3779168404Spjd "cannot rename to '%s'"), target); 3780168404Spjd 3781240870Spjd if (source != NULL) { 3782240870Spjd /* 3783240870Spjd * This is recursive snapshots rename, put snapshot name 3784240870Spjd * (that might not exist) into zfs_name. 3785240870Spjd */ 3786240870Spjd assert(flags.recurse); 3787240870Spjd 3788240870Spjd (void) strlcat(zhp->zfs_name, "@", sizeof(zhp->zfs_name)); 3789240870Spjd (void) strlcat(zhp->zfs_name, source, sizeof(zhp->zfs_name)); 3790240870Spjd zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 3791240870Spjd } 3792240870Spjd 3793168404Spjd /* 3794168404Spjd * Make sure the target name is valid 3795168404Spjd */ 3796168404Spjd if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 3797168404Spjd if ((strchr(target, '@') == NULL) || 3798168404Spjd *target == '@') { 3799168404Spjd /* 3800168404Spjd * Snapshot target name is abbreviated, 3801168404Spjd * reconstruct full dataset name 3802168404Spjd */ 3803168404Spjd (void) strlcpy(parent, zhp->zfs_name, 3804168404Spjd sizeof (parent)); 3805168404Spjd delim = strchr(parent, '@'); 3806168404Spjd if (strchr(target, '@') == NULL) 3807168404Spjd *(++delim) = '\0'; 3808168404Spjd else 3809168404Spjd *delim = '\0'; 3810168404Spjd (void) strlcat(parent, target, sizeof (parent)); 3811168404Spjd target = parent; 3812168404Spjd } else { 3813168404Spjd /* 3814168404Spjd * Make sure we're renaming within the same dataset. 3815168404Spjd */ 3816168404Spjd delim = strchr(target, '@'); 3817168404Spjd if (strncmp(zhp->zfs_name, target, delim - target) 3818168404Spjd != 0 || zhp->zfs_name[delim - target] != '@') { 3819168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3820168404Spjd "snapshots must be part of same " 3821168404Spjd "dataset")); 3822168404Spjd return (zfs_error(hdl, EZFS_CROSSTARGET, 3823168404Spjd errbuf)); 3824168404Spjd } 3825168404Spjd } 3826185029Spjd if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 3827168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3828168404Spjd } else { 3829226705Spjd if (flags.recurse) { 3830168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3831168676Spjd "recursive rename must be a snapshot")); 3832168676Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3833168676Spjd } 3834168676Spjd 3835185029Spjd if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 3836168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3837168404Spjd 3838168404Spjd /* validate parents */ 3839219089Spjd if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 3840168404Spjd return (-1); 3841168404Spjd 3842168404Spjd /* make sure we're in the same pool */ 3843168404Spjd verify((delim = strchr(target, '/')) != NULL); 3844168404Spjd if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3845168404Spjd zhp->zfs_name[delim - target] != '/') { 3846168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3847168404Spjd "datasets must be within same pool")); 3848168404Spjd return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3849168404Spjd } 3850168404Spjd 3851168404Spjd /* new name cannot be a child of the current dataset name */ 3852219089Spjd if (is_descendant(zhp->zfs_name, target)) { 3853168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3854219089Spjd "New dataset name cannot be a descendant of " 3855168404Spjd "current dataset name")); 3856168404Spjd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3857168404Spjd } 3858168404Spjd } 3859168404Spjd 3860168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 3861168404Spjd dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 3862168404Spjd 3863168404Spjd if (getzoneid() == GLOBAL_ZONEID && 3864168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 3865168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3866168404Spjd "dataset is used in a non-global zone")); 3867168404Spjd return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3868168404Spjd } 3869168404Spjd 3870226705Spjd /* 3871226705Spjd * Avoid unmounting file systems with mountpoint property set to 3872226705Spjd * 'legacy' or 'none' even if -u option is not given. 3873226705Spjd */ 3874226705Spjd if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM && 3875226705Spjd !flags.recurse && !flags.nounmount && 3876226705Spjd zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property, 3877226705Spjd sizeof (property), NULL, NULL, 0, B_FALSE) == 0 && 3878226705Spjd (strcmp(property, "legacy") == 0 || 3879226705Spjd strcmp(property, "none") == 0)) { 3880226705Spjd flags.nounmount = B_TRUE; 3881226705Spjd } 3882226705Spjd if (flags.recurse) { 3883226705Spjd 3884185029Spjd parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3885185029Spjd if (parentname == NULL) { 3886185029Spjd ret = -1; 3887185029Spjd goto error; 3888185029Spjd } 3889168676Spjd delim = strchr(parentname, '@'); 3890168676Spjd *delim = '\0'; 3891185029Spjd zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3892168676Spjd if (zhrp == NULL) { 3893185029Spjd ret = -1; 3894185029Spjd goto error; 3895168676Spjd } 3896268469Sdelphij } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) { 3897226676Spjd if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 3898235216Smm flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0, 3899235216Smm flags.forceunmount ? MS_FORCE : 0)) == NULL) { 3900168676Spjd return (-1); 3901226676Spjd } 3902168676Spjd 3903168676Spjd if (changelist_haszonedchild(cl)) { 3904168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3905168676Spjd "child dataset with inherited mountpoint is used " 3906168676Spjd "in a non-global zone")); 3907168676Spjd (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3908168676Spjd goto error; 3909168676Spjd } 3910168676Spjd 3911168676Spjd if ((ret = changelist_prefix(cl)) != 0) 3912168676Spjd goto error; 3913168404Spjd } 3914168404Spjd 3915168404Spjd if (ZFS_IS_VOLUME(zhp)) 3916168404Spjd zc.zc_objset_type = DMU_OST_ZVOL; 3917168404Spjd else 3918168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 3919168404Spjd 3920168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3921168404Spjd (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 3922168404Spjd 3923226705Spjd zc.zc_cookie = flags.recurse ? 1 : 0; 3924226705Spjd if (flags.nounmount) 3925226676Spjd zc.zc_cookie |= 2; 3926168676Spjd 3927185029Spjd if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3928168676Spjd /* 3929168676Spjd * if it was recursive, the one that actually failed will 3930168676Spjd * be in zc.zc_name 3931168676Spjd */ 3932168676Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3933185029Spjd "cannot rename '%s'"), zc.zc_name); 3934168404Spjd 3935226705Spjd if (flags.recurse && errno == EEXIST) { 3936168676Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3937168676Spjd "a child dataset already has a snapshot " 3938168676Spjd "with the new name")); 3939185029Spjd (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3940168676Spjd } else { 3941168676Spjd (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3942168676Spjd } 3943168676Spjd 3944168404Spjd /* 3945168404Spjd * On failure, we still want to remount any filesystems that 3946168404Spjd * were previously mounted, so we don't alter the system state. 3947168404Spjd */ 3948268469Sdelphij if (cl != NULL) 3949168676Spjd (void) changelist_postfix(cl); 3950168404Spjd } else { 3951268469Sdelphij if (cl != NULL) { 3952168676Spjd changelist_rename(cl, zfs_get_name(zhp), target); 3953168676Spjd ret = changelist_postfix(cl); 3954168676Spjd } 3955168404Spjd } 3956168404Spjd 3957168404Spjderror: 3958268469Sdelphij if (parentname != NULL) { 3959168676Spjd free(parentname); 3960168676Spjd } 3961268469Sdelphij if (zhrp != NULL) { 3962168676Spjd zfs_close(zhrp); 3963168676Spjd } 3964268469Sdelphij if (cl != NULL) { 3965168676Spjd changelist_free(cl); 3966168676Spjd } 3967168404Spjd return (ret); 3968168404Spjd} 3969168404Spjd 3970219089Spjdnvlist_t * 3971219089Spjdzfs_get_user_props(zfs_handle_t *zhp) 3972168404Spjd{ 3973219089Spjd return (zhp->zfs_user_props); 3974168676Spjd} 3975168676Spjd 3976168404Spjdnvlist_t * 3977219089Spjdzfs_get_recvd_props(zfs_handle_t *zhp) 3978168404Spjd{ 3979219089Spjd if (zhp->zfs_recvd_props == NULL) 3980219089Spjd if (get_recvd_props_ioctl(zhp) != 0) 3981219089Spjd return (NULL); 3982219089Spjd return (zhp->zfs_recvd_props); 3983168404Spjd} 3984168404Spjd 3985168404Spjd/* 3986168404Spjd * This function is used by 'zfs list' to determine the exact set of columns to 3987168404Spjd * display, and their maximum widths. This does two main things: 3988168404Spjd * 3989168404Spjd * - If this is a list of all properties, then expand the list to include 3990168404Spjd * all native properties, and set a flag so that for each dataset we look 3991168404Spjd * for new unique user properties and add them to the list. 3992168404Spjd * 3993168404Spjd * - For non fixed-width properties, keep track of the maximum width seen 3994219089Spjd * so that we can size the column appropriately. If the user has 3995219089Spjd * requested received property values, we also need to compute the width 3996219089Spjd * of the RECEIVED column. 3997168404Spjd */ 3998168404Spjdint 3999259850Sdelphijzfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received, 4000259850Sdelphij boolean_t literal) 4001168404Spjd{ 4002168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4003185029Spjd zprop_list_t *entry; 4004185029Spjd zprop_list_t **last, **start; 4005168404Spjd nvlist_t *userprops, *propval; 4006168404Spjd nvpair_t *elem; 4007168404Spjd char *strval; 4008168404Spjd char buf[ZFS_MAXPROPLEN]; 4009168404Spjd 4010185029Spjd if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 4011168404Spjd return (-1); 4012168404Spjd 4013168404Spjd userprops = zfs_get_user_props(zhp); 4014168404Spjd 4015168404Spjd entry = *plp; 4016168404Spjd if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 4017168404Spjd /* 4018168404Spjd * Go through and add any user properties as necessary. We 4019168404Spjd * start by incrementing our list pointer to the first 4020168404Spjd * non-native property. 4021168404Spjd */ 4022168404Spjd start = plp; 4023168404Spjd while (*start != NULL) { 4024185029Spjd if ((*start)->pl_prop == ZPROP_INVAL) 4025168404Spjd break; 4026168404Spjd start = &(*start)->pl_next; 4027168404Spjd } 4028168404Spjd 4029168404Spjd elem = NULL; 4030168404Spjd while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 4031168404Spjd /* 4032168404Spjd * See if we've already found this property in our list. 4033168404Spjd */ 4034168404Spjd for (last = start; *last != NULL; 4035168404Spjd last = &(*last)->pl_next) { 4036168404Spjd if (strcmp((*last)->pl_user_prop, 4037168404Spjd nvpair_name(elem)) == 0) 4038168404Spjd break; 4039168404Spjd } 4040168404Spjd 4041168404Spjd if (*last == NULL) { 4042168404Spjd if ((entry = zfs_alloc(hdl, 4043185029Spjd sizeof (zprop_list_t))) == NULL || 4044168404Spjd ((entry->pl_user_prop = zfs_strdup(hdl, 4045168404Spjd nvpair_name(elem)))) == NULL) { 4046168404Spjd free(entry); 4047168404Spjd return (-1); 4048168404Spjd } 4049168404Spjd 4050185029Spjd entry->pl_prop = ZPROP_INVAL; 4051168404Spjd entry->pl_width = strlen(nvpair_name(elem)); 4052168404Spjd entry->pl_all = B_TRUE; 4053168404Spjd *last = entry; 4054168404Spjd } 4055168404Spjd } 4056168404Spjd } 4057168404Spjd 4058168404Spjd /* 4059168404Spjd * Now go through and check the width of any non-fixed columns 4060168404Spjd */ 4061168404Spjd for (entry = *plp; entry != NULL; entry = entry->pl_next) { 4062259850Sdelphij if (entry->pl_fixed && !literal) 4063168404Spjd continue; 4064168404Spjd 4065185029Spjd if (entry->pl_prop != ZPROP_INVAL) { 4066168404Spjd if (zfs_prop_get(zhp, entry->pl_prop, 4067259850Sdelphij buf, sizeof (buf), NULL, NULL, 0, literal) == 0) { 4068168404Spjd if (strlen(buf) > entry->pl_width) 4069168404Spjd entry->pl_width = strlen(buf); 4070168404Spjd } 4071219089Spjd if (received && zfs_prop_get_recvd(zhp, 4072219089Spjd zfs_prop_to_name(entry->pl_prop), 4073259850Sdelphij buf, sizeof (buf), literal) == 0) 4074219089Spjd if (strlen(buf) > entry->pl_recvd_width) 4075219089Spjd entry->pl_recvd_width = strlen(buf); 4076219089Spjd } else { 4077219089Spjd if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 4078219089Spjd &propval) == 0) { 4079219089Spjd verify(nvlist_lookup_string(propval, 4080219089Spjd ZPROP_VALUE, &strval) == 0); 4081219089Spjd if (strlen(strval) > entry->pl_width) 4082219089Spjd entry->pl_width = strlen(strval); 4083219089Spjd } 4084219089Spjd if (received && zfs_prop_get_recvd(zhp, 4085219089Spjd entry->pl_user_prop, 4086259850Sdelphij buf, sizeof (buf), literal) == 0) 4087219089Spjd if (strlen(buf) > entry->pl_recvd_width) 4088219089Spjd entry->pl_recvd_width = strlen(buf); 4089168404Spjd } 4090168404Spjd } 4091168404Spjd 4092168404Spjd return (0); 4093168404Spjd} 4094168404Spjd 4095185029Spjdint 4096185029Spjdzfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 4097209962Smm char *resource, void *export, void *sharetab, 4098209962Smm int sharemax, zfs_share_op_t operation) 4099185029Spjd{ 4100185029Spjd zfs_cmd_t zc = { 0 }; 4101185029Spjd int error; 4102185029Spjd 4103185029Spjd (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4104185029Spjd (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4105209962Smm if (resource) 4106209962Smm (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 4107185029Spjd zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 4108185029Spjd zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 4109185029Spjd zc.zc_share.z_sharetype = operation; 4110185029Spjd zc.zc_share.z_sharemax = sharemax; 4111185029Spjd error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 4112185029Spjd return (error); 4113185029Spjd} 4114185029Spjd 4115205198Sdelphijvoid 4116205198Sdelphijzfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 4117205198Sdelphij{ 4118205198Sdelphij nvpair_t *curr; 4119205198Sdelphij 4120205198Sdelphij /* 4121205198Sdelphij * Keep a reference to the props-table against which we prune the 4122205198Sdelphij * properties. 4123205198Sdelphij */ 4124205198Sdelphij zhp->zfs_props_table = props; 4125205198Sdelphij 4126205198Sdelphij curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 4127205198Sdelphij 4128205198Sdelphij while (curr) { 4129205198Sdelphij zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 4130205198Sdelphij nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 4131205198Sdelphij 4132206199Sdelphij /* 4133219089Spjd * User properties will result in ZPROP_INVAL, and since we 4134219089Spjd * only know how to prune standard ZFS properties, we always 4135219089Spjd * leave these in the list. This can also happen if we 4136219089Spjd * encounter an unknown DSL property (when running older 4137219089Spjd * software, for example). 4138206199Sdelphij */ 4139206199Sdelphij if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 4140205198Sdelphij (void) nvlist_remove(zhp->zfs_props, 4141205198Sdelphij nvpair_name(curr), nvpair_type(curr)); 4142205198Sdelphij curr = next; 4143205198Sdelphij } 4144205198Sdelphij} 4145205198Sdelphij 4146209962Smm#ifdef sun 4147209962Smmstatic int 4148209962Smmzfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 4149209962Smm zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 4150209962Smm{ 4151209962Smm zfs_cmd_t zc = { 0 }; 4152209962Smm nvlist_t *nvlist = NULL; 4153209962Smm int error; 4154209962Smm 4155209962Smm (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4156209962Smm (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4157209962Smm zc.zc_cookie = (uint64_t)cmd; 4158209962Smm 4159209962Smm if (cmd == ZFS_SMB_ACL_RENAME) { 4160209962Smm if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 4161209962Smm (void) no_memory(hdl); 4162209962Smm return (NULL); 4163209962Smm } 4164209962Smm } 4165209962Smm 4166209962Smm switch (cmd) { 4167209962Smm case ZFS_SMB_ACL_ADD: 4168209962Smm case ZFS_SMB_ACL_REMOVE: 4169209962Smm (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 4170209962Smm break; 4171209962Smm case ZFS_SMB_ACL_RENAME: 4172209962Smm if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 4173209962Smm resource1) != 0) { 4174209962Smm (void) no_memory(hdl); 4175209962Smm return (-1); 4176209962Smm } 4177209962Smm if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 4178209962Smm resource2) != 0) { 4179209962Smm (void) no_memory(hdl); 4180209962Smm return (-1); 4181209962Smm } 4182209962Smm if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 4183209962Smm nvlist_free(nvlist); 4184209962Smm return (-1); 4185209962Smm } 4186209962Smm break; 4187209962Smm case ZFS_SMB_ACL_PURGE: 4188209962Smm break; 4189209962Smm default: 4190209962Smm return (-1); 4191209962Smm } 4192209962Smm error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 4193209962Smm if (nvlist) 4194209962Smm nvlist_free(nvlist); 4195209962Smm return (error); 4196209962Smm} 4197209962Smm 4198209962Smmint 4199209962Smmzfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 4200209962Smm char *path, char *resource) 4201209962Smm{ 4202209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 4203209962Smm resource, NULL)); 4204209962Smm} 4205209962Smm 4206209962Smmint 4207209962Smmzfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 4208209962Smm char *path, char *resource) 4209209962Smm{ 4210209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 4211209962Smm resource, NULL)); 4212209962Smm} 4213209962Smm 4214209962Smmint 4215209962Smmzfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 4216209962Smm{ 4217209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 4218209962Smm NULL, NULL)); 4219209962Smm} 4220209962Smm 4221209962Smmint 4222209962Smmzfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 4223209962Smm char *oldname, char *newname) 4224209962Smm{ 4225209962Smm return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 4226209962Smm oldname, newname)); 4227209962Smm} 4228209962Smm#endif /* sun */ 4229209962Smm 4230209962Smmint 4231209962Smmzfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 4232209962Smm zfs_userspace_cb_t func, void *arg) 4233209962Smm{ 4234209962Smm zfs_cmd_t zc = { 0 }; 4235209962Smm zfs_useracct_t buf[100]; 4236240415Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 4237240415Smm int ret; 4238209962Smm 4239228103Smm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4240209962Smm 4241209962Smm zc.zc_objset_type = type; 4242209962Smm zc.zc_nvlist_dst = (uintptr_t)buf; 4243209962Smm 4244240415Smm for (;;) { 4245209962Smm zfs_useracct_t *zua = buf; 4246209962Smm 4247209962Smm zc.zc_nvlist_dst_size = sizeof (buf); 4248240415Smm if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) { 4249248571Smm char errbuf[1024]; 4250240415Smm 4251240415Smm (void) snprintf(errbuf, sizeof (errbuf), 4252240415Smm dgettext(TEXT_DOMAIN, 4253240415Smm "cannot get used/quota for %s"), zc.zc_name); 4254240415Smm return (zfs_standard_error_fmt(hdl, errno, errbuf)); 4255240415Smm } 4256240415Smm if (zc.zc_nvlist_dst_size == 0) 4257209962Smm break; 4258209962Smm 4259209962Smm while (zc.zc_nvlist_dst_size > 0) { 4260240415Smm if ((ret = func(arg, zua->zu_domain, zua->zu_rid, 4261240415Smm zua->zu_space)) != 0) 4262240415Smm return (ret); 4263209962Smm zua++; 4264209962Smm zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 4265209962Smm } 4266209962Smm } 4267209962Smm 4268240415Smm return (0); 4269209962Smm} 4270209962Smm 4271248571Smmstruct holdarg { 4272248571Smm nvlist_t *nvl; 4273248571Smm const char *snapname; 4274248571Smm const char *tag; 4275248571Smm boolean_t recursive; 4276252219Sdelphij int error; 4277248571Smm}; 4278248571Smm 4279248571Smmstatic int 4280248571Smmzfs_hold_one(zfs_handle_t *zhp, void *arg) 4281248571Smm{ 4282248571Smm struct holdarg *ha = arg; 4283248571Smm char name[ZFS_MAXNAMELEN]; 4284248571Smm int rv = 0; 4285248571Smm 4286248571Smm (void) snprintf(name, sizeof (name), 4287248571Smm "%s@%s", zhp->zfs_name, ha->snapname); 4288248571Smm 4289251646Sdelphij if (lzc_exists(name)) 4290248571Smm fnvlist_add_string(ha->nvl, name, ha->tag); 4291248571Smm 4292248571Smm if (ha->recursive) 4293248571Smm rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha); 4294248571Smm zfs_close(zhp); 4295248571Smm return (rv); 4296248571Smm} 4297248571Smm 4298219089Spjdint 4299219089Spjdzfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 4300251646Sdelphij boolean_t recursive, int cleanup_fd) 4301219089Spjd{ 4302248571Smm int ret; 4303248571Smm struct holdarg ha; 4304219089Spjd 4305248571Smm ha.nvl = fnvlist_alloc(); 4306248571Smm ha.snapname = snapname; 4307248571Smm ha.tag = tag; 4308248571Smm ha.recursive = recursive; 4309248571Smm (void) zfs_hold_one(zfs_handle_dup(zhp), &ha); 4310249357Smm 4311251646Sdelphij if (nvlist_empty(ha.nvl)) { 4312251646Sdelphij char errbuf[1024]; 4313251646Sdelphij 4314249357Smm fnvlist_free(ha.nvl); 4315249357Smm ret = ENOENT; 4316251646Sdelphij (void) snprintf(errbuf, sizeof (errbuf), 4317251646Sdelphij dgettext(TEXT_DOMAIN, 4318251646Sdelphij "cannot hold snapshot '%s@%s'"), 4319251646Sdelphij zhp->zfs_name, snapname); 4320251646Sdelphij (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf); 4321249357Smm return (ret); 4322249357Smm } 4323249357Smm 4324251646Sdelphij ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl); 4325248571Smm fnvlist_free(ha.nvl); 4326219089Spjd 4327251646Sdelphij return (ret); 4328251646Sdelphij} 4329251646Sdelphij 4330251646Sdelphijint 4331251646Sdelphijzfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds) 4332251646Sdelphij{ 4333251646Sdelphij int ret; 4334251646Sdelphij nvlist_t *errors; 4335251646Sdelphij libzfs_handle_t *hdl = zhp->zfs_hdl; 4336251646Sdelphij char errbuf[1024]; 4337251646Sdelphij nvpair_t *elem; 4338251646Sdelphij 4339251646Sdelphij errors = NULL; 4340251646Sdelphij ret = lzc_hold(holds, cleanup_fd, &errors); 4341251646Sdelphij 4342251646Sdelphij if (ret == 0) { 4343251646Sdelphij /* There may be errors even in the success case. */ 4344251646Sdelphij fnvlist_free(errors); 4345248571Smm return (0); 4346251646Sdelphij } 4347219089Spjd 4348251646Sdelphij if (nvlist_empty(errors)) { 4349248571Smm /* no hold-specific errors */ 4350248571Smm (void) snprintf(errbuf, sizeof (errbuf), 4351248571Smm dgettext(TEXT_DOMAIN, "cannot hold")); 4352248571Smm switch (ret) { 4353248571Smm case ENOTSUP: 4354248571Smm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4355248571Smm "pool must be upgraded")); 4356248571Smm (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 4357248571Smm break; 4358248571Smm case EINVAL: 4359248571Smm (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 4360248571Smm break; 4361248571Smm default: 4362248571Smm (void) zfs_standard_error(hdl, ret, errbuf); 4363248571Smm } 4364248571Smm } 4365219089Spjd 4366248571Smm for (elem = nvlist_next_nvpair(errors, NULL); 4367248571Smm elem != NULL; 4368248571Smm elem = nvlist_next_nvpair(errors, elem)) { 4369248571Smm (void) snprintf(errbuf, sizeof (errbuf), 4370248571Smm dgettext(TEXT_DOMAIN, 4371248571Smm "cannot hold snapshot '%s'"), nvpair_name(elem)); 4372248571Smm switch (fnvpair_value_int32(elem)) { 4373219089Spjd case E2BIG: 4374219089Spjd /* 4375219089Spjd * Temporary tags wind up having the ds object id 4376219089Spjd * prepended. So even if we passed the length check 4377219089Spjd * above, it's still possible for the tag to wind 4378219089Spjd * up being slightly too long. 4379219089Spjd */ 4380248571Smm (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf); 4381248571Smm break; 4382219089Spjd case EINVAL: 4383248571Smm (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 4384248571Smm break; 4385219089Spjd case EEXIST: 4386248571Smm (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf); 4387248571Smm break; 4388219089Spjd default: 4389248571Smm (void) zfs_standard_error(hdl, 4390248571Smm fnvpair_value_int32(elem), errbuf); 4391219089Spjd } 4392219089Spjd } 4393219089Spjd 4394248571Smm fnvlist_free(errors); 4395248571Smm return (ret); 4396219089Spjd} 4397219089Spjd 4398248571Smmstatic int 4399248571Smmzfs_release_one(zfs_handle_t *zhp, void *arg) 4400248571Smm{ 4401248571Smm struct holdarg *ha = arg; 4402248571Smm char name[ZFS_MAXNAMELEN]; 4403248571Smm int rv = 0; 4404252219Sdelphij nvlist_t *existing_holds; 4405248571Smm 4406248571Smm (void) snprintf(name, sizeof (name), 4407248571Smm "%s@%s", zhp->zfs_name, ha->snapname); 4408248571Smm 4409252219Sdelphij if (lzc_get_holds(name, &existing_holds) != 0) { 4410252219Sdelphij ha->error = ENOENT; 4411252219Sdelphij } else if (!nvlist_exists(existing_holds, ha->tag)) { 4412252219Sdelphij ha->error = ESRCH; 4413252219Sdelphij } else { 4414252219Sdelphij nvlist_t *torelease = fnvlist_alloc(); 4415252219Sdelphij fnvlist_add_boolean(torelease, ha->tag); 4416252219Sdelphij fnvlist_add_nvlist(ha->nvl, name, torelease); 4417252219Sdelphij fnvlist_free(torelease); 4418248571Smm } 4419248571Smm 4420248571Smm if (ha->recursive) 4421248571Smm rv = zfs_iter_filesystems(zhp, zfs_release_one, ha); 4422248571Smm zfs_close(zhp); 4423248571Smm return (rv); 4424248571Smm} 4425248571Smm 4426219089Spjdint 4427219089Spjdzfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4428219089Spjd boolean_t recursive) 4429219089Spjd{ 4430248571Smm int ret; 4431248571Smm struct holdarg ha; 4432251646Sdelphij nvlist_t *errors = NULL; 4433248571Smm nvpair_t *elem; 4434219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4435249357Smm char errbuf[1024]; 4436219089Spjd 4437248571Smm ha.nvl = fnvlist_alloc(); 4438248571Smm ha.snapname = snapname; 4439248571Smm ha.tag = tag; 4440248571Smm ha.recursive = recursive; 4441252219Sdelphij ha.error = 0; 4442248571Smm (void) zfs_release_one(zfs_handle_dup(zhp), &ha); 4443249357Smm 4444251646Sdelphij if (nvlist_empty(ha.nvl)) { 4445249357Smm fnvlist_free(ha.nvl); 4446252219Sdelphij ret = ha.error; 4447249357Smm (void) snprintf(errbuf, sizeof (errbuf), 4448249357Smm dgettext(TEXT_DOMAIN, 4449249357Smm "cannot release hold from snapshot '%s@%s'"), 4450249357Smm zhp->zfs_name, snapname); 4451252219Sdelphij if (ret == ESRCH) { 4452252219Sdelphij (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf); 4453252219Sdelphij } else { 4454252219Sdelphij (void) zfs_standard_error(hdl, ret, errbuf); 4455252219Sdelphij } 4456249357Smm return (ret); 4457249357Smm } 4458249357Smm 4459248571Smm ret = lzc_release(ha.nvl, &errors); 4460248571Smm fnvlist_free(ha.nvl); 4461219089Spjd 4462251646Sdelphij if (ret == 0) { 4463251646Sdelphij /* There may be errors even in the success case. */ 4464251646Sdelphij fnvlist_free(errors); 4465248571Smm return (0); 4466251646Sdelphij } 4467219089Spjd 4468251646Sdelphij if (nvlist_empty(errors)) { 4469248571Smm /* no hold-specific errors */ 4470219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 4471248571Smm "cannot release")); 4472219089Spjd switch (errno) { 4473219089Spjd case ENOTSUP: 4474219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4475219089Spjd "pool must be upgraded")); 4476248571Smm (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 4477248571Smm break; 4478248571Smm default: 4479248571Smm (void) zfs_standard_error_fmt(hdl, errno, errbuf); 4480248571Smm } 4481248571Smm } 4482248571Smm 4483248571Smm for (elem = nvlist_next_nvpair(errors, NULL); 4484248571Smm elem != NULL; 4485248571Smm elem = nvlist_next_nvpair(errors, elem)) { 4486248571Smm (void) snprintf(errbuf, sizeof (errbuf), 4487248571Smm dgettext(TEXT_DOMAIN, 4488248571Smm "cannot release hold from snapshot '%s'"), 4489248571Smm nvpair_name(elem)); 4490248571Smm switch (fnvpair_value_int32(elem)) { 4491248571Smm case ESRCH: 4492248571Smm (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf); 4493248571Smm break; 4494219089Spjd case EINVAL: 4495248571Smm (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 4496248571Smm break; 4497219089Spjd default: 4498248571Smm (void) zfs_standard_error_fmt(hdl, 4499248571Smm fnvpair_value_int32(elem), errbuf); 4500219089Spjd } 4501219089Spjd } 4502219089Spjd 4503248571Smm fnvlist_free(errors); 4504248571Smm return (ret); 4505219089Spjd} 4506219089Spjd 4507219089Spjdint 4508219089Spjdzfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl) 4509219089Spjd{ 4510219089Spjd zfs_cmd_t zc = { 0 }; 4511219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4512219089Spjd int nvsz = 2048; 4513219089Spjd void *nvbuf; 4514219089Spjd int err = 0; 4515248571Smm char errbuf[1024]; 4516219089Spjd 4517219089Spjd assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 4518219089Spjd zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4519219089Spjd 4520219089Spjdtryagain: 4521219089Spjd 4522219089Spjd nvbuf = malloc(nvsz); 4523219089Spjd if (nvbuf == NULL) { 4524219089Spjd err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno))); 4525219089Spjd goto out; 4526219089Spjd } 4527219089Spjd 4528219089Spjd zc.zc_nvlist_dst_size = nvsz; 4529219089Spjd zc.zc_nvlist_dst = (uintptr_t)nvbuf; 4530219089Spjd 4531219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN); 4532219089Spjd 4533230514Smm if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) { 4534219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4535219089Spjd dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"), 4536219089Spjd zc.zc_name); 4537219089Spjd switch (errno) { 4538219089Spjd case ENOMEM: 4539219089Spjd free(nvbuf); 4540219089Spjd nvsz = zc.zc_nvlist_dst_size; 4541219089Spjd goto tryagain; 4542219089Spjd 4543219089Spjd case ENOTSUP: 4544219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4545219089Spjd "pool must be upgraded")); 4546219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4547219089Spjd break; 4548219089Spjd case EINVAL: 4549219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4550219089Spjd break; 4551219089Spjd case ENOENT: 4552219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4553219089Spjd break; 4554219089Spjd default: 4555219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4556219089Spjd break; 4557219089Spjd } 4558219089Spjd } else { 4559219089Spjd /* success */ 4560219089Spjd int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0); 4561219089Spjd if (rc) { 4562219089Spjd (void) snprintf(errbuf, sizeof (errbuf), dgettext( 4563219089Spjd TEXT_DOMAIN, "cannot get permissions on '%s'"), 4564219089Spjd zc.zc_name); 4565219089Spjd err = zfs_standard_error_fmt(hdl, rc, errbuf); 4566219089Spjd } 4567219089Spjd } 4568219089Spjd 4569219089Spjd free(nvbuf); 4570219089Spjdout: 4571219089Spjd return (err); 4572219089Spjd} 4573219089Spjd 4574219089Spjdint 4575219089Spjdzfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl) 4576219089Spjd{ 4577219089Spjd zfs_cmd_t zc = { 0 }; 4578219089Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4579219089Spjd char *nvbuf; 4580248571Smm char errbuf[1024]; 4581219089Spjd size_t nvsz; 4582219089Spjd int err; 4583219089Spjd 4584219089Spjd assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 4585219089Spjd zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4586219089Spjd 4587219089Spjd err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); 4588219089Spjd assert(err == 0); 4589219089Spjd 4590219089Spjd nvbuf = malloc(nvsz); 4591219089Spjd 4592219089Spjd err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); 4593219089Spjd assert(err == 0); 4594219089Spjd 4595219089Spjd zc.zc_nvlist_src_size = nvsz; 4596219089Spjd zc.zc_nvlist_src = (uintptr_t)nvbuf; 4597219089Spjd zc.zc_perm_action = un; 4598219089Spjd 4599219089Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4600219089Spjd 4601219089Spjd if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) { 4602219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4603219089Spjd dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"), 4604219089Spjd zc.zc_name); 4605219089Spjd switch (errno) { 4606219089Spjd case ENOTSUP: 4607219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4608219089Spjd "pool must be upgraded")); 4609219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4610219089Spjd break; 4611219089Spjd case EINVAL: 4612219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4613219089Spjd break; 4614219089Spjd case ENOENT: 4615219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4616219089Spjd break; 4617219089Spjd default: 4618219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4619219089Spjd break; 4620219089Spjd } 4621219089Spjd } 4622219089Spjd 4623219089Spjd free(nvbuf); 4624219089Spjd 4625219089Spjd return (err); 4626219089Spjd} 4627219089Spjd 4628219089Spjdint 4629219089Spjdzfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl) 4630219089Spjd{ 4631248571Smm int err; 4632248571Smm char errbuf[1024]; 4633219089Spjd 4634248571Smm err = lzc_get_holds(zhp->zfs_name, nvl); 4635219089Spjd 4636248571Smm if (err != 0) { 4637248571Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 4638219089Spjd 4639219089Spjd (void) snprintf(errbuf, sizeof (errbuf), 4640219089Spjd dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"), 4641248571Smm zhp->zfs_name); 4642248571Smm switch (err) { 4643219089Spjd case ENOTSUP: 4644219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4645219089Spjd "pool must be upgraded")); 4646219089Spjd err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 4647219089Spjd break; 4648219089Spjd case EINVAL: 4649219089Spjd err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 4650219089Spjd break; 4651219089Spjd case ENOENT: 4652219089Spjd err = zfs_error(hdl, EZFS_NOENT, errbuf); 4653219089Spjd break; 4654219089Spjd default: 4655219089Spjd err = zfs_standard_error_fmt(hdl, errno, errbuf); 4656219089Spjd break; 4657219089Spjd } 4658219089Spjd } 4659219089Spjd 4660219089Spjd return (err); 4661219089Spjd} 4662219089Spjd 4663251629Sdelphij/* 4664251629Sdelphij * Convert the zvol's volume size to an appropriate reservation. 4665251629Sdelphij * Note: If this routine is updated, it is necessary to update the ZFS test 4666251629Sdelphij * suite's shell version in reservation.kshlib. 4667251629Sdelphij */ 4668219089Spjduint64_t 4669219089Spjdzvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 4670219089Spjd{ 4671219089Spjd uint64_t numdb; 4672219089Spjd uint64_t nblocks, volblocksize; 4673219089Spjd int ncopies; 4674219089Spjd char *strval; 4675219089Spjd 4676219089Spjd if (nvlist_lookup_string(props, 4677219089Spjd zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 4678219089Spjd ncopies = atoi(strval); 4679219089Spjd else 4680219089Spjd ncopies = 1; 4681219089Spjd if (nvlist_lookup_uint64(props, 4682219089Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4683219089Spjd &volblocksize) != 0) 4684219089Spjd volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 4685219089Spjd nblocks = volsize/volblocksize; 4686219089Spjd /* start with metadnode L0-L6 */ 4687219089Spjd numdb = 7; 4688219089Spjd /* calculate number of indirects */ 4689219089Spjd while (nblocks > 1) { 4690219089Spjd nblocks += DNODES_PER_LEVEL - 1; 4691219089Spjd nblocks /= DNODES_PER_LEVEL; 4692219089Spjd numdb += nblocks; 4693219089Spjd } 4694219089Spjd numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 4695219089Spjd volsize *= ncopies; 4696219089Spjd /* 4697219089Spjd * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 4698219089Spjd * compressed, but in practice they compress down to about 4699219089Spjd * 1100 bytes 4700219089Spjd */ 4701219089Spjd numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 4702219089Spjd volsize += numdb; 4703219089Spjd return (volsize); 4704219089Spjd} 4705219089Spjd 4706168404Spjd/* 4707168404Spjd * Attach/detach the given filesystem to/from the given jail. 4708168404Spjd */ 4709168404Spjdint 4710168404Spjdzfs_jail(zfs_handle_t *zhp, int jailid, int attach) 4711168404Spjd{ 4712168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 4713168404Spjd zfs_cmd_t zc = { 0 }; 4714168404Spjd char errbuf[1024]; 4715224525Smm unsigned long cmd; 4716224525Smm int ret; 4717168404Spjd 4718168404Spjd if (attach) { 4719168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4720168404Spjd dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name); 4721168404Spjd } else { 4722168404Spjd (void) snprintf(errbuf, sizeof (errbuf), 4723249547Spjd dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name); 4724168404Spjd } 4725168404Spjd 4726168404Spjd switch (zhp->zfs_type) { 4727168404Spjd case ZFS_TYPE_VOLUME: 4728168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4729168404Spjd "volumes can not be jailed")); 4730168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4731168404Spjd case ZFS_TYPE_SNAPSHOT: 4732168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4733168404Spjd "snapshots can not be jailed")); 4734168404Spjd return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4735168404Spjd } 4736168404Spjd assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 4737168404Spjd 4738168404Spjd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4739168404Spjd zc.zc_objset_type = DMU_OST_ZFS; 4740168404Spjd zc.zc_jailid = jailid; 4741168404Spjd 4742168404Spjd cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL; 4743168404Spjd if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0) 4744168404Spjd zfs_standard_error(hdl, errno, errbuf); 4745168404Spjd 4746168404Spjd return (ret); 4747168404Spjd} 4748