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 /* 239e6eda55Smarks * Copyright 2008 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 15926949a980Smarks static char * 15936949a980Smarks zfs_deleg_perm_note(zfs_deleg_note_t note) 15946949a980Smarks { 15956949a980Smarks /* 15966949a980Smarks * Don't put newlines on end of lines 15976949a980Smarks */ 15986949a980Smarks switch (note) { 15996949a980Smarks case ZFS_DELEG_NOTE_CREATE: 16006949a980Smarks return (dgettext(TEXT_DOMAIN, 16016949a980Smarks "Must also have the 'mount' ability")); 16026949a980Smarks case ZFS_DELEG_NOTE_DESTROY: 16036949a980Smarks return (dgettext(TEXT_DOMAIN, 16046949a980Smarks "Must also have the 'mount' ability")); 16056949a980Smarks case ZFS_DELEG_NOTE_SNAPSHOT: 16066949a980Smarks return (dgettext(TEXT_DOMAIN, 16076949a980Smarks "Must also have the 'mount' ability")); 16086949a980Smarks case ZFS_DELEG_NOTE_ROLLBACK: 16096949a980Smarks return (dgettext(TEXT_DOMAIN, 16106949a980Smarks "Must also have the 'mount' ability")); 16116949a980Smarks case ZFS_DELEG_NOTE_CLONE: 16126949a980Smarks return (dgettext(TEXT_DOMAIN, "Must also have the 'create' " 16136949a980Smarks "ability and 'mount'\n" 16146949a980Smarks "\t\t\t\tability in the origin file system")); 16156949a980Smarks case ZFS_DELEG_NOTE_PROMOTE: 16166949a980Smarks return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'\n" 16176949a980Smarks "\t\t\t\tand 'promote' ability in the origin file system")); 16186949a980Smarks case ZFS_DELEG_NOTE_RENAME: 16196949a980Smarks return (dgettext(TEXT_DOMAIN, "Must also have the 'mount' " 16206949a980Smarks "and 'create' \n\t\t\t\tability in the new parent")); 16216949a980Smarks case ZFS_DELEG_NOTE_RECEIVE: 16226949a980Smarks return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'" 16236949a980Smarks " and 'create' ability")); 16246949a980Smarks case ZFS_DELEG_NOTE_USERPROP: 16256949a980Smarks return (dgettext(TEXT_DOMAIN, 16266949a980Smarks "Allows changing any user property")); 16276949a980Smarks case ZFS_DELEG_NOTE_ALLOW: 16286949a980Smarks return (dgettext(TEXT_DOMAIN, 16296949a980Smarks "Must also have the permission that is being\n" 16306949a980Smarks "\t\t\t\tallowed")); 16316949a980Smarks case ZFS_DELEG_NOTE_MOUNT: 16326949a980Smarks return (dgettext(TEXT_DOMAIN, 16336949a980Smarks "Allows mount/umount of ZFS datasets")); 16346949a980Smarks case ZFS_DELEG_NOTE_SHARE: 16356949a980Smarks return (dgettext(TEXT_DOMAIN, 16366949a980Smarks "Allows sharing file systems over NFS or SMB\n" 16376949a980Smarks "\t\t\t\tprotocols")); 16386949a980Smarks case ZFS_DELEG_NOTE_NONE: 16396949a980Smarks default: 16406949a980Smarks return (dgettext(TEXT_DOMAIN, "")); 16416949a980Smarks } 16426949a980Smarks } 16436949a980Smarks 16446949a980Smarks typedef enum { 16456949a980Smarks ZFS_DELEG_SUBCOMMAND, 16466949a980Smarks ZFS_DELEG_PROP, 16476949a980Smarks ZFS_DELEG_OTHER 16486949a980Smarks } zfs_deleg_perm_type_t; 16496949a980Smarks 16506949a980Smarks /* 16516949a980Smarks * is the permission a subcommand or other? 16526949a980Smarks */ 16536949a980Smarks zfs_deleg_perm_type_t 16546949a980Smarks zfs_deleg_perm_type(const char *perm) 16556949a980Smarks { 16566949a980Smarks if (strcmp(perm, "userprop") == 0) 16576949a980Smarks return (ZFS_DELEG_OTHER); 16586949a980Smarks else 16596949a980Smarks return (ZFS_DELEG_SUBCOMMAND); 16606949a980Smarks } 16616949a980Smarks 16626949a980Smarks static char * 16636949a980Smarks zfs_deleg_perm_type_str(zfs_deleg_perm_type_t type) 16646949a980Smarks { 16656949a980Smarks switch (type) { 16666949a980Smarks case ZFS_DELEG_SUBCOMMAND: 16676949a980Smarks return (dgettext(TEXT_DOMAIN, "subcommand")); 16686949a980Smarks case ZFS_DELEG_PROP: 16696949a980Smarks return (dgettext(TEXT_DOMAIN, "property")); 16706949a980Smarks case ZFS_DELEG_OTHER: 16716949a980Smarks return (dgettext(TEXT_DOMAIN, "other")); 16726949a980Smarks } 16736949a980Smarks return (""); 16746949a980Smarks } 16756949a980Smarks 16766949a980Smarks /*ARGSUSED*/ 16776949a980Smarks static int 16786949a980Smarks zfs_deleg_prop_cb(int prop, void *cb) 16796949a980Smarks { 16806949a980Smarks if (zfs_prop_delegatable(prop)) 16816949a980Smarks (void) fprintf(stderr, "%-15s %-15s\n", zfs_prop_to_name(prop), 16826949a980Smarks zfs_deleg_perm_type_str(ZFS_DELEG_PROP)); 16836949a980Smarks 16846949a980Smarks return (ZPROP_CONT); 16856949a980Smarks } 16866949a980Smarks 16876949a980Smarks void 16886949a980Smarks zfs_deleg_permissions(void) 16896949a980Smarks { 16906949a980Smarks int i; 16916949a980Smarks 16926949a980Smarks (void) fprintf(stderr, "\n%-15s %-15s\t%s\n\n", "NAME", 16936949a980Smarks "TYPE", "NOTES"); 16946949a980Smarks 16956949a980Smarks /* 16966949a980Smarks * First print out the subcommands 16976949a980Smarks */ 16986949a980Smarks for (i = 0; zfs_deleg_perm_tab[i].z_perm != NULL; i++) { 16996949a980Smarks (void) fprintf(stderr, "%-15s %-15s\t%s\n", 17006949a980Smarks zfs_deleg_perm_tab[i].z_perm, 17016949a980Smarks zfs_deleg_perm_type_str( 17026949a980Smarks zfs_deleg_perm_type(zfs_deleg_perm_tab[i].z_perm)), 17036949a980Smarks zfs_deleg_perm_note(zfs_deleg_perm_tab[i].z_note)); 17046949a980Smarks } 17056949a980Smarks 17066949a980Smarks (void) zprop_iter(zfs_deleg_prop_cb, NULL, B_FALSE, B_TRUE, 17076949a980Smarks ZFS_TYPE_DATASET|ZFS_TYPE_VOLUME); 17086949a980Smarks } 17096949a980Smarks 1710e9dbad6fSeschrock /* 1711e9dbad6fSeschrock * Given a property name and value, set the property for the given dataset. 1712e9dbad6fSeschrock */ 1713e9dbad6fSeschrock int 1714e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) 1715e9dbad6fSeschrock { 1716e9dbad6fSeschrock zfs_cmd_t zc = { 0 }; 1717e9dbad6fSeschrock int ret = -1; 1718e9dbad6fSeschrock prop_changelist_t *cl = NULL; 1719e9dbad6fSeschrock char errbuf[1024]; 1720e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1721e9dbad6fSeschrock nvlist_t *nvl = NULL, *realprops; 1722e9dbad6fSeschrock zfs_prop_t prop; 1723*a227b7f4Shs24103 int do_prefix = 1; 1724e9dbad6fSeschrock 1725e9dbad6fSeschrock (void) snprintf(errbuf, sizeof (errbuf), 1726e9dbad6fSeschrock dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), 1727e9dbad6fSeschrock zhp->zfs_name); 1728e9dbad6fSeschrock 1729e9dbad6fSeschrock if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 1730e9dbad6fSeschrock nvlist_add_string(nvl, propname, propval) != 0) { 1731e9dbad6fSeschrock (void) no_memory(hdl); 1732e9dbad6fSeschrock goto error; 1733e9dbad6fSeschrock } 1734e9dbad6fSeschrock 1735990b4856Slling if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, nvl, 1736e9dbad6fSeschrock zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) 1737e9dbad6fSeschrock goto error; 1738990b4856Slling 1739e9dbad6fSeschrock nvlist_free(nvl); 1740e9dbad6fSeschrock nvl = realprops; 1741e9dbad6fSeschrock 1742e9dbad6fSeschrock prop = zfs_name_to_prop(propname); 1743e9dbad6fSeschrock 1744fa9e4066Sahrens if ((cl = changelist_gather(zhp, prop, 0)) == NULL) 1745e9dbad6fSeschrock goto error; 1746fa9e4066Sahrens 1747fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 174899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1749fa9e4066Sahrens "child dataset with inherited mountpoint is used " 175099653d4eSeschrock "in a non-global zone")); 175199653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1752fa9e4066Sahrens goto error; 1753fa9e4066Sahrens } 1754fa9e4066Sahrens 1755*a227b7f4Shs24103 1756*a227b7f4Shs24103 /* do not unmount dataset if canmount is being set to noauto */ 1757*a227b7f4Shs24103 if (prop == ZFS_PROP_CANMOUNT && *propval == ZFS_CANMOUNT_NOAUTO) 1758*a227b7f4Shs24103 do_prefix = 0; 1759*a227b7f4Shs24103 1760*a227b7f4Shs24103 if (do_prefix && (ret = changelist_prefix(cl)) != 0) 1761fa9e4066Sahrens goto error; 1762fa9e4066Sahrens 1763fa9e4066Sahrens /* 1764fa9e4066Sahrens * Execute the corresponding ioctl() to set this property. 1765fa9e4066Sahrens */ 1766fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1767fa9e4066Sahrens 1768990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) 1769e9dbad6fSeschrock goto error; 1770e9dbad6fSeschrock 1771ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); 1772fa9e4066Sahrens if (ret != 0) { 1773fa9e4066Sahrens switch (errno) { 1774fa9e4066Sahrens 1775fa9e4066Sahrens case ENOSPC: 1776fa9e4066Sahrens /* 1777fa9e4066Sahrens * For quotas and reservations, ENOSPC indicates 1778fa9e4066Sahrens * something different; setting a quota or reservation 1779fa9e4066Sahrens * doesn't use any disk space. 1780fa9e4066Sahrens */ 1781fa9e4066Sahrens switch (prop) { 1782fa9e4066Sahrens case ZFS_PROP_QUOTA: 1783a9799022Sck153898 case ZFS_PROP_REFQUOTA: 178499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 178599653d4eSeschrock "size is less than current used or " 178699653d4eSeschrock "reserved space")); 178799653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1788fa9e4066Sahrens break; 1789fa9e4066Sahrens 1790fa9e4066Sahrens case ZFS_PROP_RESERVATION: 1791a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 179299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 179399653d4eSeschrock "size is greater than available space")); 179499653d4eSeschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 1795fa9e4066Sahrens break; 1796fa9e4066Sahrens 1797fa9e4066Sahrens default: 179899653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1799fa9e4066Sahrens break; 1800fa9e4066Sahrens } 1801fa9e4066Sahrens break; 1802fa9e4066Sahrens 1803fa9e4066Sahrens case EBUSY: 180499653d4eSeschrock if (prop == ZFS_PROP_VOLBLOCKSIZE) 180599653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); 180699653d4eSeschrock else 1807e9dbad6fSeschrock (void) zfs_standard_error(hdl, EBUSY, errbuf); 1808fa9e4066Sahrens break; 1809fa9e4066Sahrens 18102a79c5feSlling case EROFS: 181199653d4eSeschrock (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 18122a79c5feSlling break; 18132a79c5feSlling 1814c9431fa1Sahl case ENOTSUP: 1815c9431fa1Sahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 18169e6eda55Smarks "pool and or dataset must be upgraded to set this " 181740feaa91Sahrens "property or value")); 1818c9431fa1Sahl (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 1819c9431fa1Sahl break; 1820c9431fa1Sahl 1821fa9e4066Sahrens case EOVERFLOW: 1822fa9e4066Sahrens /* 1823fa9e4066Sahrens * This platform can't address a volume this big. 1824fa9e4066Sahrens */ 1825fa9e4066Sahrens #ifdef _ILP32 1826fa9e4066Sahrens if (prop == ZFS_PROP_VOLSIZE) { 182799653d4eSeschrock (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 1828fa9e4066Sahrens break; 1829fa9e4066Sahrens } 1830fa9e4066Sahrens #endif 183199653d4eSeschrock /* FALLTHROUGH */ 1832fa9e4066Sahrens default: 183399653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 1834fa9e4066Sahrens } 1835fa9e4066Sahrens } else { 1836*a227b7f4Shs24103 if (do_prefix) 1837*a227b7f4Shs24103 ret = changelist_postfix(cl); 1838*a227b7f4Shs24103 1839fa9e4066Sahrens /* 1840fa9e4066Sahrens * Refresh the statistics so the new property value 1841fa9e4066Sahrens * is reflected. 1842fa9e4066Sahrens */ 1843*a227b7f4Shs24103 if (ret == 0) 1844fa9e4066Sahrens (void) get_stats(zhp); 1845fa9e4066Sahrens } 1846fa9e4066Sahrens 1847fa9e4066Sahrens error: 1848e9dbad6fSeschrock nvlist_free(nvl); 1849e9dbad6fSeschrock zcmd_free_nvlists(&zc); 1850e9dbad6fSeschrock if (cl) 1851fa9e4066Sahrens changelist_free(cl); 1852fa9e4066Sahrens return (ret); 1853fa9e4066Sahrens } 1854fa9e4066Sahrens 1855fa9e4066Sahrens /* 1856fa9e4066Sahrens * Given a property, inherit the value from the parent dataset. 1857fa9e4066Sahrens */ 1858fa9e4066Sahrens int 1859e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) 1860fa9e4066Sahrens { 1861fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 1862fa9e4066Sahrens int ret; 1863fa9e4066Sahrens prop_changelist_t *cl; 186499653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 186599653d4eSeschrock char errbuf[1024]; 1866e9dbad6fSeschrock zfs_prop_t prop; 186799653d4eSeschrock 186899653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 186999653d4eSeschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 1870fa9e4066Sahrens 1871990b4856Slling if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { 1872e9dbad6fSeschrock /* 1873e9dbad6fSeschrock * For user properties, the amount of work we have to do is very 1874e9dbad6fSeschrock * small, so just do it here. 1875e9dbad6fSeschrock */ 1876e9dbad6fSeschrock if (!zfs_prop_user(propname)) { 1877e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1878e9dbad6fSeschrock "invalid property")); 1879e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 1880e9dbad6fSeschrock } 1881e9dbad6fSeschrock 1882e9dbad6fSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1883e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1884e9dbad6fSeschrock 1885e45ce728Sahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0) 1886e9dbad6fSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1887e9dbad6fSeschrock 1888e9dbad6fSeschrock return (0); 1889e9dbad6fSeschrock } 1890e9dbad6fSeschrock 1891fa9e4066Sahrens /* 1892fa9e4066Sahrens * Verify that this property is inheritable. 1893fa9e4066Sahrens */ 189499653d4eSeschrock if (zfs_prop_readonly(prop)) 189599653d4eSeschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 1896fa9e4066Sahrens 189799653d4eSeschrock if (!zfs_prop_inheritable(prop)) 189899653d4eSeschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 1899fa9e4066Sahrens 1900fa9e4066Sahrens /* 1901fa9e4066Sahrens * Check to see if the value applies to this type 1902fa9e4066Sahrens */ 190399653d4eSeschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 190499653d4eSeschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 1905fa9e4066Sahrens 1906bf7c2d40Srm160521 /* 1907bf7c2d40Srm160521 * Normalize the name, to get rid of shorthand abbrevations. 1908bf7c2d40Srm160521 */ 1909bf7c2d40Srm160521 propname = zfs_prop_to_name(prop); 1910fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1911e9dbad6fSeschrock (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value)); 1912fa9e4066Sahrens 1913fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 1914fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 191599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 191699653d4eSeschrock "dataset is used in a non-global zone")); 191799653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 1918fa9e4066Sahrens } 1919fa9e4066Sahrens 1920fa9e4066Sahrens /* 1921fa9e4066Sahrens * Determine datasets which will be affected by this change, if any. 1922fa9e4066Sahrens */ 1923fa9e4066Sahrens if ((cl = changelist_gather(zhp, prop, 0)) == NULL) 1924fa9e4066Sahrens return (-1); 1925fa9e4066Sahrens 1926fa9e4066Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 192799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 192899653d4eSeschrock "child dataset with inherited mountpoint is used " 192999653d4eSeschrock "in a non-global zone")); 193099653d4eSeschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 1931fa9e4066Sahrens goto error; 1932fa9e4066Sahrens } 1933fa9e4066Sahrens 1934fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 1935fa9e4066Sahrens goto error; 1936fa9e4066Sahrens 1937e45ce728Sahrens if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) { 193899653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 1939fa9e4066Sahrens } else { 1940fa9e4066Sahrens 1941efc555ebSnd150628 if ((ret = changelist_postfix(cl)) != 0) 1942fa9e4066Sahrens goto error; 1943fa9e4066Sahrens 1944fa9e4066Sahrens /* 1945fa9e4066Sahrens * Refresh the statistics so the new property is reflected. 1946fa9e4066Sahrens */ 1947fa9e4066Sahrens (void) get_stats(zhp); 1948fa9e4066Sahrens } 1949fa9e4066Sahrens 1950fa9e4066Sahrens error: 1951fa9e4066Sahrens changelist_free(cl); 1952fa9e4066Sahrens return (ret); 1953fa9e4066Sahrens } 1954fa9e4066Sahrens 1955fa9e4066Sahrens /* 19567f7322feSeschrock * True DSL properties are stored in an nvlist. The following two functions 19577f7322feSeschrock * extract them appropriately. 19587f7322feSeschrock */ 19597f7322feSeschrock static uint64_t 19607f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 19617f7322feSeschrock { 19627f7322feSeschrock nvlist_t *nv; 19637f7322feSeschrock uint64_t value; 19647f7322feSeschrock 1965a2eea2e1Sahrens *source = NULL; 19667f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 19677f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1968990b4856Slling verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0); 1969990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 19707f7322feSeschrock } else { 19717f7322feSeschrock value = zfs_prop_default_numeric(prop); 19727f7322feSeschrock *source = ""; 19737f7322feSeschrock } 19747f7322feSeschrock 19757f7322feSeschrock return (value); 19767f7322feSeschrock } 19777f7322feSeschrock 19787f7322feSeschrock static char * 19797f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 19807f7322feSeschrock { 19817f7322feSeschrock nvlist_t *nv; 19827f7322feSeschrock char *value; 19837f7322feSeschrock 1984a2eea2e1Sahrens *source = NULL; 19857f7322feSeschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 19867f7322feSeschrock zfs_prop_to_name(prop), &nv) == 0) { 1987990b4856Slling verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); 1988990b4856Slling (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); 19897f7322feSeschrock } else { 19907f7322feSeschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 19917f7322feSeschrock value = ""; 19927f7322feSeschrock *source = ""; 19937f7322feSeschrock } 19947f7322feSeschrock 19957f7322feSeschrock return (value); 19967f7322feSeschrock } 19977f7322feSeschrock 19987f7322feSeschrock /* 1999fa9e4066Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 2000fa9e4066Sahrens * zfs_prop_get_int() are built using this interface. 2001fa9e4066Sahrens * 2002fa9e4066Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 2003fa9e4066Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 2004fa9e4066Sahrens * If they differ from the on-disk values, report the current values and mark 2005fa9e4066Sahrens * the source "temporary". 2006fa9e4066Sahrens */ 200799653d4eSeschrock static int 2008990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, 200999653d4eSeschrock char **source, uint64_t *val) 2010fa9e4066Sahrens { 2011bd00f61bSrm160521 zfs_cmd_t zc = { 0 }; 201296510749Stimh nvlist_t *zplprops = NULL; 2013fa9e4066Sahrens struct mnttab mnt; 20143ccfa83cSahrens char *mntopt_on = NULL; 20153ccfa83cSahrens char *mntopt_off = NULL; 2016fa9e4066Sahrens 2017fa9e4066Sahrens *source = NULL; 2018fa9e4066Sahrens 20193ccfa83cSahrens switch (prop) { 20203ccfa83cSahrens case ZFS_PROP_ATIME: 20213ccfa83cSahrens mntopt_on = MNTOPT_ATIME; 20223ccfa83cSahrens mntopt_off = MNTOPT_NOATIME; 20233ccfa83cSahrens break; 20243ccfa83cSahrens 20253ccfa83cSahrens case ZFS_PROP_DEVICES: 20263ccfa83cSahrens mntopt_on = MNTOPT_DEVICES; 20273ccfa83cSahrens mntopt_off = MNTOPT_NODEVICES; 20283ccfa83cSahrens break; 20293ccfa83cSahrens 20303ccfa83cSahrens case ZFS_PROP_EXEC: 20313ccfa83cSahrens mntopt_on = MNTOPT_EXEC; 20323ccfa83cSahrens mntopt_off = MNTOPT_NOEXEC; 20333ccfa83cSahrens break; 20343ccfa83cSahrens 20353ccfa83cSahrens case ZFS_PROP_READONLY: 20363ccfa83cSahrens mntopt_on = MNTOPT_RO; 20373ccfa83cSahrens mntopt_off = MNTOPT_RW; 20383ccfa83cSahrens break; 20393ccfa83cSahrens 20403ccfa83cSahrens case ZFS_PROP_SETUID: 20413ccfa83cSahrens mntopt_on = MNTOPT_SETUID; 20423ccfa83cSahrens mntopt_off = MNTOPT_NOSETUID; 20433ccfa83cSahrens break; 20443ccfa83cSahrens 20453ccfa83cSahrens case ZFS_PROP_XATTR: 20463ccfa83cSahrens mntopt_on = MNTOPT_XATTR; 20473ccfa83cSahrens mntopt_off = MNTOPT_NOXATTR; 20483ccfa83cSahrens break; 2049da6c28aaSamw 2050da6c28aaSamw case ZFS_PROP_NBMAND: 2051da6c28aaSamw mntopt_on = MNTOPT_NBMAND; 2052da6c28aaSamw mntopt_off = MNTOPT_NONBMAND; 2053da6c28aaSamw break; 20543ccfa83cSahrens } 20553ccfa83cSahrens 20563bb79becSeschrock /* 20573bb79becSeschrock * Because looking up the mount options is potentially expensive 20583bb79becSeschrock * (iterating over all of /etc/mnttab), we defer its calculation until 20593bb79becSeschrock * we're looking up a property which requires its presence. 20603bb79becSeschrock */ 20613bb79becSeschrock if (!zhp->zfs_mntcheck && 20623ccfa83cSahrens (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) { 20633ccfa83cSahrens struct mnttab entry, search = { 0 }; 20643ccfa83cSahrens FILE *mnttab = zhp->zfs_hdl->libzfs_mnttab; 20653bb79becSeschrock 20663bb79becSeschrock search.mnt_special = (char *)zhp->zfs_name; 20673bb79becSeschrock search.mnt_fstype = MNTTYPE_ZFS; 20683ccfa83cSahrens rewind(mnttab); 20693bb79becSeschrock 20703ccfa83cSahrens if (getmntany(mnttab, &entry, &search) == 0) { 20713ccfa83cSahrens zhp->zfs_mntopts = zfs_strdup(zhp->zfs_hdl, 20723ccfa83cSahrens entry.mnt_mntopts); 20733ccfa83cSahrens if (zhp->zfs_mntopts == NULL) 20743bb79becSeschrock return (-1); 20753ccfa83cSahrens } 20763bb79becSeschrock 20773bb79becSeschrock zhp->zfs_mntcheck = B_TRUE; 20783bb79becSeschrock } 20793bb79becSeschrock 2080fa9e4066Sahrens if (zhp->zfs_mntopts == NULL) 2081fa9e4066Sahrens mnt.mnt_mntopts = ""; 2082fa9e4066Sahrens else 2083fa9e4066Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 2084fa9e4066Sahrens 2085fa9e4066Sahrens switch (prop) { 2086fa9e4066Sahrens case ZFS_PROP_ATIME: 2087fa9e4066Sahrens case ZFS_PROP_DEVICES: 2088fa9e4066Sahrens case ZFS_PROP_EXEC: 20893ccfa83cSahrens case ZFS_PROP_READONLY: 20903ccfa83cSahrens case ZFS_PROP_SETUID: 20913ccfa83cSahrens case ZFS_PROP_XATTR: 2092da6c28aaSamw case ZFS_PROP_NBMAND: 209399653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 2094fa9e4066Sahrens 20953ccfa83cSahrens if (hasmntopt(&mnt, mntopt_on) && !*val) { 209699653d4eSeschrock *val = B_TRUE; 2097fa9e4066Sahrens if (src) 2098990b4856Slling *src = ZPROP_SRC_TEMPORARY; 20993ccfa83cSahrens } else if (hasmntopt(&mnt, mntopt_off) && *val) { 210099653d4eSeschrock *val = B_FALSE; 2101fa9e4066Sahrens if (src) 2102990b4856Slling *src = ZPROP_SRC_TEMPORARY; 2103fa9e4066Sahrens } 210499653d4eSeschrock break; 2105fa9e4066Sahrens 21063ccfa83cSahrens case ZFS_PROP_CANMOUNT: 210799653d4eSeschrock *val = getprop_uint64(zhp, prop, source); 2108*a227b7f4Shs24103 if (*val != ZFS_CANMOUNT_ON) 2109fda77a98Srm160521 *source = zhp->zfs_name; 2110fda77a98Srm160521 else 2111fda77a98Srm160521 *source = ""; /* default */ 211299653d4eSeschrock break; 2113fa9e4066Sahrens 2114fa9e4066Sahrens case ZFS_PROP_QUOTA: 2115a9799022Sck153898 case ZFS_PROP_REFQUOTA: 2116fa9e4066Sahrens case ZFS_PROP_RESERVATION: 2117a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 2118a2eea2e1Sahrens *val = getprop_uint64(zhp, prop, source); 2119a2eea2e1Sahrens if (*val == 0) 2120fa9e4066Sahrens *source = ""; /* default */ 2121fa9e4066Sahrens else 2122fa9e4066Sahrens *source = zhp->zfs_name; 212399653d4eSeschrock break; 2124fa9e4066Sahrens 2125fa9e4066Sahrens case ZFS_PROP_MOUNTED: 212699653d4eSeschrock *val = (zhp->zfs_mntopts != NULL); 212799653d4eSeschrock break; 2128fa9e4066Sahrens 212939c23413Seschrock case ZFS_PROP_NUMCLONES: 213039c23413Seschrock *val = zhp->zfs_dmustats.dds_num_clones; 213139c23413Seschrock break; 213239c23413Seschrock 2133bd00f61bSrm160521 case ZFS_PROP_VERSION: 2134de8267e0Stimh case ZFS_PROP_NORMALIZE: 2135de8267e0Stimh case ZFS_PROP_UTF8ONLY: 2136de8267e0Stimh case ZFS_PROP_CASE: 2137de8267e0Stimh if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || 2138de8267e0Stimh zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 21393d7934e1Srm160521 return (-1); 2140bd00f61bSrm160521 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2141de8267e0Stimh if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) { 2142de8267e0Stimh zcmd_free_nvlists(&zc); 2143bd00f61bSrm160521 zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 2144de8267e0Stimh "unable to get %s property"), 2145de8267e0Stimh zfs_prop_to_name(prop)); 2146bd00f61bSrm160521 return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, 2147bd00f61bSrm160521 dgettext(TEXT_DOMAIN, "internal error"))); 2148bd00f61bSrm160521 } 2149de8267e0Stimh if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 || 2150de8267e0Stimh nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop), 2151de8267e0Stimh val) != 0) { 2152de8267e0Stimh zcmd_free_nvlists(&zc); 2153de8267e0Stimh zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 2154de8267e0Stimh "unable to get %s property"), 2155de8267e0Stimh zfs_prop_to_name(prop)); 2156de8267e0Stimh return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM, 2157de8267e0Stimh dgettext(TEXT_DOMAIN, "internal error"))); 2158de8267e0Stimh } 215996510749Stimh if (zplprops) 216096510749Stimh nvlist_free(zplprops); 2161de8267e0Stimh zcmd_free_nvlists(&zc); 2162bd00f61bSrm160521 break; 2163bd00f61bSrm160521 2164fa9e4066Sahrens default: 2165e7437265Sahrens switch (zfs_prop_get_type(prop)) { 216691ebeef5Sahrens case PROP_TYPE_NUMBER: 216791ebeef5Sahrens case PROP_TYPE_INDEX: 2168e7437265Sahrens *val = getprop_uint64(zhp, prop, source); 2169e7437265Sahrens break; 2170e7437265Sahrens 217191ebeef5Sahrens case PROP_TYPE_STRING: 2172e7437265Sahrens default: 217399653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 217499653d4eSeschrock "cannot get non-numeric property")); 217599653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 217699653d4eSeschrock dgettext(TEXT_DOMAIN, "internal error"))); 2177fa9e4066Sahrens } 2178e7437265Sahrens } 2179fa9e4066Sahrens 2180fa9e4066Sahrens return (0); 2181fa9e4066Sahrens } 2182fa9e4066Sahrens 2183fa9e4066Sahrens /* 2184fa9e4066Sahrens * Calculate the source type, given the raw source string. 2185fa9e4066Sahrens */ 2186fa9e4066Sahrens static void 2187990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, 2188fa9e4066Sahrens char *statbuf, size_t statlen) 2189fa9e4066Sahrens { 2190990b4856Slling if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY) 2191fa9e4066Sahrens return; 2192fa9e4066Sahrens 2193fa9e4066Sahrens if (source == NULL) { 2194990b4856Slling *srctype = ZPROP_SRC_NONE; 2195fa9e4066Sahrens } else if (source[0] == '\0') { 2196990b4856Slling *srctype = ZPROP_SRC_DEFAULT; 2197fa9e4066Sahrens } else { 2198fa9e4066Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 2199990b4856Slling *srctype = ZPROP_SRC_LOCAL; 2200fa9e4066Sahrens } else { 2201fa9e4066Sahrens (void) strlcpy(statbuf, source, statlen); 2202990b4856Slling *srctype = ZPROP_SRC_INHERITED; 2203fa9e4066Sahrens } 2204fa9e4066Sahrens } 2205fa9e4066Sahrens 2206fa9e4066Sahrens } 2207fa9e4066Sahrens 2208fa9e4066Sahrens /* 2209fa9e4066Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 2210fa9e4066Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 2211fa9e4066Sahrens * human-readable form. 2212fa9e4066Sahrens * 2213fa9e4066Sahrens * Returns 0 on success, or -1 on error. 2214fa9e4066Sahrens */ 2215fa9e4066Sahrens int 2216fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 2217990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 2218fa9e4066Sahrens { 2219fa9e4066Sahrens char *source = NULL; 2220fa9e4066Sahrens uint64_t val; 2221fa9e4066Sahrens char *str; 2222fa9e4066Sahrens const char *root; 2223e9dbad6fSeschrock const char *strval; 2224fa9e4066Sahrens 2225fa9e4066Sahrens /* 2226fa9e4066Sahrens * Check to see if this property applies to our object 2227fa9e4066Sahrens */ 2228fa9e4066Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 2229fa9e4066Sahrens return (-1); 2230fa9e4066Sahrens 2231fa9e4066Sahrens if (src) 2232990b4856Slling *src = ZPROP_SRC_NONE; 2233fa9e4066Sahrens 2234fa9e4066Sahrens switch (prop) { 2235fa9e4066Sahrens case ZFS_PROP_CREATION: 2236fa9e4066Sahrens /* 2237fa9e4066Sahrens * 'creation' is a time_t stored in the statistics. We convert 2238fa9e4066Sahrens * this into a string unless 'literal' is specified. 2239fa9e4066Sahrens */ 2240fa9e4066Sahrens { 2241a2eea2e1Sahrens val = getprop_uint64(zhp, prop, &source); 2242a2eea2e1Sahrens time_t time = (time_t)val; 2243fa9e4066Sahrens struct tm t; 2244fa9e4066Sahrens 2245fa9e4066Sahrens if (literal || 2246fa9e4066Sahrens localtime_r(&time, &t) == NULL || 2247fa9e4066Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 2248fa9e4066Sahrens &t) == 0) 2249a2eea2e1Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 2250fa9e4066Sahrens } 2251fa9e4066Sahrens break; 2252fa9e4066Sahrens 2253fa9e4066Sahrens case ZFS_PROP_MOUNTPOINT: 2254fa9e4066Sahrens /* 2255fa9e4066Sahrens * Getting the precise mountpoint can be tricky. 2256fa9e4066Sahrens * 2257fa9e4066Sahrens * - for 'none' or 'legacy', return those values. 2258fa9e4066Sahrens * - for default mountpoints, construct it as /zfs/<dataset> 2259fa9e4066Sahrens * - for inherited mountpoints, we want to take everything 2260fa9e4066Sahrens * after our ancestor and append it to the inherited value. 2261fa9e4066Sahrens * 2262fa9e4066Sahrens * If the pool has an alternate root, we want to prepend that 2263fa9e4066Sahrens * root to any values we return. 2264fa9e4066Sahrens */ 2265ea8dc4b6Seschrock root = zhp->zfs_root; 22667f7322feSeschrock str = getprop_string(zhp, prop, &source); 2267fa9e4066Sahrens 22687f7322feSeschrock if (str[0] == '\0') { 2269fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s/zfs/%s", 2270fa9e4066Sahrens root, zhp->zfs_name); 22717f7322feSeschrock } else if (str[0] == '/') { 22727f7322feSeschrock const char *relpath = zhp->zfs_name + strlen(source); 2273fa9e4066Sahrens 2274fa9e4066Sahrens if (relpath[0] == '/') 2275fa9e4066Sahrens relpath++; 22767f7322feSeschrock if (str[1] == '\0') 22777f7322feSeschrock str++; 2278fa9e4066Sahrens 2279fa9e4066Sahrens if (relpath[0] == '\0') 2280fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s", 22817f7322feSeschrock root, str); 2282fa9e4066Sahrens else 2283fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 22847f7322feSeschrock root, str, relpath[0] == '@' ? "" : "/", 2285fa9e4066Sahrens relpath); 2286fa9e4066Sahrens } else { 2287fa9e4066Sahrens /* 'legacy' or 'none' */ 22887f7322feSeschrock (void) strlcpy(propbuf, str, proplen); 2289fa9e4066Sahrens } 2290fa9e4066Sahrens 2291fa9e4066Sahrens break; 2292fa9e4066Sahrens 2293fa9e4066Sahrens case ZFS_PROP_ORIGIN: 2294a2eea2e1Sahrens (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 2295fa9e4066Sahrens proplen); 2296fa9e4066Sahrens /* 2297fa9e4066Sahrens * If there is no parent at all, return failure to indicate that 2298fa9e4066Sahrens * it doesn't apply to this dataset. 2299fa9e4066Sahrens */ 2300fa9e4066Sahrens if (propbuf[0] == '\0') 2301fa9e4066Sahrens return (-1); 2302fa9e4066Sahrens break; 2303fa9e4066Sahrens 2304fa9e4066Sahrens case ZFS_PROP_QUOTA: 2305a9799022Sck153898 case ZFS_PROP_REFQUOTA: 2306fa9e4066Sahrens case ZFS_PROP_RESERVATION: 2307a9799022Sck153898 case ZFS_PROP_REFRESERVATION: 2308a9799022Sck153898 230999653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 231099653d4eSeschrock return (-1); 2311fa9e4066Sahrens 2312fa9e4066Sahrens /* 2313fa9e4066Sahrens * If quota or reservation is 0, we translate this into 'none' 2314fa9e4066Sahrens * (unless literal is set), and indicate that it's the default 2315fa9e4066Sahrens * value. Otherwise, we print the number nicely and indicate 2316fa9e4066Sahrens * that its set locally. 2317fa9e4066Sahrens */ 2318fa9e4066Sahrens if (val == 0) { 2319fa9e4066Sahrens if (literal) 2320fa9e4066Sahrens (void) strlcpy(propbuf, "0", proplen); 2321fa9e4066Sahrens else 2322fa9e4066Sahrens (void) strlcpy(propbuf, "none", proplen); 2323fa9e4066Sahrens } else { 2324fa9e4066Sahrens if (literal) 23255ad82045Snd150628 (void) snprintf(propbuf, proplen, "%llu", 23265ad82045Snd150628 (u_longlong_t)val); 2327fa9e4066Sahrens else 2328fa9e4066Sahrens zfs_nicenum(val, propbuf, proplen); 2329fa9e4066Sahrens } 2330fa9e4066Sahrens break; 2331fa9e4066Sahrens 2332fa9e4066Sahrens case ZFS_PROP_COMPRESSRATIO: 233399653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 233499653d4eSeschrock return (-1); 23355ad82045Snd150628 (void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t) 23365ad82045Snd150628 val / 100, (longlong_t)val % 100); 2337fa9e4066Sahrens break; 2338fa9e4066Sahrens 2339fa9e4066Sahrens case ZFS_PROP_TYPE: 2340fa9e4066Sahrens switch (zhp->zfs_type) { 2341fa9e4066Sahrens case ZFS_TYPE_FILESYSTEM: 2342fa9e4066Sahrens str = "filesystem"; 2343fa9e4066Sahrens break; 2344fa9e4066Sahrens case ZFS_TYPE_VOLUME: 2345fa9e4066Sahrens str = "volume"; 2346fa9e4066Sahrens break; 2347fa9e4066Sahrens case ZFS_TYPE_SNAPSHOT: 2348fa9e4066Sahrens str = "snapshot"; 2349fa9e4066Sahrens break; 2350fa9e4066Sahrens default: 235199653d4eSeschrock abort(); 2352fa9e4066Sahrens } 2353fa9e4066Sahrens (void) snprintf(propbuf, proplen, "%s", str); 2354fa9e4066Sahrens break; 2355fa9e4066Sahrens 2356fa9e4066Sahrens case ZFS_PROP_MOUNTED: 2357fa9e4066Sahrens /* 2358fa9e4066Sahrens * The 'mounted' property is a pseudo-property that described 2359fa9e4066Sahrens * whether the filesystem is currently mounted. Even though 2360fa9e4066Sahrens * it's a boolean value, the typical values of "on" and "off" 2361fa9e4066Sahrens * don't make sense, so we translate to "yes" and "no". 2362fa9e4066Sahrens */ 236399653d4eSeschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 236499653d4eSeschrock src, &source, &val) != 0) 236599653d4eSeschrock return (-1); 236699653d4eSeschrock if (val) 2367fa9e4066Sahrens (void) strlcpy(propbuf, "yes", proplen); 2368fa9e4066Sahrens else 2369fa9e4066Sahrens (void) strlcpy(propbuf, "no", proplen); 2370fa9e4066Sahrens break; 2371fa9e4066Sahrens 2372fa9e4066Sahrens case ZFS_PROP_NAME: 2373fa9e4066Sahrens /* 2374fa9e4066Sahrens * The 'name' property is a pseudo-property derived from the 2375fa9e4066Sahrens * dataset name. It is presented as a real property to simplify 2376fa9e4066Sahrens * consumers. 2377fa9e4066Sahrens */ 2378fa9e4066Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 2379fa9e4066Sahrens break; 2380fa9e4066Sahrens 2381fa9e4066Sahrens default: 2382e7437265Sahrens switch (zfs_prop_get_type(prop)) { 238391ebeef5Sahrens case PROP_TYPE_NUMBER: 2384e7437265Sahrens if (get_numeric_property(zhp, prop, src, 2385e7437265Sahrens &source, &val) != 0) 2386e7437265Sahrens return (-1); 2387e7437265Sahrens if (literal) 2388e7437265Sahrens (void) snprintf(propbuf, proplen, "%llu", 2389e7437265Sahrens (u_longlong_t)val); 2390e7437265Sahrens else 2391e7437265Sahrens zfs_nicenum(val, propbuf, proplen); 2392e7437265Sahrens break; 2393e7437265Sahrens 239491ebeef5Sahrens case PROP_TYPE_STRING: 2395e7437265Sahrens (void) strlcpy(propbuf, 2396e7437265Sahrens getprop_string(zhp, prop, &source), proplen); 2397e7437265Sahrens break; 2398e7437265Sahrens 239991ebeef5Sahrens case PROP_TYPE_INDEX: 2400fe192f76Sahrens if (get_numeric_property(zhp, prop, src, 2401fe192f76Sahrens &source, &val) != 0) 2402fe192f76Sahrens return (-1); 2403fe192f76Sahrens if (zfs_prop_index_to_string(prop, val, &strval) != 0) 2404e7437265Sahrens return (-1); 2405e7437265Sahrens (void) strlcpy(propbuf, strval, proplen); 2406e7437265Sahrens break; 2407e7437265Sahrens 2408e7437265Sahrens default: 240999653d4eSeschrock abort(); 2410fa9e4066Sahrens } 2411e7437265Sahrens } 2412fa9e4066Sahrens 2413fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2414fa9e4066Sahrens 2415fa9e4066Sahrens return (0); 2416fa9e4066Sahrens } 2417fa9e4066Sahrens 2418fa9e4066Sahrens /* 2419fa9e4066Sahrens * Utility function to get the given numeric property. Does no validation that 2420fa9e4066Sahrens * the given property is the appropriate type; should only be used with 2421fa9e4066Sahrens * hard-coded property types. 2422fa9e4066Sahrens */ 2423fa9e4066Sahrens uint64_t 2424fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 2425fa9e4066Sahrens { 2426fa9e4066Sahrens char *source; 242799653d4eSeschrock uint64_t val; 2428fa9e4066Sahrens 24293cb34c60Sahrens (void) get_numeric_property(zhp, prop, NULL, &source, &val); 243099653d4eSeschrock 243199653d4eSeschrock return (val); 2432fa9e4066Sahrens } 2433fa9e4066Sahrens 24347b97dc1aSrm160521 int 24357b97dc1aSrm160521 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) 24367b97dc1aSrm160521 { 24377b97dc1aSrm160521 char buf[64]; 24387b97dc1aSrm160521 24397b97dc1aSrm160521 zfs_nicenum(val, buf, sizeof (buf)); 24407b97dc1aSrm160521 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); 24417b97dc1aSrm160521 } 24427b97dc1aSrm160521 2443fa9e4066Sahrens /* 2444fa9e4066Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 2445fa9e4066Sahrens */ 2446fa9e4066Sahrens int 2447fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 2448990b4856Slling zprop_source_t *src, char *statbuf, size_t statlen) 2449fa9e4066Sahrens { 2450fa9e4066Sahrens char *source; 2451fa9e4066Sahrens 2452fa9e4066Sahrens /* 2453fa9e4066Sahrens * Check to see if this property applies to our object 2454fa9e4066Sahrens */ 2455e45ce728Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { 2456ece3d9b3Slling return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE, 245799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 245899653d4eSeschrock zfs_prop_to_name(prop))); 2459e45ce728Sahrens } 2460fa9e4066Sahrens 2461fa9e4066Sahrens if (src) 2462990b4856Slling *src = ZPROP_SRC_NONE; 2463fa9e4066Sahrens 246499653d4eSeschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 246599653d4eSeschrock return (-1); 2466fa9e4066Sahrens 2467fa9e4066Sahrens get_source(zhp, src, source, statbuf, statlen); 2468fa9e4066Sahrens 2469fa9e4066Sahrens return (0); 2470fa9e4066Sahrens } 2471fa9e4066Sahrens 2472fa9e4066Sahrens /* 2473fa9e4066Sahrens * Returns the name of the given zfs handle. 2474fa9e4066Sahrens */ 2475fa9e4066Sahrens const char * 2476fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp) 2477fa9e4066Sahrens { 2478fa9e4066Sahrens return (zhp->zfs_name); 2479fa9e4066Sahrens } 2480fa9e4066Sahrens 2481fa9e4066Sahrens /* 2482fa9e4066Sahrens * Returns the type of the given zfs handle. 2483fa9e4066Sahrens */ 2484fa9e4066Sahrens zfs_type_t 2485fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp) 2486fa9e4066Sahrens { 2487fa9e4066Sahrens return (zhp->zfs_type); 2488fa9e4066Sahrens } 2489fa9e4066Sahrens 2490fa9e4066Sahrens /* 24917f7322feSeschrock * Iterate over all child filesystems 2492fa9e4066Sahrens */ 2493fa9e4066Sahrens int 24947f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2495fa9e4066Sahrens { 2496fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2497fa9e4066Sahrens zfs_handle_t *nzhp; 2498fa9e4066Sahrens int ret; 2499fa9e4066Sahrens 25003cb34c60Sahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 25013cb34c60Sahrens return (0); 25023cb34c60Sahrens 2503fa9e4066Sahrens for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 250499653d4eSeschrock ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; 2505fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { 2506fa9e4066Sahrens /* 2507fa9e4066Sahrens * Ignore private dataset names. 2508fa9e4066Sahrens */ 2509fa9e4066Sahrens if (dataset_name_hidden(zc.zc_name)) 2510fa9e4066Sahrens continue; 2511fa9e4066Sahrens 2512fa9e4066Sahrens /* 2513fa9e4066Sahrens * Silently ignore errors, as the only plausible explanation is 2514fa9e4066Sahrens * that the pool has since been removed. 2515fa9e4066Sahrens */ 251699653d4eSeschrock if ((nzhp = make_dataset_handle(zhp->zfs_hdl, 251799653d4eSeschrock zc.zc_name)) == NULL) 2518fa9e4066Sahrens continue; 2519fa9e4066Sahrens 2520fa9e4066Sahrens if ((ret = func(nzhp, data)) != 0) 2521fa9e4066Sahrens return (ret); 2522fa9e4066Sahrens } 2523fa9e4066Sahrens 2524fa9e4066Sahrens /* 2525fa9e4066Sahrens * An errno value of ESRCH indicates normal completion. If ENOENT is 2526fa9e4066Sahrens * returned, then the underlying dataset has been removed since we 2527fa9e4066Sahrens * obtained the handle. 2528fa9e4066Sahrens */ 2529fa9e4066Sahrens if (errno != ESRCH && errno != ENOENT) 253099653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 253199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); 2532fa9e4066Sahrens 25337f7322feSeschrock return (0); 25347f7322feSeschrock } 25357f7322feSeschrock 25367f7322feSeschrock /* 25377f7322feSeschrock * Iterate over all snapshots 25387f7322feSeschrock */ 25397f7322feSeschrock int 25407f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25417f7322feSeschrock { 25427f7322feSeschrock zfs_cmd_t zc = { 0 }; 25437f7322feSeschrock zfs_handle_t *nzhp; 25447f7322feSeschrock int ret; 2545fa9e4066Sahrens 25463cb34c60Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 25473cb34c60Sahrens return (0); 25483cb34c60Sahrens 2549fa9e4066Sahrens for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 255099653d4eSeschrock ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, 255199653d4eSeschrock &zc) == 0; 2552fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { 2553fa9e4066Sahrens 255499653d4eSeschrock if ((nzhp = make_dataset_handle(zhp->zfs_hdl, 255599653d4eSeschrock zc.zc_name)) == NULL) 2556fa9e4066Sahrens continue; 2557fa9e4066Sahrens 2558fa9e4066Sahrens if ((ret = func(nzhp, data)) != 0) 2559fa9e4066Sahrens return (ret); 2560fa9e4066Sahrens } 2561fa9e4066Sahrens 2562fa9e4066Sahrens /* 2563fa9e4066Sahrens * An errno value of ESRCH indicates normal completion. If ENOENT is 2564fa9e4066Sahrens * returned, then the underlying dataset has been removed since we 2565fa9e4066Sahrens * obtained the handle. Silently ignore this case, and return success. 2566fa9e4066Sahrens */ 2567fa9e4066Sahrens if (errno != ESRCH && errno != ENOENT) 256899653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 256999653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); 2570fa9e4066Sahrens 2571fa9e4066Sahrens return (0); 2572fa9e4066Sahrens } 2573fa9e4066Sahrens 2574fa9e4066Sahrens /* 25757f7322feSeschrock * Iterate over all children, snapshots and filesystems 25767f7322feSeschrock */ 25777f7322feSeschrock int 25787f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 25797f7322feSeschrock { 25807f7322feSeschrock int ret; 25817f7322feSeschrock 25827f7322feSeschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 25837f7322feSeschrock return (ret); 25847f7322feSeschrock 25857f7322feSeschrock return (zfs_iter_snapshots(zhp, func, data)); 25867f7322feSeschrock } 25877f7322feSeschrock 25887f7322feSeschrock /* 2589fa9e4066Sahrens * Given a complete name, return just the portion that refers to the parent. 2590fa9e4066Sahrens * Can return NULL if this is a pool. 2591fa9e4066Sahrens */ 2592fa9e4066Sahrens static int 2593fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen) 2594fa9e4066Sahrens { 2595fa9e4066Sahrens char *loc; 2596fa9e4066Sahrens 2597fa9e4066Sahrens if ((loc = strrchr(path, '/')) == NULL) 2598fa9e4066Sahrens return (-1); 2599fa9e4066Sahrens 2600fa9e4066Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 2601fa9e4066Sahrens buf[loc - path] = '\0'; 2602fa9e4066Sahrens 2603fa9e4066Sahrens return (0); 2604fa9e4066Sahrens } 2605fa9e4066Sahrens 2606fa9e4066Sahrens /* 26077f1f55eaSvb160487 * If accept_ancestor is false, then check to make sure that the given path has 26087f1f55eaSvb160487 * a parent, and that it exists. If accept_ancestor is true, then find the 26097f1f55eaSvb160487 * closest existing ancestor for the given path. In prefixlen return the 26107f1f55eaSvb160487 * length of already existing prefix of the given path. We also fetch the 26117f1f55eaSvb160487 * 'zoned' property, which is used to validate property settings when creating 26127f1f55eaSvb160487 * new datasets. 2613fa9e4066Sahrens */ 2614fa9e4066Sahrens static int 26157f1f55eaSvb160487 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, 26167f1f55eaSvb160487 boolean_t accept_ancestor, int *prefixlen) 2617fa9e4066Sahrens { 2618fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2619fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2620fa9e4066Sahrens char *slash; 26217f7322feSeschrock zfs_handle_t *zhp; 262299653d4eSeschrock char errbuf[1024]; 262399653d4eSeschrock 262499653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), "cannot create '%s'", 262599653d4eSeschrock path); 2626fa9e4066Sahrens 2627fa9e4066Sahrens /* get parent, and check to see if this is just a pool */ 2628fa9e4066Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 262999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 263099653d4eSeschrock "missing dataset name")); 263199653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2632fa9e4066Sahrens } 2633fa9e4066Sahrens 2634fa9e4066Sahrens /* check to see if the pool exists */ 2635fa9e4066Sahrens if ((slash = strchr(parent, '/')) == NULL) 2636fa9e4066Sahrens slash = parent + strlen(parent); 2637fa9e4066Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 2638fa9e4066Sahrens zc.zc_name[slash - parent] = '\0'; 263999653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 2640fa9e4066Sahrens errno == ENOENT) { 264199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 264299653d4eSeschrock "no such pool '%s'"), zc.zc_name); 264399653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2644fa9e4066Sahrens } 2645fa9e4066Sahrens 2646fa9e4066Sahrens /* check to see if the parent dataset exists */ 26477f1f55eaSvb160487 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 26487f1f55eaSvb160487 if (errno == ENOENT && accept_ancestor) { 26497f1f55eaSvb160487 /* 26507f1f55eaSvb160487 * Go deeper to find an ancestor, give up on top level. 26517f1f55eaSvb160487 */ 26527f1f55eaSvb160487 if (parent_name(parent, parent, sizeof (parent)) != 0) { 26537f1f55eaSvb160487 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26547f1f55eaSvb160487 "no such pool '%s'"), zc.zc_name); 26557f1f55eaSvb160487 return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26567f1f55eaSvb160487 } 26577f1f55eaSvb160487 } else if (errno == ENOENT) { 265899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 265999653d4eSeschrock "parent does not exist")); 266099653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 26617f1f55eaSvb160487 } else 266299653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2663fa9e4066Sahrens } 2664fa9e4066Sahrens 2665e9dbad6fSeschrock *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2666fa9e4066Sahrens /* we are in a non-global zone, but parent is in the global zone */ 2667e9dbad6fSeschrock if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) { 266899653d4eSeschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 26697f7322feSeschrock zfs_close(zhp); 2670fa9e4066Sahrens return (-1); 2671fa9e4066Sahrens } 2672fa9e4066Sahrens 2673fa9e4066Sahrens /* make sure parent is a filesystem */ 26747f7322feSeschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 267599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 267699653d4eSeschrock "parent is not a filesystem")); 267799653d4eSeschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 26787f7322feSeschrock zfs_close(zhp); 2679fa9e4066Sahrens return (-1); 2680fa9e4066Sahrens } 2681fa9e4066Sahrens 26827f7322feSeschrock zfs_close(zhp); 26837f1f55eaSvb160487 if (prefixlen != NULL) 26847f1f55eaSvb160487 *prefixlen = strlen(parent); 26857f1f55eaSvb160487 return (0); 26867f1f55eaSvb160487 } 26877f1f55eaSvb160487 26887f1f55eaSvb160487 /* 26897f1f55eaSvb160487 * Finds whether the dataset of the given type(s) exists. 26907f1f55eaSvb160487 */ 26917f1f55eaSvb160487 boolean_t 26927f1f55eaSvb160487 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) 26937f1f55eaSvb160487 { 26947f1f55eaSvb160487 zfs_handle_t *zhp; 26957f1f55eaSvb160487 2696f18faf3fSek110237 if (!zfs_validate_name(hdl, path, types, B_FALSE)) 26977f1f55eaSvb160487 return (B_FALSE); 26987f1f55eaSvb160487 26997f1f55eaSvb160487 /* 27007f1f55eaSvb160487 * Try to get stats for the dataset, which will tell us if it exists. 27017f1f55eaSvb160487 */ 27027f1f55eaSvb160487 if ((zhp = make_dataset_handle(hdl, path)) != NULL) { 27037f1f55eaSvb160487 int ds_type = zhp->zfs_type; 27047f1f55eaSvb160487 27057f1f55eaSvb160487 zfs_close(zhp); 27067f1f55eaSvb160487 if (types & ds_type) 27077f1f55eaSvb160487 return (B_TRUE); 27087f1f55eaSvb160487 } 27097f1f55eaSvb160487 return (B_FALSE); 27107f1f55eaSvb160487 } 27117f1f55eaSvb160487 27127f1f55eaSvb160487 /* 27133cb34c60Sahrens * Given a path to 'target', create all the ancestors between 27143cb34c60Sahrens * the prefixlen portion of the path, and the target itself. 27153cb34c60Sahrens * Fail if the initial prefixlen-ancestor does not already exist. 27163cb34c60Sahrens */ 27173cb34c60Sahrens int 27183cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen) 27193cb34c60Sahrens { 27203cb34c60Sahrens zfs_handle_t *h; 27213cb34c60Sahrens char *cp; 27223cb34c60Sahrens const char *opname; 27233cb34c60Sahrens 27243cb34c60Sahrens /* make sure prefix exists */ 27253cb34c60Sahrens cp = target + prefixlen; 27263cb34c60Sahrens if (*cp != '/') { 27273cb34c60Sahrens assert(strchr(cp, '/') == NULL); 27283cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27293cb34c60Sahrens } else { 27303cb34c60Sahrens *cp = '\0'; 27313cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27323cb34c60Sahrens *cp = '/'; 27333cb34c60Sahrens } 27343cb34c60Sahrens if (h == NULL) 27353cb34c60Sahrens return (-1); 27363cb34c60Sahrens zfs_close(h); 27373cb34c60Sahrens 27383cb34c60Sahrens /* 27393cb34c60Sahrens * Attempt to create, mount, and share any ancestor filesystems, 27403cb34c60Sahrens * up to the prefixlen-long one. 27413cb34c60Sahrens */ 27423cb34c60Sahrens for (cp = target + prefixlen + 1; 27433cb34c60Sahrens cp = strchr(cp, '/'); *cp = '/', cp++) { 27443cb34c60Sahrens char *logstr; 27453cb34c60Sahrens 27463cb34c60Sahrens *cp = '\0'; 27473cb34c60Sahrens 27483cb34c60Sahrens h = make_dataset_handle(hdl, target); 27493cb34c60Sahrens if (h) { 27503cb34c60Sahrens /* it already exists, nothing to do here */ 27513cb34c60Sahrens zfs_close(h); 27523cb34c60Sahrens continue; 27533cb34c60Sahrens } 27543cb34c60Sahrens 27553cb34c60Sahrens logstr = hdl->libzfs_log_str; 27563cb34c60Sahrens hdl->libzfs_log_str = NULL; 27573cb34c60Sahrens if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, 27583cb34c60Sahrens NULL) != 0) { 27593cb34c60Sahrens hdl->libzfs_log_str = logstr; 27603cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "create"); 27613cb34c60Sahrens goto ancestorerr; 27623cb34c60Sahrens } 27633cb34c60Sahrens 27643cb34c60Sahrens hdl->libzfs_log_str = logstr; 27653cb34c60Sahrens h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); 27663cb34c60Sahrens if (h == NULL) { 27673cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "open"); 27683cb34c60Sahrens goto ancestorerr; 27693cb34c60Sahrens } 27703cb34c60Sahrens 27713cb34c60Sahrens if (zfs_mount(h, NULL, 0) != 0) { 27723cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "mount"); 27733cb34c60Sahrens goto ancestorerr; 27743cb34c60Sahrens } 27753cb34c60Sahrens 27763cb34c60Sahrens if (zfs_share(h) != 0) { 27773cb34c60Sahrens opname = dgettext(TEXT_DOMAIN, "share"); 27783cb34c60Sahrens goto ancestorerr; 27793cb34c60Sahrens } 27803cb34c60Sahrens 27813cb34c60Sahrens zfs_close(h); 27823cb34c60Sahrens } 27833cb34c60Sahrens 27843cb34c60Sahrens return (0); 27853cb34c60Sahrens 27863cb34c60Sahrens ancestorerr: 27873cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27883cb34c60Sahrens "failed to %s ancestor '%s'"), opname, target); 27893cb34c60Sahrens return (-1); 27903cb34c60Sahrens } 27913cb34c60Sahrens 27923cb34c60Sahrens /* 27937f1f55eaSvb160487 * Creates non-existing ancestors of the given path. 27947f1f55eaSvb160487 */ 27957f1f55eaSvb160487 int 27967f1f55eaSvb160487 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) 27977f1f55eaSvb160487 { 27987f1f55eaSvb160487 int prefix; 27997f1f55eaSvb160487 uint64_t zoned; 28007f1f55eaSvb160487 char *path_copy; 28017f1f55eaSvb160487 int rc; 28027f1f55eaSvb160487 28037f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) 28047f1f55eaSvb160487 return (-1); 28057f1f55eaSvb160487 28067f1f55eaSvb160487 if ((path_copy = strdup(path)) != NULL) { 28077f1f55eaSvb160487 rc = create_parents(hdl, path_copy, prefix); 28087f1f55eaSvb160487 free(path_copy); 28097f1f55eaSvb160487 } 28107f1f55eaSvb160487 if (path_copy == NULL || rc != 0) 28117f1f55eaSvb160487 return (-1); 28127f1f55eaSvb160487 2813fa9e4066Sahrens return (0); 2814fa9e4066Sahrens } 2815fa9e4066Sahrens 2816fa9e4066Sahrens /* 2817e9dbad6fSeschrock * Create a new filesystem or volume. 2818fa9e4066Sahrens */ 2819fa9e4066Sahrens int 282099653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 2821e9dbad6fSeschrock nvlist_t *props) 2822fa9e4066Sahrens { 2823fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2824fa9e4066Sahrens int ret; 2825fa9e4066Sahrens uint64_t size = 0; 2826fa9e4066Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 282799653d4eSeschrock char errbuf[1024]; 2828e9dbad6fSeschrock uint64_t zoned; 282999653d4eSeschrock 283099653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 283199653d4eSeschrock "cannot create '%s'"), path); 2832fa9e4066Sahrens 2833fa9e4066Sahrens /* validate the path, taking care to note the extended error message */ 2834f18faf3fSek110237 if (!zfs_validate_name(hdl, path, type, B_TRUE)) 283599653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2836fa9e4066Sahrens 2837fa9e4066Sahrens /* validate parents exist */ 28387f1f55eaSvb160487 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) 2839fa9e4066Sahrens return (-1); 2840fa9e4066Sahrens 2841fa9e4066Sahrens /* 2842fa9e4066Sahrens * The failure modes when creating a dataset of a different type over 2843fa9e4066Sahrens * one that already exists is a little strange. In particular, if you 2844fa9e4066Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 2845fa9e4066Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 2846fa9e4066Sahrens * first try to see if the dataset exists. 2847fa9e4066Sahrens */ 2848fa9e4066Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2849990b4856Slling if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 285099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 285199653d4eSeschrock "dataset already exists")); 285299653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2853fa9e4066Sahrens } 2854fa9e4066Sahrens 2855fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) 2856fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2857fa9e4066Sahrens else 2858fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2859fa9e4066Sahrens 2860990b4856Slling if (props && (props = zfs_validate_properties(hdl, type, props, 2861b1b8ab34Slling zoned, NULL, errbuf)) == 0) 2862e9dbad6fSeschrock return (-1); 2863e9dbad6fSeschrock 2864fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME) { 28655c5460e9Seschrock /* 28665c5460e9Seschrock * If we are creating a volume, the size and block size must 28675c5460e9Seschrock * satisfy a few restraints. First, the blocksize must be a 28685c5460e9Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 28695c5460e9Seschrock * volsize must be a multiple of the block size, and cannot be 28705c5460e9Seschrock * zero. 28715c5460e9Seschrock */ 2872e9dbad6fSeschrock if (props == NULL || nvlist_lookup_uint64(props, 2873e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) { 2874e9dbad6fSeschrock nvlist_free(props); 287599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2876e9dbad6fSeschrock "missing volume size")); 2877e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2878fa9e4066Sahrens } 2879fa9e4066Sahrens 2880e9dbad6fSeschrock if ((ret = nvlist_lookup_uint64(props, 2881e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2882e9dbad6fSeschrock &blocksize)) != 0) { 2883e9dbad6fSeschrock if (ret == ENOENT) { 2884e9dbad6fSeschrock blocksize = zfs_prop_default_numeric( 2885e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2886e9dbad6fSeschrock } else { 2887e9dbad6fSeschrock nvlist_free(props); 288899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2889e9dbad6fSeschrock "missing volume block size")); 2890e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2891e9dbad6fSeschrock } 2892e9dbad6fSeschrock } 2893e9dbad6fSeschrock 2894e9dbad6fSeschrock if (size == 0) { 2895e9dbad6fSeschrock nvlist_free(props); 2896e9dbad6fSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2897e9dbad6fSeschrock "volume size cannot be zero")); 2898e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 28995c5460e9Seschrock } 29005c5460e9Seschrock 29015c5460e9Seschrock if (size % blocksize != 0) { 2902e9dbad6fSeschrock nvlist_free(props); 290399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2904e9dbad6fSeschrock "volume size must be a multiple of volume block " 2905e9dbad6fSeschrock "size")); 2906e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 2907e9dbad6fSeschrock } 29085c5460e9Seschrock } 29095c5460e9Seschrock 2910990b4856Slling if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0) 2911e9dbad6fSeschrock return (-1); 2912e9dbad6fSeschrock nvlist_free(props); 2913fa9e4066Sahrens 2914fa9e4066Sahrens /* create the dataset */ 2915ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); 2916fa9e4066Sahrens 2917b1b8ab34Slling if (ret == 0 && type == ZFS_TYPE_VOLUME) { 291899653d4eSeschrock ret = zvol_create_link(hdl, path); 2919b1b8ab34Slling if (ret) { 2920b1b8ab34Slling (void) zfs_standard_error(hdl, errno, 2921b1b8ab34Slling dgettext(TEXT_DOMAIN, 2922b1b8ab34Slling "Volume successfully created, but device links " 2923b1b8ab34Slling "were not created")); 2924b1b8ab34Slling zcmd_free_nvlists(&zc); 2925b1b8ab34Slling return (-1); 2926b1b8ab34Slling } 2927b1b8ab34Slling } 2928fa9e4066Sahrens 2929e9dbad6fSeschrock zcmd_free_nvlists(&zc); 2930e9dbad6fSeschrock 2931fa9e4066Sahrens /* check for failure */ 2932fa9e4066Sahrens if (ret != 0) { 2933fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 2934fa9e4066Sahrens (void) parent_name(path, parent, sizeof (parent)); 2935fa9e4066Sahrens 2936fa9e4066Sahrens switch (errno) { 2937fa9e4066Sahrens case ENOENT: 293899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 293999653d4eSeschrock "no such parent '%s'"), parent); 294099653d4eSeschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 2941fa9e4066Sahrens 2942fa9e4066Sahrens case EINVAL: 294399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2944d7d4af51Smmusante "parent '%s' is not a filesystem"), parent); 294599653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2946fa9e4066Sahrens 2947fa9e4066Sahrens case EDOM: 294899653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2949e9dbad6fSeschrock "volume block size must be power of 2 from " 2950e9dbad6fSeschrock "%u to %uk"), 2951fa9e4066Sahrens (uint_t)SPA_MINBLOCKSIZE, 2952fa9e4066Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 295399653d4eSeschrock 2954e9dbad6fSeschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 295599653d4eSeschrock 295640feaa91Sahrens case ENOTSUP: 295740feaa91Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 295840feaa91Sahrens "pool must be upgraded to set this " 295940feaa91Sahrens "property or value")); 296040feaa91Sahrens return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); 296140feaa91Sahrens 2962fa9e4066Sahrens #ifdef _ILP32 2963fa9e4066Sahrens case EOVERFLOW: 2964fa9e4066Sahrens /* 2965fa9e4066Sahrens * This platform can't address a volume this big. 2966fa9e4066Sahrens */ 296799653d4eSeschrock if (type == ZFS_TYPE_VOLUME) 296899653d4eSeschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 296999653d4eSeschrock errbuf)); 2970fa9e4066Sahrens #endif 297199653d4eSeschrock /* FALLTHROUGH */ 2972fa9e4066Sahrens default: 297399653d4eSeschrock return (zfs_standard_error(hdl, errno, errbuf)); 2974fa9e4066Sahrens } 2975fa9e4066Sahrens } 2976fa9e4066Sahrens 2977fa9e4066Sahrens return (0); 2978fa9e4066Sahrens } 2979fa9e4066Sahrens 2980fa9e4066Sahrens /* 2981fa9e4066Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 2982fa9e4066Sahrens * isn't mounted, and that there are no active dependents. 2983fa9e4066Sahrens */ 2984fa9e4066Sahrens int 2985fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp) 2986fa9e4066Sahrens { 2987fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 2988fa9e4066Sahrens 2989fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2990fa9e4066Sahrens 2991e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 2992f3861e1aSahl /* 2993ecd6cf80Smarks * If user doesn't have permissions to unshare volume, then 2994ecd6cf80Smarks * abort the request. This would only happen for a 2995ecd6cf80Smarks * non-privileged user. 2996f3861e1aSahl */ 2997ecd6cf80Smarks if (zfs_unshare_iscsi(zhp) != 0) { 2998ecd6cf80Smarks return (-1); 2999ecd6cf80Smarks } 3000f3861e1aSahl 300199653d4eSeschrock if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 3002fa9e4066Sahrens return (-1); 3003fa9e4066Sahrens 3004fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3005fa9e4066Sahrens } else { 3006fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3007fa9e4066Sahrens } 3008fa9e4066Sahrens 3009ecd6cf80Smarks if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { 3010ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, 301199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 301299653d4eSeschrock zhp->zfs_name)); 30131d452cf5Sahrens } 3014fa9e4066Sahrens 3015fa9e4066Sahrens remove_mountpoint(zhp); 3016fa9e4066Sahrens 3017fa9e4066Sahrens return (0); 3018fa9e4066Sahrens } 3019fa9e4066Sahrens 30201d452cf5Sahrens struct destroydata { 30211d452cf5Sahrens char *snapname; 30221d452cf5Sahrens boolean_t gotone; 30233ccfa83cSahrens boolean_t closezhp; 30241d452cf5Sahrens }; 30251d452cf5Sahrens 30261d452cf5Sahrens static int 30271d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg) 30281d452cf5Sahrens { 30291d452cf5Sahrens struct destroydata *dd = arg; 30301d452cf5Sahrens zfs_handle_t *szhp; 30311d452cf5Sahrens char name[ZFS_MAXNAMELEN]; 30323ccfa83cSahrens boolean_t closezhp = dd->closezhp; 30333ccfa83cSahrens int rv; 30341d452cf5Sahrens 3035e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 3036e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 3037e9dbad6fSeschrock (void) strlcat(name, dd->snapname, sizeof (name)); 30381d452cf5Sahrens 30391d452cf5Sahrens szhp = make_dataset_handle(zhp->zfs_hdl, name); 30401d452cf5Sahrens if (szhp) { 30411d452cf5Sahrens dd->gotone = B_TRUE; 30421d452cf5Sahrens zfs_close(szhp); 30431d452cf5Sahrens } 30441d452cf5Sahrens 30451d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 30461d452cf5Sahrens (void) zvol_remove_link(zhp->zfs_hdl, name); 30471d452cf5Sahrens /* 30481d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 30491d452cf5Sahrens * return an error, because then we wouldn't visit all 30501d452cf5Sahrens * the volumes. 30511d452cf5Sahrens */ 30521d452cf5Sahrens } 30531d452cf5Sahrens 30543ccfa83cSahrens dd->closezhp = B_TRUE; 30553ccfa83cSahrens rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg); 30563ccfa83cSahrens if (closezhp) 30573ccfa83cSahrens zfs_close(zhp); 30583ccfa83cSahrens return (rv); 30591d452cf5Sahrens } 30601d452cf5Sahrens 30611d452cf5Sahrens /* 30621d452cf5Sahrens * Destroys all snapshots with the given name in zhp & descendants. 30631d452cf5Sahrens */ 30641d452cf5Sahrens int 30651d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname) 30661d452cf5Sahrens { 30671d452cf5Sahrens zfs_cmd_t zc = { 0 }; 30681d452cf5Sahrens int ret; 30691d452cf5Sahrens struct destroydata dd = { 0 }; 30701d452cf5Sahrens 30711d452cf5Sahrens dd.snapname = snapname; 30721d452cf5Sahrens (void) zfs_remove_link_cb(zhp, &dd); 30731d452cf5Sahrens 30741d452cf5Sahrens if (!dd.gotone) { 3075ece3d9b3Slling return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT, 30761d452cf5Sahrens dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"), 30771d452cf5Sahrens zhp->zfs_name, snapname)); 30781d452cf5Sahrens } 30791d452cf5Sahrens 30801d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3081e9dbad6fSeschrock (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); 30821d452cf5Sahrens 3083ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc); 30841d452cf5Sahrens if (ret != 0) { 30851d452cf5Sahrens char errbuf[1024]; 30861d452cf5Sahrens 30871d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 30881d452cf5Sahrens "cannot destroy '%s@%s'"), zc.zc_name, snapname); 30891d452cf5Sahrens 30901d452cf5Sahrens switch (errno) { 30911d452cf5Sahrens case EEXIST: 30921d452cf5Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 30931d452cf5Sahrens "snapshot is cloned")); 30941d452cf5Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); 30951d452cf5Sahrens 30961d452cf5Sahrens default: 30971d452cf5Sahrens return (zfs_standard_error(zhp->zfs_hdl, errno, 30981d452cf5Sahrens errbuf)); 30991d452cf5Sahrens } 31001d452cf5Sahrens } 31011d452cf5Sahrens 31021d452cf5Sahrens return (0); 31031d452cf5Sahrens } 31041d452cf5Sahrens 3105fa9e4066Sahrens /* 3106fa9e4066Sahrens * Clones the given dataset. The target must be of the same type as the source. 3107fa9e4066Sahrens */ 3108fa9e4066Sahrens int 3109e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) 3110fa9e4066Sahrens { 3111fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3112fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 3113fa9e4066Sahrens int ret; 311499653d4eSeschrock char errbuf[1024]; 311599653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 3116e9dbad6fSeschrock zfs_type_t type; 3117e9dbad6fSeschrock uint64_t zoned; 3118fa9e4066Sahrens 3119fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 3120fa9e4066Sahrens 312199653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 312299653d4eSeschrock "cannot create '%s'"), target); 312399653d4eSeschrock 3124fa9e4066Sahrens /* validate the target name */ 3125f18faf3fSek110237 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE)) 312699653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3127fa9e4066Sahrens 3128fa9e4066Sahrens /* validate parents exist */ 31297f1f55eaSvb160487 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) 3130fa9e4066Sahrens return (-1); 3131fa9e4066Sahrens 3132fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3133fa9e4066Sahrens 3134fa9e4066Sahrens /* do the clone */ 3135e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) { 3136fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 31375f8e1617Snn35248 type = ZFS_TYPE_VOLUME; 3138e9dbad6fSeschrock } else { 3139fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 31405f8e1617Snn35248 type = ZFS_TYPE_FILESYSTEM; 3141e9dbad6fSeschrock } 3142e9dbad6fSeschrock 3143e9dbad6fSeschrock if (props) { 3144990b4856Slling if ((props = zfs_validate_properties(hdl, type, props, 3145b1b8ab34Slling zoned, zhp, errbuf)) == NULL) 3146e9dbad6fSeschrock return (-1); 3147e9dbad6fSeschrock 3148990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { 3149e9dbad6fSeschrock nvlist_free(props); 3150e9dbad6fSeschrock return (-1); 3151e9dbad6fSeschrock } 3152e9dbad6fSeschrock 3153e9dbad6fSeschrock nvlist_free(props); 3154e9dbad6fSeschrock } 3155fa9e4066Sahrens 3156fa9e4066Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 3157e9dbad6fSeschrock (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); 3158ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc); 3159fa9e4066Sahrens 3160e9dbad6fSeschrock zcmd_free_nvlists(&zc); 3161e9dbad6fSeschrock 3162fa9e4066Sahrens if (ret != 0) { 3163fa9e4066Sahrens switch (errno) { 3164fa9e4066Sahrens 3165fa9e4066Sahrens case ENOENT: 3166fa9e4066Sahrens /* 3167fa9e4066Sahrens * The parent doesn't exist. We should have caught this 3168fa9e4066Sahrens * above, but there may a race condition that has since 3169fa9e4066Sahrens * destroyed the parent. 3170fa9e4066Sahrens * 3171fa9e4066Sahrens * At this point, we don't know whether it's the source 3172fa9e4066Sahrens * that doesn't exist anymore, or whether the target 3173fa9e4066Sahrens * dataset doesn't exist. 3174fa9e4066Sahrens */ 317599653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 317699653d4eSeschrock "no such parent '%s'"), parent); 317799653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 3178fa9e4066Sahrens 317999653d4eSeschrock case EXDEV: 318099653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 318199653d4eSeschrock "source and target pools differ")); 318299653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 318399653d4eSeschrock errbuf)); 318499653d4eSeschrock 318599653d4eSeschrock default: 318699653d4eSeschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 318799653d4eSeschrock errbuf)); 318899653d4eSeschrock } 3189e9dbad6fSeschrock } else if (ZFS_IS_VOLUME(zhp)) { 319099653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, target); 319199653d4eSeschrock } 319299653d4eSeschrock 319399653d4eSeschrock return (ret); 319499653d4eSeschrock } 319599653d4eSeschrock 319699653d4eSeschrock typedef struct promote_data { 319799653d4eSeschrock char cb_mountpoint[MAXPATHLEN]; 319899653d4eSeschrock const char *cb_target; 319999653d4eSeschrock const char *cb_errbuf; 320099653d4eSeschrock uint64_t cb_pivot_txg; 320199653d4eSeschrock } promote_data_t; 320299653d4eSeschrock 320399653d4eSeschrock static int 320499653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data) 320599653d4eSeschrock { 320699653d4eSeschrock promote_data_t *pd = data; 320799653d4eSeschrock zfs_handle_t *szhp; 320899653d4eSeschrock char snapname[MAXPATHLEN]; 32093ccfa83cSahrens int rv = 0; 321099653d4eSeschrock 321199653d4eSeschrock /* We don't care about snapshots after the pivot point */ 32123ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) { 32133ccfa83cSahrens zfs_close(zhp); 321499653d4eSeschrock return (0); 32153ccfa83cSahrens } 321699653d4eSeschrock 32170b69c2f0Sahrens /* Remove the device link if it's a zvol. */ 3218e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 32190b69c2f0Sahrens (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); 322099653d4eSeschrock 322199653d4eSeschrock /* Check for conflicting names */ 3222e9dbad6fSeschrock (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); 3223e9dbad6fSeschrock (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); 322499653d4eSeschrock szhp = make_dataset_handle(zhp->zfs_hdl, snapname); 322599653d4eSeschrock if (szhp != NULL) { 322699653d4eSeschrock zfs_close(szhp); 322799653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 322899653d4eSeschrock "snapshot name '%s' from origin \n" 322999653d4eSeschrock "conflicts with '%s' from target"), 323099653d4eSeschrock zhp->zfs_name, snapname); 32313ccfa83cSahrens rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf); 323299653d4eSeschrock } 32333ccfa83cSahrens zfs_close(zhp); 32343ccfa83cSahrens return (rv); 323599653d4eSeschrock } 323699653d4eSeschrock 32370b69c2f0Sahrens static int 32380b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data) 32390b69c2f0Sahrens { 32400b69c2f0Sahrens promote_data_t *pd = data; 32410b69c2f0Sahrens 32420b69c2f0Sahrens /* We don't care about snapshots after the pivot point */ 32433ccfa83cSahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) { 32440b69c2f0Sahrens /* Create the device link if it's a zvol. */ 3245e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 32460b69c2f0Sahrens (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); 32473ccfa83cSahrens } 32480b69c2f0Sahrens 32493ccfa83cSahrens zfs_close(zhp); 32500b69c2f0Sahrens return (0); 32510b69c2f0Sahrens } 32520b69c2f0Sahrens 325399653d4eSeschrock /* 325499653d4eSeschrock * Promotes the given clone fs to be the clone parent. 325599653d4eSeschrock */ 325699653d4eSeschrock int 325799653d4eSeschrock zfs_promote(zfs_handle_t *zhp) 325899653d4eSeschrock { 325999653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 326099653d4eSeschrock zfs_cmd_t zc = { 0 }; 326199653d4eSeschrock char parent[MAXPATHLEN]; 326299653d4eSeschrock char *cp; 326399653d4eSeschrock int ret; 326499653d4eSeschrock zfs_handle_t *pzhp; 326599653d4eSeschrock promote_data_t pd; 326699653d4eSeschrock char errbuf[1024]; 326799653d4eSeschrock 326899653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 326999653d4eSeschrock "cannot promote '%s'"), zhp->zfs_name); 327099653d4eSeschrock 327199653d4eSeschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 327299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 327399653d4eSeschrock "snapshots can not be promoted")); 327499653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 327599653d4eSeschrock } 327699653d4eSeschrock 32773cb34c60Sahrens (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent)); 327899653d4eSeschrock if (parent[0] == '\0') { 327999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 328099653d4eSeschrock "not a cloned filesystem")); 328199653d4eSeschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 328299653d4eSeschrock } 328399653d4eSeschrock cp = strchr(parent, '@'); 328499653d4eSeschrock *cp = '\0'; 328599653d4eSeschrock 328699653d4eSeschrock /* Walk the snapshots we will be moving */ 32873cb34c60Sahrens pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); 328899653d4eSeschrock if (pzhp == NULL) 328999653d4eSeschrock return (-1); 329099653d4eSeschrock pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); 329199653d4eSeschrock zfs_close(pzhp); 329299653d4eSeschrock pd.cb_target = zhp->zfs_name; 329399653d4eSeschrock pd.cb_errbuf = errbuf; 3294990b4856Slling pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET); 329599653d4eSeschrock if (pzhp == NULL) 329699653d4eSeschrock return (-1); 329799653d4eSeschrock (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, 329899653d4eSeschrock sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); 329999653d4eSeschrock ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); 33000b69c2f0Sahrens if (ret != 0) { 33010b69c2f0Sahrens zfs_close(pzhp); 330299653d4eSeschrock return (-1); 33030b69c2f0Sahrens } 330499653d4eSeschrock 330599653d4eSeschrock /* issue the ioctl */ 33063cb34c60Sahrens (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, 3307e9dbad6fSeschrock sizeof (zc.zc_value)); 330899653d4eSeschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3309ecd6cf80Smarks ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 331099653d4eSeschrock 331199653d4eSeschrock if (ret != 0) { 33120b69c2f0Sahrens int save_errno = errno; 3313fa9e4066Sahrens 33140b69c2f0Sahrens (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd); 33150b69c2f0Sahrens zfs_close(pzhp); 33160b69c2f0Sahrens 33170b69c2f0Sahrens switch (save_errno) { 3318fa9e4066Sahrens case EEXIST: 3319fa9e4066Sahrens /* 332099653d4eSeschrock * There is a conflicting snapshot name. We 332199653d4eSeschrock * should have caught this above, but they could 332299653d4eSeschrock * have renamed something in the mean time. 3323fa9e4066Sahrens */ 332499653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 332599653d4eSeschrock "conflicting snapshot name from parent '%s'"), 332699653d4eSeschrock parent); 332799653d4eSeschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 3328fa9e4066Sahrens 3329fa9e4066Sahrens default: 33300b69c2f0Sahrens return (zfs_standard_error(hdl, save_errno, errbuf)); 3331fa9e4066Sahrens } 33320b69c2f0Sahrens } else { 33330b69c2f0Sahrens (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd); 3334fa9e4066Sahrens } 3335fa9e4066Sahrens 33360b69c2f0Sahrens zfs_close(pzhp); 3337fa9e4066Sahrens return (ret); 3338fa9e4066Sahrens } 3339fa9e4066Sahrens 3340cdf5b4caSmmusante struct createdata { 3341cdf5b4caSmmusante const char *cd_snapname; 3342cdf5b4caSmmusante int cd_ifexists; 3343cdf5b4caSmmusante }; 3344cdf5b4caSmmusante 33451d452cf5Sahrens static int 33461d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg) 33471d452cf5Sahrens { 3348cdf5b4caSmmusante struct createdata *cd = arg; 3349e9dbad6fSeschrock int ret; 33501d452cf5Sahrens 33511d452cf5Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 33521d452cf5Sahrens char name[MAXPATHLEN]; 33531d452cf5Sahrens 3354e9dbad6fSeschrock (void) strlcpy(name, zhp->zfs_name, sizeof (name)); 3355e9dbad6fSeschrock (void) strlcat(name, "@", sizeof (name)); 3356cdf5b4caSmmusante (void) strlcat(name, cd->cd_snapname, sizeof (name)); 3357cdf5b4caSmmusante (void) zvol_create_link_common(zhp->zfs_hdl, name, 3358cdf5b4caSmmusante cd->cd_ifexists); 33591d452cf5Sahrens /* 33601d452cf5Sahrens * NB: this is simply a best-effort. We don't want to 33611d452cf5Sahrens * return an error, because then we wouldn't visit all 33621d452cf5Sahrens * the volumes. 33631d452cf5Sahrens */ 33641d452cf5Sahrens } 3365e9dbad6fSeschrock 3366cdf5b4caSmmusante ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd); 3367e9dbad6fSeschrock 3368e9dbad6fSeschrock zfs_close(zhp); 3369e9dbad6fSeschrock 3370e9dbad6fSeschrock return (ret); 33711d452cf5Sahrens } 33721d452cf5Sahrens 3373fa9e4066Sahrens /* 337472bdce51Sahl * Takes a snapshot of the given dataset. 3375fa9e4066Sahrens */ 3376fa9e4066Sahrens int 33771d452cf5Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive) 3378fa9e4066Sahrens { 3379fa9e4066Sahrens const char *delim; 3380fa9e4066Sahrens char *parent; 3381fa9e4066Sahrens zfs_handle_t *zhp; 3382fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3383fa9e4066Sahrens int ret; 338499653d4eSeschrock char errbuf[1024]; 3385fa9e4066Sahrens 338699653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 338799653d4eSeschrock "cannot snapshot '%s'"), path); 338899653d4eSeschrock 338999653d4eSeschrock /* validate the target name */ 3390f18faf3fSek110237 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) 339199653d4eSeschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3392fa9e4066Sahrens 3393fa9e4066Sahrens /* make sure the parent exists and is of the appropriate type */ 33941d452cf5Sahrens delim = strchr(path, '@'); 339599653d4eSeschrock if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL) 339699653d4eSeschrock return (-1); 3397fa9e4066Sahrens (void) strncpy(parent, path, delim - path); 3398fa9e4066Sahrens parent[delim - path] = '\0'; 3399fa9e4066Sahrens 340099653d4eSeschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 3401fa9e4066Sahrens ZFS_TYPE_VOLUME)) == NULL) { 3402fa9e4066Sahrens free(parent); 3403fa9e4066Sahrens return (-1); 3404fa9e4066Sahrens } 3405fa9e4066Sahrens 34061d452cf5Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3407e9dbad6fSeschrock (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); 3408ecd6cf80Smarks if (ZFS_IS_VOLUME(zhp)) 3409ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZVOL; 3410ecd6cf80Smarks else 3411ecd6cf80Smarks zc.zc_objset_type = DMU_OST_ZFS; 34121d452cf5Sahrens zc.zc_cookie = recursive; 3413ecd6cf80Smarks ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc); 3414fa9e4066Sahrens 34151d452cf5Sahrens /* 34161d452cf5Sahrens * if it was recursive, the one that actually failed will be in 34171d452cf5Sahrens * zc.zc_name. 34181d452cf5Sahrens */ 3419ecd6cf80Smarks if (ret != 0) 34201d452cf5Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 3421e9dbad6fSeschrock "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); 3422ecd6cf80Smarks 34231d452cf5Sahrens if (ret == 0 && recursive) { 3424cdf5b4caSmmusante struct createdata cd; 3425cdf5b4caSmmusante 3426cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3427cdf5b4caSmmusante cd.cd_ifexists = B_FALSE; 3428cdf5b4caSmmusante (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd); 34291d452cf5Sahrens } 3430fa9e4066Sahrens if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { 343199653d4eSeschrock ret = zvol_create_link(zhp->zfs_hdl, path); 34321d452cf5Sahrens if (ret != 0) { 3433ecd6cf80Smarks (void) zfs_standard_error(hdl, errno, 3434ecd6cf80Smarks dgettext(TEXT_DOMAIN, 3435ecd6cf80Smarks "Volume successfully snapshotted, but device links " 3436ecd6cf80Smarks "were not created")); 3437ecd6cf80Smarks free(parent); 3438ecd6cf80Smarks zfs_close(zhp); 3439ecd6cf80Smarks return (-1); 3440fa9e4066Sahrens } 34411d452cf5Sahrens } 3442fa9e4066Sahrens 344399653d4eSeschrock if (ret != 0) 344499653d4eSeschrock (void) zfs_standard_error(hdl, errno, errbuf); 3445fa9e4066Sahrens 3446fa9e4066Sahrens free(parent); 3447fa9e4066Sahrens zfs_close(zhp); 3448fa9e4066Sahrens 3449fa9e4066Sahrens return (ret); 3450fa9e4066Sahrens } 3451fa9e4066Sahrens 3452fa9e4066Sahrens /* 3453b12a1c38Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 3454b12a1c38Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 3455b12a1c38Slling * is a dependent and we should just destroy it without checking the transaction 3456b12a1c38Slling * group. 3457fa9e4066Sahrens */ 3458b12a1c38Slling typedef struct rollback_data { 3459b12a1c38Slling const char *cb_target; /* the snapshot */ 3460b12a1c38Slling uint64_t cb_create; /* creation time reference */ 3461c391e322Sahrens boolean_t cb_error; 346299653d4eSeschrock boolean_t cb_dependent; 3463c391e322Sahrens boolean_t cb_force; 3464b12a1c38Slling } rollback_data_t; 3465b12a1c38Slling 3466b12a1c38Slling static int 3467b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data) 3468b12a1c38Slling { 3469b12a1c38Slling rollback_data_t *cbp = data; 3470b12a1c38Slling 3471b12a1c38Slling if (!cbp->cb_dependent) { 3472b12a1c38Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 3473b12a1c38Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3474b12a1c38Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3475b12a1c38Slling cbp->cb_create) { 3476ecd6cf80Smarks char *logstr; 3477b12a1c38Slling 347899653d4eSeschrock cbp->cb_dependent = B_TRUE; 34794ccbb6e7Sahrens cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, 34804ccbb6e7Sahrens rollback_destroy, cbp); 348199653d4eSeschrock cbp->cb_dependent = B_FALSE; 3482b12a1c38Slling 3483ecd6cf80Smarks logstr = zhp->zfs_hdl->libzfs_log_str; 3484ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = NULL; 34854ccbb6e7Sahrens cbp->cb_error |= zfs_destroy(zhp); 3486ecd6cf80Smarks zhp->zfs_hdl->libzfs_log_str = logstr; 3487b12a1c38Slling } 3488b12a1c38Slling } else { 3489c391e322Sahrens /* We must destroy this clone; first unmount it */ 3490c391e322Sahrens prop_changelist_t *clp; 3491c391e322Sahrens 3492c391e322Sahrens clp = changelist_gather(zhp, ZFS_PROP_NAME, 3493c391e322Sahrens cbp->cb_force ? MS_FORCE: 0); 3494c391e322Sahrens if (clp == NULL || changelist_prefix(clp) != 0) { 3495c391e322Sahrens cbp->cb_error = B_TRUE; 3496c391e322Sahrens zfs_close(zhp); 3497c391e322Sahrens return (0); 3498c391e322Sahrens } 3499c391e322Sahrens if (zfs_destroy(zhp) != 0) 3500c391e322Sahrens cbp->cb_error = B_TRUE; 3501c391e322Sahrens else 3502c391e322Sahrens changelist_remove(clp, zhp->zfs_name); 3503ba7b046eSahrens (void) changelist_postfix(clp); 3504c391e322Sahrens changelist_free(clp); 3505b12a1c38Slling } 3506b12a1c38Slling 3507b12a1c38Slling zfs_close(zhp); 3508b12a1c38Slling return (0); 3509b12a1c38Slling } 3510b12a1c38Slling 3511b12a1c38Slling /* 35124ccbb6e7Sahrens * Given a dataset, rollback to a specific snapshot, discarding any 35134ccbb6e7Sahrens * data changes since then and making it the active dataset. 35144ccbb6e7Sahrens * 35154ccbb6e7Sahrens * Any snapshots more recent than the target are destroyed, along with 35164ccbb6e7Sahrens * their dependents. 3517b12a1c38Slling */ 35184ccbb6e7Sahrens int 3519c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) 3520fa9e4066Sahrens { 35214ccbb6e7Sahrens rollback_data_t cb = { 0 }; 35224ccbb6e7Sahrens int err; 3523fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 35247b97dc1aSrm160521 boolean_t restore_resv = 0; 35257b97dc1aSrm160521 uint64_t old_volsize, new_volsize; 35267b97dc1aSrm160521 zfs_prop_t resv_prop; 3527fa9e4066Sahrens 3528fa9e4066Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 3529fa9e4066Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 3530fa9e4066Sahrens 35314ccbb6e7Sahrens /* 35324ccbb6e7Sahrens * Destroy all recent snapshots and its dependends. 35334ccbb6e7Sahrens */ 3534c391e322Sahrens cb.cb_force = force; 35354ccbb6e7Sahrens cb.cb_target = snap->zfs_name; 35364ccbb6e7Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 35374ccbb6e7Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 35384ccbb6e7Sahrens 3539c391e322Sahrens if (cb.cb_error) 3540c391e322Sahrens return (-1); 35414ccbb6e7Sahrens 35424ccbb6e7Sahrens /* 35434ccbb6e7Sahrens * Now that we have verified that the snapshot is the latest, 35444ccbb6e7Sahrens * rollback to the given snapshot. 35454ccbb6e7Sahrens */ 35464ccbb6e7Sahrens 35477b97dc1aSrm160521 if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 35487b97dc1aSrm160521 if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 3549fa9e4066Sahrens return (-1); 35507b97dc1aSrm160521 if (zfs_which_resv_prop(zhp, &resv_prop) < 0) 35517b97dc1aSrm160521 return (-1); 35527b97dc1aSrm160521 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 35537b97dc1aSrm160521 restore_resv = 35547b97dc1aSrm160521 (old_volsize == zfs_prop_get_int(zhp, resv_prop)); 35557b97dc1aSrm160521 } 3556fa9e4066Sahrens 3557fa9e4066Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3558fa9e4066Sahrens 3559e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3560fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3561fa9e4066Sahrens else 3562fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3563fa9e4066Sahrens 3564fa9e4066Sahrens /* 35654ccbb6e7Sahrens * We rely on zfs_iter_children() to verify that there are no 35664ccbb6e7Sahrens * newer snapshots for the given dataset. Therefore, we can 35674ccbb6e7Sahrens * simply pass the name on to the ioctl() call. There is still 35684ccbb6e7Sahrens * an unlikely race condition where the user has taken a 35694ccbb6e7Sahrens * snapshot since we verified that this was the most recent. 35707b97dc1aSrm160521 * 3571fa9e4066Sahrens */ 35724ccbb6e7Sahrens if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { 3573ece3d9b3Slling (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, 357499653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 357599653d4eSeschrock zhp->zfs_name); 3576b9415e83Srm160521 return (err); 3577b9415e83Srm160521 } 3578fa9e4066Sahrens 35797b97dc1aSrm160521 /* 35807b97dc1aSrm160521 * For volumes, if the pre-rollback volsize matched the pre- 35817b97dc1aSrm160521 * rollback reservation and the volsize has changed then set 35827b97dc1aSrm160521 * the reservation property to the post-rollback volsize. 35837b97dc1aSrm160521 * Make a new handle since the rollback closed the dataset. 35847b97dc1aSrm160521 */ 3585b9415e83Srm160521 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && 3586b9415e83Srm160521 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { 3587b9415e83Srm160521 if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) { 3588b9415e83Srm160521 zfs_close(zhp); 35897b97dc1aSrm160521 return (err); 3590b9415e83Srm160521 } 35917b97dc1aSrm160521 if (restore_resv) { 35927b97dc1aSrm160521 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 35937b97dc1aSrm160521 if (old_volsize != new_volsize) 3594b9415e83Srm160521 err = zfs_prop_set_int(zhp, resv_prop, 3595b9415e83Srm160521 new_volsize); 35967b97dc1aSrm160521 } 35977b97dc1aSrm160521 zfs_close(zhp); 35987b97dc1aSrm160521 } 35994ccbb6e7Sahrens return (err); 3600b12a1c38Slling } 3601b12a1c38Slling 3602b12a1c38Slling /* 3603fa9e4066Sahrens * Iterate over all dependents for a given dataset. This includes both 3604fa9e4066Sahrens * hierarchical dependents (children) and data dependents (snapshots and 3605fa9e4066Sahrens * clones). The bulk of the processing occurs in get_dependents() in 3606fa9e4066Sahrens * libzfs_graph.c. 3607fa9e4066Sahrens */ 3608fa9e4066Sahrens int 36093bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 36103bb79becSeschrock zfs_iter_f func, void *data) 3611fa9e4066Sahrens { 3612fa9e4066Sahrens char **dependents; 3613fa9e4066Sahrens size_t count; 3614fa9e4066Sahrens int i; 3615fa9e4066Sahrens zfs_handle_t *child; 3616fa9e4066Sahrens int ret = 0; 3617fa9e4066Sahrens 36183bb79becSeschrock if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name, 36193bb79becSeschrock &dependents, &count) != 0) 36203bb79becSeschrock return (-1); 36213bb79becSeschrock 3622fa9e4066Sahrens for (i = 0; i < count; i++) { 362399653d4eSeschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 362499653d4eSeschrock dependents[i])) == NULL) 3625fa9e4066Sahrens continue; 3626fa9e4066Sahrens 3627fa9e4066Sahrens if ((ret = func(child, data)) != 0) 3628fa9e4066Sahrens break; 3629fa9e4066Sahrens } 3630fa9e4066Sahrens 3631fa9e4066Sahrens for (i = 0; i < count; i++) 3632fa9e4066Sahrens free(dependents[i]); 3633fa9e4066Sahrens free(dependents); 3634fa9e4066Sahrens 3635fa9e4066Sahrens return (ret); 3636fa9e4066Sahrens } 3637fa9e4066Sahrens 3638fa9e4066Sahrens /* 3639fa9e4066Sahrens * Renames the given dataset. 3640fa9e4066Sahrens */ 3641fa9e4066Sahrens int 36427f1f55eaSvb160487 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) 3643fa9e4066Sahrens { 3644fa9e4066Sahrens int ret; 3645fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3646fa9e4066Sahrens char *delim; 3647cdf5b4caSmmusante prop_changelist_t *cl = NULL; 3648cdf5b4caSmmusante zfs_handle_t *zhrp = NULL; 3649cdf5b4caSmmusante char *parentname = NULL; 3650fa9e4066Sahrens char parent[ZFS_MAXNAMELEN]; 365199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 365299653d4eSeschrock char errbuf[1024]; 3653fa9e4066Sahrens 3654fa9e4066Sahrens /* if we have the same exact name, just return success */ 3655fa9e4066Sahrens if (strcmp(zhp->zfs_name, target) == 0) 3656fa9e4066Sahrens return (0); 3657fa9e4066Sahrens 365899653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 365999653d4eSeschrock "cannot rename to '%s'"), target); 366099653d4eSeschrock 3661fa9e4066Sahrens /* 3662fa9e4066Sahrens * Make sure the target name is valid 3663fa9e4066Sahrens */ 3664fa9e4066Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 366598579b20Snd150628 if ((strchr(target, '@') == NULL) || 366698579b20Snd150628 *target == '@') { 366798579b20Snd150628 /* 366898579b20Snd150628 * Snapshot target name is abbreviated, 366998579b20Snd150628 * reconstruct full dataset name 367098579b20Snd150628 */ 367198579b20Snd150628 (void) strlcpy(parent, zhp->zfs_name, 367298579b20Snd150628 sizeof (parent)); 367398579b20Snd150628 delim = strchr(parent, '@'); 367498579b20Snd150628 if (strchr(target, '@') == NULL) 367598579b20Snd150628 *(++delim) = '\0'; 367698579b20Snd150628 else 367798579b20Snd150628 *delim = '\0'; 367898579b20Snd150628 (void) strlcat(parent, target, sizeof (parent)); 367998579b20Snd150628 target = parent; 368098579b20Snd150628 } else { 3681fa9e4066Sahrens /* 3682fa9e4066Sahrens * Make sure we're renaming within the same dataset. 3683fa9e4066Sahrens */ 368498579b20Snd150628 delim = strchr(target, '@'); 368598579b20Snd150628 if (strncmp(zhp->zfs_name, target, delim - target) 368698579b20Snd150628 != 0 || zhp->zfs_name[delim - target] != '@') { 368799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 368898579b20Snd150628 "snapshots must be part of same " 368998579b20Snd150628 "dataset")); 369098579b20Snd150628 return (zfs_error(hdl, EZFS_CROSSTARGET, 369198579b20Snd150628 errbuf)); 3692fa9e4066Sahrens } 369398579b20Snd150628 } 3694f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 369598579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3696fa9e4066Sahrens } else { 3697cdf5b4caSmmusante if (recursive) { 3698cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3699cdf5b4caSmmusante "recursive rename must be a snapshot")); 3700cdf5b4caSmmusante return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 3701cdf5b4caSmmusante } 3702cdf5b4caSmmusante 3703f18faf3fSek110237 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE)) 370498579b20Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3705e9dbad6fSeschrock uint64_t unused; 3706e9dbad6fSeschrock 3707fa9e4066Sahrens /* validate parents */ 37087f1f55eaSvb160487 if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) 3709fa9e4066Sahrens return (-1); 3710fa9e4066Sahrens 3711fa9e4066Sahrens (void) parent_name(target, parent, sizeof (parent)); 3712fa9e4066Sahrens 3713fa9e4066Sahrens /* make sure we're in the same pool */ 3714fa9e4066Sahrens verify((delim = strchr(target, '/')) != NULL); 3715fa9e4066Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 3716fa9e4066Sahrens zhp->zfs_name[delim - target] != '/') { 371799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 371899653d4eSeschrock "datasets must be within same pool")); 371999653d4eSeschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 3720fa9e4066Sahrens } 3721f2fdf992Snd150628 3722f2fdf992Snd150628 /* new name cannot be a child of the current dataset name */ 3723f2fdf992Snd150628 if (strncmp(parent, zhp->zfs_name, 3724f2fdf992Snd150628 strlen(zhp->zfs_name)) == 0) { 3725f2fdf992Snd150628 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3726f2fdf992Snd150628 "New dataset name cannot be a descendent of " 3727f2fdf992Snd150628 "current dataset name")); 3728f2fdf992Snd150628 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3729f2fdf992Snd150628 } 3730fa9e4066Sahrens } 3731fa9e4066Sahrens 373299653d4eSeschrock (void) snprintf(errbuf, sizeof (errbuf), 373399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 373499653d4eSeschrock 3735fa9e4066Sahrens if (getzoneid() == GLOBAL_ZONEID && 3736fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 373799653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 373899653d4eSeschrock "dataset is used in a non-global zone")); 373999653d4eSeschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 3740fa9e4066Sahrens } 3741fa9e4066Sahrens 3742cdf5b4caSmmusante if (recursive) { 3743cdf5b4caSmmusante struct destroydata dd; 3744cdf5b4caSmmusante 3745f0c5ee21Smmusante parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); 3746f0c5ee21Smmusante if (parentname == NULL) { 3747f0c5ee21Smmusante ret = -1; 3748f0c5ee21Smmusante goto error; 3749f0c5ee21Smmusante } 3750cdf5b4caSmmusante delim = strchr(parentname, '@'); 3751cdf5b4caSmmusante *delim = '\0'; 3752990b4856Slling zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET); 3753cdf5b4caSmmusante if (zhrp == NULL) { 3754f0c5ee21Smmusante ret = -1; 3755f0c5ee21Smmusante goto error; 3756cdf5b4caSmmusante } 3757cdf5b4caSmmusante 3758cdf5b4caSmmusante dd.snapname = delim + 1; 3759cdf5b4caSmmusante dd.gotone = B_FALSE; 3760f0c5ee21Smmusante dd.closezhp = B_TRUE; 3761cdf5b4caSmmusante 3762cdf5b4caSmmusante /* We remove any zvol links prior to renaming them */ 3763cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd); 3764cdf5b4caSmmusante if (ret) { 3765cdf5b4caSmmusante goto error; 3766cdf5b4caSmmusante } 3767cdf5b4caSmmusante } else { 3768fa9e4066Sahrens if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL) 376999653d4eSeschrock return (-1); 3770fa9e4066Sahrens 3771fa9e4066Sahrens if (changelist_haszonedchild(cl)) { 377299653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 377399653d4eSeschrock "child dataset with inherited mountpoint is used " 377499653d4eSeschrock "in a non-global zone")); 3775e9dbad6fSeschrock (void) zfs_error(hdl, EZFS_ZONED, errbuf); 3776fa9e4066Sahrens goto error; 3777fa9e4066Sahrens } 3778fa9e4066Sahrens 3779fa9e4066Sahrens if ((ret = changelist_prefix(cl)) != 0) 3780fa9e4066Sahrens goto error; 3781cdf5b4caSmmusante } 3782fa9e4066Sahrens 3783e9dbad6fSeschrock if (ZFS_IS_VOLUME(zhp)) 3784fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 3785fa9e4066Sahrens else 3786fa9e4066Sahrens zc.zc_objset_type = DMU_OST_ZFS; 3787fa9e4066Sahrens 378898579b20Snd150628 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 3789e9dbad6fSeschrock (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); 379098579b20Snd150628 3791cdf5b4caSmmusante zc.zc_cookie = recursive; 3792cdf5b4caSmmusante 3793ecd6cf80Smarks if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) { 3794cdf5b4caSmmusante /* 3795cdf5b4caSmmusante * if it was recursive, the one that actually failed will 3796cdf5b4caSmmusante * be in zc.zc_name 3797cdf5b4caSmmusante */ 3798cdf5b4caSmmusante (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 37993cb34c60Sahrens "cannot rename '%s'"), zc.zc_name); 3800cdf5b4caSmmusante 3801cdf5b4caSmmusante if (recursive && errno == EEXIST) { 3802cdf5b4caSmmusante zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3803cdf5b4caSmmusante "a child dataset already has a snapshot " 3804cdf5b4caSmmusante "with the new name")); 3805a10acbd6Seschrock (void) zfs_error(hdl, EZFS_EXISTS, errbuf); 3806cdf5b4caSmmusante } else { 380799653d4eSeschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 3808cdf5b4caSmmusante } 3809fa9e4066Sahrens 3810fa9e4066Sahrens /* 3811fa9e4066Sahrens * On failure, we still want to remount any filesystems that 3812fa9e4066Sahrens * were previously mounted, so we don't alter the system state. 3813fa9e4066Sahrens */ 3814cdf5b4caSmmusante if (recursive) { 3815cdf5b4caSmmusante struct createdata cd; 3816cdf5b4caSmmusante 3817cdf5b4caSmmusante /* only create links for datasets that had existed */ 3818cdf5b4caSmmusante cd.cd_snapname = delim + 1; 3819cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3820cdf5b4caSmmusante (void) zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3821cdf5b4caSmmusante &cd); 3822cdf5b4caSmmusante } else { 3823fa9e4066Sahrens (void) changelist_postfix(cl); 3824cdf5b4caSmmusante } 3825cdf5b4caSmmusante } else { 3826cdf5b4caSmmusante if (recursive) { 3827cdf5b4caSmmusante struct createdata cd; 3828cdf5b4caSmmusante 3829cdf5b4caSmmusante /* only create links for datasets that had existed */ 3830cdf5b4caSmmusante cd.cd_snapname = strchr(target, '@') + 1; 3831cdf5b4caSmmusante cd.cd_ifexists = B_TRUE; 3832cdf5b4caSmmusante ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb, 3833cdf5b4caSmmusante &cd); 3834fa9e4066Sahrens } else { 3835fa9e4066Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 3836fa9e4066Sahrens ret = changelist_postfix(cl); 3837fa9e4066Sahrens } 3838cdf5b4caSmmusante } 3839fa9e4066Sahrens 3840fa9e4066Sahrens error: 3841cdf5b4caSmmusante if (parentname) { 3842cdf5b4caSmmusante free(parentname); 3843cdf5b4caSmmusante } 3844cdf5b4caSmmusante if (zhrp) { 3845cdf5b4caSmmusante zfs_close(zhrp); 3846cdf5b4caSmmusante } 3847cdf5b4caSmmusante if (cl) { 3848fa9e4066Sahrens changelist_free(cl); 3849cdf5b4caSmmusante } 3850fa9e4066Sahrens return (ret); 3851fa9e4066Sahrens } 3852fa9e4066Sahrens 3853fa9e4066Sahrens /* 3854fa9e4066Sahrens * Given a zvol dataset, issue the ioctl to create the appropriate minor node, 3855fa9e4066Sahrens * poke devfsadm to create the /dev link, and then wait for the link to appear. 3856fa9e4066Sahrens */ 3857fa9e4066Sahrens int 385899653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset) 3859fa9e4066Sahrens { 3860cdf5b4caSmmusante return (zvol_create_link_common(hdl, dataset, B_FALSE)); 3861cdf5b4caSmmusante } 3862cdf5b4caSmmusante 3863cdf5b4caSmmusante static int 3864cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists) 3865cdf5b4caSmmusante { 3866fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 386799653d4eSeschrock di_devlink_handle_t dhdl; 3868ecd6cf80Smarks priv_set_t *priv_effective; 3869ecd6cf80Smarks int privileged; 3870fa9e4066Sahrens 3871fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3872fa9e4066Sahrens 3873fa9e4066Sahrens /* 3874fa9e4066Sahrens * Issue the appropriate ioctl. 3875fa9e4066Sahrens */ 387699653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { 3877fa9e4066Sahrens switch (errno) { 3878fa9e4066Sahrens case EEXIST: 3879fa9e4066Sahrens /* 3880fa9e4066Sahrens * Silently ignore the case where the link already 3881fa9e4066Sahrens * exists. This allows 'zfs volinit' to be run multiple 3882fa9e4066Sahrens * times without errors. 3883fa9e4066Sahrens */ 3884fa9e4066Sahrens return (0); 3885fa9e4066Sahrens 3886cdf5b4caSmmusante case ENOENT: 3887cdf5b4caSmmusante /* 3888cdf5b4caSmmusante * Dataset does not exist in the kernel. If we 3889cdf5b4caSmmusante * don't care (see zfs_rename), then ignore the 3890cdf5b4caSmmusante * error quietly. 3891cdf5b4caSmmusante */ 3892cdf5b4caSmmusante if (ifexists) { 3893cdf5b4caSmmusante return (0); 3894cdf5b4caSmmusante } 3895cdf5b4caSmmusante 3896cdf5b4caSmmusante /* FALLTHROUGH */ 3897cdf5b4caSmmusante 3898fa9e4066Sahrens default: 3899ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 390099653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 390199653d4eSeschrock "for '%s'"), dataset)); 3902fa9e4066Sahrens } 3903fa9e4066Sahrens } 3904fa9e4066Sahrens 3905fa9e4066Sahrens /* 3906ecd6cf80Smarks * If privileged call devfsadm and wait for the links to 3907ecd6cf80Smarks * magically appear. 3908ecd6cf80Smarks * Otherwise, print out an informational message. 3909fa9e4066Sahrens */ 3910ecd6cf80Smarks 3911ecd6cf80Smarks priv_effective = priv_allocset(); 3912ecd6cf80Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 3913ecd6cf80Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 3914ecd6cf80Smarks priv_freeset(priv_effective); 3915ecd6cf80Smarks 3916ecd6cf80Smarks if (privileged) { 3917ecd6cf80Smarks if ((dhdl = di_devlink_init(ZFS_DRIVER, 3918ecd6cf80Smarks DI_MAKE_LINK)) == NULL) { 391999653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 3920ecd6cf80Smarks (void) zfs_standard_error_fmt(hdl, EZFS_DEVLINKS, 392199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot create device links " 392299653d4eSeschrock "for '%s'"), dataset); 392399653d4eSeschrock (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); 3924fa9e4066Sahrens return (-1); 3925fa9e4066Sahrens } else { 392699653d4eSeschrock (void) di_devlink_fini(&dhdl); 3927fa9e4066Sahrens } 3928ecd6cf80Smarks } else { 3929ecd6cf80Smarks char pathname[MAXPATHLEN]; 3930ecd6cf80Smarks struct stat64 statbuf; 3931ecd6cf80Smarks int i; 3932ecd6cf80Smarks 3933ecd6cf80Smarks #define MAX_WAIT 10 3934ecd6cf80Smarks 3935ecd6cf80Smarks /* 3936ecd6cf80Smarks * This is the poor mans way of waiting for the link 3937ecd6cf80Smarks * to show up. If after 10 seconds we still don't 3938ecd6cf80Smarks * have it, then print out a message. 3939ecd6cf80Smarks */ 3940ecd6cf80Smarks (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s", 3941ecd6cf80Smarks dataset); 3942ecd6cf80Smarks 3943ecd6cf80Smarks for (i = 0; i != MAX_WAIT; i++) { 3944ecd6cf80Smarks if (stat64(pathname, &statbuf) == 0) 3945ecd6cf80Smarks break; 3946ecd6cf80Smarks (void) sleep(1); 3947ecd6cf80Smarks } 3948ecd6cf80Smarks if (i == MAX_WAIT) 3949ecd6cf80Smarks (void) printf(gettext("%s may not be immediately " 3950ecd6cf80Smarks "available\n"), pathname); 3951ecd6cf80Smarks } 3952fa9e4066Sahrens 3953fa9e4066Sahrens return (0); 3954fa9e4066Sahrens } 3955fa9e4066Sahrens 3956fa9e4066Sahrens /* 3957fa9e4066Sahrens * Remove a minor node for the given zvol and the associated /dev links. 3958fa9e4066Sahrens */ 3959fa9e4066Sahrens int 396099653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) 3961fa9e4066Sahrens { 3962fa9e4066Sahrens zfs_cmd_t zc = { 0 }; 3963fa9e4066Sahrens 3964fa9e4066Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 3965fa9e4066Sahrens 396699653d4eSeschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { 3967fa9e4066Sahrens switch (errno) { 3968fa9e4066Sahrens case ENXIO: 3969fa9e4066Sahrens /* 3970fa9e4066Sahrens * Silently ignore the case where the link no longer 3971fa9e4066Sahrens * exists, so that 'zfs volfini' can be run multiple 3972fa9e4066Sahrens * times without errors. 3973fa9e4066Sahrens */ 3974fa9e4066Sahrens return (0); 3975fa9e4066Sahrens 3976fa9e4066Sahrens default: 3977ece3d9b3Slling return (zfs_standard_error_fmt(hdl, errno, 397899653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot remove device " 397999653d4eSeschrock "links for '%s'"), dataset)); 3980fa9e4066Sahrens } 3981fa9e4066Sahrens } 3982fa9e4066Sahrens 3983fa9e4066Sahrens return (0); 3984fa9e4066Sahrens } 3985e9dbad6fSeschrock 3986e9dbad6fSeschrock nvlist_t * 3987e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp) 3988e9dbad6fSeschrock { 3989e9dbad6fSeschrock return (zhp->zfs_user_props); 3990e9dbad6fSeschrock } 3991e9dbad6fSeschrock 3992e9dbad6fSeschrock /* 3993e9dbad6fSeschrock * This function is used by 'zfs list' to determine the exact set of columns to 3994e9dbad6fSeschrock * display, and their maximum widths. This does two main things: 3995e9dbad6fSeschrock * 3996e9dbad6fSeschrock * - If this is a list of all properties, then expand the list to include 3997e9dbad6fSeschrock * all native properties, and set a flag so that for each dataset we look 3998e9dbad6fSeschrock * for new unique user properties and add them to the list. 3999e9dbad6fSeschrock * 4000e9dbad6fSeschrock * - For non fixed-width properties, keep track of the maximum width seen 4001e9dbad6fSeschrock * so that we can size the column appropriately. 4002e9dbad6fSeschrock */ 4003e9dbad6fSeschrock int 4004990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) 4005e9dbad6fSeschrock { 4006e9dbad6fSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 4007990b4856Slling zprop_list_t *entry; 4008990b4856Slling zprop_list_t **last, **start; 4009e9dbad6fSeschrock nvlist_t *userprops, *propval; 4010e9dbad6fSeschrock nvpair_t *elem; 4011e9dbad6fSeschrock char *strval; 4012e9dbad6fSeschrock char buf[ZFS_MAXPROPLEN]; 4013e9dbad6fSeschrock 4014990b4856Slling if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0) 4015e9dbad6fSeschrock return (-1); 4016e9dbad6fSeschrock 4017e9dbad6fSeschrock userprops = zfs_get_user_props(zhp); 4018e9dbad6fSeschrock 4019e9dbad6fSeschrock entry = *plp; 4020e9dbad6fSeschrock if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) { 4021e9dbad6fSeschrock /* 4022e9dbad6fSeschrock * Go through and add any user properties as necessary. We 4023e9dbad6fSeschrock * start by incrementing our list pointer to the first 4024e9dbad6fSeschrock * non-native property. 4025e9dbad6fSeschrock */ 4026e9dbad6fSeschrock start = plp; 4027e9dbad6fSeschrock while (*start != NULL) { 4028990b4856Slling if ((*start)->pl_prop == ZPROP_INVAL) 4029e9dbad6fSeschrock break; 4030e9dbad6fSeschrock start = &(*start)->pl_next; 4031e9dbad6fSeschrock } 4032e9dbad6fSeschrock 4033e9dbad6fSeschrock elem = NULL; 4034e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) { 4035e9dbad6fSeschrock /* 4036e9dbad6fSeschrock * See if we've already found this property in our list. 4037e9dbad6fSeschrock */ 4038e9dbad6fSeschrock for (last = start; *last != NULL; 4039e9dbad6fSeschrock last = &(*last)->pl_next) { 4040e9dbad6fSeschrock if (strcmp((*last)->pl_user_prop, 4041e9dbad6fSeschrock nvpair_name(elem)) == 0) 4042e9dbad6fSeschrock break; 4043e9dbad6fSeschrock } 4044e9dbad6fSeschrock 4045e9dbad6fSeschrock if (*last == NULL) { 4046e9dbad6fSeschrock if ((entry = zfs_alloc(hdl, 4047990b4856Slling sizeof (zprop_list_t))) == NULL || 4048e9dbad6fSeschrock ((entry->pl_user_prop = zfs_strdup(hdl, 4049e9dbad6fSeschrock nvpair_name(elem)))) == NULL) { 4050e9dbad6fSeschrock free(entry); 4051e9dbad6fSeschrock return (-1); 4052e9dbad6fSeschrock } 4053e9dbad6fSeschrock 4054990b4856Slling entry->pl_prop = ZPROP_INVAL; 4055e9dbad6fSeschrock entry->pl_width = strlen(nvpair_name(elem)); 4056e9dbad6fSeschrock entry->pl_all = B_TRUE; 4057e9dbad6fSeschrock *last = entry; 4058e9dbad6fSeschrock } 4059e9dbad6fSeschrock } 4060e9dbad6fSeschrock } 4061e9dbad6fSeschrock 4062e9dbad6fSeschrock /* 4063e9dbad6fSeschrock * Now go through and check the width of any non-fixed columns 4064e9dbad6fSeschrock */ 4065e9dbad6fSeschrock for (entry = *plp; entry != NULL; entry = entry->pl_next) { 4066e9dbad6fSeschrock if (entry->pl_fixed) 4067e9dbad6fSeschrock continue; 4068e9dbad6fSeschrock 4069990b4856Slling if (entry->pl_prop != ZPROP_INVAL) { 4070e9dbad6fSeschrock if (zfs_prop_get(zhp, entry->pl_prop, 4071e9dbad6fSeschrock buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) { 4072e9dbad6fSeschrock if (strlen(buf) > entry->pl_width) 4073e9dbad6fSeschrock entry->pl_width = strlen(buf); 4074e9dbad6fSeschrock } 4075e9dbad6fSeschrock } else if (nvlist_lookup_nvlist(userprops, 4076e9dbad6fSeschrock entry->pl_user_prop, &propval) == 0) { 4077e9dbad6fSeschrock verify(nvlist_lookup_string(propval, 4078990b4856Slling ZPROP_VALUE, &strval) == 0); 4079e9dbad6fSeschrock if (strlen(strval) > entry->pl_width) 4080e9dbad6fSeschrock entry->pl_width = strlen(strval); 4081e9dbad6fSeschrock } 4082e9dbad6fSeschrock } 4083e9dbad6fSeschrock 4084e9dbad6fSeschrock return (0); 4085e9dbad6fSeschrock } 4086ecd6cf80Smarks 4087ecd6cf80Smarks int 4088ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) 4089ecd6cf80Smarks { 4090ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 4091ecd6cf80Smarks nvlist_t *nvp; 4092ecd6cf80Smarks gid_t gid; 4093ecd6cf80Smarks uid_t uid; 4094ecd6cf80Smarks const gid_t *groups; 4095ecd6cf80Smarks int group_cnt; 4096ecd6cf80Smarks int error; 4097ecd6cf80Smarks 4098ecd6cf80Smarks if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0) 4099ecd6cf80Smarks return (no_memory(hdl)); 4100ecd6cf80Smarks 4101ecd6cf80Smarks uid = ucred_geteuid(cred); 4102ecd6cf80Smarks gid = ucred_getegid(cred); 4103ecd6cf80Smarks group_cnt = ucred_getgroups(cred, &groups); 4104ecd6cf80Smarks 4105ecd6cf80Smarks if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1) 4106ecd6cf80Smarks return (1); 4107ecd6cf80Smarks 4108ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) { 4109ecd6cf80Smarks nvlist_free(nvp); 4110ecd6cf80Smarks return (1); 4111ecd6cf80Smarks } 4112ecd6cf80Smarks 4113ecd6cf80Smarks if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) { 4114ecd6cf80Smarks nvlist_free(nvp); 4115ecd6cf80Smarks return (1); 4116ecd6cf80Smarks } 4117ecd6cf80Smarks 4118ecd6cf80Smarks if (nvlist_add_uint32_array(nvp, 4119ecd6cf80Smarks ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) { 4120ecd6cf80Smarks nvlist_free(nvp); 4121ecd6cf80Smarks return (1); 4122ecd6cf80Smarks } 4123ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4124ecd6cf80Smarks 4125990b4856Slling if (zcmd_write_src_nvlist(hdl, &zc, nvp)) 4126ecd6cf80Smarks return (-1); 4127ecd6cf80Smarks 4128ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc); 4129ecd6cf80Smarks nvlist_free(nvp); 4130ecd6cf80Smarks return (error); 4131ecd6cf80Smarks } 4132ecd6cf80Smarks 4133ecd6cf80Smarks int 4134ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, 4135da6c28aaSamw void *export, void *sharetab, int sharemax, zfs_share_op_t operation) 4136ecd6cf80Smarks { 4137ecd6cf80Smarks zfs_cmd_t zc = { 0 }; 4138ecd6cf80Smarks int error; 4139ecd6cf80Smarks 4140ecd6cf80Smarks (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 4141ecd6cf80Smarks (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); 4142ecd6cf80Smarks zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; 4143ecd6cf80Smarks zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; 4144da6c28aaSamw zc.zc_share.z_sharetype = operation; 4145ecd6cf80Smarks zc.zc_share.z_sharemax = sharemax; 4146ecd6cf80Smarks 4147ecd6cf80Smarks error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); 4148ecd6cf80Smarks return (error); 4149ecd6cf80Smarks } 4150