1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5ea8dc4b6Seschrock * Common Development and Distribution License (the "License"). 6ea8dc4b6Seschrock * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21f3861e1aSahl 22fa9e4066Sahrens /* 2336db6475SEric Taylor * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 241c10ae76SMike Gerdts * Copyright (c) 2018, Joyent, Inc. All rights reserved. 25ad2760acSMatthew Ahrens * Copyright (c) 2011, 2016 by Delphix. All rights reserved. 26f0f3ef5aSGarrett D'Amore * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. 270d8fa8f8SMartin Matuska * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. 28013023d4SMartin Matuska * Copyright (c) 2013 Martin Matuska. All rights reserved. 29a7a845e4SSteven Hartland * Copyright (c) 2013 Steven Hartland. All rights reserved. 30c3d26abcSMatthew Ahrens * Copyright (c) 2014 Integros [integros.com] 319fa2266dSYuri Pankov * Copyright 2017 Nexenta Systems, Inc. 3288f61deeSIgor Kozhukhov * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> 33a4b8c9aaSAndrew Stormont * Copyright 2017 RackTop Systems. 34fa9e4066Sahrens */ 35fa9e4066Sahrens 36fa9e4066Sahrens #include <ctype.h> 37fa9e4066Sahrens #include <errno.h> 38fa9e4066Sahrens #include <libintl.h> 39fa9e4066Sahrens #include <math.h> 40fa9e4066Sahrens #include <stdio.h> 41fa9e4066Sahrens #include <stdlib.h> 42fa9e4066Sahrens #include <strings.h> 43fa9e4066Sahrens #include <unistd.h> 443cb34c60Sahrens #include <stddef.h> 45fa9e4066Sahrens #include <zone.h> 4699653d4eSeschrock #include <fcntl.h> 47fa9e4066Sahrens #include <sys/mntent.h> 48b12a1c38Slling #include <sys/mount.h> 49ecd6cf80Smarks #include <priv.h> 50ecd6cf80Smarks #include <pwd.h> 51ecd6cf80Smarks #include <grp.h> 52ecd6cf80Smarks #include <stddef.h> 53ecd6cf80Smarks #include <ucred.h> 5414843421SMatthew Ahrens #include <idmap.h> 5514843421SMatthew Ahrens #include <aclutils.h> 563b12c289SMatthew Ahrens #include <directory.h> 57*591e0e13SSebastien Roy #include <time.h> 58fa9e4066Sahrens 59c1449561SEric Taylor #include <sys/dnode.h> 60fa9e4066Sahrens #include <sys/spa.h> 61e9dbad6fSeschrock #include <sys/zap.h> 62fa9e4066Sahrens #include <libzfs.h> 63fa9e4066Sahrens 64fa9e4066Sahrens #include "zfs_namecheck.h" 65fa9e4066Sahrens #include "zfs_prop.h" 66fa9e4066Sahrens #include "libzfs_impl.h" 67ecd6cf80Smarks #include "zfs_deleg.h" 68fa9e4066Sahrens 6914843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned, 7014843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); 71cdf5b4caSmmusante 72fa9e4066Sahrens /* 73fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 74fa9e4066Sahrens * readable form. 75fa9e4066Sahrens */ 76fa9e4066Sahrens const char * 77fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 78fa9e4066Sahrens { 79fa9e4066Sahrens switch (type) { 80fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 81fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 82fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 83fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 84fa9e4066Sahrens case ZFS_TYPE_VOLUME: 85fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 8688f61deeSIgor Kozhukhov case ZFS_TYPE_POOL: 8788f61deeSIgor Kozhukhov return (dgettext(TEXT_DOMAIN, "pool")); 8888f61deeSIgor Kozhukhov case ZFS_TYPE_BOOKMARK: 8988f61deeSIgor Kozhukhov return (dgettext(TEXT_DOMAIN, "bookmark")); 9088f61deeSIgor Kozhukhov default: 9188f61deeSIgor Kozhukhov assert(!"unhandled zfs_type_t"); 92fa9e4066Sahrens } 93fa9e4066Sahrens 94fa9e4066Sahrens return (NULL); 95fa9e4066Sahrens } 96fa9e4066Sahrens 97fa9e4066Sahrens /* 98fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 9914843421SMatthew Ahrens * provide a more meaningful error message. We call zfs_error_aux() to 10014843421SMatthew Ahrens * explain exactly why the name was not valid. 101fa9e4066Sahrens */ 10299d5e173STim Haley int 103f18faf3fSek zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 104f18faf3fSek boolean_t modifying) 105fa9e4066Sahrens { 106fa9e4066Sahrens namecheck_err_t why; 107fa9e4066Sahrens char what; 108fa9e4066Sahrens 109edb901aaSMarcel Telka if (entity_namecheck(path, &why, &what) != 0) { 11099653d4eSeschrock if (hdl != NULL) { 111fa9e4066Sahrens switch (why) { 112b81d61a6Slling case NAME_ERR_TOOLONG: 11399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11499653d4eSeschrock "name is too long")); 115b81d61a6Slling break; 116b81d61a6Slling 117fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 11899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 11999653d4eSeschrock "leading slash in name")); 120fa9e4066Sahrens break; 121fa9e4066Sahrens 122fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 12399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12499653d4eSeschrock "empty component in name")); 125fa9e4066Sahrens break; 126fa9e4066Sahrens 127fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 12899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12999653d4eSeschrock "trailing slash in name")); 130fa9e4066Sahrens break; 131fa9e4066Sahrens 132fa9e4066Sahrens case NAME_ERR_INVALCHAR: 13399653d4eSeschrock zfs_error_aux(hdl, 134fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 13599653d4eSeschrock "'%c' in name"), what); 136fa9e4066Sahrens break; 137fa9e4066Sahrens 138edb901aaSMarcel Telka case NAME_ERR_MULTIPLE_DELIMITERS: 13999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 140edb901aaSMarcel Telka "multiple '@' and/or '#' delimiters in " 141edb901aaSMarcel Telka "name")); 142fa9e4066Sahrens break; 1435ad82045Snd 1445ad82045Snd case NAME_ERR_NOLETTER: 1455ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1465ad82045Snd "pool doesn't begin with a letter")); 1475ad82045Snd break; 1485ad82045Snd 1495ad82045Snd case NAME_ERR_RESERVED: 1505ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1515ad82045Snd "name is reserved")); 1525ad82045Snd break; 1535ad82045Snd 1545ad82045Snd case NAME_ERR_DISKLIKE: 1555ad82045Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1565ad82045Snd "reserved disk name")); 1575ad82045Snd break; 15888f61deeSIgor Kozhukhov 15988f61deeSIgor Kozhukhov default: 16088f61deeSIgor Kozhukhov zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 16188f61deeSIgor Kozhukhov "(%d) not defined"), why); 16288f61deeSIgor Kozhukhov break; 163fa9e4066Sahrens } 164fa9e4066Sahrens } 165fa9e4066Sahrens 166fa9e4066Sahrens return (0); 167fa9e4066Sahrens } 168fa9e4066Sahrens 169fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 17099653d4eSeschrock if (hdl != NULL) 17199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 172edb901aaSMarcel Telka "snapshot delimiter '@' is not expected here")); 173fa9e4066Sahrens return (0); 174fa9e4066Sahrens } 175fa9e4066Sahrens 1761d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 1771d452cf5Sahrens if (hdl != NULL) 1781d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 179d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 1801d452cf5Sahrens return (0); 1811d452cf5Sahrens } 1821d452cf5Sahrens 183edb901aaSMarcel Telka if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) { 184edb901aaSMarcel Telka if (hdl != NULL) 185edb901aaSMarcel Telka zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 186edb901aaSMarcel Telka "bookmark delimiter '#' is not expected here")); 187edb901aaSMarcel Telka return (0); 188edb901aaSMarcel Telka } 189edb901aaSMarcel Telka 190edb901aaSMarcel Telka if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) { 191edb901aaSMarcel Telka if (hdl != NULL) 192edb901aaSMarcel Telka zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 193edb901aaSMarcel Telka "missing '#' delimiter in bookmark name")); 194edb901aaSMarcel Telka return (0); 195edb901aaSMarcel Telka } 196edb901aaSMarcel Telka 197f18faf3fSek if (modifying && strchr(path, '%') != NULL) { 198f18faf3fSek if (hdl != NULL) 199f18faf3fSek zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 200f18faf3fSek "invalid character %c in name"), '%'); 201f18faf3fSek return (0); 202f18faf3fSek } 203f18faf3fSek 20499653d4eSeschrock return (-1); 205fa9e4066Sahrens } 206fa9e4066Sahrens 207fa9e4066Sahrens int 208fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 209fa9e4066Sahrens { 210e7cbe64fSgw if (type == ZFS_TYPE_POOL) 211e7cbe64fSgw return (zpool_name_valid(NULL, B_FALSE, name)); 212f18faf3fSek return (zfs_validate_name(NULL, name, type, B_FALSE)); 213fa9e4066Sahrens } 214fa9e4066Sahrens 215e9dbad6fSeschrock /* 216e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 217e9dbad6fSeschrock * properties into a separate nvlist. 218e9dbad6fSeschrock */ 219fac3008cSeschrock static nvlist_t * 220fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 221e9dbad6fSeschrock { 222e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 223e9dbad6fSeschrock nvpair_t *elem; 224e9dbad6fSeschrock nvlist_t *propval; 225fac3008cSeschrock nvlist_t *nvl; 226e9dbad6fSeschrock 227fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 228fac3008cSeschrock (void) no_memory(hdl); 229fac3008cSeschrock return (NULL); 230fac3008cSeschrock } 231e9dbad6fSeschrock 232e9dbad6fSeschrock elem = NULL; 233fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 234e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 235e9dbad6fSeschrock continue; 236e9dbad6fSeschrock 237e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 238fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 239fac3008cSeschrock nvlist_free(nvl); 240fac3008cSeschrock (void) no_memory(hdl); 241fac3008cSeschrock return (NULL); 242fac3008cSeschrock } 243e9dbad6fSeschrock } 244e9dbad6fSeschrock 245fac3008cSeschrock return (nvl); 246e9dbad6fSeschrock } 247e9dbad6fSeschrock 24829ab75c9Srm static zpool_handle_t * 24929ab75c9Srm zpool_add_handle(zfs_handle_t *zhp, const char *pool_name) 25029ab75c9Srm { 25129ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 25229ab75c9Srm zpool_handle_t *zph; 25329ab75c9Srm 25429ab75c9Srm if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) { 25529ab75c9Srm if (hdl->libzfs_pool_handles != NULL) 25629ab75c9Srm zph->zpool_next = hdl->libzfs_pool_handles; 25729ab75c9Srm hdl->libzfs_pool_handles = zph; 25829ab75c9Srm } 25929ab75c9Srm return (zph); 26029ab75c9Srm } 26129ab75c9Srm 26229ab75c9Srm static zpool_handle_t * 26329ab75c9Srm zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len) 26429ab75c9Srm { 26529ab75c9Srm libzfs_handle_t *hdl = zhp->zfs_hdl; 26629ab75c9Srm zpool_handle_t *zph = hdl->libzfs_pool_handles; 26729ab75c9Srm 26829ab75c9Srm while ((zph != NULL) && 26929ab75c9Srm (strncmp(pool_name, zpool_get_name(zph), len) != 0)) 27029ab75c9Srm zph = zph->zpool_next; 27129ab75c9Srm return (zph); 27229ab75c9Srm } 27329ab75c9Srm 27429ab75c9Srm /* 27529ab75c9Srm * Returns a handle to the pool that contains the provided dataset. 27629ab75c9Srm * If a handle to that pool already exists then that handle is returned. 27729ab75c9Srm * Otherwise, a new handle is created and added to the list of handles. 27829ab75c9Srm */ 27929ab75c9Srm static zpool_handle_t * 28029ab75c9Srm zpool_handle(zfs_handle_t *zhp) 28129ab75c9Srm { 28229ab75c9Srm char *pool_name; 28329ab75c9Srm int len; 28429ab75c9Srm zpool_handle_t *zph; 28529ab75c9Srm 28678f17100SMatthew Ahrens len = strcspn(zhp->zfs_name, "/@#") + 1; 28729ab75c9Srm pool_name = zfs_alloc(zhp->zfs_hdl, len); 28829ab75c9Srm (void) strlcpy(pool_name, zhp->zfs_name, len); 28929ab75c9Srm 29029ab75c9Srm zph = zpool_find_handle(zhp, pool_name, len); 29129ab75c9Srm if (zph == NULL) 29229ab75c9Srm zph = zpool_add_handle(zhp, pool_name); 29329ab75c9Srm 29429ab75c9Srm free(pool_name); 29529ab75c9Srm return (zph); 29629ab75c9Srm } 29729ab75c9Srm 29829ab75c9Srm void 29929ab75c9Srm zpool_free_handles(libzfs_handle_t *hdl) 30029ab75c9Srm { 30129ab75c9Srm zpool_handle_t *next, *zph = hdl->libzfs_pool_handles; 30229ab75c9Srm 30329ab75c9Srm while (zph != NULL) { 30429ab75c9Srm next = zph->zpool_next; 30529ab75c9Srm zpool_close(zph); 30629ab75c9Srm zph = next; 30729ab75c9Srm } 30829ab75c9Srm hdl->libzfs_pool_handles = NULL; 30929ab75c9Srm } 31029ab75c9Srm 311fa9e4066Sahrens /* 312fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 313fa9e4066Sahrens */ 314fa9e4066Sahrens static int 315ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) 316fa9e4066Sahrens { 317e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 318fa9e4066Sahrens 319ebedde84SEric Taylor (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 320fa9e4066Sahrens 321ebedde84SEric Taylor while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { 3227f7322feSeschrock if (errno == ENOMEM) { 323ebedde84SEric Taylor if (zcmd_expand_dst_nvlist(hdl, zc) != 0) { 32499653d4eSeschrock return (-1); 325e9dbad6fSeschrock } 3267f7322feSeschrock } else { 3277f7322feSeschrock return (-1); 3287f7322feSeschrock } 3297f7322feSeschrock } 330ebedde84SEric Taylor return (0); 331ebedde84SEric Taylor } 332fa9e4066Sahrens 33392241e0bSTom Erickson /* 33492241e0bSTom Erickson * Utility function to get the received properties of the given object. 33592241e0bSTom Erickson */ 33692241e0bSTom Erickson static int 33792241e0bSTom Erickson get_recvd_props_ioctl(zfs_handle_t *zhp) 33892241e0bSTom Erickson { 33992241e0bSTom Erickson libzfs_handle_t *hdl = zhp->zfs_hdl; 34092241e0bSTom Erickson nvlist_t *recvdprops; 34192241e0bSTom Erickson zfs_cmd_t zc = { 0 }; 34292241e0bSTom Erickson int err; 34392241e0bSTom Erickson 34492241e0bSTom Erickson if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 34592241e0bSTom Erickson return (-1); 34692241e0bSTom Erickson 34792241e0bSTom Erickson (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 34892241e0bSTom Erickson 34992241e0bSTom Erickson while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { 35092241e0bSTom Erickson if (errno == ENOMEM) { 35192241e0bSTom Erickson if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 35292241e0bSTom Erickson return (-1); 35392241e0bSTom Erickson } 35492241e0bSTom Erickson } else { 35592241e0bSTom Erickson zcmd_free_nvlists(&zc); 35692241e0bSTom Erickson return (-1); 35792241e0bSTom Erickson } 35892241e0bSTom Erickson } 35992241e0bSTom Erickson 36092241e0bSTom Erickson err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); 36192241e0bSTom Erickson zcmd_free_nvlists(&zc); 36292241e0bSTom Erickson if (err != 0) 36392241e0bSTom Erickson return (-1); 36492241e0bSTom Erickson 36592241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 36692241e0bSTom Erickson zhp->zfs_recvd_props = recvdprops; 36792241e0bSTom Erickson 36892241e0bSTom Erickson return (0); 36992241e0bSTom Erickson } 37092241e0bSTom Erickson 371ebedde84SEric Taylor static int 372ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) 373ebedde84SEric Taylor { 374ebedde84SEric Taylor nvlist_t *allprops, *userprops; 375fa9e4066Sahrens 376ebedde84SEric Taylor zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */ 377ebedde84SEric Taylor 378ebedde84SEric Taylor if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) { 37999653d4eSeschrock return (-1); 38099653d4eSeschrock } 381fa9e4066Sahrens 38214843421SMatthew Ahrens /* 38314843421SMatthew Ahrens * XXX Why do we store the user props separately, in addition to 38414843421SMatthew Ahrens * storing them in zfs_props? 38514843421SMatthew Ahrens */ 386fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 387fac3008cSeschrock nvlist_free(allprops); 388e9dbad6fSeschrock return (-1); 389fac3008cSeschrock } 390fac3008cSeschrock 391fac3008cSeschrock nvlist_free(zhp->zfs_props); 392fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 393fac3008cSeschrock 394fac3008cSeschrock zhp->zfs_props = allprops; 395fac3008cSeschrock zhp->zfs_user_props = userprops; 39699653d4eSeschrock 397fa9e4066Sahrens return (0); 398fa9e4066Sahrens } 399fa9e4066Sahrens 400ebedde84SEric Taylor static int 401ebedde84SEric Taylor get_stats(zfs_handle_t *zhp) 402ebedde84SEric Taylor { 403ebedde84SEric Taylor int rc = 0; 404ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 405ebedde84SEric Taylor 406ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 407ebedde84SEric Taylor return (-1); 408ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) != 0) 409ebedde84SEric Taylor rc = -1; 410ebedde84SEric Taylor else if (put_stats_zhdl(zhp, &zc) != 0) 411ebedde84SEric Taylor rc = -1; 412ebedde84SEric Taylor zcmd_free_nvlists(&zc); 413ebedde84SEric Taylor return (rc); 414ebedde84SEric Taylor } 415ebedde84SEric Taylor 416fa9e4066Sahrens /* 417fa9e4066Sahrens * Refresh the properties currently stored in the handle. 418fa9e4066Sahrens */ 419fa9e4066Sahrens void 420fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 421fa9e4066Sahrens { 422fa9e4066Sahrens (void) get_stats(zhp); 423fa9e4066Sahrens } 424fa9e4066Sahrens 425fa9e4066Sahrens /* 426fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 427fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 428fa9e4066Sahrens */ 429ebedde84SEric Taylor static int 430ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc) 431fa9e4066Sahrens { 432503ad85cSMatthew Ahrens if (put_stats_zhdl(zhp, zc) != 0) 433ebedde84SEric Taylor return (-1); 43431fd60d3Sahrens 435fa9e4066Sahrens /* 436fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 437fa9e4066Sahrens * the high-level type. 438fa9e4066Sahrens */ 439a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 440a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 441a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 442a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 443a2eea2e1Sahrens else 444a2eea2e1Sahrens abort(); 445a2eea2e1Sahrens 446fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 447fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 448fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 449fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 450fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 451fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 452fa9e4066Sahrens else 45399653d4eSeschrock abort(); /* we should never see any other types */ 454fa9e4066Sahrens 4559fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) 4569fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States return (-1); 4579fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 458ebedde84SEric Taylor return (0); 459ebedde84SEric Taylor } 460ebedde84SEric Taylor 461ebedde84SEric Taylor zfs_handle_t * 462ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path) 463ebedde84SEric Taylor { 464ebedde84SEric Taylor zfs_cmd_t zc = { 0 }; 465ebedde84SEric Taylor 466ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 467ebedde84SEric Taylor 468ebedde84SEric Taylor if (zhp == NULL) 469ebedde84SEric Taylor return (NULL); 470ebedde84SEric Taylor 471ebedde84SEric Taylor zhp->zfs_hdl = hdl; 472ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 473ebedde84SEric Taylor if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) { 474ebedde84SEric Taylor free(zhp); 475ebedde84SEric Taylor return (NULL); 476ebedde84SEric Taylor } 477ebedde84SEric Taylor if (get_stats_ioctl(zhp, &zc) == -1) { 478ebedde84SEric Taylor zcmd_free_nvlists(&zc); 479ebedde84SEric Taylor free(zhp); 480ebedde84SEric Taylor return (NULL); 481ebedde84SEric Taylor } 482ebedde84SEric Taylor if (make_dataset_handle_common(zhp, &zc) == -1) { 483ebedde84SEric Taylor free(zhp); 484ebedde84SEric Taylor zhp = NULL; 485ebedde84SEric Taylor } 486ebedde84SEric Taylor zcmd_free_nvlists(&zc); 487ebedde84SEric Taylor return (zhp); 488ebedde84SEric Taylor } 489ebedde84SEric Taylor 49019b94df9SMatthew Ahrens zfs_handle_t * 491ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc) 492ebedde84SEric Taylor { 493ebedde84SEric Taylor zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 494ebedde84SEric Taylor 495ebedde84SEric Taylor if (zhp == NULL) 496ebedde84SEric Taylor return (NULL); 497ebedde84SEric Taylor 498ebedde84SEric Taylor zhp->zfs_hdl = hdl; 499ebedde84SEric Taylor (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 500ebedde84SEric Taylor if (make_dataset_handle_common(zhp, zc) == -1) { 501ebedde84SEric Taylor free(zhp); 502ebedde84SEric Taylor return (NULL); 503ebedde84SEric Taylor } 504fa9e4066Sahrens return (zhp); 505fa9e4066Sahrens } 506fa9e4066Sahrens 5070d8fa8f8SMartin Matuska zfs_handle_t * 5080d8fa8f8SMartin Matuska make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc) 5090d8fa8f8SMartin Matuska { 5100d8fa8f8SMartin Matuska zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 5110d8fa8f8SMartin Matuska 5120d8fa8f8SMartin Matuska if (zhp == NULL) 5130d8fa8f8SMartin Matuska return (NULL); 5140d8fa8f8SMartin Matuska 5150d8fa8f8SMartin Matuska zhp->zfs_hdl = pzhp->zfs_hdl; 5160d8fa8f8SMartin Matuska (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name)); 5170d8fa8f8SMartin Matuska zhp->zfs_head_type = pzhp->zfs_type; 5180d8fa8f8SMartin Matuska zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 5190d8fa8f8SMartin Matuska zhp->zpool_hdl = zpool_handle(zhp); 5200d8fa8f8SMartin Matuska return (zhp); 5210d8fa8f8SMartin Matuska } 5220d8fa8f8SMartin Matuska 52319b94df9SMatthew Ahrens zfs_handle_t * 52419b94df9SMatthew Ahrens zfs_handle_dup(zfs_handle_t *zhp_orig) 52519b94df9SMatthew Ahrens { 52619b94df9SMatthew Ahrens zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 52719b94df9SMatthew Ahrens 52819b94df9SMatthew Ahrens if (zhp == NULL) 52919b94df9SMatthew Ahrens return (NULL); 53019b94df9SMatthew Ahrens 53119b94df9SMatthew Ahrens zhp->zfs_hdl = zhp_orig->zfs_hdl; 53219b94df9SMatthew Ahrens zhp->zpool_hdl = zhp_orig->zpool_hdl; 53319b94df9SMatthew Ahrens (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name, 53419b94df9SMatthew Ahrens sizeof (zhp->zfs_name)); 53519b94df9SMatthew Ahrens zhp->zfs_type = zhp_orig->zfs_type; 53619b94df9SMatthew Ahrens zhp->zfs_head_type = zhp_orig->zfs_head_type; 53719b94df9SMatthew Ahrens zhp->zfs_dmustats = zhp_orig->zfs_dmustats; 53819b94df9SMatthew Ahrens if (zhp_orig->zfs_props != NULL) { 53919b94df9SMatthew Ahrens if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) { 54019b94df9SMatthew Ahrens (void) no_memory(zhp->zfs_hdl); 54119b94df9SMatthew Ahrens zfs_close(zhp); 54219b94df9SMatthew Ahrens return (NULL); 54319b94df9SMatthew Ahrens } 54419b94df9SMatthew Ahrens } 54519b94df9SMatthew Ahrens if (zhp_orig->zfs_user_props != NULL) { 54619b94df9SMatthew Ahrens if (nvlist_dup(zhp_orig->zfs_user_props, 54719b94df9SMatthew Ahrens &zhp->zfs_user_props, 0) != 0) { 54819b94df9SMatthew Ahrens (void) no_memory(zhp->zfs_hdl); 54919b94df9SMatthew Ahrens zfs_close(zhp); 55019b94df9SMatthew Ahrens return (NULL); 55119b94df9SMatthew Ahrens } 55219b94df9SMatthew Ahrens } 55319b94df9SMatthew Ahrens if (zhp_orig->zfs_recvd_props != NULL) { 55419b94df9SMatthew Ahrens if (nvlist_dup(zhp_orig->zfs_recvd_props, 55519b94df9SMatthew Ahrens &zhp->zfs_recvd_props, 0)) { 55619b94df9SMatthew Ahrens (void) no_memory(zhp->zfs_hdl); 55719b94df9SMatthew Ahrens zfs_close(zhp); 55819b94df9SMatthew Ahrens return (NULL); 55919b94df9SMatthew Ahrens } 56019b94df9SMatthew Ahrens } 56119b94df9SMatthew Ahrens zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck; 56219b94df9SMatthew Ahrens if (zhp_orig->zfs_mntopts != NULL) { 56319b94df9SMatthew Ahrens zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl, 56419b94df9SMatthew Ahrens zhp_orig->zfs_mntopts); 56519b94df9SMatthew Ahrens } 56619b94df9SMatthew Ahrens zhp->zfs_props_table = zhp_orig->zfs_props_table; 56719b94df9SMatthew Ahrens return (zhp); 56819b94df9SMatthew Ahrens } 56919b94df9SMatthew Ahrens 57078f17100SMatthew Ahrens boolean_t 57178f17100SMatthew Ahrens zfs_bookmark_exists(const char *path) 57278f17100SMatthew Ahrens { 57378f17100SMatthew Ahrens nvlist_t *bmarks; 57478f17100SMatthew Ahrens nvlist_t *props; 5759adfa60dSMatthew Ahrens char fsname[ZFS_MAX_DATASET_NAME_LEN]; 57678f17100SMatthew Ahrens char *bmark_name; 57778f17100SMatthew Ahrens char *pound; 57878f17100SMatthew Ahrens int err; 57978f17100SMatthew Ahrens boolean_t rv; 58078f17100SMatthew Ahrens 58178f17100SMatthew Ahrens 58278f17100SMatthew Ahrens (void) strlcpy(fsname, path, sizeof (fsname)); 58378f17100SMatthew Ahrens pound = strchr(fsname, '#'); 58478f17100SMatthew Ahrens if (pound == NULL) 58578f17100SMatthew Ahrens return (B_FALSE); 58678f17100SMatthew Ahrens 58778f17100SMatthew Ahrens *pound = '\0'; 58878f17100SMatthew Ahrens bmark_name = pound + 1; 58978f17100SMatthew Ahrens props = fnvlist_alloc(); 59078f17100SMatthew Ahrens err = lzc_get_bookmarks(fsname, props, &bmarks); 59178f17100SMatthew Ahrens nvlist_free(props); 59278f17100SMatthew Ahrens if (err != 0) { 59378f17100SMatthew Ahrens nvlist_free(bmarks); 59478f17100SMatthew Ahrens return (B_FALSE); 59578f17100SMatthew Ahrens } 59678f17100SMatthew Ahrens 59778f17100SMatthew Ahrens rv = nvlist_exists(bmarks, bmark_name); 59878f17100SMatthew Ahrens nvlist_free(bmarks); 59978f17100SMatthew Ahrens return (rv); 60078f17100SMatthew Ahrens } 60178f17100SMatthew Ahrens 60278f17100SMatthew Ahrens zfs_handle_t * 60378f17100SMatthew Ahrens make_bookmark_handle(zfs_handle_t *parent, const char *path, 60478f17100SMatthew Ahrens nvlist_t *bmark_props) 60578f17100SMatthew Ahrens { 60678f17100SMatthew Ahrens zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 60778f17100SMatthew Ahrens 60878f17100SMatthew Ahrens if (zhp == NULL) 60978f17100SMatthew Ahrens return (NULL); 61078f17100SMatthew Ahrens 61178f17100SMatthew Ahrens /* Fill in the name. */ 61278f17100SMatthew Ahrens zhp->zfs_hdl = parent->zfs_hdl; 61378f17100SMatthew Ahrens (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 61478f17100SMatthew Ahrens 61578f17100SMatthew Ahrens /* Set the property lists. */ 61678f17100SMatthew Ahrens if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) { 61778f17100SMatthew Ahrens free(zhp); 61878f17100SMatthew Ahrens return (NULL); 61978f17100SMatthew Ahrens } 62078f17100SMatthew Ahrens 62178f17100SMatthew Ahrens /* Set the types. */ 62278f17100SMatthew Ahrens zhp->zfs_head_type = parent->zfs_head_type; 62378f17100SMatthew Ahrens zhp->zfs_type = ZFS_TYPE_BOOKMARK; 62478f17100SMatthew Ahrens 62578f17100SMatthew Ahrens if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) { 62678f17100SMatthew Ahrens nvlist_free(zhp->zfs_props); 62778f17100SMatthew Ahrens free(zhp); 62878f17100SMatthew Ahrens return (NULL); 62978f17100SMatthew Ahrens } 63078f17100SMatthew Ahrens 63178f17100SMatthew Ahrens return (zhp); 63278f17100SMatthew Ahrens } 63378f17100SMatthew Ahrens 634edb901aaSMarcel Telka struct zfs_open_bookmarks_cb_data { 635edb901aaSMarcel Telka const char *path; 636edb901aaSMarcel Telka zfs_handle_t *zhp; 637edb901aaSMarcel Telka }; 638edb901aaSMarcel Telka 639edb901aaSMarcel Telka static int 640edb901aaSMarcel Telka zfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data) 641edb901aaSMarcel Telka { 642edb901aaSMarcel Telka struct zfs_open_bookmarks_cb_data *dp = data; 643edb901aaSMarcel Telka 644edb901aaSMarcel Telka /* 645edb901aaSMarcel Telka * Is it the one we are looking for? 646edb901aaSMarcel Telka */ 647edb901aaSMarcel Telka if (strcmp(dp->path, zfs_get_name(zhp)) == 0) { 648edb901aaSMarcel Telka /* 649edb901aaSMarcel Telka * We found it. Save it and let the caller know we are done. 650edb901aaSMarcel Telka */ 651edb901aaSMarcel Telka dp->zhp = zhp; 652edb901aaSMarcel Telka return (EEXIST); 653edb901aaSMarcel Telka } 654edb901aaSMarcel Telka 655edb901aaSMarcel Telka /* 656edb901aaSMarcel Telka * Not found. Close the handle and ask for another one. 657edb901aaSMarcel Telka */ 658edb901aaSMarcel Telka zfs_close(zhp); 659edb901aaSMarcel Telka return (0); 660edb901aaSMarcel Telka } 661edb901aaSMarcel Telka 662fa9e4066Sahrens /* 663edb901aaSMarcel Telka * Opens the given snapshot, bookmark, filesystem, or volume. The 'types' 664fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 665fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 666fa9e4066Sahrens */ 667fa9e4066Sahrens zfs_handle_t * 66899653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 669fa9e4066Sahrens { 670fa9e4066Sahrens zfs_handle_t *zhp; 67199653d4eSeschrock char errbuf[1024]; 672edb901aaSMarcel Telka char *bookp; 67399653d4eSeschrock 67499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 67599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 676fa9e4066Sahrens 677fa9e4066Sahrens /* 67899653d4eSeschrock * Validate the name before we even try to open it. 679fa9e4066Sahrens */ 680edb901aaSMarcel Telka if (!zfs_validate_name(hdl, path, types, B_FALSE)) { 68199653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 682fa9e4066Sahrens return (NULL); 683fa9e4066Sahrens } 684fa9e4066Sahrens 685fa9e4066Sahrens /* 686edb901aaSMarcel Telka * Bookmarks needs to be handled separately. 687fa9e4066Sahrens */ 688edb901aaSMarcel Telka bookp = strchr(path, '#'); 689edb901aaSMarcel Telka if (bookp == NULL) { 690edb901aaSMarcel Telka /* 691edb901aaSMarcel Telka * Try to get stats for the dataset, which will tell us if it 692edb901aaSMarcel Telka * exists. 693edb901aaSMarcel Telka */ 694edb901aaSMarcel Telka errno = 0; 695edb901aaSMarcel Telka if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 696edb901aaSMarcel Telka (void) zfs_standard_error(hdl, errno, errbuf); 697edb901aaSMarcel Telka return (NULL); 698edb901aaSMarcel Telka } 699edb901aaSMarcel Telka } else { 700edb901aaSMarcel Telka char dsname[ZFS_MAX_DATASET_NAME_LEN]; 701edb901aaSMarcel Telka zfs_handle_t *pzhp; 702edb901aaSMarcel Telka struct zfs_open_bookmarks_cb_data cb_data = {path, NULL}; 703edb901aaSMarcel Telka 704edb901aaSMarcel Telka /* 705edb901aaSMarcel Telka * We need to cut out '#' and everything after '#' 706edb901aaSMarcel Telka * to get the parent dataset name only. 707edb901aaSMarcel Telka */ 708edb901aaSMarcel Telka assert(bookp - path < sizeof (dsname)); 709edb901aaSMarcel Telka (void) strncpy(dsname, path, bookp - path); 710edb901aaSMarcel Telka dsname[bookp - path] = '\0'; 711edb901aaSMarcel Telka 712edb901aaSMarcel Telka /* 713edb901aaSMarcel Telka * Create handle for the parent dataset. 714edb901aaSMarcel Telka */ 715edb901aaSMarcel Telka errno = 0; 716edb901aaSMarcel Telka if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) { 717edb901aaSMarcel Telka (void) zfs_standard_error(hdl, errno, errbuf); 718edb901aaSMarcel Telka return (NULL); 719edb901aaSMarcel Telka } 720edb901aaSMarcel Telka 721edb901aaSMarcel Telka /* 722edb901aaSMarcel Telka * Iterate bookmarks to find the right one. 723edb901aaSMarcel Telka */ 724edb901aaSMarcel Telka errno = 0; 725edb901aaSMarcel Telka if ((zfs_iter_bookmarks(pzhp, zfs_open_bookmarks_cb, 726edb901aaSMarcel Telka &cb_data) == 0) && (cb_data.zhp == NULL)) { 727edb901aaSMarcel Telka (void) zfs_error(hdl, EZFS_NOENT, errbuf); 728edb901aaSMarcel Telka zfs_close(pzhp); 729edb901aaSMarcel Telka return (NULL); 730edb901aaSMarcel Telka } 731edb901aaSMarcel Telka if (cb_data.zhp == NULL) { 732edb901aaSMarcel Telka (void) zfs_standard_error(hdl, errno, errbuf); 733edb901aaSMarcel Telka zfs_close(pzhp); 734edb901aaSMarcel Telka return (NULL); 735edb901aaSMarcel Telka } 736edb901aaSMarcel Telka zhp = cb_data.zhp; 737edb901aaSMarcel Telka 738edb901aaSMarcel Telka /* 739edb901aaSMarcel Telka * Cleanup. 740edb901aaSMarcel Telka */ 741edb901aaSMarcel Telka zfs_close(pzhp); 742fa9e4066Sahrens } 743fa9e4066Sahrens 744fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 74599653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 74694de1d4cSeschrock zfs_close(zhp); 747fa9e4066Sahrens return (NULL); 748fa9e4066Sahrens } 749fa9e4066Sahrens 750fa9e4066Sahrens return (zhp); 751fa9e4066Sahrens } 752fa9e4066Sahrens 753fa9e4066Sahrens /* 754fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 755fa9e4066Sahrens */ 756fa9e4066Sahrens void 757fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 758fa9e4066Sahrens { 759fa9e4066Sahrens if (zhp->zfs_mntopts) 760fa9e4066Sahrens free(zhp->zfs_mntopts); 761e9dbad6fSeschrock nvlist_free(zhp->zfs_props); 762e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 76392241e0bSTom Erickson nvlist_free(zhp->zfs_recvd_props); 764fa9e4066Sahrens free(zhp); 765fa9e4066Sahrens } 766fa9e4066Sahrens 767ebedde84SEric Taylor typedef struct mnttab_node { 768ebedde84SEric Taylor struct mnttab mtn_mt; 769ebedde84SEric Taylor avl_node_t mtn_node; 770ebedde84SEric Taylor } mnttab_node_t; 771ebedde84SEric Taylor 772ebedde84SEric Taylor static int 773ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2) 774ebedde84SEric Taylor { 775ebedde84SEric Taylor const mnttab_node_t *mtn1 = arg1; 776ebedde84SEric Taylor const mnttab_node_t *mtn2 = arg2; 777ebedde84SEric Taylor int rv; 778ebedde84SEric Taylor 779ebedde84SEric Taylor rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); 780ebedde84SEric Taylor 781ebedde84SEric Taylor if (rv == 0) 782ebedde84SEric Taylor return (0); 783ebedde84SEric Taylor return (rv > 0 ? 1 : -1); 784ebedde84SEric Taylor } 785ebedde84SEric Taylor 786ebedde84SEric Taylor void 787ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl) 788ebedde84SEric Taylor { 789*591e0e13SSebastien Roy (void) mutex_init(&hdl->libzfs_mnttab_cache_lock, 790*591e0e13SSebastien Roy LOCK_NORMAL | LOCK_ERRORCHECK, NULL); 791ebedde84SEric Taylor assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0); 792ebedde84SEric Taylor avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare, 793ebedde84SEric Taylor sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); 794b2634b9cSEric Taylor } 795b2634b9cSEric Taylor 796b2634b9cSEric Taylor void 797b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl) 798b2634b9cSEric Taylor { 799b2634b9cSEric Taylor struct mnttab entry; 800ebedde84SEric Taylor 801ebedde84SEric Taylor rewind(hdl->libzfs_mnttab); 802ebedde84SEric Taylor while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 803ebedde84SEric Taylor mnttab_node_t *mtn; 804ebedde84SEric Taylor 805ebedde84SEric Taylor if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 806ebedde84SEric Taylor continue; 807ebedde84SEric Taylor mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 808ebedde84SEric Taylor mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special); 809ebedde84SEric Taylor mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp); 810ebedde84SEric Taylor mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype); 811ebedde84SEric Taylor mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts); 812ebedde84SEric Taylor avl_add(&hdl->libzfs_mnttab_cache, mtn); 813ebedde84SEric Taylor } 814ebedde84SEric Taylor } 815ebedde84SEric Taylor 816ebedde84SEric Taylor void 817ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl) 818ebedde84SEric Taylor { 819ebedde84SEric Taylor void *cookie = NULL; 820ebedde84SEric Taylor mnttab_node_t *mtn; 821ebedde84SEric Taylor 82288f61deeSIgor Kozhukhov while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) 82388f61deeSIgor Kozhukhov != NULL) { 824ebedde84SEric Taylor free(mtn->mtn_mt.mnt_special); 825ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mountp); 826ebedde84SEric Taylor free(mtn->mtn_mt.mnt_fstype); 827ebedde84SEric Taylor free(mtn->mtn_mt.mnt_mntopts); 828ebedde84SEric Taylor free(mtn); 829ebedde84SEric Taylor } 830ebedde84SEric Taylor avl_destroy(&hdl->libzfs_mnttab_cache); 831*591e0e13SSebastien Roy (void) mutex_destroy(&hdl->libzfs_mnttab_cache_lock); 832ebedde84SEric Taylor } 833ebedde84SEric Taylor 834b2634b9cSEric Taylor void 835b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) 836b2634b9cSEric Taylor { 837b2634b9cSEric Taylor hdl->libzfs_mnttab_enable = enable; 838b2634b9cSEric Taylor } 839b2634b9cSEric Taylor 840ebedde84SEric Taylor int 841ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, 842ebedde84SEric Taylor struct mnttab *entry) 843ebedde84SEric Taylor { 844ebedde84SEric Taylor mnttab_node_t find; 845ebedde84SEric Taylor mnttab_node_t *mtn; 846*591e0e13SSebastien Roy int ret = ENOENT; 847ebedde84SEric Taylor 848b2634b9cSEric Taylor if (!hdl->libzfs_mnttab_enable) { 849b2634b9cSEric Taylor struct mnttab srch = { 0 }; 850b2634b9cSEric Taylor 851b2634b9cSEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache)) 852b2634b9cSEric Taylor libzfs_mnttab_fini(hdl); 853b2634b9cSEric Taylor rewind(hdl->libzfs_mnttab); 854b2634b9cSEric Taylor srch.mnt_special = (char *)fsname; 855b2634b9cSEric Taylor srch.mnt_fstype = MNTTYPE_ZFS; 856b2634b9cSEric Taylor if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0) 857b2634b9cSEric Taylor return (0); 858b2634b9cSEric Taylor else 859b2634b9cSEric Taylor return (ENOENT); 860b2634b9cSEric Taylor } 861b2634b9cSEric Taylor 862*591e0e13SSebastien Roy mutex_enter(&hdl->libzfs_mnttab_cache_lock); 863ebedde84SEric Taylor if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) 864b2634b9cSEric Taylor libzfs_mnttab_update(hdl); 865ebedde84SEric Taylor 866ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 867ebedde84SEric Taylor mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL); 868ebedde84SEric Taylor if (mtn) { 869ebedde84SEric Taylor *entry = mtn->mtn_mt; 870*591e0e13SSebastien Roy ret = 0; 871ebedde84SEric Taylor } 872*591e0e13SSebastien Roy mutex_exit(&hdl->libzfs_mnttab_cache_lock); 873*591e0e13SSebastien Roy return (ret); 874ebedde84SEric Taylor } 875ebedde84SEric Taylor 876ebedde84SEric Taylor void 877ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, 878ebedde84SEric Taylor const char *mountp, const char *mntopts) 879ebedde84SEric Taylor { 880ebedde84SEric Taylor mnttab_node_t *mtn; 881ebedde84SEric Taylor 882*591e0e13SSebastien Roy mutex_enter(&hdl->libzfs_mnttab_cache_lock); 883*591e0e13SSebastien Roy if (avl_numnodes(&hdl->libzfs_mnttab_cache) != 0) { 884*591e0e13SSebastien Roy mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); 885*591e0e13SSebastien Roy mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); 886*591e0e13SSebastien Roy mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); 887*591e0e13SSebastien Roy mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS); 888*591e0e13SSebastien Roy mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); 889*591e0e13SSebastien Roy avl_add(&hdl->libzfs_mnttab_cache, mtn); 890*591e0e13SSebastien Roy } 891*591e0e13SSebastien Roy mutex_exit(&hdl->libzfs_mnttab_cache_lock); 892ebedde84SEric Taylor } 893ebedde84SEric Taylor 894ebedde84SEric Taylor void 895ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) 896ebedde84SEric Taylor { 897ebedde84SEric Taylor mnttab_node_t find; 898ebedde84SEric Taylor mnttab_node_t *ret; 899ebedde84SEric Taylor 900*591e0e13SSebastien Roy mutex_enter(&hdl->libzfs_mnttab_cache_lock); 901ebedde84SEric Taylor find.mtn_mt.mnt_special = (char *)fsname; 90288f61deeSIgor Kozhukhov if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) 90388f61deeSIgor Kozhukhov != NULL) { 904ebedde84SEric Taylor avl_remove(&hdl->libzfs_mnttab_cache, ret); 905ebedde84SEric Taylor free(ret->mtn_mt.mnt_special); 906ebedde84SEric Taylor free(ret->mtn_mt.mnt_mountp); 907ebedde84SEric Taylor free(ret->mtn_mt.mnt_fstype); 908ebedde84SEric Taylor free(ret->mtn_mt.mnt_mntopts); 909ebedde84SEric Taylor free(ret); 910ebedde84SEric Taylor } 911*591e0e13SSebastien Roy mutex_exit(&hdl->libzfs_mnttab_cache_lock); 912ebedde84SEric Taylor } 913ebedde84SEric Taylor 9147b97dc1aSrm int 9157b97dc1aSrm zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 9167b97dc1aSrm { 91729ab75c9Srm zpool_handle_t *zpool_handle = zhp->zpool_hdl; 9187b97dc1aSrm 9197b97dc1aSrm if (zpool_handle == NULL) 9207b97dc1aSrm return (-1); 9217b97dc1aSrm 9227b97dc1aSrm *spa_version = zpool_get_prop_int(zpool_handle, 9237b97dc1aSrm ZPOOL_PROP_VERSION, NULL); 9247b97dc1aSrm return (0); 9257b97dc1aSrm } 9267b97dc1aSrm 9277b97dc1aSrm /* 9287b97dc1aSrm * The choice of reservation property depends on the SPA version. 9297b97dc1aSrm */ 9307b97dc1aSrm static int 9317b97dc1aSrm zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 9327b97dc1aSrm { 9337b97dc1aSrm int spa_version; 9347b97dc1aSrm 9357b97dc1aSrm if (zfs_spa_version(zhp, &spa_version) < 0) 9367b97dc1aSrm return (-1); 9377b97dc1aSrm 9387b97dc1aSrm if (spa_version >= SPA_VERSION_REFRESERVATION) 9397b97dc1aSrm *resv_prop = ZFS_PROP_REFRESERVATION; 9407b97dc1aSrm else 9417b97dc1aSrm *resv_prop = ZFS_PROP_RESERVATION; 9427b97dc1aSrm 9437b97dc1aSrm return (0); 9447b97dc1aSrm } 9457b97dc1aSrm 946e9dbad6fSeschrock /* 947e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 948e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 949e9dbad6fSeschrock * strings. 950e9dbad6fSeschrock */ 9510a48a24eStimh nvlist_t * 9520a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 953e9316f76SJoe Stein uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl, 954e9316f76SJoe Stein const char *errbuf) 955e9dbad6fSeschrock { 956e9dbad6fSeschrock nvpair_t *elem; 957e9dbad6fSeschrock uint64_t intval; 958e9dbad6fSeschrock char *strval; 959990b4856Slling zfs_prop_t prop; 960e9dbad6fSeschrock nvlist_t *ret; 961da6c28aaSamw int chosen_normal = -1; 962da6c28aaSamw int chosen_utf = -1; 963e9dbad6fSeschrock 964990b4856Slling if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 965990b4856Slling (void) no_memory(hdl); 966990b4856Slling return (NULL); 967e9dbad6fSeschrock } 968e9dbad6fSeschrock 96914843421SMatthew Ahrens /* 97014843421SMatthew Ahrens * Make sure this property is valid and applies to this type. 97114843421SMatthew Ahrens */ 97214843421SMatthew Ahrens 973e9dbad6fSeschrock elem = NULL; 974e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 975990b4856Slling const char *propname = nvpair_name(elem); 976e9dbad6fSeschrock 97714843421SMatthew Ahrens prop = zfs_name_to_prop(propname); 97814843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { 979990b4856Slling /* 98014843421SMatthew Ahrens * This is a user property: make sure it's a 981990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 982990b4856Slling */ 983990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 984990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 985990b4856Slling "'%s' must be a string"), propname); 986990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 987990b4856Slling goto error; 988990b4856Slling } 989990b4856Slling 990990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 991990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 992990b4856Slling "property name '%s' is too long"), 993990b4856Slling propname); 994990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 995990b4856Slling goto error; 996fa9e4066Sahrens } 997fa9e4066Sahrens 998e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 999e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 1000e9dbad6fSeschrock (void) no_memory(hdl); 1001e9dbad6fSeschrock goto error; 1002e9dbad6fSeschrock } 1003e9dbad6fSeschrock continue; 1004e9dbad6fSeschrock } 1005e9dbad6fSeschrock 100614843421SMatthew Ahrens /* 100714843421SMatthew Ahrens * Currently, only user properties can be modified on 100814843421SMatthew Ahrens * snapshots. 100914843421SMatthew Ahrens */ 1010bb0ade09Sahrens if (type == ZFS_TYPE_SNAPSHOT) { 1011bb0ade09Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1012bb0ade09Sahrens "this property can not be modified for snapshots")); 1013bb0ade09Sahrens (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 1014bb0ade09Sahrens goto error; 1015bb0ade09Sahrens } 1016bb0ade09Sahrens 101714843421SMatthew Ahrens if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { 101814843421SMatthew Ahrens zfs_userquota_prop_t uqtype; 101914843421SMatthew Ahrens char newpropname[128]; 102014843421SMatthew Ahrens char domain[128]; 102114843421SMatthew Ahrens uint64_t rid; 102214843421SMatthew Ahrens uint64_t valary[3]; 102314843421SMatthew Ahrens 102414843421SMatthew Ahrens if (userquota_propname_decode(propname, zoned, 102514843421SMatthew Ahrens &uqtype, domain, sizeof (domain), &rid) != 0) { 102614843421SMatthew Ahrens zfs_error_aux(hdl, 102714843421SMatthew Ahrens dgettext(TEXT_DOMAIN, 102814843421SMatthew Ahrens "'%s' has an invalid user/group name"), 102914843421SMatthew Ahrens propname); 103014843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 103114843421SMatthew Ahrens goto error; 103214843421SMatthew Ahrens } 103314843421SMatthew Ahrens 103414843421SMatthew Ahrens if (uqtype != ZFS_PROP_USERQUOTA && 103514843421SMatthew Ahrens uqtype != ZFS_PROP_GROUPQUOTA) { 103614843421SMatthew Ahrens zfs_error_aux(hdl, 103714843421SMatthew Ahrens dgettext(TEXT_DOMAIN, "'%s' is readonly"), 103814843421SMatthew Ahrens propname); 103914843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, 104014843421SMatthew Ahrens errbuf); 104114843421SMatthew Ahrens goto error; 104214843421SMatthew Ahrens } 104314843421SMatthew Ahrens 104414843421SMatthew Ahrens if (nvpair_type(elem) == DATA_TYPE_STRING) { 104514843421SMatthew Ahrens (void) nvpair_value_string(elem, &strval); 104614843421SMatthew Ahrens if (strcmp(strval, "none") == 0) { 104714843421SMatthew Ahrens intval = 0; 104814843421SMatthew Ahrens } else if (zfs_nicestrtonum(hdl, 104914843421SMatthew Ahrens strval, &intval) != 0) { 105014843421SMatthew Ahrens (void) zfs_error(hdl, 105114843421SMatthew Ahrens EZFS_BADPROP, errbuf); 105214843421SMatthew Ahrens goto error; 105314843421SMatthew Ahrens } 105414843421SMatthew Ahrens } else if (nvpair_type(elem) == 105514843421SMatthew Ahrens DATA_TYPE_UINT64) { 105614843421SMatthew Ahrens (void) nvpair_value_uint64(elem, &intval); 105714843421SMatthew Ahrens if (intval == 0) { 105814843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 105914843421SMatthew Ahrens "use 'none' to disable " 106014843421SMatthew Ahrens "userquota/groupquota")); 106114843421SMatthew Ahrens goto error; 106214843421SMatthew Ahrens } 106314843421SMatthew Ahrens } else { 106414843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 106514843421SMatthew Ahrens "'%s' must be a number"), propname); 106614843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 106714843421SMatthew Ahrens goto error; 106814843421SMatthew Ahrens } 106914843421SMatthew Ahrens 10702d5843dbSMatthew Ahrens /* 10712d5843dbSMatthew Ahrens * Encode the prop name as 10722d5843dbSMatthew Ahrens * userquota@<hex-rid>-domain, to make it easy 10732d5843dbSMatthew Ahrens * for the kernel to decode. 10742d5843dbSMatthew Ahrens */ 107514843421SMatthew Ahrens (void) snprintf(newpropname, sizeof (newpropname), 10762d5843dbSMatthew Ahrens "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype], 10772d5843dbSMatthew Ahrens (longlong_t)rid, domain); 107814843421SMatthew Ahrens valary[0] = uqtype; 107914843421SMatthew Ahrens valary[1] = rid; 108014843421SMatthew Ahrens valary[2] = intval; 108114843421SMatthew Ahrens if (nvlist_add_uint64_array(ret, newpropname, 108214843421SMatthew Ahrens valary, 3) != 0) { 108314843421SMatthew Ahrens (void) no_memory(hdl); 108414843421SMatthew Ahrens goto error; 108514843421SMatthew Ahrens } 108614843421SMatthew Ahrens continue; 108719b94df9SMatthew Ahrens } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) { 108819b94df9SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 108919b94df9SMatthew Ahrens "'%s' is readonly"), 109019b94df9SMatthew Ahrens propname); 109119b94df9SMatthew Ahrens (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 109219b94df9SMatthew Ahrens goto error; 109314843421SMatthew Ahrens } 109414843421SMatthew Ahrens 109514843421SMatthew Ahrens if (prop == ZPROP_INVAL) { 109614843421SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 109714843421SMatthew Ahrens "invalid property '%s'"), propname); 109814843421SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 109914843421SMatthew Ahrens goto error; 110014843421SMatthew Ahrens } 110114843421SMatthew Ahrens 1102e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 1103e9dbad6fSeschrock zfs_error_aux(hdl, 1104e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 1105e9dbad6fSeschrock "apply to datasets of this type"), propname); 1106e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 1107e9dbad6fSeschrock goto error; 1108e9dbad6fSeschrock } 1109e9dbad6fSeschrock 1110e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 1111da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 1112e9dbad6fSeschrock zfs_error_aux(hdl, 1113e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 1114e9dbad6fSeschrock propname); 1115e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 1116e9dbad6fSeschrock goto error; 1117e9dbad6fSeschrock } 1118e9dbad6fSeschrock 1119990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 1120990b4856Slling &strval, &intval, errbuf) != 0) 1121e9dbad6fSeschrock goto error; 1122fa9e4066Sahrens 1123e9dbad6fSeschrock /* 1124e9dbad6fSeschrock * Perform some additional checks for specific properties. 1125e9dbad6fSeschrock */ 1126e9dbad6fSeschrock switch (prop) { 1127e7437265Sahrens case ZFS_PROP_VERSION: 1128e7437265Sahrens { 1129e7437265Sahrens int version; 1130e7437265Sahrens 1131e7437265Sahrens if (zhp == NULL) 1132e7437265Sahrens break; 1133e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 1134e7437265Sahrens if (intval < version) { 1135e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1136e7437265Sahrens "Can not downgrade; already at version %u"), 1137e7437265Sahrens version); 1138e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1139e7437265Sahrens goto error; 1140e7437265Sahrens } 1141e7437265Sahrens break; 1142e7437265Sahrens } 1143e7437265Sahrens 1144e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 1145b5152584SMatthew Ahrens case ZFS_PROP_RECORDSIZE: 1146b5152584SMatthew Ahrens { 1147b5152584SMatthew Ahrens int maxbs = SPA_MAXBLOCKSIZE; 1148e9316f76SJoe Stein if (zpool_hdl != NULL) { 1149e9316f76SJoe Stein maxbs = zpool_get_prop_int(zpool_hdl, 1150b5152584SMatthew Ahrens ZPOOL_PROP_MAXBLOCKSIZE, NULL); 1151b5152584SMatthew Ahrens } 1152b5152584SMatthew Ahrens /* 1153b5152584SMatthew Ahrens * Volumes are limited to a volblocksize of 128KB, 1154b5152584SMatthew Ahrens * because they typically service workloads with 1155b5152584SMatthew Ahrens * small random writes, which incur a large performance 1156b5152584SMatthew Ahrens * penalty with large blocks. 1157b5152584SMatthew Ahrens */ 1158b5152584SMatthew Ahrens if (prop == ZFS_PROP_VOLBLOCKSIZE) 1159b5152584SMatthew Ahrens maxbs = SPA_OLD_MAXBLOCKSIZE; 1160b5152584SMatthew Ahrens /* 1161b5152584SMatthew Ahrens * The value must be a power of two between 1162b5152584SMatthew Ahrens * SPA_MINBLOCKSIZE and maxbs. 1163b5152584SMatthew Ahrens */ 1164e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 1165b5152584SMatthew Ahrens intval > maxbs || !ISP2(intval)) { 116699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1167b5152584SMatthew Ahrens "'%s' must be power of 2 from 512B " 1168b5152584SMatthew Ahrens "to %uKB"), propname, maxbs >> 10); 1169e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1170e9dbad6fSeschrock goto error; 1171fa9e4066Sahrens } 1172fa9e4066Sahrens break; 1173b5152584SMatthew Ahrens } 11744201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 11754201a95eSRic Aleshire { 11764201a95eSRic Aleshire /* 11774201a95eSRic Aleshire * Verify the mlslabel string and convert to 11784201a95eSRic Aleshire * internal hex label string. 11794201a95eSRic Aleshire */ 11804201a95eSRic Aleshire 11814201a95eSRic Aleshire m_label_t *new_sl; 11824201a95eSRic Aleshire char *hex = NULL; /* internal label string */ 11834201a95eSRic Aleshire 11844201a95eSRic Aleshire /* Default value is already OK. */ 11854201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 11864201a95eSRic Aleshire break; 11874201a95eSRic Aleshire 11884201a95eSRic Aleshire /* Verify the label can be converted to binary form */ 11894201a95eSRic Aleshire if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) || 11904201a95eSRic Aleshire (str_to_label(strval, &new_sl, MAC_LABEL, 11914201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1)) { 11924201a95eSRic Aleshire goto badlabel; 11934201a95eSRic Aleshire } 11944201a95eSRic Aleshire 11954201a95eSRic Aleshire /* Now translate to hex internal label string */ 11964201a95eSRic Aleshire if (label_to_str(new_sl, &hex, M_INTERNAL, 11974201a95eSRic Aleshire DEF_NAMES) != 0) { 11984201a95eSRic Aleshire if (hex) 11994201a95eSRic Aleshire free(hex); 12004201a95eSRic Aleshire goto badlabel; 12014201a95eSRic Aleshire } 12024201a95eSRic Aleshire m_label_free(new_sl); 12034201a95eSRic Aleshire 12044201a95eSRic Aleshire /* If string is already in internal form, we're done. */ 12054201a95eSRic Aleshire if (strcmp(strval, hex) == 0) { 12064201a95eSRic Aleshire free(hex); 12074201a95eSRic Aleshire break; 12084201a95eSRic Aleshire } 12094201a95eSRic Aleshire 12104201a95eSRic Aleshire /* Replace the label string with the internal form. */ 1211569038c9SRic Aleshire (void) nvlist_remove(ret, zfs_prop_to_name(prop), 12124201a95eSRic Aleshire DATA_TYPE_STRING); 12134201a95eSRic Aleshire verify(nvlist_add_string(ret, zfs_prop_to_name(prop), 12144201a95eSRic Aleshire hex) == 0); 12154201a95eSRic Aleshire free(hex); 12164201a95eSRic Aleshire 12174201a95eSRic Aleshire break; 12184201a95eSRic Aleshire 12194201a95eSRic Aleshire badlabel: 12204201a95eSRic Aleshire zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 12214201a95eSRic Aleshire "invalid mlslabel '%s'"), strval); 12224201a95eSRic Aleshire (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 12234201a95eSRic Aleshire m_label_free(new_sl); /* OK if null */ 12244201a95eSRic Aleshire goto error; 12254201a95eSRic Aleshire 12264201a95eSRic Aleshire } 12274201a95eSRic Aleshire 1228e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 122989eef05eSrm { 123089eef05eSrm namecheck_err_t why; 123189eef05eSrm 1232e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 1233e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 1234e9dbad6fSeschrock break; 1235fa9e4066Sahrens 123689eef05eSrm if (mountpoint_namecheck(strval, &why)) { 123789eef05eSrm switch (why) { 123889eef05eSrm case NAME_ERR_LEADING_SLASH: 123989eef05eSrm zfs_error_aux(hdl, 124089eef05eSrm dgettext(TEXT_DOMAIN, 124189eef05eSrm "'%s' must be an absolute path, " 124289eef05eSrm "'none', or 'legacy'"), propname); 124389eef05eSrm break; 124489eef05eSrm case NAME_ERR_TOOLONG: 124589eef05eSrm zfs_error_aux(hdl, 124689eef05eSrm dgettext(TEXT_DOMAIN, 124789eef05eSrm "component of '%s' is too long"), 124889eef05eSrm propname); 124989eef05eSrm break; 125088f61deeSIgor Kozhukhov 125188f61deeSIgor Kozhukhov default: 125288f61deeSIgor Kozhukhov zfs_error_aux(hdl, 125388f61deeSIgor Kozhukhov dgettext(TEXT_DOMAIN, 125488f61deeSIgor Kozhukhov "(%d) not defined"), 125588f61deeSIgor Kozhukhov why); 125688f61deeSIgor Kozhukhov break; 125789eef05eSrm } 1258e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1259e9dbad6fSeschrock goto error; 1260fa9e4066Sahrens } 126189eef05eSrm } 126289eef05eSrm 1263f3861e1aSahl /*FALLTHRU*/ 1264fa9e4066Sahrens 1265da6c28aaSamw case ZFS_PROP_SHARESMB: 1266f3861e1aSahl case ZFS_PROP_SHARENFS: 1267f3861e1aSahl /* 1268da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 1269da6c28aaSamw * properties, check if it can be set in a 1270da6c28aaSamw * global/non-global zone based on 1271f3861e1aSahl * the zoned property value: 1272f3861e1aSahl * 1273f3861e1aSahl * global zone non-global zone 1274f3861e1aSahl * -------------------------------------------------- 1275f3861e1aSahl * zoned=on mountpoint (no) mountpoint (yes) 1276f3861e1aSahl * sharenfs (no) sharenfs (no) 1277da6c28aaSamw * sharesmb (no) sharesmb (no) 1278f3861e1aSahl * 1279f3861e1aSahl * zoned=off mountpoint (yes) N/A 1280f3861e1aSahl * sharenfs (yes) 1281da6c28aaSamw * sharesmb (yes) 1282f3861e1aSahl */ 1283e9dbad6fSeschrock if (zoned) { 1284e9dbad6fSeschrock if (getzoneid() == GLOBAL_ZONEID) { 1285e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1286e9dbad6fSeschrock "'%s' cannot be set on " 1287e9dbad6fSeschrock "dataset in a non-global zone"), 1288e9dbad6fSeschrock propname); 1289e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1290e9dbad6fSeschrock errbuf); 1291e9dbad6fSeschrock goto error; 1292da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 1293da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 1294e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1295e9dbad6fSeschrock "'%s' cannot be set in " 1296e9dbad6fSeschrock "a non-global zone"), propname); 1297e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 1298e9dbad6fSeschrock errbuf); 1299e9dbad6fSeschrock goto error; 1300fa9e4066Sahrens } 1301e9dbad6fSeschrock } else if (getzoneid() != GLOBAL_ZONEID) { 1302e9dbad6fSeschrock /* 1303e9dbad6fSeschrock * If zoned property is 'off', this must be in 130414843421SMatthew Ahrens * a global zone. If not, something is wrong. 1305e9dbad6fSeschrock */ 1306e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1307e9dbad6fSeschrock "'%s' cannot be set while dataset " 1308e9dbad6fSeschrock "'zoned' property is set"), propname); 1309e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 1310e9dbad6fSeschrock goto error; 1311fa9e4066Sahrens } 1312f3861e1aSahl 131367331909Sdougm /* 131467331909Sdougm * At this point, it is legitimate to set the 131567331909Sdougm * property. Now we want to make sure that the 131667331909Sdougm * property value is valid if it is sharenfs. 131767331909Sdougm */ 1318da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 1319da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 1320fac3008cSeschrock strcmp(strval, "on") != 0 && 1321fac3008cSeschrock strcmp(strval, "off") != 0) { 1322da6c28aaSamw zfs_share_proto_t proto; 1323da6c28aaSamw 1324da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 1325da6c28aaSamw proto = PROTO_SMB; 1326da6c28aaSamw else 1327da6c28aaSamw proto = PROTO_NFS; 132867331909Sdougm 132967331909Sdougm /* 1330da6c28aaSamw * Must be an valid sharing protocol 1331da6c28aaSamw * option string so init the libshare 1332da6c28aaSamw * in order to enable the parser and 1333da6c28aaSamw * then parse the options. We use the 1334da6c28aaSamw * control API since we don't care about 1335da6c28aaSamw * the current configuration and don't 133667331909Sdougm * want the overhead of loading it 133767331909Sdougm * until we actually do something. 133867331909Sdougm */ 133967331909Sdougm 1340fac3008cSeschrock if (zfs_init_libshare(hdl, 1341fac3008cSeschrock SA_INIT_CONTROL_API) != SA_OK) { 1342fac3008cSeschrock /* 1343fac3008cSeschrock * An error occurred so we can't do 1344fac3008cSeschrock * anything 1345fac3008cSeschrock */ 1346fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1347fac3008cSeschrock "'%s' cannot be set: problem " 1348fac3008cSeschrock "in share initialization"), 1349fac3008cSeschrock propname); 1350fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1351fac3008cSeschrock errbuf); 1352fac3008cSeschrock goto error; 1353fac3008cSeschrock } 135467331909Sdougm 1355da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 1356fac3008cSeschrock /* 1357fac3008cSeschrock * There was an error in parsing so 1358fac3008cSeschrock * deal with it by issuing an error 1359fac3008cSeschrock * message and leaving after 1360fac3008cSeschrock * uninitializing the the libshare 1361fac3008cSeschrock * interface. 1362fac3008cSeschrock */ 1363fac3008cSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1364fac3008cSeschrock "'%s' cannot be set to invalid " 1365fac3008cSeschrock "options"), propname); 1366fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1367fac3008cSeschrock errbuf); 1368fac3008cSeschrock zfs_uninit_libshare(hdl); 1369fac3008cSeschrock goto error; 1370fac3008cSeschrock } 137167331909Sdougm zfs_uninit_libshare(hdl); 137267331909Sdougm } 137367331909Sdougm 1374da6c28aaSamw break; 137588f61deeSIgor Kozhukhov 1376da6c28aaSamw case ZFS_PROP_UTF8ONLY: 1377da6c28aaSamw chosen_utf = (int)intval; 1378da6c28aaSamw break; 137988f61deeSIgor Kozhukhov 1380da6c28aaSamw case ZFS_PROP_NORMALIZE: 1381da6c28aaSamw chosen_normal = (int)intval; 1382f3861e1aSahl break; 138388f61deeSIgor Kozhukhov 138488f61deeSIgor Kozhukhov default: 138588f61deeSIgor Kozhukhov break; 1386e9dbad6fSeschrock } 1387fa9e4066Sahrens 1388e9dbad6fSeschrock /* 1389e9dbad6fSeschrock * For changes to existing volumes, we have some additional 1390e9dbad6fSeschrock * checks to enforce. 1391e9dbad6fSeschrock */ 1392e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 1393e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 1394e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 1395e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 1396e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 1397e9dbad6fSeschrock char buf[64]; 1398e9dbad6fSeschrock 1399e9dbad6fSeschrock switch (prop) { 1400e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1401e9dbad6fSeschrock if (intval > volsize) { 1402e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1403e9dbad6fSeschrock "'%s' is greater than current " 1404e9dbad6fSeschrock "volume size"), propname); 1405e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1406e9dbad6fSeschrock errbuf); 1407e9dbad6fSeschrock goto error; 1408e9dbad6fSeschrock } 1409e9dbad6fSeschrock break; 1410e9dbad6fSeschrock 14111c10ae76SMike Gerdts case ZFS_PROP_REFRESERVATION: 14121c10ae76SMike Gerdts if (intval > volsize && intval != UINT64_MAX) { 14131c10ae76SMike Gerdts zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14141c10ae76SMike Gerdts "'%s' is greater than current " 14151c10ae76SMike Gerdts "volume size"), propname); 14161c10ae76SMike Gerdts (void) zfs_error(hdl, EZFS_BADPROP, 14171c10ae76SMike Gerdts errbuf); 14181c10ae76SMike Gerdts goto error; 14191c10ae76SMike Gerdts } 14201c10ae76SMike Gerdts break; 14211c10ae76SMike Gerdts 1422e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1423e9dbad6fSeschrock if (intval % blocksize != 0) { 1424e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 1425e9dbad6fSeschrock sizeof (buf)); 1426e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1427e9dbad6fSeschrock "'%s' must be a multiple of " 1428e9dbad6fSeschrock "volume block size (%s)"), 1429e9dbad6fSeschrock propname, buf); 1430e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1431e9dbad6fSeschrock errbuf); 1432e9dbad6fSeschrock goto error; 1433e9dbad6fSeschrock } 1434e9dbad6fSeschrock 1435e9dbad6fSeschrock if (intval == 0) { 1436e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1437e9dbad6fSeschrock "'%s' cannot be zero"), 1438e9dbad6fSeschrock propname); 1439e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 1440e9dbad6fSeschrock errbuf); 1441e9dbad6fSeschrock goto error; 1442e9dbad6fSeschrock } 1443f3861e1aSahl break; 144488f61deeSIgor Kozhukhov 144588f61deeSIgor Kozhukhov default: 144688f61deeSIgor Kozhukhov break; 1447fa9e4066Sahrens } 1448e9dbad6fSeschrock } 1449e9dbad6fSeschrock } 1450fa9e4066Sahrens 1451da6c28aaSamw /* 1452da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 1453da6c28aaSamw * enforce rejection of non-UTF8 names. 1454da6c28aaSamw * 1455da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 1456da6c28aaSamw * was explicitly not chosen, it is an error. 1457da6c28aaSamw */ 1458de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 1459da6c28aaSamw if (nvlist_add_uint64(ret, 1460da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 1461da6c28aaSamw (void) no_memory(hdl); 1462da6c28aaSamw goto error; 1463da6c28aaSamw } 1464de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 1465da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1466da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 1467da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 1468da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1469da6c28aaSamw goto error; 1470da6c28aaSamw } 147136db6475SEric Taylor return (ret); 147236db6475SEric Taylor 147336db6475SEric Taylor error: 147436db6475SEric Taylor nvlist_free(ret); 147536db6475SEric Taylor return (NULL); 147636db6475SEric Taylor } 147736db6475SEric Taylor 147836db6475SEric Taylor int 147936db6475SEric Taylor zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) 148036db6475SEric Taylor { 148136db6475SEric Taylor uint64_t old_volsize; 148236db6475SEric Taylor uint64_t new_volsize; 148336db6475SEric Taylor uint64_t old_reservation; 148436db6475SEric Taylor uint64_t new_reservation; 148536db6475SEric Taylor zfs_prop_t resv_prop; 1486c61ea566SGeorge Wilson nvlist_t *props; 1487da6c28aaSamw 1488e9dbad6fSeschrock /* 1489e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 1490e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 1491e9dbad6fSeschrock */ 149236db6475SEric Taylor old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 149336db6475SEric Taylor if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 149436db6475SEric Taylor return (-1); 149536db6475SEric Taylor old_reservation = zfs_prop_get_int(zhp, resv_prop); 1496c61ea566SGeorge Wilson 1497c61ea566SGeorge Wilson props = fnvlist_alloc(); 1498c61ea566SGeorge Wilson fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 1499c61ea566SGeorge Wilson zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE)); 1500c61ea566SGeorge Wilson 1501c61ea566SGeorge Wilson if ((zvol_volsize_to_reservation(old_volsize, props) != 1502c61ea566SGeorge Wilson old_reservation) || nvlist_exists(nvl, 1503c61ea566SGeorge Wilson zfs_prop_to_name(resv_prop))) { 1504c61ea566SGeorge Wilson fnvlist_free(props); 150536db6475SEric Taylor return (0); 1506fa9e4066Sahrens } 150736db6475SEric Taylor if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1508c61ea566SGeorge Wilson &new_volsize) != 0) { 1509c61ea566SGeorge Wilson fnvlist_free(props); 151036db6475SEric Taylor return (-1); 1511c61ea566SGeorge Wilson } 1512c61ea566SGeorge Wilson new_reservation = zvol_volsize_to_reservation(new_volsize, props); 1513c61ea566SGeorge Wilson fnvlist_free(props); 1514c61ea566SGeorge Wilson 151536db6475SEric Taylor if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), 151636db6475SEric Taylor new_reservation) != 0) { 151736db6475SEric Taylor (void) no_memory(zhp->zfs_hdl); 151836db6475SEric Taylor return (-1); 151936db6475SEric Taylor } 152036db6475SEric Taylor return (1); 1521fa9e4066Sahrens } 1522fa9e4066Sahrens 15231c10ae76SMike Gerdts /* 15241c10ae76SMike Gerdts * Helper for 'zfs {set|clone} refreservation=auto'. Must be called after 15251c10ae76SMike Gerdts * zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinal value. 15261c10ae76SMike Gerdts * Return codes must match zfs_add_synthetic_resv(). 15271c10ae76SMike Gerdts */ 15281c10ae76SMike Gerdts static int 15291c10ae76SMike Gerdts zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl) 15301c10ae76SMike Gerdts { 15311c10ae76SMike Gerdts uint64_t volsize; 15321c10ae76SMike Gerdts uint64_t resvsize; 15331c10ae76SMike Gerdts zfs_prop_t prop; 15341c10ae76SMike Gerdts nvlist_t *props; 15351c10ae76SMike Gerdts 15361c10ae76SMike Gerdts if (!ZFS_IS_VOLUME(zhp)) { 15371c10ae76SMike Gerdts return (0); 15381c10ae76SMike Gerdts } 15391c10ae76SMike Gerdts 15401c10ae76SMike Gerdts if (zfs_which_resv_prop(zhp, &prop) != 0) { 15411c10ae76SMike Gerdts return (-1); 15421c10ae76SMike Gerdts } 15431c10ae76SMike Gerdts 15441c10ae76SMike Gerdts if (prop != ZFS_PROP_REFRESERVATION) { 15451c10ae76SMike Gerdts return (0); 15461c10ae76SMike Gerdts } 15471c10ae76SMike Gerdts 15481c10ae76SMike Gerdts if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(prop), &resvsize) != 0) { 15491c10ae76SMike Gerdts /* No value being set, so it can't be "auto" */ 15501c10ae76SMike Gerdts return (0); 15511c10ae76SMike Gerdts } 15521c10ae76SMike Gerdts if (resvsize != UINT64_MAX) { 15531c10ae76SMike Gerdts /* Being set to a value other than "auto" */ 15541c10ae76SMike Gerdts return (0); 15551c10ae76SMike Gerdts } 15561c10ae76SMike Gerdts 15571c10ae76SMike Gerdts props = fnvlist_alloc(); 15581c10ae76SMike Gerdts 15591c10ae76SMike Gerdts fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 15601c10ae76SMike Gerdts zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE)); 15611c10ae76SMike Gerdts 15621c10ae76SMike Gerdts if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 15631c10ae76SMike Gerdts &volsize) != 0) { 15641c10ae76SMike Gerdts volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 15651c10ae76SMike Gerdts } 15661c10ae76SMike Gerdts 15671c10ae76SMike Gerdts resvsize = zvol_volsize_to_reservation(volsize, props); 15681c10ae76SMike Gerdts fnvlist_free(props); 15691c10ae76SMike Gerdts 15701c10ae76SMike Gerdts (void) nvlist_remove_all(nvl, zfs_prop_to_name(prop)); 15711c10ae76SMike Gerdts if (nvlist_add_uint64(nvl, zfs_prop_to_name(prop), resvsize) != 0) { 15721c10ae76SMike Gerdts (void) no_memory(zhp->zfs_hdl); 15731c10ae76SMike Gerdts return (-1); 15741c10ae76SMike Gerdts } 15751c10ae76SMike Gerdts return (1); 15761c10ae76SMike Gerdts } 15771c10ae76SMike Gerdts 157892241e0bSTom Erickson void 157992241e0bSTom Erickson zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, 158092241e0bSTom Erickson char *errbuf) 158192241e0bSTom Erickson { 158292241e0bSTom Erickson switch (err) { 158392241e0bSTom Erickson 158492241e0bSTom Erickson case ENOSPC: 158592241e0bSTom Erickson /* 158692241e0bSTom Erickson * For quotas and reservations, ENOSPC indicates 158792241e0bSTom Erickson * something different; setting a quota or reservation 158892241e0bSTom Erickson * doesn't use any disk space. 158992241e0bSTom Erickson */ 159092241e0bSTom Erickson switch (prop) { 159192241e0bSTom Erickson case ZFS_PROP_QUOTA: 159292241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 159392241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 159492241e0bSTom Erickson "size is less than current used or " 159592241e0bSTom Erickson "reserved space")); 159692241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 159792241e0bSTom Erickson break; 159892241e0bSTom Erickson 159992241e0bSTom Erickson case ZFS_PROP_RESERVATION: 160092241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 160192241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 160292241e0bSTom Erickson "size is greater than available space")); 160392241e0bSTom Erickson (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 160492241e0bSTom Erickson break; 160592241e0bSTom Erickson 160692241e0bSTom Erickson default: 160792241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 160892241e0bSTom Erickson break; 160992241e0bSTom Erickson } 161092241e0bSTom Erickson break; 161192241e0bSTom Erickson 161292241e0bSTom Erickson case EBUSY: 161392241e0bSTom Erickson (void) zfs_standard_error(hdl, EBUSY, errbuf); 161492241e0bSTom Erickson break; 161592241e0bSTom Erickson 161692241e0bSTom Erickson case EROFS: 161792241e0bSTom Erickson (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 161892241e0bSTom Erickson break; 161992241e0bSTom Erickson 16206fdcb3d1SWill Andrews case E2BIG: 16216fdcb3d1SWill Andrews zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 16226fdcb3d1SWill Andrews "property value too long")); 16236fdcb3d1SWill Andrews (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 16246fdcb3d1SWill Andrews break; 16256fdcb3d1SWill Andrews 162692241e0bSTom Erickson case ENOTSUP: 162792241e0bSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 162892241e0bSTom Erickson "pool and or dataset must be upgraded to set this " 162992241e0bSTom Erickson "property or value")); 163092241e0bSTom Erickson (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 163192241e0bSTom Erickson break; 163292241e0bSTom Erickson 163392241e0bSTom Erickson case ERANGE: 1634b5152584SMatthew Ahrens if (prop == ZFS_PROP_COMPRESSION || 1635b5152584SMatthew Ahrens prop == ZFS_PROP_RECORDSIZE) { 163692241e0bSTom Erickson (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 163792241e0bSTom Erickson "property setting is not allowed on " 163892241e0bSTom Erickson "bootable datasets")); 163992241e0bSTom Erickson (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 164045818ee1SMatthew Ahrens } else if (prop == ZFS_PROP_CHECKSUM || 164145818ee1SMatthew Ahrens prop == ZFS_PROP_DEDUP) { 164245818ee1SMatthew Ahrens (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 164345818ee1SMatthew Ahrens "property setting is not allowed on " 164445818ee1SMatthew Ahrens "root pools")); 164545818ee1SMatthew Ahrens (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); 164692241e0bSTom Erickson } else { 164792241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 164892241e0bSTom Erickson } 164992241e0bSTom Erickson break; 165092241e0bSTom Erickson 1651ab003da8SJim Dunham case EINVAL: 1652ab003da8SJim Dunham if (prop == ZPROP_INVAL) { 1653ab003da8SJim Dunham (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 1654ab003da8SJim Dunham } else { 1655ab003da8SJim Dunham (void) zfs_standard_error(hdl, err, errbuf); 1656ab003da8SJim Dunham } 1657ab003da8SJim Dunham break; 1658ab003da8SJim Dunham 165992241e0bSTom Erickson case EOVERFLOW: 166092241e0bSTom Erickson /* 166192241e0bSTom Erickson * This platform can't address a volume this big. 166292241e0bSTom Erickson */ 166392241e0bSTom Erickson #ifdef _ILP32 166492241e0bSTom Erickson if (prop == ZFS_PROP_VOLSIZE) { 166592241e0bSTom Erickson (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 166692241e0bSTom Erickson break; 166792241e0bSTom Erickson } 166892241e0bSTom Erickson #endif 166992241e0bSTom Erickson /* FALLTHROUGH */ 167092241e0bSTom Erickson default: 167192241e0bSTom Erickson (void) zfs_standard_error(hdl, err, errbuf); 167292241e0bSTom Erickson } 167392241e0bSTom Erickson } 167492241e0bSTom Erickson 1675fa9e4066Sahrens /* 1676fa9e4066Sahrens * Given a property name and value, set the property for the given dataset. 1677fa9e4066Sahrens */ 1678fa9e4066Sahrens int 1679e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1680fa9e4066Sahrens { 1681e9dbad6fSeschrock int ret = -1; 168299653d4eSeschrock char errbuf[1024]; 168399653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 168430925561SChris Williamson nvlist_t *nvl = NULL; 168599653d4eSeschrock 168699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1687e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 168899653d4eSeschrock zhp->zfs_name); 168999653d4eSeschrock 1690e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1691e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1692e9dbad6fSeschrock (void) no_memory(hdl); 1693e9dbad6fSeschrock goto error; 1694fa9e4066Sahrens } 1695fa9e4066Sahrens 169630925561SChris Williamson ret = zfs_prop_set_list(zhp, nvl); 1697990b4856Slling 169830925561SChris Williamson error: 1699e9dbad6fSeschrock nvlist_free(nvl); 170030925561SChris Williamson return (ret); 170130925561SChris Williamson } 1702e9dbad6fSeschrock 1703e9dbad6fSeschrock 170436db6475SEric Taylor 170530925561SChris Williamson /* 170630925561SChris Williamson * Given an nvlist of property names and values, set the properties for the 170730925561SChris Williamson * given dataset. 170830925561SChris Williamson */ 170930925561SChris Williamson int 171030925561SChris Williamson zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props) 171130925561SChris Williamson { 171230925561SChris Williamson zfs_cmd_t zc = { 0 }; 171330925561SChris Williamson int ret = -1; 171430925561SChris Williamson prop_changelist_t **cls = NULL; 171530925561SChris Williamson int cl_idx; 171630925561SChris Williamson char errbuf[1024]; 171730925561SChris Williamson libzfs_handle_t *hdl = zhp->zfs_hdl; 171830925561SChris Williamson nvlist_t *nvl; 171930925561SChris Williamson int nvl_len; 1720f83b46baSPaul Dagnelie int added_resv = 0; 1721fa9e4066Sahrens 172230925561SChris Williamson (void) snprintf(errbuf, sizeof (errbuf), 172330925561SChris Williamson dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 172430925561SChris Williamson zhp->zfs_name); 172530925561SChris Williamson 172630925561SChris Williamson if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props, 1727e9316f76SJoe Stein zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl, 1728e9316f76SJoe Stein errbuf)) == NULL) 1729fa9e4066Sahrens goto error; 1730fa9e4066Sahrens 17310068372bSMark J Musante /* 173230925561SChris Williamson * We have to check for any extra properties which need to be added 173330925561SChris Williamson * before computing the length of the nvlist. 17340068372bSMark J Musante */ 173530925561SChris Williamson for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 173630925561SChris Williamson elem != NULL; 173730925561SChris Williamson elem = nvlist_next_nvpair(nvl, elem)) { 173830925561SChris Williamson if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE && 173930925561SChris Williamson (added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) { 174030925561SChris Williamson goto error; 174130925561SChris Williamson } 17424445fffbSMatthew Ahrens } 17431c10ae76SMike Gerdts 17441c10ae76SMike Gerdts if (added_resv != 1 && 17451c10ae76SMike Gerdts (added_resv = zfs_fix_auto_resv(zhp, nvl)) == -1) { 17461c10ae76SMike Gerdts goto error; 17471c10ae76SMike Gerdts } 17481c10ae76SMike Gerdts 174930925561SChris Williamson /* 175030925561SChris Williamson * Check how many properties we're setting and allocate an array to 175130925561SChris Williamson * store changelist pointers for postfix(). 175230925561SChris Williamson */ 175330925561SChris Williamson nvl_len = 0; 175430925561SChris Williamson for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 175530925561SChris Williamson elem != NULL; 175630925561SChris Williamson elem = nvlist_next_nvpair(nvl, elem)) 175730925561SChris Williamson nvl_len++; 175830925561SChris Williamson if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL) 17590068372bSMark J Musante goto error; 1760fa9e4066Sahrens 176130925561SChris Williamson cl_idx = 0; 176230925561SChris Williamson for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 176330925561SChris Williamson elem != NULL; 176430925561SChris Williamson elem = nvlist_next_nvpair(nvl, elem)) { 176530925561SChris Williamson 176630925561SChris Williamson zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem)); 176730925561SChris Williamson 176830925561SChris Williamson assert(cl_idx < nvl_len); 176930925561SChris Williamson /* 177030925561SChris Williamson * We don't want to unmount & remount the dataset when changing 177130925561SChris Williamson * its canmount property to 'on' or 'noauto'. We only use 177230925561SChris Williamson * the changelist logic to unmount when setting canmount=off. 177330925561SChris Williamson */ 1774c079fa4dSAndriy Gapon if (prop != ZFS_PROP_CANMOUNT || 1775c079fa4dSAndriy Gapon (fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF && 1776c079fa4dSAndriy Gapon zfs_is_mounted(zhp, NULL))) { 177730925561SChris Williamson cls[cl_idx] = changelist_gather(zhp, prop, 0, 0); 177830925561SChris Williamson if (cls[cl_idx] == NULL) 177930925561SChris Williamson goto error; 178030925561SChris Williamson } 178130925561SChris Williamson 178230925561SChris Williamson if (prop == ZFS_PROP_MOUNTPOINT && 178330925561SChris Williamson changelist_haszonedchild(cls[cl_idx])) { 178430925561SChris Williamson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 178530925561SChris Williamson "child dataset with inherited mountpoint is used " 178630925561SChris Williamson "in a non-global zone")); 178730925561SChris Williamson ret = zfs_error(hdl, EZFS_ZONED, errbuf); 178830925561SChris Williamson goto error; 178930925561SChris Williamson } 179030925561SChris Williamson 179130925561SChris Williamson if (cls[cl_idx] != NULL && 179230925561SChris Williamson (ret = changelist_prefix(cls[cl_idx])) != 0) 179330925561SChris Williamson goto error; 179430925561SChris Williamson 179530925561SChris Williamson cl_idx++; 179630925561SChris Williamson } 179730925561SChris Williamson assert(cl_idx == nvl_len); 179830925561SChris Williamson 1799fa9e4066Sahrens /* 180030925561SChris Williamson * Execute the corresponding ioctl() to set this list of properties. 1801fa9e4066Sahrens */ 1802fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1803fa9e4066Sahrens 180430925561SChris Williamson if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 || 180530925561SChris Williamson (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0) 1806e9dbad6fSeschrock goto error; 1807e9dbad6fSeschrock 1808ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1809743a77edSAlan Wright 1810fa9e4066Sahrens if (ret != 0) { 181130925561SChris Williamson /* Get the list of unset properties back and report them. */ 181230925561SChris Williamson nvlist_t *errorprops = NULL; 181330925561SChris Williamson if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0) 181430925561SChris Williamson goto error; 181530925561SChris Williamson for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); 181630925561SChris Williamson elem != NULL; 181730925561SChris Williamson elem = nvlist_next_nvpair(nvl, elem)) { 181830925561SChris Williamson zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem)); 181930925561SChris Williamson zfs_setprop_error(hdl, prop, errno, errbuf); 182030925561SChris Williamson } 182130925561SChris Williamson nvlist_free(errorprops); 182230925561SChris Williamson 182336db6475SEric Taylor if (added_resv && errno == ENOSPC) { 182436db6475SEric Taylor /* clean up the volsize property we tried to set */ 182536db6475SEric Taylor uint64_t old_volsize = zfs_prop_get_int(zhp, 182636db6475SEric Taylor ZFS_PROP_VOLSIZE); 182736db6475SEric Taylor nvlist_free(nvl); 182830925561SChris Williamson nvl = NULL; 182936db6475SEric Taylor zcmd_free_nvlists(&zc); 183030925561SChris Williamson 183136db6475SEric Taylor if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 183236db6475SEric Taylor goto error; 183336db6475SEric Taylor if (nvlist_add_uint64(nvl, 183436db6475SEric Taylor zfs_prop_to_name(ZFS_PROP_VOLSIZE), 183536db6475SEric Taylor old_volsize) != 0) 183636db6475SEric Taylor goto error; 183736db6475SEric Taylor if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 183836db6475SEric Taylor goto error; 183936db6475SEric Taylor (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 184036db6475SEric Taylor } 1841fa9e4066Sahrens } else { 184230925561SChris Williamson for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) { 184330925561SChris Williamson if (cls[cl_idx] != NULL) { 184430925561SChris Williamson int clp_err = changelist_postfix(cls[cl_idx]); 184530925561SChris Williamson if (clp_err != 0) 184630925561SChris Williamson ret = clp_err; 184730925561SChris Williamson } 184830925561SChris Williamson } 1849a227b7f4Shs 1850fa9e4066Sahrens /* 1851fa9e4066Sahrens * Refresh the statistics so the new property value 1852fa9e4066Sahrens * is reflected. 1853fa9e4066Sahrens */ 1854a227b7f4Shs if (ret == 0) 1855e9dbad6fSeschrock (void) get_stats(zhp); 1856fa9e4066Sahrens } 1857fa9e4066Sahrens 1858fa9e4066Sahrens error: 1859e9dbad6fSeschrock nvlist_free(nvl); 1860e9dbad6fSeschrock zcmd_free_nvlists(&zc); 186130925561SChris Williamson if (cls != NULL) { 186230925561SChris Williamson for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) { 186330925561SChris Williamson if (cls[cl_idx] != NULL) 186430925561SChris Williamson changelist_free(cls[cl_idx]); 186530925561SChris Williamson } 186630925561SChris Williamson free(cls); 186730925561SChris Williamson } 1868fa9e4066Sahrens return (ret); 1869fa9e4066Sahrens } 1870fa9e4066Sahrens 1871fa9e4066Sahrens /* 187292241e0bSTom Erickson * Given a property, inherit the value from the parent dataset, or if received 187392241e0bSTom Erickson * is TRUE, revert to the received value, if any. 1874fa9e4066Sahrens */ 1875fa9e4066Sahrens int 187692241e0bSTom Erickson zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) 1877fa9e4066Sahrens { 1878fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1879fa9e4066Sahrens int ret; 1880fa9e4066Sahrens prop_changelist_t *cl; 188199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 188299653d4eSeschrock char errbuf[1024]; 1883e9dbad6fSeschrock zfs_prop_t prop; 188499653d4eSeschrock 188599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 188699653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1887fa9e4066Sahrens 188892241e0bSTom Erickson zc.zc_cookie = received; 1889990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1890e9dbad6fSeschrock /* 1891e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1892e9dbad6fSeschrock * small, so just do it here. 1893e9dbad6fSeschrock */ 1894e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1895e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1896e9dbad6fSeschrock "invalid property")); 1897e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1898e9dbad6fSeschrock } 1899e9dbad6fSeschrock 1900e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1901e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1902e9dbad6fSeschrock 1903e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1904e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1905e9dbad6fSeschrock 1906e9dbad6fSeschrock return (0); 1907e9dbad6fSeschrock } 1908e9dbad6fSeschrock 1909fa9e4066Sahrens /* 1910fa9e4066Sahrens * Verify that this property is inheritable. 1911fa9e4066Sahrens */ 191299653d4eSeschrock if (zfs_prop_readonly(prop)) 191399653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1914fa9e4066Sahrens 191592241e0bSTom Erickson if (!zfs_prop_inheritable(prop) && !received) 191699653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1917fa9e4066Sahrens 1918fa9e4066Sahrens /* 1919fa9e4066Sahrens * Check to see if the value applies to this type 1920fa9e4066Sahrens */ 192199653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 192299653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1923fa9e4066Sahrens 1924bf7c2d40Srm /* 192536db6475SEric Taylor * Normalize the name, to get rid of shorthand abbreviations. 1926bf7c2d40Srm */ 1927bf7c2d40Srm propname = zfs_prop_to_name(prop); 1928fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1929e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1930fa9e4066Sahrens 1931fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1932fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 193399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 193499653d4eSeschrock "dataset is used in a non-global zone")); 193599653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1936fa9e4066Sahrens } 1937fa9e4066Sahrens 1938fa9e4066Sahrens /* 1939fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1940fa9e4066Sahrens */ 19410069fd67STim Haley if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) 1942fa9e4066Sahrens return (-1); 1943fa9e4066Sahrens 1944fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 194599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 194699653d4eSeschrock "child dataset with inherited mountpoint is used " 194799653d4eSeschrock "in a non-global zone")); 194899653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1949fa9e4066Sahrens goto error; 1950fa9e4066Sahrens } 1951fa9e4066Sahrens 1952fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1953fa9e4066Sahrens goto error; 1954fa9e4066Sahrens 1955e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 195699653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1957fa9e4066Sahrens } else { 1958fa9e4066Sahrens 1959efc555ebSnd if ((ret = changelist_postfix(cl)) != 0) 1960fa9e4066Sahrens goto error; 1961fa9e4066Sahrens 1962fa9e4066Sahrens /* 1963fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1964fa9e4066Sahrens */ 1965fa9e4066Sahrens (void) get_stats(zhp); 1966fa9e4066Sahrens } 1967fa9e4066Sahrens 1968fa9e4066Sahrens error: 1969fa9e4066Sahrens changelist_free(cl); 1970fa9e4066Sahrens return (ret); 1971fa9e4066Sahrens } 1972fa9e4066Sahrens 19737f7322feSeschrock /* 19747f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 19757f7322feSeschrock * extract them appropriately. 19767f7322feSeschrock */ 19777f7322feSeschrock static uint64_t 19787f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 19797f7322feSeschrock { 19807f7322feSeschrock nvlist_t *nv; 19817f7322feSeschrock uint64_t value; 19827f7322feSeschrock 1983a2eea2e1Sahrens *source = NULL; 19847f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 19857f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1986990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1987990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 19887f7322feSeschrock } else { 19892e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 19902e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 19917f7322feSeschrock value = zfs_prop_default_numeric(prop); 19927f7322feSeschrock *source = ""; 19937f7322feSeschrock } 19947f7322feSeschrock 19957f7322feSeschrock return (value); 19967f7322feSeschrock } 19977f7322feSeschrock 19989c3fd121SMatthew Ahrens static const char * 19997f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 20007f7322feSeschrock { 20017f7322feSeschrock nvlist_t *nv; 20029c3fd121SMatthew Ahrens const char *value; 20037f7322feSeschrock 2004a2eea2e1Sahrens *source = NULL; 20057f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 20067f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 20079c3fd121SMatthew Ahrens value = fnvlist_lookup_string(nv, ZPROP_VALUE); 2008990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 20097f7322feSeschrock } else { 20102e5e9e19SSanjeev Bagewadi verify(!zhp->zfs_props_table || 20112e5e9e19SSanjeev Bagewadi zhp->zfs_props_table[prop] == B_TRUE); 20129c3fd121SMatthew Ahrens value = zfs_prop_default_string(prop); 20137f7322feSeschrock *source = ""; 20147f7322feSeschrock } 20157f7322feSeschrock 20167f7322feSeschrock return (value); 20177f7322feSeschrock } 20187f7322feSeschrock 201992241e0bSTom Erickson static boolean_t 202092241e0bSTom Erickson zfs_is_recvd_props_mode(zfs_handle_t *zhp) 202192241e0bSTom Erickson { 202292241e0bSTom Erickson return (zhp->zfs_props == zhp->zfs_recvd_props); 202392241e0bSTom Erickson } 202492241e0bSTom Erickson 202592241e0bSTom Erickson static void 202692241e0bSTom Erickson zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 202792241e0bSTom Erickson { 202892241e0bSTom Erickson *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; 202992241e0bSTom Erickson zhp->zfs_props = zhp->zfs_recvd_props; 203092241e0bSTom Erickson } 203192241e0bSTom Erickson 203292241e0bSTom Erickson static void 203392241e0bSTom Erickson zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) 203492241e0bSTom Erickson { 203592241e0bSTom Erickson zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; 203692241e0bSTom Erickson *cookie = 0; 203792241e0bSTom Erickson } 203892241e0bSTom Erickson 2039fa9e4066Sahrens /* 2040fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 2041fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 2042fa9e4066Sahrens * 2043fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 2044fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 2045fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 2046fa9e4066Sahrens * the source "temporary". 2047fa9e4066Sahrens */ 204899653d4eSeschrock static int 2049990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 205099653d4eSeschrock char **source, uint64_t *val) 2051fa9e4066Sahrens { 2052bd00f61bSrm zfs_cmd_t zc = { 0 }; 205396510749Stimh nvlist_t *zplprops = NULL; 2054fa9e4066Sahrens struct mnttab mnt; 20553ccfa83cSahrens char *mntopt_on = NULL; 20563ccfa83cSahrens char *mntopt_off = NULL; 205792241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 2058fa9e4066Sahrens 2059fa9e4066Sahrens *source = NULL; 2060fa9e4066Sahrens 20613ccfa83cSahrens switch (prop) { 20623ccfa83cSahrens case ZFS_PROP_ATIME: 20633ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 20643ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 20653ccfa83cSahrens break; 20663ccfa83cSahrens 20673ccfa83cSahrens case ZFS_PROP_DEVICES: 20683ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 20693ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 20703ccfa83cSahrens break; 20713ccfa83cSahrens 20723ccfa83cSahrens case ZFS_PROP_EXEC: 20733ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 20743ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 20753ccfa83cSahrens break; 20763ccfa83cSahrens 20773ccfa83cSahrens case ZFS_PROP_READONLY: 20783ccfa83cSahrens mntopt_on = MNTOPT_RO; 20793ccfa83cSahrens mntopt_off = MNTOPT_RW; 20803ccfa83cSahrens break; 20813ccfa83cSahrens 20823ccfa83cSahrens case ZFS_PROP_SETUID: 20833ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 20843ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 20853ccfa83cSahrens break; 20863ccfa83cSahrens 20873ccfa83cSahrens case ZFS_PROP_XATTR: 20883ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 20893ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 20903ccfa83cSahrens break; 2091da6c28aaSamw 2092da6c28aaSamw case ZFS_PROP_NBMAND: 2093da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 2094da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 2095da6c28aaSamw break; 209688f61deeSIgor Kozhukhov 209788f61deeSIgor Kozhukhov default: 209888f61deeSIgor Kozhukhov break; 20993ccfa83cSahrens } 21003ccfa83cSahrens 21013bb79becSeschrock /* 21023bb79becSeschrock * Because looking up the mount options is potentially expensive 21033bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 21043bb79becSeschrock * we're looking up a property which requires its presence. 21053bb79becSeschrock */ 21063bb79becSeschrock if (!zhp->zfs_mntcheck && 21073ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 2108ebedde84SEric Taylor libzfs_handle_t *hdl = zhp->zfs_hdl; 2109ebedde84SEric Taylor struct mnttab entry; 21103bb79becSeschrock 2111ebedde84SEric Taylor if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) { 2112ebedde84SEric Taylor zhp->zfs_mntopts = zfs_strdup(hdl, 21133ccfa83cSahrens entry.mnt_mntopts); 21143ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 21153ccfa83cSahrens return (-1); 21163ccfa83cSahrens } 21173bb79becSeschrock 21183bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 21193bb79becSeschrock } 21203bb79becSeschrock 2121fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 2122fa9e4066Sahrens mnt.mnt_mntopts = ""; 2123fa9e4066Sahrens else 2124fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 2125fa9e4066Sahrens 2126fa9e4066Sahrens switch (prop) { 2127fa9e4066Sahrens case ZFS_PROP_ATIME: 2128fa9e4066Sahrens case ZFS_PROP_DEVICES: 2129fa9e4066Sahrens case ZFS_PROP_EXEC: 21303ccfa83cSahrens case ZFS_PROP_READONLY: 21313ccfa83cSahrens case ZFS_PROP_SETUID: 21323ccfa83cSahrens case ZFS_PROP_XATTR: 2133da6c28aaSamw case ZFS_PROP_NBMAND: 213499653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 2135fa9e4066Sahrens 213692241e0bSTom Erickson if (received) 213792241e0bSTom Erickson break; 213892241e0bSTom Erickson 21393ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 214099653d4eSeschrock *val = B_TRUE; 2141fa9e4066Sahrens if (src) 2142990b4856Slling *src = ZPROP_SRC_TEMPORARY; 21433ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 214499653d4eSeschrock *val = B_FALSE; 2145fa9e4066Sahrens if (src) 2146990b4856Slling *src = ZPROP_SRC_TEMPORARY; 2147fa9e4066Sahrens } 214899653d4eSeschrock break; 2149fa9e4066Sahrens 21503ccfa83cSahrens case ZFS_PROP_CANMOUNT: 2151d41c4376SMark J Musante case ZFS_PROP_VOLSIZE: 2152fa9e4066Sahrens case ZFS_PROP_QUOTA: 2153a9799022Sck case ZFS_PROP_REFQUOTA: 2154fa9e4066Sahrens case ZFS_PROP_RESERVATION: 2155a9799022Sck case ZFS_PROP_REFRESERVATION: 2156a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 2157a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 2158a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_COUNT: 2159a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_COUNT: 2160a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 216192241e0bSTom Erickson 216292241e0bSTom Erickson if (*source == NULL) { 216392241e0bSTom Erickson /* not default, must be local */ 2164fa9e4066Sahrens *source = zhp->zfs_name; 216592241e0bSTom Erickson } 216699653d4eSeschrock break; 2167fa9e4066Sahrens 2168fa9e4066Sahrens case ZFS_PROP_MOUNTED: 216999653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 217099653d4eSeschrock break; 2171fa9e4066Sahrens 217239c23413Seschrock case ZFS_PROP_NUMCLONES: 217339c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 217439c23413Seschrock break; 217539c23413Seschrock 2176bd00f61bSrm case ZFS_PROP_VERSION: 2177de8267e0Stimh case ZFS_PROP_NORMALIZE: 2178de8267e0Stimh case ZFS_PROP_UTF8ONLY: 2179de8267e0Stimh case ZFS_PROP_CASE: 2180de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 2181de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 21823d7934e1Srm return (-1); 2183bd00f61bSrm (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2184de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 2185de8267e0Stimh zcmd_free_nvlists(&zc); 2186f4b94bdeSMatthew Ahrens return (-1); 2187bd00f61bSrm } 2188de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 2189de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 2190de8267e0Stimh val) != 0) { 2191de8267e0Stimh zcmd_free_nvlists(&zc); 2192f4b94bdeSMatthew Ahrens return (-1); 2193de8267e0Stimh } 2194aab83bb8SJosef 'Jeff' Sipek nvlist_free(zplprops); 2195de8267e0Stimh zcmd_free_nvlists(&zc); 2196bd00f61bSrm break; 2197bd00f61bSrm 2198ca48f36fSKeith M Wesolowski case ZFS_PROP_INCONSISTENT: 2199ca48f36fSKeith M Wesolowski *val = zhp->zfs_dmustats.dds_inconsistent; 2200ca48f36fSKeith M Wesolowski break; 2201ca48f36fSKeith M Wesolowski 2202fa9e4066Sahrens default: 2203e7437265Sahrens switch (zfs_prop_get_type(prop)) { 220491ebeef5Sahrens case PROP_TYPE_NUMBER: 220591ebeef5Sahrens case PROP_TYPE_INDEX: 2206e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 220774e7dc98SMatthew Ahrens /* 220814843421SMatthew Ahrens * If we tried to use a default value for a 220974e7dc98SMatthew Ahrens * readonly property, it means that it was not 22104d86c0eaSMatthew Ahrens * present. Note this only applies to "truly" 22114d86c0eaSMatthew Ahrens * readonly properties, not set-once properties 22124d86c0eaSMatthew Ahrens * like volblocksize. 221374e7dc98SMatthew Ahrens */ 221474e7dc98SMatthew Ahrens if (zfs_prop_readonly(prop) && 22154d86c0eaSMatthew Ahrens !zfs_prop_setonce(prop) && 221631f572c2STom Erickson *source != NULL && (*source)[0] == '\0') { 221731f572c2STom Erickson *source = NULL; 2218ad2760acSMatthew Ahrens return (-1); 221974e7dc98SMatthew Ahrens } 2220e7437265Sahrens break; 2221e7437265Sahrens 222291ebeef5Sahrens case PROP_TYPE_STRING: 2223e7437265Sahrens default: 2224e7437265Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 2225e7437265Sahrens "cannot get non-numeric property")); 2226e7437265Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 2227e7437265Sahrens dgettext(TEXT_DOMAIN, "internal error"))); 2228e7437265Sahrens } 2229fa9e4066Sahrens } 2230fa9e4066Sahrens 2231fa9e4066Sahrens return (0); 2232fa9e4066Sahrens } 2233fa9e4066Sahrens 2234fa9e4066Sahrens /* 2235fa9e4066Sahrens * Calculate the source type, given the raw source string. 2236fa9e4066Sahrens */ 2237fa9e4066Sahrens static void 2238990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 2239fa9e4066Sahrens char *statbuf, size_t statlen) 2240fa9e4066Sahrens { 2241990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 2242fa9e4066Sahrens return; 2243fa9e4066Sahrens 2244fa9e4066Sahrens if (source == NULL) { 2245990b4856Slling *srctype = ZPROP_SRC_NONE; 2246fa9e4066Sahrens } else if (source[0] == '\0') { 2247990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 224892241e0bSTom Erickson } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { 224992241e0bSTom Erickson *srctype = ZPROP_SRC_RECEIVED; 2250fa9e4066Sahrens } else { 2251fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 2252990b4856Slling *srctype = ZPROP_SRC_LOCAL; 2253fa9e4066Sahrens } else { 2254fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 2255990b4856Slling *srctype = ZPROP_SRC_INHERITED; 2256fa9e4066Sahrens } 2257fa9e4066Sahrens } 2258fa9e4066Sahrens 2259fa9e4066Sahrens } 2260fa9e4066Sahrens 226192241e0bSTom Erickson int 226292241e0bSTom Erickson zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, 226392241e0bSTom Erickson size_t proplen, boolean_t literal) 226492241e0bSTom Erickson { 226592241e0bSTom Erickson zfs_prop_t prop; 226692241e0bSTom Erickson int err = 0; 226792241e0bSTom Erickson 226892241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 226992241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 227092241e0bSTom Erickson return (-1); 227192241e0bSTom Erickson 227292241e0bSTom Erickson prop = zfs_name_to_prop(propname); 227392241e0bSTom Erickson 227492241e0bSTom Erickson if (prop != ZPROP_INVAL) { 227592241e0bSTom Erickson uint64_t cookie; 227692241e0bSTom Erickson if (!nvlist_exists(zhp->zfs_recvd_props, propname)) 227792241e0bSTom Erickson return (-1); 227892241e0bSTom Erickson zfs_set_recvd_props_mode(zhp, &cookie); 227992241e0bSTom Erickson err = zfs_prop_get(zhp, prop, propbuf, proplen, 228092241e0bSTom Erickson NULL, NULL, 0, literal); 228192241e0bSTom Erickson zfs_unset_recvd_props_mode(zhp, &cookie); 228292241e0bSTom Erickson } else { 228392241e0bSTom Erickson nvlist_t *propval; 228492241e0bSTom Erickson char *recvdval; 228592241e0bSTom Erickson if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, 228692241e0bSTom Erickson propname, &propval) != 0) 228792241e0bSTom Erickson return (-1); 228892241e0bSTom Erickson verify(nvlist_lookup_string(propval, ZPROP_VALUE, 228992241e0bSTom Erickson &recvdval) == 0); 229092241e0bSTom Erickson (void) strlcpy(propbuf, recvdval, proplen); 229192241e0bSTom Erickson } 229292241e0bSTom Erickson 229392241e0bSTom Erickson return (err == 0 ? 0 : -1); 229492241e0bSTom Erickson } 229592241e0bSTom Erickson 229619b94df9SMatthew Ahrens static int 229719b94df9SMatthew Ahrens get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen) 229819b94df9SMatthew Ahrens { 229919b94df9SMatthew Ahrens nvlist_t *value; 230019b94df9SMatthew Ahrens nvpair_t *pair; 230119b94df9SMatthew Ahrens 230219b94df9SMatthew Ahrens value = zfs_get_clones_nvl(zhp); 230319b94df9SMatthew Ahrens if (value == NULL) 230419b94df9SMatthew Ahrens return (-1); 230519b94df9SMatthew Ahrens 230619b94df9SMatthew Ahrens propbuf[0] = '\0'; 230719b94df9SMatthew Ahrens for (pair = nvlist_next_nvpair(value, NULL); pair != NULL; 230819b94df9SMatthew Ahrens pair = nvlist_next_nvpair(value, pair)) { 230919b94df9SMatthew Ahrens if (propbuf[0] != '\0') 231019b94df9SMatthew Ahrens (void) strlcat(propbuf, ",", proplen); 231119b94df9SMatthew Ahrens (void) strlcat(propbuf, nvpair_name(pair), proplen); 231219b94df9SMatthew Ahrens } 231319b94df9SMatthew Ahrens 231419b94df9SMatthew Ahrens return (0); 231519b94df9SMatthew Ahrens } 231619b94df9SMatthew Ahrens 231719b94df9SMatthew Ahrens struct get_clones_arg { 231819b94df9SMatthew Ahrens uint64_t numclones; 231919b94df9SMatthew Ahrens nvlist_t *value; 232019b94df9SMatthew Ahrens const char *origin; 23219adfa60dSMatthew Ahrens char buf[ZFS_MAX_DATASET_NAME_LEN]; 232219b94df9SMatthew Ahrens }; 232319b94df9SMatthew Ahrens 232419b94df9SMatthew Ahrens int 232519b94df9SMatthew Ahrens get_clones_cb(zfs_handle_t *zhp, void *arg) 232619b94df9SMatthew Ahrens { 232719b94df9SMatthew Ahrens struct get_clones_arg *gca = arg; 232819b94df9SMatthew Ahrens 232919b94df9SMatthew Ahrens if (gca->numclones == 0) { 233019b94df9SMatthew Ahrens zfs_close(zhp); 233119b94df9SMatthew Ahrens return (0); 233219b94df9SMatthew Ahrens } 233319b94df9SMatthew Ahrens 233419b94df9SMatthew Ahrens if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf), 233519b94df9SMatthew Ahrens NULL, NULL, 0, B_TRUE) != 0) 233619b94df9SMatthew Ahrens goto out; 233719b94df9SMatthew Ahrens if (strcmp(gca->buf, gca->origin) == 0) { 23383b2aab18SMatthew Ahrens fnvlist_add_boolean(gca->value, zfs_get_name(zhp)); 233919b94df9SMatthew Ahrens gca->numclones--; 234019b94df9SMatthew Ahrens } 234119b94df9SMatthew Ahrens 234219b94df9SMatthew Ahrens out: 234319b94df9SMatthew Ahrens (void) zfs_iter_children(zhp, get_clones_cb, gca); 234419b94df9SMatthew Ahrens zfs_close(zhp); 234519b94df9SMatthew Ahrens return (0); 234619b94df9SMatthew Ahrens } 234719b94df9SMatthew Ahrens 234819b94df9SMatthew Ahrens nvlist_t * 234919b94df9SMatthew Ahrens zfs_get_clones_nvl(zfs_handle_t *zhp) 235019b94df9SMatthew Ahrens { 235119b94df9SMatthew Ahrens nvlist_t *nv, *value; 235219b94df9SMatthew Ahrens 235319b94df9SMatthew Ahrens if (nvlist_lookup_nvlist(zhp->zfs_props, 235419b94df9SMatthew Ahrens zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) { 235519b94df9SMatthew Ahrens struct get_clones_arg gca; 235619b94df9SMatthew Ahrens 235719b94df9SMatthew Ahrens /* 235819b94df9SMatthew Ahrens * if this is a snapshot, then the kernel wasn't able 235919b94df9SMatthew Ahrens * to get the clones. Do it by slowly iterating. 236019b94df9SMatthew Ahrens */ 236119b94df9SMatthew Ahrens if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) 236219b94df9SMatthew Ahrens return (NULL); 236319b94df9SMatthew Ahrens if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0) 236419b94df9SMatthew Ahrens return (NULL); 236519b94df9SMatthew Ahrens if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) { 236619b94df9SMatthew Ahrens nvlist_free(nv); 236719b94df9SMatthew Ahrens return (NULL); 236819b94df9SMatthew Ahrens } 236919b94df9SMatthew Ahrens 237019b94df9SMatthew Ahrens gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES); 237119b94df9SMatthew Ahrens gca.value = value; 237219b94df9SMatthew Ahrens gca.origin = zhp->zfs_name; 237319b94df9SMatthew Ahrens 237419b94df9SMatthew Ahrens if (gca.numclones != 0) { 237519b94df9SMatthew Ahrens zfs_handle_t *root; 23769adfa60dSMatthew Ahrens char pool[ZFS_MAX_DATASET_NAME_LEN]; 237719b94df9SMatthew Ahrens char *cp = pool; 237819b94df9SMatthew Ahrens 237919b94df9SMatthew Ahrens /* get the pool name */ 238019b94df9SMatthew Ahrens (void) strlcpy(pool, zhp->zfs_name, sizeof (pool)); 238119b94df9SMatthew Ahrens (void) strsep(&cp, "/@"); 238219b94df9SMatthew Ahrens root = zfs_open(zhp->zfs_hdl, pool, 238319b94df9SMatthew Ahrens ZFS_TYPE_FILESYSTEM); 238419b94df9SMatthew Ahrens 238519b94df9SMatthew Ahrens (void) get_clones_cb(root, &gca); 238619b94df9SMatthew Ahrens } 238719b94df9SMatthew Ahrens 238819b94df9SMatthew Ahrens if (gca.numclones != 0 || 238919b94df9SMatthew Ahrens nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 || 239019b94df9SMatthew Ahrens nvlist_add_nvlist(zhp->zfs_props, 239119b94df9SMatthew Ahrens zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) { 239219b94df9SMatthew Ahrens nvlist_free(nv); 239319b94df9SMatthew Ahrens nvlist_free(value); 239419b94df9SMatthew Ahrens return (NULL); 239519b94df9SMatthew Ahrens } 239619b94df9SMatthew Ahrens nvlist_free(nv); 239719b94df9SMatthew Ahrens nvlist_free(value); 239819b94df9SMatthew Ahrens verify(0 == nvlist_lookup_nvlist(zhp->zfs_props, 239919b94df9SMatthew Ahrens zfs_prop_to_name(ZFS_PROP_CLONES), &nv)); 240019b94df9SMatthew Ahrens } 240119b94df9SMatthew Ahrens 240219b94df9SMatthew Ahrens verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0); 240319b94df9SMatthew Ahrens 240419b94df9SMatthew Ahrens return (value); 240519b94df9SMatthew Ahrens } 240619b94df9SMatthew Ahrens 2407dfc11533SChris Williamson /* 2408dfc11533SChris Williamson * Accepts a property and value and checks that the value 2409dfc11533SChris Williamson * matches the one found by the channel program. If they are 2410dfc11533SChris Williamson * not equal, print both of them. 2411dfc11533SChris Williamson */ 2412dfc11533SChris Williamson void 2413dfc11533SChris Williamson zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval, 2414dfc11533SChris Williamson const char *strval) 2415dfc11533SChris Williamson { 2416dfc11533SChris Williamson if (!zhp->zfs_hdl->libzfs_prop_debug) 2417dfc11533SChris Williamson return; 2418dfc11533SChris Williamson int error; 2419dfc11533SChris Williamson char *poolname = zhp->zpool_hdl->zpool_name; 2420dfc11533SChris Williamson const char *program = 2421dfc11533SChris Williamson "args = ...\n" 2422dfc11533SChris Williamson "ds = args['dataset']\n" 2423dfc11533SChris Williamson "prop = args['property']\n" 2424dfc11533SChris Williamson "value, setpoint = zfs.get_prop(ds, prop)\n" 2425dfc11533SChris Williamson "return {value=value, setpoint=setpoint}\n"; 2426dfc11533SChris Williamson nvlist_t *outnvl; 2427dfc11533SChris Williamson nvlist_t *retnvl; 2428dfc11533SChris Williamson nvlist_t *argnvl = fnvlist_alloc(); 2429dfc11533SChris Williamson 2430dfc11533SChris Williamson fnvlist_add_string(argnvl, "dataset", zhp->zfs_name); 2431dfc11533SChris Williamson fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop)); 2432dfc11533SChris Williamson 2433a3b28680SSerapheim Dimitropoulos error = lzc_channel_program_nosync(poolname, program, 2434dfc11533SChris Williamson 10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl); 2435dfc11533SChris Williamson 2436dfc11533SChris Williamson if (error == 0) { 2437dfc11533SChris Williamson retnvl = fnvlist_lookup_nvlist(outnvl, "return"); 2438dfc11533SChris Williamson if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) { 2439dfc11533SChris Williamson int64_t ans; 2440dfc11533SChris Williamson error = nvlist_lookup_int64(retnvl, "value", &ans); 2441dfc11533SChris Williamson if (error != 0) { 2442dfc11533SChris Williamson (void) fprintf(stderr, "zcp check error: %u\n", 2443dfc11533SChris Williamson error); 2444dfc11533SChris Williamson return; 2445dfc11533SChris Williamson } 2446dfc11533SChris Williamson if (ans != intval) { 2447dfc11533SChris Williamson (void) fprintf(stderr, 2448dfc11533SChris Williamson "%s: zfs found %lld, but zcp found %lld\n", 2449dfc11533SChris Williamson zfs_prop_to_name(prop), 2450dfc11533SChris Williamson (longlong_t)intval, (longlong_t)ans); 2451dfc11533SChris Williamson } 2452dfc11533SChris Williamson } else { 2453dfc11533SChris Williamson char *str_ans; 2454dfc11533SChris Williamson error = nvlist_lookup_string(retnvl, "value", &str_ans); 2455dfc11533SChris Williamson if (error != 0) { 2456dfc11533SChris Williamson (void) fprintf(stderr, "zcp check error: %u\n", 2457dfc11533SChris Williamson error); 2458dfc11533SChris Williamson return; 2459dfc11533SChris Williamson } 2460dfc11533SChris Williamson if (strcmp(strval, str_ans) != 0) { 2461dfc11533SChris Williamson (void) fprintf(stderr, 2462dfc11533SChris Williamson "%s: zfs found %s, but zcp found %s\n", 2463dfc11533SChris Williamson zfs_prop_to_name(prop), 2464dfc11533SChris Williamson strval, str_ans); 2465dfc11533SChris Williamson } 2466dfc11533SChris Williamson } 2467dfc11533SChris Williamson } else { 2468dfc11533SChris Williamson (void) fprintf(stderr, 2469dfc11533SChris Williamson "zcp check failed, channel program error: %u\n", error); 2470dfc11533SChris Williamson } 2471dfc11533SChris Williamson nvlist_free(argnvl); 2472dfc11533SChris Williamson nvlist_free(outnvl); 2473dfc11533SChris Williamson } 2474dfc11533SChris Williamson 2475fa9e4066Sahrens /* 2476fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 2477fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 2478fa9e4066Sahrens * human-readable form. 2479fa9e4066Sahrens * 2480fa9e4066Sahrens * Returns 0 on success, or -1 on error. 2481fa9e4066Sahrens */ 2482fa9e4066Sahrens int 2483fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 2484990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 2485fa9e4066Sahrens { 2486fa9e4066Sahrens char *source = NULL; 2487fa9e4066Sahrens uint64_t val; 24889c3fd121SMatthew Ahrens const char *str; 2489e9dbad6fSeschrock const char *strval; 249092241e0bSTom Erickson boolean_t received = zfs_is_recvd_props_mode(zhp); 2491fa9e4066Sahrens 2492fa9e4066Sahrens /* 2493fa9e4066Sahrens * Check to see if this property applies to our object 2494fa9e4066Sahrens */ 2495fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 2496fa9e4066Sahrens return (-1); 2497fa9e4066Sahrens 249892241e0bSTom Erickson if (received && zfs_prop_readonly(prop)) 249992241e0bSTom Erickson return (-1); 250092241e0bSTom Erickson 2501fa9e4066Sahrens if (src) 2502990b4856Slling *src = ZPROP_SRC_NONE; 2503fa9e4066Sahrens 2504fa9e4066Sahrens switch (prop) { 2505fa9e4066Sahrens case ZFS_PROP_CREATION: 2506fa9e4066Sahrens /* 2507fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 2508fa9e4066Sahrens * this into a string unless 'literal' is specified. 2509fa9e4066Sahrens */ 2510fa9e4066Sahrens { 2511a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 2512a2eea2e1Sahrens time_t time = (time_t)val; 2513fa9e4066Sahrens struct tm t; 2514fa9e4066Sahrens 2515fa9e4066Sahrens if (literal || 2516fa9e4066Sahrens localtime_r(&time, &t) == NULL || 2517fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 2518fa9e4066Sahrens &t) == 0) 2519a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 2520fa9e4066Sahrens } 2521dfc11533SChris Williamson zcp_check(zhp, prop, val, NULL); 2522fa9e4066Sahrens break; 2523fa9e4066Sahrens 2524fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 2525fa9e4066Sahrens /* 2526fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 2527fa9e4066Sahrens * 2528fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 2529fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 2530fa9e4066Sahrens * after our ancestor and append it to the inherited value. 2531fa9e4066Sahrens * 2532fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 2533fa9e4066Sahrens * root to any values we return. 2534fa9e4066Sahrens */ 253529ab75c9Srm 25367f7322feSeschrock str = getprop_string(zhp, prop, &source); 2537fa9e4066Sahrens 2538b21718f0Sgw if (str[0] == '/') { 253929ab75c9Srm char buf[MAXPATHLEN]; 254029ab75c9Srm char *root = buf; 2541a79992aaSTom Erickson const char *relpath; 2542fa9e4066Sahrens 2543a79992aaSTom Erickson /* 2544a79992aaSTom Erickson * If we inherit the mountpoint, even from a dataset 2545a79992aaSTom Erickson * with a received value, the source will be the path of 2546a79992aaSTom Erickson * the dataset we inherit from. If source is 2547a79992aaSTom Erickson * ZPROP_SOURCE_VAL_RECVD, the received value is not 2548a79992aaSTom Erickson * inherited. 2549a79992aaSTom Erickson */ 2550a79992aaSTom Erickson if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { 2551a79992aaSTom Erickson relpath = ""; 2552a79992aaSTom Erickson } else { 2553a79992aaSTom Erickson relpath = zhp->zfs_name + strlen(source); 2554a79992aaSTom Erickson if (relpath[0] == '/') 2555a79992aaSTom Erickson relpath++; 2556a79992aaSTom Erickson } 2557b21718f0Sgw 255829ab75c9Srm if ((zpool_get_prop(zhp->zpool_hdl, 2559c58b3526SAdam Stevko ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL, 2560c58b3526SAdam Stevko B_FALSE)) || (strcmp(root, "-") == 0)) 256129ab75c9Srm root[0] = '\0'; 2562b21718f0Sgw /* 2563b21718f0Sgw * Special case an alternate root of '/'. This will 2564b21718f0Sgw * avoid having multiple leading slashes in the 2565b21718f0Sgw * mountpoint path. 2566b21718f0Sgw */ 2567b21718f0Sgw if (strcmp(root, "/") == 0) 2568b21718f0Sgw root++; 2569b21718f0Sgw 2570b21718f0Sgw /* 2571b21718f0Sgw * If the mountpoint is '/' then skip over this 2572b21718f0Sgw * if we are obtaining either an alternate root or 2573b21718f0Sgw * an inherited mountpoint. 2574b21718f0Sgw */ 2575b21718f0Sgw if (str[1] == '\0' && (root[0] != '\0' || 2576b21718f0Sgw relpath[0] != '\0')) 25777f7322feSeschrock str++; 2578fa9e4066Sahrens 2579fa9e4066Sahrens if (relpath[0] == '\0') 2580fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 25817f7322feSeschrock root, str); 2582fa9e4066Sahrens else 2583fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 25847f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 2585fa9e4066Sahrens relpath); 2586fa9e4066Sahrens } else { 2587fa9e4066Sahrens /* 'legacy' or 'none' */ 25887f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 2589fa9e4066Sahrens } 2590dfc11533SChris Williamson zcp_check(zhp, prop, NULL, propbuf); 2591fa9e4066Sahrens break; 2592fa9e4066Sahrens 2593fa9e4066Sahrens case ZFS_PROP_ORIGIN: 25949c3fd121SMatthew Ahrens str = getprop_string(zhp, prop, &source); 25959c3fd121SMatthew Ahrens if (str == NULL) 2596fa9e4066Sahrens return (-1); 25979c3fd121SMatthew Ahrens (void) strlcpy(propbuf, str, proplen); 2598dfc11533SChris Williamson zcp_check(zhp, prop, NULL, str); 2599fa9e4066Sahrens break; 2600fa9e4066Sahrens 260119b94df9SMatthew Ahrens case ZFS_PROP_CLONES: 260219b94df9SMatthew Ahrens if (get_clones_string(zhp, propbuf, proplen) != 0) 260319b94df9SMatthew Ahrens return (-1); 260419b94df9SMatthew Ahrens break; 260519b94df9SMatthew Ahrens 2606fa9e4066Sahrens case ZFS_PROP_QUOTA: 2607a9799022Sck case ZFS_PROP_REFQUOTA: 2608fa9e4066Sahrens case ZFS_PROP_RESERVATION: 2609a9799022Sck case ZFS_PROP_REFRESERVATION: 2610a9799022Sck 261199653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 261299653d4eSeschrock return (-1); 2613fa9e4066Sahrens /* 2614fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 2615fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 2616fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 2617fa9e4066Sahrens * that its set locally. 2618fa9e4066Sahrens */ 2619fa9e4066Sahrens if (val == 0) { 2620fa9e4066Sahrens if (literal) 2621fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 2622fa9e4066Sahrens else 2623fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 2624fa9e4066Sahrens } else { 2625fa9e4066Sahrens if (literal) 26265ad82045Snd (void) snprintf(propbuf, proplen, "%llu", 2627b1b8ab34Slling (u_longlong_t)val); 2628fa9e4066Sahrens else 2629fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 2630fa9e4066Sahrens } 2631dfc11533SChris Williamson zcp_check(zhp, prop, val, NULL); 2632fa9e4066Sahrens break; 2633fa9e4066Sahrens 2634a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 2635a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 2636a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_COUNT: 2637a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_COUNT: 2638a2afb611SJerry Jelinek 2639a2afb611SJerry Jelinek if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2640a2afb611SJerry Jelinek return (-1); 2641a2afb611SJerry Jelinek 2642a2afb611SJerry Jelinek /* 2643a2afb611SJerry Jelinek * If limit is UINT64_MAX, we translate this into 'none' (unless 2644a2afb611SJerry Jelinek * literal is set), and indicate that it's the default value. 2645a2afb611SJerry Jelinek * Otherwise, we print the number nicely and indicate that it's 2646a2afb611SJerry Jelinek * set locally. 2647a2afb611SJerry Jelinek */ 2648a2afb611SJerry Jelinek if (literal) { 2649a2afb611SJerry Jelinek (void) snprintf(propbuf, proplen, "%llu", 2650a2afb611SJerry Jelinek (u_longlong_t)val); 2651a2afb611SJerry Jelinek } else if (val == UINT64_MAX) { 2652a2afb611SJerry Jelinek (void) strlcpy(propbuf, "none", proplen); 2653a2afb611SJerry Jelinek } else { 2654a2afb611SJerry Jelinek zfs_nicenum(val, propbuf, proplen); 2655a2afb611SJerry Jelinek } 2656dfc11533SChris Williamson 2657dfc11533SChris Williamson zcp_check(zhp, prop, val, NULL); 2658a2afb611SJerry Jelinek break; 2659a2afb611SJerry Jelinek 2660187d6ac0SMatt Ahrens case ZFS_PROP_REFRATIO: 2661fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 266299653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 266399653d4eSeschrock return (-1); 2664b24ab676SJeff Bonwick (void) snprintf(propbuf, proplen, "%llu.%02llux", 2665b24ab676SJeff Bonwick (u_longlong_t)(val / 100), 2666b24ab676SJeff Bonwick (u_longlong_t)(val % 100)); 2667dfc11533SChris Williamson zcp_check(zhp, prop, val, NULL); 2668fa9e4066Sahrens break; 2669fa9e4066Sahrens 2670fa9e4066Sahrens case ZFS_PROP_TYPE: 2671fa9e4066Sahrens switch (zhp->zfs_type) { 2672fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 2673fa9e4066Sahrens str = "filesystem"; 2674fa9e4066Sahrens break; 2675fa9e4066Sahrens case ZFS_TYPE_VOLUME: 2676fa9e4066Sahrens str = "volume"; 2677fa9e4066Sahrens break; 2678fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 2679fa9e4066Sahrens str = "snapshot"; 2680fa9e4066Sahrens break; 268178f17100SMatthew Ahrens case ZFS_TYPE_BOOKMARK: 268278f17100SMatthew Ahrens str = "bookmark"; 268378f17100SMatthew Ahrens break; 2684fa9e4066Sahrens default: 268599653d4eSeschrock abort(); 2686fa9e4066Sahrens } 2687fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2688dfc11533SChris Williamson zcp_check(zhp, prop, NULL, propbuf); 2689fa9e4066Sahrens break; 2690fa9e4066Sahrens 2691fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2692fa9e4066Sahrens /* 2693fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2694fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2695fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2696fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2697fa9e4066Sahrens */ 269899653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 269999653d4eSeschrock src, &source, &val) != 0) 270099653d4eSeschrock return (-1); 270199653d4eSeschrock if (val) 2702fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2703fa9e4066Sahrens else 2704fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2705fa9e4066Sahrens break; 2706fa9e4066Sahrens 2707fa9e4066Sahrens case ZFS_PROP_NAME: 2708fa9e4066Sahrens /* 2709fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2710fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2711fa9e4066Sahrens * consumers. 2712fa9e4066Sahrens */ 2713fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2714dfc11533SChris Williamson zcp_check(zhp, prop, NULL, propbuf); 2715fa9e4066Sahrens break; 2716fa9e4066Sahrens 27174201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 27184201a95eSRic Aleshire { 27194201a95eSRic Aleshire m_label_t *new_sl = NULL; 27204201a95eSRic Aleshire char *ascii = NULL; /* human readable label */ 27214201a95eSRic Aleshire 27224201a95eSRic Aleshire (void) strlcpy(propbuf, 27234201a95eSRic Aleshire getprop_string(zhp, prop, &source), proplen); 27244201a95eSRic Aleshire 27254201a95eSRic Aleshire if (literal || (strcasecmp(propbuf, 27264201a95eSRic Aleshire ZFS_MLSLABEL_DEFAULT) == 0)) 27274201a95eSRic Aleshire break; 27284201a95eSRic Aleshire 27294201a95eSRic Aleshire /* 27304201a95eSRic Aleshire * Try to translate the internal hex string to 27314201a95eSRic Aleshire * human-readable output. If there are any 27324201a95eSRic Aleshire * problems just use the hex string. 27334201a95eSRic Aleshire */ 27344201a95eSRic Aleshire 27354201a95eSRic Aleshire if (str_to_label(propbuf, &new_sl, MAC_LABEL, 27364201a95eSRic Aleshire L_NO_CORRECTION, NULL) == -1) { 27374201a95eSRic Aleshire m_label_free(new_sl); 27384201a95eSRic Aleshire break; 27394201a95eSRic Aleshire } 27404201a95eSRic Aleshire 27414201a95eSRic Aleshire if (label_to_str(new_sl, &ascii, M_LABEL, 27424201a95eSRic Aleshire DEF_NAMES) != 0) { 27434201a95eSRic Aleshire if (ascii) 27444201a95eSRic Aleshire free(ascii); 27454201a95eSRic Aleshire m_label_free(new_sl); 27464201a95eSRic Aleshire break; 27474201a95eSRic Aleshire } 27484201a95eSRic Aleshire m_label_free(new_sl); 27494201a95eSRic Aleshire 27504201a95eSRic Aleshire (void) strlcpy(propbuf, ascii, proplen); 27514201a95eSRic Aleshire free(ascii); 27524201a95eSRic Aleshire } 27534201a95eSRic Aleshire break; 27544201a95eSRic Aleshire 2755f0f3ef5aSGarrett D'Amore case ZFS_PROP_GUID: 2756f0f3ef5aSGarrett D'Amore /* 2757f0f3ef5aSGarrett D'Amore * GUIDs are stored as numbers, but they are identifiers. 2758f0f3ef5aSGarrett D'Amore * We don't want them to be pretty printed, because pretty 2759f0f3ef5aSGarrett D'Amore * printing mangles the ID into a truncated and useless value. 2760f0f3ef5aSGarrett D'Amore */ 2761f0f3ef5aSGarrett D'Amore if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 2762f0f3ef5aSGarrett D'Amore return (-1); 2763f0f3ef5aSGarrett D'Amore (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); 2764dfc11533SChris Williamson zcp_check(zhp, prop, val, NULL); 2765f0f3ef5aSGarrett D'Amore break; 2766f0f3ef5aSGarrett D'Amore 2767fa9e4066Sahrens default: 2768e7437265Sahrens switch (zfs_prop_get_type(prop)) { 276991ebeef5Sahrens case PROP_TYPE_NUMBER: 2770e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2771dfc11533SChris Williamson &source, &val) != 0) { 2772e7437265Sahrens return (-1); 2773dfc11533SChris Williamson } 2774dfc11533SChris Williamson 2775dfc11533SChris Williamson if (literal) { 2776e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2777e7437265Sahrens (u_longlong_t)val); 2778dfc11533SChris Williamson } else { 2779e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2780dfc11533SChris Williamson } 2781dfc11533SChris Williamson zcp_check(zhp, prop, val, NULL); 2782e7437265Sahrens break; 2783e7437265Sahrens 278491ebeef5Sahrens case PROP_TYPE_STRING: 27859c3fd121SMatthew Ahrens str = getprop_string(zhp, prop, &source); 27869c3fd121SMatthew Ahrens if (str == NULL) 27879c3fd121SMatthew Ahrens return (-1); 2788dfc11533SChris Williamson 27899c3fd121SMatthew Ahrens (void) strlcpy(propbuf, str, proplen); 2790dfc11533SChris Williamson zcp_check(zhp, prop, NULL, str); 2791e7437265Sahrens break; 2792e7437265Sahrens 279391ebeef5Sahrens case PROP_TYPE_INDEX: 2794fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2795fe192f76Sahrens &source, &val) != 0) 2796fe192f76Sahrens return (-1); 2797fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2798e7437265Sahrens return (-1); 2799dfc11533SChris Williamson 2800e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2801dfc11533SChris Williamson zcp_check(zhp, prop, NULL, strval); 2802e7437265Sahrens break; 2803e7437265Sahrens 2804e7437265Sahrens default: 2805e7437265Sahrens abort(); 2806e7437265Sahrens } 2807fa9e4066Sahrens } 2808fa9e4066Sahrens 2809fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2810fa9e4066Sahrens 2811fa9e4066Sahrens return (0); 2812fa9e4066Sahrens } 2813fa9e4066Sahrens 2814fa9e4066Sahrens /* 2815fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2816fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2817fa9e4066Sahrens * hard-coded property types. 2818fa9e4066Sahrens */ 2819fa9e4066Sahrens uint64_t 2820fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2821fa9e4066Sahrens { 2822fa9e4066Sahrens char *source; 282399653d4eSeschrock uint64_t val; 282499653d4eSeschrock 28253cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 2826fa9e4066Sahrens 282799653d4eSeschrock return (val); 2828fa9e4066Sahrens } 2829fa9e4066Sahrens 28307b97dc1aSrm int 28317b97dc1aSrm zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 28327b97dc1aSrm { 28337b97dc1aSrm char buf[64]; 28347b97dc1aSrm 283514843421SMatthew Ahrens (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); 28367b97dc1aSrm return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 28377b97dc1aSrm } 28387b97dc1aSrm 2839fa9e4066Sahrens /* 2840fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2841fa9e4066Sahrens */ 2842fa9e4066Sahrens int 2843fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2844990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2845fa9e4066Sahrens { 2846fa9e4066Sahrens char *source; 2847fa9e4066Sahrens 2848fa9e4066Sahrens /* 2849fa9e4066Sahrens * Check to see if this property applies to our object 2850fa9e4066Sahrens */ 2851e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2852ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 285399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 285499653d4eSeschrock zfs_prop_to_name(prop))); 2855e45ce728Sahrens } 2856fa9e4066Sahrens 2857fa9e4066Sahrens if (src) 2858990b4856Slling *src = ZPROP_SRC_NONE; 2859fa9e4066Sahrens 286099653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 286199653d4eSeschrock return (-1); 2862fa9e4066Sahrens 2863fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2864fa9e4066Sahrens 2865fa9e4066Sahrens return (0); 2866fa9e4066Sahrens } 2867fa9e4066Sahrens 286814843421SMatthew Ahrens static int 286914843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, 287014843421SMatthew Ahrens char **domainp, idmap_rid_t *ridp) 287114843421SMatthew Ahrens { 287214843421SMatthew Ahrens idmap_get_handle_t *get_hdl = NULL; 287314843421SMatthew Ahrens idmap_stat status; 287414843421SMatthew Ahrens int err = EINVAL; 287514843421SMatthew Ahrens 28761fdeec65Sjoyce mcintosh if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS) 287714843421SMatthew Ahrens goto out; 287814843421SMatthew Ahrens 287914843421SMatthew Ahrens if (isuser) { 288014843421SMatthew Ahrens err = idmap_get_sidbyuid(get_hdl, id, 288114843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 288214843421SMatthew Ahrens } else { 288314843421SMatthew Ahrens err = idmap_get_sidbygid(get_hdl, id, 288414843421SMatthew Ahrens IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); 288514843421SMatthew Ahrens } 288614843421SMatthew Ahrens if (err == IDMAP_SUCCESS && 288714843421SMatthew Ahrens idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && 288814843421SMatthew Ahrens status == IDMAP_SUCCESS) 288914843421SMatthew Ahrens err = 0; 289014843421SMatthew Ahrens else 289114843421SMatthew Ahrens err = EINVAL; 289214843421SMatthew Ahrens out: 289314843421SMatthew Ahrens if (get_hdl) 289414843421SMatthew Ahrens idmap_get_destroy(get_hdl); 289514843421SMatthew Ahrens return (err); 289614843421SMatthew Ahrens } 289714843421SMatthew Ahrens 289814843421SMatthew Ahrens /* 289914843421SMatthew Ahrens * convert the propname into parameters needed by kernel 290014843421SMatthew Ahrens * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 290114843421SMatthew Ahrens * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 290214843421SMatthew Ahrens */ 290314843421SMatthew Ahrens static int 290414843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned, 290514843421SMatthew Ahrens zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) 290614843421SMatthew Ahrens { 290714843421SMatthew Ahrens zfs_userquota_prop_t type; 290814843421SMatthew Ahrens char *cp, *end; 29093b12c289SMatthew Ahrens char *numericsid = NULL; 291014843421SMatthew Ahrens boolean_t isuser; 291114843421SMatthew Ahrens 291214843421SMatthew Ahrens domain[0] = '\0'; 29131ed6b69aSGordon Ross *ridp = 0; 291414843421SMatthew Ahrens /* Figure out the property type ({user|group}{quota|space}) */ 291514843421SMatthew Ahrens for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { 291614843421SMatthew Ahrens if (strncmp(propname, zfs_userquota_prop_prefixes[type], 291714843421SMatthew Ahrens strlen(zfs_userquota_prop_prefixes[type])) == 0) 291814843421SMatthew Ahrens break; 291914843421SMatthew Ahrens } 292014843421SMatthew Ahrens if (type == ZFS_NUM_USERQUOTA_PROPS) 292114843421SMatthew Ahrens return (EINVAL); 292214843421SMatthew Ahrens *typep = type; 292314843421SMatthew Ahrens 292414843421SMatthew Ahrens isuser = (type == ZFS_PROP_USERQUOTA || 292514843421SMatthew Ahrens type == ZFS_PROP_USERUSED); 292614843421SMatthew Ahrens 292714843421SMatthew Ahrens cp = strchr(propname, '@') + 1; 292814843421SMatthew Ahrens 292914843421SMatthew Ahrens if (strchr(cp, '@')) { 293014843421SMatthew Ahrens /* 293114843421SMatthew Ahrens * It's a SID name (eg "user@domain") that needs to be 29323b12c289SMatthew Ahrens * turned into S-1-domainID-RID. 293314843421SMatthew Ahrens */ 29341ed6b69aSGordon Ross int flag = 0; 29351ed6b69aSGordon Ross idmap_stat stat, map_stat; 29361ed6b69aSGordon Ross uid_t pid; 29371ed6b69aSGordon Ross idmap_rid_t rid; 29381ed6b69aSGordon Ross idmap_get_handle_t *gh = NULL; 29391ed6b69aSGordon Ross 29401ed6b69aSGordon Ross stat = idmap_get_create(&gh); 29411ed6b69aSGordon Ross if (stat != IDMAP_SUCCESS) { 29421ed6b69aSGordon Ross idmap_get_destroy(gh); 29431ed6b69aSGordon Ross return (ENOMEM); 29441ed6b69aSGordon Ross } 294514843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 294614843421SMatthew Ahrens return (ENOENT); 29473b12c289SMatthew Ahrens if (isuser) { 29481ed6b69aSGordon Ross stat = idmap_getuidbywinname(cp, NULL, flag, &pid); 29491ed6b69aSGordon Ross if (stat < 0) 29501ed6b69aSGordon Ross return (ENOENT); 29511ed6b69aSGordon Ross stat = idmap_get_sidbyuid(gh, pid, flag, &numericsid, 29521ed6b69aSGordon Ross &rid, &map_stat); 29533b12c289SMatthew Ahrens } else { 29541ed6b69aSGordon Ross stat = idmap_getgidbywinname(cp, NULL, flag, &pid); 29551ed6b69aSGordon Ross if (stat < 0) 29561ed6b69aSGordon Ross return (ENOENT); 29571ed6b69aSGordon Ross stat = idmap_get_sidbygid(gh, pid, flag, &numericsid, 29581ed6b69aSGordon Ross &rid, &map_stat); 29591ed6b69aSGordon Ross } 29601ed6b69aSGordon Ross if (stat < 0) { 29611ed6b69aSGordon Ross idmap_get_destroy(gh); 29621ed6b69aSGordon Ross return (ENOENT); 29633b12c289SMatthew Ahrens } 29641ed6b69aSGordon Ross stat = idmap_get_mappings(gh); 29651ed6b69aSGordon Ross idmap_get_destroy(gh); 29661ed6b69aSGordon Ross 29671ed6b69aSGordon Ross if (stat < 0) { 296814843421SMatthew Ahrens return (ENOENT); 29693b12c289SMatthew Ahrens } 29703b12c289SMatthew Ahrens if (numericsid == NULL) 297114843421SMatthew Ahrens return (ENOENT); 29723b12c289SMatthew Ahrens cp = numericsid; 29731ed6b69aSGordon Ross *ridp = rid; 29743b12c289SMatthew Ahrens /* will be further decoded below */ 29753b12c289SMatthew Ahrens } 29763b12c289SMatthew Ahrens 29773b12c289SMatthew Ahrens if (strncmp(cp, "S-1-", 4) == 0) { 297814843421SMatthew Ahrens /* It's a numeric SID (eg "S-1-234-567-89") */ 29793b12c289SMatthew Ahrens (void) strlcpy(domain, cp, domainlen); 298014843421SMatthew Ahrens errno = 0; 29811ed6b69aSGordon Ross if (*ridp == 0) { 29821ed6b69aSGordon Ross cp = strrchr(domain, '-'); 29831ed6b69aSGordon Ross *cp = '\0'; 29841ed6b69aSGordon Ross cp++; 29851ed6b69aSGordon Ross *ridp = strtoull(cp, &end, 10); 29861ed6b69aSGordon Ross } else { 29871ed6b69aSGordon Ross end = ""; 29881ed6b69aSGordon Ross } 29893b12c289SMatthew Ahrens if (numericsid) { 29903b12c289SMatthew Ahrens free(numericsid); 29913b12c289SMatthew Ahrens numericsid = NULL; 29923b12c289SMatthew Ahrens } 2993777badbaSMatthew Ahrens if (errno != 0 || *end != '\0') 299414843421SMatthew Ahrens return (EINVAL); 299514843421SMatthew Ahrens } else if (!isdigit(*cp)) { 299614843421SMatthew Ahrens /* 299714843421SMatthew Ahrens * It's a user/group name (eg "user") that needs to be 299814843421SMatthew Ahrens * turned into a uid/gid 299914843421SMatthew Ahrens */ 300014843421SMatthew Ahrens if (zoned && getzoneid() == GLOBAL_ZONEID) 300114843421SMatthew Ahrens return (ENOENT); 300214843421SMatthew Ahrens if (isuser) { 300314843421SMatthew Ahrens struct passwd *pw; 300414843421SMatthew Ahrens pw = getpwnam(cp); 300514843421SMatthew Ahrens if (pw == NULL) 300614843421SMatthew Ahrens return (ENOENT); 300714843421SMatthew Ahrens *ridp = pw->pw_uid; 300814843421SMatthew Ahrens } else { 300914843421SMatthew Ahrens struct group *gr; 301014843421SMatthew Ahrens gr = getgrnam(cp); 301114843421SMatthew Ahrens if (gr == NULL) 301214843421SMatthew Ahrens return (ENOENT); 301314843421SMatthew Ahrens *ridp = gr->gr_gid; 301414843421SMatthew Ahrens } 301514843421SMatthew Ahrens } else { 301614843421SMatthew Ahrens /* It's a user/group ID (eg "12345"). */ 301714843421SMatthew Ahrens uid_t id = strtoul(cp, &end, 10); 301814843421SMatthew Ahrens idmap_rid_t rid; 301914843421SMatthew Ahrens char *mapdomain; 302014843421SMatthew Ahrens 302114843421SMatthew Ahrens if (*end != '\0') 302214843421SMatthew Ahrens return (EINVAL); 302314843421SMatthew Ahrens if (id > MAXUID) { 302414843421SMatthew Ahrens /* It's an ephemeral ID. */ 302514843421SMatthew Ahrens if (idmap_id_to_numeric_domain_rid(id, isuser, 302614843421SMatthew Ahrens &mapdomain, &rid) != 0) 302714843421SMatthew Ahrens return (ENOENT); 30283b12c289SMatthew Ahrens (void) strlcpy(domain, mapdomain, domainlen); 302914843421SMatthew Ahrens *ridp = rid; 303014843421SMatthew Ahrens } else { 303114843421SMatthew Ahrens *ridp = id; 303214843421SMatthew Ahrens } 303314843421SMatthew Ahrens } 303414843421SMatthew Ahrens 30353b12c289SMatthew Ahrens ASSERT3P(numericsid, ==, NULL); 303614843421SMatthew Ahrens return (0); 303714843421SMatthew Ahrens } 303814843421SMatthew Ahrens 3039edea4b55SLin Ling static int 3040edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname, 3041edea4b55SLin Ling uint64_t *propvalue, zfs_userquota_prop_t *typep) 304214843421SMatthew Ahrens { 304314843421SMatthew Ahrens int err; 304414843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 304514843421SMatthew Ahrens 304619b94df9SMatthew Ahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 304714843421SMatthew Ahrens 304814843421SMatthew Ahrens err = userquota_propname_decode(propname, 304914843421SMatthew Ahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED), 3050edea4b55SLin Ling typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); 3051edea4b55SLin Ling zc.zc_objset_type = *typep; 305214843421SMatthew Ahrens if (err) 305314843421SMatthew Ahrens return (err); 305414843421SMatthew Ahrens 305514843421SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); 305614843421SMatthew Ahrens if (err) 305714843421SMatthew Ahrens return (err); 305814843421SMatthew Ahrens 3059edea4b55SLin Ling *propvalue = zc.zc_cookie; 3060edea4b55SLin Ling return (0); 3061edea4b55SLin Ling } 3062edea4b55SLin Ling 3063edea4b55SLin Ling int 3064edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, 3065edea4b55SLin Ling uint64_t *propvalue) 3066edea4b55SLin Ling { 3067edea4b55SLin Ling zfs_userquota_prop_t type; 3068edea4b55SLin Ling 3069edea4b55SLin Ling return (zfs_prop_get_userquota_common(zhp, propname, propvalue, 3070edea4b55SLin Ling &type)); 3071edea4b55SLin Ling } 3072edea4b55SLin Ling 3073edea4b55SLin Ling int 3074edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, 3075edea4b55SLin Ling char *propbuf, int proplen, boolean_t literal) 3076edea4b55SLin Ling { 3077edea4b55SLin Ling int err; 3078edea4b55SLin Ling uint64_t propvalue; 3079edea4b55SLin Ling zfs_userquota_prop_t type; 3080edea4b55SLin Ling 3081edea4b55SLin Ling err = zfs_prop_get_userquota_common(zhp, propname, &propvalue, 3082edea4b55SLin Ling &type); 3083edea4b55SLin Ling 3084edea4b55SLin Ling if (err) 3085edea4b55SLin Ling return (err); 3086edea4b55SLin Ling 308714843421SMatthew Ahrens if (literal) { 3088edea4b55SLin Ling (void) snprintf(propbuf, proplen, "%llu", propvalue); 3089edea4b55SLin Ling } else if (propvalue == 0 && 309014843421SMatthew Ahrens (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { 309114843421SMatthew Ahrens (void) strlcpy(propbuf, "none", proplen); 309214843421SMatthew Ahrens } else { 3093edea4b55SLin Ling zfs_nicenum(propvalue, propbuf, proplen); 309414843421SMatthew Ahrens } 309514843421SMatthew Ahrens return (0); 309614843421SMatthew Ahrens } 309714843421SMatthew Ahrens 309819b94df9SMatthew Ahrens int 309919b94df9SMatthew Ahrens zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname, 310019b94df9SMatthew Ahrens uint64_t *propvalue) 31018ac09fceSRichard Lowe { 310219b94df9SMatthew Ahrens int err; 310319b94df9SMatthew Ahrens zfs_cmd_t zc = { 0 }; 310419b94df9SMatthew Ahrens const char *snapname; 3105ebedde84SEric Taylor 310619b94df9SMatthew Ahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3107e5351341SMatthew Ahrens 310819b94df9SMatthew Ahrens snapname = strchr(propname, '@') + 1; 310919b94df9SMatthew Ahrens if (strchr(snapname, '@')) { 311019b94df9SMatthew Ahrens (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 311119b94df9SMatthew Ahrens } else { 311219b94df9SMatthew Ahrens /* snapname is the short name, append it to zhp's fsname */ 311319b94df9SMatthew Ahrens char *cp; 3114e5351341SMatthew Ahrens 311519b94df9SMatthew Ahrens (void) strlcpy(zc.zc_value, zhp->zfs_name, 311619b94df9SMatthew Ahrens sizeof (zc.zc_value)); 311719b94df9SMatthew Ahrens cp = strchr(zc.zc_value, '@'); 311819b94df9SMatthew Ahrens if (cp != NULL) 311919b94df9SMatthew Ahrens *cp = '\0'; 312019b94df9SMatthew Ahrens (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value)); 312119b94df9SMatthew Ahrens (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value)); 31228ac09fceSRichard Lowe } 312319b94df9SMatthew Ahrens 312419b94df9SMatthew Ahrens err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc); 312519b94df9SMatthew Ahrens if (err) 312619b94df9SMatthew Ahrens return (err); 312719b94df9SMatthew Ahrens 312819b94df9SMatthew Ahrens *propvalue = zc.zc_cookie; 312919b94df9SMatthew Ahrens return (0); 3130ebedde84SEric Taylor } 3131ebedde84SEric Taylor 3132fa9e4066Sahrens int 313319b94df9SMatthew Ahrens zfs_prop_get_written(zfs_handle_t *zhp, const char *propname, 313419b94df9SMatthew Ahrens char *propbuf, int proplen, boolean_t literal) 3135fa9e4066Sahrens { 313619b94df9SMatthew Ahrens int err; 313719b94df9SMatthew Ahrens uint64_t propvalue; 31383cb34c60Sahrens 313919b94df9SMatthew Ahrens err = zfs_prop_get_written_int(zhp, propname, &propvalue); 3140ebedde84SEric Taylor 314119b94df9SMatthew Ahrens if (err) 314219b94df9SMatthew Ahrens return (err); 31438ac09fceSRichard Lowe 314419b94df9SMatthew Ahrens if (literal) { 314519b94df9SMatthew Ahrens (void) snprintf(propbuf, proplen, "%llu", propvalue); 314619b94df9SMatthew Ahrens } else { 314719b94df9SMatthew Ahrens zfs_nicenum(propvalue, propbuf, proplen); 3148fa9e4066Sahrens } 314919b94df9SMatthew Ahrens return (0); 31507f7322feSeschrock } 31517f7322feSeschrock 31527f7322feSeschrock /* 315319b94df9SMatthew Ahrens * Returns the name of the given zfs handle. 31547f7322feSeschrock */ 315519b94df9SMatthew Ahrens const char * 315619b94df9SMatthew Ahrens zfs_get_name(const zfs_handle_t *zhp) 31577f7322feSeschrock { 315819b94df9SMatthew Ahrens return (zhp->zfs_name); 315919b94df9SMatthew Ahrens } 31608ac09fceSRichard Lowe 31618808ac5dSYuri Pankov /* 31628808ac5dSYuri Pankov * Returns the name of the parent pool for the given zfs handle. 31638808ac5dSYuri Pankov */ 31648808ac5dSYuri Pankov const char * 31658808ac5dSYuri Pankov zfs_get_pool_name(const zfs_handle_t *zhp) 31668808ac5dSYuri Pankov { 31678808ac5dSYuri Pankov return (zhp->zpool_hdl->zpool_name); 31688808ac5dSYuri Pankov } 31698808ac5dSYuri Pankov 317019b94df9SMatthew Ahrens /* 317119b94df9SMatthew Ahrens * Returns the type of the given zfs handle. 317219b94df9SMatthew Ahrens */ 317319b94df9SMatthew Ahrens zfs_type_t 317419b94df9SMatthew Ahrens zfs_get_type(const zfs_handle_t *zhp) 317519b94df9SMatthew Ahrens { 317619b94df9SMatthew Ahrens return (zhp->zfs_type); 31777f7322feSeschrock } 31787f7322feSeschrock 3179d41c4376SMark J Musante /* 3180d41c4376SMark J Musante * Is one dataset name a child dataset of another? 3181d41c4376SMark J Musante * 3182d41c4376SMark J Musante * Needs to handle these cases: 3183d41c4376SMark J Musante * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo" 3184d41c4376SMark J Musante * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar" 3185d41c4376SMark J Musante * Descendant? No. No. No. Yes. 3186d41c4376SMark J Musante */ 3187d41c4376SMark J Musante static boolean_t 3188d41c4376SMark J Musante is_descendant(const char *ds1, const char *ds2) 3189d41c4376SMark J Musante { 3190d41c4376SMark J Musante size_t d1len = strlen(ds1); 3191d41c4376SMark J Musante 3192d41c4376SMark J Musante /* ds2 can't be a descendant if it's smaller */ 3193d41c4376SMark J Musante if (strlen(ds2) < d1len) 3194d41c4376SMark J Musante return (B_FALSE); 3195d41c4376SMark J Musante 3196d41c4376SMark J Musante /* otherwise, compare strings and verify that there's a '/' char */ 3197d41c4376SMark J Musante return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0)); 3198d41c4376SMark J Musante } 3199d41c4376SMark J Musante 3200fa9e4066Sahrens /* 3201fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 320219b94df9SMatthew Ahrens * Will return -1 if there is no parent (path is just the name of the 320319b94df9SMatthew Ahrens * pool). 3204fa9e4066Sahrens */ 3205fa9e4066Sahrens static int 3206fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 3207fa9e4066Sahrens { 320819b94df9SMatthew Ahrens char *slashp; 3209fa9e4066Sahrens 321019b94df9SMatthew Ahrens (void) strlcpy(buf, path, buflen); 3211fa9e4066Sahrens 321219b94df9SMatthew Ahrens if ((slashp = strrchr(buf, '/')) == NULL) 321319b94df9SMatthew Ahrens return (-1); 321419b94df9SMatthew Ahrens *slashp = '\0'; 3215fa9e4066Sahrens 3216fa9e4066Sahrens return (0); 3217fa9e4066Sahrens } 3218fa9e4066Sahrens 3219fa9e4066Sahrens /* 32207f1f55eaSvb * If accept_ancestor is false, then check to make sure that the given path has 32217f1f55eaSvb * a parent, and that it exists. If accept_ancestor is true, then find the 32227f1f55eaSvb * closest existing ancestor for the given path. In prefixlen return the 32237f1f55eaSvb * length of already existing prefix of the given path. We also fetch the 32247f1f55eaSvb * 'zoned' property, which is used to validate property settings when creating 32257f1f55eaSvb * new datasets. 3226fa9e4066Sahrens */ 3227fa9e4066Sahrens static int 32287f1f55eaSvb check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 32297f1f55eaSvb boolean_t accept_ancestor, int *prefixlen) 3230fa9e4066Sahrens { 3231fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 32329adfa60dSMatthew Ahrens char parent[ZFS_MAX_DATASET_NAME_LEN]; 3233fa9e4066Sahrens char *slash; 32347f7322feSeschrock zfs_handle_t *zhp; 323599653d4eSeschrock char errbuf[1024]; 3236d41c4376SMark J Musante uint64_t is_zoned; 323799653d4eSeschrock 3238deb8317bSMark J Musante (void) snprintf(errbuf, sizeof (errbuf), 3239deb8317bSMark J Musante dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); 3240fa9e4066Sahrens 3241fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 3242fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 324399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 324499653d4eSeschrock "missing dataset name")); 324599653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3246fa9e4066Sahrens } 3247fa9e4066Sahrens 3248fa9e4066Sahrens /* check to see if the pool exists */ 3249fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 3250fa9e4066Sahrens slash = parent + strlen(parent); 32518ac09fceSRichard Lowe (void) strncpy(zc.zc_name, parent, slash - parent); 3252fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 325399653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 3254fa9e4066Sahrens errno == ENOENT) { 325599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 325699653d4eSeschrock "no such pool '%s'"), zc.zc_name); 325799653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3258fa9e4066Sahrens } 3259fa9e4066Sahrens 3260fa9e4066Sahrens /* check to see if the parent dataset exists */ 32617f1f55eaSvb while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 32627f1f55eaSvb if (errno == ENOENT && accept_ancestor) { 32637f1f55eaSvb /* 32647f1f55eaSvb * Go deeper to find an ancestor, give up on top level. 32657f1f55eaSvb */ 32667f1f55eaSvb if (parent_name(parent, parent, sizeof (parent)) != 0) { 32677f1f55eaSvb zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 32687f1f55eaSvb "no such pool '%s'"), zc.zc_name); 32697f1f55eaSvb return (zfs_error(hdl, EZFS_NOENT, errbuf)); 32707f1f55eaSvb } 32717f1f55eaSvb } else if (errno == ENOENT) { 327299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 327399653d4eSeschrock "parent does not exist")); 327499653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 32757f1f55eaSvb } else 327699653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 3277fa9e4066Sahrens } 3278fa9e4066Sahrens 3279d41c4376SMark J Musante is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 3280d41c4376SMark J Musante if (zoned != NULL) 3281d41c4376SMark J Musante *zoned = is_zoned; 3282d41c4376SMark J Musante 3283fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 3284d41c4376SMark J Musante if (getzoneid() != GLOBAL_ZONEID && !is_zoned) { 328599653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 32867f7322feSeschrock zfs_close(zhp); 3287fa9e4066Sahrens return (-1); 3288fa9e4066Sahrens } 3289fa9e4066Sahrens 3290fa9e4066Sahrens /* make sure parent is a filesystem */ 32917f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 329299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 329399653d4eSeschrock "parent is not a filesystem")); 329499653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 32957f7322feSeschrock zfs_close(zhp); 3296fa9e4066Sahrens return (-1); 3297fa9e4066Sahrens } 3298fa9e4066Sahrens 32997f7322feSeschrock zfs_close(zhp); 33007f1f55eaSvb if (prefixlen != NULL) 33017f1f55eaSvb *prefixlen = strlen(parent); 33027f1f55eaSvb return (0); 33037f1f55eaSvb } 33047f1f55eaSvb 33057f1f55eaSvb /* 33067f1f55eaSvb * Finds whether the dataset of the given type(s) exists. 33077f1f55eaSvb */ 33087f1f55eaSvb boolean_t 33097f1f55eaSvb zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 33107f1f55eaSvb { 33117f1f55eaSvb zfs_handle_t *zhp; 33127f1f55eaSvb 3313f18faf3fSek if (!zfs_validate_name(hdl, path, types, B_FALSE)) 33147f1f55eaSvb return (B_FALSE); 33157f1f55eaSvb 33167f1f55eaSvb /* 33177f1f55eaSvb * Try to get stats for the dataset, which will tell us if it exists. 33187f1f55eaSvb */ 33197f1f55eaSvb if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 33207f1f55eaSvb int ds_type = zhp->zfs_type; 33217f1f55eaSvb 33227f1f55eaSvb zfs_close(zhp); 33237f1f55eaSvb if (types & ds_type) 33247f1f55eaSvb return (B_TRUE); 33257f1f55eaSvb } 33267f1f55eaSvb return (B_FALSE); 33277f1f55eaSvb } 33287f1f55eaSvb 33293cb34c60Sahrens /* 33303cb34c60Sahrens * Given a path to 'target', create all the ancestors between 33313cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 33323cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 33333cb34c60Sahrens */ 33343cb34c60Sahrens int 33353cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 33363cb34c60Sahrens { 33373cb34c60Sahrens zfs_handle_t *h; 33383cb34c60Sahrens char *cp; 33393cb34c60Sahrens const char *opname; 33403cb34c60Sahrens 33413cb34c60Sahrens /* make sure prefix exists */ 33423cb34c60Sahrens cp = target + prefixlen; 33433cb34c60Sahrens if (*cp != '/') { 33443cb34c60Sahrens assert(strchr(cp, '/') == NULL); 33453cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 33463cb34c60Sahrens } else { 33473cb34c60Sahrens *cp = '\0'; 33483cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 33493cb34c60Sahrens *cp = '/'; 33503cb34c60Sahrens } 33513cb34c60Sahrens if (h == NULL) 33523cb34c60Sahrens return (-1); 33533cb34c60Sahrens zfs_close(h); 33543cb34c60Sahrens 33553cb34c60Sahrens /* 33563cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 33573cb34c60Sahrens * up to the prefixlen-long one. 33583cb34c60Sahrens */ 33593cb34c60Sahrens for (cp = target + prefixlen + 1; 336088f61deeSIgor Kozhukhov (cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) { 33613cb34c60Sahrens 33623cb34c60Sahrens *cp = '\0'; 33633cb34c60Sahrens 33643cb34c60Sahrens h = make_dataset_handle(hdl, target); 33653cb34c60Sahrens if (h) { 33663cb34c60Sahrens /* it already exists, nothing to do here */ 33673cb34c60Sahrens zfs_close(h); 33683cb34c60Sahrens continue; 33693cb34c60Sahrens } 33703cb34c60Sahrens 33713cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 33723cb34c60Sahrens NULL) != 0) { 33733cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 33743cb34c60Sahrens goto ancestorerr; 33753cb34c60Sahrens } 33763cb34c60Sahrens 33773cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 33783cb34c60Sahrens if (h == NULL) { 33793cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 33803cb34c60Sahrens goto ancestorerr; 33813cb34c60Sahrens } 33823cb34c60Sahrens 33833cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 33843cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 33853cb34c60Sahrens goto ancestorerr; 33863cb34c60Sahrens } 33873cb34c60Sahrens 33883cb34c60Sahrens if (zfs_share(h) != 0) { 33893cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 33903cb34c60Sahrens goto ancestorerr; 33913cb34c60Sahrens } 33923cb34c60Sahrens 33933cb34c60Sahrens zfs_close(h); 33943cb34c60Sahrens } 33953cb34c60Sahrens 33963cb34c60Sahrens return (0); 33973cb34c60Sahrens 33983cb34c60Sahrens ancestorerr: 33993cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34003cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 34013cb34c60Sahrens return (-1); 34023cb34c60Sahrens } 34033cb34c60Sahrens 34047f1f55eaSvb /* 34057f1f55eaSvb * Creates non-existing ancestors of the given path. 34067f1f55eaSvb */ 34077f1f55eaSvb int 34087f1f55eaSvb zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 34097f1f55eaSvb { 34107f1f55eaSvb int prefix; 34117f1f55eaSvb char *path_copy; 3412f83b46baSPaul Dagnelie int rc = 0; 34137f1f55eaSvb 3414d41c4376SMark J Musante if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0) 34157f1f55eaSvb return (-1); 34167f1f55eaSvb 34177f1f55eaSvb if ((path_copy = strdup(path)) != NULL) { 34187f1f55eaSvb rc = create_parents(hdl, path_copy, prefix); 34197f1f55eaSvb free(path_copy); 34207f1f55eaSvb } 34217f1f55eaSvb if (path_copy == NULL || rc != 0) 34227f1f55eaSvb return (-1); 34237f1f55eaSvb 3424fa9e4066Sahrens return (0); 3425fa9e4066Sahrens } 3426fa9e4066Sahrens 3427fa9e4066Sahrens /* 3428e9dbad6fSeschrock * Create a new filesystem or volume. 3429fa9e4066Sahrens */ 3430fa9e4066Sahrens int 343199653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 3432e9dbad6fSeschrock nvlist_t *props) 3433fa9e4066Sahrens { 3434fa9e4066Sahrens int ret; 3435fa9e4066Sahrens uint64_t size = 0; 3436fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 343799653d4eSeschrock char errbuf[1024]; 3438e9dbad6fSeschrock uint64_t zoned; 343926455f9eSAndriy Gapon enum lzc_dataset_type ost; 3440690031d3Sloli zpool_handle_t *zpool_handle; 344199653d4eSeschrock 344299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 344399653d4eSeschrock "cannot create '%s'"), path); 3444fa9e4066Sahrens 3445fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 3446f18faf3fSek if (!zfs_validate_name(hdl, path, type, B_TRUE)) 344799653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3448fa9e4066Sahrens 3449fa9e4066Sahrens /* validate parents exist */ 34507f1f55eaSvb if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 3451fa9e4066Sahrens return (-1); 3452fa9e4066Sahrens 3453fa9e4066Sahrens /* 3454fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 3455fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 3456fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 3457fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 3458fa9e4066Sahrens * first try to see if the dataset exists. 3459fa9e4066Sahrens */ 34604445fffbSMatthew Ahrens if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) { 346199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 346299653d4eSeschrock "dataset already exists")); 346399653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3464fa9e4066Sahrens } 3465fa9e4066Sahrens 3466fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 346726455f9eSAndriy Gapon ost = LZC_DATSET_TYPE_ZVOL; 3468fa9e4066Sahrens else 346926455f9eSAndriy Gapon ost = LZC_DATSET_TYPE_ZFS; 3470fa9e4066Sahrens 3471e9316f76SJoe Stein /* open zpool handle for prop validation */ 34729adfa60dSMatthew Ahrens char pool_path[ZFS_MAX_DATASET_NAME_LEN]; 3473e9316f76SJoe Stein (void) strlcpy(pool_path, path, sizeof (pool_path)); 3474e9316f76SJoe Stein 3475e9316f76SJoe Stein /* truncate pool_path at first slash */ 3476e9316f76SJoe Stein char *p = strchr(pool_path, '/'); 3477e9316f76SJoe Stein if (p != NULL) 3478e9316f76SJoe Stein *p = '\0'; 3479e9316f76SJoe Stein 3480690031d3Sloli if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL) 3481690031d3Sloli return (-1); 3482e9316f76SJoe Stein 34830a48a24eStimh if (props && (props = zfs_valid_proplist(hdl, type, props, 3484e9316f76SJoe Stein zoned, NULL, zpool_handle, errbuf)) == 0) { 3485e9316f76SJoe Stein zpool_close(zpool_handle); 3486e9dbad6fSeschrock return (-1); 3487e9316f76SJoe Stein } 3488e9316f76SJoe Stein zpool_close(zpool_handle); 3489e9dbad6fSeschrock 3490fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 34915c5460e9Seschrock /* 34925c5460e9Seschrock * If we are creating a volume, the size and block size must 34935c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 34945c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 34955c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 34965c5460e9Seschrock * zero. 34975c5460e9Seschrock */ 3498e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 3499e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 3500e9dbad6fSeschrock nvlist_free(props); 350199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3502e9dbad6fSeschrock "missing volume size")); 3503e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3504e9dbad6fSeschrock } 3505e9dbad6fSeschrock 3506e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 3507e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 3508e9dbad6fSeschrock &blocksize)) != 0) { 3509e9dbad6fSeschrock if (ret == ENOENT) { 3510e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 3511e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 3512e9dbad6fSeschrock } else { 3513e9dbad6fSeschrock nvlist_free(props); 3514e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3515e9dbad6fSeschrock "missing volume block size")); 3516e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3517e9dbad6fSeschrock } 3518fa9e4066Sahrens } 3519fa9e4066Sahrens 3520e9dbad6fSeschrock if (size == 0) { 3521e9dbad6fSeschrock nvlist_free(props); 352299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3523e9dbad6fSeschrock "volume size cannot be zero")); 3524e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 35255c5460e9Seschrock } 35265c5460e9Seschrock 35275c5460e9Seschrock if (size % blocksize != 0) { 3528e9dbad6fSeschrock nvlist_free(props); 352999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3530e9dbad6fSeschrock "volume size must be a multiple of volume block " 3531e9dbad6fSeschrock "size")); 3532e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 35335c5460e9Seschrock } 3534fa9e4066Sahrens } 3535fa9e4066Sahrens 3536fa9e4066Sahrens /* create the dataset */ 35374445fffbSMatthew Ahrens ret = lzc_create(path, ost, props); 35384445fffbSMatthew Ahrens nvlist_free(props); 3539e9dbad6fSeschrock 3540fa9e4066Sahrens /* check for failure */ 3541fa9e4066Sahrens if (ret != 0) { 35429adfa60dSMatthew Ahrens char parent[ZFS_MAX_DATASET_NAME_LEN]; 3543fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 3544fa9e4066Sahrens 3545fa9e4066Sahrens switch (errno) { 3546fa9e4066Sahrens case ENOENT: 354799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 354899653d4eSeschrock "no such parent '%s'"), parent); 354999653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3550fa9e4066Sahrens 3551fa9e4066Sahrens case EINVAL: 355299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3553d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 355499653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3555fa9e4066Sahrens 355640feaa91Sahrens case ENOTSUP: 355740feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 355840feaa91Sahrens "pool must be upgraded to set this " 355940feaa91Sahrens "property or value")); 356040feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 35619fa2266dSYuri Pankov case ERANGE: 35629fa2266dSYuri Pankov zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 35639fa2266dSYuri Pankov "invalid property value(s) specified")); 35649fa2266dSYuri Pankov return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 3565fa9e4066Sahrens #ifdef _ILP32 3566fa9e4066Sahrens case EOVERFLOW: 3567fa9e4066Sahrens /* 3568fa9e4066Sahrens * This platform can't address a volume this big. 3569fa9e4066Sahrens */ 357099653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 357199653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 357299653d4eSeschrock errbuf)); 3573fa9e4066Sahrens #endif 357499653d4eSeschrock /* FALLTHROUGH */ 3575fa9e4066Sahrens default: 357699653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 3577fa9e4066Sahrens } 3578fa9e4066Sahrens } 3579fa9e4066Sahrens 3580fa9e4066Sahrens return (0); 3581fa9e4066Sahrens } 3582fa9e4066Sahrens 3583fa9e4066Sahrens /* 3584fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 358565fec9f6SChristopher Siden * isn't mounted, and that there are no active dependents. If the file system 358665fec9f6SChristopher Siden * does not exist this function does nothing. 3587fa9e4066Sahrens */ 3588fa9e4066Sahrens int 3589842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer) 3590fa9e4066Sahrens { 3591fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3592fa9e4066Sahrens 359378f17100SMatthew Ahrens if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) { 359478f17100SMatthew Ahrens nvlist_t *nv = fnvlist_alloc(); 359578f17100SMatthew Ahrens fnvlist_add_boolean(nv, zhp->zfs_name); 359678f17100SMatthew Ahrens int error = lzc_destroy_bookmarks(nv, NULL); 359778f17100SMatthew Ahrens fnvlist_free(nv); 359878f17100SMatthew Ahrens if (error != 0) { 359978f17100SMatthew Ahrens return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 360078f17100SMatthew Ahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 360178f17100SMatthew Ahrens zhp->zfs_name)); 360278f17100SMatthew Ahrens } 360378f17100SMatthew Ahrens return (0); 360478f17100SMatthew Ahrens } 360578f17100SMatthew Ahrens 3606fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3607fa9e4066Sahrens 3608e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 3609fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3610fa9e4066Sahrens } else { 3611fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3612fa9e4066Sahrens } 3613fa9e4066Sahrens 3614842727c2SChris Kirby zc.zc_defer_destroy = defer; 361565fec9f6SChristopher Siden if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 && 361665fec9f6SChristopher Siden errno != ENOENT) { 3617ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 361899653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 361999653d4eSeschrock zhp->zfs_name)); 36201d452cf5Sahrens } 3621fa9e4066Sahrens 3622fa9e4066Sahrens remove_mountpoint(zhp); 3623fa9e4066Sahrens 3624fa9e4066Sahrens return (0); 3625fa9e4066Sahrens } 3626fa9e4066Sahrens 36271d452cf5Sahrens struct destroydata { 362819b94df9SMatthew Ahrens nvlist_t *nvl; 362919b94df9SMatthew Ahrens const char *snapname; 36301d452cf5Sahrens }; 36311d452cf5Sahrens 36321d452cf5Sahrens static int 3633681d9761SEric Taylor zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) 36341d452cf5Sahrens { 36351d452cf5Sahrens struct destroydata *dd = arg; 36369adfa60dSMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 3637681d9761SEric Taylor int rv = 0; 36381d452cf5Sahrens 363919b94df9SMatthew Ahrens (void) snprintf(name, sizeof (name), 364019b94df9SMatthew Ahrens "%s@%s", zhp->zfs_name, dd->snapname); 36411d452cf5Sahrens 3642a7a845e4SSteven Hartland if (lzc_exists(name)) 364319b94df9SMatthew Ahrens verify(nvlist_add_boolean(dd->nvl, name) == 0); 36441d452cf5Sahrens 364519b94df9SMatthew Ahrens rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd); 364619b94df9SMatthew Ahrens zfs_close(zhp); 36473ccfa83cSahrens return (rv); 36481d452cf5Sahrens } 36491d452cf5Sahrens 36501d452cf5Sahrens /* 36511d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 36521d452cf5Sahrens */ 36531d452cf5Sahrens int 3654842727c2SChris Kirby zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer) 36551d452cf5Sahrens { 36561d452cf5Sahrens int ret; 36571d452cf5Sahrens struct destroydata dd = { 0 }; 36581d452cf5Sahrens 36591d452cf5Sahrens dd.snapname = snapname; 366019b94df9SMatthew Ahrens verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0); 366119b94df9SMatthew Ahrens (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd); 36621d452cf5Sahrens 3663a7a845e4SSteven Hartland if (nvlist_empty(dd.nvl)) { 366419b94df9SMatthew Ahrens ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 36651d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 366619b94df9SMatthew Ahrens zhp->zfs_name, snapname); 366719b94df9SMatthew Ahrens } else { 36683b2aab18SMatthew Ahrens ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer); 36691d452cf5Sahrens } 367019b94df9SMatthew Ahrens nvlist_free(dd.nvl); 367119b94df9SMatthew Ahrens return (ret); 367219b94df9SMatthew Ahrens } 367319b94df9SMatthew Ahrens 367419b94df9SMatthew Ahrens /* 36753b2aab18SMatthew Ahrens * Destroys all the snapshots named in the nvlist. 367619b94df9SMatthew Ahrens */ 367719b94df9SMatthew Ahrens int 36783b2aab18SMatthew Ahrens zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer) 367919b94df9SMatthew Ahrens { 368019b94df9SMatthew Ahrens int ret; 36814cde22c2SChris Williamson nvlist_t *errlist = NULL; 36821d452cf5Sahrens 36834445fffbSMatthew Ahrens ret = lzc_destroy_snaps(snaps, defer, &errlist); 36841d452cf5Sahrens 36854cde22c2SChris Williamson if (ret == 0) { 36864cde22c2SChris Williamson nvlist_free(errlist); 36873b2aab18SMatthew Ahrens return (0); 36884cde22c2SChris Williamson } 36894445fffbSMatthew Ahrens 3690a7a845e4SSteven Hartland if (nvlist_empty(errlist)) { 36913b2aab18SMatthew Ahrens char errbuf[1024]; 36923b2aab18SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 36933b2aab18SMatthew Ahrens dgettext(TEXT_DOMAIN, "cannot destroy snapshots")); 36943b2aab18SMatthew Ahrens 36953b2aab18SMatthew Ahrens ret = zfs_standard_error(hdl, ret, errbuf); 36963b2aab18SMatthew Ahrens } 36973b2aab18SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL); 36983b2aab18SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) { 36993b2aab18SMatthew Ahrens char errbuf[1024]; 37003b2aab18SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 37013b2aab18SMatthew Ahrens dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"), 37023b2aab18SMatthew Ahrens nvpair_name(pair)); 37033b2aab18SMatthew Ahrens 37043b2aab18SMatthew Ahrens switch (fnvpair_value_int32(pair)) { 37053b2aab18SMatthew Ahrens case EEXIST: 37063b2aab18SMatthew Ahrens zfs_error_aux(hdl, 37073b2aab18SMatthew Ahrens dgettext(TEXT_DOMAIN, "snapshot is cloned")); 37083b2aab18SMatthew Ahrens ret = zfs_error(hdl, EZFS_EXISTS, errbuf); 37093b2aab18SMatthew Ahrens break; 37103b2aab18SMatthew Ahrens default: 37113b2aab18SMatthew Ahrens ret = zfs_standard_error(hdl, errno, errbuf); 37123b2aab18SMatthew Ahrens break; 37131d452cf5Sahrens } 37141d452cf5Sahrens } 37151d452cf5Sahrens 37164cde22c2SChris Williamson nvlist_free(errlist); 37174445fffbSMatthew Ahrens return (ret); 37181d452cf5Sahrens } 37191d452cf5Sahrens 3720fa9e4066Sahrens /* 3721fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 3722fa9e4066Sahrens */ 3723fa9e4066Sahrens int 3724e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3725fa9e4066Sahrens { 37269adfa60dSMatthew Ahrens char parent[ZFS_MAX_DATASET_NAME_LEN]; 3727fa9e4066Sahrens int ret; 372899653d4eSeschrock char errbuf[1024]; 372999653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3730e9dbad6fSeschrock uint64_t zoned; 3731fa9e4066Sahrens 3732fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3733fa9e4066Sahrens 373499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 373599653d4eSeschrock "cannot create '%s'"), target); 373699653d4eSeschrock 373719b94df9SMatthew Ahrens /* validate the target/clone name */ 3738f18faf3fSek if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 373999653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3740fa9e4066Sahrens 3741fa9e4066Sahrens /* validate parents exist */ 37427f1f55eaSvb if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3743fa9e4066Sahrens return (-1); 3744fa9e4066Sahrens 3745fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3746fa9e4066Sahrens 3747fa9e4066Sahrens /* do the clone */ 3748e9dbad6fSeschrock 3749e9dbad6fSeschrock if (props) { 37504445fffbSMatthew Ahrens zfs_type_t type; 37511c10ae76SMike Gerdts 37524445fffbSMatthew Ahrens if (ZFS_IS_VOLUME(zhp)) { 37534445fffbSMatthew Ahrens type = ZFS_TYPE_VOLUME; 37544445fffbSMatthew Ahrens } else { 37554445fffbSMatthew Ahrens type = ZFS_TYPE_FILESYSTEM; 37564445fffbSMatthew Ahrens } 37570a48a24eStimh if ((props = zfs_valid_proplist(hdl, type, props, zoned, 3758e9316f76SJoe Stein zhp, zhp->zpool_hdl, errbuf)) == NULL) 3759e9dbad6fSeschrock return (-1); 37601c10ae76SMike Gerdts if (zfs_fix_auto_resv(zhp, props) == -1) { 37611c10ae76SMike Gerdts nvlist_free(props); 37621c10ae76SMike Gerdts return (-1); 37631c10ae76SMike Gerdts } 3764e9dbad6fSeschrock } 3765fa9e4066Sahrens 37664445fffbSMatthew Ahrens ret = lzc_clone(target, zhp->zfs_name, props); 37674445fffbSMatthew Ahrens nvlist_free(props); 3768e9dbad6fSeschrock 3769fa9e4066Sahrens if (ret != 0) { 3770fa9e4066Sahrens switch (errno) { 3771fa9e4066Sahrens 3772fa9e4066Sahrens case ENOENT: 3773fa9e4066Sahrens /* 3774fa9e4066Sahrens * The parent doesn't exist. We should have caught this 3775fa9e4066Sahrens * above, but there may a race condition that has since 3776fa9e4066Sahrens * destroyed the parent. 3777fa9e4066Sahrens * 3778fa9e4066Sahrens * At this point, we don't know whether it's the source 3779fa9e4066Sahrens * that doesn't exist anymore, or whether the target 3780fa9e4066Sahrens * dataset doesn't exist. 3781fa9e4066Sahrens */ 378299653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 378399653d4eSeschrock "no such parent '%s'"), parent); 378499653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3785fa9e4066Sahrens 378699653d4eSeschrock case EXDEV: 378799653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 378899653d4eSeschrock "source and target pools differ")); 378999653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 379099653d4eSeschrock errbuf)); 3791fa9e4066Sahrens 379299653d4eSeschrock default: 379399653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 379499653d4eSeschrock errbuf)); 379599653d4eSeschrock } 379699653d4eSeschrock } 3797fa9e4066Sahrens 379899653d4eSeschrock return (ret); 379999653d4eSeschrock } 380099653d4eSeschrock 380199653d4eSeschrock /* 380299653d4eSeschrock * Promotes the given clone fs to be the clone parent. 380399653d4eSeschrock */ 380499653d4eSeschrock int 380599653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 380699653d4eSeschrock { 380799653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3808a4b8c9aaSAndrew Stormont char snapname[ZFS_MAX_DATASET_NAME_LEN]; 380999653d4eSeschrock int ret; 381099653d4eSeschrock char errbuf[1024]; 381199653d4eSeschrock 381299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 381399653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 381499653d4eSeschrock 381599653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 381699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 381799653d4eSeschrock "snapshots can not be promoted")); 381899653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 381999653d4eSeschrock } 382099653d4eSeschrock 3821a4b8c9aaSAndrew Stormont if (zhp->zfs_dmustats.dds_origin[0] == '\0') { 382299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 382399653d4eSeschrock "not a cloned filesystem")); 382499653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 382599653d4eSeschrock } 382699653d4eSeschrock 3827add927f8Sloli if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE)) 3828add927f8Sloli return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3829add927f8Sloli 3830a4b8c9aaSAndrew Stormont ret = lzc_promote(zhp->zfs_name, snapname, sizeof (snapname)); 383199653d4eSeschrock 383299653d4eSeschrock if (ret != 0) { 3833a4b8c9aaSAndrew Stormont switch (ret) { 383499653d4eSeschrock case EEXIST: 3835681d9761SEric Taylor /* There is a conflicting snapshot name. */ 383699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3837681d9761SEric Taylor "conflicting snapshot '%s' from parent '%s'"), 3838a4b8c9aaSAndrew Stormont snapname, zhp->zfs_dmustats.dds_origin); 383999653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3840fa9e4066Sahrens 3841fa9e4066Sahrens default: 3842a4b8c9aaSAndrew Stormont return (zfs_standard_error(hdl, ret, errbuf)); 3843fa9e4066Sahrens } 3844fa9e4066Sahrens } 3845e9dbad6fSeschrock return (ret); 38461d452cf5Sahrens } 38471d452cf5Sahrens 38484445fffbSMatthew Ahrens typedef struct snapdata { 38494445fffbSMatthew Ahrens nvlist_t *sd_nvl; 38504445fffbSMatthew Ahrens const char *sd_snapname; 38514445fffbSMatthew Ahrens } snapdata_t; 38524445fffbSMatthew Ahrens 38534445fffbSMatthew Ahrens static int 38544445fffbSMatthew Ahrens zfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 38554445fffbSMatthew Ahrens { 38564445fffbSMatthew Ahrens snapdata_t *sd = arg; 38579adfa60dSMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 38584445fffbSMatthew Ahrens int rv = 0; 38594445fffbSMatthew Ahrens 3860ca48f36fSKeith M Wesolowski if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) { 3861ca48f36fSKeith M Wesolowski (void) snprintf(name, sizeof (name), 3862ca48f36fSKeith M Wesolowski "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 38634445fffbSMatthew Ahrens 3864ca48f36fSKeith M Wesolowski fnvlist_add_boolean(sd->sd_nvl, name); 38654445fffbSMatthew Ahrens 3866ca48f36fSKeith M Wesolowski rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 3867ca48f36fSKeith M Wesolowski } 38684445fffbSMatthew Ahrens zfs_close(zhp); 3869ca48f36fSKeith M Wesolowski 38704445fffbSMatthew Ahrens return (rv); 38714445fffbSMatthew Ahrens } 38724445fffbSMatthew Ahrens 38735cabbc6bSPrashanth Sreenivasa int 38745cabbc6bSPrashanth Sreenivasa zfs_remap_indirects(libzfs_handle_t *hdl, const char *fs) 38755cabbc6bSPrashanth Sreenivasa { 38765cabbc6bSPrashanth Sreenivasa int err; 38775cabbc6bSPrashanth Sreenivasa char errbuf[1024]; 38785cabbc6bSPrashanth Sreenivasa 38795cabbc6bSPrashanth Sreenivasa (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 38805cabbc6bSPrashanth Sreenivasa "cannot remap filesystem '%s' "), fs); 38815cabbc6bSPrashanth Sreenivasa 38825cabbc6bSPrashanth Sreenivasa err = lzc_remap(fs); 38835cabbc6bSPrashanth Sreenivasa 38845cabbc6bSPrashanth Sreenivasa if (err != 0) { 38855cabbc6bSPrashanth Sreenivasa (void) zfs_standard_error(hdl, err, errbuf); 38865cabbc6bSPrashanth Sreenivasa } 38875cabbc6bSPrashanth Sreenivasa 38885cabbc6bSPrashanth Sreenivasa return (err); 38895cabbc6bSPrashanth Sreenivasa } 38905cabbc6bSPrashanth Sreenivasa 3891fa9e4066Sahrens /* 38924445fffbSMatthew Ahrens * Creates snapshots. The keys in the snaps nvlist are the snapshots to be 38934445fffbSMatthew Ahrens * created. 3894fa9e4066Sahrens */ 3895fa9e4066Sahrens int 38964445fffbSMatthew Ahrens zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props) 3897fa9e4066Sahrens { 3898fa9e4066Sahrens int ret; 389999653d4eSeschrock char errbuf[1024]; 39004445fffbSMatthew Ahrens nvpair_t *elem; 39014445fffbSMatthew Ahrens nvlist_t *errors; 3902fa9e4066Sahrens 390399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 39044445fffbSMatthew Ahrens "cannot create snapshots ")); 390599653d4eSeschrock 39064445fffbSMatthew Ahrens elem = NULL; 39074445fffbSMatthew Ahrens while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) { 39084445fffbSMatthew Ahrens const char *snapname = nvpair_name(elem); 39094445fffbSMatthew Ahrens 39104445fffbSMatthew Ahrens /* validate the target name */ 39114445fffbSMatthew Ahrens if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT, 39124445fffbSMatthew Ahrens B_TRUE)) { 39134445fffbSMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 39144445fffbSMatthew Ahrens dgettext(TEXT_DOMAIN, 39154445fffbSMatthew Ahrens "cannot create snapshot '%s'"), snapname); 39164445fffbSMatthew Ahrens return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 39174445fffbSMatthew Ahrens } 39184445fffbSMatthew Ahrens } 3919fa9e4066Sahrens 3920e9316f76SJoe Stein /* 3921e9316f76SJoe Stein * get pool handle for prop validation. assumes all snaps are in the 3922e9316f76SJoe Stein * same pool, as does lzc_snapshot (below). 3923e9316f76SJoe Stein */ 39249adfa60dSMatthew Ahrens char pool[ZFS_MAX_DATASET_NAME_LEN]; 3925e9316f76SJoe Stein elem = nvlist_next_nvpair(snaps, NULL); 3926e9316f76SJoe Stein (void) strlcpy(pool, nvpair_name(elem), sizeof (pool)); 3927e9316f76SJoe Stein pool[strcspn(pool, "/@")] = '\0'; 3928e9316f76SJoe Stein zpool_handle_t *zpool_hdl = zpool_open(hdl, pool); 3929e9316f76SJoe Stein 39304445fffbSMatthew Ahrens if (props != NULL && 39314445fffbSMatthew Ahrens (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, 3932e9316f76SJoe Stein props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) { 3933e9316f76SJoe Stein zpool_close(zpool_hdl); 39344445fffbSMatthew Ahrens return (-1); 39354445fffbSMatthew Ahrens } 3936e9316f76SJoe Stein zpool_close(zpool_hdl); 3937bb0ade09Sahrens 39384445fffbSMatthew Ahrens ret = lzc_snapshot(snaps, props, &errors); 39394445fffbSMatthew Ahrens 39404445fffbSMatthew Ahrens if (ret != 0) { 39414445fffbSMatthew Ahrens boolean_t printed = B_FALSE; 39424445fffbSMatthew Ahrens for (elem = nvlist_next_nvpair(errors, NULL); 39434445fffbSMatthew Ahrens elem != NULL; 39444445fffbSMatthew Ahrens elem = nvlist_next_nvpair(errors, elem)) { 39454445fffbSMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 39464445fffbSMatthew Ahrens dgettext(TEXT_DOMAIN, 39474445fffbSMatthew Ahrens "cannot create snapshot '%s'"), nvpair_name(elem)); 39484445fffbSMatthew Ahrens (void) zfs_standard_error(hdl, 39494445fffbSMatthew Ahrens fnvpair_value_int32(elem), errbuf); 39504445fffbSMatthew Ahrens printed = B_TRUE; 3951bb0ade09Sahrens } 39524445fffbSMatthew Ahrens if (!printed) { 39534445fffbSMatthew Ahrens switch (ret) { 39544445fffbSMatthew Ahrens case EXDEV: 39554445fffbSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 39564445fffbSMatthew Ahrens "multiple snapshots of same " 39574445fffbSMatthew Ahrens "fs not allowed")); 39584445fffbSMatthew Ahrens (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3959bb0ade09Sahrens 39604445fffbSMatthew Ahrens break; 39614445fffbSMatthew Ahrens default: 39624445fffbSMatthew Ahrens (void) zfs_standard_error(hdl, ret, errbuf); 39634445fffbSMatthew Ahrens } 39644445fffbSMatthew Ahrens } 3965bb0ade09Sahrens } 3966bb0ade09Sahrens 39674445fffbSMatthew Ahrens nvlist_free(props); 39684445fffbSMatthew Ahrens nvlist_free(errors); 39694445fffbSMatthew Ahrens return (ret); 39704445fffbSMatthew Ahrens } 39714445fffbSMatthew Ahrens 39724445fffbSMatthew Ahrens int 39734445fffbSMatthew Ahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, 39744445fffbSMatthew Ahrens nvlist_t *props) 39754445fffbSMatthew Ahrens { 39764445fffbSMatthew Ahrens int ret; 39774445fffbSMatthew Ahrens snapdata_t sd = { 0 }; 39789adfa60dSMatthew Ahrens char fsname[ZFS_MAX_DATASET_NAME_LEN]; 39794445fffbSMatthew Ahrens char *cp; 39804445fffbSMatthew Ahrens zfs_handle_t *zhp; 39814445fffbSMatthew Ahrens char errbuf[1024]; 39824445fffbSMatthew Ahrens 39834445fffbSMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 39844445fffbSMatthew Ahrens "cannot snapshot %s"), path); 39854445fffbSMatthew Ahrens 39864445fffbSMatthew Ahrens if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 39874445fffbSMatthew Ahrens return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 39884445fffbSMatthew Ahrens 39894445fffbSMatthew Ahrens (void) strlcpy(fsname, path, sizeof (fsname)); 39904445fffbSMatthew Ahrens cp = strchr(fsname, '@'); 39914445fffbSMatthew Ahrens *cp = '\0'; 39924445fffbSMatthew Ahrens sd.sd_snapname = cp + 1; 3993fa9e4066Sahrens 39944445fffbSMatthew Ahrens if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | 3995fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3996fa9e4066Sahrens return (-1); 3997fa9e4066Sahrens } 3998fa9e4066Sahrens 39994445fffbSMatthew Ahrens verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0); 40004445fffbSMatthew Ahrens if (recursive) { 40014445fffbSMatthew Ahrens (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd); 40024445fffbSMatthew Ahrens } else { 40034445fffbSMatthew Ahrens fnvlist_add_boolean(sd.sd_nvl, path); 4004681d9761SEric Taylor } 4005fa9e4066Sahrens 40064445fffbSMatthew Ahrens ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props); 40074445fffbSMatthew Ahrens nvlist_free(sd.sd_nvl); 4008fa9e4066Sahrens zfs_close(zhp); 4009fa9e4066Sahrens return (ret); 4010fa9e4066Sahrens } 4011fa9e4066Sahrens 4012fa9e4066Sahrens /* 4013b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 4014b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 4015b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 4016b12a1c38Slling * group. 4017fa9e4066Sahrens */ 4018b12a1c38Slling typedef struct rollback_data { 4019b12a1c38Slling const char *cb_target; /* the snapshot */ 4020b12a1c38Slling uint64_t cb_create; /* creation time reference */ 4021c391e322Sahrens boolean_t cb_error; 4022c391e322Sahrens boolean_t cb_force; 4023b12a1c38Slling } rollback_data_t; 4024b12a1c38Slling 4025b12a1c38Slling static int 402678f17100SMatthew Ahrens rollback_destroy_dependent(zfs_handle_t *zhp, void *data) 4027b12a1c38Slling { 4028b12a1c38Slling rollback_data_t *cbp = data; 402978f17100SMatthew Ahrens prop_changelist_t *clp; 403078f17100SMatthew Ahrens 403178f17100SMatthew Ahrens /* We must destroy this clone; first unmount it */ 403278f17100SMatthew Ahrens clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 403378f17100SMatthew Ahrens cbp->cb_force ? MS_FORCE: 0); 403478f17100SMatthew Ahrens if (clp == NULL || changelist_prefix(clp) != 0) { 403578f17100SMatthew Ahrens cbp->cb_error = B_TRUE; 403678f17100SMatthew Ahrens zfs_close(zhp); 403778f17100SMatthew Ahrens return (0); 403878f17100SMatthew Ahrens } 403978f17100SMatthew Ahrens if (zfs_destroy(zhp, B_FALSE) != 0) 404078f17100SMatthew Ahrens cbp->cb_error = B_TRUE; 404178f17100SMatthew Ahrens else 404278f17100SMatthew Ahrens changelist_remove(clp, zhp->zfs_name); 404378f17100SMatthew Ahrens (void) changelist_postfix(clp); 404478f17100SMatthew Ahrens changelist_free(clp); 4045b12a1c38Slling 404678f17100SMatthew Ahrens zfs_close(zhp); 404778f17100SMatthew Ahrens return (0); 404878f17100SMatthew Ahrens } 4049b12a1c38Slling 405078f17100SMatthew Ahrens static int 405178f17100SMatthew Ahrens rollback_destroy(zfs_handle_t *zhp, void *data) 405278f17100SMatthew Ahrens { 405378f17100SMatthew Ahrens rollback_data_t *cbp = data; 4054b12a1c38Slling 405578f17100SMatthew Ahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { 405678f17100SMatthew Ahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 405778f17100SMatthew Ahrens rollback_destroy_dependent, cbp); 4058c391e322Sahrens 405978f17100SMatthew Ahrens cbp->cb_error |= zfs_destroy(zhp, B_FALSE); 4060b12a1c38Slling } 4061b12a1c38Slling 4062b12a1c38Slling zfs_close(zhp); 4063b12a1c38Slling return (0); 4064b12a1c38Slling } 4065b12a1c38Slling 4066b12a1c38Slling /* 4067b12a1c38Slling * Given a dataset, rollback to a specific snapshot, discarding any 4068b12a1c38Slling * data changes since then and making it the active dataset. 4069b12a1c38Slling * 407078f17100SMatthew Ahrens * Any snapshots and bookmarks more recent than the target are 407178f17100SMatthew Ahrens * destroyed, along with their dependents (i.e. clones). 4072b12a1c38Slling */ 4073b12a1c38Slling int 4074c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 4075b12a1c38Slling { 4076b12a1c38Slling rollback_data_t cb = { 0 }; 40774ccbb6e7Sahrens int err; 40787b97dc1aSrm boolean_t restore_resv = 0; 4079f83b46baSPaul Dagnelie uint64_t old_volsize = 0, new_volsize; 40807b97dc1aSrm zfs_prop_t resv_prop; 4081b12a1c38Slling 40824ccbb6e7Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 40834ccbb6e7Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 4084b12a1c38Slling 4085b12a1c38Slling /* 40862e2c1355SMatthew Ahrens * Destroy all recent snapshots and their dependents. 4087b12a1c38Slling */ 4088c391e322Sahrens cb.cb_force = force; 4089b12a1c38Slling cb.cb_target = snap->zfs_name; 4090b12a1c38Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 40910d8fa8f8SMartin Matuska (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb); 409278f17100SMatthew Ahrens (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb); 4093b12a1c38Slling 4094c391e322Sahrens if (cb.cb_error) 4095c391e322Sahrens return (-1); 4096b12a1c38Slling 4097b12a1c38Slling /* 4098b12a1c38Slling * Now that we have verified that the snapshot is the latest, 4099b12a1c38Slling * rollback to the given snapshot. 4100b12a1c38Slling */ 4101b12a1c38Slling 41027b97dc1aSrm if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 41037b97dc1aSrm if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 41047b97dc1aSrm return (-1); 41057b97dc1aSrm old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 41067b97dc1aSrm restore_resv = 41077b97dc1aSrm (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 41087b97dc1aSrm } 41094ccbb6e7Sahrens 4110b12a1c38Slling /* 411177b17137SAndriy Gapon * Pass both the filesystem and the wanted snapshot names, 411277b17137SAndriy Gapon * we would get an error back if the snapshot is destroyed or 411377b17137SAndriy Gapon * a new snapshot is created before this request is processed. 4114b12a1c38Slling */ 411577b17137SAndriy Gapon err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name); 411695643f75SAndriy Gapon if (err != 0) { 411795643f75SAndriy Gapon char errbuf[1024]; 411895643f75SAndriy Gapon 411995643f75SAndriy Gapon (void) snprintf(errbuf, sizeof (errbuf), 41204ccbb6e7Sahrens dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 41214ccbb6e7Sahrens zhp->zfs_name); 412295643f75SAndriy Gapon switch (err) { 412395643f75SAndriy Gapon case EEXIST: 412495643f75SAndriy Gapon zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 412595643f75SAndriy Gapon "there is a snapshot or bookmark more recent " 412695643f75SAndriy Gapon "than '%s'"), snap->zfs_name); 412795643f75SAndriy Gapon (void) zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf); 412895643f75SAndriy Gapon break; 412995643f75SAndriy Gapon case ESRCH: 413095643f75SAndriy Gapon zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 413195643f75SAndriy Gapon "'%s' is not found among snapshots of '%s'"), 413295643f75SAndriy Gapon snap->zfs_name, zhp->zfs_name); 413395643f75SAndriy Gapon (void) zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf); 413495643f75SAndriy Gapon break; 413595643f75SAndriy Gapon case EINVAL: 413695643f75SAndriy Gapon (void) zfs_error(zhp->zfs_hdl, EZFS_BADTYPE, errbuf); 413795643f75SAndriy Gapon break; 413895643f75SAndriy Gapon default: 413995643f75SAndriy Gapon (void) zfs_standard_error(zhp->zfs_hdl, err, errbuf); 414095643f75SAndriy Gapon } 4141b9415e83Srm return (err); 4142b9415e83Srm } 41437b97dc1aSrm 41447b97dc1aSrm /* 41457b97dc1aSrm * For volumes, if the pre-rollback volsize matched the pre- 41467b97dc1aSrm * rollback reservation and the volsize has changed then set 41477b97dc1aSrm * the reservation property to the post-rollback volsize. 41487b97dc1aSrm * Make a new handle since the rollback closed the dataset. 41497b97dc1aSrm */ 4150b9415e83Srm if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 4151b9415e83Srm (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 41527b97dc1aSrm if (restore_resv) { 41537b97dc1aSrm new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 41547b97dc1aSrm if (old_volsize != new_volsize) 4155b9415e83Srm err = zfs_prop_set_int(zhp, resv_prop, 4156b9415e83Srm new_volsize); 41577b97dc1aSrm } 41587b97dc1aSrm zfs_close(zhp); 41594ccbb6e7Sahrens } 41604ccbb6e7Sahrens return (err); 4161b12a1c38Slling } 4162b12a1c38Slling 4163fa9e4066Sahrens /* 4164fa9e4066Sahrens * Renames the given dataset. 4165fa9e4066Sahrens */ 4166fa9e4066Sahrens int 41676a9cb0eaSEric Schrock zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive, 41686a9cb0eaSEric Schrock boolean_t force_unmount) 4169fa9e4066Sahrens { 417088f61deeSIgor Kozhukhov int ret = 0; 4171fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 4172fa9e4066Sahrens char *delim; 4173cdf5b4caSmmusante prop_changelist_t *cl = NULL; 4174cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 4175cdf5b4caSmmusante char *parentname = NULL; 41769adfa60dSMatthew Ahrens char parent[ZFS_MAX_DATASET_NAME_LEN]; 417799653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 417899653d4eSeschrock char errbuf[1024]; 4179fa9e4066Sahrens 4180fa9e4066Sahrens /* if we have the same exact name, just return success */ 4181fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 4182fa9e4066Sahrens return (0); 4183fa9e4066Sahrens 418499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 418599653d4eSeschrock "cannot rename to '%s'"), target); 418699653d4eSeschrock 4187add927f8Sloli /* make sure source name is valid */ 4188add927f8Sloli if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE)) 4189add927f8Sloli return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4190add927f8Sloli 4191fa9e4066Sahrens /* 4192fa9e4066Sahrens * Make sure the target name is valid 4193fa9e4066Sahrens */ 4194fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 419598579b20Snd if ((strchr(target, '@') == NULL) || 419698579b20Snd *target == '@') { 419798579b20Snd /* 419898579b20Snd * Snapshot target name is abbreviated, 419998579b20Snd * reconstruct full dataset name 420098579b20Snd */ 420198579b20Snd (void) strlcpy(parent, zhp->zfs_name, 420298579b20Snd sizeof (parent)); 420398579b20Snd delim = strchr(parent, '@'); 420498579b20Snd if (strchr(target, '@') == NULL) 420598579b20Snd *(++delim) = '\0'; 420698579b20Snd else 420798579b20Snd *delim = '\0'; 420898579b20Snd (void) strlcat(parent, target, sizeof (parent)); 420998579b20Snd target = parent; 421098579b20Snd } else { 421198579b20Snd /* 421298579b20Snd * Make sure we're renaming within the same dataset. 421398579b20Snd */ 421498579b20Snd delim = strchr(target, '@'); 421598579b20Snd if (strncmp(zhp->zfs_name, target, delim - target) 421698579b20Snd != 0 || zhp->zfs_name[delim - target] != '@') { 421798579b20Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 421898579b20Snd "snapshots must be part of same " 421998579b20Snd "dataset")); 422098579b20Snd return (zfs_error(hdl, EZFS_CROSSTARGET, 4221b1b8ab34Slling errbuf)); 422298579b20Snd } 4223fa9e4066Sahrens } 4224f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 422598579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4226fa9e4066Sahrens } else { 4227cdf5b4caSmmusante if (recursive) { 4228cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4229cdf5b4caSmmusante "recursive rename must be a snapshot")); 4230cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 4231cdf5b4caSmmusante } 4232cdf5b4caSmmusante 4233f18faf3fSek if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 423498579b20Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4235e9dbad6fSeschrock 4236fa9e4066Sahrens /* validate parents */ 4237d41c4376SMark J Musante if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0) 4238fa9e4066Sahrens return (-1); 4239fa9e4066Sahrens 4240fa9e4066Sahrens /* make sure we're in the same pool */ 4241fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 4242fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 4243fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 424499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 424599653d4eSeschrock "datasets must be within same pool")); 424699653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 4247fa9e4066Sahrens } 4248f2fdf992Snd 4249f2fdf992Snd /* new name cannot be a child of the current dataset name */ 4250d41c4376SMark J Musante if (is_descendant(zhp->zfs_name, target)) { 4251f2fdf992Snd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4252d41c4376SMark J Musante "New dataset name cannot be a descendant of " 4253f2fdf992Snd "current dataset name")); 4254f2fdf992Snd return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 4255f2fdf992Snd } 4256fa9e4066Sahrens } 4257fa9e4066Sahrens 425899653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 425999653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 426099653d4eSeschrock 4261fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 4262fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 426399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 426499653d4eSeschrock "dataset is used in a non-global zone")); 426599653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 4266fa9e4066Sahrens } 4267fa9e4066Sahrens 4268cdf5b4caSmmusante if (recursive) { 4269f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 4270f0c5ee21Smmusante if (parentname == NULL) { 4271f0c5ee21Smmusante ret = -1; 4272f0c5ee21Smmusante goto error; 4273f0c5ee21Smmusante } 4274cdf5b4caSmmusante delim = strchr(parentname, '@'); 4275cdf5b4caSmmusante *delim = '\0'; 4276990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 4277cdf5b4caSmmusante if (zhrp == NULL) { 4278f0c5ee21Smmusante ret = -1; 4279f0c5ee21Smmusante goto error; 4280cdf5b4caSmmusante } 428133cde0d0SMatthew Ahrens } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) { 42826a9cb0eaSEric Schrock if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 42836a9cb0eaSEric Schrock force_unmount ? MS_FORCE : 0)) == NULL) 4284cdf5b4caSmmusante return (-1); 4285cdf5b4caSmmusante 4286cdf5b4caSmmusante if (changelist_haszonedchild(cl)) { 4287cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4288cdf5b4caSmmusante "child dataset with inherited mountpoint is used " 4289cdf5b4caSmmusante "in a non-global zone")); 4290cdf5b4caSmmusante (void) zfs_error(hdl, EZFS_ZONED, errbuf); 4291f83b46baSPaul Dagnelie ret = -1; 4292cdf5b4caSmmusante goto error; 4293cdf5b4caSmmusante } 4294cdf5b4caSmmusante 4295cdf5b4caSmmusante if ((ret = changelist_prefix(cl)) != 0) 4296cdf5b4caSmmusante goto error; 4297cdf5b4caSmmusante } 4298fa9e4066Sahrens 4299e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 4300fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 4301fa9e4066Sahrens else 4302fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 4303fa9e4066Sahrens 430498579b20Snd (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 4305e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 430698579b20Snd 4307cdf5b4caSmmusante zc.zc_cookie = recursive; 4308cdf5b4caSmmusante 4309ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 4310cdf5b4caSmmusante /* 4311cdf5b4caSmmusante * if it was recursive, the one that actually failed will 4312cdf5b4caSmmusante * be in zc.zc_name 4313cdf5b4caSmmusante */ 4314cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 43153cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 4316cdf5b4caSmmusante 4317cdf5b4caSmmusante if (recursive && errno == EEXIST) { 4318cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4319cdf5b4caSmmusante "a child dataset already has a snapshot " 4320cdf5b4caSmmusante "with the new name")); 4321a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 4322cdf5b4caSmmusante } else { 4323cdf5b4caSmmusante (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 4324cdf5b4caSmmusante } 4325fa9e4066Sahrens 4326fa9e4066Sahrens /* 4327fa9e4066Sahrens * On failure, we still want to remount any filesystems that 4328fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 4329fa9e4066Sahrens */ 433033cde0d0SMatthew Ahrens if (cl != NULL) 4331cdf5b4caSmmusante (void) changelist_postfix(cl); 4332fa9e4066Sahrens } else { 433333cde0d0SMatthew Ahrens if (cl != NULL) { 4334cdf5b4caSmmusante changelist_rename(cl, zfs_get_name(zhp), target); 4335cdf5b4caSmmusante ret = changelist_postfix(cl); 4336cdf5b4caSmmusante } 4337fa9e4066Sahrens } 4338fa9e4066Sahrens 4339fa9e4066Sahrens error: 434033cde0d0SMatthew Ahrens if (parentname != NULL) { 4341cdf5b4caSmmusante free(parentname); 4342cdf5b4caSmmusante } 434333cde0d0SMatthew Ahrens if (zhrp != NULL) { 4344cdf5b4caSmmusante zfs_close(zhrp); 4345cdf5b4caSmmusante } 434633cde0d0SMatthew Ahrens if (cl != NULL) { 4347cdf5b4caSmmusante changelist_free(cl); 4348cdf5b4caSmmusante } 4349fa9e4066Sahrens return (ret); 4350fa9e4066Sahrens } 4351fa9e4066Sahrens 4352e9dbad6fSeschrock nvlist_t * 4353e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 4354e9dbad6fSeschrock { 4355e9dbad6fSeschrock return (zhp->zfs_user_props); 4356e9dbad6fSeschrock } 4357e9dbad6fSeschrock 435892241e0bSTom Erickson nvlist_t * 435992241e0bSTom Erickson zfs_get_recvd_props(zfs_handle_t *zhp) 436092241e0bSTom Erickson { 436192241e0bSTom Erickson if (zhp->zfs_recvd_props == NULL) 436292241e0bSTom Erickson if (get_recvd_props_ioctl(zhp) != 0) 436392241e0bSTom Erickson return (NULL); 436492241e0bSTom Erickson return (zhp->zfs_recvd_props); 436592241e0bSTom Erickson } 436692241e0bSTom Erickson 4367b1b8ab34Slling /* 4368b1b8ab34Slling * This function is used by 'zfs list' to determine the exact set of columns to 4369b1b8ab34Slling * display, and their maximum widths. This does two main things: 4370b1b8ab34Slling * 4371b1b8ab34Slling * - If this is a list of all properties, then expand the list to include 4372b1b8ab34Slling * all native properties, and set a flag so that for each dataset we look 4373b1b8ab34Slling * for new unique user properties and add them to the list. 4374b1b8ab34Slling * 4375b1b8ab34Slling * - For non fixed-width properties, keep track of the maximum width seen 437692241e0bSTom Erickson * so that we can size the column appropriately. If the user has 437792241e0bSTom Erickson * requested received property values, we also need to compute the width 437892241e0bSTom Erickson * of the RECEIVED column. 4379b1b8ab34Slling */ 4380b1b8ab34Slling int 438143d68d68SYuri Pankov zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received, 438243d68d68SYuri Pankov boolean_t literal) 4383b1b8ab34Slling { 4384b1b8ab34Slling libzfs_handle_t *hdl = zhp->zfs_hdl; 4385990b4856Slling zprop_list_t *entry; 4386990b4856Slling zprop_list_t **last, **start; 4387b1b8ab34Slling nvlist_t *userprops, *propval; 4388b1b8ab34Slling nvpair_t *elem; 4389b1b8ab34Slling char *strval; 4390b1b8ab34Slling char buf[ZFS_MAXPROPLEN]; 4391b1b8ab34Slling 4392990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 4393b1b8ab34Slling return (-1); 4394e9dbad6fSeschrock 4395e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 4396e9dbad6fSeschrock 4397e9dbad6fSeschrock entry = *plp; 4398e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 4399e9dbad6fSeschrock /* 4400e9dbad6fSeschrock * Go through and add any user properties as necessary. We 4401e9dbad6fSeschrock * start by incrementing our list pointer to the first 4402e9dbad6fSeschrock * non-native property. 4403e9dbad6fSeschrock */ 4404e9dbad6fSeschrock start = plp; 4405e9dbad6fSeschrock while (*start != NULL) { 4406990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 4407e9dbad6fSeschrock break; 4408e9dbad6fSeschrock start = &(*start)->pl_next; 4409e9dbad6fSeschrock } 4410e9dbad6fSeschrock 4411e9dbad6fSeschrock elem = NULL; 4412e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 4413e9dbad6fSeschrock /* 4414e9dbad6fSeschrock * See if we've already found this property in our list. 4415e9dbad6fSeschrock */ 4416e9dbad6fSeschrock for (last = start; *last != NULL; 4417e9dbad6fSeschrock last = &(*last)->pl_next) { 4418e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 4419e9dbad6fSeschrock nvpair_name(elem)) == 0) 4420e9dbad6fSeschrock break; 4421e9dbad6fSeschrock } 4422e9dbad6fSeschrock 4423e9dbad6fSeschrock if (*last == NULL) { 4424e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 4425990b4856Slling sizeof (zprop_list_t))) == NULL || 4426e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 4427e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 4428e9dbad6fSeschrock free(entry); 4429e9dbad6fSeschrock return (-1); 4430e9dbad6fSeschrock } 4431e9dbad6fSeschrock 4432990b4856Slling entry->pl_prop = ZPROP_INVAL; 4433e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 4434e9dbad6fSeschrock entry->pl_all = B_TRUE; 4435e9dbad6fSeschrock *last = entry; 4436e9dbad6fSeschrock } 4437e9dbad6fSeschrock } 4438e9dbad6fSeschrock } 4439e9dbad6fSeschrock 4440e9dbad6fSeschrock /* 4441e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 4442e9dbad6fSeschrock */ 4443e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 444443d68d68SYuri Pankov if (entry->pl_fixed && !literal) 4445e9dbad6fSeschrock continue; 4446e9dbad6fSeschrock 4447990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 4448e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 444943d68d68SYuri Pankov buf, sizeof (buf), NULL, NULL, 0, literal) == 0) { 4450e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 4451e9dbad6fSeschrock entry->pl_width = strlen(buf); 4452e9dbad6fSeschrock } 445392241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 445492241e0bSTom Erickson zfs_prop_to_name(entry->pl_prop), 445543d68d68SYuri Pankov buf, sizeof (buf), literal) == 0) 445692241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 445792241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 445892241e0bSTom Erickson } else { 445992241e0bSTom Erickson if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, 446092241e0bSTom Erickson &propval) == 0) { 446192241e0bSTom Erickson verify(nvlist_lookup_string(propval, 446292241e0bSTom Erickson ZPROP_VALUE, &strval) == 0); 446392241e0bSTom Erickson if (strlen(strval) > entry->pl_width) 446492241e0bSTom Erickson entry->pl_width = strlen(strval); 446592241e0bSTom Erickson } 446692241e0bSTom Erickson if (received && zfs_prop_get_recvd(zhp, 446792241e0bSTom Erickson entry->pl_user_prop, 446843d68d68SYuri Pankov buf, sizeof (buf), literal) == 0) 446992241e0bSTom Erickson if (strlen(buf) > entry->pl_recvd_width) 447092241e0bSTom Erickson entry->pl_recvd_width = strlen(buf); 4471e9dbad6fSeschrock } 4472e9dbad6fSeschrock } 4473e9dbad6fSeschrock 4474e9dbad6fSeschrock return (0); 4475e9dbad6fSeschrock } 4476ecd6cf80Smarks 4477ecd6cf80Smarks int 4478ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 4479743a77edSAlan Wright char *resource, void *export, void *sharetab, 4480743a77edSAlan Wright int sharemax, zfs_share_op_t operation) 4481ecd6cf80Smarks { 4482ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 4483ecd6cf80Smarks int error; 4484ecd6cf80Smarks 4485ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4486ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4487743a77edSAlan Wright if (resource) 4488743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string)); 4489ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 4490ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 4491da6c28aaSamw zc.zc_share.z_sharetype = operation; 4492ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 4493ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 4494ecd6cf80Smarks return (error); 4495ecd6cf80Smarks } 44962e5e9e19SSanjeev Bagewadi 44972e5e9e19SSanjeev Bagewadi void 44982e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) 44992e5e9e19SSanjeev Bagewadi { 45002e5e9e19SSanjeev Bagewadi nvpair_t *curr; 45012e5e9e19SSanjeev Bagewadi 45022e5e9e19SSanjeev Bagewadi /* 45032e5e9e19SSanjeev Bagewadi * Keep a reference to the props-table against which we prune the 45042e5e9e19SSanjeev Bagewadi * properties. 45052e5e9e19SSanjeev Bagewadi */ 45062e5e9e19SSanjeev Bagewadi zhp->zfs_props_table = props; 45072e5e9e19SSanjeev Bagewadi 45082e5e9e19SSanjeev Bagewadi curr = nvlist_next_nvpair(zhp->zfs_props, NULL); 45092e5e9e19SSanjeev Bagewadi 45102e5e9e19SSanjeev Bagewadi while (curr) { 45112e5e9e19SSanjeev Bagewadi zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); 45122e5e9e19SSanjeev Bagewadi nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); 45132e5e9e19SSanjeev Bagewadi 451414843421SMatthew Ahrens /* 4515faaa6415SEric Schrock * User properties will result in ZPROP_INVAL, and since we 4516faaa6415SEric Schrock * only know how to prune standard ZFS properties, we always 4517faaa6415SEric Schrock * leave these in the list. This can also happen if we 4518faaa6415SEric Schrock * encounter an unknown DSL property (when running older 4519faaa6415SEric Schrock * software, for example). 452014843421SMatthew Ahrens */ 452114843421SMatthew Ahrens if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) 45222e5e9e19SSanjeev Bagewadi (void) nvlist_remove(zhp->zfs_props, 45232e5e9e19SSanjeev Bagewadi nvpair_name(curr), nvpair_type(curr)); 45242e5e9e19SSanjeev Bagewadi curr = next; 45252e5e9e19SSanjeev Bagewadi } 45262e5e9e19SSanjeev Bagewadi } 4527743a77edSAlan Wright 4528743a77edSAlan Wright static int 4529743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, 4530743a77edSAlan Wright zfs_smb_acl_op_t cmd, char *resource1, char *resource2) 4531743a77edSAlan Wright { 4532743a77edSAlan Wright zfs_cmd_t zc = { 0 }; 4533743a77edSAlan Wright nvlist_t *nvlist = NULL; 4534743a77edSAlan Wright int error; 4535743a77edSAlan Wright 4536743a77edSAlan Wright (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4537743a77edSAlan Wright (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4538743a77edSAlan Wright zc.zc_cookie = (uint64_t)cmd; 4539743a77edSAlan Wright 4540743a77edSAlan Wright if (cmd == ZFS_SMB_ACL_RENAME) { 4541743a77edSAlan Wright if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 4542743a77edSAlan Wright (void) no_memory(hdl); 454330925561SChris Williamson return (0); 4544743a77edSAlan Wright } 4545743a77edSAlan Wright } 4546743a77edSAlan Wright 4547743a77edSAlan Wright switch (cmd) { 4548743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 4549743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 4550743a77edSAlan Wright (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string)); 4551743a77edSAlan Wright break; 4552743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 4553743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC, 4554743a77edSAlan Wright resource1) != 0) { 4555743a77edSAlan Wright (void) no_memory(hdl); 4556743a77edSAlan Wright return (-1); 4557743a77edSAlan Wright } 4558743a77edSAlan Wright if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET, 4559743a77edSAlan Wright resource2) != 0) { 4560743a77edSAlan Wright (void) no_memory(hdl); 4561743a77edSAlan Wright return (-1); 4562743a77edSAlan Wright } 4563743a77edSAlan Wright if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) { 4564743a77edSAlan Wright nvlist_free(nvlist); 4565743a77edSAlan Wright return (-1); 4566743a77edSAlan Wright } 4567743a77edSAlan Wright break; 4568743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 4569743a77edSAlan Wright break; 4570743a77edSAlan Wright default: 4571743a77edSAlan Wright return (-1); 4572743a77edSAlan Wright } 4573743a77edSAlan Wright error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc); 4574aab83bb8SJosef 'Jeff' Sipek nvlist_free(nvlist); 4575743a77edSAlan Wright return (error); 4576743a77edSAlan Wright } 4577743a77edSAlan Wright 4578743a77edSAlan Wright int 4579743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset, 4580743a77edSAlan Wright char *path, char *resource) 4581743a77edSAlan Wright { 4582743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD, 4583743a77edSAlan Wright resource, NULL)); 4584743a77edSAlan Wright } 4585743a77edSAlan Wright 4586743a77edSAlan Wright int 4587743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset, 4588743a77edSAlan Wright char *path, char *resource) 4589743a77edSAlan Wright { 4590743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE, 4591743a77edSAlan Wright resource, NULL)); 4592743a77edSAlan Wright } 4593743a77edSAlan Wright 4594743a77edSAlan Wright int 4595743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path) 4596743a77edSAlan Wright { 4597743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE, 4598743a77edSAlan Wright NULL, NULL)); 4599743a77edSAlan Wright } 4600743a77edSAlan Wright 4601743a77edSAlan Wright int 4602743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, 4603743a77edSAlan Wright char *oldname, char *newname) 4604743a77edSAlan Wright { 4605743a77edSAlan Wright return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, 4606743a77edSAlan Wright oldname, newname)); 4607743a77edSAlan Wright } 460814843421SMatthew Ahrens 460914843421SMatthew Ahrens int 461014843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, 461114843421SMatthew Ahrens zfs_userspace_cb_t func, void *arg) 461214843421SMatthew Ahrens { 461314843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 461414843421SMatthew Ahrens zfs_useracct_t buf[100]; 461570f56fa6SYuri Pankov libzfs_handle_t *hdl = zhp->zfs_hdl; 461670f56fa6SYuri Pankov int ret; 461714843421SMatthew Ahrens 461819b94df9SMatthew Ahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 461914843421SMatthew Ahrens 462014843421SMatthew Ahrens zc.zc_objset_type = type; 462114843421SMatthew Ahrens zc.zc_nvlist_dst = (uintptr_t)buf; 462214843421SMatthew Ahrens 462370f56fa6SYuri Pankov for (;;) { 462414843421SMatthew Ahrens zfs_useracct_t *zua = buf; 462514843421SMatthew Ahrens 462614843421SMatthew Ahrens zc.zc_nvlist_dst_size = sizeof (buf); 462770f56fa6SYuri Pankov if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) { 46283b2aab18SMatthew Ahrens char errbuf[1024]; 462970f56fa6SYuri Pankov 463070f56fa6SYuri Pankov (void) snprintf(errbuf, sizeof (errbuf), 463170f56fa6SYuri Pankov dgettext(TEXT_DOMAIN, 463270f56fa6SYuri Pankov "cannot get used/quota for %s"), zc.zc_name); 463370f56fa6SYuri Pankov return (zfs_standard_error_fmt(hdl, errno, errbuf)); 463470f56fa6SYuri Pankov } 463570f56fa6SYuri Pankov if (zc.zc_nvlist_dst_size == 0) 463614843421SMatthew Ahrens break; 463714843421SMatthew Ahrens 463814843421SMatthew Ahrens while (zc.zc_nvlist_dst_size > 0) { 463970f56fa6SYuri Pankov if ((ret = func(arg, zua->zu_domain, zua->zu_rid, 464070f56fa6SYuri Pankov zua->zu_space)) != 0) 464170f56fa6SYuri Pankov return (ret); 464214843421SMatthew Ahrens zua++; 464314843421SMatthew Ahrens zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); 464414843421SMatthew Ahrens } 464514843421SMatthew Ahrens } 464614843421SMatthew Ahrens 464770f56fa6SYuri Pankov return (0); 464814843421SMatthew Ahrens } 4649842727c2SChris Kirby 46503b2aab18SMatthew Ahrens struct holdarg { 46513b2aab18SMatthew Ahrens nvlist_t *nvl; 46523b2aab18SMatthew Ahrens const char *snapname; 46533b2aab18SMatthew Ahrens const char *tag; 46543b2aab18SMatthew Ahrens boolean_t recursive; 4655bb6e7075SMatthew Ahrens int error; 46563b2aab18SMatthew Ahrens }; 46573b2aab18SMatthew Ahrens 46583b2aab18SMatthew Ahrens static int 46593b2aab18SMatthew Ahrens zfs_hold_one(zfs_handle_t *zhp, void *arg) 46603b2aab18SMatthew Ahrens { 46613b2aab18SMatthew Ahrens struct holdarg *ha = arg; 46629adfa60dSMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 46633b2aab18SMatthew Ahrens int rv = 0; 46643b2aab18SMatthew Ahrens 46653b2aab18SMatthew Ahrens (void) snprintf(name, sizeof (name), 46663b2aab18SMatthew Ahrens "%s@%s", zhp->zfs_name, ha->snapname); 46673b2aab18SMatthew Ahrens 4668a7a845e4SSteven Hartland if (lzc_exists(name)) 46693b2aab18SMatthew Ahrens fnvlist_add_string(ha->nvl, name, ha->tag); 46703b2aab18SMatthew Ahrens 46713b2aab18SMatthew Ahrens if (ha->recursive) 46723b2aab18SMatthew Ahrens rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha); 46733b2aab18SMatthew Ahrens zfs_close(zhp); 46743b2aab18SMatthew Ahrens return (rv); 46753b2aab18SMatthew Ahrens } 46763b2aab18SMatthew Ahrens 4677842727c2SChris Kirby int 4678842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag, 4679a7a845e4SSteven Hartland boolean_t recursive, int cleanup_fd) 4680842727c2SChris Kirby { 46813b2aab18SMatthew Ahrens int ret; 46823b2aab18SMatthew Ahrens struct holdarg ha; 4683842727c2SChris Kirby 46843b2aab18SMatthew Ahrens ha.nvl = fnvlist_alloc(); 46853b2aab18SMatthew Ahrens ha.snapname = snapname; 46863b2aab18SMatthew Ahrens ha.tag = tag; 46873b2aab18SMatthew Ahrens ha.recursive = recursive; 46883b2aab18SMatthew Ahrens (void) zfs_hold_one(zfs_handle_dup(zhp), &ha); 4689013023d4SMartin Matuska 4690a7a845e4SSteven Hartland if (nvlist_empty(ha.nvl)) { 4691a7a845e4SSteven Hartland char errbuf[1024]; 4692a7a845e4SSteven Hartland 4693013023d4SMartin Matuska fnvlist_free(ha.nvl); 4694013023d4SMartin Matuska ret = ENOENT; 4695a7a845e4SSteven Hartland (void) snprintf(errbuf, sizeof (errbuf), 4696a7a845e4SSteven Hartland dgettext(TEXT_DOMAIN, 4697a7a845e4SSteven Hartland "cannot hold snapshot '%s@%s'"), 4698a7a845e4SSteven Hartland zhp->zfs_name, snapname); 4699a7a845e4SSteven Hartland (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf); 4700013023d4SMartin Matuska return (ret); 4701013023d4SMartin Matuska } 4702013023d4SMartin Matuska 4703a7a845e4SSteven Hartland ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl); 47043b2aab18SMatthew Ahrens fnvlist_free(ha.nvl); 4705a7f53a56SChris Kirby 4706a7a845e4SSteven Hartland return (ret); 4707a7a845e4SSteven Hartland } 4708a7a845e4SSteven Hartland 4709a7a845e4SSteven Hartland int 4710a7a845e4SSteven Hartland zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds) 4711a7a845e4SSteven Hartland { 4712a7a845e4SSteven Hartland int ret; 4713a7a845e4SSteven Hartland nvlist_t *errors; 4714a7a845e4SSteven Hartland libzfs_handle_t *hdl = zhp->zfs_hdl; 4715a7a845e4SSteven Hartland char errbuf[1024]; 4716a7a845e4SSteven Hartland nvpair_t *elem; 4717a7a845e4SSteven Hartland 4718a7a845e4SSteven Hartland errors = NULL; 4719a7a845e4SSteven Hartland ret = lzc_hold(holds, cleanup_fd, &errors); 4720a7a845e4SSteven Hartland 4721a7a845e4SSteven Hartland if (ret == 0) { 4722a7a845e4SSteven Hartland /* There may be errors even in the success case. */ 4723a7a845e4SSteven Hartland fnvlist_free(errors); 47243b2aab18SMatthew Ahrens return (0); 4725a7a845e4SSteven Hartland } 4726842727c2SChris Kirby 4727a7a845e4SSteven Hartland if (nvlist_empty(errors)) { 47283b2aab18SMatthew Ahrens /* no hold-specific errors */ 47293b2aab18SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 47303b2aab18SMatthew Ahrens dgettext(TEXT_DOMAIN, "cannot hold")); 47313b2aab18SMatthew Ahrens switch (ret) { 47323b2aab18SMatthew Ahrens case ENOTSUP: 47333b2aab18SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 47343b2aab18SMatthew Ahrens "pool must be upgraded")); 47353b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 47363b2aab18SMatthew Ahrens break; 47373b2aab18SMatthew Ahrens case EINVAL: 47383b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 47393b2aab18SMatthew Ahrens break; 47403b2aab18SMatthew Ahrens default: 47413b2aab18SMatthew Ahrens (void) zfs_standard_error(hdl, ret, errbuf); 47423b2aab18SMatthew Ahrens } 47433b2aab18SMatthew Ahrens } 4744842727c2SChris Kirby 47453b2aab18SMatthew Ahrens for (elem = nvlist_next_nvpair(errors, NULL); 47463b2aab18SMatthew Ahrens elem != NULL; 47473b2aab18SMatthew Ahrens elem = nvlist_next_nvpair(errors, elem)) { 47483b2aab18SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 47493b2aab18SMatthew Ahrens dgettext(TEXT_DOMAIN, 47503b2aab18SMatthew Ahrens "cannot hold snapshot '%s'"), nvpair_name(elem)); 47513b2aab18SMatthew Ahrens switch (fnvpair_value_int32(elem)) { 475215508ac0SChris Kirby case E2BIG: 475315508ac0SChris Kirby /* 475415508ac0SChris Kirby * Temporary tags wind up having the ds object id 475515508ac0SChris Kirby * prepended. So even if we passed the length check 475615508ac0SChris Kirby * above, it's still possible for the tag to wind 475715508ac0SChris Kirby * up being slightly too long. 475815508ac0SChris Kirby */ 47593b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf); 47603b2aab18SMatthew Ahrens break; 4761842727c2SChris Kirby case EINVAL: 47623b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 47633b2aab18SMatthew Ahrens break; 4764842727c2SChris Kirby case EEXIST: 47653b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf); 47663b2aab18SMatthew Ahrens break; 4767842727c2SChris Kirby default: 47683b2aab18SMatthew Ahrens (void) zfs_standard_error(hdl, 47693b2aab18SMatthew Ahrens fnvpair_value_int32(elem), errbuf); 4770842727c2SChris Kirby } 4771842727c2SChris Kirby } 4772842727c2SChris Kirby 47733b2aab18SMatthew Ahrens fnvlist_free(errors); 47743b2aab18SMatthew Ahrens return (ret); 47753b2aab18SMatthew Ahrens } 47763b2aab18SMatthew Ahrens 47773b2aab18SMatthew Ahrens static int 47783b2aab18SMatthew Ahrens zfs_release_one(zfs_handle_t *zhp, void *arg) 47793b2aab18SMatthew Ahrens { 47803b2aab18SMatthew Ahrens struct holdarg *ha = arg; 47819adfa60dSMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 47823b2aab18SMatthew Ahrens int rv = 0; 4783bb6e7075SMatthew Ahrens nvlist_t *existing_holds; 47843b2aab18SMatthew Ahrens 47853b2aab18SMatthew Ahrens (void) snprintf(name, sizeof (name), 47863b2aab18SMatthew Ahrens "%s@%s", zhp->zfs_name, ha->snapname); 47873b2aab18SMatthew Ahrens 4788bb6e7075SMatthew Ahrens if (lzc_get_holds(name, &existing_holds) != 0) { 4789bb6e7075SMatthew Ahrens ha->error = ENOENT; 4790bb6e7075SMatthew Ahrens } else if (!nvlist_exists(existing_holds, ha->tag)) { 4791bb6e7075SMatthew Ahrens ha->error = ESRCH; 4792bb6e7075SMatthew Ahrens } else { 4793bb6e7075SMatthew Ahrens nvlist_t *torelease = fnvlist_alloc(); 4794bb6e7075SMatthew Ahrens fnvlist_add_boolean(torelease, ha->tag); 4795bb6e7075SMatthew Ahrens fnvlist_add_nvlist(ha->nvl, name, torelease); 4796bb6e7075SMatthew Ahrens fnvlist_free(torelease); 47973b2aab18SMatthew Ahrens } 47983b2aab18SMatthew Ahrens 47993b2aab18SMatthew Ahrens if (ha->recursive) 48003b2aab18SMatthew Ahrens rv = zfs_iter_filesystems(zhp, zfs_release_one, ha); 48013b2aab18SMatthew Ahrens zfs_close(zhp); 48023b2aab18SMatthew Ahrens return (rv); 4803842727c2SChris Kirby } 4804842727c2SChris Kirby 4805842727c2SChris Kirby int 4806842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag, 4807842727c2SChris Kirby boolean_t recursive) 4808842727c2SChris Kirby { 48093b2aab18SMatthew Ahrens int ret; 48103b2aab18SMatthew Ahrens struct holdarg ha; 4811a7a845e4SSteven Hartland nvlist_t *errors = NULL; 48123b2aab18SMatthew Ahrens nvpair_t *elem; 4813842727c2SChris Kirby libzfs_handle_t *hdl = zhp->zfs_hdl; 4814013023d4SMartin Matuska char errbuf[1024]; 4815842727c2SChris Kirby 48163b2aab18SMatthew Ahrens ha.nvl = fnvlist_alloc(); 48173b2aab18SMatthew Ahrens ha.snapname = snapname; 48183b2aab18SMatthew Ahrens ha.tag = tag; 48193b2aab18SMatthew Ahrens ha.recursive = recursive; 4820bb6e7075SMatthew Ahrens ha.error = 0; 48213b2aab18SMatthew Ahrens (void) zfs_release_one(zfs_handle_dup(zhp), &ha); 4822013023d4SMartin Matuska 4823a7a845e4SSteven Hartland if (nvlist_empty(ha.nvl)) { 4824013023d4SMartin Matuska fnvlist_free(ha.nvl); 4825bb6e7075SMatthew Ahrens ret = ha.error; 4826013023d4SMartin Matuska (void) snprintf(errbuf, sizeof (errbuf), 4827013023d4SMartin Matuska dgettext(TEXT_DOMAIN, 4828013023d4SMartin Matuska "cannot release hold from snapshot '%s@%s'"), 4829013023d4SMartin Matuska zhp->zfs_name, snapname); 4830bb6e7075SMatthew Ahrens if (ret == ESRCH) { 4831bb6e7075SMatthew Ahrens (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf); 4832bb6e7075SMatthew Ahrens } else { 4833bb6e7075SMatthew Ahrens (void) zfs_standard_error(hdl, ret, errbuf); 4834bb6e7075SMatthew Ahrens } 4835013023d4SMartin Matuska return (ret); 4836013023d4SMartin Matuska } 4837013023d4SMartin Matuska 48383b2aab18SMatthew Ahrens ret = lzc_release(ha.nvl, &errors); 48393b2aab18SMatthew Ahrens fnvlist_free(ha.nvl); 4840842727c2SChris Kirby 4841a7a845e4SSteven Hartland if (ret == 0) { 4842a7a845e4SSteven Hartland /* There may be errors even in the success case. */ 4843a7a845e4SSteven Hartland fnvlist_free(errors); 48443b2aab18SMatthew Ahrens return (0); 4845a7a845e4SSteven Hartland } 48463b2aab18SMatthew Ahrens 4847a7a845e4SSteven Hartland if (nvlist_empty(errors)) { 48483b2aab18SMatthew Ahrens /* no hold-specific errors */ 4849842727c2SChris Kirby (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 48503b2aab18SMatthew Ahrens "cannot release")); 4851842727c2SChris Kirby switch (errno) { 4852842727c2SChris Kirby case ENOTSUP: 4853842727c2SChris Kirby zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4854842727c2SChris Kirby "pool must be upgraded")); 48553b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 48563b2aab18SMatthew Ahrens break; 48573b2aab18SMatthew Ahrens default: 48583b2aab18SMatthew Ahrens (void) zfs_standard_error_fmt(hdl, errno, errbuf); 48593b2aab18SMatthew Ahrens } 48603b2aab18SMatthew Ahrens } 48613b2aab18SMatthew Ahrens 48623b2aab18SMatthew Ahrens for (elem = nvlist_next_nvpair(errors, NULL); 48633b2aab18SMatthew Ahrens elem != NULL; 48643b2aab18SMatthew Ahrens elem = nvlist_next_nvpair(errors, elem)) { 48653b2aab18SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), 48663b2aab18SMatthew Ahrens dgettext(TEXT_DOMAIN, 48673b2aab18SMatthew Ahrens "cannot release hold from snapshot '%s'"), 48683b2aab18SMatthew Ahrens nvpair_name(elem)); 48693b2aab18SMatthew Ahrens switch (fnvpair_value_int32(elem)) { 48703b2aab18SMatthew Ahrens case ESRCH: 48713b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf); 48723b2aab18SMatthew Ahrens break; 4873842727c2SChris Kirby case EINVAL: 48743b2aab18SMatthew Ahrens (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 48753b2aab18SMatthew Ahrens break; 4876842727c2SChris Kirby default: 48773b2aab18SMatthew Ahrens (void) zfs_standard_error_fmt(hdl, 48783b2aab18SMatthew Ahrens fnvpair_value_int32(elem), errbuf); 4879842727c2SChris Kirby } 4880842727c2SChris Kirby } 4881842727c2SChris Kirby 48823b2aab18SMatthew Ahrens fnvlist_free(errors); 48833b2aab18SMatthew Ahrens return (ret); 4884842727c2SChris Kirby } 4885ca45db41SChris Kirby 48861af68beaSAlexander Stetsenko int 48871af68beaSAlexander Stetsenko zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl) 48881af68beaSAlexander Stetsenko { 48891af68beaSAlexander Stetsenko zfs_cmd_t zc = { 0 }; 48901af68beaSAlexander Stetsenko libzfs_handle_t *hdl = zhp->zfs_hdl; 48911af68beaSAlexander Stetsenko int nvsz = 2048; 48921af68beaSAlexander Stetsenko void *nvbuf; 48931af68beaSAlexander Stetsenko int err = 0; 48943b2aab18SMatthew Ahrens char errbuf[1024]; 48951af68beaSAlexander Stetsenko 48961af68beaSAlexander Stetsenko assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 48971af68beaSAlexander Stetsenko zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 48981af68beaSAlexander Stetsenko 48991af68beaSAlexander Stetsenko tryagain: 49001af68beaSAlexander Stetsenko 49011af68beaSAlexander Stetsenko nvbuf = malloc(nvsz); 49021af68beaSAlexander Stetsenko if (nvbuf == NULL) { 49031af68beaSAlexander Stetsenko err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno))); 49041af68beaSAlexander Stetsenko goto out; 49051af68beaSAlexander Stetsenko } 49061af68beaSAlexander Stetsenko 49071af68beaSAlexander Stetsenko zc.zc_nvlist_dst_size = nvsz; 49081af68beaSAlexander Stetsenko zc.zc_nvlist_dst = (uintptr_t)nvbuf; 49091af68beaSAlexander Stetsenko 49109adfa60dSMatthew Ahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 49111af68beaSAlexander Stetsenko 4912d7f601efSGeorge Wilson if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) { 49131af68beaSAlexander Stetsenko (void) snprintf(errbuf, sizeof (errbuf), 49141af68beaSAlexander Stetsenko dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"), 49151af68beaSAlexander Stetsenko zc.zc_name); 49161af68beaSAlexander Stetsenko switch (errno) { 49171af68beaSAlexander Stetsenko case ENOMEM: 49181af68beaSAlexander Stetsenko free(nvbuf); 49191af68beaSAlexander Stetsenko nvsz = zc.zc_nvlist_dst_size; 49201af68beaSAlexander Stetsenko goto tryagain; 49211af68beaSAlexander Stetsenko 49221af68beaSAlexander Stetsenko case ENOTSUP: 49231af68beaSAlexander Stetsenko zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 49241af68beaSAlexander Stetsenko "pool must be upgraded")); 49251af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 49261af68beaSAlexander Stetsenko break; 49271af68beaSAlexander Stetsenko case EINVAL: 49281af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 49291af68beaSAlexander Stetsenko break; 49301af68beaSAlexander Stetsenko case ENOENT: 49311af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_NOENT, errbuf); 49321af68beaSAlexander Stetsenko break; 49331af68beaSAlexander Stetsenko default: 49341af68beaSAlexander Stetsenko err = zfs_standard_error_fmt(hdl, errno, errbuf); 49351af68beaSAlexander Stetsenko break; 49361af68beaSAlexander Stetsenko } 49371af68beaSAlexander Stetsenko } else { 49381af68beaSAlexander Stetsenko /* success */ 49391af68beaSAlexander Stetsenko int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0); 49401af68beaSAlexander Stetsenko if (rc) { 49411af68beaSAlexander Stetsenko (void) snprintf(errbuf, sizeof (errbuf), dgettext( 49421af68beaSAlexander Stetsenko TEXT_DOMAIN, "cannot get permissions on '%s'"), 49431af68beaSAlexander Stetsenko zc.zc_name); 49441af68beaSAlexander Stetsenko err = zfs_standard_error_fmt(hdl, rc, errbuf); 49451af68beaSAlexander Stetsenko } 49461af68beaSAlexander Stetsenko } 49471af68beaSAlexander Stetsenko 49481af68beaSAlexander Stetsenko free(nvbuf); 49491af68beaSAlexander Stetsenko out: 49501af68beaSAlexander Stetsenko return (err); 49511af68beaSAlexander Stetsenko } 49521af68beaSAlexander Stetsenko 49531af68beaSAlexander Stetsenko int 49541af68beaSAlexander Stetsenko zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl) 49551af68beaSAlexander Stetsenko { 49561af68beaSAlexander Stetsenko zfs_cmd_t zc = { 0 }; 49571af68beaSAlexander Stetsenko libzfs_handle_t *hdl = zhp->zfs_hdl; 49581af68beaSAlexander Stetsenko char *nvbuf; 49593b2aab18SMatthew Ahrens char errbuf[1024]; 49601af68beaSAlexander Stetsenko size_t nvsz; 49611af68beaSAlexander Stetsenko int err; 49621af68beaSAlexander Stetsenko 49631af68beaSAlexander Stetsenko assert(zhp->zfs_type == ZFS_TYPE_VOLUME || 49641af68beaSAlexander Stetsenko zhp->zfs_type == ZFS_TYPE_FILESYSTEM); 49651af68beaSAlexander Stetsenko 49661af68beaSAlexander Stetsenko err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); 49671af68beaSAlexander Stetsenko assert(err == 0); 49681af68beaSAlexander Stetsenko 49691af68beaSAlexander Stetsenko nvbuf = malloc(nvsz); 49701af68beaSAlexander Stetsenko 49711af68beaSAlexander Stetsenko err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); 49721af68beaSAlexander Stetsenko assert(err == 0); 49731af68beaSAlexander Stetsenko 49741af68beaSAlexander Stetsenko zc.zc_nvlist_src_size = nvsz; 49751af68beaSAlexander Stetsenko zc.zc_nvlist_src = (uintptr_t)nvbuf; 49761af68beaSAlexander Stetsenko zc.zc_perm_action = un; 49771af68beaSAlexander Stetsenko 49781af68beaSAlexander Stetsenko (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 49791af68beaSAlexander Stetsenko 49801af68beaSAlexander Stetsenko if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) { 49811af68beaSAlexander Stetsenko (void) snprintf(errbuf, sizeof (errbuf), 49821af68beaSAlexander Stetsenko dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"), 49831af68beaSAlexander Stetsenko zc.zc_name); 49841af68beaSAlexander Stetsenko switch (errno) { 49851af68beaSAlexander Stetsenko case ENOTSUP: 49861af68beaSAlexander Stetsenko zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 49871af68beaSAlexander Stetsenko "pool must be upgraded")); 49881af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 49891af68beaSAlexander Stetsenko break; 49901af68beaSAlexander Stetsenko case EINVAL: 49911af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 49921af68beaSAlexander Stetsenko break; 49931af68beaSAlexander Stetsenko case ENOENT: 49941af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_NOENT, errbuf); 49951af68beaSAlexander Stetsenko break; 49961af68beaSAlexander Stetsenko default: 49971af68beaSAlexander Stetsenko err = zfs_standard_error_fmt(hdl, errno, errbuf); 49981af68beaSAlexander Stetsenko break; 49991af68beaSAlexander Stetsenko } 50001af68beaSAlexander Stetsenko } 50011af68beaSAlexander Stetsenko 50021af68beaSAlexander Stetsenko free(nvbuf); 50031af68beaSAlexander Stetsenko 50041af68beaSAlexander Stetsenko return (err); 50051af68beaSAlexander Stetsenko } 50061af68beaSAlexander Stetsenko 50071af68beaSAlexander Stetsenko int 50081af68beaSAlexander Stetsenko zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl) 50091af68beaSAlexander Stetsenko { 50103b2aab18SMatthew Ahrens int err; 50113b2aab18SMatthew Ahrens char errbuf[1024]; 50121af68beaSAlexander Stetsenko 50133b2aab18SMatthew Ahrens err = lzc_get_holds(zhp->zfs_name, nvl); 50141af68beaSAlexander Stetsenko 50153b2aab18SMatthew Ahrens if (err != 0) { 50163b2aab18SMatthew Ahrens libzfs_handle_t *hdl = zhp->zfs_hdl; 50171af68beaSAlexander Stetsenko 50181af68beaSAlexander Stetsenko (void) snprintf(errbuf, sizeof (errbuf), 50191af68beaSAlexander Stetsenko dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"), 50203b2aab18SMatthew Ahrens zhp->zfs_name); 50213b2aab18SMatthew Ahrens switch (err) { 50221af68beaSAlexander Stetsenko case ENOTSUP: 50231af68beaSAlexander Stetsenko zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 50241af68beaSAlexander Stetsenko "pool must be upgraded")); 50251af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_BADVERSION, errbuf); 50261af68beaSAlexander Stetsenko break; 50271af68beaSAlexander Stetsenko case EINVAL: 50281af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_BADTYPE, errbuf); 50291af68beaSAlexander Stetsenko break; 50301af68beaSAlexander Stetsenko case ENOENT: 50311af68beaSAlexander Stetsenko err = zfs_error(hdl, EZFS_NOENT, errbuf); 50321af68beaSAlexander Stetsenko break; 50331af68beaSAlexander Stetsenko default: 50341af68beaSAlexander Stetsenko err = zfs_standard_error_fmt(hdl, errno, errbuf); 50351af68beaSAlexander Stetsenko break; 50361af68beaSAlexander Stetsenko } 50371af68beaSAlexander Stetsenko } 50381af68beaSAlexander Stetsenko 50391af68beaSAlexander Stetsenko return (err); 50401af68beaSAlexander Stetsenko } 50411af68beaSAlexander Stetsenko 50423e30c24aSWill Andrews /* 50433e30c24aSWill Andrews * Convert the zvol's volume size to an appropriate reservation. 50443e30c24aSWill Andrews * Note: If this routine is updated, it is necessary to update the ZFS test 50453e30c24aSWill Andrews * suite's shell version in reservation.kshlib. 50463e30c24aSWill Andrews */ 5047c1449561SEric Taylor uint64_t 5048c1449561SEric Taylor zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props) 5049c1449561SEric Taylor { 5050c1449561SEric Taylor uint64_t numdb; 5051c1449561SEric Taylor uint64_t nblocks, volblocksize; 5052c1449561SEric Taylor int ncopies; 5053c1449561SEric Taylor char *strval; 5054c1449561SEric Taylor 5055c1449561SEric Taylor if (nvlist_lookup_string(props, 5056c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0) 5057c1449561SEric Taylor ncopies = atoi(strval); 5058c1449561SEric Taylor else 5059c1449561SEric Taylor ncopies = 1; 5060c1449561SEric Taylor if (nvlist_lookup_uint64(props, 5061c1449561SEric Taylor zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 5062c1449561SEric Taylor &volblocksize) != 0) 5063c1449561SEric Taylor volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 5064c1449561SEric Taylor nblocks = volsize/volblocksize; 5065c1449561SEric Taylor /* start with metadnode L0-L6 */ 5066c1449561SEric Taylor numdb = 7; 5067c1449561SEric Taylor /* calculate number of indirects */ 5068c1449561SEric Taylor while (nblocks > 1) { 5069c1449561SEric Taylor nblocks += DNODES_PER_LEVEL - 1; 5070c1449561SEric Taylor nblocks /= DNODES_PER_LEVEL; 5071c1449561SEric Taylor numdb += nblocks; 5072c1449561SEric Taylor } 5073c1449561SEric Taylor numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1); 5074c1449561SEric Taylor volsize *= ncopies; 5075c1449561SEric Taylor /* 5076c1449561SEric Taylor * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't 5077c1449561SEric Taylor * compressed, but in practice they compress down to about 5078c1449561SEric Taylor * 1100 bytes 5079c1449561SEric Taylor */ 5080c1449561SEric Taylor numdb *= 1ULL << DN_MAX_INDBLKSHIFT; 5081c1449561SEric Taylor volsize += numdb; 5082c1449561SEric Taylor return (volsize); 5083c1449561SEric Taylor } 5084