Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_dataset.c
48378 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.24* Copyright 2019 Joyent, Inc.25* Copyright (c) 2011, 2020 by Delphix. All rights reserved.26* Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.27* Copyright (c) 2012 Pawel Jakub Dawidek <[email protected]>.28* Copyright (c) 2013 Martin Matuska. All rights reserved.29* Copyright (c) 2013 Steven Hartland. All rights reserved.30* Copyright 2017 Nexenta Systems, Inc.31* Copyright 2016 Igor Kozhukhov <[email protected]>32* Copyright 2017-2018 RackTop Systems.33* Copyright (c) 2019 Datto Inc.34* Copyright (c) 2019, loli10K <[email protected]>35* Copyright (c) 2021 Matt Fiddaman36*/3738#include <ctype.h>39#include <errno.h>40#include <libintl.h>41#include <stdio.h>42#include <stdlib.h>43#include <strings.h>44#include <unistd.h>45#include <stddef.h>46#include <zone.h>47#include <fcntl.h>48#include <sys/mntent.h>49#include <sys/mount.h>50#include <pwd.h>51#include <grp.h>52#ifdef HAVE_IDMAP53#include <idmap.h>54#include <aclutils.h>55#include <directory.h>56#endif /* HAVE_IDMAP */5758#include <sys/dnode.h>59#include <sys/spa.h>60#include <sys/zap.h>61#include <sys/dsl_crypt.h>62#include <libzfs.h>63#include <libzutil.h>6465#include "zfs_namecheck.h"66#include "zfs_prop.h"67#include "libzfs_impl.h"68#include "zfs_deleg.h"6970static __thread struct passwd gpwd;71static __thread struct group ggrp;72static __thread char rpbuf[2048];7374static int userquota_propname_decode(const char *propname, boolean_t zoned,75zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);7677/*78* Given a single type (not a mask of types), return the type in a human79* readable form.80*/81const char *82zfs_type_to_name(zfs_type_t type)83{84switch (type) {85case ZFS_TYPE_FILESYSTEM:86return (dgettext(TEXT_DOMAIN, "filesystem"));87case ZFS_TYPE_SNAPSHOT:88return (dgettext(TEXT_DOMAIN, "snapshot"));89case ZFS_TYPE_VOLUME:90return (dgettext(TEXT_DOMAIN, "volume"));91case ZFS_TYPE_POOL:92return (dgettext(TEXT_DOMAIN, "pool"));93case ZFS_TYPE_BOOKMARK:94return (dgettext(TEXT_DOMAIN, "bookmark"));95default:96assert(!"unhandled zfs_type_t");97}9899return (NULL);100}101102/*103* Validate a ZFS path. This is used even before trying to open the dataset, to104* provide a more meaningful error message. We call zfs_error_aux() to105* explain exactly why the name was not valid.106*/107int108zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,109boolean_t modifying)110{111namecheck_err_t why;112char what;113114if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {115if (hdl != NULL)116zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,117"snapshot delimiter '@' is not expected here"));118return (0);119}120121if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {122if (hdl != NULL)123zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,124"missing '@' delimiter in snapshot name"));125return (0);126}127128if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) {129if (hdl != NULL)130zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,131"bookmark delimiter '#' is not expected here"));132return (0);133}134135if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) {136if (hdl != NULL)137zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,138"missing '#' delimiter in bookmark name"));139return (0);140}141142if (modifying && strchr(path, '%') != NULL) {143if (hdl != NULL)144zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,145"invalid character %c in name"), '%');146return (0);147}148149if (entity_namecheck(path, &why, &what) != 0) {150if (hdl != NULL) {151switch (why) {152case NAME_ERR_TOOLONG:153zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,154"name is too long"));155break;156157case NAME_ERR_LEADING_SLASH:158zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,159"leading slash in name"));160break;161162case NAME_ERR_EMPTY_COMPONENT:163zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,164"empty component or misplaced '@'"165" or '#' delimiter in name"));166break;167168case NAME_ERR_TRAILING_SLASH:169zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,170"trailing slash in name"));171break;172173case NAME_ERR_INVALCHAR:174zfs_error_aux(hdl,175dgettext(TEXT_DOMAIN, "invalid character "176"'%c' in name"), what);177break;178179case NAME_ERR_MULTIPLE_DELIMITERS:180zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,181"multiple '@' and/or '#' delimiters in "182"name"));183break;184185case NAME_ERR_NOLETTER:186zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,187"pool doesn't begin with a letter"));188break;189190case NAME_ERR_RESERVED:191zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,192"name is reserved"));193break;194195case NAME_ERR_DISKLIKE:196zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,197"reserved disk name"));198break;199200case NAME_ERR_SELF_REF:201zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,202"self reference, '.' is found in name"));203break;204205case NAME_ERR_PARENT_REF:206zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,207"parent reference, '..' is found in name"));208break;209210default:211zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,212"(%d) not defined"), why);213break;214}215}216217return (0);218}219220return (-1);221}222223int224zfs_name_valid(const char *name, zfs_type_t type)225{226if (type == ZFS_TYPE_POOL)227return (zpool_name_valid(NULL, B_FALSE, name));228return (zfs_validate_name(NULL, name, type, B_FALSE));229}230231/*232* This function takes the raw DSL properties, and filters out the user-defined233* properties into a separate nvlist.234*/235static nvlist_t *236process_user_props(zfs_handle_t *zhp, nvlist_t *props)237{238libzfs_handle_t *hdl = zhp->zfs_hdl;239nvpair_t *elem;240nvlist_t *nvl;241242if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {243(void) no_memory(hdl);244return (NULL);245}246247elem = NULL;248while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {249if (!zfs_prop_user(nvpair_name(elem)))250continue;251252nvlist_t *propval = fnvpair_value_nvlist(elem);253if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {254nvlist_free(nvl);255(void) no_memory(hdl);256return (NULL);257}258}259260return (nvl);261}262263static zpool_handle_t *264zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)265{266libzfs_handle_t *hdl = zhp->zfs_hdl;267zpool_handle_t *zph;268269if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {270if (hdl->libzfs_pool_handles != NULL)271zph->zpool_next = hdl->libzfs_pool_handles;272hdl->libzfs_pool_handles = zph;273}274return (zph);275}276277static zpool_handle_t *278zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)279{280libzfs_handle_t *hdl = zhp->zfs_hdl;281zpool_handle_t *zph = hdl->libzfs_pool_handles;282283while ((zph != NULL) &&284(strncmp(pool_name, zpool_get_name(zph), len) != 0))285zph = zph->zpool_next;286return (zph);287}288289/*290* Returns a handle to the pool that contains the provided dataset.291* If a handle to that pool already exists then that handle is returned.292* Otherwise, a new handle is created and added to the list of handles.293*/294static zpool_handle_t *295zpool_handle(zfs_handle_t *zhp)296{297char *pool_name;298int len;299zpool_handle_t *zph;300301len = strcspn(zhp->zfs_name, "/@#") + 1;302pool_name = zfs_alloc(zhp->zfs_hdl, len);303(void) strlcpy(pool_name, zhp->zfs_name, len);304305zph = zpool_find_handle(zhp, pool_name, len);306if (zph == NULL)307zph = zpool_add_handle(zhp, pool_name);308309free(pool_name);310return (zph);311}312313void314zpool_free_handles(libzfs_handle_t *hdl)315{316zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;317318while (zph != NULL) {319next = zph->zpool_next;320zpool_close(zph);321zph = next;322}323hdl->libzfs_pool_handles = NULL;324}325326/*327* Utility function to gather stats (objset and zpl) for the given object.328*/329static int330get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)331{332libzfs_handle_t *hdl = zhp->zfs_hdl;333334(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));335336while (zfs_ioctl(hdl, ZFS_IOC_OBJSET_STATS, zc) != 0) {337if (errno == ENOMEM)338zcmd_expand_dst_nvlist(hdl, zc);339else340return (-1);341}342return (0);343}344345/*346* Utility function to get the received properties of the given object.347*/348static int349get_recvd_props_ioctl(zfs_handle_t *zhp)350{351libzfs_handle_t *hdl = zhp->zfs_hdl;352nvlist_t *recvdprops;353zfs_cmd_t zc = {"\0"};354int err;355356zcmd_alloc_dst_nvlist(hdl, &zc, 0);357358(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));359360while (zfs_ioctl(hdl, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {361if (errno == ENOMEM)362zcmd_expand_dst_nvlist(hdl, &zc);363else {364zcmd_free_nvlists(&zc);365return (-1);366}367}368369err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);370zcmd_free_nvlists(&zc);371if (err != 0)372return (-1);373374nvlist_free(zhp->zfs_recvd_props);375zhp->zfs_recvd_props = recvdprops;376377return (0);378}379380static int381put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)382{383nvlist_t *allprops, *userprops;384385zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */386387if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {388return (-1);389}390391/*392* XXX Why do we store the user props separately, in addition to393* storing them in zfs_props?394*/395if ((userprops = process_user_props(zhp, allprops)) == NULL) {396nvlist_free(allprops);397return (-1);398}399400nvlist_free(zhp->zfs_props);401nvlist_free(zhp->zfs_user_props);402403zhp->zfs_props = allprops;404zhp->zfs_user_props = userprops;405406return (0);407}408409static int410get_stats(zfs_handle_t *zhp)411{412int rc = 0;413zfs_cmd_t zc = {"\0"};414415zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0);416417if (get_stats_ioctl(zhp, &zc) != 0)418rc = -1;419else if (put_stats_zhdl(zhp, &zc) != 0)420rc = -1;421zcmd_free_nvlists(&zc);422return (rc);423}424425/*426* Refresh the properties currently stored in the handle.427*/428void429zfs_refresh_properties(zfs_handle_t *zhp)430{431(void) get_stats(zhp);432}433434/*435* Makes a handle from the given dataset name. Used by zfs_open() and436* zfs_iter_* to create child handles on the fly.437*/438static int439make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)440{441if (put_stats_zhdl(zhp, zc) != 0)442return (-1);443444/*445* We've managed to open the dataset and gather statistics. Determine446* the high-level type.447*/448if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) {449zhp->zfs_head_type = ZFS_TYPE_VOLUME;450} else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) {451zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;452} else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER) {453errno = EINVAL;454return (-1);455} else if (zhp->zfs_dmustats.dds_inconsistent) {456errno = EBUSY;457return (-1);458} else {459abort();460}461462if (zhp->zfs_dmustats.dds_is_snapshot)463zhp->zfs_type = ZFS_TYPE_SNAPSHOT;464else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)465zhp->zfs_type = ZFS_TYPE_VOLUME;466else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)467zhp->zfs_type = ZFS_TYPE_FILESYSTEM;468else469abort(); /* we should never see any other types */470471if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL)472return (-1);473474return (0);475}476477zfs_handle_t *478make_dataset_handle(libzfs_handle_t *hdl, const char *path)479{480zfs_cmd_t zc = {"\0"};481482zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));483484if (zhp == NULL)485return (NULL);486487zhp->zfs_hdl = hdl;488(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));489zcmd_alloc_dst_nvlist(hdl, &zc, 0);490491if (get_stats_ioctl(zhp, &zc) == -1) {492zcmd_free_nvlists(&zc);493free(zhp);494return (NULL);495}496if (make_dataset_handle_common(zhp, &zc) == -1) {497free(zhp);498zhp = NULL;499}500zcmd_free_nvlists(&zc);501return (zhp);502}503504zfs_handle_t *505make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)506{507zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));508509if (zhp == NULL)510return (NULL);511512zhp->zfs_hdl = hdl;513(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));514if (make_dataset_handle_common(zhp, zc) == -1) {515free(zhp);516return (NULL);517}518return (zhp);519}520521zfs_handle_t *522make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)523{524zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));525526if (zhp == NULL)527return (NULL);528529zhp->zfs_hdl = pzhp->zfs_hdl;530(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));531zhp->zfs_head_type = pzhp->zfs_type;532zhp->zfs_type = ZFS_TYPE_SNAPSHOT;533zhp->zpool_hdl = zpool_handle(zhp);534535if (zc->zc_objset_stats.dds_creation_txg != 0) {536/* structure assignment */537zhp->zfs_dmustats = zc->zc_objset_stats;538} else {539if (get_stats_ioctl(zhp, zc) == -1) {540zcmd_free_nvlists(zc);541free(zhp);542return (NULL);543}544if (make_dataset_handle_common(zhp, zc) == -1) {545zcmd_free_nvlists(zc);546free(zhp);547return (NULL);548}549}550551if (zhp->zfs_dmustats.dds_is_snapshot ||552strchr(zc->zc_name, '@') != NULL)553zhp->zfs_type = ZFS_TYPE_SNAPSHOT;554else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)555zhp->zfs_type = ZFS_TYPE_VOLUME;556else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)557zhp->zfs_type = ZFS_TYPE_FILESYSTEM;558559return (zhp);560}561562zfs_handle_t *563zfs_handle_dup(zfs_handle_t *zhp_orig)564{565zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));566567if (zhp == NULL)568return (NULL);569570zhp->zfs_hdl = zhp_orig->zfs_hdl;571zhp->zpool_hdl = zhp_orig->zpool_hdl;572(void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,573sizeof (zhp->zfs_name));574zhp->zfs_type = zhp_orig->zfs_type;575zhp->zfs_head_type = zhp_orig->zfs_head_type;576zhp->zfs_dmustats = zhp_orig->zfs_dmustats;577if (zhp_orig->zfs_props != NULL) {578if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {579(void) no_memory(zhp->zfs_hdl);580zfs_close(zhp);581return (NULL);582}583}584if (zhp_orig->zfs_user_props != NULL) {585if (nvlist_dup(zhp_orig->zfs_user_props,586&zhp->zfs_user_props, 0) != 0) {587(void) no_memory(zhp->zfs_hdl);588zfs_close(zhp);589return (NULL);590}591}592if (zhp_orig->zfs_recvd_props != NULL) {593if (nvlist_dup(zhp_orig->zfs_recvd_props,594&zhp->zfs_recvd_props, 0)) {595(void) no_memory(zhp->zfs_hdl);596zfs_close(zhp);597return (NULL);598}599}600zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;601if (zhp_orig->zfs_mntopts != NULL) {602zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,603zhp_orig->zfs_mntopts);604}605zhp->zfs_props_table = zhp_orig->zfs_props_table;606return (zhp);607}608609boolean_t610zfs_bookmark_exists(const char *path)611{612nvlist_t *bmarks;613nvlist_t *props;614char fsname[ZFS_MAX_DATASET_NAME_LEN];615char *bmark_name;616char *pound;617int err;618boolean_t rv;619620(void) strlcpy(fsname, path, sizeof (fsname));621pound = strchr(fsname, '#');622if (pound == NULL)623return (B_FALSE);624625*pound = '\0';626bmark_name = pound + 1;627props = fnvlist_alloc();628err = lzc_get_bookmarks(fsname, props, &bmarks);629nvlist_free(props);630if (err != 0) {631nvlist_free(bmarks);632return (B_FALSE);633}634635rv = nvlist_exists(bmarks, bmark_name);636nvlist_free(bmarks);637return (rv);638}639640zfs_handle_t *641make_bookmark_handle(zfs_handle_t *parent, const char *path,642nvlist_t *bmark_props)643{644zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));645646if (zhp == NULL)647return (NULL);648649/* Fill in the name. */650zhp->zfs_hdl = parent->zfs_hdl;651(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));652653/* Set the property lists. */654if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) {655free(zhp);656return (NULL);657}658659/* Set the types. */660zhp->zfs_head_type = parent->zfs_head_type;661zhp->zfs_type = ZFS_TYPE_BOOKMARK;662663if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) {664nvlist_free(zhp->zfs_props);665free(zhp);666return (NULL);667}668669return (zhp);670}671672struct zfs_open_bookmarks_cb_data {673const char *path;674zfs_handle_t *zhp;675};676677static int678zfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data)679{680struct zfs_open_bookmarks_cb_data *dp = data;681682/*683* Is it the one we are looking for?684*/685if (strcmp(dp->path, zfs_get_name(zhp)) == 0) {686/*687* We found it. Save it and let the caller know we are done.688*/689dp->zhp = zhp;690return (EEXIST);691}692693/*694* Not found. Close the handle and ask for another one.695*/696zfs_close(zhp);697return (0);698}699700/*701* Opens the given snapshot, bookmark, filesystem, or volume. The 'types'702* argument is a mask of acceptable types. The function will print an703* appropriate error message and return NULL if it can't be opened.704*/705zfs_handle_t *706zfs_open(libzfs_handle_t *hdl, const char *path, int types)707{708zfs_handle_t *zhp;709char errbuf[ERRBUFLEN];710char *bookp;711712(void) snprintf(errbuf, sizeof (errbuf),713dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);714715/*716* Validate the name before we even try to open it.717*/718if (!zfs_validate_name(hdl, path, types, B_FALSE)) {719(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);720errno = EINVAL;721return (NULL);722}723724/*725* Bookmarks needs to be handled separately.726*/727bookp = strchr(path, '#');728if (bookp == NULL) {729/*730* Try to get stats for the dataset, which will tell us if it731* exists.732*/733errno = 0;734if ((zhp = make_dataset_handle(hdl, path)) == NULL) {735(void) zfs_standard_error(hdl, errno, errbuf);736return (NULL);737}738} else {739char dsname[ZFS_MAX_DATASET_NAME_LEN];740zfs_handle_t *pzhp;741struct zfs_open_bookmarks_cb_data cb_data = {path, NULL};742743/*744* We need to cut out '#' and everything after '#'745* to get the parent dataset name only.746*/747assert(bookp - path < sizeof (dsname));748(void) strlcpy(dsname, path,749MIN(sizeof (dsname), bookp - path + 1));750751/*752* Create handle for the parent dataset.753*/754errno = 0;755if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) {756(void) zfs_standard_error(hdl, errno, errbuf);757return (NULL);758}759760/*761* Iterate bookmarks to find the right one.762*/763errno = 0;764if ((zfs_iter_bookmarks_v2(pzhp, 0, zfs_open_bookmarks_cb,765&cb_data) == 0) && (cb_data.zhp == NULL)) {766(void) zfs_error(hdl, EZFS_NOENT, errbuf);767zfs_close(pzhp);768errno = ENOENT;769return (NULL);770}771if (cb_data.zhp == NULL) {772(void) zfs_standard_error(hdl, errno, errbuf);773zfs_close(pzhp);774return (NULL);775}776zhp = cb_data.zhp;777778/*779* Cleanup.780*/781zfs_close(pzhp);782}783784if (!(types & zhp->zfs_type)) {785(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);786zfs_close(zhp);787errno = EINVAL;788return (NULL);789}790791return (zhp);792}793794/*795* Release a ZFS handle. Nothing to do but free the associated memory.796*/797void798zfs_close(zfs_handle_t *zhp)799{800if (zhp->zfs_mntopts)801free(zhp->zfs_mntopts);802nvlist_free(zhp->zfs_props);803nvlist_free(zhp->zfs_user_props);804nvlist_free(zhp->zfs_recvd_props);805free(zhp);806}807808typedef struct mnttab_node {809struct mnttab mtn_mt;810avl_node_t mtn_node;811} mnttab_node_t;812813static int814libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)815{816const mnttab_node_t *mtn1 = (const mnttab_node_t *)arg1;817const mnttab_node_t *mtn2 = (const mnttab_node_t *)arg2;818int rv;819820rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);821822return (TREE_ISIGN(rv));823}824825void826libzfs_mnttab_init(libzfs_handle_t *hdl)827{828pthread_mutex_init(&hdl->libzfs_mnttab_cache_lock, NULL);829assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);830avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,831sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));832}833834static int835libzfs_mnttab_update(libzfs_handle_t *hdl)836{837FILE *mnttab;838struct mnttab entry;839840if ((mnttab = fopen(MNTTAB, "re")) == NULL)841return (ENOENT);842843while (getmntent(mnttab, &entry) == 0) {844mnttab_node_t *mtn;845avl_index_t where;846847if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)848continue;849850mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));851mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);852mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);853mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);854mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);855856/* Exclude duplicate mounts */857if (avl_find(&hdl->libzfs_mnttab_cache, mtn, &where) != NULL) {858free(mtn->mtn_mt.mnt_special);859free(mtn->mtn_mt.mnt_mountp);860free(mtn->mtn_mt.mnt_fstype);861free(mtn->mtn_mt.mnt_mntopts);862free(mtn);863continue;864}865866avl_add(&hdl->libzfs_mnttab_cache, mtn);867}868869(void) fclose(mnttab);870return (0);871}872873void874libzfs_mnttab_fini(libzfs_handle_t *hdl)875{876void *cookie = NULL;877mnttab_node_t *mtn;878879while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))880!= NULL) {881free(mtn->mtn_mt.mnt_special);882free(mtn->mtn_mt.mnt_mountp);883free(mtn->mtn_mt.mnt_fstype);884free(mtn->mtn_mt.mnt_mntopts);885free(mtn);886}887avl_destroy(&hdl->libzfs_mnttab_cache);888(void) pthread_mutex_destroy(&hdl->libzfs_mnttab_cache_lock);889}890891void892libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)893{894hdl->libzfs_mnttab_enable = enable;895}896897int898libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,899struct mnttab *entry)900{901FILE *mnttab;902mnttab_node_t find;903mnttab_node_t *mtn;904int ret = ENOENT;905906if (!hdl->libzfs_mnttab_enable) {907struct mnttab srch = { 0 };908909if (avl_numnodes(&hdl->libzfs_mnttab_cache))910libzfs_mnttab_fini(hdl);911912if ((mnttab = fopen(MNTTAB, "re")) == NULL)913return (ENOENT);914915srch.mnt_special = (char *)fsname;916srch.mnt_fstype = (char *)MNTTYPE_ZFS;917ret = getmntany(mnttab, entry, &srch) ? ENOENT : 0;918(void) fclose(mnttab);919return (ret);920}921922pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);923if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) {924int error;925926if ((error = libzfs_mnttab_update(hdl)) != 0) {927pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);928return (error);929}930}931932find.mtn_mt.mnt_special = (char *)fsname;933mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);934if (mtn) {935*entry = mtn->mtn_mt;936ret = 0;937}938pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);939return (ret);940}941942void943libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,944const char *mountp, const char *mntopts)945{946mnttab_node_t *mtn;947948pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);949if (avl_numnodes(&hdl->libzfs_mnttab_cache) != 0) {950mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));951mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);952mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);953mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);954mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);955/*956* Another thread may have already added this entry957* via libzfs_mnttab_update. If so we should skip it.958*/959if (avl_find(&hdl->libzfs_mnttab_cache, mtn, NULL) != NULL) {960free(mtn->mtn_mt.mnt_special);961free(mtn->mtn_mt.mnt_mountp);962free(mtn->mtn_mt.mnt_fstype);963free(mtn->mtn_mt.mnt_mntopts);964free(mtn);965} else {966avl_add(&hdl->libzfs_mnttab_cache, mtn);967}968}969pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);970}971972void973libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)974{975mnttab_node_t find;976mnttab_node_t *ret;977978pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);979find.mtn_mt.mnt_special = (char *)fsname;980if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))981!= NULL) {982avl_remove(&hdl->libzfs_mnttab_cache, ret);983free(ret->mtn_mt.mnt_special);984free(ret->mtn_mt.mnt_mountp);985free(ret->mtn_mt.mnt_fstype);986free(ret->mtn_mt.mnt_mntopts);987free(ret);988}989pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);990}991992int993zfs_spa_version(zfs_handle_t *zhp, int *spa_version)994{995zpool_handle_t *zpool_handle = zhp->zpool_hdl;996997if (zpool_handle == NULL)998return (-1);9991000*spa_version = zpool_get_prop_int(zpool_handle,1001ZPOOL_PROP_VERSION, NULL);1002return (0);1003}10041005/*1006* The choice of reservation property depends on the SPA version.1007*/1008static int1009zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)1010{1011int spa_version;10121013if (zfs_spa_version(zhp, &spa_version) < 0)1014return (-1);10151016if (spa_version >= SPA_VERSION_REFRESERVATION)1017*resv_prop = ZFS_PROP_REFRESERVATION;1018else1019*resv_prop = ZFS_PROP_RESERVATION;10201021return (0);1022}10231024/*1025* Given an nvlist of properties to set, validates that they are correct, and1026* parses any numeric properties (index, boolean, etc) if they are specified as1027* strings.1028*/1029nvlist_t *1030zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,1031uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,1032boolean_t key_params_ok, const char *errbuf)1033{1034nvpair_t *elem;1035uint64_t intval;1036const char *strval;1037zfs_prop_t prop;1038nvlist_t *ret;1039int chosen_normal = -1;1040int chosen_utf = -1;10411042if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {1043(void) no_memory(hdl);1044return (NULL);1045}10461047/*1048* Make sure this property is valid and applies to this type.1049*/10501051elem = NULL;1052while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {1053const char *propname = nvpair_name(elem);10541055prop = zfs_name_to_prop(propname);1056if (prop == ZPROP_USERPROP && zfs_prop_user(propname)) {1057/*1058* This is a user property: make sure it's a1059* string, and that it's less than ZAP_MAXNAMELEN.1060*/1061if (nvpair_type(elem) != DATA_TYPE_STRING) {1062zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1063"'%s' must be a string"), propname);1064(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1065goto error;1066}10671068if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {1069zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1070"property name '%s' is too long"),1071propname);1072(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1073goto error;1074}10751076(void) nvpair_value_string(elem, &strval);1077if (nvlist_add_string(ret, propname, strval) != 0) {1078(void) no_memory(hdl);1079goto error;1080}1081continue;1082}10831084/*1085* Currently, only user properties can be modified on1086* snapshots.1087*/1088if (type == ZFS_TYPE_SNAPSHOT) {1089zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1090"this property can not be modified for snapshots"));1091(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);1092goto error;1093}10941095if (prop == ZPROP_USERPROP && zfs_prop_userquota(propname)) {1096zfs_userquota_prop_t uqtype;1097char *newpropname = NULL;1098char domain[128];1099uint64_t rid;1100uint64_t valary[3];1101int rc;11021103if (userquota_propname_decode(propname, zoned,1104&uqtype, domain, sizeof (domain), &rid) != 0) {1105zfs_error_aux(hdl,1106dgettext(TEXT_DOMAIN,1107"'%s' has an invalid user/group name"),1108propname);1109(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1110goto error;1111}11121113if (uqtype != ZFS_PROP_USERQUOTA &&1114uqtype != ZFS_PROP_GROUPQUOTA &&1115uqtype != ZFS_PROP_USEROBJQUOTA &&1116uqtype != ZFS_PROP_GROUPOBJQUOTA &&1117uqtype != ZFS_PROP_PROJECTQUOTA &&1118uqtype != ZFS_PROP_PROJECTOBJQUOTA) {1119zfs_error_aux(hdl,1120dgettext(TEXT_DOMAIN, "'%s' is readonly"),1121propname);1122(void) zfs_error(hdl, EZFS_PROPREADONLY,1123errbuf);1124goto error;1125}11261127if (nvpair_type(elem) == DATA_TYPE_STRING) {1128(void) nvpair_value_string(elem, &strval);1129if (strcmp(strval, "none") == 0) {1130intval = 0;1131} else if (zfs_nicestrtonum(hdl,1132strval, &intval) != 0) {1133(void) zfs_error(hdl,1134EZFS_BADPROP, errbuf);1135goto error;1136}1137} else if (nvpair_type(elem) ==1138DATA_TYPE_UINT64) {1139(void) nvpair_value_uint64(elem, &intval);1140if (intval == 0) {1141zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1142"use 'none' to disable "1143"{user|group|project}quota"));1144goto error;1145}1146} else {1147zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1148"'%s' must be a number"), propname);1149(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1150goto error;1151}11521153/*1154* Encode the prop name as1155* userquota@<hex-rid>-domain, to make it easy1156* for the kernel to decode.1157*/1158rc = asprintf(&newpropname, "%s%llx-%s",1159zfs_userquota_prop_prefixes[uqtype],1160(longlong_t)rid, domain);1161if (rc == -1 || newpropname == NULL) {1162(void) no_memory(hdl);1163goto error;1164}11651166valary[0] = uqtype;1167valary[1] = rid;1168valary[2] = intval;1169if (nvlist_add_uint64_array(ret, newpropname,1170valary, 3) != 0) {1171free(newpropname);1172(void) no_memory(hdl);1173goto error;1174}1175free(newpropname);1176continue;1177} else if (prop == ZPROP_USERPROP &&1178zfs_prop_written(propname)) {1179zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1180"'%s' is readonly"),1181propname);1182(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);1183goto error;1184}11851186if (prop == ZPROP_INVAL) {1187zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1188"invalid property '%s'"), propname);1189(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1190goto error;1191}11921193if (!zfs_prop_valid_for_type(prop, type, B_FALSE)) {1194zfs_error_aux(hdl,1195dgettext(TEXT_DOMAIN, "'%s' does not "1196"apply to datasets of this type"), propname);1197(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);1198goto error;1199}12001201if (zfs_prop_readonly(prop) &&1202!(zfs_prop_setonce(prop) && zhp == NULL) &&1203!(zfs_prop_encryption_key_param(prop) && key_params_ok)) {1204zfs_error_aux(hdl,1205dgettext(TEXT_DOMAIN, "'%s' is readonly"),1206propname);1207(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);1208goto error;1209}12101211if (zprop_parse_value(hdl, elem, prop, type, ret,1212&strval, &intval, errbuf) != 0)1213goto error;12141215/*1216* Perform some additional checks for specific properties.1217*/1218switch (prop) {1219case ZFS_PROP_VERSION:1220{1221int version;12221223if (zhp == NULL)1224break;1225version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);1226if (intval < version) {1227zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1228"Can not downgrade; already at version %u"),1229version);1230(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1231goto error;1232}1233break;1234}12351236case ZFS_PROP_VOLBLOCKSIZE:1237case ZFS_PROP_RECORDSIZE:1238{1239int maxbs = SPA_MAXBLOCKSIZE;1240char buf[64];12411242if (zpool_hdl != NULL) {1243maxbs = zpool_get_prop_int(zpool_hdl,1244ZPOOL_PROP_MAXBLOCKSIZE, NULL);1245}1246/*1247* The value must be a power of two between1248* SPA_MINBLOCKSIZE and maxbs.1249*/1250if (intval < SPA_MINBLOCKSIZE ||1251intval > maxbs || !ISP2(intval)) {1252zfs_nicebytes(maxbs, buf, sizeof (buf));1253zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1254"'%s' must be power of 2 from 512B "1255"to %s"), propname, buf);1256(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1257goto error;1258}1259break;1260}12611262case ZFS_PROP_SPECIAL_SMALL_BLOCKS:1263{1264int maxbs = SPA_MAXBLOCKSIZE;1265char buf[64];12661267if (intval > SPA_MAXBLOCKSIZE) {1268zfs_nicebytes(maxbs, buf, sizeof (buf));1269zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1270"invalid '%s' property: must be between "1271"zero and %s"),1272propname, buf);1273(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1274goto error;1275}1276break;1277}12781279case ZFS_PROP_MLSLABEL:1280{1281#ifdef HAVE_MLSLABEL1282/*1283* Verify the mlslabel string and convert to1284* internal hex label string.1285*/12861287m_label_t *new_sl;1288char *hex = NULL; /* internal label string */12891290/* Default value is already OK. */1291if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)1292break;12931294/* Verify the label can be converted to binary form */1295if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||1296(str_to_label(strval, &new_sl, MAC_LABEL,1297L_NO_CORRECTION, NULL) == -1)) {1298goto badlabel;1299}13001301/* Now translate to hex internal label string */1302if (label_to_str(new_sl, &hex, M_INTERNAL,1303DEF_NAMES) != 0) {1304if (hex)1305free(hex);1306goto badlabel;1307}1308m_label_free(new_sl);13091310/* If string is already in internal form, we're done. */1311if (strcmp(strval, hex) == 0) {1312free(hex);1313break;1314}13151316/* Replace the label string with the internal form. */1317(void) nvlist_remove(ret, zfs_prop_to_name(prop),1318DATA_TYPE_STRING);1319fnvlist_add_string(ret, zfs_prop_to_name(prop), hex);1320free(hex);13211322break;13231324badlabel:1325zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1326"invalid mlslabel '%s'"), strval);1327(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1328m_label_free(new_sl); /* OK if null */1329goto error;1330#else1331zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1332"mlslabels are unsupported"));1333(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1334goto error;1335#endif /* HAVE_MLSLABEL */1336}13371338case ZFS_PROP_MOUNTPOINT:1339{1340namecheck_err_t why;13411342if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||1343strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)1344break;13451346if (mountpoint_namecheck(strval, &why)) {1347switch (why) {1348case NAME_ERR_LEADING_SLASH:1349zfs_error_aux(hdl,1350dgettext(TEXT_DOMAIN,1351"'%s' must be an absolute path, "1352"'none', or 'legacy'"), propname);1353break;1354case NAME_ERR_TOOLONG:1355zfs_error_aux(hdl,1356dgettext(TEXT_DOMAIN,1357"component of '%s' is too long"),1358propname);1359break;13601361default:1362zfs_error_aux(hdl,1363dgettext(TEXT_DOMAIN,1364"(%d) not defined"),1365why);1366break;1367}1368(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1369goto error;1370}1371zfs_fallthrough;1372}13731374case ZFS_PROP_SHARESMB:1375case ZFS_PROP_SHARENFS:1376/*1377* For the mountpoint and sharenfs or sharesmb1378* properties, check if it can be set in a1379* global/non-global zone based on1380* the zoned property value:1381*1382* global zone non-global zone1383* --------------------------------------------------1384* zoned=on mountpoint (no) mountpoint (yes)1385* sharenfs (no) sharenfs (no)1386* sharesmb (no) sharesmb (no)1387*1388* zoned=off mountpoint (yes) N/A1389* sharenfs (yes)1390* sharesmb (yes)1391*/1392if (zoned) {1393if (getzoneid() == GLOBAL_ZONEID) {1394zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1395"'%s' cannot be set on "1396"dataset in a non-global zone"),1397propname);1398(void) zfs_error(hdl, EZFS_ZONED,1399errbuf);1400goto error;1401} else if (prop == ZFS_PROP_SHARENFS ||1402prop == ZFS_PROP_SHARESMB) {1403zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1404"'%s' cannot be set in "1405"a non-global zone"), propname);1406(void) zfs_error(hdl, EZFS_ZONED,1407errbuf);1408goto error;1409}1410} else if (getzoneid() != GLOBAL_ZONEID) {1411/*1412* If zoned property is 'off', this must be in1413* a global zone. If not, something is wrong.1414*/1415zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1416"'%s' cannot be set while dataset "1417"'zoned' property is set"), propname);1418(void) zfs_error(hdl, EZFS_ZONED, errbuf);1419goto error;1420}14211422/*1423* At this point, it is legitimate to set the1424* property. Now we want to make sure that the1425* property value is valid if it is sharenfs.1426*/1427if ((prop == ZFS_PROP_SHARENFS ||1428prop == ZFS_PROP_SHARESMB) &&1429strcmp(strval, "on") != 0 &&1430strcmp(strval, "off") != 0) {1431enum sa_protocol proto;14321433if (prop == ZFS_PROP_SHARESMB)1434proto = SA_PROTOCOL_SMB;1435else1436proto = SA_PROTOCOL_NFS;14371438if (sa_validate_shareopts(strval, proto) !=1439SA_OK) {1440zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1441"'%s' cannot be set to invalid "1442"options"), propname);1443(void) zfs_error(hdl, EZFS_BADPROP,1444errbuf);1445goto error;1446}1447}14481449break;14501451case ZFS_PROP_KEYLOCATION:1452if (!zfs_prop_valid_keylocation(strval, B_FALSE)) {1453zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1454"invalid keylocation"));1455(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1456goto error;1457}14581459if (zhp != NULL) {1460uint64_t crypt =1461zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);14621463if (crypt == ZIO_CRYPT_OFF &&1464strcmp(strval, "none") != 0) {1465zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1466"keylocation must be 'none' "1467"for unencrypted datasets"));1468(void) zfs_error(hdl, EZFS_BADPROP,1469errbuf);1470goto error;1471} else if (crypt != ZIO_CRYPT_OFF &&1472strcmp(strval, "none") == 0) {1473zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1474"keylocation must not be 'none' "1475"for encrypted datasets"));1476(void) zfs_error(hdl, EZFS_BADPROP,1477errbuf);1478goto error;1479}1480}1481break;14821483case ZFS_PROP_PBKDF2_ITERS:1484if (intval < MIN_PBKDF2_ITERATIONS) {1485zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1486"minimum pbkdf2 iterations is %u"),1487MIN_PBKDF2_ITERATIONS);1488(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1489goto error;1490}1491break;14921493case ZFS_PROP_UTF8ONLY:1494chosen_utf = (int)intval;1495break;14961497case ZFS_PROP_NORMALIZE:1498chosen_normal = (int)intval;1499break;15001501default:1502break;1503}15041505/*1506* For changes to existing volumes, we have some additional1507* checks to enforce.1508*/1509if (type == ZFS_TYPE_VOLUME && zhp != NULL) {1510uint64_t blocksize = zfs_prop_get_int(zhp,1511ZFS_PROP_VOLBLOCKSIZE);1512char buf[64];15131514switch (prop) {1515case ZFS_PROP_VOLSIZE:1516if (intval % blocksize != 0) {1517zfs_nicebytes(blocksize, buf,1518sizeof (buf));1519zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1520"'%s' must be a multiple of "1521"volume block size (%s)"),1522propname, buf);1523(void) zfs_error(hdl, EZFS_BADPROP,1524errbuf);1525goto error;1526}15271528if (intval == 0) {1529zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1530"'%s' cannot be zero"),1531propname);1532(void) zfs_error(hdl, EZFS_BADPROP,1533errbuf);1534goto error;1535}1536break;15371538default:1539break;1540}1541}15421543/* check encryption properties */1544if (zhp != NULL) {1545int64_t crypt = zfs_prop_get_int(zhp,1546ZFS_PROP_ENCRYPTION);15471548switch (prop) {1549case ZFS_PROP_COPIES:1550if (crypt != ZIO_CRYPT_OFF && intval > 2) {1551zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1552"encrypted datasets cannot have "1553"3 copies"));1554(void) zfs_error(hdl, EZFS_BADPROP,1555errbuf);1556goto error;1557}1558break;1559default:1560break;1561}1562}1563}15641565/*1566* If normalization was chosen, but no UTF8 choice was made,1567* enforce rejection of non-UTF8 names.1568*1569* If normalization was chosen, but rejecting non-UTF8 names1570* was explicitly not chosen, it is an error.1571*1572* If utf8only was turned off, but the parent has normalization,1573* turn off normalization.1574*/1575if (chosen_normal > 0 && chosen_utf < 0) {1576if (nvlist_add_uint64(ret,1577zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {1578(void) no_memory(hdl);1579goto error;1580}1581} else if (chosen_normal > 0 && chosen_utf == 0) {1582zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1583"'%s' must be set 'on' if normalization chosen"),1584zfs_prop_to_name(ZFS_PROP_UTF8ONLY));1585(void) zfs_error(hdl, EZFS_BADPROP, errbuf);1586goto error;1587} else if (chosen_normal < 0 && chosen_utf == 0) {1588if (nvlist_add_uint64(ret,1589zfs_prop_to_name(ZFS_PROP_NORMALIZE), 0) != 0) {1590(void) no_memory(hdl);1591goto error;1592}1593}1594return (ret);15951596error:1597nvlist_free(ret);1598return (NULL);1599}16001601static int1602zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)1603{1604uint64_t old_volsize;1605uint64_t new_volsize;1606uint64_t old_reservation;1607uint64_t new_reservation;1608zfs_prop_t resv_prop;1609nvlist_t *props;1610zpool_handle_t *zph = zpool_handle(zhp);16111612/*1613* If this is an existing volume, and someone is setting the volsize,1614* make sure that it matches the reservation, or add it if necessary.1615*/1616old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);1617if (zfs_which_resv_prop(zhp, &resv_prop) < 0)1618return (-1);1619old_reservation = zfs_prop_get_int(zhp, resv_prop);16201621props = fnvlist_alloc();1622fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),1623zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));16241625if ((zvol_volsize_to_reservation(zph, old_volsize, props) !=1626old_reservation) || nvlist_exists(nvl,1627zfs_prop_to_name(resv_prop))) {1628fnvlist_free(props);1629return (0);1630}1631if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),1632&new_volsize) != 0) {1633fnvlist_free(props);1634return (-1);1635}1636new_reservation = zvol_volsize_to_reservation(zph, new_volsize, props);1637fnvlist_free(props);16381639if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),1640new_reservation) != 0) {1641(void) no_memory(zhp->zfs_hdl);1642return (-1);1643}1644return (1);1645}16461647/*1648* Helper for 'zfs {set|clone} refreservation=auto'. Must be called after1649* zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinel value.1650* Return codes must match zfs_add_synthetic_resv().1651*/1652static int1653zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl)1654{1655uint64_t volsize;1656uint64_t resvsize;1657zfs_prop_t prop;1658nvlist_t *props;16591660if (!ZFS_IS_VOLUME(zhp)) {1661return (0);1662}16631664if (zfs_which_resv_prop(zhp, &prop) != 0) {1665return (-1);1666}16671668if (prop != ZFS_PROP_REFRESERVATION) {1669return (0);1670}16711672if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(prop), &resvsize) != 0) {1673/* No value being set, so it can't be "auto" */1674return (0);1675}1676if (resvsize != UINT64_MAX) {1677/* Being set to a value other than "auto" */1678return (0);1679}16801681props = fnvlist_alloc();16821683fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),1684zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));16851686if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),1687&volsize) != 0) {1688volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);1689}16901691resvsize = zvol_volsize_to_reservation(zpool_handle(zhp), volsize,1692props);1693fnvlist_free(props);16941695(void) nvlist_remove_all(nvl, zfs_prop_to_name(prop));1696if (nvlist_add_uint64(nvl, zfs_prop_to_name(prop), resvsize) != 0) {1697(void) no_memory(zhp->zfs_hdl);1698return (-1);1699}1700return (1);1701}17021703static boolean_t1704zfs_is_namespace_prop(zfs_prop_t prop)1705{1706switch (prop) {17071708case ZFS_PROP_ATIME:1709case ZFS_PROP_RELATIME:1710case ZFS_PROP_DEVICES:1711case ZFS_PROP_EXEC:1712case ZFS_PROP_SETUID:1713case ZFS_PROP_READONLY:1714case ZFS_PROP_XATTR:1715case ZFS_PROP_NBMAND:1716return (B_TRUE);17171718default:1719return (B_FALSE);1720}1721}17221723/*1724* Given a property name and value, set the property for the given dataset.1725*/1726int1727zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)1728{1729int ret = -1;1730char errbuf[ERRBUFLEN];1731libzfs_handle_t *hdl = zhp->zfs_hdl;1732nvlist_t *nvl = NULL;17331734(void) snprintf(errbuf, sizeof (errbuf),1735dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),1736zhp->zfs_name);17371738if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||1739nvlist_add_string(nvl, propname, propval) != 0) {1740(void) no_memory(hdl);1741goto error;1742}17431744ret = zfs_prop_set_list(zhp, nvl);17451746error:1747nvlist_free(nvl);1748return (ret);1749}17501751/*1752* Given an nvlist of property names and values, set the properties for the1753* given dataset.1754*/1755int1756zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)1757{1758return (zfs_prop_set_list_flags(zhp, props, 0));1759}17601761/*1762* Given an nvlist of property names, values and flags, set the properties1763* for the given dataset. If ZFS_SET_NOMOUNT is set, it allows to update1764* mountpoint, sharenfs and sharesmb properties without (un/re)mounting1765* and (un/re)sharing the dataset.1766*/1767int1768zfs_prop_set_list_flags(zfs_handle_t *zhp, nvlist_t *props, int flags)1769{1770zfs_cmd_t zc = {"\0"};1771int ret = -1;1772prop_changelist_t **cls = NULL;1773int cl_idx;1774char errbuf[ERRBUFLEN];1775libzfs_handle_t *hdl = zhp->zfs_hdl;1776nvlist_t *nvl;1777int nvl_len = 0;1778int added_resv = 0;1779zfs_prop_t prop;1780boolean_t nsprop = B_FALSE;1781nvpair_t *elem;17821783(void) snprintf(errbuf, sizeof (errbuf),1784dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),1785zhp->zfs_name);17861787if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,1788zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,1789B_FALSE, errbuf)) == NULL)1790goto error;17911792/*1793* We have to check for any extra properties which need to be added1794* before computing the length of the nvlist.1795*/1796for (elem = nvlist_next_nvpair(nvl, NULL);1797elem != NULL;1798elem = nvlist_next_nvpair(nvl, elem)) {1799if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE &&1800(added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) {1801goto error;1802}1803}18041805if (added_resv != 1 &&1806(added_resv = zfs_fix_auto_resv(zhp, nvl)) == -1) {1807goto error;1808}18091810/*1811* Check how many properties we're setting and allocate an array to1812* store changelist pointers for postfix().1813*/1814for (elem = nvlist_next_nvpair(nvl, NULL);1815elem != NULL;1816elem = nvlist_next_nvpair(nvl, elem))1817nvl_len++;1818if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL)1819goto error;18201821cl_idx = 0;1822for (elem = nvlist_next_nvpair(nvl, NULL);1823elem != NULL;1824elem = nvlist_next_nvpair(nvl, elem)) {18251826prop = zfs_name_to_prop(nvpair_name(elem));1827nsprop |= zfs_is_namespace_prop(prop);18281829assert(cl_idx < nvl_len);1830/*1831* We don't want to unmount & remount the dataset when changing1832* its canmount property to 'on' or 'noauto'. We only use1833* the changelist logic to unmount when setting canmount=off.1834*/1835if (prop != ZFS_PROP_CANMOUNT ||1836(fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF &&1837zfs_is_mounted(zhp, NULL))) {1838cls[cl_idx] = changelist_gather(zhp, prop,1839((flags & ZFS_SET_NOMOUNT) ?1840CL_GATHER_DONT_UNMOUNT : 0), 0);1841if (cls[cl_idx] == NULL)1842goto error;1843}18441845if (prop == ZFS_PROP_MOUNTPOINT &&1846changelist_haszonedchild(cls[cl_idx])) {1847zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1848"child dataset with inherited mountpoint is used "1849"in a non-global zone"));1850ret = zfs_error(hdl, EZFS_ZONED, errbuf);1851goto error;1852}18531854if (cls[cl_idx] != NULL &&1855(ret = changelist_prefix(cls[cl_idx])) != 0)1856goto error;18571858cl_idx++;1859}1860assert(cl_idx == nvl_len);18611862/*1863* Execute the corresponding ioctl() to set this list of properties.1864*/1865(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));18661867zcmd_write_src_nvlist(hdl, &zc, nvl);1868zcmd_alloc_dst_nvlist(hdl, &zc, 0);18691870ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);18711872if (ret != 0) {1873if (zc.zc_nvlist_dst_filled == B_FALSE) {1874(void) zfs_standard_error(hdl, errno, errbuf);1875goto error;1876}18771878/* Get the list of unset properties back and report them. */1879nvlist_t *errorprops = NULL;1880if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0)1881goto error;1882for (nvpair_t *elem = nvlist_next_nvpair(errorprops, NULL);1883elem != NULL;1884elem = nvlist_next_nvpair(errorprops, elem)) {1885prop = zfs_name_to_prop(nvpair_name(elem));1886zfs_setprop_error(hdl, prop, errno, errbuf);1887}1888nvlist_free(errorprops);18891890if (added_resv && errno == ENOSPC) {1891/* clean up the volsize property we tried to set */1892uint64_t old_volsize = zfs_prop_get_int(zhp,1893ZFS_PROP_VOLSIZE);1894nvlist_free(nvl);1895nvl = NULL;1896zcmd_free_nvlists(&zc);18971898if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)1899goto error;1900if (nvlist_add_uint64(nvl,1901zfs_prop_to_name(ZFS_PROP_VOLSIZE),1902old_volsize) != 0)1903goto error;1904zcmd_write_src_nvlist(hdl, &zc, nvl);1905(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);1906}1907} else {1908for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {1909if (cls[cl_idx] != NULL) {1910int clp_err = changelist_postfix(cls[cl_idx]);1911if (clp_err != 0)1912ret = clp_err;1913}1914}19151916if (ret == 0) {1917/*1918* Refresh the statistics so the new property1919* value is reflected.1920*/1921(void) get_stats(zhp);19221923/*1924* Remount the filesystem to propagate the change1925* if one of the options handled by the generic1926* Linux namespace layer has been modified.1927*/1928if (nsprop && zfs_is_mounted(zhp, NULL))1929ret = zfs_mount(zhp, MNTOPT_REMOUNT, 0);1930}1931}19321933error:1934nvlist_free(nvl);1935zcmd_free_nvlists(&zc);1936if (cls != NULL) {1937for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {1938if (cls[cl_idx] != NULL)1939changelist_free(cls[cl_idx]);1940}1941free(cls);1942}1943return (ret);1944}19451946/*1947* Given a property, inherit the value from the parent dataset, or if received1948* is TRUE, revert to the received value, if any.1949*/1950int1951zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)1952{1953zfs_cmd_t zc = {"\0"};1954int ret;1955prop_changelist_t *cl;1956libzfs_handle_t *hdl = zhp->zfs_hdl;1957char errbuf[ERRBUFLEN];1958zfs_prop_t prop;19591960(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,1961"cannot inherit %s for '%s'"), propname, zhp->zfs_name);19621963zc.zc_cookie = received;1964if ((prop = zfs_name_to_prop(propname)) == ZPROP_USERPROP) {1965/*1966* For user properties, the amount of work we have to do is very1967* small, so just do it here.1968*/1969if (!zfs_prop_user(propname)) {1970zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,1971"invalid property"));1972return (zfs_error(hdl, EZFS_BADPROP, errbuf));1973}19741975(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));1976(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));19771978if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)1979return (zfs_standard_error(hdl, errno, errbuf));19801981(void) get_stats(zhp);1982return (0);1983}19841985/*1986* Verify that this property is inheritable.1987*/1988if (zfs_prop_readonly(prop))1989return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));19901991if (!zfs_prop_inheritable(prop) && !received)1992return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));19931994/*1995* Check to see if the value applies to this type1996*/1997if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE))1998return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));19992000/*2001* Normalize the name, to get rid of shorthand abbreviations.2002*/2003propname = zfs_prop_to_name(prop);2004(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));2005(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));20062007if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&2008zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {2009zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,2010"dataset is used in a non-global zone"));2011return (zfs_error(hdl, EZFS_ZONED, errbuf));2012}20132014/*2015* Determine datasets which will be affected by this change, if any.2016*/2017if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)2018return (-1);20192020if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {2021zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,2022"child dataset with inherited mountpoint is used "2023"in a non-global zone"));2024ret = zfs_error(hdl, EZFS_ZONED, errbuf);2025goto error;2026}20272028if ((ret = changelist_prefix(cl)) != 0)2029goto error;20302031if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) {2032changelist_free(cl);2033return (zfs_standard_error(hdl, errno, errbuf));2034} else {20352036if ((ret = changelist_postfix(cl)) != 0)2037goto error;20382039/*2040* Refresh the statistics so the new property is reflected.2041*/2042(void) get_stats(zhp);20432044/*2045* Remount the filesystem to propagate the change2046* if one of the options handled by the generic2047* Linux namespace layer has been modified.2048*/2049if (zfs_is_namespace_prop(prop) &&2050zfs_is_mounted(zhp, NULL))2051ret = zfs_mount(zhp, MNTOPT_REMOUNT, 0);2052}20532054error:2055changelist_free(cl);2056return (ret);2057}20582059/*2060* True DSL properties are stored in an nvlist. The following two functions2061* extract them appropriately.2062*/2063uint64_t2064getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, const char **source)2065{2066nvlist_t *nv;2067uint64_t value;20682069*source = NULL;2070if (nvlist_lookup_nvlist(zhp->zfs_props,2071zfs_prop_to_name(prop), &nv) == 0) {2072value = fnvlist_lookup_uint64(nv, ZPROP_VALUE);2073(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);2074} else {2075verify(!zhp->zfs_props_table ||2076zhp->zfs_props_table[prop] == B_TRUE);2077value = zfs_prop_default_numeric(prop);2078*source = "";2079}20802081return (value);2082}20832084static const char *2085getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, const char **source)2086{2087nvlist_t *nv;2088const char *value;20892090*source = NULL;2091if (nvlist_lookup_nvlist(zhp->zfs_props,2092zfs_prop_to_name(prop), &nv) == 0) {2093value = fnvlist_lookup_string(nv, ZPROP_VALUE);2094(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);2095} else {2096verify(!zhp->zfs_props_table ||2097zhp->zfs_props_table[prop] == B_TRUE);2098value = zfs_prop_default_string(prop);2099*source = "";2100}21012102return (value);2103}21042105static boolean_t2106zfs_is_recvd_props_mode(zfs_handle_t *zhp)2107{2108return (zhp->zfs_props != NULL &&2109zhp->zfs_props == zhp->zfs_recvd_props);2110}21112112static void2113zfs_set_recvd_props_mode(zfs_handle_t *zhp, uintptr_t *cookie)2114{2115*cookie = (uintptr_t)zhp->zfs_props;2116zhp->zfs_props = zhp->zfs_recvd_props;2117}21182119static void2120zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uintptr_t *cookie)2121{2122zhp->zfs_props = (nvlist_t *)*cookie;2123*cookie = 0;2124}21252126/*2127* Internal function for getting a numeric property. Both zfs_prop_get() and2128* zfs_prop_get_int() are built using this interface.2129*2130* Certain properties can be overridden using 'mount -o'. In this case, scan2131* the contents of the /proc/self/mounts entry, searching for the2132* appropriate options. If they differ from the on-disk values, report the2133* current values and mark the source "temporary".2134*/2135static int2136get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,2137const char **source, uint64_t *val)2138{2139zfs_cmd_t zc = {"\0"};2140nvlist_t *zplprops = NULL;2141struct mnttab mnt;2142const char *mntopt_on = NULL;2143const char *mntopt_off = NULL;2144boolean_t received = zfs_is_recvd_props_mode(zhp);21452146*source = NULL;21472148/*2149* If the property is being fetched for a snapshot, check whether2150* the property is valid for the snapshot's head dataset type.2151*/2152if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT &&2153!zfs_prop_valid_for_type(prop, zhp->zfs_head_type, B_TRUE)) {2154*val = zfs_prop_default_numeric(prop);2155return (-1);2156}21572158switch (prop) {2159case ZFS_PROP_ATIME:2160mntopt_on = MNTOPT_ATIME;2161mntopt_off = MNTOPT_NOATIME;2162break;21632164case ZFS_PROP_RELATIME:2165mntopt_on = MNTOPT_RELATIME;2166mntopt_off = MNTOPT_NORELATIME;2167break;21682169case ZFS_PROP_DEVICES:2170mntopt_on = MNTOPT_DEVICES;2171mntopt_off = MNTOPT_NODEVICES;2172break;21732174case ZFS_PROP_EXEC:2175mntopt_on = MNTOPT_EXEC;2176mntopt_off = MNTOPT_NOEXEC;2177break;21782179case ZFS_PROP_READONLY:2180mntopt_on = MNTOPT_RO;2181mntopt_off = MNTOPT_RW;2182break;21832184case ZFS_PROP_SETUID:2185mntopt_on = MNTOPT_SETUID;2186mntopt_off = MNTOPT_NOSETUID;2187break;21882189case ZFS_PROP_XATTR:2190mntopt_on = MNTOPT_XATTR;2191mntopt_off = MNTOPT_NOXATTR;2192break;21932194case ZFS_PROP_NBMAND:2195mntopt_on = MNTOPT_NBMAND;2196mntopt_off = MNTOPT_NONBMAND;2197break;21982199default:2200break;2201}22022203/*2204* Because looking up the mount options is potentially expensive2205* (iterating over all of /proc/self/mounts), we defer its2206* calculation until we're looking up a property which requires2207* its presence.2208*/2209if (!zhp->zfs_mntcheck &&2210(mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {2211libzfs_handle_t *hdl = zhp->zfs_hdl;2212struct mnttab entry;22132214if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)2215zhp->zfs_mntopts = zfs_strdup(hdl,2216entry.mnt_mntopts);22172218zhp->zfs_mntcheck = B_TRUE;2219}22202221if (zhp->zfs_mntopts == NULL)2222mnt.mnt_mntopts = (char *)"";2223else2224mnt.mnt_mntopts = zhp->zfs_mntopts;22252226switch (prop) {2227case ZFS_PROP_ATIME:2228case ZFS_PROP_RELATIME:2229case ZFS_PROP_DEVICES:2230case ZFS_PROP_EXEC:2231case ZFS_PROP_READONLY:2232case ZFS_PROP_SETUID:2233#ifndef __FreeBSD__2234case ZFS_PROP_XATTR:2235#endif2236case ZFS_PROP_NBMAND:2237*val = getprop_uint64(zhp, prop, source);22382239if (received)2240break;22412242if (hasmntopt(&mnt, mntopt_on) && !*val) {2243*val = B_TRUE;2244if (src)2245*src = ZPROP_SRC_TEMPORARY;2246} else if (hasmntopt(&mnt, mntopt_off) && *val) {2247*val = B_FALSE;2248if (src)2249*src = ZPROP_SRC_TEMPORARY;2250}2251break;22522253case ZFS_PROP_CANMOUNT:2254case ZFS_PROP_VOLSIZE:2255case ZFS_PROP_QUOTA:2256case ZFS_PROP_REFQUOTA:2257case ZFS_PROP_RESERVATION:2258case ZFS_PROP_REFRESERVATION:2259case ZFS_PROP_FILESYSTEM_LIMIT:2260case ZFS_PROP_SNAPSHOT_LIMIT:2261case ZFS_PROP_FILESYSTEM_COUNT:2262case ZFS_PROP_SNAPSHOT_COUNT:2263*val = getprop_uint64(zhp, prop, source);22642265if (*source == NULL) {2266/* not default, must be local */2267*source = zhp->zfs_name;2268}2269break;22702271case ZFS_PROP_MOUNTED:2272*val = (zhp->zfs_mntopts != NULL);2273break;22742275case ZFS_PROP_NUMCLONES:2276*val = zhp->zfs_dmustats.dds_num_clones;2277break;22782279case ZFS_PROP_VERSION:2280case ZFS_PROP_NORMALIZE:2281case ZFS_PROP_UTF8ONLY:2282case ZFS_PROP_CASE:2283case ZFS_PROP_DEFAULTUSERQUOTA:2284case ZFS_PROP_DEFAULTGROUPQUOTA:2285case ZFS_PROP_DEFAULTPROJECTQUOTA:2286case ZFS_PROP_DEFAULTUSEROBJQUOTA:2287case ZFS_PROP_DEFAULTGROUPOBJQUOTA:2288case ZFS_PROP_DEFAULTPROJECTOBJQUOTA:2289zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0);22902291(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));2292if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {2293zcmd_free_nvlists(&zc);2294if (prop == ZFS_PROP_VERSION &&2295zhp->zfs_type == ZFS_TYPE_VOLUME)2296*val = zfs_prop_default_numeric(prop);2297return (-1);2298}2299if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||2300nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),2301val) != 0) {2302zcmd_free_nvlists(&zc);2303return (-1);2304}2305nvlist_free(zplprops);2306zcmd_free_nvlists(&zc);2307break;23082309case ZFS_PROP_INCONSISTENT:2310*val = zhp->zfs_dmustats.dds_inconsistent;2311break;23122313case ZFS_PROP_REDACTED:2314*val = zhp->zfs_dmustats.dds_redacted;2315break;23162317case ZFS_PROP_GUID:2318if (zhp->zfs_dmustats.dds_guid != 0)2319*val = zhp->zfs_dmustats.dds_guid;2320else2321*val = getprop_uint64(zhp, prop, source);2322break;23232324case ZFS_PROP_CREATETXG:2325/*2326* We can directly read createtxg property from zfs2327* handle for Filesystem, Snapshot and ZVOL types.2328*/2329if (((zhp->zfs_type == ZFS_TYPE_FILESYSTEM) ||2330(zhp->zfs_type == ZFS_TYPE_SNAPSHOT) ||2331(zhp->zfs_type == ZFS_TYPE_VOLUME)) &&2332(zhp->zfs_dmustats.dds_creation_txg != 0)) {2333*val = zhp->zfs_dmustats.dds_creation_txg;2334break;2335} else {2336*val = getprop_uint64(zhp, prop, source);2337}2338zfs_fallthrough;2339default:2340switch (zfs_prop_get_type(prop)) {2341case PROP_TYPE_NUMBER:2342case PROP_TYPE_INDEX:2343*val = getprop_uint64(zhp, prop, source);2344/*2345* If we tried to use a default value for a2346* readonly property, it means that it was not2347* present. Note this only applies to "truly"2348* readonly properties, not set-once properties2349* like volblocksize.2350*/2351if (zfs_prop_readonly(prop) &&2352!zfs_prop_setonce(prop) &&2353*source != NULL && (*source)[0] == '\0') {2354*source = NULL;2355return (-1);2356}2357break;23582359case PROP_TYPE_STRING:2360default:2361zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,2362"cannot get non-numeric property"));2363return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,2364dgettext(TEXT_DOMAIN, "internal error")));2365}2366}23672368return (0);2369}23702371/*2372* Calculate the source type, given the raw source string.2373*/2374static void2375get_source(zfs_handle_t *zhp, zprop_source_t *srctype, const char *source,2376char *statbuf, size_t statlen)2377{2378if (statbuf == NULL ||2379srctype == NULL || *srctype == ZPROP_SRC_TEMPORARY) {2380return;2381}23822383if (source == NULL) {2384*srctype = ZPROP_SRC_NONE;2385} else if (source[0] == '\0') {2386*srctype = ZPROP_SRC_DEFAULT;2387} else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) {2388*srctype = ZPROP_SRC_RECEIVED;2389} else {2390if (strcmp(source, zhp->zfs_name) == 0) {2391*srctype = ZPROP_SRC_LOCAL;2392} else {2393(void) strlcpy(statbuf, source, statlen);2394*srctype = ZPROP_SRC_INHERITED;2395}2396}23972398}23992400int2401zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,2402size_t proplen, boolean_t literal)2403{2404zfs_prop_t prop;2405int err = 0;24062407if (zhp->zfs_recvd_props == NULL)2408if (get_recvd_props_ioctl(zhp) != 0)2409return (-1);24102411prop = zfs_name_to_prop(propname);24122413if (prop != ZPROP_USERPROP) {2414uintptr_t cookie;2415if (!nvlist_exists(zhp->zfs_recvd_props, propname))2416return (-1);2417zfs_set_recvd_props_mode(zhp, &cookie);2418err = zfs_prop_get(zhp, prop, propbuf, proplen,2419NULL, NULL, 0, literal);2420zfs_unset_recvd_props_mode(zhp, &cookie);2421} else {2422nvlist_t *propval;2423const char *recvdval;2424if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,2425propname, &propval) != 0)2426return (-1);2427recvdval = fnvlist_lookup_string(propval, ZPROP_VALUE);2428(void) strlcpy(propbuf, recvdval, proplen);2429}24302431return (err == 0 ? 0 : -1);2432}24332434static int2435get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)2436{2437nvlist_t *value;2438nvpair_t *pair;24392440value = zfs_get_clones_nvl(zhp);2441if (value == NULL || nvlist_empty(value))2442return (-1);24432444propbuf[0] = '\0';2445for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;2446pair = nvlist_next_nvpair(value, pair)) {2447if (propbuf[0] != '\0')2448(void) strlcat(propbuf, ",", proplen);2449(void) strlcat(propbuf, nvpair_name(pair), proplen);2450}24512452return (0);2453}24542455struct get_clones_arg {2456uint64_t numclones;2457nvlist_t *value;2458const char *origin;2459char buf[ZFS_MAX_DATASET_NAME_LEN];2460};24612462static int2463get_clones_cb(zfs_handle_t *zhp, void *arg)2464{2465struct get_clones_arg *gca = arg;24662467if (gca->numclones == 0) {2468zfs_close(zhp);2469return (0);2470}24712472if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),2473NULL, NULL, 0, B_TRUE) != 0)2474goto out;2475if (strcmp(gca->buf, gca->origin) == 0) {2476fnvlist_add_boolean(gca->value, zfs_get_name(zhp));2477gca->numclones--;2478}24792480out:2481(void) zfs_iter_children_v2(zhp, 0, get_clones_cb, gca);2482zfs_close(zhp);2483return (0);2484}24852486nvlist_t *2487zfs_get_clones_nvl(zfs_handle_t *zhp)2488{2489nvlist_t *nv, *value;24902491if (nvlist_lookup_nvlist(zhp->zfs_props,2492zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {2493struct get_clones_arg gca;24942495/*2496* if this is a snapshot, then the kernel wasn't able2497* to get the clones. Do it by slowly iterating.2498*/2499if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)2500return (NULL);2501if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)2502return (NULL);2503if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {2504nvlist_free(nv);2505return (NULL);2506}25072508gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);2509gca.value = value;2510gca.origin = zhp->zfs_name;25112512if (gca.numclones != 0) {2513zfs_handle_t *root;2514char pool[ZFS_MAX_DATASET_NAME_LEN];2515char *cp = pool;25162517/* get the pool name */2518(void) strlcpy(pool, zhp->zfs_name, sizeof (pool));2519(void) strsep(&cp, "/@");2520root = zfs_open(zhp->zfs_hdl, pool,2521ZFS_TYPE_FILESYSTEM);2522if (root == NULL) {2523nvlist_free(nv);2524nvlist_free(value);2525return (NULL);2526}25272528(void) get_clones_cb(root, &gca);2529}25302531if (gca.numclones != 0 ||2532nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||2533nvlist_add_nvlist(zhp->zfs_props,2534zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {2535nvlist_free(nv);2536nvlist_free(value);2537return (NULL);2538}2539nvlist_free(nv);2540nvlist_free(value);2541nv = fnvlist_lookup_nvlist(zhp->zfs_props,2542zfs_prop_to_name(ZFS_PROP_CLONES));2543}25442545return (fnvlist_lookup_nvlist(nv, ZPROP_VALUE));2546}25472548static int2549get_rsnaps_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)2550{2551nvlist_t *value;2552uint64_t *snaps;2553uint_t nsnaps;25542555if (nvlist_lookup_nvlist(zhp->zfs_props,2556zfs_prop_to_name(ZFS_PROP_REDACT_SNAPS), &value) != 0)2557return (-1);2558if (nvlist_lookup_uint64_array(value, ZPROP_VALUE, &snaps,2559&nsnaps) != 0)2560return (-1);2561if (nsnaps == 0) {2562/* There's no redaction snapshots; pass a special value back */2563(void) snprintf(propbuf, proplen, "none");2564return (0);2565}2566propbuf[0] = '\0';2567for (int i = 0; i < nsnaps; i++) {2568char buf[128];2569if (propbuf[0] != '\0')2570(void) strlcat(propbuf, ",", proplen);2571(void) snprintf(buf, sizeof (buf), "%llu",2572(u_longlong_t)snaps[i]);2573(void) strlcat(propbuf, buf, proplen);2574}25752576return (0);2577}25782579/*2580* Accepts a property and value and checks that the value2581* matches the one found by the channel program. If they are2582* not equal, print both of them.2583*/2584static void2585zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval,2586const char *strval)2587{2588if (!zhp->zfs_hdl->libzfs_prop_debug)2589return;2590int error;2591char *poolname = zhp->zpool_hdl->zpool_name;2592const char *prop_name = zfs_prop_to_name(prop);2593const char *program =2594"args = ...\n"2595"ds = args['dataset']\n"2596"prop = args['property']\n"2597"value, setpoint = zfs.get_prop(ds, prop)\n"2598"return {value=value, setpoint=setpoint}\n";2599nvlist_t *outnvl;2600nvlist_t *retnvl;2601nvlist_t *argnvl = fnvlist_alloc();26022603fnvlist_add_string(argnvl, "dataset", zhp->zfs_name);2604fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop));26052606error = lzc_channel_program_nosync(poolname, program,260710 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl);26082609if (error == 0) {2610retnvl = fnvlist_lookup_nvlist(outnvl, "return");2611if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) {2612int64_t ans;2613error = nvlist_lookup_int64(retnvl, "value", &ans);2614if (error != 0) {2615(void) fprintf(stderr, "%s: zcp check error: "2616"%u\n", prop_name, error);2617return;2618}2619if (ans != intval) {2620(void) fprintf(stderr, "%s: zfs found %llu, "2621"but zcp found %llu\n", prop_name,2622(u_longlong_t)intval, (u_longlong_t)ans);2623}2624} else {2625const char *str_ans;2626error = nvlist_lookup_string(retnvl, "value", &str_ans);2627if (error != 0) {2628(void) fprintf(stderr, "%s: zcp check error: "2629"%u\n", prop_name, error);2630return;2631}2632if (strcmp(strval, str_ans) != 0) {2633(void) fprintf(stderr,2634"%s: zfs found '%s', but zcp found '%s'\n",2635prop_name, strval, str_ans);2636}2637}2638} else {2639(void) fprintf(stderr, "%s: zcp check failed, channel program "2640"error: %u\n", prop_name, error);2641}2642nvlist_free(argnvl);2643nvlist_free(outnvl);2644}26452646/*2647* Retrieve a property from the given object. If 'literal' is specified, then2648* numbers are left as exact values. Otherwise, numbers are converted to a2649* human-readable form.2650*2651* Returns 0 on success, or -1 on error.2652*/2653int2654zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,2655zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)2656{2657const char *source = NULL;2658uint64_t val;2659const char *str;2660const char *strval;2661boolean_t received = zfs_is_recvd_props_mode(zhp);26622663/*2664* Check to see if this property applies to our object2665*/2666if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE))2667return (-1);26682669if (received && zfs_prop_readonly(prop))2670return (-1);26712672if (src)2673*src = ZPROP_SRC_NONE;26742675switch (prop) {2676case ZFS_PROP_CREATION:2677/*2678* 'creation' is a time_t stored in the statistics. We convert2679* this into a string unless 'literal' is specified.2680*/2681{2682val = getprop_uint64(zhp, prop, &source);2683time_t time = (time_t)val;2684struct tm t;26852686if (literal ||2687localtime_r(&time, &t) == NULL ||2688strftime(propbuf, proplen, "%a %b %e %k:%M %Y",2689&t) == 0)2690(void) snprintf(propbuf, proplen, "%llu",2691(u_longlong_t)val);2692}2693zcp_check(zhp, prop, val, NULL);2694break;26952696case ZFS_PROP_MOUNTPOINT:2697/*2698* Getting the precise mountpoint can be tricky.2699*2700* - for 'none' or 'legacy', return those values.2701* - for inherited mountpoints, we want to take everything2702* after our ancestor and append it to the inherited value.2703*2704* If the pool has an alternate root, we want to prepend that2705* root to any values we return.2706*/27072708str = getprop_string(zhp, prop, &source);27092710if (str[0] == '/') {2711char buf[MAXPATHLEN];2712char *root = buf;2713const char *relpath;27142715/*2716* If we inherit the mountpoint, even from a dataset2717* with a received value, the source will be the path of2718* the dataset we inherit from. If source is2719* ZPROP_SOURCE_VAL_RECVD, the received value is not2720* inherited.2721*/2722if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) {2723relpath = "";2724} else {2725relpath = zhp->zfs_name + strlen(source);2726if (relpath[0] == '/')2727relpath++;2728}27292730if ((zpool_get_prop(zhp->zpool_hdl,2731ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL,2732B_FALSE)) || (strcmp(root, "-") == 0))2733root[0] = '\0';2734/*2735* Special case an alternate root of '/'. This will2736* avoid having multiple leading slashes in the2737* mountpoint path.2738*/2739if (strcmp(root, "/") == 0)2740root++;27412742/*2743* If the mountpoint is '/' then skip over this2744* if we are obtaining either an alternate root or2745* an inherited mountpoint.2746*/2747if (str[1] == '\0' && (root[0] != '\0' ||2748relpath[0] != '\0'))2749str++;27502751if (relpath[0] == '\0')2752(void) snprintf(propbuf, proplen, "%s%s",2753root, str);2754else2755(void) snprintf(propbuf, proplen, "%s%s%s%s",2756root, str, relpath[0] == '@' ? "" : "/",2757relpath);2758} else {2759/* 'legacy' or 'none' */2760(void) strlcpy(propbuf, str, proplen);2761}2762zcp_check(zhp, prop, 0, propbuf);2763break;27642765case ZFS_PROP_ORIGIN:2766if (*zhp->zfs_dmustats.dds_origin != '\0') {2767str = (char *)&zhp->zfs_dmustats.dds_origin;2768} else {2769str = getprop_string(zhp, prop, &source);2770}2771if (str == NULL || *str == '\0')2772str = zfs_prop_default_string(prop);2773if (str == NULL)2774return (-1);2775(void) strlcpy(propbuf, str, proplen);2776zcp_check(zhp, prop, 0, str);2777break;27782779case ZFS_PROP_REDACT_SNAPS:2780if (get_rsnaps_string(zhp, propbuf, proplen) != 0)2781return (-1);2782break;27832784case ZFS_PROP_CLONES:2785if (get_clones_string(zhp, propbuf, proplen) != 0)2786return (-1);2787break;27882789case ZFS_PROP_QUOTA:2790case ZFS_PROP_REFQUOTA:2791case ZFS_PROP_RESERVATION:2792case ZFS_PROP_REFRESERVATION:27932794if (get_numeric_property(zhp, prop, src, &source, &val) != 0)2795return (-1);2796/*2797* If quota or reservation is 0, we translate this into 'none'2798* (unless literal is set), and indicate that it's the default2799* value. Otherwise, we print the number nicely and indicate2800* that its set locally.2801*/2802if (val == 0) {2803if (literal)2804(void) strlcpy(propbuf, "0", proplen);2805else2806(void) strlcpy(propbuf, "none", proplen);2807} else {2808if (literal)2809(void) snprintf(propbuf, proplen, "%llu",2810(u_longlong_t)val);2811else2812zfs_nicebytes(val, propbuf, proplen);2813}2814zcp_check(zhp, prop, val, NULL);2815break;28162817case ZFS_PROP_FILESYSTEM_LIMIT:2818case ZFS_PROP_SNAPSHOT_LIMIT:2819case ZFS_PROP_FILESYSTEM_COUNT:2820case ZFS_PROP_SNAPSHOT_COUNT:28212822if (get_numeric_property(zhp, prop, src, &source, &val) != 0)2823return (-1);28242825/*2826* If limit is UINT64_MAX, we translate this into 'none', and2827* indicate that it's the default value. Otherwise, we print2828* the number nicely and indicate that it's set locally.2829*/2830if (val == UINT64_MAX) {2831(void) strlcpy(propbuf, "none", proplen);2832} else if (literal) {2833(void) snprintf(propbuf, proplen, "%llu",2834(u_longlong_t)val);2835} else {2836zfs_nicenum(val, propbuf, proplen);2837}28382839zcp_check(zhp, prop, val, NULL);2840break;28412842case ZFS_PROP_REFRATIO:2843case ZFS_PROP_COMPRESSRATIO:2844if (get_numeric_property(zhp, prop, src, &source, &val) != 0)2845return (-1);2846if (literal)2847(void) snprintf(propbuf, proplen, "%llu.%02llu",2848(u_longlong_t)(val / 100),2849(u_longlong_t)(val % 100));2850else2851(void) snprintf(propbuf, proplen, "%llu.%02llux",2852(u_longlong_t)(val / 100),2853(u_longlong_t)(val % 100));2854zcp_check(zhp, prop, val, NULL);2855break;28562857case ZFS_PROP_TYPE:2858switch (zhp->zfs_type) {2859case ZFS_TYPE_FILESYSTEM:2860str = "filesystem";2861break;2862case ZFS_TYPE_VOLUME:2863str = "volume";2864break;2865case ZFS_TYPE_SNAPSHOT:2866str = "snapshot";2867break;2868case ZFS_TYPE_BOOKMARK:2869str = "bookmark";2870break;2871default:2872abort();2873}2874(void) snprintf(propbuf, proplen, "%s", str);2875zcp_check(zhp, prop, 0, propbuf);2876break;28772878case ZFS_PROP_MOUNTED:2879/*2880* The 'mounted' property is a pseudo-property that described2881* whether the filesystem is currently mounted. Even though2882* it's a boolean value, the typical values of "on" and "off"2883* don't make sense, so we translate to "yes" and "no".2884*/2885if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,2886src, &source, &val) != 0)2887return (-1);2888if (val)2889(void) strlcpy(propbuf, "yes", proplen);2890else2891(void) strlcpy(propbuf, "no", proplen);2892break;28932894case ZFS_PROP_NAME:2895/*2896* The 'name' property is a pseudo-property derived from the2897* dataset name. It is presented as a real property to simplify2898* consumers.2899*/2900(void) strlcpy(propbuf, zhp->zfs_name, proplen);2901zcp_check(zhp, prop, 0, propbuf);2902break;29032904case ZFS_PROP_MLSLABEL:2905{2906#ifdef HAVE_MLSLABEL2907m_label_t *new_sl = NULL;2908char *ascii = NULL; /* human readable label */29092910(void) strlcpy(propbuf,2911getprop_string(zhp, prop, &source), proplen);29122913if (literal || (strcasecmp(propbuf,2914ZFS_MLSLABEL_DEFAULT) == 0))2915break;29162917/*2918* Try to translate the internal hex string to2919* human-readable output. If there are any2920* problems just use the hex string.2921*/29222923if (str_to_label(propbuf, &new_sl, MAC_LABEL,2924L_NO_CORRECTION, NULL) == -1) {2925m_label_free(new_sl);2926break;2927}29282929if (label_to_str(new_sl, &ascii, M_LABEL,2930DEF_NAMES) != 0) {2931if (ascii)2932free(ascii);2933m_label_free(new_sl);2934break;2935}2936m_label_free(new_sl);29372938(void) strlcpy(propbuf, ascii, proplen);2939free(ascii);2940#else2941(void) strlcpy(propbuf,2942getprop_string(zhp, prop, &source), proplen);2943#endif /* HAVE_MLSLABEL */2944}2945break;29462947case ZFS_PROP_GUID:2948case ZFS_PROP_KEY_GUID:2949case ZFS_PROP_IVSET_GUID:2950case ZFS_PROP_CREATETXG:2951case ZFS_PROP_OBJSETID:2952case ZFS_PROP_PBKDF2_ITERS:2953/*2954* These properties are stored as numbers, but they are2955* identifiers or counters.2956* We don't want them to be pretty printed, because pretty2957* printing truncates their values making them useless.2958*/2959if (get_numeric_property(zhp, prop, src, &source, &val) != 0)2960return (-1);2961(void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);2962zcp_check(zhp, prop, val, NULL);2963break;29642965case ZFS_PROP_REFERENCED:2966case ZFS_PROP_AVAILABLE:2967case ZFS_PROP_USED:2968case ZFS_PROP_USEDSNAP:2969case ZFS_PROP_USEDDS:2970case ZFS_PROP_USEDREFRESERV:2971case ZFS_PROP_USEDCHILD:2972if (get_numeric_property(zhp, prop, src, &source, &val) != 0)2973return (-1);2974if (literal) {2975(void) snprintf(propbuf, proplen, "%llu",2976(u_longlong_t)val);2977} else {2978zfs_nicebytes(val, propbuf, proplen);2979}2980zcp_check(zhp, prop, val, NULL);2981break;29822983case ZFS_PROP_SNAPSHOTS_CHANGED:2984{2985if ((get_numeric_property(zhp, prop, src, &source,2986&val) != 0) || val == 0) {2987return (-1);2988}29892990time_t time = (time_t)val;2991struct tm t;29922993if (literal ||2994localtime_r(&time, &t) == NULL ||2995strftime(propbuf, proplen, "%a %b %e %k:%M:%S %Y",2996&t) == 0)2997(void) snprintf(propbuf, proplen, "%llu",2998(u_longlong_t)val);2999}3000zcp_check(zhp, prop, val, NULL);3001break;30023003default:3004switch (zfs_prop_get_type(prop)) {3005case PROP_TYPE_NUMBER:3006if (get_numeric_property(zhp, prop, src,3007&source, &val) != 0) {3008return (-1);3009}30103011if (literal) {3012(void) snprintf(propbuf, proplen, "%llu",3013(u_longlong_t)val);3014} else {3015zfs_nicenum(val, propbuf, proplen);3016}3017zcp_check(zhp, prop, val, NULL);3018break;30193020case PROP_TYPE_STRING:3021str = getprop_string(zhp, prop, &source);3022if (str == NULL)3023return (-1);30243025(void) strlcpy(propbuf, str, proplen);3026zcp_check(zhp, prop, 0, str);3027break;30283029case PROP_TYPE_INDEX:3030if (get_numeric_property(zhp, prop, src,3031&source, &val) != 0)3032return (-1);3033if (zfs_prop_index_to_string(prop, val, &strval) != 0)3034return (-1);30353036(void) strlcpy(propbuf, strval, proplen);3037zcp_check(zhp, prop, 0, strval);3038break;30393040default:3041abort();3042}3043}30443045get_source(zhp, src, source, statbuf, statlen);30463047return (0);3048}30493050/*3051* Utility function to get the given numeric property. Does no validation that3052* the given property is the appropriate type; should only be used with3053* hard-coded property types.3054*/3055uint64_t3056zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)3057{3058const char *source;3059uint64_t val = 0;30603061(void) get_numeric_property(zhp, prop, NULL, &source, &val);30623063return (val);3064}30653066static int3067zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)3068{3069char buf[64];30703071(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);3072return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));3073}30743075/*3076* Similar to zfs_prop_get(), but returns the value as an integer.3077*/3078int3079zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,3080zprop_source_t *src, char *statbuf, size_t statlen)3081{3082const char *source;30833084/*3085* Check to see if this property applies to our object3086*/3087if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE)) {3088return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,3089dgettext(TEXT_DOMAIN, "cannot get property '%s'"),3090zfs_prop_to_name(prop)));3091}30923093if (src)3094*src = ZPROP_SRC_NONE;30953096if (get_numeric_property(zhp, prop, src, &source, value) != 0)3097return (-1);30983099get_source(zhp, src, source, statbuf, statlen);31003101return (0);3102}31033104#ifdef HAVE_IDMAP3105static int3106idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,3107char **domainp, idmap_rid_t *ridp)3108{3109idmap_get_handle_t *get_hdl = NULL;3110idmap_stat status;3111int err = EINVAL;31123113if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)3114goto out;31153116if (isuser) {3117err = idmap_get_sidbyuid(get_hdl, id,3118IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);3119} else {3120err = idmap_get_sidbygid(get_hdl, id,3121IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);3122}3123if (err == IDMAP_SUCCESS &&3124idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&3125status == IDMAP_SUCCESS)3126err = 0;3127else3128err = EINVAL;3129out:3130if (get_hdl)3131idmap_get_destroy(get_hdl);3132return (err);3133}3134#endif /* HAVE_IDMAP */31353136/*3137* convert the propname into parameters needed by kernel3138* Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 1268293139* Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 7893140* Eg: groupquota@staff -> ZFS_PROP_GROUPQUOTA, "", 12343141* Eg: groupused@staff -> ZFS_PROP_GROUPUSED, "", 12343142* Eg: projectquota@123 -> ZFS_PROP_PROJECTQUOTA, "", 1233143* Eg: projectused@789 -> ZFS_PROP_PROJECTUSED, "", 7893144*/3145static int3146userquota_propname_decode(const char *propname, boolean_t zoned,3147zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)3148{3149zfs_userquota_prop_t type;3150char *cp;3151boolean_t isuser;3152boolean_t isgroup;3153boolean_t isproject;3154struct passwd *pw;3155struct group *gr;31563157domain[0] = '\0';31583159/* Figure out the property type ({user|group|project}{quota|space}) */3160for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {3161if (strncmp(propname, zfs_userquota_prop_prefixes[type],3162strlen(zfs_userquota_prop_prefixes[type])) == 0)3163break;3164}3165if (type == ZFS_NUM_USERQUOTA_PROPS)3166return (EINVAL);3167*typep = type;31683169isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED ||3170type == ZFS_PROP_USEROBJQUOTA ||3171type == ZFS_PROP_USEROBJUSED);3172isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED ||3173type == ZFS_PROP_GROUPOBJQUOTA ||3174type == ZFS_PROP_GROUPOBJUSED);3175isproject = (type == ZFS_PROP_PROJECTQUOTA ||3176type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTOBJQUOTA ||3177type == ZFS_PROP_PROJECTOBJUSED);31783179cp = strchr(propname, '@') + 1;31803181if (isuser &&3182getpwnam_r(cp, &gpwd, rpbuf, sizeof (rpbuf), &pw) == 0 &&3183pw != NULL) {3184if (zoned && getzoneid() == GLOBAL_ZONEID)3185return (ENOENT);3186*ridp = pw->pw_uid;3187} else if (isgroup &&3188getgrnam_r(cp, &ggrp, rpbuf, sizeof (rpbuf), &gr) == 0 &&3189gr != NULL) {3190if (zoned && getzoneid() == GLOBAL_ZONEID)3191return (ENOENT);3192*ridp = gr->gr_gid;3193} else if (!isproject && strchr(cp, '@')) {3194#ifdef HAVE_IDMAP3195/*3196* It's a SID name (eg "user@domain") that needs to be3197* turned into S-1-domainID-RID.3198*/3199directory_error_t e;3200char *numericsid = NULL;3201char *end;32023203if (zoned && getzoneid() == GLOBAL_ZONEID)3204return (ENOENT);3205if (isuser) {3206e = directory_sid_from_user_name(NULL,3207cp, &numericsid);3208} else {3209e = directory_sid_from_group_name(NULL,3210cp, &numericsid);3211}3212if (e != NULL) {3213directory_error_free(e);3214return (ENOENT);3215}3216if (numericsid == NULL)3217return (ENOENT);3218cp = numericsid;3219(void) strlcpy(domain, cp, domainlen);3220cp = strrchr(domain, '-');3221*cp = '\0';3222cp++;32233224errno = 0;3225*ridp = strtoull(cp, &end, 10);3226free(numericsid);32273228if (errno != 0 || *end != '\0')3229return (EINVAL);3230#else3231(void) domainlen;3232return (ENOSYS);3233#endif /* HAVE_IDMAP */3234} else {3235/* It's a user/group/project ID (eg "12345"). */3236uid_t id;3237char *end;3238id = strtoul(cp, &end, 10);3239if (*end != '\0')3240return (EINVAL);3241if (id > MAXUID && !isproject) {3242#ifdef HAVE_IDMAP3243/* It's an ephemeral ID. */3244idmap_rid_t rid;3245char *mapdomain;32463247if (idmap_id_to_numeric_domain_rid(id, isuser,3248&mapdomain, &rid) != 0)3249return (ENOENT);3250(void) strlcpy(domain, mapdomain, domainlen);3251*ridp = rid;3252#else3253return (ENOSYS);3254#endif /* HAVE_IDMAP */3255} else {3256*ridp = id;3257}3258}32593260return (0);3261}32623263static int3264zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,3265uint64_t *propvalue, zfs_userquota_prop_t *typep)3266{3267int err;3268zfs_cmd_t zc = {"\0"};32693270(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));32713272err = userquota_propname_decode(propname,3273zfs_prop_get_int(zhp, ZFS_PROP_ZONED),3274typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);3275zc.zc_objset_type = *typep;3276if (err)3277return (err);32783279err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_USERSPACE_ONE, &zc);3280if (err)3281return (err);32823283*propvalue = zc.zc_cookie;3284return (0);3285}32863287int3288zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,3289uint64_t *propvalue)3290{3291zfs_userquota_prop_t type;32923293return (zfs_prop_get_userquota_common(zhp, propname, propvalue,3294&type));3295}32963297int3298zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,3299char *propbuf, int proplen, boolean_t literal)3300{3301int err;3302uint64_t propvalue;3303zfs_userquota_prop_t type;33043305err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,3306&type);33073308if (err)3309return (err);33103311if (literal) {3312(void) snprintf(propbuf, proplen, "%llu",3313(u_longlong_t)propvalue);3314} else if (propvalue == 0 &&3315(type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||3316type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||3317type == ZFS_PROP_PROJECTQUOTA ||3318type == ZFS_PROP_PROJECTOBJQUOTA)) {3319(void) strlcpy(propbuf, "none", proplen);3320} else if (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||3321type == ZFS_PROP_USERUSED || type == ZFS_PROP_GROUPUSED ||3322type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTQUOTA) {3323zfs_nicebytes(propvalue, propbuf, proplen);3324} else {3325zfs_nicenum(propvalue, propbuf, proplen);3326}3327return (0);3328}33293330/*3331* propname must start with "written@" or "written#".3332*/3333int3334zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,3335uint64_t *propvalue)3336{3337int err;3338zfs_cmd_t zc = {"\0"};3339const char *snapname;33403341(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));33423343assert(zfs_prop_written(propname));3344snapname = propname + strlen("written@");3345if (strchr(snapname, '@') != NULL || strchr(snapname, '#') != NULL) {3346/* full snapshot or bookmark name specified */3347(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));3348} else {3349/* snapname is the short name, append it to zhp's fsname */3350char *cp;33513352(void) strlcpy(zc.zc_value, zhp->zfs_name,3353sizeof (zc.zc_value));3354cp = strchr(zc.zc_value, '@');3355if (cp != NULL)3356*cp = '\0';3357(void) strlcat(zc.zc_value, snapname - 1, sizeof (zc.zc_value));3358}33593360err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SPACE_WRITTEN, &zc);3361if (err)3362return (err);33633364*propvalue = zc.zc_cookie;3365return (0);3366}33673368int3369zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,3370char *propbuf, int proplen, boolean_t literal)3371{3372int err;3373uint64_t propvalue;33743375err = zfs_prop_get_written_int(zhp, propname, &propvalue);33763377if (err)3378return (err);33793380if (literal) {3381(void) snprintf(propbuf, proplen, "%llu",3382(u_longlong_t)propvalue);3383} else {3384zfs_nicebytes(propvalue, propbuf, proplen);3385}33863387return (0);3388}33893390/*3391* Returns the name of the given zfs handle.3392*/3393const char *3394zfs_get_name(const zfs_handle_t *zhp)3395{3396return (zhp->zfs_name);3397}33983399/*3400* Returns the name of the parent pool for the given zfs handle.3401*/3402const char *3403zfs_get_pool_name(const zfs_handle_t *zhp)3404{3405return (zhp->zpool_hdl->zpool_name);3406}34073408/*3409* Returns the type of the given zfs handle.3410*/3411zfs_type_t3412zfs_get_type(const zfs_handle_t *zhp)3413{3414return (zhp->zfs_type);3415}34163417/*3418* Returns the type of the given zfs handle,3419* or, if a snapshot, the type of the snapshotted dataset.3420*/3421zfs_type_t3422zfs_get_underlying_type(const zfs_handle_t *zhp)3423{3424return (zhp->zfs_head_type);3425}34263427/*3428* Is one dataset name a child dataset of another?3429*3430* Needs to handle these cases:3431* Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo"3432* Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar"3433* Descendant? No. No. No. Yes.3434*/3435static boolean_t3436is_descendant(const char *ds1, const char *ds2)3437{3438size_t d1len = strlen(ds1);34393440/* ds2 can't be a descendant if it's smaller */3441if (strlen(ds2) < d1len)3442return (B_FALSE);34433444/* otherwise, compare strings and verify that there's a '/' char */3445return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0));3446}34473448/*3449* Given a complete name, return just the portion that refers to the parent.3450* Will return -1 if there is no parent (path is just the name of the3451* pool).3452*/3453static int3454parent_name(const char *path, char *buf, size_t buflen)3455{3456char *slashp;34573458(void) strlcpy(buf, path, buflen);34593460if ((slashp = strrchr(buf, '/')) == NULL)3461return (-1);3462*slashp = '\0';34633464return (0);3465}34663467int3468zfs_parent_name(zfs_handle_t *zhp, char *buf, size_t buflen)3469{3470return (parent_name(zfs_get_name(zhp), buf, buflen));3471}34723473/*3474* If accept_ancestor is false, then check to make sure that the given path has3475* a parent, and that it exists. If accept_ancestor is true, then find the3476* closest existing ancestor for the given path. In prefixlen return the3477* length of already existing prefix of the given path. We also fetch the3478* 'zoned' property, which is used to validate property settings when creating3479* new datasets.3480*/3481static int3482check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,3483boolean_t accept_ancestor, int *prefixlen)3484{3485zfs_cmd_t zc = {"\0"};3486char parent[ZFS_MAX_DATASET_NAME_LEN];3487char *slash;3488zfs_handle_t *zhp;3489char errbuf[ERRBUFLEN];3490uint64_t is_zoned;34913492(void) snprintf(errbuf, sizeof (errbuf),3493dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);34943495/* get parent, and check to see if this is just a pool */3496if (parent_name(path, parent, sizeof (parent)) != 0) {3497zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3498"missing dataset name"));3499return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));3500}35013502/* check to see if the pool exists */3503if ((slash = strchr(parent, '/')) == NULL)3504slash = parent + strlen(parent);3505(void) strlcpy(zc.zc_name, parent,3506MIN(sizeof (zc.zc_name), slash - parent + 1));3507if (zfs_ioctl(hdl, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&3508errno == ENOENT) {3509zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3510"no such pool '%s'"), zc.zc_name);3511return (zfs_error(hdl, EZFS_NOENT, errbuf));3512}35133514/* check to see if the parent dataset exists */3515while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {3516if (errno == ENOENT && accept_ancestor) {3517/*3518* Go deeper to find an ancestor, give up on top level.3519*/3520if (parent_name(parent, parent, sizeof (parent)) != 0) {3521zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3522"no such pool '%s'"), zc.zc_name);3523return (zfs_error(hdl, EZFS_NOENT, errbuf));3524}3525} else if (errno == ENOENT) {3526zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3527"parent does not exist"));3528return (zfs_error(hdl, EZFS_NOENT, errbuf));3529} else3530return (zfs_standard_error(hdl, errno, errbuf));3531}35323533is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);3534if (zoned != NULL)3535*zoned = is_zoned;35363537/* we are in a non-global zone, but parent is in the global zone */3538if (getzoneid() != GLOBAL_ZONEID && !is_zoned) {3539(void) zfs_standard_error(hdl, EPERM, errbuf);3540zfs_close(zhp);3541return (-1);3542}35433544/* make sure parent is a filesystem */3545if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {3546zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3547"parent is not a filesystem"));3548(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);3549zfs_close(zhp);3550return (-1);3551}35523553zfs_close(zhp);3554if (prefixlen != NULL)3555*prefixlen = strlen(parent);3556return (0);3557}35583559/*3560* Finds whether the dataset of the given type(s) exists.3561*/3562boolean_t3563zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)3564{3565zfs_handle_t *zhp;35663567if (!zfs_validate_name(hdl, path, types, B_FALSE))3568return (B_FALSE);35693570/*3571* Try to get stats for the dataset, which will tell us if it exists.3572*/3573if ((zhp = make_dataset_handle(hdl, path)) != NULL) {3574int ds_type = zhp->zfs_type;35753576zfs_close(zhp);3577if (types & ds_type)3578return (B_TRUE);3579}3580return (B_FALSE);3581}35823583/*3584* Given a path to 'target', create all the ancestors between3585* the prefixlen portion of the path, and the target itself.3586* Fail if the initial prefixlen-ancestor does not already exist.3587*/3588int3589create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)3590{3591zfs_handle_t *h;3592char *cp;3593const char *opname;35943595/* make sure prefix exists */3596cp = target + prefixlen;3597if (*cp != '/') {3598assert(strchr(cp, '/') == NULL);3599h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);3600} else {3601*cp = '\0';3602h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);3603*cp = '/';3604}3605if (h == NULL)3606return (-1);3607zfs_close(h);36083609/*3610* Attempt to create, mount, and share any ancestor filesystems,3611* up to the prefixlen-long one.3612*/3613for (cp = target + prefixlen + 1;3614(cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) {36153616*cp = '\0';36173618h = make_dataset_handle(hdl, target);3619if (h) {3620/* it already exists, nothing to do here */3621zfs_close(h);3622continue;3623}36243625if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,3626NULL) != 0) {3627opname = dgettext(TEXT_DOMAIN, "create");3628goto ancestorerr;3629}36303631h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);3632if (h == NULL) {3633opname = dgettext(TEXT_DOMAIN, "open");3634goto ancestorerr;3635}36363637if (zfs_mount(h, NULL, 0) != 0) {3638opname = dgettext(TEXT_DOMAIN, "mount");3639goto ancestorerr;3640}36413642if (zfs_share(h, NULL) != 0) {3643opname = dgettext(TEXT_DOMAIN, "share");3644goto ancestorerr;3645}36463647zfs_close(h);3648}3649zfs_commit_shares(NULL);36503651return (0);36523653ancestorerr:3654zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3655"failed to %s ancestor '%s'"), opname, target);3656return (-1);3657}36583659/*3660* Creates non-existing ancestors of the given path.3661*/3662int3663zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)3664{3665int prefix;3666char *path_copy;3667char errbuf[ERRBUFLEN];3668int rc = 0;36693670(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,3671"cannot create '%s'"), path);36723673/*3674* Check that we are not passing the nesting limit3675* before we start creating any ancestors.3676*/3677if (dataset_nestcheck(path) != 0) {3678zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3679"maximum name nesting depth exceeded"));3680return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));3681}36823683if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)3684return (-1);36853686if ((path_copy = strdup(path)) != NULL) {3687rc = create_parents(hdl, path_copy, prefix);3688free(path_copy);3689}3690if (path_copy == NULL || rc != 0)3691return (-1);36923693return (0);3694}36953696/*3697* Create a new filesystem or volume.3698*/3699int3700zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,3701nvlist_t *props)3702{3703int ret;3704uint64_t size = 0;3705uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);3706uint64_t zoned;3707enum lzc_dataset_type ost;3708zpool_handle_t *zpool_handle;3709uint8_t *wkeydata = NULL;3710uint_t wkeylen = 0;3711char errbuf[ERRBUFLEN];3712char parent[ZFS_MAX_DATASET_NAME_LEN];37133714(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,3715"cannot create '%s'"), path);37163717/* validate the path, taking care to note the extended error message */3718if (!zfs_validate_name(hdl, path, type, B_TRUE))3719return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));37203721if (dataset_nestcheck(path) != 0) {3722zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3723"maximum name nesting depth exceeded"));3724return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));3725}37263727/* validate parents exist */3728if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)3729return (-1);37303731/*3732* The failure modes when creating a dataset of a different type over3733* one that already exists is a little strange. In particular, if you3734* try to create a dataset on top of an existing dataset, the ioctl()3735* will return ENOENT, not EEXIST. To prevent this from happening, we3736* first try to see if the dataset exists.3737*/3738if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {3739zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3740"dataset already exists"));3741return (zfs_error(hdl, EZFS_EXISTS, errbuf));3742}37433744if (type == ZFS_TYPE_VOLUME)3745ost = LZC_DATSET_TYPE_ZVOL;3746else3747ost = LZC_DATSET_TYPE_ZFS;37483749/* open zpool handle for prop validation */3750char pool_path[ZFS_MAX_DATASET_NAME_LEN];3751(void) strlcpy(pool_path, path, sizeof (pool_path));37523753/* truncate pool_path at first slash */3754char *p = strchr(pool_path, '/');3755if (p != NULL)3756*p = '\0';37573758if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL)3759return (-1);37603761if (props && (props = zfs_valid_proplist(hdl, type, props,3762zoned, NULL, zpool_handle, B_TRUE, errbuf)) == 0) {3763zpool_close(zpool_handle);3764return (-1);3765}3766zpool_close(zpool_handle);37673768if (type == ZFS_TYPE_VOLUME) {3769/*3770* If we are creating a volume, the size and block size must3771* satisfy a few restraints. First, the blocksize must be a3772* valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the3773* volsize must be a multiple of the block size, and cannot be3774* zero.3775*/3776if (props == NULL || nvlist_lookup_uint64(props,3777zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {3778nvlist_free(props);3779zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3780"missing volume size"));3781return (zfs_error(hdl, EZFS_BADPROP, errbuf));3782}37833784if ((ret = nvlist_lookup_uint64(props,3785zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),3786&blocksize)) != 0) {3787if (ret == ENOENT) {3788blocksize = zfs_prop_default_numeric(3789ZFS_PROP_VOLBLOCKSIZE);3790} else {3791nvlist_free(props);3792zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3793"missing volume block size"));3794return (zfs_error(hdl, EZFS_BADPROP, errbuf));3795}3796}37973798if (size == 0) {3799nvlist_free(props);3800zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3801"volume size cannot be zero"));3802return (zfs_error(hdl, EZFS_BADPROP, errbuf));3803}38043805if (size % blocksize != 0) {3806nvlist_free(props);3807zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3808"volume size must be a multiple of volume block "3809"size"));3810return (zfs_error(hdl, EZFS_BADPROP, errbuf));3811}3812}38133814(void) parent_name(path, parent, sizeof (parent));3815if (zfs_crypto_create(hdl, parent, props, NULL, B_TRUE,3816&wkeydata, &wkeylen) != 0) {3817nvlist_free(props);3818return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));3819}38203821/* create the dataset */3822ret = lzc_create(path, ost, props, wkeydata, wkeylen);3823nvlist_free(props);3824if (wkeydata != NULL)3825free(wkeydata);38263827/* check for failure */3828if (ret != 0) {3829switch (errno) {3830case ENOENT:3831zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3832"no such parent '%s'"), parent);3833return (zfs_error(hdl, EZFS_NOENT, errbuf));38343835case ENOTSUP:3836zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3837"pool must be upgraded to set this "3838"property or value"));3839return (zfs_error(hdl, EZFS_BADVERSION, errbuf));38403841case EACCES:3842zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3843"encryption root's key is not loaded "3844"or provided"));3845return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));38463847case ERANGE:3848zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,3849"invalid property value(s) specified"));3850return (zfs_error(hdl, EZFS_BADPROP, errbuf));3851#ifdef _ILP323852case EOVERFLOW:3853/*3854* This platform can't address a volume this big.3855*/3856if (type == ZFS_TYPE_VOLUME)3857return (zfs_error(hdl, EZFS_VOLTOOBIG,3858errbuf));3859zfs_fallthrough;3860#endif3861default:3862return (zfs_standard_error(hdl, errno, errbuf));3863}3864}38653866return (0);3867}38683869/*3870* Destroys the given dataset. The caller must make sure that the filesystem3871* isn't mounted, and that there are no active dependents. If the file system3872* does not exist this function does nothing.3873*/3874int3875zfs_destroy(zfs_handle_t *zhp, boolean_t defer)3876{3877int error;38783879if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT && defer)3880return (EINVAL);38813882if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {3883nvlist_t *nv = fnvlist_alloc();3884fnvlist_add_boolean(nv, zhp->zfs_name);3885error = lzc_destroy_bookmarks(nv, NULL);3886fnvlist_free(nv);3887if (error != 0) {3888return (zfs_standard_error_fmt(zhp->zfs_hdl, error,3889dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),3890zhp->zfs_name));3891}3892return (0);3893}38943895if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {3896nvlist_t *nv = fnvlist_alloc();3897fnvlist_add_boolean(nv, zhp->zfs_name);3898error = lzc_destroy_snaps(nv, defer, NULL);3899fnvlist_free(nv);3900} else {3901error = lzc_destroy(zhp->zfs_name);3902}39033904if (error != 0 && error != ENOENT) {3905return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,3906dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),3907zhp->zfs_name));3908}39093910remove_mountpoint(zhp);39113912return (0);3913}39143915struct destroydata {3916nvlist_t *nvl;3917const char *snapname;3918};39193920static int3921zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)3922{3923struct destroydata *dd = arg;3924char name[ZFS_MAX_DATASET_NAME_LEN];3925int rv = 0;39263927if (snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name,3928dd->snapname) >= sizeof (name))3929return (EINVAL);39303931if (lzc_exists(name))3932fnvlist_add_boolean(dd->nvl, name);39333934rv = zfs_iter_filesystems_v2(zhp, 0, zfs_check_snap_cb, dd);3935zfs_close(zhp);3936return (rv);3937}39383939/*3940* Destroys all snapshots with the given name in zhp & descendants.3941*/3942int3943zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)3944{3945int ret;3946struct destroydata dd = { 0 };39473948dd.snapname = snapname;3949dd.nvl = fnvlist_alloc();3950(void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);39513952if (nvlist_empty(dd.nvl)) {3953ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,3954dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),3955zhp->zfs_name, snapname);3956} else {3957ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);3958}3959fnvlist_free(dd.nvl);3960return (ret);3961}39623963/*3964* Destroys all the snapshots named in the nvlist.3965*/3966int3967zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)3968{3969nvlist_t *errlist = NULL;3970nvpair_t *pair;39713972int ret = zfs_destroy_snaps_nvl_os(hdl, snaps);3973if (ret != 0)3974return (ret);39753976ret = lzc_destroy_snaps(snaps, defer, &errlist);39773978if (ret == 0) {3979nvlist_free(errlist);3980return (0);3981}39823983if (nvlist_empty(errlist)) {3984char errbuf[ERRBUFLEN];3985(void) snprintf(errbuf, sizeof (errbuf),3986dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));39873988ret = zfs_standard_error(hdl, ret, errbuf);3989}3990for (pair = nvlist_next_nvpair(errlist, NULL);3991pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {3992char errbuf[ERRBUFLEN];3993(void) snprintf(errbuf, sizeof (errbuf),3994dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),3995nvpair_name(pair));39963997switch (fnvpair_value_int32(pair)) {3998case EEXIST:3999zfs_error_aux(hdl,4000dgettext(TEXT_DOMAIN, "snapshot is cloned"));4001ret = zfs_error(hdl, EZFS_EXISTS, errbuf);4002break;4003case EBUSY: {4004nvlist_t *existing_holds;4005int err = lzc_get_holds(nvpair_name(pair),4006&existing_holds);40074008/* check the presence of holders */4009if (err == 0 && !nvlist_empty(existing_holds)) {4010zfs_error_aux(hdl,4011dgettext(TEXT_DOMAIN, "it's being held. "4012"Run 'zfs holds -r %s' to see holders."),4013nvpair_name(pair));4014ret = zfs_error(hdl, EBUSY, errbuf);4015} else {4016ret = zfs_standard_error(hdl, errno, errbuf);4017}40184019if (err == 0)4020nvlist_free(existing_holds);4021break;4022}4023default:4024ret = zfs_standard_error(hdl, errno, errbuf);4025break;4026}4027}40284029nvlist_free(errlist);4030return (ret);4031}40324033/*4034* Clones the given dataset. The target must be of the same type as the source.4035*/4036int4037zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)4038{4039char parent[ZFS_MAX_DATASET_NAME_LEN];4040int ret;4041char errbuf[ERRBUFLEN];4042libzfs_handle_t *hdl = zhp->zfs_hdl;4043uint64_t zoned;40444045assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);40464047(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,4048"cannot create '%s'"), target);40494050/* validate the target/clone name */4051if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))4052return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));40534054/* validate parents exist */4055if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)4056return (-1);40574058(void) parent_name(target, parent, sizeof (parent));40594060/* do the clone */40614062if (props) {4063zfs_type_t type = ZFS_TYPE_FILESYSTEM;40644065if (ZFS_IS_VOLUME(zhp))4066type = ZFS_TYPE_VOLUME;4067if ((props = zfs_valid_proplist(hdl, type, props, zoned,4068zhp, zhp->zpool_hdl, B_TRUE, errbuf)) == NULL)4069return (-1);4070if (zfs_fix_auto_resv(zhp, props) == -1) {4071nvlist_free(props);4072return (-1);4073}4074}40754076if (zfs_crypto_clone_check(hdl, zhp, parent, props) != 0) {4077nvlist_free(props);4078return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));4079}40804081ret = lzc_clone(target, zhp->zfs_name, props);4082nvlist_free(props);40834084if (ret != 0) {4085switch (errno) {40864087case ENOENT:4088/*4089* The parent doesn't exist. We should have caught this4090* above, but there may a race condition that has since4091* destroyed the parent.4092*4093* At this point, we don't know whether it's the source4094* that doesn't exist anymore, or whether the target4095* dataset doesn't exist.4096*/4097zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,4098"no such parent '%s'"), parent);4099return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));41004101case EXDEV:4102zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,4103"source and target pools differ"));4104return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,4105errbuf));41064107default:4108return (zfs_standard_error(zhp->zfs_hdl, errno,4109errbuf));4110}4111}41124113return (ret);4114}41154116/*4117* Promotes the given clone fs to be the clone parent.4118*/4119int4120zfs_promote(zfs_handle_t *zhp)4121{4122libzfs_handle_t *hdl = zhp->zfs_hdl;4123char snapname[ZFS_MAX_DATASET_NAME_LEN];4124int ret;4125char errbuf[ERRBUFLEN];41264127(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,4128"cannot promote '%s'"), zhp->zfs_name);41294130if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {4131zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4132"snapshots can not be promoted"));4133return (zfs_error(hdl, EZFS_BADTYPE, errbuf));4134}41354136if (zhp->zfs_dmustats.dds_origin[0] == '\0') {4137zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4138"not a cloned filesystem"));4139return (zfs_error(hdl, EZFS_BADTYPE, errbuf));4140}41414142if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))4143return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));41444145ret = lzc_promote(zhp->zfs_name, snapname, sizeof (snapname));41464147if (ret != 0) {4148switch (ret) {4149case EACCES:4150/*4151* Promoting encrypted dataset outside its4152* encryption root.4153*/4154zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4155"cannot promote dataset outside its "4156"encryption root"));4157return (zfs_error(hdl, EZFS_EXISTS, errbuf));41584159case EEXIST:4160/* There is a conflicting snapshot name. */4161zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4162"conflicting snapshot '%s' from parent '%s'"),4163snapname, zhp->zfs_dmustats.dds_origin);4164return (zfs_error(hdl, EZFS_EXISTS, errbuf));41654166default:4167return (zfs_standard_error(hdl, ret, errbuf));4168}4169}4170return (ret);4171}41724173typedef struct snapdata {4174nvlist_t *sd_nvl;4175const char *sd_snapname;4176} snapdata_t;41774178static int4179zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)4180{4181snapdata_t *sd = arg;4182char name[ZFS_MAX_DATASET_NAME_LEN];4183int rv = 0;41844185if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) {4186if (snprintf(name, sizeof (name), "%s@%s", zfs_get_name(zhp),4187sd->sd_snapname) >= sizeof (name))4188return (EINVAL);41894190fnvlist_add_boolean(sd->sd_nvl, name);41914192rv = zfs_iter_filesystems_v2(zhp, 0, zfs_snapshot_cb, sd);4193}4194zfs_close(zhp);41954196return (rv);4197}41984199/*4200* Creates snapshots. The keys in the snaps nvlist are the snapshots to be4201* created.4202*/4203int4204zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)4205{4206int ret;4207char errbuf[ERRBUFLEN];4208nvpair_t *elem;4209nvlist_t *errors;4210zpool_handle_t *zpool_hdl;4211char pool[ZFS_MAX_DATASET_NAME_LEN];42124213(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,4214"cannot create snapshots "));42154216elem = NULL;4217while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {4218const char *snapname = nvpair_name(elem);42194220/* validate the target name */4221if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,4222B_TRUE)) {4223(void) snprintf(errbuf, sizeof (errbuf),4224dgettext(TEXT_DOMAIN,4225"cannot create snapshot '%s'"), snapname);4226return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));4227}4228}42294230/*4231* get pool handle for prop validation. assumes all snaps are in the4232* same pool, as does lzc_snapshot (below).4233*/4234elem = nvlist_next_nvpair(snaps, NULL);4235if (elem == NULL)4236return (-1);4237(void) strlcpy(pool, nvpair_name(elem), sizeof (pool));4238pool[strcspn(pool, "/@")] = '\0';4239zpool_hdl = zpool_open(hdl, pool);4240if (zpool_hdl == NULL)4241return (-1);42424243if (props != NULL &&4244(props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,4245props, B_FALSE, NULL, zpool_hdl, B_FALSE, errbuf)) == NULL) {4246zpool_close(zpool_hdl);4247return (-1);4248}4249zpool_close(zpool_hdl);42504251ret = lzc_snapshot(snaps, props, &errors);42524253if (ret != 0) {4254boolean_t printed = B_FALSE;4255for (elem = nvlist_next_nvpair(errors, NULL);4256elem != NULL;4257elem = nvlist_next_nvpair(errors, elem)) {4258(void) snprintf(errbuf, sizeof (errbuf),4259dgettext(TEXT_DOMAIN,4260"cannot create snapshot '%s'"), nvpair_name(elem));4261(void) zfs_standard_error(hdl,4262fnvpair_value_int32(elem), errbuf);4263printed = B_TRUE;4264}4265if (!printed) {4266switch (ret) {4267case EXDEV:4268zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4269"multiple snapshots of same "4270"fs not allowed"));4271(void) zfs_error(hdl, EZFS_EXISTS, errbuf);42724273break;4274default:4275(void) zfs_standard_error(hdl, ret, errbuf);4276}4277}4278}42794280nvlist_free(props);4281nvlist_free(errors);4282return (ret);4283}42844285int4286zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,4287nvlist_t *props)4288{4289int ret;4290snapdata_t sd = { 0 };4291char fsname[ZFS_MAX_DATASET_NAME_LEN];4292char *cp;4293zfs_handle_t *zhp;4294char errbuf[ERRBUFLEN];42954296(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,4297"cannot snapshot %s"), path);42984299if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))4300return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));43014302(void) strlcpy(fsname, path, sizeof (fsname));4303cp = strchr(fsname, '@');4304*cp = '\0';4305sd.sd_snapname = cp + 1;43064307if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |4308ZFS_TYPE_VOLUME)) == NULL) {4309return (-1);4310}43114312sd.sd_nvl = fnvlist_alloc();4313if (recursive) {4314(void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);4315} else {4316fnvlist_add_boolean(sd.sd_nvl, path);4317}43184319ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);4320fnvlist_free(sd.sd_nvl);4321zfs_close(zhp);4322return (ret);4323}43244325/*4326* Destroy any more recent snapshots. We invoke this callback on any dependents4327* of the snapshot first. If the 'cb_dependent' member is non-zero, then this4328* is a dependent and we should just destroy it without checking the transaction4329* group.4330*/4331typedef struct rollback_data {4332const char *cb_target; /* the snapshot */4333uint64_t cb_create; /* creation time reference */4334boolean_t cb_error;4335boolean_t cb_force;4336} rollback_data_t;43374338static int4339rollback_destroy_dependent(zfs_handle_t *zhp, void *data)4340{4341rollback_data_t *cbp = data;4342prop_changelist_t *clp;43434344/* We must destroy this clone; first unmount it */4345clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,4346cbp->cb_force ? MS_FORCE: 0);4347if (clp == NULL || changelist_prefix(clp) != 0) {4348cbp->cb_error = B_TRUE;4349zfs_close(zhp);4350return (0);4351}4352if (zfs_destroy(zhp, B_FALSE) != 0)4353cbp->cb_error = B_TRUE;4354else4355changelist_remove(clp, zhp->zfs_name);4356(void) changelist_postfix(clp);4357changelist_free(clp);43584359zfs_close(zhp);4360return (0);4361}43624363static int4364rollback_destroy(zfs_handle_t *zhp, void *data)4365{4366rollback_data_t *cbp = data;43674368if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {4369cbp->cb_error |= zfs_iter_dependents_v2(zhp, 0, B_FALSE,4370rollback_destroy_dependent, cbp);43714372cbp->cb_error |= zfs_destroy(zhp, B_FALSE);4373}43744375zfs_close(zhp);4376return (0);4377}43784379/*4380* Given a dataset, rollback to a specific snapshot, discarding any4381* data changes since then and making it the active dataset.4382*4383* Any snapshots and bookmarks more recent than the target are4384* destroyed, along with their dependents (i.e. clones).4385*/4386int4387zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)4388{4389rollback_data_t cb = { 0 };4390int err;4391boolean_t restore_resv = 0;4392uint64_t old_volsize = 0, new_volsize;4393zfs_prop_t resv_prop = { 0 };4394uint64_t min_txg = 0;43954396assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||4397zhp->zfs_type == ZFS_TYPE_VOLUME);43984399/*4400* Destroy all recent snapshots and their dependents.4401*/4402cb.cb_force = force;4403cb.cb_target = snap->zfs_name;4404cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);44054406if (cb.cb_create > 0)4407min_txg = cb.cb_create;44084409(void) zfs_iter_snapshots_v2(zhp, 0, rollback_destroy, &cb,4410min_txg, 0);44114412(void) zfs_iter_bookmarks_v2(zhp, 0, rollback_destroy, &cb);44134414if (cb.cb_error)4415return (-1);44164417/*4418* Now that we have verified that the snapshot is the latest,4419* rollback to the given snapshot.4420*/44214422if (zhp->zfs_type == ZFS_TYPE_VOLUME) {4423if (zfs_which_resv_prop(zhp, &resv_prop) < 0)4424return (-1);4425old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);4426restore_resv =4427(old_volsize == zfs_prop_get_int(zhp, resv_prop));4428}44294430/*4431* Pass both the filesystem and the wanted snapshot names,4432* we would get an error back if the snapshot is destroyed or4433* a new snapshot is created before this request is processed.4434*/4435err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);4436if (err != 0) {4437char errbuf[ERRBUFLEN];44384439(void) snprintf(errbuf, sizeof (errbuf),4440dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),4441zhp->zfs_name);4442switch (err) {4443case EEXIST:4444zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,4445"there is a snapshot or bookmark more recent "4446"than '%s'"), snap->zfs_name);4447(void) zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf);4448break;4449case ESRCH:4450zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,4451"'%s' is not found among snapshots of '%s'"),4452snap->zfs_name, zhp->zfs_name);4453(void) zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf);4454break;4455case EINVAL:4456(void) zfs_error(zhp->zfs_hdl, EZFS_BADTYPE, errbuf);4457break;4458default:4459(void) zfs_standard_error(zhp->zfs_hdl, err, errbuf);4460}4461return (err);4462}44634464/*4465* For volumes, if the pre-rollback volsize matched the pre-4466* rollback reservation and the volsize has changed then set4467* the reservation property to the post-rollback volsize.4468* Make a new handle since the rollback closed the dataset.4469*/4470if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&4471(zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {4472if (restore_resv) {4473new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);4474if (old_volsize != new_volsize)4475err = zfs_prop_set_int(zhp, resv_prop,4476new_volsize);4477}4478zfs_close(zhp);4479}4480return (err);4481}44824483/*4484* Renames the given dataset.4485*/4486int4487zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)4488{4489int ret = 0;4490zfs_cmd_t zc = {"\0"};4491char *delim;4492prop_changelist_t *cl = NULL;4493char parent[ZFS_MAX_DATASET_NAME_LEN];4494char property[ZFS_MAXPROPLEN];4495libzfs_handle_t *hdl = zhp->zfs_hdl;4496char errbuf[ERRBUFLEN];44974498/* if we have the same exact name, just return success */4499if (strcmp(zhp->zfs_name, target) == 0)4500return (0);45014502(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,4503"cannot rename to '%s'"), target);45044505/* make sure source name is valid */4506if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))4507return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));45084509/*4510* Make sure the target name is valid4511*/4512if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {4513if ((strchr(target, '@') == NULL) ||4514*target == '@') {4515/*4516* Snapshot target name is abbreviated,4517* reconstruct full dataset name4518*/4519(void) strlcpy(parent, zhp->zfs_name,4520sizeof (parent));4521delim = strchr(parent, '@');4522if (strchr(target, '@') == NULL)4523*(++delim) = '\0';4524else4525*delim = '\0';4526(void) strlcat(parent, target, sizeof (parent));4527target = parent;4528} else {4529/*4530* Make sure we're renaming within the same dataset.4531*/4532delim = strchr(target, '@');4533if (strncmp(zhp->zfs_name, target, delim - target)4534!= 0 || zhp->zfs_name[delim - target] != '@') {4535zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4536"snapshots must be part of same "4537"dataset"));4538return (zfs_error(hdl, EZFS_CROSSTARGET,4539errbuf));4540}4541}45424543if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))4544return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));4545} else {4546if (flags.recursive) {4547zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4548"recursive rename must be a snapshot"));4549return (zfs_error(hdl, EZFS_BADTYPE, errbuf));4550}45514552if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))4553return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));45544555/* validate parents */4556if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0)4557return (-1);45584559/* make sure we're in the same pool */4560verify((delim = strchr(target, '/')) != NULL);4561if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||4562zhp->zfs_name[delim - target] != '/') {4563zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4564"datasets must be within same pool"));4565return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));4566}45674568/* new name cannot be a child of the current dataset name */4569if (is_descendant(zhp->zfs_name, target)) {4570zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4571"New dataset name cannot be a descendant of "4572"current dataset name"));4573return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));4574}4575}45764577(void) snprintf(errbuf, sizeof (errbuf),4578dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);45794580if (getzoneid() == GLOBAL_ZONEID &&4581zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {4582zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4583"dataset is used in a non-global zone"));4584return (zfs_error(hdl, EZFS_ZONED, errbuf));4585}45864587/*4588* Avoid unmounting file systems with mountpoint property set to4589* 'legacy' or 'none' even if -u option is not given.4590*/4591if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&4592!flags.recursive && !flags.nounmount &&4593zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,4594sizeof (property), NULL, NULL, 0, B_FALSE) == 0 &&4595(strcmp(property, "legacy") == 0 ||4596strcmp(property, "none") == 0)) {4597flags.nounmount = B_TRUE;4598}4599if (flags.recursive) {4600char *parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);4601delim = strchr(parentname, '@');4602*delim = '\0';4603zfs_handle_t *zhrp = zfs_open(zhp->zfs_hdl, parentname,4604ZFS_TYPE_DATASET);4605free(parentname);4606if (zhrp == NULL) {4607ret = -1;4608goto error;4609}4610zfs_close(zhrp);4611} else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {4612if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,4613flags.nounmount ? CL_GATHER_DONT_UNMOUNT :4614CL_GATHER_ITER_MOUNTED,4615flags.forceunmount ? MS_FORCE : 0)) == NULL)4616return (-1);46174618if (changelist_haszonedchild(cl)) {4619zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4620"child dataset with inherited mountpoint is used "4621"in a non-global zone"));4622(void) zfs_error(hdl, EZFS_ZONED, errbuf);4623ret = -1;4624goto error;4625}46264627if ((ret = changelist_prefix(cl)) != 0)4628goto error;4629}46304631if (ZFS_IS_VOLUME(zhp))4632zc.zc_objset_type = DMU_OST_ZVOL;4633else4634zc.zc_objset_type = DMU_OST_ZFS;46354636(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));4637(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));46384639zc.zc_cookie = !!flags.recursive;4640zc.zc_cookie |= (!!flags.nounmount) << 1;46414642if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {4643/*4644* if it was recursive, the one that actually failed will4645* be in zc.zc_name4646*/4647(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,4648"cannot rename '%s'"), zc.zc_name);46494650if (flags.recursive && errno == EEXIST) {4651zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4652"a child dataset already has a snapshot "4653"with the new name"));4654(void) zfs_error(hdl, EZFS_EXISTS, errbuf);4655} else if (errno == EACCES) {4656zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,4657"cannot move encrypted child outside of "4658"its encryption root"));4659(void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);4660} else {4661(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);4662}46634664/*4665* On failure, we still want to remount any filesystems that4666* were previously mounted, so we don't alter the system state.4667*/4668if (cl != NULL)4669(void) changelist_postfix(cl);4670} else {4671if (cl != NULL) {4672changelist_rename(cl, zfs_get_name(zhp), target);4673ret = changelist_postfix(cl);4674}4675(void) strlcpy(zhp->zfs_name, target, sizeof (zhp->zfs_name));4676}46774678error:4679if (cl != NULL) {4680changelist_free(cl);4681}4682return (ret);4683}46844685nvlist_t *4686zfs_get_all_props(zfs_handle_t *zhp)4687{4688return (zhp->zfs_props);4689}46904691nvlist_t *4692zfs_get_recvd_props(zfs_handle_t *zhp)4693{4694if (zhp->zfs_recvd_props == NULL)4695if (get_recvd_props_ioctl(zhp) != 0)4696return (NULL);4697return (zhp->zfs_recvd_props);4698}46994700nvlist_t *4701zfs_get_user_props(zfs_handle_t *zhp)4702{4703return (zhp->zfs_user_props);4704}47054706/*4707* This function is used by 'zfs list' to determine the exact set of columns to4708* display, and their maximum widths. This does two main things:4709*4710* - If this is a list of all properties, then expand the list to include4711* all native properties, and set a flag so that for each dataset we look4712* for new unique user properties and add them to the list.4713*4714* - For non fixed-width properties, keep track of the maximum width seen4715* so that we can size the column appropriately. If the user has4716* requested received property values, we also need to compute the width4717* of the RECEIVED column.4718*/4719int4720zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,4721boolean_t literal)4722{4723libzfs_handle_t *hdl = zhp->zfs_hdl;4724zprop_list_t *entry;4725zprop_list_t **last, **start;4726nvlist_t *userprops, *propval;4727nvpair_t *elem;4728const char *strval;4729char buf[ZFS_MAXPROPLEN];47304731if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)4732return (-1);47334734userprops = zfs_get_user_props(zhp);47354736entry = *plp;4737if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {4738/*4739* Go through and add any user properties as necessary. We4740* start by incrementing our list pointer to the first4741* non-native property.4742*/4743start = plp;4744while (*start != NULL) {4745if ((*start)->pl_prop == ZPROP_USERPROP)4746break;4747start = &(*start)->pl_next;4748}47494750elem = NULL;4751while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {4752/*4753* See if we've already found this property in our list.4754*/4755for (last = start; *last != NULL;4756last = &(*last)->pl_next) {4757if (strcmp((*last)->pl_user_prop,4758nvpair_name(elem)) == 0)4759break;4760}47614762if (*last == NULL) {4763entry = zfs_alloc(hdl, sizeof (zprop_list_t));4764entry->pl_user_prop =4765zfs_strdup(hdl, nvpair_name(elem));4766entry->pl_prop = ZPROP_USERPROP;4767entry->pl_width = strlen(nvpair_name(elem));4768entry->pl_all = B_TRUE;4769*last = entry;4770}4771}4772}47734774/*4775* Now go through and check the width of any non-fixed columns4776*/4777for (entry = *plp; entry != NULL; entry = entry->pl_next) {4778if (entry->pl_fixed && !literal)4779continue;47804781if (entry->pl_prop != ZPROP_USERPROP) {4782if (zfs_prop_get(zhp, entry->pl_prop,4783buf, sizeof (buf), NULL, NULL, 0, literal) == 0) {4784if (strlen(buf) > entry->pl_width)4785entry->pl_width = strlen(buf);4786}4787if (received && zfs_prop_get_recvd(zhp,4788zfs_prop_to_name(entry->pl_prop),4789buf, sizeof (buf), literal) == 0)4790if (strlen(buf) > entry->pl_recvd_width)4791entry->pl_recvd_width = strlen(buf);4792} else {4793if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,4794&propval) == 0) {4795strval = fnvlist_lookup_string(propval,4796ZPROP_VALUE);4797if (strlen(strval) > entry->pl_width)4798entry->pl_width = strlen(strval);4799}4800if (received && zfs_prop_get_recvd(zhp,4801entry->pl_user_prop,4802buf, sizeof (buf), literal) == 0)4803if (strlen(buf) > entry->pl_recvd_width)4804entry->pl_recvd_width = strlen(buf);4805}4806}48074808return (0);4809}48104811void4812zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)4813{4814nvpair_t *curr;4815nvpair_t *next;48164817/*4818* Keep a reference to the props-table against which we prune the4819* properties.4820*/4821zhp->zfs_props_table = props;48224823curr = nvlist_next_nvpair(zhp->zfs_props, NULL);48244825while (curr) {4826zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));4827next = nvlist_next_nvpair(zhp->zfs_props, curr);48284829/*4830* User properties will result in ZPROP_USERPROP (an alias4831* for ZPROP_INVAL), and since we4832* only know how to prune standard ZFS properties, we always4833* leave these in the list. This can also happen if we4834* encounter an unknown DSL property (when running older4835* software, for example).4836*/4837if (zfs_prop != ZPROP_USERPROP && props[zfs_prop] == B_FALSE)4838(void) nvlist_remove(zhp->zfs_props,4839nvpair_name(curr), nvpair_type(curr));4840curr = next;4841}4842}48434844static int4845zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,4846zfs_smb_acl_op_t cmd, char *resource1, char *resource2)4847{4848zfs_cmd_t zc = {"\0"};4849nvlist_t *nvlist = NULL;4850int error;48514852(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));4853(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));4854zc.zc_cookie = (uint64_t)cmd;48554856if (cmd == ZFS_SMB_ACL_RENAME) {4857if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {4858(void) no_memory(hdl);4859return (0);4860}4861}48624863switch (cmd) {4864case ZFS_SMB_ACL_ADD:4865case ZFS_SMB_ACL_REMOVE:4866(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));4867break;4868case ZFS_SMB_ACL_RENAME:4869if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,4870resource1) != 0) {4871(void) no_memory(hdl);4872return (-1);4873}4874if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,4875resource2) != 0) {4876(void) no_memory(hdl);4877return (-1);4878}4879zcmd_write_src_nvlist(hdl, &zc, nvlist);4880break;4881case ZFS_SMB_ACL_PURGE:4882break;4883default:4884return (-1);4885}4886error = lzc_ioctl_fd(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);4887nvlist_free(nvlist);4888return (error);4889}48904891int4892zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,4893char *path, char *resource)4894{4895return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,4896resource, NULL));4897}48984899int4900zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,4901char *path, char *resource)4902{4903return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,4904resource, NULL));4905}49064907int4908zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)4909{4910return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,4911NULL, NULL));4912}49134914int4915zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,4916char *oldname, char *newname)4917{4918return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,4919oldname, newname));4920}49214922int4923zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,4924zfs_userspace_cb_t func, void *arg)4925{4926zfs_cmd_t zc = {"\0"};4927zfs_useracct_t buf[100];4928libzfs_handle_t *hdl = zhp->zfs_hdl;4929int ret;49304931(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));49324933zc.zc_objset_type = type;4934zc.zc_nvlist_dst = (uintptr_t)buf;49354936for (;;) {4937zfs_useracct_t *zua = buf;49384939zc.zc_nvlist_dst_size = sizeof (buf);4940if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {4941if ((errno == ENOTSUP &&4942(type == ZFS_PROP_USEROBJUSED ||4943type == ZFS_PROP_GROUPOBJUSED ||4944type == ZFS_PROP_USEROBJQUOTA ||4945type == ZFS_PROP_GROUPOBJQUOTA ||4946type == ZFS_PROP_PROJECTOBJUSED ||4947type == ZFS_PROP_PROJECTOBJQUOTA ||4948type == ZFS_PROP_PROJECTUSED ||4949type == ZFS_PROP_PROJECTQUOTA)))4950break;49514952return (zfs_standard_error_fmt(hdl, errno,4953dgettext(TEXT_DOMAIN,4954"cannot get used/quota for %s"), zc.zc_name));4955}4956if (zc.zc_nvlist_dst_size == 0)4957break;49584959while (zc.zc_nvlist_dst_size > 0) {4960if ((ret = func(arg, zua->zu_domain, zua->zu_rid,4961zua->zu_space, zc.zc_guid)) != 0)4962return (ret);4963zua++;4964zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);4965}4966}49674968return (0);4969}49704971struct holdarg {4972nvlist_t *nvl;4973const char *snapname;4974const char *tag;4975boolean_t recursive;4976int error;4977};49784979static int4980zfs_hold_one(zfs_handle_t *zhp, void *arg)4981{4982struct holdarg *ha = arg;4983char name[ZFS_MAX_DATASET_NAME_LEN];4984int rv = 0;49854986if (snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name,4987ha->snapname) >= sizeof (name))4988return (EINVAL);49894990if (lzc_exists(name))4991fnvlist_add_string(ha->nvl, name, ha->tag);49924993if (ha->recursive)4994rv = zfs_iter_filesystems_v2(zhp, 0, zfs_hold_one, ha);4995zfs_close(zhp);4996return (rv);4997}49984999int5000zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,5001boolean_t recursive, int cleanup_fd)5002{5003int ret;5004struct holdarg ha;50055006ha.nvl = fnvlist_alloc();5007ha.snapname = snapname;5008ha.tag = tag;5009ha.recursive = recursive;5010(void) zfs_hold_one(zfs_handle_dup(zhp), &ha);50115012if (nvlist_empty(ha.nvl)) {5013char errbuf[ERRBUFLEN];50145015fnvlist_free(ha.nvl);5016ret = ENOENT;5017(void) snprintf(errbuf, sizeof (errbuf),5018dgettext(TEXT_DOMAIN,5019"cannot hold snapshot '%s@%s'"),5020zhp->zfs_name, snapname);5021(void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);5022return (ret);5023}50245025ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);5026fnvlist_free(ha.nvl);50275028return (ret);5029}50305031int5032zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)5033{5034int ret;5035nvlist_t *errors;5036libzfs_handle_t *hdl = zhp->zfs_hdl;5037char errbuf[ERRBUFLEN];5038nvpair_t *elem;50395040errors = NULL;5041ret = lzc_hold(holds, cleanup_fd, &errors);50425043if (ret == 0) {5044/* There may be errors even in the success case. */5045fnvlist_free(errors);5046return (0);5047}50485049if (nvlist_empty(errors)) {5050/* no hold-specific errors */5051(void) snprintf(errbuf, sizeof (errbuf),5052dgettext(TEXT_DOMAIN, "cannot hold"));5053switch (ret) {5054case ENOTSUP:5055zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,5056"pool must be upgraded"));5057(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);5058break;5059case EINVAL:5060(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);5061break;5062default:5063(void) zfs_standard_error(hdl, ret, errbuf);5064}5065}50665067for (elem = nvlist_next_nvpair(errors, NULL);5068elem != NULL;5069elem = nvlist_next_nvpair(errors, elem)) {5070(void) snprintf(errbuf, sizeof (errbuf),5071dgettext(TEXT_DOMAIN,5072"cannot hold snapshot '%s'"), nvpair_name(elem));5073switch (fnvpair_value_int32(elem)) {5074case E2BIG:5075/*5076* Temporary tags wind up having the ds object id5077* prepended. So even if we passed the length check5078* above, it's still possible for the tag to wind5079* up being slightly too long.5080*/5081(void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);5082break;5083case EINVAL:5084(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);5085break;5086case EEXIST:5087(void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);5088break;5089default:5090(void) zfs_standard_error(hdl,5091fnvpair_value_int32(elem), errbuf);5092}5093}50945095fnvlist_free(errors);5096return (ret);5097}50985099static int5100zfs_release_one(zfs_handle_t *zhp, void *arg)5101{5102struct holdarg *ha = arg;5103char name[ZFS_MAX_DATASET_NAME_LEN];5104int rv = 0;5105nvlist_t *existing_holds;51065107if (snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name,5108ha->snapname) >= sizeof (name)) {5109ha->error = EINVAL;5110rv = EINVAL;5111}51125113if (lzc_get_holds(name, &existing_holds) != 0) {5114ha->error = ENOENT;5115} else if (!nvlist_exists(existing_holds, ha->tag)) {5116ha->error = ESRCH;5117} else {5118nvlist_t *torelease = fnvlist_alloc();5119fnvlist_add_boolean(torelease, ha->tag);5120fnvlist_add_nvlist(ha->nvl, name, torelease);5121fnvlist_free(torelease);5122}51235124if (ha->recursive)5125rv = zfs_iter_filesystems_v2(zhp, 0, zfs_release_one, ha);5126zfs_close(zhp);5127return (rv);5128}51295130int5131zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,5132boolean_t recursive)5133{5134int ret;5135struct holdarg ha;5136nvlist_t *errors = NULL;5137nvpair_t *elem;5138libzfs_handle_t *hdl = zhp->zfs_hdl;5139char errbuf[ERRBUFLEN];51405141ha.nvl = fnvlist_alloc();5142ha.snapname = snapname;5143ha.tag = tag;5144ha.recursive = recursive;5145ha.error = 0;5146(void) zfs_release_one(zfs_handle_dup(zhp), &ha);51475148if (nvlist_empty(ha.nvl)) {5149fnvlist_free(ha.nvl);5150ret = ha.error;5151(void) snprintf(errbuf, sizeof (errbuf),5152dgettext(TEXT_DOMAIN,5153"cannot release hold from snapshot '%s@%s'"),5154zhp->zfs_name, snapname);5155if (ret == ESRCH) {5156(void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);5157} else {5158(void) zfs_standard_error(hdl, ret, errbuf);5159}5160return (ret);5161}51625163ret = lzc_release(ha.nvl, &errors);5164fnvlist_free(ha.nvl);51655166if (ret == 0) {5167/* There may be errors even in the success case. */5168fnvlist_free(errors);5169return (0);5170}51715172if (nvlist_empty(errors)) {5173/* no hold-specific errors */5174(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,5175"cannot release"));5176switch (errno) {5177case ENOTSUP:5178zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,5179"pool must be upgraded"));5180(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);5181break;5182default:5183(void) zfs_standard_error(hdl, errno, errbuf);5184}5185}51865187for (elem = nvlist_next_nvpair(errors, NULL);5188elem != NULL;5189elem = nvlist_next_nvpair(errors, elem)) {5190(void) snprintf(errbuf, sizeof (errbuf),5191dgettext(TEXT_DOMAIN,5192"cannot release hold from snapshot '%s'"),5193nvpair_name(elem));5194switch (fnvpair_value_int32(elem)) {5195case ESRCH:5196(void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);5197break;5198case EINVAL:5199(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);5200break;5201default:5202(void) zfs_standard_error(hdl,5203fnvpair_value_int32(elem), errbuf);5204}5205}52065207fnvlist_free(errors);5208return (ret);5209}52105211int5212zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)5213{5214zfs_cmd_t zc = {"\0"};5215libzfs_handle_t *hdl = zhp->zfs_hdl;5216int nvsz = 2048;5217void *nvbuf;5218int err = 0;5219char errbuf[ERRBUFLEN];52205221assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||5222zhp->zfs_type == ZFS_TYPE_FILESYSTEM);52235224tryagain:52255226nvbuf = malloc(nvsz);5227if (nvbuf == NULL) {5228err = (zfs_error(hdl, EZFS_NOMEM, zfs_strerror(errno)));5229goto out;5230}52315232zc.zc_nvlist_dst_size = nvsz;5233zc.zc_nvlist_dst = (uintptr_t)nvbuf;52345235(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));52365237if (zfs_ioctl(hdl, ZFS_IOC_GET_FSACL, &zc) != 0) {5238(void) snprintf(errbuf, sizeof (errbuf),5239dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),5240zc.zc_name);5241switch (errno) {5242case ENOMEM:5243free(nvbuf);5244nvsz = zc.zc_nvlist_dst_size;5245goto tryagain;52465247case ENOTSUP:5248zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,5249"pool must be upgraded"));5250err = zfs_error(hdl, EZFS_BADVERSION, errbuf);5251break;5252case EINVAL:5253err = zfs_error(hdl, EZFS_BADTYPE, errbuf);5254break;5255case ENOENT:5256err = zfs_error(hdl, EZFS_NOENT, errbuf);5257break;5258default:5259err = zfs_standard_error(hdl, errno, errbuf);5260break;5261}5262} else {5263/* success */5264int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);5265if (rc) {5266err = zfs_standard_error_fmt(hdl, rc, dgettext(5267TEXT_DOMAIN, "cannot get permissions on '%s'"),5268zc.zc_name);5269}5270}52715272free(nvbuf);5273out:5274return (err);5275}52765277int5278zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)5279{5280zfs_cmd_t zc = {"\0"};5281libzfs_handle_t *hdl = zhp->zfs_hdl;5282char *nvbuf;5283char errbuf[ERRBUFLEN];5284size_t nvsz;5285int err;52865287assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||5288zhp->zfs_type == ZFS_TYPE_FILESYSTEM);52895290err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);5291assert(err == 0);52925293nvbuf = malloc(nvsz);52945295err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);5296assert(err == 0);52975298zc.zc_nvlist_src_size = nvsz;5299zc.zc_nvlist_src = (uintptr_t)nvbuf;5300zc.zc_perm_action = un;53015302(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));53035304if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {5305(void) snprintf(errbuf, sizeof (errbuf),5306dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),5307zc.zc_name);5308switch (errno) {5309case ENOTSUP:5310zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,5311"pool must be upgraded"));5312err = zfs_error(hdl, EZFS_BADVERSION, errbuf);5313break;5314case EINVAL:5315err = zfs_error(hdl, EZFS_BADTYPE, errbuf);5316break;5317case ENOENT:5318err = zfs_error(hdl, EZFS_NOENT, errbuf);5319break;5320default:5321err = zfs_standard_error(hdl, errno, errbuf);5322break;5323}5324}53255326free(nvbuf);53275328return (err);5329}53305331int5332zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)5333{5334int err;5335char errbuf[ERRBUFLEN];53365337err = lzc_get_holds(zhp->zfs_name, nvl);53385339if (err != 0) {5340libzfs_handle_t *hdl = zhp->zfs_hdl;53415342(void) snprintf(errbuf, sizeof (errbuf),5343dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),5344zhp->zfs_name);5345switch (err) {5346case ENOTSUP:5347zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,5348"pool must be upgraded"));5349err = zfs_error(hdl, EZFS_BADVERSION, errbuf);5350break;5351case EINVAL:5352err = zfs_error(hdl, EZFS_BADTYPE, errbuf);5353break;5354case ENOENT:5355err = zfs_error(hdl, EZFS_NOENT, errbuf);5356break;5357default:5358err = zfs_standard_error(hdl, errno, errbuf);5359break;5360}5361}53625363return (err);5364}53655366/*5367* The theory of raidz space accounting5368*5369* The "referenced" property of RAIDZ vdevs is scaled such that a 128KB block5370* will "reference" 128KB, even though it allocates more than that, to store the5371* parity information (and perhaps skip sectors). This concept of the5372* "referenced" (and other DMU space accounting) being lower than the allocated5373* space by a constant factor is called "raidz deflation."5374*5375* As mentioned above, the constant factor for raidz deflation assumes a 128KB5376* block size. However, zvols typically have a much smaller block size (default5377* 8KB). These smaller blocks may require proportionally much more parity5378* information (and perhaps skip sectors). In this case, the change to the5379* "referenced" property may be much more than the logical block size.5380*5381* Suppose a raidz vdev has 5 disks with ashift=12. A 128k block may be written5382* as follows.5383*5384* +-------+-------+-------+-------+-------+5385* | disk1 | disk2 | disk3 | disk4 | disk5 |5386* +-------+-------+-------+-------+-------+5387* | P0 | D0 | D8 | D16 | D24 |5388* | P1 | D1 | D9 | D17 | D25 |5389* | P2 | D2 | D10 | D18 | D26 |5390* | P3 | D3 | D11 | D19 | D27 |5391* | P4 | D4 | D12 | D20 | D28 |5392* | P5 | D5 | D13 | D21 | D29 |5393* | P6 | D6 | D14 | D22 | D30 |5394* | P7 | D7 | D15 | D23 | D31 |5395* +-------+-------+-------+-------+-------+5396*5397* Above, notice that 160k was allocated: 8 x 4k parity sectors + 32 x 4k data5398* sectors. The dataset's referenced will increase by 128k and the pool's5399* allocated and free properties will be adjusted by 160k.5400*5401* A 4k block written to the same raidz vdev will require two 4k sectors. The5402* blank cells represent unallocated space.5403*5404* +-------+-------+-------+-------+-------+5405* | disk1 | disk2 | disk3 | disk4 | disk5 |5406* +-------+-------+-------+-------+-------+5407* | P0 | D0 | | | |5408* +-------+-------+-------+-------+-------+5409*5410* Above, notice that the 4k block required one sector for parity and another5411* for data. vdev_raidz_psize_to_asize() will return 8k and as such the pool's5412* allocated and free properties will be adjusted by 8k. The dataset will not5413* be charged 8k. Rather, it will be charged a value that is scaled according5414* to the overhead of the 128k block on the same vdev. This 8k allocation will5415* be charged 8k * 128k / 160k. 128k is from SPA_OLD_MAXBLOCKSIZE and 160k is5416* as calculated in the 128k block example above.5417*5418* Every raidz allocation is sized to be a multiple of nparity+1 sectors. That5419* is, every raidz1 allocation will be a multiple of 2 sectors, raidz25420* allocations are a multiple of 3 sectors, and raidz3 allocations are a5421* multiple of of 4 sectors. When a block does not fill the required number of5422* sectors, skip blocks (sectors) are used.5423*5424* An 8k block being written to a raidz vdev may be written as follows:5425*5426* +-------+-------+-------+-------+-------+5427* | disk1 | disk2 | disk3 | disk4 | disk5 |5428* +-------+-------+-------+-------+-------+5429* | P0 | D0 | D1 | S0 | |5430* +-------+-------+-------+-------+-------+5431*5432* In order to maintain the nparity+1 allocation size, a skip block (S0) was5433* added. For this 8k block, the pool's allocated and free properties are5434* adjusted by 16k and the dataset's referenced is increased by 16k * 128k /5435* 160k. Again, 128k is from SPA_OLD_MAXBLOCKSIZE and 160k is as calculated in5436* the 128k block example above.5437*5438* The situation is slightly different for dRAID since the minimum allocation5439* size is the full group width. The same 8K block above would be written as5440* follows in a dRAID group:5441*5442* +-------+-------+-------+-------+-------+5443* | disk1 | disk2 | disk3 | disk4 | disk5 |5444* +-------+-------+-------+-------+-------+5445* | P0 | D0 | D1 | S0 | S1 |5446* +-------+-------+-------+-------+-------+5447*5448* Compression may lead to a variety of block sizes being written for the same5449* volume or file. There is no clear way to reserve just the amount of space5450* that will be required, so the worst case (no compression) is assumed.5451* Note that metadata blocks will typically be compressed, so the reservation5452* size returned by zvol_volsize_to_reservation() will generally be slightly5453* larger than the maximum that the volume can reference.5454*/54555456/*5457* Derived from function of same name in module/zfs/vdev_raidz.c. Returns the5458* amount of space (in bytes) that will be allocated for the specified block5459* size. Note that the "referenced" space accounted will be less than this, but5460* not necessarily equal to "blksize", due to RAIDZ deflation.5461*/5462static uint64_t5463vdev_raidz_psize_to_asize(uint64_t ndisks, uint64_t nparity, uint64_t ashift,5464uint64_t blksize)5465{5466uint64_t asize, ndata;54675468ASSERT3U(ndisks, >, nparity);5469ndata = ndisks - nparity;5470asize = ((blksize - 1) >> ashift) + 1;5471asize += nparity * ((asize + ndata - 1) / ndata);5472asize = roundup(asize, nparity + 1) << ashift;54735474return (asize);5475}54765477/*5478* Derived from function of same name in module/zfs/vdev_draid.c. Returns the5479* amount of space (in bytes) that will be allocated for the specified block5480* size.5481*/5482static uint64_t5483vdev_draid_psize_to_asize(uint64_t ndisks, uint64_t nparity, uint64_t ashift,5484uint64_t blksize)5485{5486ASSERT3U(ndisks, >, nparity);5487uint64_t ndata = ndisks - nparity;5488uint64_t rows = ((blksize - 1) / (ndata << ashift)) + 1;5489uint64_t asize = (rows * ndisks) << ashift;54905491return (asize);5492}54935494/*5495* Determine how much space will be allocated if it lands on the most space-5496* inefficient top-level vdev. Returns the size in bytes required to store one5497* copy of the volume data. See theory comment above.5498*/5499static uint64_t5500volsize_from_vdevs(zpool_handle_t *zhp, uint64_t nblocks, uint64_t blksize)5501{5502nvlist_t *config, *tree, **vdevs;5503uint_t nvdevs;5504uint64_t ret = 0;55055506config = zpool_get_config(zhp, NULL);5507if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree) != 0 ||5508nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN,5509&vdevs, &nvdevs) != 0) {5510return (nblocks * blksize);5511}55125513for (int v = 0; v < nvdevs; v++) {5514const char *type;5515uint64_t nparity, ashift, asize, tsize;5516uint64_t volsize;55175518if (nvlist_lookup_string(vdevs[v], ZPOOL_CONFIG_TYPE,5519&type) != 0)5520continue;55215522if (strcmp(type, VDEV_TYPE_RAIDZ) != 0 &&5523strcmp(type, VDEV_TYPE_DRAID) != 0)5524continue;55255526if (nvlist_lookup_uint64(vdevs[v],5527ZPOOL_CONFIG_NPARITY, &nparity) != 0)5528continue;55295530if (nvlist_lookup_uint64(vdevs[v],5531ZPOOL_CONFIG_ASHIFT, &ashift) != 0)5532continue;55335534if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {5535nvlist_t **disks;5536uint_t ndisks;55375538if (nvlist_lookup_nvlist_array(vdevs[v],5539ZPOOL_CONFIG_CHILDREN, &disks, &ndisks) != 0)5540continue;55415542/* allocation size for the "typical" 128k block */5543tsize = vdev_raidz_psize_to_asize(ndisks, nparity,5544ashift, SPA_OLD_MAXBLOCKSIZE);55455546/* allocation size for the blksize block */5547asize = vdev_raidz_psize_to_asize(ndisks, nparity,5548ashift, blksize);5549} else {5550uint64_t ndata;55515552if (nvlist_lookup_uint64(vdevs[v],5553ZPOOL_CONFIG_DRAID_NDATA, &ndata) != 0)5554continue;55555556/* allocation size for the "typical" 128k block */5557tsize = vdev_draid_psize_to_asize(ndata + nparity,5558nparity, ashift, SPA_OLD_MAXBLOCKSIZE);55595560/* allocation size for the blksize block */5561asize = vdev_draid_psize_to_asize(ndata + nparity,5562nparity, ashift, blksize);5563}55645565/*5566* Scale this size down as a ratio of 128k / tsize.5567* See theory statement above.5568*5569* Bitshift is to avoid the case of nblocks * asize < tsize5570* producing a size of 0.5571*/5572volsize = (nblocks * asize) / (tsize >> SPA_MINBLOCKSHIFT);5573/*5574* If we would blow UINT64_MAX with this next multiplication,5575* don't.5576*/5577if (volsize >5578(UINT64_MAX / (SPA_OLD_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT)))5579volsize = UINT64_MAX;5580else5581volsize *= (SPA_OLD_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);55825583if (volsize > ret) {5584ret = volsize;5585}5586}55875588if (ret == 0) {5589ret = nblocks * blksize;5590}55915592return (ret);5593}55945595/*5596* Convert the zvol's volume size to an appropriate reservation. See theory5597* comment above.5598*5599* Note: If this routine is updated, it is necessary to update the ZFS test5600* suite's shell version in reservation.shlib.5601*/5602uint64_t5603zvol_volsize_to_reservation(zpool_handle_t *zph, uint64_t volsize,5604nvlist_t *props)5605{5606uint64_t numdb;5607uint64_t nblocks, volblocksize;5608int ncopies;5609const char *strval;56105611if (nvlist_lookup_string(props,5612zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)5613ncopies = atoi(strval);5614else5615ncopies = 1;5616if (nvlist_lookup_uint64(props,5617zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),5618&volblocksize) != 0)5619volblocksize = ZVOL_DEFAULT_BLOCKSIZE;56205621nblocks = volsize / volblocksize;5622/*5623* Metadata defaults to using 128k blocks, not volblocksize blocks. For5624* this reason, only the data blocks are scaled based on vdev config.5625*/5626volsize = volsize_from_vdevs(zph, nblocks, volblocksize);56275628/* start with metadnode L0-L6 */5629numdb = 7;5630/* calculate number of indirects */5631while (nblocks > 1) {5632nblocks += DNODES_PER_LEVEL - 1;5633nblocks /= DNODES_PER_LEVEL;5634numdb += nblocks;5635}5636numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);5637volsize *= ncopies;5638/*5639* this is exactly DN_MAX_INDBLKSHIFT when metadata isn't5640* compressed, but in practice they compress down to about5641* 1100 bytes5642*/5643numdb *= 1ULL << DN_MAX_INDBLKSHIFT;5644volsize += numdb;5645return (volsize);5646}56475648/*5649* Wait for the given activity and return the status of the wait (whether or not5650* any waiting was done) in the 'waited' parameter. Non-existent fses are5651* reported via the 'missing' parameter, rather than by printing an error5652* message. This is convenient when this function is called in a loop over a5653* long period of time (as it is, for example, by zfs's wait cmd). In that5654* scenario, a fs being exported or destroyed should be considered a normal5655* event, so we don't want to print an error when we find that the fs doesn't5656* exist.5657*/5658int5659zfs_wait_status(zfs_handle_t *zhp, zfs_wait_activity_t activity,5660boolean_t *missing, boolean_t *waited)5661{5662int error = lzc_wait_fs(zhp->zfs_name, activity, waited);5663*missing = (error == ENOENT);5664if (*missing)5665return (0);56665667if (error != 0) {5668(void) zfs_standard_error_fmt(zhp->zfs_hdl, error,5669dgettext(TEXT_DOMAIN, "error waiting in fs '%s'"),5670zhp->zfs_name);5671}56725673return (error);5674}567556765677