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 /* 23798d5834Sgw25295 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28fa9e4066Sahrens 29fa9e4066Sahrens #include <assert.h> 30fa9e4066Sahrens #include <ctype.h> 31fa9e4066Sahrens #include <errno.h> 32fa9e4066Sahrens #include <libdevinfo.h> 33fa9e4066Sahrens #include <libintl.h> 34fa9e4066Sahrens #include <math.h> 35fa9e4066Sahrens #include <stdio.h> 36fa9e4066Sahrens #include <stdlib.h> 37fa9e4066Sahrens #include <strings.h> 38fa9e4066Sahrens #include <unistd.h> 393cb34c60Sahrens #include <stddef.h> 40fa9e4066Sahrens #include <zone.h> 4199653d4eSeschrock #include <fcntl.h> 42fa9e4066Sahrens #include <sys/mntent.h> 43fa9e4066Sahrens #include <sys/mnttab.h> 44b12a1c38Slling #include <sys/mount.h> 45ecd6cf80Smarks #include <sys/avl.h> 46ecd6cf80Smarks #include <priv.h> 47ecd6cf80Smarks #include <pwd.h> 48ecd6cf80Smarks #include <grp.h> 49ecd6cf80Smarks #include <stddef.h> 50ecd6cf80Smarks #include <ucred.h> 51fa9e4066Sahrens 52fa9e4066Sahrens #include <sys/spa.h> 53e9dbad6fSeschrock #include <sys/zap.h> 54fa9e4066Sahrens #include <libzfs.h> 55fa9e4066Sahrens 56fa9e4066Sahrens #include "zfs_namecheck.h" 57fa9e4066Sahrens #include "zfs_prop.h" 58fa9e4066Sahrens #include "libzfs_impl.h" 59ecd6cf80Smarks #include "zfs_deleg.h" 60fa9e4066Sahrens 61cdf5b4caSmmusante static int zvol_create_link_common(libzfs_handle_t *, const char *, int); 62cdf5b4caSmmusante 63fa9e4066Sahrens /* 64fa9e4066Sahrens * Given a single type (not a mask of types), return the type in a human 65fa9e4066Sahrens * readable form. 66fa9e4066Sahrens */ 67fa9e4066Sahrens const char * 68fa9e4066Sahrens zfs_type_to_name(zfs_type_t type) 69fa9e4066Sahrens { 70fa9e4066Sahrens switch (type) { 71fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 72fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 73fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 74fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 75fa9e4066Sahrens case ZFS_TYPE_VOLUME: 76fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 77fa9e4066Sahrens } 78fa9e4066Sahrens 79fa9e4066Sahrens return (NULL); 80fa9e4066Sahrens } 81fa9e4066Sahrens 82fa9e4066Sahrens /* 83fa9e4066Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 84fa9e4066Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 85fa9e4066Sahrens * We guess what the type would have been based on the path and the mask of 86fa9e4066Sahrens * acceptable types. 87fa9e4066Sahrens */ 88fa9e4066Sahrens static const char * 89fa9e4066Sahrens path_to_str(const char *path, int types) 90fa9e4066Sahrens { 91fa9e4066Sahrens /* 92fa9e4066Sahrens * When given a single type, always report the exact type. 93fa9e4066Sahrens */ 94fa9e4066Sahrens if (types == ZFS_TYPE_SNAPSHOT) 95fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 96fa9e4066Sahrens if (types == ZFS_TYPE_FILESYSTEM) 97fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 98fa9e4066Sahrens if (types == ZFS_TYPE_VOLUME) 99fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 100fa9e4066Sahrens 101fa9e4066Sahrens /* 102fa9e4066Sahrens * The user is requesting more than one type of dataset. If this is the 103fa9e4066Sahrens * case, consult the path itself. If we're looking for a snapshot, and 104fa9e4066Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 105fa9e4066Sahrens * snapshot attribute and try again. 106fa9e4066Sahrens */ 107fa9e4066Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 108fa9e4066Sahrens if (strchr(path, '@') != NULL) 109fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 110fa9e4066Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 111fa9e4066Sahrens } 112fa9e4066Sahrens 113fa9e4066Sahrens 114fa9e4066Sahrens /* 115fa9e4066Sahrens * The user has requested either filesystems or volumes. 116fa9e4066Sahrens * We have no way of knowing a priori what type this would be, so always 117fa9e4066Sahrens * report it as "filesystem" or "volume", our two primitive types. 118fa9e4066Sahrens */ 119fa9e4066Sahrens if (types & ZFS_TYPE_FILESYSTEM) 120fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 121fa9e4066Sahrens 122fa9e4066Sahrens assert(types & ZFS_TYPE_VOLUME); 123fa9e4066Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 124fa9e4066Sahrens } 125fa9e4066Sahrens 126fa9e4066Sahrens /* 127fa9e4066Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 128fa9e4066Sahrens * provide a more meaningful error message. We place a more useful message in 129fa9e4066Sahrens * 'buf' detailing exactly why the name was not valid. 130fa9e4066Sahrens */ 131fa9e4066Sahrens static int 132f18faf3fSek110237 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, 133f18faf3fSek110237 boolean_t modifying) 134fa9e4066Sahrens { 135fa9e4066Sahrens namecheck_err_t why; 136fa9e4066Sahrens char what; 137fa9e4066Sahrens 138fa9e4066Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 13999653d4eSeschrock if (hdl != NULL) { 140fa9e4066Sahrens switch (why) { 141b81d61a6Slling case NAME_ERR_TOOLONG: 14299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14399653d4eSeschrock "name is too long")); 144b81d61a6Slling break; 145b81d61a6Slling 146fa9e4066Sahrens case NAME_ERR_LEADING_SLASH: 14799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14899653d4eSeschrock "leading slash in name")); 149fa9e4066Sahrens break; 150fa9e4066Sahrens 151fa9e4066Sahrens case NAME_ERR_EMPTY_COMPONENT: 15299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15399653d4eSeschrock "empty component in name")); 154fa9e4066Sahrens break; 155fa9e4066Sahrens 156fa9e4066Sahrens case NAME_ERR_TRAILING_SLASH: 15799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15899653d4eSeschrock "trailing slash in name")); 159fa9e4066Sahrens break; 160fa9e4066Sahrens 161fa9e4066Sahrens case NAME_ERR_INVALCHAR: 16299653d4eSeschrock zfs_error_aux(hdl, 163fa9e4066Sahrens dgettext(TEXT_DOMAIN, "invalid character " 16499653d4eSeschrock "'%c' in name"), what); 165fa9e4066Sahrens break; 166fa9e4066Sahrens 167fa9e4066Sahrens case NAME_ERR_MULTIPLE_AT: 16899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 16999653d4eSeschrock "multiple '@' delimiters in name")); 170fa9e4066Sahrens break; 1715ad82045Snd150628 1725ad82045Snd150628 case NAME_ERR_NOLETTER: 1735ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1745ad82045Snd150628 "pool doesn't begin with a letter")); 1755ad82045Snd150628 break; 1765ad82045Snd150628 1775ad82045Snd150628 case NAME_ERR_RESERVED: 1785ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1795ad82045Snd150628 "name is reserved")); 1805ad82045Snd150628 break; 1815ad82045Snd150628 1825ad82045Snd150628 case NAME_ERR_DISKLIKE: 1835ad82045Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1845ad82045Snd150628 "reserved disk name")); 1855ad82045Snd150628 break; 186fa9e4066Sahrens } 187fa9e4066Sahrens } 188fa9e4066Sahrens 189fa9e4066Sahrens return (0); 190fa9e4066Sahrens } 191fa9e4066Sahrens 192fa9e4066Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 19399653d4eSeschrock if (hdl != NULL) 19499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19599653d4eSeschrock "snapshot delimiter '@' in filesystem name")); 196fa9e4066Sahrens return (0); 197fa9e4066Sahrens } 198fa9e4066Sahrens 1991d452cf5Sahrens if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) { 2001d452cf5Sahrens if (hdl != NULL) 2011d452cf5Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 202d7d4af51Smmusante "missing '@' delimiter in snapshot name")); 2031d452cf5Sahrens return (0); 2041d452cf5Sahrens } 2051d452cf5Sahrens 206f18faf3fSek110237 if (modifying && strchr(path, '%') != NULL) { 207f18faf3fSek110237 if (hdl != NULL) 208f18faf3fSek110237 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 209f18faf3fSek110237 "invalid character %c in name"), '%'); 210f18faf3fSek110237 return (0); 211f18faf3fSek110237 } 212f18faf3fSek110237 21399653d4eSeschrock return (-1); 214fa9e4066Sahrens } 215fa9e4066Sahrens 216fa9e4066Sahrens int 217fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type) 218fa9e4066Sahrens { 219f18faf3fSek110237 return (zfs_validate_name(NULL, name, type, B_FALSE)); 220fa9e4066Sahrens } 221fa9e4066Sahrens 222fa9e4066Sahrens /* 223e9dbad6fSeschrock * This function takes the raw DSL properties, and filters out the user-defined 224e9dbad6fSeschrock * properties into a separate nvlist. 225e9dbad6fSeschrock */ 226fac3008cSeschrock static nvlist_t * 227fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props) 228e9dbad6fSeschrock { 229e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 230e9dbad6fSeschrock nvpair_t *elem; 231e9dbad6fSeschrock nvlist_t *propval; 232fac3008cSeschrock nvlist_t *nvl; 233e9dbad6fSeschrock 234fac3008cSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 235fac3008cSeschrock (void) no_memory(hdl); 236fac3008cSeschrock return (NULL); 237fac3008cSeschrock } 238e9dbad6fSeschrock 239e9dbad6fSeschrock elem = NULL; 240fac3008cSeschrock while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 241e9dbad6fSeschrock if (!zfs_prop_user(nvpair_name(elem))) 242e9dbad6fSeschrock continue; 243e9dbad6fSeschrock 244e9dbad6fSeschrock verify(nvpair_value_nvlist(elem, &propval) == 0); 245fac3008cSeschrock if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) { 246fac3008cSeschrock nvlist_free(nvl); 247fac3008cSeschrock (void) no_memory(hdl); 248fac3008cSeschrock return (NULL); 249fac3008cSeschrock } 250e9dbad6fSeschrock } 251e9dbad6fSeschrock 252fac3008cSeschrock return (nvl); 253e9dbad6fSeschrock } 254e9dbad6fSeschrock 255e9dbad6fSeschrock /* 256fa9e4066Sahrens * Utility function to gather stats (objset and zpl) for the given object. 257fa9e4066Sahrens */ 258fa9e4066Sahrens static int 259fa9e4066Sahrens get_stats(zfs_handle_t *zhp) 260fa9e4066Sahrens { 261fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 262e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 263fac3008cSeschrock nvlist_t *allprops, *userprops; 264fa9e4066Sahrens 265fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 266fa9e4066Sahrens 267e9dbad6fSeschrock if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) 26899653d4eSeschrock return (-1); 2697f7322feSeschrock 27099653d4eSeschrock while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) { 2717f7322feSeschrock if (errno == ENOMEM) { 272e9dbad6fSeschrock if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { 273e9dbad6fSeschrock zcmd_free_nvlists(&zc); 27499653d4eSeschrock return (-1); 275e9dbad6fSeschrock } 2767f7322feSeschrock } else { 277e9dbad6fSeschrock zcmd_free_nvlists(&zc); 278fa9e4066Sahrens return (-1); 2797f7322feSeschrock } 2807f7322feSeschrock } 281fa9e4066Sahrens 282a2eea2e1Sahrens zhp->zfs_dmustats = zc.zc_objset_stats; /* structure assignment */ 283fa9e4066Sahrens 284e9dbad6fSeschrock (void) strlcpy(zhp->zfs_root, zc.zc_value, sizeof (zhp->zfs_root)); 285ea8dc4b6Seschrock 286fac3008cSeschrock if (zcmd_read_dst_nvlist(hdl, &zc, &allprops) != 0) { 287fac3008cSeschrock zcmd_free_nvlists(&zc); 288fac3008cSeschrock return (-1); 289fac3008cSeschrock } 290fac3008cSeschrock 291fac3008cSeschrock zcmd_free_nvlists(&zc); 292fac3008cSeschrock 293fac3008cSeschrock if ((userprops = process_user_props(zhp, allprops)) == NULL) { 294fac3008cSeschrock nvlist_free(allprops); 295fac3008cSeschrock return (-1); 296fac3008cSeschrock } 297fac3008cSeschrock 29899653d4eSeschrock nvlist_free(zhp->zfs_props); 299fac3008cSeschrock nvlist_free(zhp->zfs_user_props); 30099653d4eSeschrock 301fac3008cSeschrock zhp->zfs_props = allprops; 302fac3008cSeschrock zhp->zfs_user_props = userprops; 30399653d4eSeschrock 304fa9e4066Sahrens return (0); 305fa9e4066Sahrens } 306fa9e4066Sahrens 307fa9e4066Sahrens /* 308fa9e4066Sahrens * Refresh the properties currently stored in the handle. 309fa9e4066Sahrens */ 310fa9e4066Sahrens void 311fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 312fa9e4066Sahrens { 313fa9e4066Sahrens (void) get_stats(zhp); 314fa9e4066Sahrens } 315fa9e4066Sahrens 316fa9e4066Sahrens /* 317fa9e4066Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 318fa9e4066Sahrens * zfs_iter_* to create child handles on the fly. 319fa9e4066Sahrens */ 320fa9e4066Sahrens zfs_handle_t * 32199653d4eSeschrock make_dataset_handle(libzfs_handle_t *hdl, const char *path) 322fa9e4066Sahrens { 32399653d4eSeschrock zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 324ecd6cf80Smarks char *logstr; 32599653d4eSeschrock 32699653d4eSeschrock if (zhp == NULL) 32799653d4eSeschrock return (NULL); 32899653d4eSeschrock 32999653d4eSeschrock zhp->zfs_hdl = hdl; 330fa9e4066Sahrens 331ecd6cf80Smarks /* 332ecd6cf80Smarks * Preserve history log string. 333ecd6cf80Smarks * any changes performed here will be 334ecd6cf80Smarks * logged as an internal event. 335ecd6cf80Smarks */ 336ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 337ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 33831fd60d3Sahrens top: 339fa9e4066Sahrens (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 340fa9e4066Sahrens 341fa9e4066Sahrens if (get_stats(zhp) != 0) { 342ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 343fa9e4066Sahrens free(zhp); 344fa9e4066Sahrens return (NULL); 345fa9e4066Sahrens } 346fa9e4066Sahrens 34731fd60d3Sahrens if (zhp->zfs_dmustats.dds_inconsistent) { 34831fd60d3Sahrens zfs_cmd_t zc = { 0 }; 34931fd60d3Sahrens 35031fd60d3Sahrens /* 35131fd60d3Sahrens * If it is dds_inconsistent, then we've caught it in 35231fd60d3Sahrens * the middle of a 'zfs receive' or 'zfs destroy', and 35331fd60d3Sahrens * it is inconsistent from the ZPL's point of view, so 35431fd60d3Sahrens * can't be mounted. However, it could also be that we 35531fd60d3Sahrens * have crashed in the middle of one of those 35631fd60d3Sahrens * operations, in which case we need to get rid of the 35731fd60d3Sahrens * inconsistent state. We do that by either rolling 35831fd60d3Sahrens * back to the previous snapshot (which will fail if 35931fd60d3Sahrens * there is none), or destroying the filesystem. Note 36031fd60d3Sahrens * that if we are still in the middle of an active 36131fd60d3Sahrens * 'receive' or 'destroy', then the rollback and destroy 36231fd60d3Sahrens * will fail with EBUSY and we will drive on as usual. 36331fd60d3Sahrens */ 36431fd60d3Sahrens 36531fd60d3Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 36631fd60d3Sahrens 367a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) { 36899653d4eSeschrock (void) zvol_remove_link(hdl, zhp->zfs_name); 36931fd60d3Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 37031fd60d3Sahrens } else { 37131fd60d3Sahrens zc.zc_objset_type = DMU_OST_ZFS; 37231fd60d3Sahrens } 37331fd60d3Sahrens 37431fd60d3Sahrens /* 375da6c28aaSamw * If we can successfully destroy it, pretend that it 37631fd60d3Sahrens * never existed. 37731fd60d3Sahrens */ 37899653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) { 379ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 38031fd60d3Sahrens free(zhp); 38131fd60d3Sahrens errno = ENOENT; 38231fd60d3Sahrens return (NULL); 38331fd60d3Sahrens } 3843cb34c60Sahrens /* If we can successfully roll it back, reget the stats */ 3853cb34c60Sahrens if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0) 3863cb34c60Sahrens goto top; 38731fd60d3Sahrens } 38831fd60d3Sahrens 389fa9e4066Sahrens /* 390fa9e4066Sahrens * We've managed to open the dataset and gather statistics. Determine 391fa9e4066Sahrens * the high-level type. 392fa9e4066Sahrens */ 393a2eea2e1Sahrens if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 394a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_VOLUME; 395a2eea2e1Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 396a2eea2e1Sahrens zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM; 397a2eea2e1Sahrens else 398a2eea2e1Sahrens abort(); 399a2eea2e1Sahrens 400fa9e4066Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 401fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 402fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 403fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 404fa9e4066Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 405fa9e4066Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 406fa9e4066Sahrens else 40799653d4eSeschrock abort(); /* we should never see any other types */ 408fa9e4066Sahrens 409ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 410fa9e4066Sahrens return (zhp); 411fa9e4066Sahrens } 412fa9e4066Sahrens 413fa9e4066Sahrens /* 414fa9e4066Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 415fa9e4066Sahrens * argument is a mask of acceptable types. The function will print an 416fa9e4066Sahrens * appropriate error message and return NULL if it can't be opened. 417fa9e4066Sahrens */ 418fa9e4066Sahrens zfs_handle_t * 41999653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 420fa9e4066Sahrens { 421fa9e4066Sahrens zfs_handle_t *zhp; 42299653d4eSeschrock char errbuf[1024]; 42399653d4eSeschrock 42499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 42599653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 426fa9e4066Sahrens 427fa9e4066Sahrens /* 42899653d4eSeschrock * Validate the name before we even try to open it. 429fa9e4066Sahrens */ 430f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) { 43199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 43299653d4eSeschrock "invalid dataset name")); 43399653d4eSeschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 434fa9e4066Sahrens return (NULL); 435fa9e4066Sahrens } 436fa9e4066Sahrens 437fa9e4066Sahrens /* 438fa9e4066Sahrens * Try to get stats for the dataset, which will tell us if it exists. 439fa9e4066Sahrens */ 440fa9e4066Sahrens errno = 0; 44199653d4eSeschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 442ece3d9b3Slling (void) zfs_standard_error(hdl, errno, errbuf); 443fa9e4066Sahrens return (NULL); 444fa9e4066Sahrens } 445fa9e4066Sahrens 446fa9e4066Sahrens if (!(types & zhp->zfs_type)) { 44799653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 44894de1d4cSeschrock zfs_close(zhp); 449fa9e4066Sahrens return (NULL); 450fa9e4066Sahrens } 451fa9e4066Sahrens 452fa9e4066Sahrens return (zhp); 453fa9e4066Sahrens } 454fa9e4066Sahrens 455fa9e4066Sahrens /* 456fa9e4066Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 457fa9e4066Sahrens */ 458fa9e4066Sahrens void 459fa9e4066Sahrens zfs_close(zfs_handle_t *zhp) 460fa9e4066Sahrens { 461fa9e4066Sahrens if (zhp->zfs_mntopts) 462fa9e4066Sahrens free(zhp->zfs_mntopts); 46399653d4eSeschrock nvlist_free(zhp->zfs_props); 464e9dbad6fSeschrock nvlist_free(zhp->zfs_user_props); 465fa9e4066Sahrens free(zhp); 466fa9e4066Sahrens } 467fa9e4066Sahrens 4687b97dc1aSrm160521 int 4697b97dc1aSrm160521 zfs_spa_version(zfs_handle_t *zhp, int *spa_version) 4707b97dc1aSrm160521 { 4717b97dc1aSrm160521 char *pool_name; 4727b97dc1aSrm160521 zpool_handle_t *zpool_handle; 4737b97dc1aSrm160521 char *p; 4747b97dc1aSrm160521 4757b97dc1aSrm160521 pool_name = zfs_alloc(zhp->zfs_hdl, MAXPATHLEN); 4767b97dc1aSrm160521 if (zfs_prop_get(zhp, ZFS_PROP_NAME, pool_name, 4777b97dc1aSrm160521 MAXPATHLEN, NULL, NULL, 0, B_FALSE) != 0) { 4787b97dc1aSrm160521 free(pool_name); 4797b97dc1aSrm160521 return (-1); 4807b97dc1aSrm160521 } 4817b97dc1aSrm160521 4827b97dc1aSrm160521 if (p = strchr(pool_name, '/')) 4837b97dc1aSrm160521 *p = '\0'; 4847b97dc1aSrm160521 zpool_handle = zpool_open(zhp->zfs_hdl, pool_name); 4857b97dc1aSrm160521 free(pool_name); 4867b97dc1aSrm160521 if (zpool_handle == NULL) 4877b97dc1aSrm160521 return (-1); 4887b97dc1aSrm160521 4897b97dc1aSrm160521 *spa_version = zpool_get_prop_int(zpool_handle, 4907b97dc1aSrm160521 ZPOOL_PROP_VERSION, NULL); 4917b97dc1aSrm160521 zpool_close(zpool_handle); 4927b97dc1aSrm160521 return (0); 4937b97dc1aSrm160521 } 4947b97dc1aSrm160521 4957b97dc1aSrm160521 /* 4967b97dc1aSrm160521 * The choice of reservation property depends on the SPA version. 4977b97dc1aSrm160521 */ 4987b97dc1aSrm160521 static int 4997b97dc1aSrm160521 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) 5007b97dc1aSrm160521 { 5017b97dc1aSrm160521 int spa_version; 5027b97dc1aSrm160521 5037b97dc1aSrm160521 if (zfs_spa_version(zhp, &spa_version) < 0) 5047b97dc1aSrm160521 return (-1); 5057b97dc1aSrm160521 5067b97dc1aSrm160521 if (spa_version >= SPA_VERSION_REFRESERVATION) 5077b97dc1aSrm160521 *resv_prop = ZFS_PROP_REFRESERVATION; 5087b97dc1aSrm160521 else 5097b97dc1aSrm160521 *resv_prop = ZFS_PROP_RESERVATION; 5107b97dc1aSrm160521 5117b97dc1aSrm160521 return (0); 5127b97dc1aSrm160521 } 5137b97dc1aSrm160521 514b1b8ab34Slling /* 515e9dbad6fSeschrock * Given an nvlist of properties to set, validates that they are correct, and 516e9dbad6fSeschrock * parses any numeric properties (index, boolean, etc) if they are specified as 517e9dbad6fSeschrock * strings. 518fa9e4066Sahrens */ 519990b4856Slling static nvlist_t * 520990b4856Slling zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, 521990b4856Slling uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) 522fa9e4066Sahrens { 523e9dbad6fSeschrock nvpair_t *elem; 524e9dbad6fSeschrock uint64_t intval; 525e9dbad6fSeschrock char *strval; 526990b4856Slling zfs_prop_t prop; 527e9dbad6fSeschrock nvlist_t *ret; 528da6c28aaSamw int chosen_normal = -1; 529da6c28aaSamw int chosen_utf = -1; 530990b4856Slling 531990b4856Slling if (type == ZFS_TYPE_SNAPSHOT) { 532990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 533990b4856Slling "snapshot properties cannot be modified")); 534990b4856Slling (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 535990b4856Slling return (NULL); 536990b4856Slling } 537fa9e4066Sahrens 538e9dbad6fSeschrock if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) { 539e9dbad6fSeschrock (void) no_memory(hdl); 540e9dbad6fSeschrock return (NULL); 541e9dbad6fSeschrock } 542fa9e4066Sahrens 543e9dbad6fSeschrock elem = NULL; 544e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 545990b4856Slling const char *propname = nvpair_name(elem); 54699653d4eSeschrock 547fa9e4066Sahrens /* 548e9dbad6fSeschrock * Make sure this property is valid and applies to this type. 549fa9e4066Sahrens */ 550990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 551990b4856Slling if (!zfs_prop_user(propname)) { 552e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 553990b4856Slling "invalid property '%s'"), propname); 554e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 555e9dbad6fSeschrock goto error; 556e9dbad6fSeschrock } 557e9dbad6fSeschrock 558990b4856Slling /* 559990b4856Slling * If this is a user property, make sure it's a 560990b4856Slling * string, and that it's less than ZAP_MAXNAMELEN. 561990b4856Slling */ 562990b4856Slling if (nvpair_type(elem) != DATA_TYPE_STRING) { 563990b4856Slling zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 564990b4856Slling "'%s' must be a string"), propname); 565990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 566990b4856Slling goto error; 567990b4856Slling } 568990b4856Slling 569990b4856Slling if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 570e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 571e9dbad6fSeschrock "property name '%s' is too long"), 572e9dbad6fSeschrock propname); 573990b4856Slling (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 574e9dbad6fSeschrock goto error; 575e9dbad6fSeschrock } 576e9dbad6fSeschrock 577e9dbad6fSeschrock (void) nvpair_value_string(elem, &strval); 578e9dbad6fSeschrock if (nvlist_add_string(ret, propname, strval) != 0) { 579e9dbad6fSeschrock (void) no_memory(hdl); 580e9dbad6fSeschrock goto error; 581e9dbad6fSeschrock } 582e9dbad6fSeschrock continue; 583e9dbad6fSeschrock } 584fa9e4066Sahrens 585e9dbad6fSeschrock if (!zfs_prop_valid_for_type(prop, type)) { 586e9dbad6fSeschrock zfs_error_aux(hdl, 587e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' does not " 588e9dbad6fSeschrock "apply to datasets of this type"), propname); 589e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf); 590e9dbad6fSeschrock goto error; 591e9dbad6fSeschrock } 592e9dbad6fSeschrock 593e9dbad6fSeschrock if (zfs_prop_readonly(prop) && 594da6c28aaSamw (!zfs_prop_setonce(prop) || zhp != NULL)) { 595e9dbad6fSeschrock zfs_error_aux(hdl, 596e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "'%s' is readonly"), 597e9dbad6fSeschrock propname); 598e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); 599e9dbad6fSeschrock goto error; 600e9dbad6fSeschrock } 601e9dbad6fSeschrock 602990b4856Slling if (zprop_parse_value(hdl, elem, prop, type, ret, 603990b4856Slling &strval, &intval, errbuf) != 0) 604e9dbad6fSeschrock goto error; 605e9dbad6fSeschrock 606e9dbad6fSeschrock /* 607e9dbad6fSeschrock * Perform some additional checks for specific properties. 608e9dbad6fSeschrock */ 609e9dbad6fSeschrock switch (prop) { 610e7437265Sahrens case ZFS_PROP_VERSION: 611e7437265Sahrens { 612e7437265Sahrens int version; 613e7437265Sahrens 614e7437265Sahrens if (zhp == NULL) 615e7437265Sahrens break; 616e7437265Sahrens version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 617e7437265Sahrens if (intval < version) { 618e7437265Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 619e7437265Sahrens "Can not downgrade; already at version %u"), 620e7437265Sahrens version); 621e7437265Sahrens (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 622e7437265Sahrens goto error; 623e7437265Sahrens } 624e7437265Sahrens break; 625e7437265Sahrens } 626e7437265Sahrens 627e9dbad6fSeschrock case ZFS_PROP_RECORDSIZE: 628e9dbad6fSeschrock case ZFS_PROP_VOLBLOCKSIZE: 629e9dbad6fSeschrock /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 630e9dbad6fSeschrock if (intval < SPA_MINBLOCKSIZE || 631e9dbad6fSeschrock intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) { 632e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 633e9dbad6fSeschrock "'%s' must be power of 2 from %u " 634e9dbad6fSeschrock "to %uk"), propname, 635e9dbad6fSeschrock (uint_t)SPA_MINBLOCKSIZE, 636e9dbad6fSeschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 637e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 638e9dbad6fSeschrock goto error; 639e9dbad6fSeschrock } 640e9dbad6fSeschrock break; 641e9dbad6fSeschrock 642f3861e1aSahl case ZFS_PROP_SHAREISCSI: 643f3861e1aSahl if (strcmp(strval, "off") != 0 && 644f3861e1aSahl strcmp(strval, "on") != 0 && 645f3861e1aSahl strcmp(strval, "type=disk") != 0) { 646f3861e1aSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 647f3861e1aSahl "'%s' must be 'on', 'off', or 'type=disk'"), 648f3861e1aSahl propname); 649f3861e1aSahl (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 650f3861e1aSahl goto error; 651f3861e1aSahl } 652f3861e1aSahl 653f3861e1aSahl break; 654f3861e1aSahl 655e9dbad6fSeschrock case ZFS_PROP_MOUNTPOINT: 65689eef05eSrm160521 { 65789eef05eSrm160521 namecheck_err_t why; 65889eef05eSrm160521 659e9dbad6fSeschrock if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 || 660e9dbad6fSeschrock strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0) 661e9dbad6fSeschrock break; 662e9dbad6fSeschrock 66389eef05eSrm160521 if (mountpoint_namecheck(strval, &why)) { 66489eef05eSrm160521 switch (why) { 66589eef05eSrm160521 case NAME_ERR_LEADING_SLASH: 66689eef05eSrm160521 zfs_error_aux(hdl, 66789eef05eSrm160521 dgettext(TEXT_DOMAIN, 668e9dbad6fSeschrock "'%s' must be an absolute path, " 669e9dbad6fSeschrock "'none', or 'legacy'"), propname); 67089eef05eSrm160521 break; 67189eef05eSrm160521 case NAME_ERR_TOOLONG: 67289eef05eSrm160521 zfs_error_aux(hdl, 67389eef05eSrm160521 dgettext(TEXT_DOMAIN, 67489eef05eSrm160521 "component of '%s' is too long"), 67589eef05eSrm160521 propname); 67689eef05eSrm160521 break; 67789eef05eSrm160521 } 678e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 679e9dbad6fSeschrock goto error; 680e9dbad6fSeschrock } 68189eef05eSrm160521 } 68289eef05eSrm160521 683f3861e1aSahl /*FALLTHRU*/ 684e9dbad6fSeschrock 685da6c28aaSamw case ZFS_PROP_SHARESMB: 686f3861e1aSahl case ZFS_PROP_SHARENFS: 687e9dbad6fSeschrock /* 688da6c28aaSamw * For the mountpoint and sharenfs or sharesmb 689da6c28aaSamw * properties, check if it can be set in a 690da6c28aaSamw * global/non-global zone based on 691f3861e1aSahl * the zoned property value: 692fa9e4066Sahrens * 693fa9e4066Sahrens * global zone non-global zone 694f3861e1aSahl * -------------------------------------------------- 695fa9e4066Sahrens * zoned=on mountpoint (no) mountpoint (yes) 696fa9e4066Sahrens * sharenfs (no) sharenfs (no) 697da6c28aaSamw * sharesmb (no) sharesmb (no) 698fa9e4066Sahrens * 699fa9e4066Sahrens * zoned=off mountpoint (yes) N/A 700fa9e4066Sahrens * sharenfs (yes) 701da6c28aaSamw * sharesmb (yes) 702fa9e4066Sahrens */ 703e9dbad6fSeschrock if (zoned) { 704fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID) { 70599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 706e9dbad6fSeschrock "'%s' cannot be set on " 707e9dbad6fSeschrock "dataset in a non-global zone"), 708e9dbad6fSeschrock propname); 709e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 710e9dbad6fSeschrock errbuf); 711e9dbad6fSeschrock goto error; 712da6c28aaSamw } else if (prop == ZFS_PROP_SHARENFS || 713da6c28aaSamw prop == ZFS_PROP_SHARESMB) { 71499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 715e9dbad6fSeschrock "'%s' cannot be set in " 716e9dbad6fSeschrock "a non-global zone"), propname); 717e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, 718e9dbad6fSeschrock errbuf); 719e9dbad6fSeschrock goto error; 720fa9e4066Sahrens } 721fa9e4066Sahrens } else if (getzoneid() != GLOBAL_ZONEID) { 722fa9e4066Sahrens /* 723fa9e4066Sahrens * If zoned property is 'off', this must be in 724fa9e4066Sahrens * a globle zone. If not, something is wrong. 725fa9e4066Sahrens */ 72699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 727e9dbad6fSeschrock "'%s' cannot be set while dataset " 728e9dbad6fSeschrock "'zoned' property is set"), propname); 729e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 730e9dbad6fSeschrock goto error; 731fa9e4066Sahrens } 732f3861e1aSahl 73367331909Sdougm /* 73467331909Sdougm * At this point, it is legitimate to set the 73567331909Sdougm * property. Now we want to make sure that the 73667331909Sdougm * property value is valid if it is sharenfs. 73767331909Sdougm */ 738da6c28aaSamw if ((prop == ZFS_PROP_SHARENFS || 739da6c28aaSamw prop == ZFS_PROP_SHARESMB) && 74067331909Sdougm strcmp(strval, "on") != 0 && 74167331909Sdougm strcmp(strval, "off") != 0) { 742da6c28aaSamw zfs_share_proto_t proto; 743da6c28aaSamw 744da6c28aaSamw if (prop == ZFS_PROP_SHARESMB) 745da6c28aaSamw proto = PROTO_SMB; 746da6c28aaSamw else 747da6c28aaSamw proto = PROTO_NFS; 74867331909Sdougm 74967331909Sdougm /* 750da6c28aaSamw * Must be an valid sharing protocol 751da6c28aaSamw * option string so init the libshare 752da6c28aaSamw * in order to enable the parser and 753da6c28aaSamw * then parse the options. We use the 754da6c28aaSamw * control API since we don't care about 755da6c28aaSamw * the current configuration and don't 75667331909Sdougm * want the overhead of loading it 75767331909Sdougm * until we actually do something. 75867331909Sdougm */ 75967331909Sdougm 76067331909Sdougm if (zfs_init_libshare(hdl, 76167331909Sdougm SA_INIT_CONTROL_API) != SA_OK) { 762fac3008cSeschrock /* 763fac3008cSeschrock * An error occurred so we can't do 764fac3008cSeschrock * anything 765fac3008cSeschrock */ 76667331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 76767331909Sdougm "'%s' cannot be set: problem " 76867331909Sdougm "in share initialization"), 76967331909Sdougm propname); 770fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 771fac3008cSeschrock errbuf); 77267331909Sdougm goto error; 77367331909Sdougm } 77467331909Sdougm 775da6c28aaSamw if (zfs_parse_options(strval, proto) != SA_OK) { 77667331909Sdougm /* 77767331909Sdougm * There was an error in parsing so 77867331909Sdougm * deal with it by issuing an error 77967331909Sdougm * message and leaving after 78067331909Sdougm * uninitializing the the libshare 78167331909Sdougm * interface. 78267331909Sdougm */ 78367331909Sdougm zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 78467331909Sdougm "'%s' cannot be set to invalid " 78567331909Sdougm "options"), propname); 786fac3008cSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 787fac3008cSeschrock errbuf); 78867331909Sdougm zfs_uninit_libshare(hdl); 78967331909Sdougm goto error; 79067331909Sdougm } 79167331909Sdougm zfs_uninit_libshare(hdl); 79267331909Sdougm } 79367331909Sdougm 794f3861e1aSahl break; 795da6c28aaSamw case ZFS_PROP_UTF8ONLY: 796da6c28aaSamw chosen_utf = (int)intval; 797da6c28aaSamw break; 798da6c28aaSamw case ZFS_PROP_NORMALIZE: 799da6c28aaSamw chosen_normal = (int)intval; 800da6c28aaSamw break; 801fa9e4066Sahrens } 802fa9e4066Sahrens 803e9dbad6fSeschrock /* 804e9dbad6fSeschrock * For changes to existing volumes, we have some additional 805e9dbad6fSeschrock * checks to enforce. 806e9dbad6fSeschrock */ 807e9dbad6fSeschrock if (type == ZFS_TYPE_VOLUME && zhp != NULL) { 808e9dbad6fSeschrock uint64_t volsize = zfs_prop_get_int(zhp, 809e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 810e9dbad6fSeschrock uint64_t blocksize = zfs_prop_get_int(zhp, 811e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 812e9dbad6fSeschrock char buf[64]; 813e9dbad6fSeschrock 814e9dbad6fSeschrock switch (prop) { 815e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 816a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 817e9dbad6fSeschrock if (intval > volsize) { 818e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 819e9dbad6fSeschrock "'%s' is greater than current " 820e9dbad6fSeschrock "volume size"), propname); 821e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 822e9dbad6fSeschrock errbuf); 823e9dbad6fSeschrock goto error; 824e9dbad6fSeschrock } 825e9dbad6fSeschrock break; 826e9dbad6fSeschrock 827e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 828e9dbad6fSeschrock if (intval % blocksize != 0) { 829e9dbad6fSeschrock zfs_nicenum(blocksize, buf, 830e9dbad6fSeschrock sizeof (buf)); 831e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 832e9dbad6fSeschrock "'%s' must be a multiple of " 833e9dbad6fSeschrock "volume block size (%s)"), 834e9dbad6fSeschrock propname, buf); 835e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 836e9dbad6fSeschrock errbuf); 837e9dbad6fSeschrock goto error; 838e9dbad6fSeschrock } 839e9dbad6fSeschrock 840e9dbad6fSeschrock if (intval == 0) { 841e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 842e9dbad6fSeschrock "'%s' cannot be zero"), 843e9dbad6fSeschrock propname); 844e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_BADPROP, 845e9dbad6fSeschrock errbuf); 846e9dbad6fSeschrock goto error; 847e9dbad6fSeschrock } 848f3861e1aSahl break; 849e9dbad6fSeschrock } 850e9dbad6fSeschrock } 851e9dbad6fSeschrock } 852e9dbad6fSeschrock 853e9dbad6fSeschrock /* 854da6c28aaSamw * If normalization was chosen, but no UTF8 choice was made, 855da6c28aaSamw * enforce rejection of non-UTF8 names. 856da6c28aaSamw * 857da6c28aaSamw * If normalization was chosen, but rejecting non-UTF8 names 858da6c28aaSamw * was explicitly not chosen, it is an error. 859da6c28aaSamw */ 860de8267e0Stimh if (chosen_normal > 0 && chosen_utf < 0) { 861da6c28aaSamw if (nvlist_add_uint64(ret, 862da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { 863da6c28aaSamw (void) no_memory(hdl); 864da6c28aaSamw goto error; 865da6c28aaSamw } 866de8267e0Stimh } else if (chosen_normal > 0 && chosen_utf == 0) { 867da6c28aaSamw zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 868da6c28aaSamw "'%s' must be set 'on' if normalization chosen"), 869da6c28aaSamw zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 870da6c28aaSamw (void) zfs_error(hdl, EZFS_BADPROP, errbuf); 871da6c28aaSamw goto error; 872da6c28aaSamw } 873da6c28aaSamw 874da6c28aaSamw /* 875e9dbad6fSeschrock * If this is an existing volume, and someone is setting the volsize, 876e9dbad6fSeschrock * make sure that it matches the reservation, or add it if necessary. 877e9dbad6fSeschrock */ 878e9dbad6fSeschrock if (zhp != NULL && type == ZFS_TYPE_VOLUME && 879e9dbad6fSeschrock nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE), 880e9dbad6fSeschrock &intval) == 0) { 881e9dbad6fSeschrock uint64_t old_volsize = zfs_prop_get_int(zhp, 882e9dbad6fSeschrock ZFS_PROP_VOLSIZE); 883a9b821a0Sck153898 uint64_t old_reservation; 884e9dbad6fSeschrock uint64_t new_reservation; 885a9b821a0Sck153898 zfs_prop_t resv_prop; 886a9b821a0Sck153898 8877b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 888a9b821a0Sck153898 goto error; 889a9b821a0Sck153898 old_reservation = zfs_prop_get_int(zhp, resv_prop); 890e9dbad6fSeschrock 891e9dbad6fSeschrock if (old_volsize == old_reservation && 892a9b821a0Sck153898 nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop), 893e9dbad6fSeschrock &new_reservation) != 0) { 894e9dbad6fSeschrock if (nvlist_add_uint64(ret, 895a9b821a0Sck153898 zfs_prop_to_name(resv_prop), intval) != 0) { 896e9dbad6fSeschrock (void) no_memory(hdl); 897e9dbad6fSeschrock goto error; 898e9dbad6fSeschrock } 899e9dbad6fSeschrock } 900e9dbad6fSeschrock } 901e9dbad6fSeschrock return (ret); 902e9dbad6fSeschrock 903e9dbad6fSeschrock error: 904e9dbad6fSeschrock nvlist_free(ret); 905e9dbad6fSeschrock return (NULL); 906e9dbad6fSeschrock } 907e9dbad6fSeschrock 908ecd6cf80Smarks static int 909ecd6cf80Smarks zfs_get_perm_who(const char *who, zfs_deleg_who_type_t *who_type, 910ecd6cf80Smarks uint64_t *ret_who) 911ecd6cf80Smarks { 912ecd6cf80Smarks struct passwd *pwd; 913ecd6cf80Smarks struct group *grp; 914ecd6cf80Smarks uid_t id; 915ecd6cf80Smarks 916ecd6cf80Smarks if (*who_type == ZFS_DELEG_EVERYONE || *who_type == ZFS_DELEG_CREATE || 917ecd6cf80Smarks *who_type == ZFS_DELEG_NAMED_SET) { 918ecd6cf80Smarks *ret_who = -1; 919ecd6cf80Smarks return (0); 920ecd6cf80Smarks } 921ecd6cf80Smarks if (who == NULL && !(*who_type == ZFS_DELEG_EVERYONE)) 922ecd6cf80Smarks return (EZFS_BADWHO); 923ecd6cf80Smarks 924ecd6cf80Smarks if (*who_type == ZFS_DELEG_WHO_UNKNOWN && 925ecd6cf80Smarks strcmp(who, "everyone") == 0) { 926ecd6cf80Smarks *ret_who = -1; 927ecd6cf80Smarks *who_type = ZFS_DELEG_EVERYONE; 928ecd6cf80Smarks return (0); 929ecd6cf80Smarks } 930ecd6cf80Smarks 931ecd6cf80Smarks pwd = getpwnam(who); 932ecd6cf80Smarks grp = getgrnam(who); 933ecd6cf80Smarks 934ecd6cf80Smarks if ((*who_type == ZFS_DELEG_USER) && pwd) { 935ecd6cf80Smarks *ret_who = pwd->pw_uid; 936ecd6cf80Smarks } else if ((*who_type == ZFS_DELEG_GROUP) && grp) { 937ecd6cf80Smarks *ret_who = grp->gr_gid; 938ecd6cf80Smarks } else if (pwd) { 939ecd6cf80Smarks *ret_who = pwd->pw_uid; 940ecd6cf80Smarks *who_type = ZFS_DELEG_USER; 941ecd6cf80Smarks } else if (grp) { 942ecd6cf80Smarks *ret_who = grp->gr_gid; 943ecd6cf80Smarks *who_type = ZFS_DELEG_GROUP; 944ecd6cf80Smarks } else { 945ecd6cf80Smarks char *end; 946ecd6cf80Smarks 947ecd6cf80Smarks id = strtol(who, &end, 10); 948ecd6cf80Smarks if (errno != 0 || *end != '\0') { 949ecd6cf80Smarks return (EZFS_BADWHO); 950ecd6cf80Smarks } else { 951ecd6cf80Smarks *ret_who = id; 952ecd6cf80Smarks if (*who_type == ZFS_DELEG_WHO_UNKNOWN) 953ecd6cf80Smarks *who_type = ZFS_DELEG_USER; 954ecd6cf80Smarks } 955ecd6cf80Smarks } 956ecd6cf80Smarks 957ecd6cf80Smarks return (0); 958ecd6cf80Smarks } 959ecd6cf80Smarks 960ecd6cf80Smarks static void 961ecd6cf80Smarks zfs_perms_add_to_nvlist(nvlist_t *who_nvp, char *name, nvlist_t *perms_nvp) 962ecd6cf80Smarks { 963ecd6cf80Smarks if (perms_nvp != NULL) { 964ecd6cf80Smarks verify(nvlist_add_nvlist(who_nvp, 965ecd6cf80Smarks name, perms_nvp) == 0); 966ecd6cf80Smarks } else { 967ecd6cf80Smarks verify(nvlist_add_boolean(who_nvp, name) == 0); 968ecd6cf80Smarks } 969ecd6cf80Smarks } 970ecd6cf80Smarks 971ecd6cf80Smarks static void 972ecd6cf80Smarks helper(zfs_deleg_who_type_t who_type, uint64_t whoid, char *whostr, 973ecd6cf80Smarks zfs_deleg_inherit_t inherit, nvlist_t *who_nvp, nvlist_t *perms_nvp, 974ecd6cf80Smarks nvlist_t *sets_nvp) 975ecd6cf80Smarks { 976ecd6cf80Smarks boolean_t do_perms, do_sets; 977ecd6cf80Smarks char name[ZFS_MAX_DELEG_NAME]; 978ecd6cf80Smarks 979ecd6cf80Smarks do_perms = (nvlist_next_nvpair(perms_nvp, NULL) != NULL); 980ecd6cf80Smarks do_sets = (nvlist_next_nvpair(sets_nvp, NULL) != NULL); 981ecd6cf80Smarks 982ecd6cf80Smarks if (!do_perms && !do_sets) 983ecd6cf80Smarks do_perms = do_sets = B_TRUE; 984ecd6cf80Smarks 985ecd6cf80Smarks if (do_perms) { 986ecd6cf80Smarks zfs_deleg_whokey(name, who_type, inherit, 987ecd6cf80Smarks (who_type == ZFS_DELEG_NAMED_SET) ? 988ecd6cf80Smarks whostr : (void *)&whoid); 989ecd6cf80Smarks zfs_perms_add_to_nvlist(who_nvp, name, perms_nvp); 990ecd6cf80Smarks } 991ecd6cf80Smarks if (do_sets) { 992ecd6cf80Smarks zfs_deleg_whokey(name, toupper(who_type), inherit, 993ecd6cf80Smarks (who_type == ZFS_DELEG_NAMED_SET) ? 994ecd6cf80Smarks whostr : (void *)&whoid); 995ecd6cf80Smarks zfs_perms_add_to_nvlist(who_nvp, name, sets_nvp); 996ecd6cf80Smarks } 997ecd6cf80Smarks } 998ecd6cf80Smarks 999ecd6cf80Smarks static void 1000ecd6cf80Smarks zfs_perms_add_who_nvlist(nvlist_t *who_nvp, uint64_t whoid, void *whostr, 1001ecd6cf80Smarks nvlist_t *perms_nvp, nvlist_t *sets_nvp, 1002ecd6cf80Smarks zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit) 1003ecd6cf80Smarks { 1004ecd6cf80Smarks if (who_type == ZFS_DELEG_NAMED_SET || who_type == ZFS_DELEG_CREATE) { 1005ecd6cf80Smarks helper(who_type, whoid, whostr, 0, 1006ecd6cf80Smarks who_nvp, perms_nvp, sets_nvp); 1007ecd6cf80Smarks } else { 1008ecd6cf80Smarks if (inherit & ZFS_DELEG_PERM_LOCAL) { 1009ecd6cf80Smarks helper(who_type, whoid, whostr, ZFS_DELEG_LOCAL, 1010ecd6cf80Smarks who_nvp, perms_nvp, sets_nvp); 1011ecd6cf80Smarks } 1012ecd6cf80Smarks if (inherit & ZFS_DELEG_PERM_DESCENDENT) { 1013ecd6cf80Smarks helper(who_type, whoid, whostr, ZFS_DELEG_DESCENDENT, 1014ecd6cf80Smarks who_nvp, perms_nvp, sets_nvp); 1015ecd6cf80Smarks } 1016ecd6cf80Smarks } 1017ecd6cf80Smarks } 1018ecd6cf80Smarks 1019ecd6cf80Smarks /* 1020ecd6cf80Smarks * Construct nvlist to pass down to kernel for setting/removing permissions. 1021ecd6cf80Smarks * 1022ecd6cf80Smarks * The nvlist is constructed as a series of nvpairs with an optional embedded 1023ecd6cf80Smarks * nvlist of permissions to remove or set. The topmost nvpairs are the actual 1024ecd6cf80Smarks * base attribute named stored in the dsl. 1025ecd6cf80Smarks * Arguments: 1026ecd6cf80Smarks * 1027ecd6cf80Smarks * whostr: is a comma separated list of users, groups, or a single set name. 1028ecd6cf80Smarks * whostr may be null for everyone or create perms. 1029ecd6cf80Smarks * who_type: is the type of entry in whostr. Typically this will be 1030ecd6cf80Smarks * ZFS_DELEG_WHO_UNKNOWN. 1031da6c28aaSamw * perms: common separated list of permissions. May be null if user 1032ecd6cf80Smarks * is requested to remove permissions by who. 1033ecd6cf80Smarks * inherit: Specifies the inheritance of the permissions. Will be either 1034ecd6cf80Smarks * ZFS_DELEG_PERM_LOCAL and/or ZFS_DELEG_PERM_DESCENDENT. 1035ecd6cf80Smarks * nvp The constructed nvlist to pass to zfs_perm_set(). 1036ecd6cf80Smarks * The output nvp will look something like this. 1037ecd6cf80Smarks * ul$1234 -> {create ; destroy } 1038ecd6cf80Smarks * Ul$1234 -> { @myset } 1039ecd6cf80Smarks * s-$@myset - { snapshot; checksum; compression } 1040ecd6cf80Smarks */ 1041ecd6cf80Smarks int 1042ecd6cf80Smarks zfs_build_perms(zfs_handle_t *zhp, char *whostr, char *perms, 1043ecd6cf80Smarks zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit, nvlist_t **nvp) 1044ecd6cf80Smarks { 1045ecd6cf80Smarks nvlist_t *who_nvp; 1046ecd6cf80Smarks nvlist_t *perms_nvp = NULL; 1047ecd6cf80Smarks nvlist_t *sets_nvp = NULL; 1048ecd6cf80Smarks char errbuf[1024]; 104991ebeef5Sahrens char *who_tok, *perm; 1050ecd6cf80Smarks int error; 1051ecd6cf80Smarks 1052ecd6cf80Smarks *nvp = NULL; 1053ecd6cf80Smarks 1054ecd6cf80Smarks if (perms) { 1055ecd6cf80Smarks if ((error = nvlist_alloc(&perms_nvp, 1056ecd6cf80Smarks NV_UNIQUE_NAME, 0)) != 0) { 1057ecd6cf80Smarks return (1); 1058ecd6cf80Smarks } 1059ecd6cf80Smarks if ((error = nvlist_alloc(&sets_nvp, 1060ecd6cf80Smarks NV_UNIQUE_NAME, 0)) != 0) { 1061ecd6cf80Smarks nvlist_free(perms_nvp); 1062ecd6cf80Smarks return (1); 1063ecd6cf80Smarks } 1064ecd6cf80Smarks } 1065ecd6cf80Smarks 1066ecd6cf80Smarks if ((error = nvlist_alloc(&who_nvp, NV_UNIQUE_NAME, 0)) != 0) { 1067ecd6cf80Smarks if (perms_nvp) 1068ecd6cf80Smarks nvlist_free(perms_nvp); 1069ecd6cf80Smarks if (sets_nvp) 1070ecd6cf80Smarks nvlist_free(sets_nvp); 1071ecd6cf80Smarks return (1); 1072ecd6cf80Smarks } 1073ecd6cf80Smarks 1074ecd6cf80Smarks if (who_type == ZFS_DELEG_NAMED_SET) { 1075ecd6cf80Smarks namecheck_err_t why; 1076ecd6cf80Smarks char what; 1077ecd6cf80Smarks 1078ecd6cf80Smarks if ((error = permset_namecheck(whostr, &why, &what)) != 0) { 107991ebeef5Sahrens nvlist_free(who_nvp); 108091ebeef5Sahrens if (perms_nvp) 108191ebeef5Sahrens nvlist_free(perms_nvp); 108291ebeef5Sahrens if (sets_nvp) 108391ebeef5Sahrens nvlist_free(sets_nvp); 108491ebeef5Sahrens 1085ecd6cf80Smarks switch (why) { 1086ecd6cf80Smarks case NAME_ERR_NO_AT: 1087ecd6cf80Smarks zfs_error_aux(zhp->zfs_hdl, 1088ecd6cf80Smarks dgettext(TEXT_DOMAIN, 1089ecd6cf80Smarks "set definition must begin with an '@' " 1090ecd6cf80Smarks "character")); 1091ecd6cf80Smarks } 1092ecd6cf80Smarks return (zfs_error(zhp->zfs_hdl, 1093ecd6cf80Smarks EZFS_BADPERMSET, whostr)); 1094ecd6cf80Smarks } 1095ecd6cf80Smarks } 1096ecd6cf80Smarks 1097ecd6cf80Smarks /* 1098ecd6cf80Smarks * Build up nvlist(s) of permissions. Two nvlists are maintained. 1099ecd6cf80Smarks * The first nvlist perms_nvp will have normal permissions and the 1100ecd6cf80Smarks * other sets_nvp will have only permssion set names in it. 1101ecd6cf80Smarks */ 110291ebeef5Sahrens for (perm = strtok(perms, ","); perm; perm = strtok(NULL, ",")) { 110391ebeef5Sahrens const char *perm_canonical = zfs_deleg_canonicalize_perm(perm); 1104ecd6cf80Smarks 110591ebeef5Sahrens if (perm_canonical) { 110691ebeef5Sahrens verify(nvlist_add_boolean(perms_nvp, 110791ebeef5Sahrens perm_canonical) == 0); 110891ebeef5Sahrens } else if (perm[0] == '@') { 110991ebeef5Sahrens verify(nvlist_add_boolean(sets_nvp, perm) == 0); 1110ecd6cf80Smarks } else { 1111ecd6cf80Smarks nvlist_free(who_nvp); 1112ecd6cf80Smarks nvlist_free(perms_nvp); 1113ecd6cf80Smarks nvlist_free(sets_nvp); 111491ebeef5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_BADPERM, perm)); 1115ecd6cf80Smarks } 1116ecd6cf80Smarks } 1117ecd6cf80Smarks 1118ecd6cf80Smarks if (whostr && who_type != ZFS_DELEG_CREATE) { 1119ecd6cf80Smarks who_tok = strtok(whostr, ","); 1120ecd6cf80Smarks if (who_tok == NULL) { 1121ecd6cf80Smarks nvlist_free(who_nvp); 112291ebeef5Sahrens if (perms_nvp) 1123ecd6cf80Smarks nvlist_free(perms_nvp); 1124ecd6cf80Smarks if (sets_nvp) 1125ecd6cf80Smarks nvlist_free(sets_nvp); 1126ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), 1127ecd6cf80Smarks dgettext(TEXT_DOMAIN, "Who string is NULL"), 1128ecd6cf80Smarks whostr); 1129ecd6cf80Smarks return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf)); 1130ecd6cf80Smarks } 1131ecd6cf80Smarks } 1132ecd6cf80Smarks 1133ecd6cf80Smarks /* 1134ecd6cf80Smarks * Now create the nvlist(s) 1135ecd6cf80Smarks */ 1136ecd6cf80Smarks do { 1137ecd6cf80Smarks uint64_t who_id; 1138ecd6cf80Smarks 1139ecd6cf80Smarks error = zfs_get_perm_who(who_tok, &who_type, 1140ecd6cf80Smarks &who_id); 1141ecd6cf80Smarks if (error) { 1142ecd6cf80Smarks nvlist_free(who_nvp); 114391ebeef5Sahrens if (perms_nvp) 1144ecd6cf80Smarks nvlist_free(perms_nvp); 1145ecd6cf80Smarks if (sets_nvp) 1146ecd6cf80Smarks nvlist_free(sets_nvp); 1147ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), 1148ecd6cf80Smarks dgettext(TEXT_DOMAIN, 1149ecd6cf80Smarks "Unable to determine uid/gid for " 1150ecd6cf80Smarks "%s "), who_tok); 1151ecd6cf80Smarks return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf)); 1152ecd6cf80Smarks } 1153ecd6cf80Smarks 1154ecd6cf80Smarks /* 1155ecd6cf80Smarks * add entries for both local and descendent when required 1156ecd6cf80Smarks */ 1157ecd6cf80Smarks zfs_perms_add_who_nvlist(who_nvp, who_id, who_tok, 1158ecd6cf80Smarks perms_nvp, sets_nvp, who_type, inherit); 1159ecd6cf80Smarks 1160ecd6cf80Smarks } while (who_tok = strtok(NULL, ",")); 1161ecd6cf80Smarks *nvp = who_nvp; 1162ecd6cf80Smarks return (0); 1163ecd6cf80Smarks } 1164ecd6cf80Smarks 1165ecd6cf80Smarks static int 1166ecd6cf80Smarks zfs_perm_set_common(zfs_handle_t *zhp, nvlist_t *nvp, boolean_t unset) 1167ecd6cf80Smarks { 1168ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 1169ecd6cf80Smarks int error; 1170ecd6cf80Smarks char errbuf[1024]; 1171ecd6cf80Smarks 1172ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), 1173ecd6cf80Smarks dgettext(TEXT_DOMAIN, "Cannot update 'allows' for '%s'"), 1174ecd6cf80Smarks zhp->zfs_name); 1175ecd6cf80Smarks 1176990b4856Slling if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, nvp)) 1177ecd6cf80Smarks return (-1); 1178ecd6cf80Smarks 1179ecd6cf80Smarks (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1180ecd6cf80Smarks zc.zc_perm_action = unset; 1181ecd6cf80Smarks 1182ecd6cf80Smarks error = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_FSACL, &zc); 1183ecd6cf80Smarks if (error && errno == ENOTSUP) { 1184ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), 1185ecd6cf80Smarks gettext("Pool must be upgraded to use 'allow/unallow'")); 1186ecd6cf80Smarks zcmd_free_nvlists(&zc); 1187ecd6cf80Smarks return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, errbuf)); 1188ecd6cf80Smarks } else if (error) { 1189ecd6cf80Smarks return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); 1190ecd6cf80Smarks } 1191ecd6cf80Smarks zcmd_free_nvlists(&zc); 1192ecd6cf80Smarks 1193ecd6cf80Smarks return (error); 1194ecd6cf80Smarks } 1195ecd6cf80Smarks 1196ecd6cf80Smarks int 1197ecd6cf80Smarks zfs_perm_set(zfs_handle_t *zhp, nvlist_t *nvp) 1198ecd6cf80Smarks { 1199ecd6cf80Smarks return (zfs_perm_set_common(zhp, nvp, B_FALSE)); 1200ecd6cf80Smarks } 1201ecd6cf80Smarks 1202ecd6cf80Smarks int 1203ecd6cf80Smarks zfs_perm_remove(zfs_handle_t *zhp, nvlist_t *perms) 1204ecd6cf80Smarks { 1205ecd6cf80Smarks return (zfs_perm_set_common(zhp, perms, B_TRUE)); 1206ecd6cf80Smarks } 1207ecd6cf80Smarks 1208ecd6cf80Smarks static int 1209ecd6cf80Smarks perm_compare(const void *arg1, const void *arg2) 1210ecd6cf80Smarks { 1211ecd6cf80Smarks const zfs_perm_node_t *node1 = arg1; 1212ecd6cf80Smarks const zfs_perm_node_t *node2 = arg2; 1213ecd6cf80Smarks int ret; 1214ecd6cf80Smarks 1215ecd6cf80Smarks ret = strcmp(node1->z_pname, node2->z_pname); 1216ecd6cf80Smarks 1217ecd6cf80Smarks if (ret > 0) 1218ecd6cf80Smarks return (1); 1219ecd6cf80Smarks if (ret < 0) 1220ecd6cf80Smarks return (-1); 1221ecd6cf80Smarks else 1222ecd6cf80Smarks return (0); 1223ecd6cf80Smarks } 1224ecd6cf80Smarks 1225ecd6cf80Smarks static void 1226ecd6cf80Smarks zfs_destroy_perm_tree(avl_tree_t *tree) 1227ecd6cf80Smarks { 1228ecd6cf80Smarks zfs_perm_node_t *permnode; 12293cb34c60Sahrens void *cookie = NULL; 1230ecd6cf80Smarks 12313cb34c60Sahrens while ((permnode = avl_destroy_nodes(tree, &cookie)) != NULL) 1232ecd6cf80Smarks free(permnode); 12333cb34c60Sahrens avl_destroy(tree); 1234ecd6cf80Smarks } 1235ecd6cf80Smarks 1236ecd6cf80Smarks static void 1237ecd6cf80Smarks zfs_destroy_tree(avl_tree_t *tree) 1238ecd6cf80Smarks { 1239ecd6cf80Smarks zfs_allow_node_t *allownode; 12403cb34c60Sahrens void *cookie = NULL; 1241ecd6cf80Smarks 1242ecd6cf80Smarks while ((allownode = avl_destroy_nodes(tree, &cookie)) != NULL) { 1243ecd6cf80Smarks zfs_destroy_perm_tree(&allownode->z_localdescend); 1244ecd6cf80Smarks zfs_destroy_perm_tree(&allownode->z_local); 1245ecd6cf80Smarks zfs_destroy_perm_tree(&allownode->z_descend); 1246ecd6cf80Smarks free(allownode); 1247ecd6cf80Smarks } 12483cb34c60Sahrens avl_destroy(tree); 1249ecd6cf80Smarks } 1250ecd6cf80Smarks 1251ecd6cf80Smarks void 1252ecd6cf80Smarks zfs_free_allows(zfs_allow_t *allow) 1253ecd6cf80Smarks { 1254ecd6cf80Smarks zfs_allow_t *allownext; 1255ecd6cf80Smarks zfs_allow_t *freeallow; 1256ecd6cf80Smarks 1257ecd6cf80Smarks allownext = allow; 1258ecd6cf80Smarks while (allownext) { 1259ecd6cf80Smarks zfs_destroy_tree(&allownext->z_sets); 1260ecd6cf80Smarks zfs_destroy_tree(&allownext->z_crperms); 1261ecd6cf80Smarks zfs_destroy_tree(&allownext->z_user); 1262ecd6cf80Smarks zfs_destroy_tree(&allownext->z_group); 1263ecd6cf80Smarks zfs_destroy_tree(&allownext->z_everyone); 1264ecd6cf80Smarks freeallow = allownext; 1265ecd6cf80Smarks allownext = allownext->z_next; 1266ecd6cf80Smarks free(freeallow); 1267ecd6cf80Smarks } 1268ecd6cf80Smarks } 1269ecd6cf80Smarks 1270ecd6cf80Smarks static zfs_allow_t * 1271ecd6cf80Smarks zfs_alloc_perm_tree(zfs_handle_t *zhp, zfs_allow_t *prev, char *setpoint) 1272ecd6cf80Smarks { 1273ecd6cf80Smarks zfs_allow_t *ptree; 1274ecd6cf80Smarks 1275ecd6cf80Smarks if ((ptree = zfs_alloc(zhp->zfs_hdl, 1276ecd6cf80Smarks sizeof (zfs_allow_t))) == NULL) { 1277ecd6cf80Smarks return (NULL); 1278ecd6cf80Smarks } 1279ecd6cf80Smarks 1280ecd6cf80Smarks (void) strlcpy(ptree->z_setpoint, setpoint, sizeof (ptree->z_setpoint)); 1281ecd6cf80Smarks avl_create(&ptree->z_sets, 1282ecd6cf80Smarks perm_compare, sizeof (zfs_allow_node_t), 1283ecd6cf80Smarks offsetof(zfs_allow_node_t, z_node)); 1284ecd6cf80Smarks avl_create(&ptree->z_crperms, 1285ecd6cf80Smarks perm_compare, sizeof (zfs_allow_node_t), 1286ecd6cf80Smarks offsetof(zfs_allow_node_t, z_node)); 1287ecd6cf80Smarks avl_create(&ptree->z_user, 1288ecd6cf80Smarks perm_compare, sizeof (zfs_allow_node_t), 1289ecd6cf80Smarks offsetof(zfs_allow_node_t, z_node)); 1290ecd6cf80Smarks avl_create(&ptree->z_group, 1291ecd6cf80Smarks perm_compare, sizeof (zfs_allow_node_t), 1292ecd6cf80Smarks offsetof(zfs_allow_node_t, z_node)); 1293ecd6cf80Smarks avl_create(&ptree->z_everyone, 1294ecd6cf80Smarks perm_compare, sizeof (zfs_allow_node_t), 1295ecd6cf80Smarks offsetof(zfs_allow_node_t, z_node)); 1296ecd6cf80Smarks 1297ecd6cf80Smarks if (prev) 1298ecd6cf80Smarks prev->z_next = ptree; 1299ecd6cf80Smarks ptree->z_next = NULL; 1300ecd6cf80Smarks return (ptree); 1301ecd6cf80Smarks } 1302ecd6cf80Smarks 1303ecd6cf80Smarks /* 1304ecd6cf80Smarks * Add permissions to the appropriate AVL permission tree. 1305ecd6cf80Smarks * The appropriate tree may not be the requested tree. 1306ecd6cf80Smarks * For example if ld indicates a local permission, but 1307ecd6cf80Smarks * same permission also exists as a descendent permission 1308ecd6cf80Smarks * then the permission will be removed from the descendent 1309ecd6cf80Smarks * tree and add the the local+descendent tree. 1310ecd6cf80Smarks */ 1311ecd6cf80Smarks static int 1312ecd6cf80Smarks zfs_coalesce_perm(zfs_handle_t *zhp, zfs_allow_node_t *allownode, 1313ecd6cf80Smarks char *perm, char ld) 1314ecd6cf80Smarks { 1315ecd6cf80Smarks zfs_perm_node_t pnode, *permnode, *permnode2; 1316ecd6cf80Smarks zfs_perm_node_t *newnode; 1317ecd6cf80Smarks avl_index_t where, where2; 1318ecd6cf80Smarks avl_tree_t *tree, *altree; 1319ecd6cf80Smarks 1320ecd6cf80Smarks (void) strlcpy(pnode.z_pname, perm, sizeof (pnode.z_pname)); 1321ecd6cf80Smarks 1322ecd6cf80Smarks if (ld == ZFS_DELEG_NA) { 1323ecd6cf80Smarks tree = &allownode->z_localdescend; 1324ecd6cf80Smarks altree = &allownode->z_descend; 1325ecd6cf80Smarks } else if (ld == ZFS_DELEG_LOCAL) { 1326ecd6cf80Smarks tree = &allownode->z_local; 1327ecd6cf80Smarks altree = &allownode->z_descend; 1328ecd6cf80Smarks } else { 1329ecd6cf80Smarks tree = &allownode->z_descend; 1330ecd6cf80Smarks altree = &allownode->z_local; 1331ecd6cf80Smarks } 1332ecd6cf80Smarks permnode = avl_find(tree, &pnode, &where); 1333ecd6cf80Smarks permnode2 = avl_find(altree, &pnode, &where2); 1334ecd6cf80Smarks 1335ecd6cf80Smarks if (permnode2) { 1336ecd6cf80Smarks avl_remove(altree, permnode2); 1337ecd6cf80Smarks free(permnode2); 1338ecd6cf80Smarks if (permnode == NULL) { 1339ecd6cf80Smarks tree = &allownode->z_localdescend; 1340ecd6cf80Smarks } 1341ecd6cf80Smarks } 1342ecd6cf80Smarks 1343ecd6cf80Smarks /* 1344ecd6cf80Smarks * Now insert new permission in either requested location 1345ecd6cf80Smarks * local/descendent or into ld when perm will exist in both. 1346ecd6cf80Smarks */ 1347ecd6cf80Smarks if (permnode == NULL) { 1348ecd6cf80Smarks if ((newnode = zfs_alloc(zhp->zfs_hdl, 1349ecd6cf80Smarks sizeof (zfs_perm_node_t))) == NULL) { 1350ecd6cf80Smarks return (-1); 1351ecd6cf80Smarks } 1352ecd6cf80Smarks *newnode = pnode; 1353ecd6cf80Smarks avl_add(tree, newnode); 1354ecd6cf80Smarks } 1355ecd6cf80Smarks return (0); 1356ecd6cf80Smarks } 1357e7437265Sahrens 1358ecd6cf80Smarks /* 1359ecd6cf80Smarks * Uggh, this is going to be a bit complicated. 1360ecd6cf80Smarks * we have an nvlist coming out of the kernel that 1361ecd6cf80Smarks * will indicate where the permission is set and then 1362ecd6cf80Smarks * it will contain allow of the various "who's", and what 1363ecd6cf80Smarks * their permissions are. To further complicate this 1364ecd6cf80Smarks * we will then have to coalesce the local,descendent 1365ecd6cf80Smarks * and local+descendent permissions where appropriate. 1366ecd6cf80Smarks * The kernel only knows about a permission as being local 1367ecd6cf80Smarks * or descendent, but not both. 1368ecd6cf80Smarks * 1369ecd6cf80Smarks * In order to make this easier for zfs_main to deal with 1370ecd6cf80Smarks * a series of AVL trees will be used to maintain 1371ecd6cf80Smarks * all of this, primarily for sorting purposes as well 1372ecd6cf80Smarks * as the ability to quickly locate a specific entry. 1373ecd6cf80Smarks * 1374ecd6cf80Smarks * What we end up with are tree's for sets, create perms, 1375ecd6cf80Smarks * user, groups and everyone. With each of those trees 1376ecd6cf80Smarks * we have subtrees for local, descendent and local+descendent 1377ecd6cf80Smarks * permissions. 1378ecd6cf80Smarks */ 1379ecd6cf80Smarks int 1380ecd6cf80Smarks zfs_perm_get(zfs_handle_t *zhp, zfs_allow_t **zfs_perms) 1381ecd6cf80Smarks { 1382ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 1383ecd6cf80Smarks int error; 1384ecd6cf80Smarks nvlist_t *nvlist; 1385ecd6cf80Smarks nvlist_t *permnv, *sourcenv; 1386ecd6cf80Smarks nvpair_t *who_pair, *source_pair; 1387ecd6cf80Smarks nvpair_t *perm_pair; 1388ecd6cf80Smarks char errbuf[1024]; 1389ecd6cf80Smarks zfs_allow_t *zallowp, *newallowp; 1390ecd6cf80Smarks char ld; 1391ecd6cf80Smarks char *nvpname; 1392ecd6cf80Smarks uid_t uid; 1393ecd6cf80Smarks gid_t gid; 1394ecd6cf80Smarks avl_tree_t *tree; 1395ecd6cf80Smarks avl_index_t where; 1396ecd6cf80Smarks 1397ecd6cf80Smarks (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1398ecd6cf80Smarks 1399ecd6cf80Smarks if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 1400ecd6cf80Smarks return (-1); 1401ecd6cf80Smarks 1402ecd6cf80Smarks while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) { 1403ecd6cf80Smarks if (errno == ENOMEM) { 1404ecd6cf80Smarks if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, &zc) != 0) { 1405ecd6cf80Smarks zcmd_free_nvlists(&zc); 1406ecd6cf80Smarks return (-1); 1407ecd6cf80Smarks } 1408ecd6cf80Smarks } else if (errno == ENOTSUP) { 1409ecd6cf80Smarks zcmd_free_nvlists(&zc); 1410ecd6cf80Smarks (void) snprintf(errbuf, sizeof (errbuf), 1411ecd6cf80Smarks gettext("Pool must be upgraded to use 'allow'")); 1412ecd6cf80Smarks return (zfs_error(zhp->zfs_hdl, 1413ecd6cf80Smarks EZFS_BADVERSION, errbuf)); 1414ecd6cf80Smarks } else { 1415ecd6cf80Smarks zcmd_free_nvlists(&zc); 1416ecd6cf80Smarks return (-1); 1417ecd6cf80Smarks } 1418ecd6cf80Smarks } 1419ecd6cf80Smarks 1420ecd6cf80Smarks if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &nvlist) != 0) { 1421ecd6cf80Smarks zcmd_free_nvlists(&zc); 1422ecd6cf80Smarks return (-1); 1423ecd6cf80Smarks } 1424ecd6cf80Smarks 1425ecd6cf80Smarks zcmd_free_nvlists(&zc); 1426ecd6cf80Smarks 1427ecd6cf80Smarks source_pair = nvlist_next_nvpair(nvlist, NULL); 1428ecd6cf80Smarks 1429ecd6cf80Smarks if (source_pair == NULL) { 1430ecd6cf80Smarks *zfs_perms = NULL; 1431ecd6cf80Smarks return (0); 1432ecd6cf80Smarks } 1433ecd6cf80Smarks 1434ecd6cf80Smarks *zfs_perms = zfs_alloc_perm_tree(zhp, NULL, nvpair_name(source_pair)); 1435ecd6cf80Smarks if (*zfs_perms == NULL) { 1436ecd6cf80Smarks return (0); 1437ecd6cf80Smarks } 1438ecd6cf80Smarks 1439ecd6cf80Smarks zallowp = *zfs_perms; 1440ecd6cf80Smarks 1441ecd6cf80Smarks for (;;) { 1442ecd6cf80Smarks struct passwd *pwd; 1443ecd6cf80Smarks struct group *grp; 1444ecd6cf80Smarks zfs_allow_node_t *allownode; 1445ecd6cf80Smarks zfs_allow_node_t findallownode; 1446ecd6cf80Smarks zfs_allow_node_t *newallownode; 1447ecd6cf80Smarks 1448ecd6cf80Smarks (void) strlcpy(zallowp->z_setpoint, 1449ecd6cf80Smarks nvpair_name(source_pair), 1450ecd6cf80Smarks sizeof (zallowp->z_setpoint)); 1451ecd6cf80Smarks 1452ecd6cf80Smarks if ((error = nvpair_value_nvlist(source_pair, &sourcenv)) != 0) 1453ecd6cf80Smarks goto abort; 1454ecd6cf80Smarks 1455ecd6cf80Smarks /* 1456ecd6cf80Smarks * Make sure nvlist is composed correctly 1457ecd6cf80Smarks */ 1458ecd6cf80Smarks if (zfs_deleg_verify_nvlist(sourcenv)) { 1459ecd6cf80Smarks goto abort; 1460ecd6cf80Smarks } 1461ecd6cf80Smarks 1462ecd6cf80Smarks who_pair = nvlist_next_nvpair(sourcenv, NULL); 1463ecd6cf80Smarks if (who_pair == NULL) { 1464ecd6cf80Smarks goto abort; 1465ecd6cf80Smarks } 1466ecd6cf80Smarks 1467ecd6cf80Smarks do { 1468ecd6cf80Smarks error = nvpair_value_nvlist(who_pair, &permnv); 1469ecd6cf80Smarks if (error) { 1470ecd6cf80Smarks goto abort; 1471ecd6cf80Smarks } 1472ecd6cf80Smarks 1473ecd6cf80Smarks /* 1474ecd6cf80Smarks * First build up the key to use 1475ecd6cf80Smarks * for looking up in the various 1476ecd6cf80Smarks * who trees. 1477ecd6cf80Smarks */ 1478ecd6cf80Smarks ld = nvpair_name(who_pair)[1]; 1479ecd6cf80Smarks nvpname = nvpair_name(who_pair); 1480ecd6cf80Smarks switch (nvpair_name(who_pair)[0]) { 1481ecd6cf80Smarks case ZFS_DELEG_USER: 1482ecd6cf80Smarks case ZFS_DELEG_USER_SETS: 1483ecd6cf80Smarks tree = &zallowp->z_user; 1484ecd6cf80Smarks uid = atol(&nvpname[3]); 1485ecd6cf80Smarks pwd = getpwuid(uid); 1486ecd6cf80Smarks (void) snprintf(findallownode.z_key, 1487ecd6cf80Smarks sizeof (findallownode.z_key), "user %s", 1488ecd6cf80Smarks (pwd) ? pwd->pw_name : 1489ecd6cf80Smarks &nvpair_name(who_pair)[3]); 1490ecd6cf80Smarks break; 1491ecd6cf80Smarks case ZFS_DELEG_GROUP: 1492ecd6cf80Smarks case ZFS_DELEG_GROUP_SETS: 1493ecd6cf80Smarks tree = &zallowp->z_group; 1494ecd6cf80Smarks gid = atol(&nvpname[3]); 1495ecd6cf80Smarks grp = getgrgid(gid); 1496ecd6cf80Smarks (void) snprintf(findallownode.z_key, 1497ecd6cf80Smarks sizeof (findallownode.z_key), "group %s", 1498ecd6cf80Smarks (grp) ? grp->gr_name : 1499ecd6cf80Smarks &nvpair_name(who_pair)[3]); 1500ecd6cf80Smarks break; 1501ecd6cf80Smarks case ZFS_DELEG_CREATE: 1502ecd6cf80Smarks case ZFS_DELEG_CREATE_SETS: 1503ecd6cf80Smarks tree = &zallowp->z_crperms; 1504ecd6cf80Smarks (void) strlcpy(findallownode.z_key, "", 1505ecd6cf80Smarks sizeof (findallownode.z_key)); 1506ecd6cf80Smarks break; 1507ecd6cf80Smarks case ZFS_DELEG_EVERYONE: 1508ecd6cf80Smarks case ZFS_DELEG_EVERYONE_SETS: 1509ecd6cf80Smarks (void) snprintf(findallownode.z_key, 1510ecd6cf80Smarks sizeof (findallownode.z_key), "everyone"); 1511ecd6cf80Smarks tree = &zallowp->z_everyone; 1512ecd6cf80Smarks break; 1513ecd6cf80Smarks case ZFS_DELEG_NAMED_SET: 1514ecd6cf80Smarks case ZFS_DELEG_NAMED_SET_SETS: 1515ecd6cf80Smarks (void) snprintf(findallownode.z_key, 1516ecd6cf80Smarks sizeof (findallownode.z_key), "%s", 1517ecd6cf80Smarks &nvpair_name(who_pair)[3]); 1518ecd6cf80Smarks tree = &zallowp->z_sets; 1519ecd6cf80Smarks break; 1520ecd6cf80Smarks } 1521ecd6cf80Smarks 1522ecd6cf80Smarks /* 1523ecd6cf80Smarks * Place who in tree 1524ecd6cf80Smarks */ 1525ecd6cf80Smarks allownode = avl_find(tree, &findallownode, &where); 1526ecd6cf80Smarks if (allownode == NULL) { 1527ecd6cf80Smarks if ((newallownode = zfs_alloc(zhp->zfs_hdl, 1528ecd6cf80Smarks sizeof (zfs_allow_node_t))) == NULL) { 1529ecd6cf80Smarks goto abort; 1530ecd6cf80Smarks } 1531ecd6cf80Smarks avl_create(&newallownode->z_localdescend, 1532ecd6cf80Smarks perm_compare, 1533ecd6cf80Smarks sizeof (zfs_perm_node_t), 1534ecd6cf80Smarks offsetof(zfs_perm_node_t, z_node)); 1535ecd6cf80Smarks avl_create(&newallownode->z_local, 1536ecd6cf80Smarks perm_compare, 1537ecd6cf80Smarks sizeof (zfs_perm_node_t), 1538ecd6cf80Smarks offsetof(zfs_perm_node_t, z_node)); 1539ecd6cf80Smarks avl_create(&newallownode->z_descend, 1540ecd6cf80Smarks perm_compare, 1541ecd6cf80Smarks sizeof (zfs_perm_node_t), 1542ecd6cf80Smarks offsetof(zfs_perm_node_t, z_node)); 1543ecd6cf80Smarks (void) strlcpy(newallownode->z_key, 1544ecd6cf80Smarks findallownode.z_key, 1545ecd6cf80Smarks sizeof (findallownode.z_key)); 1546ecd6cf80Smarks avl_insert(tree, newallownode, where); 1547ecd6cf80Smarks allownode = newallownode; 1548ecd6cf80Smarks } 1549ecd6cf80Smarks 1550ecd6cf80Smarks /* 1551ecd6cf80Smarks * Now iterate over the permissions and 1552ecd6cf80Smarks * place them in the appropriate local, 1553ecd6cf80Smarks * descendent or local+descendent tree. 1554ecd6cf80Smarks * 1555ecd6cf80Smarks * The permissions are added to the tree 1556ecd6cf80Smarks * via zfs_coalesce_perm(). 1557ecd6cf80Smarks */ 1558ecd6cf80Smarks perm_pair = nvlist_next_nvpair(permnv, NULL); 1559ecd6cf80Smarks if (perm_pair == NULL) 1560ecd6cf80Smarks goto abort; 1561ecd6cf80Smarks do { 1562ecd6cf80Smarks if (zfs_coalesce_perm(zhp, allownode, 1563ecd6cf80Smarks nvpair_name(perm_pair), ld) != 0) 1564ecd6cf80Smarks goto abort; 1565ecd6cf80Smarks } while (perm_pair = nvlist_next_nvpair(permnv, 1566ecd6cf80Smarks perm_pair)); 1567ecd6cf80Smarks } while (who_pair = nvlist_next_nvpair(sourcenv, who_pair)); 1568ecd6cf80Smarks 1569ecd6cf80Smarks source_pair = nvlist_next_nvpair(nvlist, source_pair); 1570ecd6cf80Smarks if (source_pair == NULL) 1571ecd6cf80Smarks break; 1572ecd6cf80Smarks 1573ecd6cf80Smarks /* 1574ecd6cf80Smarks * allocate another node from the link list of 1575ecd6cf80Smarks * zfs_allow_t structures 1576ecd6cf80Smarks */ 1577ecd6cf80Smarks newallowp = zfs_alloc_perm_tree(zhp, zallowp, 1578ecd6cf80Smarks nvpair_name(source_pair)); 1579ecd6cf80Smarks if (newallowp == NULL) { 1580ecd6cf80Smarks goto abort; 1581ecd6cf80Smarks } 1582ecd6cf80Smarks zallowp = newallowp; 1583ecd6cf80Smarks } 1584ecd6cf80Smarks nvlist_free(nvlist); 1585ecd6cf80Smarks return (0); 1586ecd6cf80Smarks abort: 1587ecd6cf80Smarks zfs_free_allows(*zfs_perms); 1588ecd6cf80Smarks nvlist_free(nvlist); 1589ecd6cf80Smarks return (-1); 1590ecd6cf80Smarks } 1591ecd6cf80Smarks 1592e9dbad6fSeschrock /* 1593e9dbad6fSeschrock * Given a property name and value, set the property for the given dataset. 1594e9dbad6fSeschrock */ 1595e9dbad6fSeschrock int 1596e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1597e9dbad6fSeschrock { 1598e9dbad6fSeschrock zfs_cmd_t zc = { 0 }; 1599e9dbad6fSeschrock int ret = -1; 1600e9dbad6fSeschrock prop_changelist_t *cl = NULL; 1601e9dbad6fSeschrock char errbuf[1024]; 1602e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1603e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1604e9dbad6fSeschrock zfs_prop_t prop; 1605e9dbad6fSeschrock 1606e9dbad6fSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1607e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1608e9dbad6fSeschrock zhp->zfs_name); 1609e9dbad6fSeschrock 1610e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1611e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1612e9dbad6fSeschrock (void) no_memory(hdl); 1613e9dbad6fSeschrock goto error; 1614e9dbad6fSeschrock } 1615e9dbad6fSeschrock 1616990b4856Slling if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, nvl, 1617e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1618e9dbad6fSeschrock goto error; 1619990b4856Slling 1620e9dbad6fSeschrock nvlist_free(nvl); 1621e9dbad6fSeschrock nvl = realprops; 1622e9dbad6fSeschrock 1623e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1624e9dbad6fSeschrock 1625fa9e4066Sahrens if ((cl = changelist_gather(zhp, prop, 0)) == NULL) 1626e9dbad6fSeschrock goto error; 1627fa9e4066Sahrens 1628fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 162999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1630fa9e4066Sahrens "child dataset with inherited mountpoint is used " 163199653d4eSeschrock "in a non-global zone")); 163299653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1633fa9e4066Sahrens goto error; 1634fa9e4066Sahrens } 1635fa9e4066Sahrens 1636fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1637fa9e4066Sahrens goto error; 1638fa9e4066Sahrens 1639fa9e4066Sahrens /* 1640fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1641fa9e4066Sahrens */ 1642fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1643fa9e4066Sahrens 1644990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1645e9dbad6fSeschrock goto error; 1646e9dbad6fSeschrock 1647ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1648fa9e4066Sahrens if (ret != 0) { 1649fa9e4066Sahrens switch (errno) { 1650fa9e4066Sahrens 1651fa9e4066Sahrens case ENOSPC: 1652fa9e4066Sahrens /* 1653fa9e4066Sahrens * For quotas and reservations, ENOSPC indicates 1654fa9e4066Sahrens * something different; setting a quota or reservation 1655fa9e4066Sahrens * doesn't use any disk space. 1656fa9e4066Sahrens */ 1657fa9e4066Sahrens switch (prop) { 1658fa9e4066Sahrens case ZFS_PROP_QUOTA: 1659a9799022Sck153898 case ZFS_PROP_REFQUOTA: 166099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 166199653d4eSeschrock "size is less than current used or " 166299653d4eSeschrock "reserved space")); 166399653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1664fa9e4066Sahrens break; 1665fa9e4066Sahrens 1666fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1667a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 166899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 166999653d4eSeschrock "size is greater than available space")); 167099653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1671fa9e4066Sahrens break; 1672fa9e4066Sahrens 1673fa9e4066Sahrens default: 167499653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1675fa9e4066Sahrens break; 1676fa9e4066Sahrens } 1677fa9e4066Sahrens break; 1678fa9e4066Sahrens 1679fa9e4066Sahrens case EBUSY: 168099653d4eSeschrock if (prop == ZFS_PROP_VOLBLOCKSIZE) 168199653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); 168299653d4eSeschrock else 1683e9dbad6fSeschrock (void) zfs_standard_error(hdl, EBUSY, errbuf); 1684fa9e4066Sahrens break; 1685fa9e4066Sahrens 16862a79c5feSlling case EROFS: 168799653d4eSeschrock (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 16882a79c5feSlling break; 16892a79c5feSlling 1690c9431fa1Sahl case ENOTSUP: 1691c9431fa1Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 169240feaa91Sahrens "pool must be upgraded to set this " 169340feaa91Sahrens "property or value")); 1694c9431fa1Sahl (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1695c9431fa1Sahl break; 1696c9431fa1Sahl 1697fa9e4066Sahrens case EOVERFLOW: 1698fa9e4066Sahrens /* 1699fa9e4066Sahrens * This platform can't address a volume this big. 1700fa9e4066Sahrens */ 1701fa9e4066Sahrens #ifdef _ILP32 1702fa9e4066Sahrens if (prop == ZFS_PROP_VOLSIZE) { 170399653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1704fa9e4066Sahrens break; 1705fa9e4066Sahrens } 1706fa9e4066Sahrens #endif 170799653d4eSeschrock /* FALLTHROUGH */ 1708fa9e4066Sahrens default: 170999653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1710fa9e4066Sahrens } 1711fa9e4066Sahrens } else { 1712fa9e4066Sahrens /* 1713fa9e4066Sahrens * Refresh the statistics so the new property value 1714fa9e4066Sahrens * is reflected. 1715fa9e4066Sahrens */ 1716e9dbad6fSeschrock if ((ret = changelist_postfix(cl)) == 0) 1717fa9e4066Sahrens (void) get_stats(zhp); 1718fa9e4066Sahrens } 1719fa9e4066Sahrens 1720fa9e4066Sahrens error: 1721e9dbad6fSeschrock nvlist_free(nvl); 1722e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1723e9dbad6fSeschrock if (cl) 1724fa9e4066Sahrens changelist_free(cl); 1725fa9e4066Sahrens return (ret); 1726fa9e4066Sahrens } 1727fa9e4066Sahrens 1728fa9e4066Sahrens /* 1729fa9e4066Sahrens * Given a property, inherit the value from the parent dataset. 1730fa9e4066Sahrens */ 1731fa9e4066Sahrens int 1732e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) 1733fa9e4066Sahrens { 1734fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1735fa9e4066Sahrens int ret; 1736fa9e4066Sahrens prop_changelist_t *cl; 173799653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 173899653d4eSeschrock char errbuf[1024]; 1739e9dbad6fSeschrock zfs_prop_t prop; 174099653d4eSeschrock 174199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 174299653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1743fa9e4066Sahrens 1744990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1745e9dbad6fSeschrock /* 1746e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1747e9dbad6fSeschrock * small, so just do it here. 1748e9dbad6fSeschrock */ 1749e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1750e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1751e9dbad6fSeschrock "invalid property")); 1752e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1753e9dbad6fSeschrock } 1754e9dbad6fSeschrock 1755e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1756e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1757e9dbad6fSeschrock 1758e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1759e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1760e9dbad6fSeschrock 1761e9dbad6fSeschrock return (0); 1762e9dbad6fSeschrock } 1763e9dbad6fSeschrock 1764fa9e4066Sahrens /* 1765fa9e4066Sahrens * Verify that this property is inheritable. 1766fa9e4066Sahrens */ 176799653d4eSeschrock if (zfs_prop_readonly(prop)) 176899653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1769fa9e4066Sahrens 177099653d4eSeschrock if (!zfs_prop_inheritable(prop)) 177199653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1772fa9e4066Sahrens 1773fa9e4066Sahrens /* 1774fa9e4066Sahrens * Check to see if the value applies to this type 1775fa9e4066Sahrens */ 177699653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 177799653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1778fa9e4066Sahrens 1779bf7c2d40Srm160521 /* 1780bf7c2d40Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 1781bf7c2d40Srm160521 */ 1782bf7c2d40Srm160521 propname = zfs_prop_to_name(prop); 1783fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1784e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1785fa9e4066Sahrens 1786fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1787fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 178899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 178999653d4eSeschrock "dataset is used in a non-global zone")); 179099653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1791fa9e4066Sahrens } 1792fa9e4066Sahrens 1793fa9e4066Sahrens /* 1794fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1795fa9e4066Sahrens */ 1796fa9e4066Sahrens if ((cl = changelist_gather(zhp, prop, 0)) == NULL) 1797fa9e4066Sahrens return (-1); 1798fa9e4066Sahrens 1799fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 180099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 180199653d4eSeschrock "child dataset with inherited mountpoint is used " 180299653d4eSeschrock "in a non-global zone")); 180399653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1804fa9e4066Sahrens goto error; 1805fa9e4066Sahrens } 1806fa9e4066Sahrens 1807fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1808fa9e4066Sahrens goto error; 1809fa9e4066Sahrens 1810e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 181199653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1812fa9e4066Sahrens } else { 1813fa9e4066Sahrens 1814efc555ebSnd150628 if ((ret = changelist_postfix(cl)) != 0) 1815fa9e4066Sahrens goto error; 1816fa9e4066Sahrens 1817fa9e4066Sahrens /* 1818fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1819fa9e4066Sahrens */ 1820fa9e4066Sahrens (void) get_stats(zhp); 1821fa9e4066Sahrens } 1822fa9e4066Sahrens 1823fa9e4066Sahrens error: 1824fa9e4066Sahrens changelist_free(cl); 1825fa9e4066Sahrens return (ret); 1826fa9e4066Sahrens } 1827fa9e4066Sahrens 1828fa9e4066Sahrens /* 18297f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 18307f7322feSeschrock * extract them appropriately. 18317f7322feSeschrock */ 18327f7322feSeschrock static uint64_t 18337f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 18347f7322feSeschrock { 18357f7322feSeschrock nvlist_t *nv; 18367f7322feSeschrock uint64_t value; 18377f7322feSeschrock 1838a2eea2e1Sahrens *source = NULL; 18397f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 18407f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1841990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1842990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 18437f7322feSeschrock } else { 18447f7322feSeschrock value = zfs_prop_default_numeric(prop); 18457f7322feSeschrock *source = ""; 18467f7322feSeschrock } 18477f7322feSeschrock 18487f7322feSeschrock return (value); 18497f7322feSeschrock } 18507f7322feSeschrock 18517f7322feSeschrock static char * 18527f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 18537f7322feSeschrock { 18547f7322feSeschrock nvlist_t *nv; 18557f7322feSeschrock char *value; 18567f7322feSeschrock 1857a2eea2e1Sahrens *source = NULL; 18587f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 18597f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1860990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1861990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 18627f7322feSeschrock } else { 18637f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 18647f7322feSeschrock value = ""; 18657f7322feSeschrock *source = ""; 18667f7322feSeschrock } 18677f7322feSeschrock 18687f7322feSeschrock return (value); 18697f7322feSeschrock } 18707f7322feSeschrock 18717f7322feSeschrock /* 1872fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1873fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 1874fa9e4066Sahrens * 1875fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1876fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1877fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 1878fa9e4066Sahrens * the source "temporary". 1879fa9e4066Sahrens */ 188099653d4eSeschrock static int 1881990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 188299653d4eSeschrock char **source, uint64_t *val) 1883fa9e4066Sahrens { 1884bd00f61bSrm160521 zfs_cmd_t zc = { 0 }; 188596510749Stimh nvlist_t *zplprops = NULL; 1886fa9e4066Sahrens struct mnttab mnt; 18873ccfa83cSahrens char *mntopt_on = NULL; 18883ccfa83cSahrens char *mntopt_off = NULL; 1889fa9e4066Sahrens 1890fa9e4066Sahrens *source = NULL; 1891fa9e4066Sahrens 18923ccfa83cSahrens switch (prop) { 18933ccfa83cSahrens case ZFS_PROP_ATIME: 18943ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 18953ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 18963ccfa83cSahrens break; 18973ccfa83cSahrens 18983ccfa83cSahrens case ZFS_PROP_DEVICES: 18993ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 19003ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 19013ccfa83cSahrens break; 19023ccfa83cSahrens 19033ccfa83cSahrens case ZFS_PROP_EXEC: 19043ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 19053ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 19063ccfa83cSahrens break; 19073ccfa83cSahrens 19083ccfa83cSahrens case ZFS_PROP_READONLY: 19093ccfa83cSahrens mntopt_on = MNTOPT_RO; 19103ccfa83cSahrens mntopt_off = MNTOPT_RW; 19113ccfa83cSahrens break; 19123ccfa83cSahrens 19133ccfa83cSahrens case ZFS_PROP_SETUID: 19143ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 19153ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 19163ccfa83cSahrens break; 19173ccfa83cSahrens 19183ccfa83cSahrens case ZFS_PROP_XATTR: 19193ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 19203ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 19213ccfa83cSahrens break; 1922da6c28aaSamw 1923da6c28aaSamw case ZFS_PROP_NBMAND: 1924da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 1925da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 1926da6c28aaSamw break; 19273ccfa83cSahrens } 19283ccfa83cSahrens 19293bb79becSeschrock /* 19303bb79becSeschrock * Because looking up the mount options is potentially expensive 19313bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 19323bb79becSeschrock * we're looking up a property which requires its presence. 19333bb79becSeschrock */ 19343bb79becSeschrock if (!zhp->zfs_mntcheck && 19353ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 19363ccfa83cSahrens struct mnttab entry, search = { 0 }; 19373ccfa83cSahrens FILE *mnttab = zhp->zfs_hdl->libzfs_mnttab; 19383bb79becSeschrock 19393bb79becSeschrock search.mnt_special = (char *)zhp->zfs_name; 19403bb79becSeschrock search.mnt_fstype = MNTTYPE_ZFS; 19413ccfa83cSahrens rewind(mnttab); 19423bb79becSeschrock 19433ccfa83cSahrens if (getmntany(mnttab, &entry, &search) == 0) { 19443ccfa83cSahrens zhp->zfs_mntopts = zfs_strdup(zhp->zfs_hdl, 19453ccfa83cSahrens entry.mnt_mntopts); 19463ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 19473bb79becSeschrock return (-1); 19483ccfa83cSahrens } 19493bb79becSeschrock 19503bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 19513bb79becSeschrock } 19523bb79becSeschrock 1953fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 1954fa9e4066Sahrens mnt.mnt_mntopts = ""; 1955fa9e4066Sahrens else 1956fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1957fa9e4066Sahrens 1958fa9e4066Sahrens switch (prop) { 1959fa9e4066Sahrens case ZFS_PROP_ATIME: 1960fa9e4066Sahrens case ZFS_PROP_DEVICES: 1961fa9e4066Sahrens case ZFS_PROP_EXEC: 19623ccfa83cSahrens case ZFS_PROP_READONLY: 19633ccfa83cSahrens case ZFS_PROP_SETUID: 19643ccfa83cSahrens case ZFS_PROP_XATTR: 1965da6c28aaSamw case ZFS_PROP_NBMAND: 196699653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1967fa9e4066Sahrens 19683ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 196999653d4eSeschrock *val = B_TRUE; 1970fa9e4066Sahrens if (src) 1971990b4856Slling *src = ZPROP_SRC_TEMPORARY; 19723ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 197399653d4eSeschrock *val = B_FALSE; 1974fa9e4066Sahrens if (src) 1975990b4856Slling *src = ZPROP_SRC_TEMPORARY; 1976fa9e4066Sahrens } 197799653d4eSeschrock break; 1978fa9e4066Sahrens 19793ccfa83cSahrens case ZFS_PROP_CANMOUNT: 198099653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 1981fda77a98Srm160521 if (*val == 0) 1982fda77a98Srm160521 *source = zhp->zfs_name; 1983fda77a98Srm160521 else 1984fda77a98Srm160521 *source = ""; /* default */ 198599653d4eSeschrock break; 1986fa9e4066Sahrens 1987fa9e4066Sahrens case ZFS_PROP_QUOTA: 1988a9799022Sck153898 case ZFS_PROP_REFQUOTA: 1989fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1990a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 1991a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 1992a2eea2e1Sahrens if (*val == 0) 1993fa9e4066Sahrens *source = ""; /* default */ 1994fa9e4066Sahrens else 1995fa9e4066Sahrens *source = zhp->zfs_name; 199699653d4eSeschrock break; 1997fa9e4066Sahrens 1998fa9e4066Sahrens case ZFS_PROP_MOUNTED: 199999653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 200099653d4eSeschrock break; 2001fa9e4066Sahrens 200239c23413Seschrock case ZFS_PROP_NUMCLONES: 200339c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 200439c23413Seschrock break; 200539c23413Seschrock 2006bd00f61bSrm160521 case ZFS_PROP_VERSION: 2007de8267e0Stimh case ZFS_PROP_NORMALIZE: 2008de8267e0Stimh case ZFS_PROP_UTF8ONLY: 2009de8267e0Stimh case ZFS_PROP_CASE: 2010de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 2011de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 20123d7934e1Srm160521 return (-1); 2013bd00f61bSrm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2014de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 2015de8267e0Stimh zcmd_free_nvlists(&zc); 2016bd00f61bSrm160521 zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 2017de8267e0Stimh "unable to get %s property"), 2018de8267e0Stimh zfs_prop_to_name(prop)); 2019bd00f61bSrm160521 return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, 2020bd00f61bSrm160521 dgettext(TEXT_DOMAIN, "internal error"))); 2021bd00f61bSrm160521 } 2022de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 2023de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 2024de8267e0Stimh val) != 0) { 2025de8267e0Stimh zcmd_free_nvlists(&zc); 2026de8267e0Stimh zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 2027de8267e0Stimh "unable to get %s property"), 2028de8267e0Stimh zfs_prop_to_name(prop)); 2029de8267e0Stimh return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM, 2030de8267e0Stimh dgettext(TEXT_DOMAIN, "internal error"))); 2031de8267e0Stimh } 203296510749Stimh if (zplprops) 203396510749Stimh nvlist_free(zplprops); 2034de8267e0Stimh zcmd_free_nvlists(&zc); 2035bd00f61bSrm160521 break; 2036bd00f61bSrm160521 2037fa9e4066Sahrens default: 2038e7437265Sahrens switch (zfs_prop_get_type(prop)) { 203991ebeef5Sahrens case PROP_TYPE_NUMBER: 204091ebeef5Sahrens case PROP_TYPE_INDEX: 2041e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 2042e7437265Sahrens break; 2043e7437265Sahrens 204491ebeef5Sahrens case PROP_TYPE_STRING: 2045e7437265Sahrens default: 204699653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 204799653d4eSeschrock "cannot get non-numeric property")); 204899653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 204999653d4eSeschrock dgettext(TEXT_DOMAIN, "internal error"))); 2050fa9e4066Sahrens } 2051e7437265Sahrens } 2052fa9e4066Sahrens 2053fa9e4066Sahrens return (0); 2054fa9e4066Sahrens } 2055fa9e4066Sahrens 2056fa9e4066Sahrens /* 2057fa9e4066Sahrens * Calculate the source type, given the raw source string. 2058fa9e4066Sahrens */ 2059fa9e4066Sahrens static void 2060990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 2061fa9e4066Sahrens char *statbuf, size_t statlen) 2062fa9e4066Sahrens { 2063990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 2064fa9e4066Sahrens return; 2065fa9e4066Sahrens 2066fa9e4066Sahrens if (source == NULL) { 2067990b4856Slling *srctype = ZPROP_SRC_NONE; 2068fa9e4066Sahrens } else if (source[0] == '\0') { 2069990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 2070fa9e4066Sahrens } else { 2071fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 2072990b4856Slling *srctype = ZPROP_SRC_LOCAL; 2073fa9e4066Sahrens } else { 2074fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 2075990b4856Slling *srctype = ZPROP_SRC_INHERITED; 2076fa9e4066Sahrens } 2077fa9e4066Sahrens } 2078fa9e4066Sahrens 2079fa9e4066Sahrens } 2080fa9e4066Sahrens 2081fa9e4066Sahrens /* 2082fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 2083fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 2084fa9e4066Sahrens * human-readable form. 2085fa9e4066Sahrens * 2086fa9e4066Sahrens * Returns 0 on success, or -1 on error. 2087fa9e4066Sahrens */ 2088fa9e4066Sahrens int 2089fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 2090990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 2091fa9e4066Sahrens { 2092fa9e4066Sahrens char *source = NULL; 2093fa9e4066Sahrens uint64_t val; 2094fa9e4066Sahrens char *str; 2095fa9e4066Sahrens const char *root; 2096e9dbad6fSeschrock const char *strval; 2097fa9e4066Sahrens 2098fa9e4066Sahrens /* 2099fa9e4066Sahrens * Check to see if this property applies to our object 2100fa9e4066Sahrens */ 2101fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 2102fa9e4066Sahrens return (-1); 2103fa9e4066Sahrens 2104fa9e4066Sahrens if (src) 2105990b4856Slling *src = ZPROP_SRC_NONE; 2106fa9e4066Sahrens 2107fa9e4066Sahrens switch (prop) { 2108fa9e4066Sahrens case ZFS_PROP_CREATION: 2109fa9e4066Sahrens /* 2110fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 2111fa9e4066Sahrens * this into a string unless 'literal' is specified. 2112fa9e4066Sahrens */ 2113fa9e4066Sahrens { 2114a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 2115a2eea2e1Sahrens time_t time = (time_t)val; 2116fa9e4066Sahrens struct tm t; 2117fa9e4066Sahrens 2118fa9e4066Sahrens if (literal || 2119fa9e4066Sahrens localtime_r(&time, &t) == NULL || 2120fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 2121fa9e4066Sahrens &t) == 0) 2122a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 2123fa9e4066Sahrens } 2124fa9e4066Sahrens break; 2125fa9e4066Sahrens 2126fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 2127fa9e4066Sahrens /* 2128fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 2129fa9e4066Sahrens * 2130fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 2131fa9e4066Sahrens * - for default mountpoints, construct it as /zfs/<dataset> 2132fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 2133fa9e4066Sahrens * after our ancestor and append it to the inherited value. 2134fa9e4066Sahrens * 2135fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 2136fa9e4066Sahrens * root to any values we return. 2137fa9e4066Sahrens */ 2138ea8dc4b6Seschrock root = zhp->zfs_root; 21397f7322feSeschrock str = getprop_string(zhp, prop, &source); 2140fa9e4066Sahrens 21417f7322feSeschrock if (str[0] == '\0') { 2142fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s/zfs/%s", 2143fa9e4066Sahrens root, zhp->zfs_name); 21447f7322feSeschrock } else if (str[0] == '/') { 21457f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 2146fa9e4066Sahrens 2147fa9e4066Sahrens if (relpath[0] == '/') 2148fa9e4066Sahrens relpath++; 21497f7322feSeschrock if (str[1] == '\0') 21507f7322feSeschrock str++; 2151fa9e4066Sahrens 2152fa9e4066Sahrens if (relpath[0] == '\0') 2153fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 21547f7322feSeschrock root, str); 2155fa9e4066Sahrens else 2156fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 21577f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 2158fa9e4066Sahrens relpath); 2159fa9e4066Sahrens } else { 2160fa9e4066Sahrens /* 'legacy' or 'none' */ 21617f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 2162fa9e4066Sahrens } 2163fa9e4066Sahrens 2164fa9e4066Sahrens break; 2165fa9e4066Sahrens 2166fa9e4066Sahrens case ZFS_PROP_ORIGIN: 2167a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 2168fa9e4066Sahrens proplen); 2169fa9e4066Sahrens /* 2170fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 2171fa9e4066Sahrens * it doesn't apply to this dataset. 2172fa9e4066Sahrens */ 2173fa9e4066Sahrens if (propbuf[0] == '\0') 2174fa9e4066Sahrens return (-1); 2175fa9e4066Sahrens break; 2176fa9e4066Sahrens 2177fa9e4066Sahrens case ZFS_PROP_QUOTA: 2178a9799022Sck153898 case ZFS_PROP_REFQUOTA: 2179fa9e4066Sahrens case ZFS_PROP_RESERVATION: 2180a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 2181a9799022Sck153898 218299653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 218399653d4eSeschrock return (-1); 2184fa9e4066Sahrens 2185fa9e4066Sahrens /* 2186fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 2187fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 2188fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 2189fa9e4066Sahrens * that its set locally. 2190fa9e4066Sahrens */ 2191fa9e4066Sahrens if (val == 0) { 2192fa9e4066Sahrens if (literal) 2193fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 2194fa9e4066Sahrens else 2195fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 2196fa9e4066Sahrens } else { 2197fa9e4066Sahrens if (literal) 21985ad82045Snd150628 (void) snprintf(propbuf, proplen, "%llu", 21995ad82045Snd150628 (u_longlong_t)val); 2200fa9e4066Sahrens else 2201fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 2202fa9e4066Sahrens } 2203fa9e4066Sahrens break; 2204fa9e4066Sahrens 2205fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 220699653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 220799653d4eSeschrock return (-1); 22085ad82045Snd150628 (void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t) 22095ad82045Snd150628 val / 100, (longlong_t)val % 100); 2210fa9e4066Sahrens break; 2211fa9e4066Sahrens 2212fa9e4066Sahrens case ZFS_PROP_TYPE: 2213fa9e4066Sahrens switch (zhp->zfs_type) { 2214fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 2215fa9e4066Sahrens str = "filesystem"; 2216fa9e4066Sahrens break; 2217fa9e4066Sahrens case ZFS_TYPE_VOLUME: 2218fa9e4066Sahrens str = "volume"; 2219fa9e4066Sahrens break; 2220fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 2221fa9e4066Sahrens str = "snapshot"; 2222fa9e4066Sahrens break; 2223fa9e4066Sahrens default: 222499653d4eSeschrock abort(); 2225fa9e4066Sahrens } 2226fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2227fa9e4066Sahrens break; 2228fa9e4066Sahrens 2229fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2230fa9e4066Sahrens /* 2231fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2232fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2233fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2234fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2235fa9e4066Sahrens */ 223699653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 223799653d4eSeschrock src, &source, &val) != 0) 223899653d4eSeschrock return (-1); 223999653d4eSeschrock if (val) 2240fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2241fa9e4066Sahrens else 2242fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2243fa9e4066Sahrens break; 2244fa9e4066Sahrens 2245fa9e4066Sahrens case ZFS_PROP_NAME: 2246fa9e4066Sahrens /* 2247fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2248fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2249fa9e4066Sahrens * consumers. 2250fa9e4066Sahrens */ 2251fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2252fa9e4066Sahrens break; 2253fa9e4066Sahrens 2254fa9e4066Sahrens default: 2255e7437265Sahrens switch (zfs_prop_get_type(prop)) { 225691ebeef5Sahrens case PROP_TYPE_NUMBER: 2257e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2258e7437265Sahrens &source, &val) != 0) 2259e7437265Sahrens return (-1); 2260e7437265Sahrens if (literal) 2261e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2262e7437265Sahrens (u_longlong_t)val); 2263e7437265Sahrens else 2264e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2265e7437265Sahrens break; 2266e7437265Sahrens 226791ebeef5Sahrens case PROP_TYPE_STRING: 2268e7437265Sahrens (void) strlcpy(propbuf, 2269e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 2270e7437265Sahrens break; 2271e7437265Sahrens 227291ebeef5Sahrens case PROP_TYPE_INDEX: 2273fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2274fe192f76Sahrens &source, &val) != 0) 2275fe192f76Sahrens return (-1); 2276fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2277e7437265Sahrens return (-1); 2278e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2279e7437265Sahrens break; 2280e7437265Sahrens 2281e7437265Sahrens default: 228299653d4eSeschrock abort(); 2283fa9e4066Sahrens } 2284e7437265Sahrens } 2285fa9e4066Sahrens 2286fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2287fa9e4066Sahrens 2288fa9e4066Sahrens return (0); 2289fa9e4066Sahrens } 2290fa9e4066Sahrens 2291fa9e4066Sahrens /* 2292fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2293fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2294fa9e4066Sahrens * hard-coded property types. 2295fa9e4066Sahrens */ 2296fa9e4066Sahrens uint64_t 2297fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2298fa9e4066Sahrens { 2299fa9e4066Sahrens char *source; 230099653d4eSeschrock uint64_t val; 2301fa9e4066Sahrens 23023cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 230399653d4eSeschrock 230499653d4eSeschrock return (val); 2305fa9e4066Sahrens } 2306fa9e4066Sahrens 23077b97dc1aSrm160521 int 23087b97dc1aSrm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 23097b97dc1aSrm160521 { 23107b97dc1aSrm160521 char buf[64]; 23117b97dc1aSrm160521 23127b97dc1aSrm160521 zfs_nicenum(val, buf, sizeof (buf)); 23137b97dc1aSrm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 23147b97dc1aSrm160521 } 23157b97dc1aSrm160521 2316fa9e4066Sahrens /* 2317fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2318fa9e4066Sahrens */ 2319fa9e4066Sahrens int 2320fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2321990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2322fa9e4066Sahrens { 2323fa9e4066Sahrens char *source; 2324fa9e4066Sahrens 2325fa9e4066Sahrens /* 2326fa9e4066Sahrens * Check to see if this property applies to our object 2327fa9e4066Sahrens */ 2328e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2329ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 233099653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 233199653d4eSeschrock zfs_prop_to_name(prop))); 2332e45ce728Sahrens } 2333fa9e4066Sahrens 2334fa9e4066Sahrens if (src) 2335990b4856Slling *src = ZPROP_SRC_NONE; 2336fa9e4066Sahrens 233799653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 233899653d4eSeschrock return (-1); 2339fa9e4066Sahrens 2340fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2341fa9e4066Sahrens 2342fa9e4066Sahrens return (0); 2343fa9e4066Sahrens } 2344fa9e4066Sahrens 2345fa9e4066Sahrens /* 2346fa9e4066Sahrens * Returns the name of the given zfs handle. 2347fa9e4066Sahrens */ 2348fa9e4066Sahrens const char * 2349fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2350fa9e4066Sahrens { 2351fa9e4066Sahrens return (zhp->zfs_name); 2352fa9e4066Sahrens } 2353fa9e4066Sahrens 2354fa9e4066Sahrens /* 2355fa9e4066Sahrens * Returns the type of the given zfs handle. 2356fa9e4066Sahrens */ 2357fa9e4066Sahrens zfs_type_t 2358fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2359fa9e4066Sahrens { 2360fa9e4066Sahrens return (zhp->zfs_type); 2361fa9e4066Sahrens } 2362fa9e4066Sahrens 2363fa9e4066Sahrens /* 23647f7322feSeschrock * Iterate over all child filesystems 2365fa9e4066Sahrens */ 2366fa9e4066Sahrens int 23677f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2368fa9e4066Sahrens { 2369fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2370fa9e4066Sahrens zfs_handle_t *nzhp; 2371fa9e4066Sahrens int ret; 2372fa9e4066Sahrens 23733cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 23743cb34c60Sahrens return (0); 23753cb34c60Sahrens 2376fa9e4066Sahrens for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 237799653d4eSeschrock ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; 2378fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { 2379fa9e4066Sahrens /* 2380fa9e4066Sahrens * Ignore private dataset names. 2381fa9e4066Sahrens */ 2382fa9e4066Sahrens if (dataset_name_hidden(zc.zc_name)) 2383fa9e4066Sahrens continue; 2384fa9e4066Sahrens 2385fa9e4066Sahrens /* 2386fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2387fa9e4066Sahrens * that the pool has since been removed. 2388fa9e4066Sahrens */ 238999653d4eSeschrock if ((nzhp = make_dataset_handle(zhp->zfs_hdl, 239099653d4eSeschrock zc.zc_name)) == NULL) 2391fa9e4066Sahrens continue; 2392fa9e4066Sahrens 2393fa9e4066Sahrens if ((ret = func(nzhp, data)) != 0) 2394fa9e4066Sahrens return (ret); 2395fa9e4066Sahrens } 2396fa9e4066Sahrens 2397fa9e4066Sahrens /* 2398fa9e4066Sahrens * An errno value of ESRCH indicates normal completion. If ENOENT is 2399fa9e4066Sahrens * returned, then the underlying dataset has been removed since we 2400fa9e4066Sahrens * obtained the handle. 2401fa9e4066Sahrens */ 2402fa9e4066Sahrens if (errno != ESRCH && errno != ENOENT) 240399653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 240499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); 2405fa9e4066Sahrens 24067f7322feSeschrock return (0); 24077f7322feSeschrock } 24087f7322feSeschrock 24097f7322feSeschrock /* 24107f7322feSeschrock * Iterate over all snapshots 24117f7322feSeschrock */ 24127f7322feSeschrock int 24137f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24147f7322feSeschrock { 24157f7322feSeschrock zfs_cmd_t zc = { 0 }; 24167f7322feSeschrock zfs_handle_t *nzhp; 24177f7322feSeschrock int ret; 2418fa9e4066Sahrens 24193cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 24203cb34c60Sahrens return (0); 24213cb34c60Sahrens 2422fa9e4066Sahrens for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 242399653d4eSeschrock ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, 242499653d4eSeschrock &zc) == 0; 2425fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { 2426fa9e4066Sahrens 242799653d4eSeschrock if ((nzhp = make_dataset_handle(zhp->zfs_hdl, 242899653d4eSeschrock zc.zc_name)) == NULL) 2429fa9e4066Sahrens continue; 2430fa9e4066Sahrens 2431fa9e4066Sahrens if ((ret = func(nzhp, data)) != 0) 2432fa9e4066Sahrens return (ret); 2433fa9e4066Sahrens } 2434fa9e4066Sahrens 2435fa9e4066Sahrens /* 2436fa9e4066Sahrens * An errno value of ESRCH indicates normal completion. If ENOENT is 2437fa9e4066Sahrens * returned, then the underlying dataset has been removed since we 2438fa9e4066Sahrens * obtained the handle. Silently ignore this case, and return success. 2439fa9e4066Sahrens */ 2440fa9e4066Sahrens if (errno != ESRCH && errno != ENOENT) 244199653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 244299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); 2443fa9e4066Sahrens 2444fa9e4066Sahrens return (0); 2445fa9e4066Sahrens } 2446fa9e4066Sahrens 2447fa9e4066Sahrens /* 24487f7322feSeschrock * Iterate over all children, snapshots and filesystems 24497f7322feSeschrock */ 24507f7322feSeschrock int 24517f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 24527f7322feSeschrock { 24537f7322feSeschrock int ret; 24547f7322feSeschrock 24557f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 24567f7322feSeschrock return (ret); 24577f7322feSeschrock 24587f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 24597f7322feSeschrock } 24607f7322feSeschrock 24617f7322feSeschrock /* 2462fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2463fa9e4066Sahrens * Can return NULL if this is a pool. 2464fa9e4066Sahrens */ 2465fa9e4066Sahrens static int 2466fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2467fa9e4066Sahrens { 2468fa9e4066Sahrens char *loc; 2469fa9e4066Sahrens 2470fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2471fa9e4066Sahrens return (-1); 2472fa9e4066Sahrens 2473fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2474fa9e4066Sahrens buf[loc - path] = '\0'; 2475fa9e4066Sahrens 2476fa9e4066Sahrens return (0); 2477fa9e4066Sahrens } 2478fa9e4066Sahrens 2479fa9e4066Sahrens /* 24807f1f55eaSvb160487 * If accept_ancestor is false, then check to make sure that the given path has 24817f1f55eaSvb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 24827f1f55eaSvb160487 * closest existing ancestor for the given path. In prefixlen return the 24837f1f55eaSvb160487 * length of already existing prefix of the given path. We also fetch the 24847f1f55eaSvb160487 * 'zoned' property, which is used to validate property settings when creating 24857f1f55eaSvb160487 * new datasets. 2486fa9e4066Sahrens */ 2487fa9e4066Sahrens static int 24887f1f55eaSvb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 24897f1f55eaSvb160487 boolean_t accept_ancestor, int *prefixlen) 2490fa9e4066Sahrens { 2491fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2492fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2493fa9e4066Sahrens char *slash; 24947f7322feSeschrock zfs_handle_t *zhp; 249599653d4eSeschrock char errbuf[1024]; 249699653d4eSeschrock 249799653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), "cannot create '%s'", 249899653d4eSeschrock path); 2499fa9e4066Sahrens 2500fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2501fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 250299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 250399653d4eSeschrock "missing dataset name")); 250499653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2505fa9e4066Sahrens } 2506fa9e4066Sahrens 2507fa9e4066Sahrens /* check to see if the pool exists */ 2508fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2509fa9e4066Sahrens slash = parent + strlen(parent); 2510fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2511fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 251299653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2513fa9e4066Sahrens errno == ENOENT) { 251499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 251599653d4eSeschrock "no such pool '%s'"), zc.zc_name); 251699653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2517fa9e4066Sahrens } 2518fa9e4066Sahrens 2519fa9e4066Sahrens /* check to see if the parent dataset exists */ 25207f1f55eaSvb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 25217f1f55eaSvb160487 if (errno == ENOENT && accept_ancestor) { 25227f1f55eaSvb160487 /* 25237f1f55eaSvb160487 * Go deeper to find an ancestor, give up on top level. 25247f1f55eaSvb160487 */ 25257f1f55eaSvb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 25267f1f55eaSvb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25277f1f55eaSvb160487 "no such pool '%s'"), zc.zc_name); 25287f1f55eaSvb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 25297f1f55eaSvb160487 } 25307f1f55eaSvb160487 } else if (errno == ENOENT) { 253199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 253299653d4eSeschrock "parent does not exist")); 253399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 25347f1f55eaSvb160487 } else 253599653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2536fa9e4066Sahrens } 2537fa9e4066Sahrens 2538e9dbad6fSeschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2539fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2540e9dbad6fSeschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 254199653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 25427f7322feSeschrock zfs_close(zhp); 2543fa9e4066Sahrens return (-1); 2544fa9e4066Sahrens } 2545fa9e4066Sahrens 2546fa9e4066Sahrens /* make sure parent is a filesystem */ 25477f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 254899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 254999653d4eSeschrock "parent is not a filesystem")); 255099653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 25517f7322feSeschrock zfs_close(zhp); 2552fa9e4066Sahrens return (-1); 2553fa9e4066Sahrens } 2554fa9e4066Sahrens 25557f7322feSeschrock zfs_close(zhp); 25567f1f55eaSvb160487 if (prefixlen != NULL) 25577f1f55eaSvb160487 *prefixlen = strlen(parent); 25587f1f55eaSvb160487 return (0); 25597f1f55eaSvb160487 } 25607f1f55eaSvb160487 25617f1f55eaSvb160487 /* 25627f1f55eaSvb160487 * Finds whether the dataset of the given type(s) exists. 25637f1f55eaSvb160487 */ 25647f1f55eaSvb160487 boolean_t 25657f1f55eaSvb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 25667f1f55eaSvb160487 { 25677f1f55eaSvb160487 zfs_handle_t *zhp; 25687f1f55eaSvb160487 2569f18faf3fSek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 25707f1f55eaSvb160487 return (B_FALSE); 25717f1f55eaSvb160487 25727f1f55eaSvb160487 /* 25737f1f55eaSvb160487 * Try to get stats for the dataset, which will tell us if it exists. 25747f1f55eaSvb160487 */ 25757f1f55eaSvb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 25767f1f55eaSvb160487 int ds_type = zhp->zfs_type; 25777f1f55eaSvb160487 25787f1f55eaSvb160487 zfs_close(zhp); 25797f1f55eaSvb160487 if (types & ds_type) 25807f1f55eaSvb160487 return (B_TRUE); 25817f1f55eaSvb160487 } 25827f1f55eaSvb160487 return (B_FALSE); 25837f1f55eaSvb160487 } 25847f1f55eaSvb160487 25857f1f55eaSvb160487 /* 25863cb34c60Sahrens * Given a path to 'target', create all the ancestors between 25873cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 25883cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 25893cb34c60Sahrens */ 25903cb34c60Sahrens int 25913cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 25923cb34c60Sahrens { 25933cb34c60Sahrens zfs_handle_t *h; 25943cb34c60Sahrens char *cp; 25953cb34c60Sahrens const char *opname; 25963cb34c60Sahrens 25973cb34c60Sahrens /* make sure prefix exists */ 25983cb34c60Sahrens cp = target + prefixlen; 25993cb34c60Sahrens if (*cp != '/') { 26003cb34c60Sahrens assert(strchr(cp, '/') == NULL); 26013cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26023cb34c60Sahrens } else { 26033cb34c60Sahrens *cp = '\0'; 26043cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26053cb34c60Sahrens *cp = '/'; 26063cb34c60Sahrens } 26073cb34c60Sahrens if (h == NULL) 26083cb34c60Sahrens return (-1); 26093cb34c60Sahrens zfs_close(h); 26103cb34c60Sahrens 26113cb34c60Sahrens /* 26123cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 26133cb34c60Sahrens * up to the prefixlen-long one. 26143cb34c60Sahrens */ 26153cb34c60Sahrens for (cp = target + prefixlen + 1; 26163cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 26173cb34c60Sahrens char *logstr; 26183cb34c60Sahrens 26193cb34c60Sahrens *cp = '\0'; 26203cb34c60Sahrens 26213cb34c60Sahrens h = make_dataset_handle(hdl, target); 26223cb34c60Sahrens if (h) { 26233cb34c60Sahrens /* it already exists, nothing to do here */ 26243cb34c60Sahrens zfs_close(h); 26253cb34c60Sahrens continue; 26263cb34c60Sahrens } 26273cb34c60Sahrens 26283cb34c60Sahrens logstr = hdl->libzfs_log_str; 26293cb34c60Sahrens hdl->libzfs_log_str = NULL; 26303cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 26313cb34c60Sahrens NULL) != 0) { 26323cb34c60Sahrens hdl->libzfs_log_str = logstr; 26333cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 26343cb34c60Sahrens goto ancestorerr; 26353cb34c60Sahrens } 26363cb34c60Sahrens 26373cb34c60Sahrens hdl->libzfs_log_str = logstr; 26383cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 26393cb34c60Sahrens if (h == NULL) { 26403cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 26413cb34c60Sahrens goto ancestorerr; 26423cb34c60Sahrens } 26433cb34c60Sahrens 26443cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 26453cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 26463cb34c60Sahrens goto ancestorerr; 26473cb34c60Sahrens } 26483cb34c60Sahrens 26493cb34c60Sahrens if (zfs_share(h) != 0) { 26503cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 26513cb34c60Sahrens goto ancestorerr; 26523cb34c60Sahrens } 26533cb34c60Sahrens 26543cb34c60Sahrens zfs_close(h); 26553cb34c60Sahrens } 26563cb34c60Sahrens 26573cb34c60Sahrens return (0); 26583cb34c60Sahrens 26593cb34c60Sahrens ancestorerr: 26603cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26613cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 26623cb34c60Sahrens return (-1); 26633cb34c60Sahrens } 26643cb34c60Sahrens 26653cb34c60Sahrens /* 26667f1f55eaSvb160487 * Creates non-existing ancestors of the given path. 26677f1f55eaSvb160487 */ 26687f1f55eaSvb160487 int 26697f1f55eaSvb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 26707f1f55eaSvb160487 { 26717f1f55eaSvb160487 int prefix; 26727f1f55eaSvb160487 uint64_t zoned; 26737f1f55eaSvb160487 char *path_copy; 26747f1f55eaSvb160487 int rc; 26757f1f55eaSvb160487 26767f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 26777f1f55eaSvb160487 return (-1); 26787f1f55eaSvb160487 26797f1f55eaSvb160487 if ((path_copy = strdup(path)) != NULL) { 26807f1f55eaSvb160487 rc = create_parents(hdl, path_copy, prefix); 26817f1f55eaSvb160487 free(path_copy); 26827f1f55eaSvb160487 } 26837f1f55eaSvb160487 if (path_copy == NULL || rc != 0) 26847f1f55eaSvb160487 return (-1); 26857f1f55eaSvb160487 2686fa9e4066Sahrens return (0); 2687fa9e4066Sahrens } 2688fa9e4066Sahrens 2689fa9e4066Sahrens /* 2690e9dbad6fSeschrock * Create a new filesystem or volume. 2691fa9e4066Sahrens */ 2692fa9e4066Sahrens int 269399653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2694e9dbad6fSeschrock nvlist_t *props) 2695fa9e4066Sahrens { 2696fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2697fa9e4066Sahrens int ret; 2698fa9e4066Sahrens uint64_t size = 0; 2699fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 270099653d4eSeschrock char errbuf[1024]; 2701e9dbad6fSeschrock uint64_t zoned; 270299653d4eSeschrock 270399653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 270499653d4eSeschrock "cannot create '%s'"), path); 2705fa9e4066Sahrens 2706fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2707f18faf3fSek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 270899653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2709fa9e4066Sahrens 2710fa9e4066Sahrens /* validate parents exist */ 27117f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2712fa9e4066Sahrens return (-1); 2713fa9e4066Sahrens 2714fa9e4066Sahrens /* 2715fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2716fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2717fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2718fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2719fa9e4066Sahrens * first try to see if the dataset exists. 2720fa9e4066Sahrens */ 2721fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2722990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 272399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 272499653d4eSeschrock "dataset already exists")); 272599653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2726fa9e4066Sahrens } 2727fa9e4066Sahrens 2728fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2729fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2730fa9e4066Sahrens else 2731fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2732fa9e4066Sahrens 2733990b4856Slling if (props && (props = zfs_validate_properties(hdl, type, props, 2734b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2735e9dbad6fSeschrock return (-1); 2736e9dbad6fSeschrock 2737fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 27385c5460e9Seschrock /* 27395c5460e9Seschrock * If we are creating a volume, the size and block size must 27405c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 27415c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 27425c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 27435c5460e9Seschrock * zero. 27445c5460e9Seschrock */ 2745e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2746e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2747e9dbad6fSeschrock nvlist_free(props); 274899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2749e9dbad6fSeschrock "missing volume size")); 2750e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2751fa9e4066Sahrens } 2752fa9e4066Sahrens 2753e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2754e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2755e9dbad6fSeschrock &blocksize)) != 0) { 2756e9dbad6fSeschrock if (ret == ENOENT) { 2757e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2758e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2759e9dbad6fSeschrock } else { 2760e9dbad6fSeschrock nvlist_free(props); 276199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2762e9dbad6fSeschrock "missing volume block size")); 2763e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2764e9dbad6fSeschrock } 2765e9dbad6fSeschrock } 2766e9dbad6fSeschrock 2767e9dbad6fSeschrock if (size == 0) { 2768e9dbad6fSeschrock nvlist_free(props); 2769e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2770e9dbad6fSeschrock "volume size cannot be zero")); 2771e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 27725c5460e9Seschrock } 27735c5460e9Seschrock 27745c5460e9Seschrock if (size % blocksize != 0) { 2775e9dbad6fSeschrock nvlist_free(props); 277699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2777e9dbad6fSeschrock "volume size must be a multiple of volume block " 2778e9dbad6fSeschrock "size")); 2779e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2780e9dbad6fSeschrock } 27815c5460e9Seschrock } 27825c5460e9Seschrock 2783990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2784e9dbad6fSeschrock return (-1); 2785e9dbad6fSeschrock nvlist_free(props); 2786fa9e4066Sahrens 2787fa9e4066Sahrens /* create the dataset */ 2788ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2789fa9e4066Sahrens 2790b1b8ab34Slling if (ret == 0 && type == ZFS_TYPE_VOLUME) { 279199653d4eSeschrock ret = zvol_create_link(hdl, path); 2792b1b8ab34Slling if (ret) { 2793b1b8ab34Slling (void) zfs_standard_error(hdl, errno, 2794b1b8ab34Slling dgettext(TEXT_DOMAIN, 2795b1b8ab34Slling "Volume successfully created, but device links " 2796b1b8ab34Slling "were not created")); 2797b1b8ab34Slling zcmd_free_nvlists(&zc); 2798b1b8ab34Slling return (-1); 2799b1b8ab34Slling } 2800b1b8ab34Slling } 2801fa9e4066Sahrens 2802e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2803e9dbad6fSeschrock 2804fa9e4066Sahrens /* check for failure */ 2805fa9e4066Sahrens if (ret != 0) { 2806fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2807fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2808fa9e4066Sahrens 2809fa9e4066Sahrens switch (errno) { 2810fa9e4066Sahrens case ENOENT: 281199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 281299653d4eSeschrock "no such parent '%s'"), parent); 281399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2814fa9e4066Sahrens 2815fa9e4066Sahrens case EINVAL: 281699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2817d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 281899653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2819fa9e4066Sahrens 2820fa9e4066Sahrens case EDOM: 282199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2822e9dbad6fSeschrock "volume block size must be power of 2 from " 2823e9dbad6fSeschrock "%u to %uk"), 2824fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2825fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 282699653d4eSeschrock 2827e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 282899653d4eSeschrock 282940feaa91Sahrens case ENOTSUP: 283040feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 283140feaa91Sahrens "pool must be upgraded to set this " 283240feaa91Sahrens "property or value")); 283340feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 283440feaa91Sahrens 2835fa9e4066Sahrens #ifdef _ILP32 2836fa9e4066Sahrens case EOVERFLOW: 2837fa9e4066Sahrens /* 2838fa9e4066Sahrens * This platform can't address a volume this big. 2839fa9e4066Sahrens */ 284099653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 284199653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 284299653d4eSeschrock errbuf)); 2843fa9e4066Sahrens #endif 284499653d4eSeschrock /* FALLTHROUGH */ 2845fa9e4066Sahrens default: 284699653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2847fa9e4066Sahrens } 2848fa9e4066Sahrens } 2849fa9e4066Sahrens 2850fa9e4066Sahrens return (0); 2851fa9e4066Sahrens } 2852fa9e4066Sahrens 2853fa9e4066Sahrens /* 2854fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2855fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2856fa9e4066Sahrens */ 2857fa9e4066Sahrens int 2858fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp) 2859fa9e4066Sahrens { 2860fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2861fa9e4066Sahrens 2862fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2863fa9e4066Sahrens 2864e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2865f3861e1aSahl /* 2866ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2867ecd6cf80Smarks * abort the request. This would only happen for a 2868ecd6cf80Smarks * non-privileged user. 2869f3861e1aSahl */ 2870ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2871ecd6cf80Smarks return (-1); 2872ecd6cf80Smarks } 2873f3861e1aSahl 287499653d4eSeschrock if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 2875fa9e4066Sahrens return (-1); 2876fa9e4066Sahrens 2877fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2878fa9e4066Sahrens } else { 2879fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2880fa9e4066Sahrens } 2881fa9e4066Sahrens 2882ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 2883ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 288499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 288599653d4eSeschrock zhp->zfs_name)); 28861d452cf5Sahrens } 2887fa9e4066Sahrens 2888fa9e4066Sahrens remove_mountpoint(zhp); 2889fa9e4066Sahrens 2890fa9e4066Sahrens return (0); 2891fa9e4066Sahrens } 2892fa9e4066Sahrens 28931d452cf5Sahrens struct destroydata { 28941d452cf5Sahrens char *snapname; 28951d452cf5Sahrens boolean_t gotone; 28963ccfa83cSahrens boolean_t closezhp; 28971d452cf5Sahrens }; 28981d452cf5Sahrens 28991d452cf5Sahrens static int 29001d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg) 29011d452cf5Sahrens { 29021d452cf5Sahrens struct destroydata *dd = arg; 29031d452cf5Sahrens zfs_handle_t *szhp; 29041d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 29053ccfa83cSahrens boolean_t closezhp = dd->closezhp; 29063ccfa83cSahrens int rv; 29071d452cf5Sahrens 2908e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 2909e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 2910e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 29111d452cf5Sahrens 29121d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 29131d452cf5Sahrens if (szhp) { 29141d452cf5Sahrens dd->gotone = B_TRUE; 29151d452cf5Sahrens zfs_close(szhp); 29161d452cf5Sahrens } 29171d452cf5Sahrens 29181d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 29191d452cf5Sahrens (void) zvol_remove_link(zhp->zfs_hdl, name); 29201d452cf5Sahrens /* 29211d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 29221d452cf5Sahrens * return an error, because then we wouldn't visit all 29231d452cf5Sahrens * the volumes. 29241d452cf5Sahrens */ 29251d452cf5Sahrens } 29261d452cf5Sahrens 29273ccfa83cSahrens dd->closezhp = B_TRUE; 29283ccfa83cSahrens rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg); 29293ccfa83cSahrens if (closezhp) 29303ccfa83cSahrens zfs_close(zhp); 29313ccfa83cSahrens return (rv); 29321d452cf5Sahrens } 29331d452cf5Sahrens 29341d452cf5Sahrens /* 29351d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 29361d452cf5Sahrens */ 29371d452cf5Sahrens int 29381d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname) 29391d452cf5Sahrens { 29401d452cf5Sahrens zfs_cmd_t zc = { 0 }; 29411d452cf5Sahrens int ret; 29421d452cf5Sahrens struct destroydata dd = { 0 }; 29431d452cf5Sahrens 29441d452cf5Sahrens dd.snapname = snapname; 29451d452cf5Sahrens (void) zfs_remove_link_cb(zhp, &dd); 29461d452cf5Sahrens 29471d452cf5Sahrens if (!dd.gotone) { 2948ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 29491d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 29501d452cf5Sahrens zhp->zfs_name, snapname)); 29511d452cf5Sahrens } 29521d452cf5Sahrens 29531d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2954e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 29551d452cf5Sahrens 2956ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 29571d452cf5Sahrens if (ret != 0) { 29581d452cf5Sahrens char errbuf[1024]; 29591d452cf5Sahrens 29601d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 29611d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 29621d452cf5Sahrens 29631d452cf5Sahrens switch (errno) { 29641d452cf5Sahrens case EEXIST: 29651d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 29661d452cf5Sahrens "snapshot is cloned")); 29671d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 29681d452cf5Sahrens 29691d452cf5Sahrens default: 29701d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 29711d452cf5Sahrens errbuf)); 29721d452cf5Sahrens } 29731d452cf5Sahrens } 29741d452cf5Sahrens 29751d452cf5Sahrens return (0); 29761d452cf5Sahrens } 29771d452cf5Sahrens 2978fa9e4066Sahrens /* 2979fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 2980fa9e4066Sahrens */ 2981fa9e4066Sahrens int 2982e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 2983fa9e4066Sahrens { 2984fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2985fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2986fa9e4066Sahrens int ret; 298799653d4eSeschrock char errbuf[1024]; 298899653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 2989e9dbad6fSeschrock zfs_type_t type; 2990e9dbad6fSeschrock uint64_t zoned; 2991fa9e4066Sahrens 2992fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 2993fa9e4066Sahrens 299499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 299599653d4eSeschrock "cannot create '%s'"), target); 299699653d4eSeschrock 2997fa9e4066Sahrens /* validate the target name */ 2998f18faf3fSek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 299999653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3000fa9e4066Sahrens 3001fa9e4066Sahrens /* validate parents exist */ 30027f1f55eaSvb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3003fa9e4066Sahrens return (-1); 3004fa9e4066Sahrens 3005fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3006fa9e4066Sahrens 3007fa9e4066Sahrens /* do the clone */ 3008e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 3009fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 30105f8e1617Snn35248 type = ZFS_TYPE_VOLUME; 3011e9dbad6fSeschrock } else { 3012fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 30135f8e1617Snn35248 type = ZFS_TYPE_FILESYSTEM; 3014e9dbad6fSeschrock } 3015e9dbad6fSeschrock 3016e9dbad6fSeschrock if (props) { 3017990b4856Slling if ((props = zfs_validate_properties(hdl, type, props, 3018b1b8ab34Slling zoned, zhp, errbuf)) == NULL) 3019e9dbad6fSeschrock return (-1); 3020e9dbad6fSeschrock 3021990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3022e9dbad6fSeschrock nvlist_free(props); 3023e9dbad6fSeschrock return (-1); 3024e9dbad6fSeschrock } 3025e9dbad6fSeschrock 3026e9dbad6fSeschrock nvlist_free(props); 3027e9dbad6fSeschrock } 3028fa9e4066Sahrens 3029fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 3030e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 3031ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3032fa9e4066Sahrens 3033e9dbad6fSeschrock zcmd_free_nvlists(&zc); 3034e9dbad6fSeschrock 3035fa9e4066Sahrens if (ret != 0) { 3036fa9e4066Sahrens switch (errno) { 3037fa9e4066Sahrens 3038fa9e4066Sahrens case ENOENT: 3039fa9e4066Sahrens /* 3040fa9e4066Sahrens * The parent doesn't exist. We should have caught this 3041fa9e4066Sahrens * above, but there may a race condition that has since 3042fa9e4066Sahrens * destroyed the parent. 3043fa9e4066Sahrens * 3044fa9e4066Sahrens * At this point, we don't know whether it's the source 3045fa9e4066Sahrens * that doesn't exist anymore, or whether the target 3046fa9e4066Sahrens * dataset doesn't exist. 3047fa9e4066Sahrens */ 304899653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 304999653d4eSeschrock "no such parent '%s'"), parent); 305099653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3051fa9e4066Sahrens 305299653d4eSeschrock case EXDEV: 305399653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 305499653d4eSeschrock "source and target pools differ")); 305599653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 305699653d4eSeschrock errbuf)); 305799653d4eSeschrock 305899653d4eSeschrock default: 305999653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 306099653d4eSeschrock errbuf)); 306199653d4eSeschrock } 3062e9dbad6fSeschrock } else if (ZFS_IS_VOLUME(zhp)) { 306399653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, target); 306499653d4eSeschrock } 306599653d4eSeschrock 306699653d4eSeschrock return (ret); 306799653d4eSeschrock } 306899653d4eSeschrock 306999653d4eSeschrock typedef struct promote_data { 307099653d4eSeschrock char cb_mountpoint[MAXPATHLEN]; 307199653d4eSeschrock const char *cb_target; 307299653d4eSeschrock const char *cb_errbuf; 307399653d4eSeschrock uint64_t cb_pivot_txg; 307499653d4eSeschrock } promote_data_t; 307599653d4eSeschrock 307699653d4eSeschrock static int 307799653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data) 307899653d4eSeschrock { 307999653d4eSeschrock promote_data_t *pd = data; 308099653d4eSeschrock zfs_handle_t *szhp; 308199653d4eSeschrock char snapname[MAXPATHLEN]; 30823ccfa83cSahrens int rv = 0; 308399653d4eSeschrock 308499653d4eSeschrock /* We don't care about snapshots after the pivot point */ 30853ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) { 30863ccfa83cSahrens zfs_close(zhp); 308799653d4eSeschrock return (0); 30883ccfa83cSahrens } 308999653d4eSeschrock 30900b69c2f0Sahrens /* Remove the device link if it's a zvol. */ 3091e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 30920b69c2f0Sahrens (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); 309399653d4eSeschrock 309499653d4eSeschrock /* Check for conflicting names */ 3095e9dbad6fSeschrock (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); 3096e9dbad6fSeschrock (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); 309799653d4eSeschrock szhp = make_dataset_handle(zhp->zfs_hdl, snapname); 309899653d4eSeschrock if (szhp != NULL) { 309999653d4eSeschrock zfs_close(szhp); 310099653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 310199653d4eSeschrock "snapshot name '%s' from origin \n" 310299653d4eSeschrock "conflicts with '%s' from target"), 310399653d4eSeschrock zhp->zfs_name, snapname); 31043ccfa83cSahrens rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf); 310599653d4eSeschrock } 31063ccfa83cSahrens zfs_close(zhp); 31073ccfa83cSahrens return (rv); 310899653d4eSeschrock } 310999653d4eSeschrock 31100b69c2f0Sahrens static int 31110b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data) 31120b69c2f0Sahrens { 31130b69c2f0Sahrens promote_data_t *pd = data; 31140b69c2f0Sahrens 31150b69c2f0Sahrens /* We don't care about snapshots after the pivot point */ 31163ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) { 31170b69c2f0Sahrens /* Create the device link if it's a zvol. */ 3118e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 31190b69c2f0Sahrens (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); 31203ccfa83cSahrens } 31210b69c2f0Sahrens 31223ccfa83cSahrens zfs_close(zhp); 31230b69c2f0Sahrens return (0); 31240b69c2f0Sahrens } 31250b69c2f0Sahrens 312699653d4eSeschrock /* 312799653d4eSeschrock * Promotes the given clone fs to be the clone parent. 312899653d4eSeschrock */ 312999653d4eSeschrock int 313099653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 313199653d4eSeschrock { 313299653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 313399653d4eSeschrock zfs_cmd_t zc = { 0 }; 313499653d4eSeschrock char parent[MAXPATHLEN]; 313599653d4eSeschrock char *cp; 313699653d4eSeschrock int ret; 313799653d4eSeschrock zfs_handle_t *pzhp; 313899653d4eSeschrock promote_data_t pd; 313999653d4eSeschrock char errbuf[1024]; 314099653d4eSeschrock 314199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 314299653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 314399653d4eSeschrock 314499653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 314599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 314699653d4eSeschrock "snapshots can not be promoted")); 314799653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 314899653d4eSeschrock } 314999653d4eSeschrock 31503cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 315199653d4eSeschrock if (parent[0] == '\0') { 315299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 315399653d4eSeschrock "not a cloned filesystem")); 315499653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 315599653d4eSeschrock } 315699653d4eSeschrock cp = strchr(parent, '@'); 315799653d4eSeschrock *cp = '\0'; 315899653d4eSeschrock 315999653d4eSeschrock /* Walk the snapshots we will be moving */ 31603cb34c60Sahrens pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); 316199653d4eSeschrock if (pzhp == NULL) 316299653d4eSeschrock return (-1); 316399653d4eSeschrock pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); 316499653d4eSeschrock zfs_close(pzhp); 316599653d4eSeschrock pd.cb_target = zhp->zfs_name; 316699653d4eSeschrock pd.cb_errbuf = errbuf; 3167990b4856Slling pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET); 316899653d4eSeschrock if (pzhp == NULL) 316999653d4eSeschrock return (-1); 317099653d4eSeschrock (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, 317199653d4eSeschrock sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); 317299653d4eSeschrock ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); 31730b69c2f0Sahrens if (ret != 0) { 31740b69c2f0Sahrens zfs_close(pzhp); 317599653d4eSeschrock return (-1); 31760b69c2f0Sahrens } 317799653d4eSeschrock 317899653d4eSeschrock /* issue the ioctl */ 31793cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3180e9dbad6fSeschrock sizeof (zc.zc_value)); 318199653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3182ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 318399653d4eSeschrock 318499653d4eSeschrock if (ret != 0) { 31850b69c2f0Sahrens int save_errno = errno; 3186fa9e4066Sahrens 31870b69c2f0Sahrens (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd); 31880b69c2f0Sahrens zfs_close(pzhp); 31890b69c2f0Sahrens 31900b69c2f0Sahrens switch (save_errno) { 3191fa9e4066Sahrens case EEXIST: 3192fa9e4066Sahrens /* 319399653d4eSeschrock * There is a conflicting snapshot name. We 319499653d4eSeschrock * should have caught this above, but they could 319599653d4eSeschrock * have renamed something in the mean time. 3196fa9e4066Sahrens */ 319799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 319899653d4eSeschrock "conflicting snapshot name from parent '%s'"), 319999653d4eSeschrock parent); 320099653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3201fa9e4066Sahrens 3202fa9e4066Sahrens default: 32030b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3204fa9e4066Sahrens } 32050b69c2f0Sahrens } else { 32060b69c2f0Sahrens (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd); 3207fa9e4066Sahrens } 3208fa9e4066Sahrens 32090b69c2f0Sahrens zfs_close(pzhp); 3210fa9e4066Sahrens return (ret); 3211fa9e4066Sahrens } 3212fa9e4066Sahrens 3213cdf5b4caSmmusante struct createdata { 3214cdf5b4caSmmusante const char *cd_snapname; 3215cdf5b4caSmmusante int cd_ifexists; 3216cdf5b4caSmmusante }; 3217cdf5b4caSmmusante 32181d452cf5Sahrens static int 32191d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg) 32201d452cf5Sahrens { 3221cdf5b4caSmmusante struct createdata *cd = arg; 3222e9dbad6fSeschrock int ret; 32231d452cf5Sahrens 32241d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 32251d452cf5Sahrens char name[MAXPATHLEN]; 32261d452cf5Sahrens 3227e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 3228e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 3229cdf5b4caSmmusante (void) strlcat(name, cd->cd_snapname, sizeof (name)); 3230cdf5b4caSmmusante (void) zvol_create_link_common(zhp->zfs_hdl, name, 3231cdf5b4caSmmusante cd->cd_ifexists); 32321d452cf5Sahrens /* 32331d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 32341d452cf5Sahrens * return an error, because then we wouldn't visit all 32351d452cf5Sahrens * the volumes. 32361d452cf5Sahrens */ 32371d452cf5Sahrens } 3238e9dbad6fSeschrock 3239cdf5b4caSmmusante ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd); 3240e9dbad6fSeschrock 3241e9dbad6fSeschrock zfs_close(zhp); 3242e9dbad6fSeschrock 3243e9dbad6fSeschrock return (ret); 32441d452cf5Sahrens } 32451d452cf5Sahrens 3246fa9e4066Sahrens /* 324772bdce51Sahl * Takes a snapshot of the given dataset. 3248fa9e4066Sahrens */ 3249fa9e4066Sahrens int 32501d452cf5Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive) 3251fa9e4066Sahrens { 3252fa9e4066Sahrens const char *delim; 3253fa9e4066Sahrens char *parent; 3254fa9e4066Sahrens zfs_handle_t *zhp; 3255fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3256fa9e4066Sahrens int ret; 325799653d4eSeschrock char errbuf[1024]; 3258fa9e4066Sahrens 325999653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 326099653d4eSeschrock "cannot snapshot '%s'"), path); 326199653d4eSeschrock 326299653d4eSeschrock /* validate the target name */ 3263f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 326499653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3265fa9e4066Sahrens 3266fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 32671d452cf5Sahrens delim = strchr(path, '@'); 326899653d4eSeschrock if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL) 326999653d4eSeschrock return (-1); 3270fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3271fa9e4066Sahrens parent[delim - path] = '\0'; 3272fa9e4066Sahrens 327399653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3274fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3275fa9e4066Sahrens free(parent); 3276fa9e4066Sahrens return (-1); 3277fa9e4066Sahrens } 3278fa9e4066Sahrens 32791d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3280e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3281ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3282ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3283ecd6cf80Smarks else 3284ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 32851d452cf5Sahrens zc.zc_cookie = recursive; 3286ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3287fa9e4066Sahrens 32881d452cf5Sahrens /* 32891d452cf5Sahrens * if it was recursive, the one that actually failed will be in 32901d452cf5Sahrens * zc.zc_name. 32911d452cf5Sahrens */ 3292ecd6cf80Smarks if (ret != 0) 32931d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3294e9dbad6fSeschrock "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 3295ecd6cf80Smarks 32961d452cf5Sahrens if (ret == 0 && recursive) { 3297cdf5b4caSmmusante struct createdata cd; 3298cdf5b4caSmmusante 3299cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3300cdf5b4caSmmusante cd.cd_ifexists = B_FALSE; 3301cdf5b4caSmmusante (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd); 33021d452cf5Sahrens } 3303fa9e4066Sahrens if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { 330499653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, path); 33051d452cf5Sahrens if (ret != 0) { 3306ecd6cf80Smarks (void) zfs_standard_error(hdl, errno, 3307ecd6cf80Smarks dgettext(TEXT_DOMAIN, 3308ecd6cf80Smarks "Volume successfully snapshotted, but device links " 3309ecd6cf80Smarks "were not created")); 3310ecd6cf80Smarks free(parent); 3311ecd6cf80Smarks zfs_close(zhp); 3312ecd6cf80Smarks return (-1); 3313fa9e4066Sahrens } 33141d452cf5Sahrens } 3315fa9e4066Sahrens 331699653d4eSeschrock if (ret != 0) 331799653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3318fa9e4066Sahrens 3319fa9e4066Sahrens free(parent); 3320fa9e4066Sahrens zfs_close(zhp); 3321fa9e4066Sahrens 3322fa9e4066Sahrens return (ret); 3323fa9e4066Sahrens } 3324fa9e4066Sahrens 3325fa9e4066Sahrens /* 3326b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3327b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3328b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3329b12a1c38Slling * group. 3330fa9e4066Sahrens */ 3331b12a1c38Slling typedef struct rollback_data { 3332b12a1c38Slling const char *cb_target; /* the snapshot */ 3333b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3334c391e322Sahrens boolean_t cb_error; 333599653d4eSeschrock boolean_t cb_dependent; 3336c391e322Sahrens boolean_t cb_force; 3337b12a1c38Slling } rollback_data_t; 3338b12a1c38Slling 3339b12a1c38Slling static int 3340b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3341b12a1c38Slling { 3342b12a1c38Slling rollback_data_t *cbp = data; 3343b12a1c38Slling 3344b12a1c38Slling if (!cbp->cb_dependent) { 3345b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3346b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3347b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3348b12a1c38Slling cbp->cb_create) { 3349ecd6cf80Smarks char *logstr; 3350b12a1c38Slling 335199653d4eSeschrock cbp->cb_dependent = B_TRUE; 33524ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 33534ccbb6e7Sahrens rollback_destroy, cbp); 335499653d4eSeschrock cbp->cb_dependent = B_FALSE; 3355b12a1c38Slling 3356ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3357ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 33584ccbb6e7Sahrens cbp->cb_error |= zfs_destroy(zhp); 3359ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3360b12a1c38Slling } 3361b12a1c38Slling } else { 3362c391e322Sahrens /* We must destroy this clone; first unmount it */ 3363c391e322Sahrens prop_changelist_t *clp; 3364c391e322Sahrens 3365c391e322Sahrens clp = changelist_gather(zhp, ZFS_PROP_NAME, 3366c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3367c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3368c391e322Sahrens cbp->cb_error = B_TRUE; 3369c391e322Sahrens zfs_close(zhp); 3370c391e322Sahrens return (0); 3371c391e322Sahrens } 3372c391e322Sahrens if (zfs_destroy(zhp) != 0) 3373c391e322Sahrens cbp->cb_error = B_TRUE; 3374c391e322Sahrens else 3375c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3376*ba7b046eSahrens (void) changelist_postfix(clp); 3377c391e322Sahrens changelist_free(clp); 3378b12a1c38Slling } 3379b12a1c38Slling 3380b12a1c38Slling zfs_close(zhp); 3381b12a1c38Slling return (0); 3382b12a1c38Slling } 3383b12a1c38Slling 3384b12a1c38Slling /* 33854ccbb6e7Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 33864ccbb6e7Sahrens * data changes since then and making it the active dataset. 33874ccbb6e7Sahrens * 33884ccbb6e7Sahrens * Any snapshots more recent than the target are destroyed, along with 33894ccbb6e7Sahrens * their dependents. 3390b12a1c38Slling */ 33914ccbb6e7Sahrens int 3392c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3393fa9e4066Sahrens { 33944ccbb6e7Sahrens rollback_data_t cb = { 0 }; 33954ccbb6e7Sahrens int err; 3396fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 33977b97dc1aSrm160521 boolean_t restore_resv = 0; 33987b97dc1aSrm160521 uint64_t old_volsize, new_volsize; 33997b97dc1aSrm160521 zfs_prop_t resv_prop; 3400fa9e4066Sahrens 3401fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3402fa9e4066Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3403fa9e4066Sahrens 34044ccbb6e7Sahrens /* 34054ccbb6e7Sahrens * Destroy all recent snapshots and its dependends. 34064ccbb6e7Sahrens */ 3407c391e322Sahrens cb.cb_force = force; 34084ccbb6e7Sahrens cb.cb_target = snap->zfs_name; 34094ccbb6e7Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 34104ccbb6e7Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 34114ccbb6e7Sahrens 3412c391e322Sahrens if (cb.cb_error) 3413c391e322Sahrens return (-1); 34144ccbb6e7Sahrens 34154ccbb6e7Sahrens /* 34164ccbb6e7Sahrens * Now that we have verified that the snapshot is the latest, 34174ccbb6e7Sahrens * rollback to the given snapshot. 34184ccbb6e7Sahrens */ 34194ccbb6e7Sahrens 34207b97dc1aSrm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 34217b97dc1aSrm160521 if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 3422fa9e4066Sahrens return (-1); 34237b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 34247b97dc1aSrm160521 return (-1); 34257b97dc1aSrm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 34267b97dc1aSrm160521 restore_resv = 34277b97dc1aSrm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 34287b97dc1aSrm160521 } 3429fa9e4066Sahrens 3430fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3431fa9e4066Sahrens 3432e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3433fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3434fa9e4066Sahrens else 3435fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3436fa9e4066Sahrens 3437fa9e4066Sahrens /* 34384ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 34394ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 34404ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 34414ccbb6e7Sahrens * an unlikely race condition where the user has taken a 34424ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 34437b97dc1aSrm160521 * 3444fa9e4066Sahrens */ 34454ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3446ece3d9b3Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 344799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 344899653d4eSeschrock zhp->zfs_name); 3449b9415e83Srm160521 return (err); 3450b9415e83Srm160521 } 3451fa9e4066Sahrens 34527b97dc1aSrm160521 /* 34537b97dc1aSrm160521 * For volumes, if the pre-rollback volsize matched the pre- 34547b97dc1aSrm160521 * rollback reservation and the volsize has changed then set 34557b97dc1aSrm160521 * the reservation property to the post-rollback volsize. 34567b97dc1aSrm160521 * Make a new handle since the rollback closed the dataset. 34577b97dc1aSrm160521 */ 3458b9415e83Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3459b9415e83Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3460b9415e83Srm160521 if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) { 3461b9415e83Srm160521 zfs_close(zhp); 34627b97dc1aSrm160521 return (err); 3463b9415e83Srm160521 } 34647b97dc1aSrm160521 if (restore_resv) { 34657b97dc1aSrm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 34667b97dc1aSrm160521 if (old_volsize != new_volsize) 3467b9415e83Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 3468b9415e83Srm160521 new_volsize); 34697b97dc1aSrm160521 } 34707b97dc1aSrm160521 zfs_close(zhp); 34717b97dc1aSrm160521 } 34724ccbb6e7Sahrens return (err); 3473b12a1c38Slling } 3474b12a1c38Slling 3475b12a1c38Slling /* 3476fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3477fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3478fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3479fa9e4066Sahrens * libzfs_graph.c. 3480fa9e4066Sahrens */ 3481fa9e4066Sahrens int 34823bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 34833bb79becSeschrock zfs_iter_f func, void *data) 3484fa9e4066Sahrens { 3485fa9e4066Sahrens char **dependents; 3486fa9e4066Sahrens size_t count; 3487fa9e4066Sahrens int i; 3488fa9e4066Sahrens zfs_handle_t *child; 3489fa9e4066Sahrens int ret = 0; 3490fa9e4066Sahrens 34913bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 34923bb79becSeschrock &dependents, &count) != 0) 34933bb79becSeschrock return (-1); 34943bb79becSeschrock 3495fa9e4066Sahrens for (i = 0; i < count; i++) { 349699653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 349799653d4eSeschrock dependents[i])) == NULL) 3498fa9e4066Sahrens continue; 3499fa9e4066Sahrens 3500fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3501fa9e4066Sahrens break; 3502fa9e4066Sahrens } 3503fa9e4066Sahrens 3504fa9e4066Sahrens for (i = 0; i < count; i++) 3505fa9e4066Sahrens free(dependents[i]); 3506fa9e4066Sahrens free(dependents); 3507fa9e4066Sahrens 3508fa9e4066Sahrens return (ret); 3509fa9e4066Sahrens } 3510fa9e4066Sahrens 3511fa9e4066Sahrens /* 3512fa9e4066Sahrens * Renames the given dataset. 3513fa9e4066Sahrens */ 3514fa9e4066Sahrens int 35157f1f55eaSvb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3516fa9e4066Sahrens { 3517fa9e4066Sahrens int ret; 3518fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3519fa9e4066Sahrens char *delim; 3520cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3521cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3522cdf5b4caSmmusante char *parentname = NULL; 3523fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 352499653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 352599653d4eSeschrock char errbuf[1024]; 3526fa9e4066Sahrens 3527fa9e4066Sahrens /* if we have the same exact name, just return success */ 3528fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3529fa9e4066Sahrens return (0); 3530fa9e4066Sahrens 353199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 353299653d4eSeschrock "cannot rename to '%s'"), target); 353399653d4eSeschrock 3534fa9e4066Sahrens /* 3535fa9e4066Sahrens * Make sure the target name is valid 3536fa9e4066Sahrens */ 3537fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 353898579b20Snd150628 if ((strchr(target, '@') == NULL) || 353998579b20Snd150628 *target == '@') { 354098579b20Snd150628 /* 354198579b20Snd150628 * Snapshot target name is abbreviated, 354298579b20Snd150628 * reconstruct full dataset name 354398579b20Snd150628 */ 354498579b20Snd150628 (void) strlcpy(parent, zhp->zfs_name, 354598579b20Snd150628 sizeof (parent)); 354698579b20Snd150628 delim = strchr(parent, '@'); 354798579b20Snd150628 if (strchr(target, '@') == NULL) 354898579b20Snd150628 *(++delim) = '\0'; 354998579b20Snd150628 else 355098579b20Snd150628 *delim = '\0'; 355198579b20Snd150628 (void) strlcat(parent, target, sizeof (parent)); 355298579b20Snd150628 target = parent; 355398579b20Snd150628 } else { 3554fa9e4066Sahrens /* 3555fa9e4066Sahrens * Make sure we're renaming within the same dataset. 3556fa9e4066Sahrens */ 355798579b20Snd150628 delim = strchr(target, '@'); 355898579b20Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 355998579b20Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 356099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 356198579b20Snd150628 "snapshots must be part of same " 356298579b20Snd150628 "dataset")); 356398579b20Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 356498579b20Snd150628 errbuf)); 3565fa9e4066Sahrens } 356698579b20Snd150628 } 3567f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 356898579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3569fa9e4066Sahrens } else { 3570cdf5b4caSmmusante if (recursive) { 3571cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3572cdf5b4caSmmusante "recursive rename must be a snapshot")); 3573cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3574cdf5b4caSmmusante } 3575cdf5b4caSmmusante 3576f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 357798579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3578e9dbad6fSeschrock uint64_t unused; 3579e9dbad6fSeschrock 3580fa9e4066Sahrens /* validate parents */ 35817f1f55eaSvb160487 if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3582fa9e4066Sahrens return (-1); 3583fa9e4066Sahrens 3584fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3585fa9e4066Sahrens 3586fa9e4066Sahrens /* make sure we're in the same pool */ 3587fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3588fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3589fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 359099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 359199653d4eSeschrock "datasets must be within same pool")); 359299653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3593fa9e4066Sahrens } 3594f2fdf992Snd150628 3595f2fdf992Snd150628 /* new name cannot be a child of the current dataset name */ 3596f2fdf992Snd150628 if (strncmp(parent, zhp->zfs_name, 3597f2fdf992Snd150628 strlen(zhp->zfs_name)) == 0) { 3598f2fdf992Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3599f2fdf992Snd150628 "New dataset name cannot be a descendent of " 3600f2fdf992Snd150628 "current dataset name")); 3601f2fdf992Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3602f2fdf992Snd150628 } 3603fa9e4066Sahrens } 3604fa9e4066Sahrens 360599653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 360699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 360799653d4eSeschrock 3608fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3609fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 361099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 361199653d4eSeschrock "dataset is used in a non-global zone")); 361299653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3613fa9e4066Sahrens } 3614fa9e4066Sahrens 3615cdf5b4caSmmusante if (recursive) { 3616cdf5b4caSmmusante struct destroydata dd; 3617cdf5b4caSmmusante 3618f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3619f0c5ee21Smmusante if (parentname == NULL) { 3620f0c5ee21Smmusante ret = -1; 3621f0c5ee21Smmusante goto error; 3622f0c5ee21Smmusante } 3623cdf5b4caSmmusante delim = strchr(parentname, '@'); 3624cdf5b4caSmmusante *delim = '\0'; 3625990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3626cdf5b4caSmmusante if (zhrp == NULL) { 3627f0c5ee21Smmusante ret = -1; 3628f0c5ee21Smmusante goto error; 3629cdf5b4caSmmusante } 3630cdf5b4caSmmusante 3631cdf5b4caSmmusante dd.snapname = delim + 1; 3632cdf5b4caSmmusante dd.gotone = B_FALSE; 3633f0c5ee21Smmusante dd.closezhp = B_TRUE; 3634cdf5b4caSmmusante 3635cdf5b4caSmmusante /* We remove any zvol links prior to renaming them */ 3636cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd); 3637cdf5b4caSmmusante if (ret) { 3638cdf5b4caSmmusante goto error; 3639cdf5b4caSmmusante } 3640cdf5b4caSmmusante } else { 3641fa9e4066Sahrens if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL) 364299653d4eSeschrock return (-1); 3643fa9e4066Sahrens 3644fa9e4066Sahrens if (changelist_haszonedchild(cl)) { 364599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 364699653d4eSeschrock "child dataset with inherited mountpoint is used " 364799653d4eSeschrock "in a non-global zone")); 3648e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3649fa9e4066Sahrens goto error; 3650fa9e4066Sahrens } 3651fa9e4066Sahrens 3652fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 3653fa9e4066Sahrens goto error; 3654cdf5b4caSmmusante } 3655fa9e4066Sahrens 3656e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3657fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3658fa9e4066Sahrens else 3659fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3660fa9e4066Sahrens 366198579b20Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3662e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 366398579b20Snd150628 3664cdf5b4caSmmusante zc.zc_cookie = recursive; 3665cdf5b4caSmmusante 3666ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3667cdf5b4caSmmusante /* 3668cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3669cdf5b4caSmmusante * be in zc.zc_name 3670cdf5b4caSmmusante */ 3671cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 36723cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3673cdf5b4caSmmusante 3674cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3675cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3676cdf5b4caSmmusante "a child dataset already has a snapshot " 3677cdf5b4caSmmusante "with the new name")); 3678a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3679cdf5b4caSmmusante } else { 368099653d4eSeschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3681cdf5b4caSmmusante } 3682fa9e4066Sahrens 3683fa9e4066Sahrens /* 3684fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3685fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3686fa9e4066Sahrens */ 3687cdf5b4caSmmusante if (recursive) { 3688cdf5b4caSmmusante struct createdata cd; 3689cdf5b4caSmmusante 3690cdf5b4caSmmusante /* only create links for datasets that had existed */ 3691cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3692cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3693cdf5b4caSmmusante (void) zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3694cdf5b4caSmmusante &cd); 3695cdf5b4caSmmusante } else { 3696fa9e4066Sahrens (void) changelist_postfix(cl); 3697cdf5b4caSmmusante } 3698cdf5b4caSmmusante } else { 3699cdf5b4caSmmusante if (recursive) { 3700cdf5b4caSmmusante struct createdata cd; 3701cdf5b4caSmmusante 3702cdf5b4caSmmusante /* only create links for datasets that had existed */ 3703cdf5b4caSmmusante cd.cd_snapname = strchr(target, '@') + 1; 3704cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3705cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3706cdf5b4caSmmusante &cd); 3707fa9e4066Sahrens } else { 3708fa9e4066Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 3709fa9e4066Sahrens ret = changelist_postfix(cl); 3710fa9e4066Sahrens } 3711cdf5b4caSmmusante } 3712fa9e4066Sahrens 3713fa9e4066Sahrens error: 3714cdf5b4caSmmusante if (parentname) { 3715cdf5b4caSmmusante free(parentname); 3716cdf5b4caSmmusante } 3717cdf5b4caSmmusante if (zhrp) { 3718cdf5b4caSmmusante zfs_close(zhrp); 3719cdf5b4caSmmusante } 3720cdf5b4caSmmusante if (cl) { 3721fa9e4066Sahrens changelist_free(cl); 3722cdf5b4caSmmusante } 3723fa9e4066Sahrens return (ret); 3724fa9e4066Sahrens } 3725fa9e4066Sahrens 3726fa9e4066Sahrens /* 3727fa9e4066Sahrens * Given a zvol dataset, issue the ioctl to create the appropriate minor node, 3728fa9e4066Sahrens * poke devfsadm to create the /dev link, and then wait for the link to appear. 3729fa9e4066Sahrens */ 3730fa9e4066Sahrens int 373199653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset) 3732fa9e4066Sahrens { 3733cdf5b4caSmmusante return (zvol_create_link_common(hdl, dataset, B_FALSE)); 3734cdf5b4caSmmusante } 3735cdf5b4caSmmusante 3736cdf5b4caSmmusante static int 3737cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists) 3738cdf5b4caSmmusante { 3739fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 374099653d4eSeschrock di_devlink_handle_t dhdl; 3741ecd6cf80Smarks priv_set_t *priv_effective; 3742ecd6cf80Smarks int privileged; 3743fa9e4066Sahrens 3744fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3745fa9e4066Sahrens 3746fa9e4066Sahrens /* 3747fa9e4066Sahrens * Issue the appropriate ioctl. 3748fa9e4066Sahrens */ 374999653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { 3750fa9e4066Sahrens switch (errno) { 3751fa9e4066Sahrens case EEXIST: 3752fa9e4066Sahrens /* 3753fa9e4066Sahrens * Silently ignore the case where the link already 3754fa9e4066Sahrens * exists. This allows 'zfs volinit' to be run multiple 3755fa9e4066Sahrens * times without errors. 3756fa9e4066Sahrens */ 3757fa9e4066Sahrens return (0); 3758fa9e4066Sahrens 3759cdf5b4caSmmusante case ENOENT: 3760cdf5b4caSmmusante /* 3761cdf5b4caSmmusante * Dataset does not exist in the kernel. If we 3762cdf5b4caSmmusante * don't care (see zfs_rename), then ignore the 3763cdf5b4caSmmusante * error quietly. 3764cdf5b4caSmmusante */ 3765cdf5b4caSmmusante if (ifexists) { 3766cdf5b4caSmmusante return (0); 3767cdf5b4caSmmusante } 3768cdf5b4caSmmusante 3769cdf5b4caSmmusante /* FALLTHROUGH */ 3770cdf5b4caSmmusante 3771fa9e4066Sahrens default: 3772ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 377399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 377499653d4eSeschrock "for '%s'"), dataset)); 3775fa9e4066Sahrens } 3776fa9e4066Sahrens } 3777fa9e4066Sahrens 3778fa9e4066Sahrens /* 3779ecd6cf80Smarks * If privileged call devfsadm and wait for the links to 3780ecd6cf80Smarks * magically appear. 3781ecd6cf80Smarks * Otherwise, print out an informational message. 3782fa9e4066Sahrens */ 3783ecd6cf80Smarks 3784ecd6cf80Smarks priv_effective = priv_allocset(); 3785ecd6cf80Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 3786ecd6cf80Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 3787ecd6cf80Smarks priv_freeset(priv_effective); 3788ecd6cf80Smarks 3789ecd6cf80Smarks if (privileged) { 3790ecd6cf80Smarks if ((dhdl = di_devlink_init(ZFS_DRIVER, 3791ecd6cf80Smarks DI_MAKE_LINK)) == NULL) { 379299653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 3793ecd6cf80Smarks (void) zfs_standard_error_fmt(hdl, EZFS_DEVLINKS, 379499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 379599653d4eSeschrock "for '%s'"), dataset); 379699653d4eSeschrock (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); 3797fa9e4066Sahrens return (-1); 3798fa9e4066Sahrens } else { 379999653d4eSeschrock (void) di_devlink_fini(&dhdl); 3800fa9e4066Sahrens } 3801ecd6cf80Smarks } else { 3802ecd6cf80Smarks char pathname[MAXPATHLEN]; 3803ecd6cf80Smarks struct stat64 statbuf; 3804ecd6cf80Smarks int i; 3805ecd6cf80Smarks 3806ecd6cf80Smarks #define MAX_WAIT 10 3807ecd6cf80Smarks 3808ecd6cf80Smarks /* 3809ecd6cf80Smarks * This is the poor mans way of waiting for the link 3810ecd6cf80Smarks * to show up. If after 10 seconds we still don't 3811ecd6cf80Smarks * have it, then print out a message. 3812ecd6cf80Smarks */ 3813ecd6cf80Smarks (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s", 3814ecd6cf80Smarks dataset); 3815ecd6cf80Smarks 3816ecd6cf80Smarks for (i = 0; i != MAX_WAIT; i++) { 3817ecd6cf80Smarks if (stat64(pathname, &statbuf) == 0) 3818ecd6cf80Smarks break; 3819ecd6cf80Smarks (void) sleep(1); 3820ecd6cf80Smarks } 3821ecd6cf80Smarks if (i == MAX_WAIT) 3822ecd6cf80Smarks (void) printf(gettext("%s may not be immediately " 3823ecd6cf80Smarks "available\n"), pathname); 3824ecd6cf80Smarks } 3825fa9e4066Sahrens 3826fa9e4066Sahrens return (0); 3827fa9e4066Sahrens } 3828fa9e4066Sahrens 3829fa9e4066Sahrens /* 3830fa9e4066Sahrens * Remove a minor node for the given zvol and the associated /dev links. 3831fa9e4066Sahrens */ 3832fa9e4066Sahrens int 383399653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) 3834fa9e4066Sahrens { 3835fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3836fa9e4066Sahrens 3837fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3838fa9e4066Sahrens 383999653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { 3840fa9e4066Sahrens switch (errno) { 3841fa9e4066Sahrens case ENXIO: 3842fa9e4066Sahrens /* 3843fa9e4066Sahrens * Silently ignore the case where the link no longer 3844fa9e4066Sahrens * exists, so that 'zfs volfini' can be run multiple 3845fa9e4066Sahrens * times without errors. 3846fa9e4066Sahrens */ 3847fa9e4066Sahrens return (0); 3848fa9e4066Sahrens 3849fa9e4066Sahrens default: 3850ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 385199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot remove device " 385299653d4eSeschrock "links for '%s'"), dataset)); 3853fa9e4066Sahrens } 3854fa9e4066Sahrens } 3855fa9e4066Sahrens 3856fa9e4066Sahrens return (0); 3857fa9e4066Sahrens } 3858e9dbad6fSeschrock 3859e9dbad6fSeschrock nvlist_t * 3860e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3861e9dbad6fSeschrock { 3862e9dbad6fSeschrock return (zhp->zfs_user_props); 3863e9dbad6fSeschrock } 3864e9dbad6fSeschrock 3865e9dbad6fSeschrock /* 3866e9dbad6fSeschrock * This function is used by 'zfs list' to determine the exact set of columns to 3867e9dbad6fSeschrock * display, and their maximum widths. This does two main things: 3868e9dbad6fSeschrock * 3869e9dbad6fSeschrock * - If this is a list of all properties, then expand the list to include 3870e9dbad6fSeschrock * all native properties, and set a flag so that for each dataset we look 3871e9dbad6fSeschrock * for new unique user properties and add them to the list. 3872e9dbad6fSeschrock * 3873e9dbad6fSeschrock * - For non fixed-width properties, keep track of the maximum width seen 3874e9dbad6fSeschrock * so that we can size the column appropriately. 3875e9dbad6fSeschrock */ 3876e9dbad6fSeschrock int 3877990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) 3878e9dbad6fSeschrock { 3879e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3880990b4856Slling zprop_list_t *entry; 3881990b4856Slling zprop_list_t **last, **start; 3882e9dbad6fSeschrock nvlist_t *userprops, *propval; 3883e9dbad6fSeschrock nvpair_t *elem; 3884e9dbad6fSeschrock char *strval; 3885e9dbad6fSeschrock char buf[ZFS_MAXPROPLEN]; 3886e9dbad6fSeschrock 3887990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 3888e9dbad6fSeschrock return (-1); 3889e9dbad6fSeschrock 3890e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 3891e9dbad6fSeschrock 3892e9dbad6fSeschrock entry = *plp; 3893e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 3894e9dbad6fSeschrock /* 3895e9dbad6fSeschrock * Go through and add any user properties as necessary. We 3896e9dbad6fSeschrock * start by incrementing our list pointer to the first 3897e9dbad6fSeschrock * non-native property. 3898e9dbad6fSeschrock */ 3899e9dbad6fSeschrock start = plp; 3900e9dbad6fSeschrock while (*start != NULL) { 3901990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 3902e9dbad6fSeschrock break; 3903e9dbad6fSeschrock start = &(*start)->pl_next; 3904e9dbad6fSeschrock } 3905e9dbad6fSeschrock 3906e9dbad6fSeschrock elem = NULL; 3907e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 3908e9dbad6fSeschrock /* 3909e9dbad6fSeschrock * See if we've already found this property in our list. 3910e9dbad6fSeschrock */ 3911e9dbad6fSeschrock for (last = start; *last != NULL; 3912e9dbad6fSeschrock last = &(*last)->pl_next) { 3913e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 3914e9dbad6fSeschrock nvpair_name(elem)) == 0) 3915e9dbad6fSeschrock break; 3916e9dbad6fSeschrock } 3917e9dbad6fSeschrock 3918e9dbad6fSeschrock if (*last == NULL) { 3919e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 3920990b4856Slling sizeof (zprop_list_t))) == NULL || 3921e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 3922e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 3923e9dbad6fSeschrock free(entry); 3924e9dbad6fSeschrock return (-1); 3925e9dbad6fSeschrock } 3926e9dbad6fSeschrock 3927990b4856Slling entry->pl_prop = ZPROP_INVAL; 3928e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 3929e9dbad6fSeschrock entry->pl_all = B_TRUE; 3930e9dbad6fSeschrock *last = entry; 3931e9dbad6fSeschrock } 3932e9dbad6fSeschrock } 3933e9dbad6fSeschrock } 3934e9dbad6fSeschrock 3935e9dbad6fSeschrock /* 3936e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 3937e9dbad6fSeschrock */ 3938e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 3939e9dbad6fSeschrock if (entry->pl_fixed) 3940e9dbad6fSeschrock continue; 3941e9dbad6fSeschrock 3942990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 3943e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 3944e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 3945e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 3946e9dbad6fSeschrock entry->pl_width = strlen(buf); 3947e9dbad6fSeschrock } 3948e9dbad6fSeschrock } else if (nvlist_lookup_nvlist(userprops, 3949e9dbad6fSeschrock entry->pl_user_prop, &propval) == 0) { 3950e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 3951990b4856Slling ZPROP_VALUE, &strval) == 0); 3952e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 3953e9dbad6fSeschrock entry->pl_width = strlen(strval); 3954e9dbad6fSeschrock } 3955e9dbad6fSeschrock } 3956e9dbad6fSeschrock 3957e9dbad6fSeschrock return (0); 3958e9dbad6fSeschrock } 3959ecd6cf80Smarks 3960ecd6cf80Smarks int 3961ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 3962ecd6cf80Smarks { 3963ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 3964ecd6cf80Smarks nvlist_t *nvp; 3965ecd6cf80Smarks gid_t gid; 3966ecd6cf80Smarks uid_t uid; 3967ecd6cf80Smarks const gid_t *groups; 3968ecd6cf80Smarks int group_cnt; 3969ecd6cf80Smarks int error; 3970ecd6cf80Smarks 3971ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 3972ecd6cf80Smarks return (no_memory(hdl)); 3973ecd6cf80Smarks 3974ecd6cf80Smarks uid = ucred_geteuid(cred); 3975ecd6cf80Smarks gid = ucred_getegid(cred); 3976ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 3977ecd6cf80Smarks 3978ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 3979ecd6cf80Smarks return (1); 3980ecd6cf80Smarks 3981ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 3982ecd6cf80Smarks nvlist_free(nvp); 3983ecd6cf80Smarks return (1); 3984ecd6cf80Smarks } 3985ecd6cf80Smarks 3986ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 3987ecd6cf80Smarks nvlist_free(nvp); 3988ecd6cf80Smarks return (1); 3989ecd6cf80Smarks } 3990ecd6cf80Smarks 3991ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 3992ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 3993ecd6cf80Smarks nvlist_free(nvp); 3994ecd6cf80Smarks return (1); 3995ecd6cf80Smarks } 3996ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3997ecd6cf80Smarks 3998990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 3999ecd6cf80Smarks return (-1); 4000ecd6cf80Smarks 4001ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 4002ecd6cf80Smarks nvlist_free(nvp); 4003ecd6cf80Smarks return (error); 4004ecd6cf80Smarks } 4005ecd6cf80Smarks 4006ecd6cf80Smarks int 4007ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 4008da6c28aaSamw void *export, void *sharetab, int sharemax, zfs_share_op_t operation) 4009ecd6cf80Smarks { 4010ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 4011ecd6cf80Smarks int error; 4012ecd6cf80Smarks 4013ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4014ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4015ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 4016ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 4017da6c28aaSamw zc.zc_share.z_sharetype = operation; 4018ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 4019ecd6cf80Smarks 4020ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 4021ecd6cf80Smarks return (error); 4022ecd6cf80Smarks } 4023