1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221294Slling * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <assert.h> 29789Sahrens #include <ctype.h> 30789Sahrens #include <errno.h> 31789Sahrens #include <libdevinfo.h> 32789Sahrens #include <libintl.h> 33789Sahrens #include <math.h> 34789Sahrens #include <stdio.h> 35789Sahrens #include <stdlib.h> 36789Sahrens #include <strings.h> 37789Sahrens #include <unistd.h> 38789Sahrens #include <zone.h> 392082Seschrock #include <fcntl.h> 40789Sahrens #include <sys/mntent.h> 41789Sahrens #include <sys/mnttab.h> 421294Slling #include <sys/mount.h> 43789Sahrens 44789Sahrens #include <sys/spa.h> 45789Sahrens #include <sys/zio.h> 46789Sahrens #include <libzfs.h> 47789Sahrens 48789Sahrens #include "zfs_namecheck.h" 49789Sahrens #include "zfs_prop.h" 50789Sahrens #include "libzfs_impl.h" 51789Sahrens 52789Sahrens /* 53789Sahrens * Given a single type (not a mask of types), return the type in a human 54789Sahrens * readable form. 55789Sahrens */ 56789Sahrens const char * 57789Sahrens zfs_type_to_name(zfs_type_t type) 58789Sahrens { 59789Sahrens switch (type) { 60789Sahrens case ZFS_TYPE_FILESYSTEM: 61789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 62789Sahrens case ZFS_TYPE_SNAPSHOT: 63789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 64789Sahrens case ZFS_TYPE_VOLUME: 65789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 66789Sahrens } 67789Sahrens 68789Sahrens return (NULL); 69789Sahrens } 70789Sahrens 71789Sahrens /* 72789Sahrens * Given a path and mask of ZFS types, return a string describing this dataset. 73789Sahrens * This is used when we fail to open a dataset and we cannot get an exact type. 74789Sahrens * We guess what the type would have been based on the path and the mask of 75789Sahrens * acceptable types. 76789Sahrens */ 77789Sahrens static const char * 78789Sahrens path_to_str(const char *path, int types) 79789Sahrens { 80789Sahrens /* 81789Sahrens * When given a single type, always report the exact type. 82789Sahrens */ 83789Sahrens if (types == ZFS_TYPE_SNAPSHOT) 84789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 85789Sahrens if (types == ZFS_TYPE_FILESYSTEM) 86789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 87789Sahrens if (types == ZFS_TYPE_VOLUME) 88789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 89789Sahrens 90789Sahrens /* 91789Sahrens * The user is requesting more than one type of dataset. If this is the 92789Sahrens * case, consult the path itself. If we're looking for a snapshot, and 93789Sahrens * a '@' is found, then report it as "snapshot". Otherwise, remove the 94789Sahrens * snapshot attribute and try again. 95789Sahrens */ 96789Sahrens if (types & ZFS_TYPE_SNAPSHOT) { 97789Sahrens if (strchr(path, '@') != NULL) 98789Sahrens return (dgettext(TEXT_DOMAIN, "snapshot")); 99789Sahrens return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT)); 100789Sahrens } 101789Sahrens 102789Sahrens 103789Sahrens /* 104789Sahrens * The user has requested either filesystems or volumes. 105789Sahrens * We have no way of knowing a priori what type this would be, so always 106789Sahrens * report it as "filesystem" or "volume", our two primitive types. 107789Sahrens */ 108789Sahrens if (types & ZFS_TYPE_FILESYSTEM) 109789Sahrens return (dgettext(TEXT_DOMAIN, "filesystem")); 110789Sahrens 111789Sahrens assert(types & ZFS_TYPE_VOLUME); 112789Sahrens return (dgettext(TEXT_DOMAIN, "volume")); 113789Sahrens } 114789Sahrens 115789Sahrens /* 116789Sahrens * Validate a ZFS path. This is used even before trying to open the dataset, to 117789Sahrens * provide a more meaningful error message. We place a more useful message in 118789Sahrens * 'buf' detailing exactly why the name was not valid. 119789Sahrens */ 120789Sahrens static int 1212082Seschrock zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type) 122789Sahrens { 123789Sahrens namecheck_err_t why; 124789Sahrens char what; 125789Sahrens 126789Sahrens if (dataset_namecheck(path, &why, &what) != 0) { 1272082Seschrock if (hdl != NULL) { 128789Sahrens switch (why) { 1291003Slling case NAME_ERR_TOOLONG: 1302082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1312082Seschrock "name is too long")); 1321003Slling break; 1331003Slling 134789Sahrens case NAME_ERR_LEADING_SLASH: 1352082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1362082Seschrock "leading slash in name")); 137789Sahrens break; 138789Sahrens 139789Sahrens case NAME_ERR_EMPTY_COMPONENT: 1402082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1412082Seschrock "empty component in name")); 142789Sahrens break; 143789Sahrens 144789Sahrens case NAME_ERR_TRAILING_SLASH: 1452082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1462082Seschrock "trailing slash in name")); 147789Sahrens break; 148789Sahrens 149789Sahrens case NAME_ERR_INVALCHAR: 1502082Seschrock zfs_error_aux(hdl, 151789Sahrens dgettext(TEXT_DOMAIN, "invalid character " 1522082Seschrock "'%c' in name"), what); 153789Sahrens break; 154789Sahrens 155789Sahrens case NAME_ERR_MULTIPLE_AT: 1562082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1572082Seschrock "multiple '@' delimiters in name")); 158789Sahrens break; 159789Sahrens } 160789Sahrens } 161789Sahrens 162789Sahrens return (0); 163789Sahrens } 164789Sahrens 165789Sahrens if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { 1662082Seschrock if (hdl != NULL) 1672082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1682082Seschrock "snapshot delimiter '@' in filesystem name")); 169789Sahrens return (0); 170789Sahrens } 171789Sahrens 1722082Seschrock return (-1); 173789Sahrens } 174789Sahrens 175789Sahrens int 176789Sahrens zfs_name_valid(const char *name, zfs_type_t type) 177789Sahrens { 1782082Seschrock return (zfs_validate_name(NULL, name, type)); 179789Sahrens } 180789Sahrens 181789Sahrens /* 182789Sahrens * Utility function to gather stats (objset and zpl) for the given object. 183789Sahrens */ 184789Sahrens static int 185789Sahrens get_stats(zfs_handle_t *zhp) 186789Sahrens { 187789Sahrens zfs_cmd_t zc = { 0 }; 188789Sahrens 189789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 190789Sahrens 1912082Seschrock if ((zc.zc_config_src = (uint64_t)(uintptr_t)malloc(1024)) == NULL) 1922082Seschrock return (-1); 1931356Seschrock zc.zc_config_src_size = 1024; 1941356Seschrock 1952082Seschrock while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) { 1961356Seschrock if (errno == ENOMEM) { 1972082Seschrock free((void *)(uintptr_t)zc.zc_config_src); 1982082Seschrock if ((zc.zc_config_src = (uint64_t)(uintptr_t) 1992082Seschrock malloc(zc.zc_config_src_size)) == NULL) 2002082Seschrock return (-1); 2011356Seschrock } else { 2021356Seschrock free((void *)(uintptr_t)zc.zc_config_src); 2031356Seschrock return (-1); 2041356Seschrock } 2051356Seschrock } 206789Sahrens 207789Sahrens bcopy(&zc.zc_objset_stats, &zhp->zfs_dmustats, 208789Sahrens sizeof (zc.zc_objset_stats)); 209789Sahrens 2101544Seschrock (void) strcpy(zhp->zfs_root, zc.zc_root); 2111544Seschrock 2122082Seschrock if (zhp->zfs_props) { 2132082Seschrock nvlist_free(zhp->zfs_props); 2142082Seschrock zhp->zfs_props = NULL; 2152082Seschrock } 2162082Seschrock 2172082Seschrock if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_src, 2182082Seschrock zc.zc_config_src_size, &zhp->zfs_props, 0) != 0) { 2192082Seschrock free((void *)(uintptr_t)zc.zc_config_src); 2202082Seschrock return (-1); 2212082Seschrock } 222789Sahrens 223789Sahrens zhp->zfs_volsize = zc.zc_volsize; 224789Sahrens zhp->zfs_volblocksize = zc.zc_volblocksize; 225789Sahrens 2262082Seschrock free((void *)(uintptr_t)zc.zc_config_src); 2272082Seschrock 228789Sahrens return (0); 229789Sahrens } 230789Sahrens 231789Sahrens /* 232789Sahrens * Refresh the properties currently stored in the handle. 233789Sahrens */ 234789Sahrens void 235789Sahrens zfs_refresh_properties(zfs_handle_t *zhp) 236789Sahrens { 237789Sahrens (void) get_stats(zhp); 238789Sahrens } 239789Sahrens 240789Sahrens /* 241789Sahrens * Makes a handle from the given dataset name. Used by zfs_open() and 242789Sahrens * zfs_iter_* to create child handles on the fly. 243789Sahrens */ 244789Sahrens zfs_handle_t * 2452082Seschrock make_dataset_handle(libzfs_handle_t *hdl, const char *path) 246789Sahrens { 2472082Seschrock zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); 2482082Seschrock 2492082Seschrock if (zhp == NULL) 2502082Seschrock return (NULL); 2512082Seschrock 2522082Seschrock zhp->zfs_hdl = hdl; 253789Sahrens 2541758Sahrens top: 255789Sahrens (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); 256789Sahrens 257789Sahrens if (get_stats(zhp) != 0) { 258789Sahrens free(zhp); 259789Sahrens return (NULL); 260789Sahrens } 261789Sahrens 2621758Sahrens if (zhp->zfs_dmustats.dds_inconsistent) { 2631758Sahrens zfs_cmd_t zc = { 0 }; 2641758Sahrens 2651758Sahrens /* 2661758Sahrens * If it is dds_inconsistent, then we've caught it in 2671758Sahrens * the middle of a 'zfs receive' or 'zfs destroy', and 2681758Sahrens * it is inconsistent from the ZPL's point of view, so 2691758Sahrens * can't be mounted. However, it could also be that we 2701758Sahrens * have crashed in the middle of one of those 2711758Sahrens * operations, in which case we need to get rid of the 2721758Sahrens * inconsistent state. We do that by either rolling 2731758Sahrens * back to the previous snapshot (which will fail if 2741758Sahrens * there is none), or destroying the filesystem. Note 2751758Sahrens * that if we are still in the middle of an active 2761758Sahrens * 'receive' or 'destroy', then the rollback and destroy 2771758Sahrens * will fail with EBUSY and we will drive on as usual. 2781758Sahrens */ 2791758Sahrens 2801758Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2811758Sahrens 2821758Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 2832082Seschrock (void) zvol_remove_link(hdl, zhp->zfs_name); 2841758Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2851758Sahrens } else { 2861758Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2871758Sahrens } 2881758Sahrens 2891758Sahrens /* If we can successfully roll it back, reget the stats */ 2902082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0) 2911758Sahrens goto top; 2921758Sahrens /* 2931758Sahrens * If we can sucessfully destroy it, pretend that it 2941758Sahrens * never existed. 2951758Sahrens */ 2962082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) { 2971758Sahrens free(zhp); 2981758Sahrens errno = ENOENT; 2991758Sahrens return (NULL); 3001758Sahrens } 3011758Sahrens } 3021758Sahrens 303789Sahrens /* 304789Sahrens * We've managed to open the dataset and gather statistics. Determine 305789Sahrens * the high-level type. 306789Sahrens */ 307789Sahrens if (zhp->zfs_dmustats.dds_is_snapshot) 308789Sahrens zhp->zfs_type = ZFS_TYPE_SNAPSHOT; 309789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) 310789Sahrens zhp->zfs_type = ZFS_TYPE_VOLUME; 311789Sahrens else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) 312789Sahrens zhp->zfs_type = ZFS_TYPE_FILESYSTEM; 313789Sahrens else 3142082Seschrock abort(); /* we should never see any other types */ 315789Sahrens 316789Sahrens return (zhp); 317789Sahrens } 318789Sahrens 319789Sahrens /* 320789Sahrens * Opens the given snapshot, filesystem, or volume. The 'types' 321789Sahrens * argument is a mask of acceptable types. The function will print an 322789Sahrens * appropriate error message and return NULL if it can't be opened. 323789Sahrens */ 324789Sahrens zfs_handle_t * 3252082Seschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types) 326789Sahrens { 327789Sahrens zfs_handle_t *zhp; 3282082Seschrock char errbuf[1024]; 3292082Seschrock 3302082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 3312082Seschrock dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); 332789Sahrens 333789Sahrens /* 3342082Seschrock * Validate the name before we even try to open it. 335789Sahrens */ 3362082Seschrock if (!zfs_validate_name(hdl, path, ZFS_TYPE_ANY)) { 3372082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3382082Seschrock "invalid dataset name")); 3392082Seschrock (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); 340789Sahrens return (NULL); 341789Sahrens } 342789Sahrens 343789Sahrens /* 344789Sahrens * Try to get stats for the dataset, which will tell us if it exists. 345789Sahrens */ 346789Sahrens errno = 0; 3472082Seschrock if ((zhp = make_dataset_handle(hdl, path)) == NULL) { 3482082Seschrock (void) zfs_standard_error(hdl, errno, errbuf, path); 349789Sahrens return (NULL); 350789Sahrens } 351789Sahrens 352789Sahrens if (!(types & zhp->zfs_type)) { 3532082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 354*2142Seschrock zfs_close(zhp); 355789Sahrens return (NULL); 356789Sahrens } 357789Sahrens 358789Sahrens return (zhp); 359789Sahrens } 360789Sahrens 361789Sahrens /* 362789Sahrens * Release a ZFS handle. Nothing to do but free the associated memory. 363789Sahrens */ 364789Sahrens void 365789Sahrens zfs_close(zfs_handle_t *zhp) 366789Sahrens { 367789Sahrens if (zhp->zfs_mntopts) 368789Sahrens free(zhp->zfs_mntopts); 3692082Seschrock if (zhp->zfs_props) 3702082Seschrock nvlist_free(zhp->zfs_props); 371789Sahrens free(zhp); 372789Sahrens } 373789Sahrens 374789Sahrens struct { 375789Sahrens const char *name; 376789Sahrens uint64_t value; 377789Sahrens } checksum_table[] = { 378789Sahrens { "on", ZIO_CHECKSUM_ON }, 379789Sahrens { "off", ZIO_CHECKSUM_OFF }, 380789Sahrens { "fletcher2", ZIO_CHECKSUM_FLETCHER_2 }, 381789Sahrens { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 }, 382789Sahrens { "sha256", ZIO_CHECKSUM_SHA256 }, 383789Sahrens { NULL } 384789Sahrens }; 385789Sahrens 386789Sahrens struct { 387789Sahrens const char *name; 388789Sahrens uint64_t value; 389789Sahrens } compress_table[] = { 390789Sahrens { "on", ZIO_COMPRESS_ON }, 391789Sahrens { "off", ZIO_COMPRESS_OFF }, 392789Sahrens { "lzjb", ZIO_COMPRESS_LZJB }, 393789Sahrens { NULL } 394789Sahrens }; 395789Sahrens 396789Sahrens struct { 397789Sahrens const char *name; 398789Sahrens uint64_t value; 399789Sahrens } snapdir_table[] = { 400849Sbonwick { "hidden", ZFS_SNAPDIR_HIDDEN }, 401849Sbonwick { "visible", ZFS_SNAPDIR_VISIBLE }, 402789Sahrens { NULL } 403789Sahrens }; 404789Sahrens 405789Sahrens struct { 406789Sahrens const char *name; 407789Sahrens uint64_t value; 408789Sahrens } acl_mode_table[] = { 409789Sahrens { "discard", DISCARD }, 410789Sahrens { "groupmask", GROUPMASK }, 411789Sahrens { "passthrough", PASSTHROUGH }, 412789Sahrens { NULL } 413789Sahrens }; 414789Sahrens 415789Sahrens struct { 416789Sahrens const char *name; 417789Sahrens uint64_t value; 418789Sahrens } acl_inherit_table[] = { 419789Sahrens { "discard", DISCARD }, 420789Sahrens { "noallow", NOALLOW }, 421789Sahrens { "secure", SECURE }, 422789Sahrens { "passthrough", PASSTHROUGH }, 423789Sahrens { NULL } 424789Sahrens }; 425789Sahrens 426789Sahrens 427789Sahrens /* 428789Sahrens * Given a numeric suffix, convert the value into a number of bits that the 429789Sahrens * resulting value must be shifted. 430789Sahrens */ 431789Sahrens static int 4322082Seschrock str2shift(libzfs_handle_t *hdl, const char *buf) 433789Sahrens { 434789Sahrens const char *ends = "BKMGTPEZ"; 435789Sahrens int i; 436789Sahrens 437789Sahrens if (buf[0] == '\0') 438789Sahrens return (0); 439789Sahrens for (i = 0; i < strlen(ends); i++) { 440789Sahrens if (toupper(buf[0]) == ends[i]) 441789Sahrens break; 442789Sahrens } 443789Sahrens if (i == strlen(ends)) { 4442082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4452082Seschrock "invalid numeric suffix '%s'"), buf); 446789Sahrens return (-1); 447789Sahrens } 448789Sahrens 449789Sahrens /* 450789Sahrens * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't 451789Sahrens * allow 'BB' - that's just weird. 452789Sahrens */ 453789Sahrens if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' && 4542082Seschrock toupper(buf[0]) != 'B')) 455789Sahrens return (10*i); 4562082Seschrock 4572082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4582082Seschrock "invalid numeric suffix '%s'"), buf); 459789Sahrens return (-1); 460789Sahrens } 461789Sahrens 462789Sahrens /* 463789Sahrens * Convert a string of the form '100G' into a real number. Used when setting 464789Sahrens * properties or creating a volume. 'buf' is used to place an extended error 465789Sahrens * message for the caller to use. 466789Sahrens */ 467789Sahrens static int 4682082Seschrock nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num) 469789Sahrens { 470789Sahrens char *end; 471789Sahrens int shift; 472789Sahrens 473789Sahrens *num = 0; 474789Sahrens 475789Sahrens /* Check to see if this looks like a number. */ 476789Sahrens if ((value[0] < '0' || value[0] > '9') && value[0] != '.') { 4772082Seschrock if (hdl) 4782082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4792082Seschrock "bad numeric value '%s'"), value); 480789Sahrens return (-1); 481789Sahrens } 482789Sahrens 483789Sahrens /* Rely on stroll() to process the numeric portion. */ 484789Sahrens errno = 0; 485789Sahrens *num = strtoll(value, &end, 10); 486789Sahrens 487789Sahrens /* 488789Sahrens * Check for ERANGE, which indicates that the value is too large to fit 489789Sahrens * in a 64-bit value. 490789Sahrens */ 491789Sahrens if (errno == ERANGE) { 4922082Seschrock if (hdl) 4932082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 4942082Seschrock "numeric value is too large")); 495789Sahrens return (-1); 496789Sahrens } 497789Sahrens 498789Sahrens /* 499789Sahrens * If we have a decimal value, then do the computation with floating 500789Sahrens * point arithmetic. Otherwise, use standard arithmetic. 501789Sahrens */ 502789Sahrens if (*end == '.') { 503789Sahrens double fval = strtod(value, &end); 504789Sahrens 5052082Seschrock if ((shift = str2shift(hdl, end)) == -1) 506789Sahrens return (-1); 507789Sahrens 508789Sahrens fval *= pow(2, shift); 509789Sahrens 510789Sahrens if (fval > UINT64_MAX) { 5112082Seschrock if (hdl) 5122082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5132082Seschrock "numeric value is too large")); 514789Sahrens return (-1); 515789Sahrens } 516789Sahrens 517789Sahrens *num = (uint64_t)fval; 518789Sahrens } else { 5192082Seschrock if ((shift = str2shift(hdl, end)) == -1) 520789Sahrens return (-1); 521789Sahrens 522789Sahrens /* Check for overflow */ 523789Sahrens if (shift >= 64 || (*num << shift) >> shift != *num) { 5242082Seschrock if (hdl) 5252082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5262082Seschrock "numeric value is too large")); 527789Sahrens return (-1); 528789Sahrens } 529789Sahrens 530789Sahrens *num <<= shift; 531789Sahrens } 532789Sahrens 533789Sahrens return (0); 534789Sahrens } 535789Sahrens 536789Sahrens int 537789Sahrens zfs_nicestrtonum(const char *str, uint64_t *val) 538789Sahrens { 5392082Seschrock return (nicestrtonum(NULL, str, val)); 540789Sahrens } 541789Sahrens 542789Sahrens /* 543789Sahrens * Given a property type and value, verify that the value is appropriate. Used 544789Sahrens * by zfs_prop_set() and some libzfs consumers. 545789Sahrens */ 546789Sahrens int 5472082Seschrock zfs_prop_validate(libzfs_handle_t *hdl, zfs_prop_t prop, const char *value, 5482082Seschrock uint64_t *intval) 549789Sahrens { 550789Sahrens const char *propname = zfs_prop_to_name(prop); 551789Sahrens uint64_t number; 5522082Seschrock char errbuf[1024]; 553789Sahrens int i; 554789Sahrens 555789Sahrens /* 556789Sahrens * Check to see if this a read-only property. 557789Sahrens */ 5582082Seschrock if (zfs_prop_readonly(prop)) 5592082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, 5602082Seschrock dgettext(TEXT_DOMAIN, "cannot set %s property"), propname)); 5612082Seschrock 5622082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 5632082Seschrock dgettext(TEXT_DOMAIN, "bad %s value '%s'"), propname, value); 564789Sahrens 565789Sahrens /* See if the property value is too long */ 566789Sahrens if (strlen(value) >= ZFS_MAXPROPLEN) { 5672082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "value is too long")); 5682082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 569789Sahrens } 570789Sahrens 571789Sahrens /* Perform basic checking based on property type */ 572789Sahrens switch (zfs_prop_get_type(prop)) { 573789Sahrens case prop_type_boolean: 574789Sahrens if (strcmp(value, "on") == 0) { 575789Sahrens number = 1; 576789Sahrens } else if (strcmp(value, "off") == 0) { 577789Sahrens number = 0; 578789Sahrens } else { 5792082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5802082Seschrock "must be 'on' or 'off'")); 5812082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 582789Sahrens } 583789Sahrens break; 584789Sahrens 585789Sahrens case prop_type_number: 586789Sahrens /* treat 'none' as 0 */ 587789Sahrens if (strcmp(value, "none") == 0) { 588789Sahrens number = 0; 589789Sahrens break; 590789Sahrens } 591789Sahrens 5922082Seschrock if (nicestrtonum(hdl, value, &number) != 0) 5932082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 594789Sahrens 595789Sahrens /* don't allow 0 for quota, use 'none' instead */ 596789Sahrens if (prop == ZFS_PROP_QUOTA && number == 0 && 597789Sahrens strcmp(value, "none") != 0) { 5982082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 5992082Seschrock "use 'quota=none' to disable")); 6002082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 601789Sahrens } 602789Sahrens 603789Sahrens /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ 604789Sahrens if (prop == ZFS_PROP_RECORDSIZE || 605789Sahrens prop == ZFS_PROP_VOLBLOCKSIZE) { 606789Sahrens if (number < SPA_MINBLOCKSIZE || 607789Sahrens number > SPA_MAXBLOCKSIZE || !ISP2(number)) { 6082082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 609789Sahrens "must be power of 2 from %u to %uk"), 610789Sahrens (uint_t)SPA_MINBLOCKSIZE, 611789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 6122082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 613789Sahrens } 614789Sahrens } 615789Sahrens 616789Sahrens break; 617789Sahrens 618789Sahrens case prop_type_string: 619789Sahrens case prop_type_index: 620789Sahrens /* 621789Sahrens * The two writable string values, 'mountpoint' and 622789Sahrens * 'checksum' need special consideration. The 'index' types are 623789Sahrens * specified as strings by the user, but passed to the kernel as 624789Sahrens * integers. 625789Sahrens */ 626789Sahrens switch (prop) { 627789Sahrens case ZFS_PROP_MOUNTPOINT: 628789Sahrens if (strcmp(value, ZFS_MOUNTPOINT_NONE) == 0 || 629789Sahrens strcmp(value, ZFS_MOUNTPOINT_LEGACY) == 0) 630789Sahrens break; 631789Sahrens 632789Sahrens if (value[0] != '/') { 6332082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 6342082Seschrock "must be an absolute path, 'none', or " 6352082Seschrock "'legacy'")); 6362082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 637789Sahrens } 638789Sahrens break; 639789Sahrens 640789Sahrens case ZFS_PROP_CHECKSUM: 641789Sahrens for (i = 0; checksum_table[i].name != NULL; i++) { 642789Sahrens if (strcmp(value, checksum_table[i].name) 643789Sahrens == 0) { 644789Sahrens number = checksum_table[i].value; 645789Sahrens break; 646789Sahrens } 647789Sahrens } 648789Sahrens 649789Sahrens if (checksum_table[i].name == NULL) { 6502082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 6512082Seschrock "must be 'on', 'off', 'fletcher2', " 6522082Seschrock "'fletcher4', or 'sha256'")); 6532082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 654789Sahrens } 655789Sahrens break; 656789Sahrens 657789Sahrens case ZFS_PROP_COMPRESSION: 658789Sahrens for (i = 0; compress_table[i].name != NULL; i++) { 659789Sahrens if (strcmp(value, compress_table[i].name) 660789Sahrens == 0) { 661789Sahrens number = compress_table[i].value; 662789Sahrens break; 663789Sahrens } 664789Sahrens } 665789Sahrens 666789Sahrens if (compress_table[i].name == NULL) { 6672082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 6682082Seschrock "must be 'on', 'off', or 'lzjb'")); 6692082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 670789Sahrens } 671789Sahrens break; 672789Sahrens 673789Sahrens case ZFS_PROP_SNAPDIR: 674789Sahrens for (i = 0; snapdir_table[i].name != NULL; i++) { 675789Sahrens if (strcmp(value, snapdir_table[i].name) == 0) { 676789Sahrens number = snapdir_table[i].value; 677789Sahrens break; 678789Sahrens } 679789Sahrens } 680789Sahrens 681789Sahrens if (snapdir_table[i].name == NULL) { 6822082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 6832082Seschrock "must be 'hidden' or 'visible'")); 6842082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 685789Sahrens } 686789Sahrens break; 687789Sahrens 688789Sahrens case ZFS_PROP_ACLMODE: 689789Sahrens for (i = 0; acl_mode_table[i].name != NULL; i++) { 690789Sahrens if (strcmp(value, acl_mode_table[i].name) 691789Sahrens == 0) { 692789Sahrens number = acl_mode_table[i].value; 693789Sahrens break; 694789Sahrens } 695789Sahrens } 696789Sahrens 697789Sahrens if (acl_mode_table[i].name == NULL) { 6982082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 6992082Seschrock "must be 'disacard', 'groupmask', or " 7002082Seschrock "'passthrough'")); 7012082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 702789Sahrens } 703789Sahrens break; 704789Sahrens 705789Sahrens case ZFS_PROP_ACLINHERIT: 706789Sahrens for (i = 0; acl_inherit_table[i].name != NULL; i++) { 707789Sahrens if (strcmp(value, acl_inherit_table[i].name) 708789Sahrens == 0) { 709789Sahrens number = acl_inherit_table[i].value; 710789Sahrens break; 711789Sahrens } 712789Sahrens } 713789Sahrens 714789Sahrens if (acl_inherit_table[i].name == NULL) { 7152082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7162082Seschrock "must be 'discard, 'noallow', 'secure', " 7172082Seschrock "or 'passthrough'")); 7182082Seschrock return (zfs_error(hdl, EZFS_BADPROP, errbuf)); 719789Sahrens } 720789Sahrens break; 721789Sahrens 722789Sahrens case ZFS_PROP_SHARENFS: 723789Sahrens /* 724789Sahrens * Nothing to do for 'sharenfs', this gets passed on to 725789Sahrens * share(1M) verbatim. 726789Sahrens */ 727789Sahrens break; 728789Sahrens } 729789Sahrens } 730789Sahrens 731789Sahrens if (intval != NULL) 732789Sahrens *intval = number; 733789Sahrens 734789Sahrens return (0); 735789Sahrens } 736789Sahrens 737789Sahrens /* 738789Sahrens * Given a property name and value, set the property for the given dataset. 739789Sahrens */ 740789Sahrens int 741789Sahrens zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) 742789Sahrens { 743789Sahrens const char *propname = zfs_prop_to_name(prop); 744789Sahrens uint64_t number; 745789Sahrens zfs_cmd_t zc = { 0 }; 746789Sahrens int ret; 747789Sahrens prop_changelist_t *cl; 7482082Seschrock char errbuf[1024]; 7492082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 7502082Seschrock 7512082Seschrock if (zfs_prop_validate(zhp->zfs_hdl, prop, propval, &number) != 0) 752789Sahrens return (-1); 753789Sahrens 7542082Seschrock 7552082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 7562082Seschrock dgettext(TEXT_DOMAIN, "cannot set %s for '%s'"), propname, 7572082Seschrock zhp->zfs_name); 7582082Seschrock 759789Sahrens /* 760789Sahrens * Check to see if the value applies to this type 761789Sahrens */ 7622082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 7632082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 764789Sahrens 765789Sahrens /* 766789Sahrens * For the mountpoint and sharenfs properties, check if it can be set 767789Sahrens * in a global/non-global zone based on the zoned property value: 768789Sahrens * 769789Sahrens * global zone non-global zone 770789Sahrens * ----------------------------------------------------- 771789Sahrens * zoned=on mountpoint (no) mountpoint (yes) 772789Sahrens * sharenfs (no) sharenfs (no) 773789Sahrens * 774789Sahrens * zoned=off mountpoint (yes) N/A 775789Sahrens * sharenfs (yes) 776789Sahrens */ 777789Sahrens if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { 778789Sahrens if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 779789Sahrens if (getzoneid() == GLOBAL_ZONEID) { 7802082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7812082Seschrock "dataset is used in a non-global zone")); 7822082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 783789Sahrens } else if (prop == ZFS_PROP_SHARENFS) { 7842082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7852082Seschrock "filesystems cannot be shared in a " 7862082Seschrock "non-global zone")); 7872082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 788789Sahrens } 789789Sahrens } else if (getzoneid() != GLOBAL_ZONEID) { 790789Sahrens /* 791789Sahrens * If zoned property is 'off', this must be in 792789Sahrens * a globle zone. If not, something is wrong. 793789Sahrens */ 7942082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 7952082Seschrock "dataset is used in a non-global zone, but " 7962082Seschrock "'zoned' property is not set")); 7972082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 798789Sahrens } 799789Sahrens } 800789Sahrens 801789Sahrens if ((cl = changelist_gather(zhp, prop, 0)) == NULL) 802789Sahrens return (-1); 803789Sahrens 804789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 8052082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8062082Seschrock "child dataset with inherited mountpoint is used " 8072082Seschrock "in a non-global zone")); 8082082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 809789Sahrens goto error; 810789Sahrens } 811789Sahrens 812789Sahrens if ((ret = changelist_prefix(cl)) != 0) 813789Sahrens goto error; 814789Sahrens 815789Sahrens /* 816789Sahrens * Execute the corresponding ioctl() to set this property. 817789Sahrens */ 818789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 819789Sahrens 820789Sahrens switch (prop) { 821789Sahrens case ZFS_PROP_QUOTA: 822789Sahrens zc.zc_cookie = number; 8232082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_QUOTA, &zc); 824789Sahrens break; 825789Sahrens case ZFS_PROP_RESERVATION: 826789Sahrens zc.zc_cookie = number; 8272082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_RESERVATION, 8282082Seschrock &zc); 829789Sahrens break; 830789Sahrens case ZFS_PROP_MOUNTPOINT: 831789Sahrens case ZFS_PROP_SHARENFS: 832789Sahrens /* 833789Sahrens * These properties are passed down as real strings. 834789Sahrens */ 835789Sahrens (void) strlcpy(zc.zc_prop_name, propname, 836789Sahrens sizeof (zc.zc_prop_name)); 837789Sahrens (void) strlcpy(zc.zc_prop_value, propval, 838789Sahrens sizeof (zc.zc_prop_value)); 839789Sahrens zc.zc_intsz = 1; 840789Sahrens zc.zc_numints = strlen(propval) + 1; 8412082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); 842789Sahrens break; 843789Sahrens case ZFS_PROP_VOLSIZE: 844789Sahrens zc.zc_volsize = number; 8452082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLSIZE, &zc); 846789Sahrens break; 847789Sahrens case ZFS_PROP_VOLBLOCKSIZE: 848789Sahrens zc.zc_volblocksize = number; 8492082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLBLOCKSIZE, 8502082Seschrock &zc); 851789Sahrens break; 852789Sahrens default: 853789Sahrens (void) strlcpy(zc.zc_prop_name, propname, 854789Sahrens sizeof (zc.zc_prop_name)); 855789Sahrens /* LINTED - alignment */ 856789Sahrens *(uint64_t *)zc.zc_prop_value = number; 857789Sahrens zc.zc_intsz = 8; 858789Sahrens zc.zc_numints = 1; 8592082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); 860789Sahrens break; 861789Sahrens } 862789Sahrens 863789Sahrens if (ret != 0) { 864789Sahrens switch (errno) { 865789Sahrens 866789Sahrens case ENOSPC: 867789Sahrens /* 868789Sahrens * For quotas and reservations, ENOSPC indicates 869789Sahrens * something different; setting a quota or reservation 870789Sahrens * doesn't use any disk space. 871789Sahrens */ 872789Sahrens switch (prop) { 873789Sahrens case ZFS_PROP_QUOTA: 8742082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8752082Seschrock "size is less than current used or " 8762082Seschrock "reserved space")); 8772082Seschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 878789Sahrens break; 879789Sahrens 880789Sahrens case ZFS_PROP_RESERVATION: 8812082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 8822082Seschrock "size is greater than available space")); 8832082Seschrock (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); 884789Sahrens break; 885789Sahrens 886789Sahrens default: 8872082Seschrock (void) zfs_standard_error(hdl, errno, errbuf); 888789Sahrens break; 889789Sahrens } 890789Sahrens break; 891789Sahrens 892789Sahrens case EBUSY: 8932082Seschrock if (prop == ZFS_PROP_VOLBLOCKSIZE) 8942082Seschrock (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); 8952082Seschrock else 8962082Seschrock return (zfs_standard_error(hdl, EBUSY, errbuf)); 897789Sahrens break; 898789Sahrens 8991175Slling case EROFS: 9002082Seschrock (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); 9011175Slling break; 9021175Slling 903789Sahrens case EOVERFLOW: 904789Sahrens /* 905789Sahrens * This platform can't address a volume this big. 906789Sahrens */ 907789Sahrens #ifdef _ILP32 908789Sahrens if (prop == ZFS_PROP_VOLSIZE) { 9092082Seschrock (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); 910789Sahrens break; 911789Sahrens } 912789Sahrens #endif 9132082Seschrock /* FALLTHROUGH */ 914789Sahrens default: 9152082Seschrock (void) zfs_standard_error(hdl, errno, errbuf); 916789Sahrens } 917789Sahrens } else { 918789Sahrens /* 919789Sahrens * Refresh the statistics so the new property value 920789Sahrens * is reflected. 921789Sahrens */ 922789Sahrens if ((ret = changelist_postfix(cl)) != 0) 923789Sahrens goto error; 924789Sahrens 925789Sahrens (void) get_stats(zhp); 926789Sahrens } 927789Sahrens 928789Sahrens error: 929789Sahrens changelist_free(cl); 930789Sahrens return (ret); 931789Sahrens } 932789Sahrens 933789Sahrens /* 934789Sahrens * Given a property, inherit the value from the parent dataset. 935789Sahrens */ 936789Sahrens int 937789Sahrens zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) 938789Sahrens { 939789Sahrens const char *propname = zfs_prop_to_name(prop); 940789Sahrens zfs_cmd_t zc = { 0 }; 941789Sahrens int ret; 942789Sahrens prop_changelist_t *cl; 9432082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 9442082Seschrock char errbuf[1024]; 9452082Seschrock 9462082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 9472082Seschrock "cannot inherit %s for '%s'"), propname, zhp->zfs_name); 948789Sahrens 949789Sahrens /* 950789Sahrens * Verify that this property is inheritable. 951789Sahrens */ 9522082Seschrock if (zfs_prop_readonly(prop)) 9532082Seschrock return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); 9542082Seschrock 9552082Seschrock if (!zfs_prop_inheritable(prop)) 9562082Seschrock return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); 957789Sahrens 958789Sahrens /* 959789Sahrens * Check to see if the value applies to this type 960789Sahrens */ 9612082Seschrock if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 9622082Seschrock return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); 963789Sahrens 964789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 965789Sahrens (void) strlcpy(zc.zc_prop_name, propname, sizeof (zc.zc_prop_name)); 966789Sahrens 967789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && 968789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 9692082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9702082Seschrock "dataset is used in a non-global zone")); 9712082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 972789Sahrens } 973789Sahrens 974789Sahrens /* 975789Sahrens * Determine datasets which will be affected by this change, if any. 976789Sahrens */ 977789Sahrens if ((cl = changelist_gather(zhp, prop, 0)) == NULL) 978789Sahrens return (-1); 979789Sahrens 980789Sahrens if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { 9812082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9822082Seschrock "child dataset with inherited mountpoint is used " 9832082Seschrock "in a non-global zone")); 9842082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 985789Sahrens goto error; 986789Sahrens } 987789Sahrens 988789Sahrens if ((ret = changelist_prefix(cl)) != 0) 989789Sahrens goto error; 990789Sahrens 991789Sahrens zc.zc_numints = 0; 992789Sahrens 9932082Seschrock if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, 9942082Seschrock ZFS_IOC_SET_PROP, &zc)) != 0) { 9952082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 996789Sahrens } else { 997789Sahrens 998789Sahrens if ((ret = changelist_postfix(cl)) != 0) 999789Sahrens goto error; 1000789Sahrens 1001789Sahrens /* 1002789Sahrens * Refresh the statistics so the new property is reflected. 1003789Sahrens */ 1004789Sahrens (void) get_stats(zhp); 1005789Sahrens } 1006789Sahrens 1007789Sahrens 1008789Sahrens error: 1009789Sahrens changelist_free(cl); 1010789Sahrens return (ret); 1011789Sahrens } 1012789Sahrens 1013789Sahrens static void 1014789Sahrens nicebool(int value, char *buf, size_t buflen) 1015789Sahrens { 1016789Sahrens if (value) 1017789Sahrens (void) strlcpy(buf, "on", buflen); 1018789Sahrens else 1019789Sahrens (void) strlcpy(buf, "off", buflen); 1020789Sahrens } 1021789Sahrens 1022789Sahrens /* 10231356Seschrock * True DSL properties are stored in an nvlist. The following two functions 10241356Seschrock * extract them appropriately. 10251356Seschrock */ 10261356Seschrock static uint64_t 10271356Seschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 10281356Seschrock { 10291356Seschrock nvlist_t *nv; 10301356Seschrock uint64_t value; 10311356Seschrock 10321356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 10331356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 10341356Seschrock verify(nvlist_lookup_uint64(nv, ZFS_PROP_VALUE, &value) == 0); 10351356Seschrock verify(nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source) == 0); 10361356Seschrock } else { 10371356Seschrock value = zfs_prop_default_numeric(prop); 10381356Seschrock *source = ""; 10391356Seschrock } 10401356Seschrock 10411356Seschrock return (value); 10421356Seschrock } 10431356Seschrock 10441356Seschrock static char * 10451356Seschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) 10461356Seschrock { 10471356Seschrock nvlist_t *nv; 10481356Seschrock char *value; 10491356Seschrock 10501356Seschrock if (nvlist_lookup_nvlist(zhp->zfs_props, 10511356Seschrock zfs_prop_to_name(prop), &nv) == 0) { 10521356Seschrock verify(nvlist_lookup_string(nv, ZFS_PROP_VALUE, &value) == 0); 10531356Seschrock verify(nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source) == 0); 10541356Seschrock } else { 10551356Seschrock if ((value = (char *)zfs_prop_default_string(prop)) == NULL) 10561356Seschrock value = ""; 10571356Seschrock *source = ""; 10581356Seschrock } 10591356Seschrock 10601356Seschrock return (value); 10611356Seschrock } 10621356Seschrock 10631356Seschrock /* 1064789Sahrens * Internal function for getting a numeric property. Both zfs_prop_get() and 1065789Sahrens * zfs_prop_get_int() are built using this interface. 1066789Sahrens * 1067789Sahrens * Certain properties can be overridden using 'mount -o'. In this case, scan 1068789Sahrens * the contents of the /etc/mnttab entry, searching for the appropriate options. 1069789Sahrens * If they differ from the on-disk values, report the current values and mark 1070789Sahrens * the source "temporary". 1071789Sahrens */ 10722082Seschrock static int 1073789Sahrens get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, 10742082Seschrock char **source, uint64_t *val) 1075789Sahrens { 1076789Sahrens struct mnttab mnt; 1077789Sahrens 1078789Sahrens *source = NULL; 1079789Sahrens 1080789Sahrens if (zhp->zfs_mntopts == NULL) 1081789Sahrens mnt.mnt_mntopts = ""; 1082789Sahrens else 1083789Sahrens mnt.mnt_mntopts = zhp->zfs_mntopts; 1084789Sahrens 1085789Sahrens switch (prop) { 1086789Sahrens case ZFS_PROP_ATIME: 10872082Seschrock *val = getprop_uint64(zhp, prop, source); 10882082Seschrock 10892082Seschrock if (hasmntopt(&mnt, MNTOPT_ATIME) && !*val) { 10902082Seschrock *val = B_TRUE; 1091789Sahrens if (src) 1092789Sahrens *src = ZFS_SRC_TEMPORARY; 10932082Seschrock } else if (hasmntopt(&mnt, MNTOPT_NOATIME) && *val) { 10942082Seschrock *val = B_FALSE; 1095789Sahrens if (src) 1096789Sahrens *src = ZFS_SRC_TEMPORARY; 1097789Sahrens } 10982082Seschrock break; 1099789Sahrens 1100789Sahrens case ZFS_PROP_AVAILABLE: 11012082Seschrock *val = zhp->zfs_dmustats.dds_available; 11022082Seschrock break; 1103789Sahrens 1104789Sahrens case ZFS_PROP_DEVICES: 11052082Seschrock *val = getprop_uint64(zhp, prop, source); 11062082Seschrock 11072082Seschrock if (hasmntopt(&mnt, MNTOPT_DEVICES) && !*val) { 11082082Seschrock *val = B_TRUE; 1109789Sahrens if (src) 1110789Sahrens *src = ZFS_SRC_TEMPORARY; 11112082Seschrock } else if (hasmntopt(&mnt, MNTOPT_NODEVICES) && *val) { 11122082Seschrock *val = B_FALSE; 1113789Sahrens if (src) 1114789Sahrens *src = ZFS_SRC_TEMPORARY; 1115789Sahrens } 11162082Seschrock break; 1117789Sahrens 1118789Sahrens case ZFS_PROP_EXEC: 11192082Seschrock *val = getprop_uint64(zhp, prop, source); 11202082Seschrock 11212082Seschrock if (hasmntopt(&mnt, MNTOPT_EXEC) && !*val) { 11222082Seschrock *val = B_TRUE; 1123789Sahrens if (src) 1124789Sahrens *src = ZFS_SRC_TEMPORARY; 11252082Seschrock } else if (hasmntopt(&mnt, MNTOPT_NOEXEC) && *val) { 11262082Seschrock *val = B_FALSE; 1127789Sahrens if (src) 1128789Sahrens *src = ZFS_SRC_TEMPORARY; 1129789Sahrens } 11302082Seschrock break; 1131789Sahrens 1132789Sahrens case ZFS_PROP_RECORDSIZE: 1133789Sahrens case ZFS_PROP_COMPRESSION: 11341356Seschrock case ZFS_PROP_ZONED: 11352082Seschrock *val = getprop_uint64(zhp, prop, source); 11362082Seschrock break; 1137789Sahrens 1138789Sahrens case ZFS_PROP_READONLY: 11392082Seschrock *val = getprop_uint64(zhp, prop, source); 11402082Seschrock 11412082Seschrock if (hasmntopt(&mnt, MNTOPT_RO) && !*val) { 11422082Seschrock *val = B_TRUE; 1143789Sahrens if (src) 1144789Sahrens *src = ZFS_SRC_TEMPORARY; 11452082Seschrock } else if (hasmntopt(&mnt, MNTOPT_RW) && *val) { 11462082Seschrock *val = B_FALSE; 1147789Sahrens if (src) 1148789Sahrens *src = ZFS_SRC_TEMPORARY; 1149789Sahrens } 11502082Seschrock break; 1151789Sahrens 11521544Seschrock case ZFS_PROP_CREATION: 11532082Seschrock *val = zhp->zfs_dmustats.dds_creation_time; 11542082Seschrock break; 11551544Seschrock 1156789Sahrens case ZFS_PROP_QUOTA: 1157789Sahrens if (zhp->zfs_dmustats.dds_quota == 0) 1158789Sahrens *source = ""; /* default */ 1159789Sahrens else 1160789Sahrens *source = zhp->zfs_name; 11612082Seschrock *val = zhp->zfs_dmustats.dds_quota; 11622082Seschrock break; 1163789Sahrens 1164789Sahrens case ZFS_PROP_RESERVATION: 1165789Sahrens if (zhp->zfs_dmustats.dds_reserved == 0) 1166789Sahrens *source = ""; /* default */ 1167789Sahrens else 1168789Sahrens *source = zhp->zfs_name; 11692082Seschrock *val = zhp->zfs_dmustats.dds_reserved; 11702082Seschrock break; 1171789Sahrens 1172789Sahrens case ZFS_PROP_COMPRESSRATIO: 1173789Sahrens /* 1174789Sahrens * Using physical space and logical space, calculate the 1175789Sahrens * compression ratio. We return the number as a multiple of 1176789Sahrens * 100, so '2.5x' would be returned as 250. 1177789Sahrens */ 1178789Sahrens if (zhp->zfs_dmustats.dds_compressed_bytes == 0) 11792082Seschrock *val = 100ULL; 1180789Sahrens else 11812082Seschrock *val = 11822082Seschrock (zhp->zfs_dmustats.dds_uncompressed_bytes * 100 / 1183789Sahrens zhp->zfs_dmustats.dds_compressed_bytes); 11842082Seschrock break; 1185789Sahrens 1186789Sahrens case ZFS_PROP_REFERENCED: 1187789Sahrens /* 1188789Sahrens * 'referenced' refers to the amount of physical space 1189789Sahrens * referenced (possibly shared) by this object. 1190789Sahrens */ 11912082Seschrock *val = zhp->zfs_dmustats.dds_space_refd; 11922082Seschrock break; 1193789Sahrens 1194789Sahrens case ZFS_PROP_SETUID: 11952082Seschrock *val = getprop_uint64(zhp, prop, source); 11962082Seschrock 11972082Seschrock if (hasmntopt(&mnt, MNTOPT_SETUID) && !*val) { 11982082Seschrock *val = B_TRUE; 1199789Sahrens if (src) 1200789Sahrens *src = ZFS_SRC_TEMPORARY; 12012082Seschrock } else if (hasmntopt(&mnt, MNTOPT_NOSETUID) && *val) { 12022082Seschrock *val = B_FALSE; 1203789Sahrens if (src) 1204789Sahrens *src = ZFS_SRC_TEMPORARY; 1205789Sahrens } 12062082Seschrock break; 1207789Sahrens 1208789Sahrens case ZFS_PROP_VOLSIZE: 12092082Seschrock *val = zhp->zfs_volsize; 12102082Seschrock break; 1211789Sahrens 1212789Sahrens case ZFS_PROP_VOLBLOCKSIZE: 12132082Seschrock *val = zhp->zfs_volblocksize; 12142082Seschrock break; 1215789Sahrens 1216789Sahrens case ZFS_PROP_USED: 12172082Seschrock *val = zhp->zfs_dmustats.dds_space_used; 12182082Seschrock break; 1219789Sahrens 1220789Sahrens case ZFS_PROP_CREATETXG: 12212082Seschrock *val = zhp->zfs_dmustats.dds_creation_txg; 12222082Seschrock break; 1223789Sahrens 1224789Sahrens case ZFS_PROP_MOUNTED: 1225789Sahrens /* 1226789Sahrens * Unlike other properties, we defer calculation of 'MOUNTED' 1227789Sahrens * until actually requested. This is because the getmntany() 1228789Sahrens * call can be extremely expensive on systems with a large 1229789Sahrens * number of filesystems, and the property isn't needed in 1230789Sahrens * normal use cases. 1231789Sahrens */ 1232789Sahrens if (zhp->zfs_mntopts == NULL) { 1233789Sahrens struct mnttab search = { 0 }, entry; 1234789Sahrens 1235789Sahrens search.mnt_special = (char *)zhp->zfs_name; 12361407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 12372082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 12382082Seschrock 12392082Seschrock if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, 12402082Seschrock &search) == 0 && (zhp->zfs_mntopts = 12412082Seschrock zfs_strdup(zhp->zfs_hdl, 12422082Seschrock entry.mnt_mntopts)) == NULL) 12432082Seschrock return (-1); 1244789Sahrens } 12452082Seschrock *val = (zhp->zfs_mntopts != NULL); 12462082Seschrock break; 1247789Sahrens 1248789Sahrens default: 12492082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 12502082Seschrock "cannot get non-numeric property")); 12512082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP, 12522082Seschrock dgettext(TEXT_DOMAIN, "internal error"))); 1253789Sahrens } 1254789Sahrens 1255789Sahrens return (0); 1256789Sahrens } 1257789Sahrens 1258789Sahrens /* 1259789Sahrens * Calculate the source type, given the raw source string. 1260789Sahrens */ 1261789Sahrens static void 1262789Sahrens get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source, 1263789Sahrens char *statbuf, size_t statlen) 1264789Sahrens { 1265789Sahrens if (statbuf == NULL || *srctype == ZFS_SRC_TEMPORARY) 1266789Sahrens return; 1267789Sahrens 1268789Sahrens if (source == NULL) { 1269789Sahrens *srctype = ZFS_SRC_NONE; 1270789Sahrens } else if (source[0] == '\0') { 1271789Sahrens *srctype = ZFS_SRC_DEFAULT; 1272789Sahrens } else { 1273789Sahrens if (strcmp(source, zhp->zfs_name) == 0) { 1274789Sahrens *srctype = ZFS_SRC_LOCAL; 1275789Sahrens } else { 1276789Sahrens (void) strlcpy(statbuf, source, statlen); 1277789Sahrens *srctype = ZFS_SRC_INHERITED; 1278789Sahrens } 1279789Sahrens } 1280789Sahrens 1281789Sahrens } 1282789Sahrens 1283789Sahrens /* 1284789Sahrens * Retrieve a property from the given object. If 'literal' is specified, then 1285789Sahrens * numbers are left as exact values. Otherwise, numbers are converted to a 1286789Sahrens * human-readable form. 1287789Sahrens * 1288789Sahrens * Returns 0 on success, or -1 on error. 1289789Sahrens */ 1290789Sahrens int 1291789Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 12922082Seschrock zfs_source_t *src, char *statbuf, size_t statlen, boolean_t literal) 1293789Sahrens { 1294789Sahrens char *source = NULL; 1295789Sahrens uint64_t val; 1296789Sahrens char *str; 1297789Sahrens int i; 1298789Sahrens const char *root; 1299789Sahrens 1300789Sahrens /* 1301789Sahrens * Check to see if this property applies to our object 1302789Sahrens */ 1303789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 1304789Sahrens return (-1); 1305789Sahrens 1306789Sahrens if (src) 1307789Sahrens *src = ZFS_SRC_NONE; 1308789Sahrens 1309789Sahrens switch (prop) { 1310789Sahrens case ZFS_PROP_ATIME: 1311789Sahrens case ZFS_PROP_READONLY: 1312789Sahrens case ZFS_PROP_SETUID: 1313789Sahrens case ZFS_PROP_ZONED: 1314789Sahrens case ZFS_PROP_DEVICES: 1315789Sahrens case ZFS_PROP_EXEC: 1316789Sahrens /* 1317789Sahrens * Basic boolean values are built on top of 1318789Sahrens * get_numeric_property(). 1319789Sahrens */ 13202082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 13212082Seschrock return (-1); 13222082Seschrock nicebool(val, propbuf, proplen); 1323789Sahrens 1324789Sahrens break; 1325789Sahrens 1326789Sahrens case ZFS_PROP_AVAILABLE: 1327789Sahrens case ZFS_PROP_RECORDSIZE: 1328789Sahrens case ZFS_PROP_CREATETXG: 1329789Sahrens case ZFS_PROP_REFERENCED: 1330789Sahrens case ZFS_PROP_USED: 1331789Sahrens case ZFS_PROP_VOLSIZE: 1332789Sahrens case ZFS_PROP_VOLBLOCKSIZE: 1333789Sahrens /* 1334789Sahrens * Basic numeric values are built on top of 1335789Sahrens * get_numeric_property(). 1336789Sahrens */ 13372082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 13382082Seschrock return (-1); 1339789Sahrens if (literal) 1340789Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1341789Sahrens else 1342789Sahrens zfs_nicenum(val, propbuf, proplen); 1343789Sahrens break; 1344789Sahrens 1345789Sahrens case ZFS_PROP_COMPRESSION: 13461356Seschrock val = getprop_uint64(zhp, prop, &source); 1347789Sahrens for (i = 0; compress_table[i].name != NULL; i++) { 13481356Seschrock if (compress_table[i].value == val) 1349789Sahrens break; 1350789Sahrens } 1351789Sahrens assert(compress_table[i].name != NULL); 1352789Sahrens (void) strlcpy(propbuf, compress_table[i].name, proplen); 1353789Sahrens break; 1354789Sahrens 1355789Sahrens case ZFS_PROP_CHECKSUM: 13561356Seschrock val = getprop_uint64(zhp, prop, &source); 1357789Sahrens for (i = 0; checksum_table[i].name != NULL; i++) { 13581356Seschrock if (checksum_table[i].value == val) 1359789Sahrens break; 1360789Sahrens } 1361789Sahrens assert(checksum_table[i].name != NULL); 1362789Sahrens (void) strlcpy(propbuf, checksum_table[i].name, proplen); 1363789Sahrens break; 1364789Sahrens 1365789Sahrens case ZFS_PROP_SNAPDIR: 13661356Seschrock val = getprop_uint64(zhp, prop, &source); 1367789Sahrens for (i = 0; snapdir_table[i].name != NULL; i++) { 13681356Seschrock if (snapdir_table[i].value == val) 1369789Sahrens break; 1370789Sahrens } 1371789Sahrens assert(snapdir_table[i].name != NULL); 1372789Sahrens (void) strlcpy(propbuf, snapdir_table[i].name, proplen); 1373789Sahrens break; 1374789Sahrens 1375789Sahrens case ZFS_PROP_ACLMODE: 13761356Seschrock val = getprop_uint64(zhp, prop, &source); 1377789Sahrens for (i = 0; acl_mode_table[i].name != NULL; i++) { 13781356Seschrock if (acl_mode_table[i].value == val) 1379789Sahrens break; 1380789Sahrens } 1381789Sahrens assert(acl_mode_table[i].name != NULL); 1382789Sahrens (void) strlcpy(propbuf, acl_mode_table[i].name, proplen); 1383789Sahrens break; 1384789Sahrens 1385789Sahrens case ZFS_PROP_ACLINHERIT: 13861356Seschrock val = getprop_uint64(zhp, prop, &source); 1387789Sahrens for (i = 0; acl_inherit_table[i].name != NULL; i++) { 13881356Seschrock if (acl_inherit_table[i].value == val) 1389789Sahrens break; 1390789Sahrens } 1391789Sahrens assert(acl_inherit_table[i].name != NULL); 1392789Sahrens (void) strlcpy(propbuf, acl_inherit_table[i].name, proplen); 1393789Sahrens break; 1394789Sahrens 1395789Sahrens case ZFS_PROP_CREATION: 1396789Sahrens /* 1397789Sahrens * 'creation' is a time_t stored in the statistics. We convert 1398789Sahrens * this into a string unless 'literal' is specified. 1399789Sahrens */ 1400789Sahrens { 1401789Sahrens time_t time = (time_t) 1402789Sahrens zhp->zfs_dmustats.dds_creation_time; 1403789Sahrens struct tm t; 1404789Sahrens 1405789Sahrens if (literal || 1406789Sahrens localtime_r(&time, &t) == NULL || 1407789Sahrens strftime(propbuf, proplen, "%a %b %e %k:%M %Y", 1408789Sahrens &t) == 0) 1409789Sahrens (void) snprintf(propbuf, proplen, "%llu", 1410789Sahrens zhp->zfs_dmustats.dds_creation_time); 1411789Sahrens } 1412789Sahrens break; 1413789Sahrens 1414789Sahrens case ZFS_PROP_MOUNTPOINT: 1415789Sahrens /* 1416789Sahrens * Getting the precise mountpoint can be tricky. 1417789Sahrens * 1418789Sahrens * - for 'none' or 'legacy', return those values. 1419789Sahrens * - for default mountpoints, construct it as /zfs/<dataset> 1420789Sahrens * - for inherited mountpoints, we want to take everything 1421789Sahrens * after our ancestor and append it to the inherited value. 1422789Sahrens * 1423789Sahrens * If the pool has an alternate root, we want to prepend that 1424789Sahrens * root to any values we return. 1425789Sahrens */ 14261544Seschrock root = zhp->zfs_root; 14271356Seschrock str = getprop_string(zhp, prop, &source); 14281356Seschrock 14291356Seschrock if (str[0] == '\0') { 1430789Sahrens (void) snprintf(propbuf, proplen, "%s/zfs/%s", 1431789Sahrens root, zhp->zfs_name); 14321356Seschrock } else if (str[0] == '/') { 14331356Seschrock const char *relpath = zhp->zfs_name + strlen(source); 1434789Sahrens 1435789Sahrens if (relpath[0] == '/') 1436789Sahrens relpath++; 14371356Seschrock if (str[1] == '\0') 14381356Seschrock str++; 1439789Sahrens 1440789Sahrens if (relpath[0] == '\0') 1441789Sahrens (void) snprintf(propbuf, proplen, "%s%s", 14421356Seschrock root, str); 1443789Sahrens else 1444789Sahrens (void) snprintf(propbuf, proplen, "%s%s%s%s", 14451356Seschrock root, str, relpath[0] == '@' ? "" : "/", 1446789Sahrens relpath); 1447789Sahrens } else { 1448789Sahrens /* 'legacy' or 'none' */ 14491356Seschrock (void) strlcpy(propbuf, str, proplen); 1450789Sahrens } 1451789Sahrens 1452789Sahrens break; 1453789Sahrens 1454789Sahrens case ZFS_PROP_SHARENFS: 14551356Seschrock (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), 14561356Seschrock proplen); 1457789Sahrens break; 1458789Sahrens 1459789Sahrens case ZFS_PROP_ORIGIN: 14601544Seschrock (void) strlcpy(propbuf, zhp->zfs_dmustats.dds_clone_of, 1461789Sahrens proplen); 1462789Sahrens /* 1463789Sahrens * If there is no parent at all, return failure to indicate that 1464789Sahrens * it doesn't apply to this dataset. 1465789Sahrens */ 1466789Sahrens if (propbuf[0] == '\0') 1467789Sahrens return (-1); 1468789Sahrens break; 1469789Sahrens 1470789Sahrens case ZFS_PROP_QUOTA: 1471789Sahrens case ZFS_PROP_RESERVATION: 14722082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 14732082Seschrock return (-1); 1474789Sahrens 1475789Sahrens /* 1476789Sahrens * If quota or reservation is 0, we translate this into 'none' 1477789Sahrens * (unless literal is set), and indicate that it's the default 1478789Sahrens * value. Otherwise, we print the number nicely and indicate 1479789Sahrens * that its set locally. 1480789Sahrens */ 1481789Sahrens if (val == 0) { 1482789Sahrens if (literal) 1483789Sahrens (void) strlcpy(propbuf, "0", proplen); 1484789Sahrens else 1485789Sahrens (void) strlcpy(propbuf, "none", proplen); 1486789Sahrens } else { 1487789Sahrens if (literal) 1488789Sahrens (void) snprintf(propbuf, proplen, "%llu", val); 1489789Sahrens else 1490789Sahrens zfs_nicenum(val, propbuf, proplen); 1491789Sahrens } 1492789Sahrens break; 1493789Sahrens 1494789Sahrens case ZFS_PROP_COMPRESSRATIO: 14952082Seschrock if (get_numeric_property(zhp, prop, src, &source, &val) != 0) 14962082Seschrock return (-1); 1497789Sahrens (void) snprintf(propbuf, proplen, "%lld.%02lldx", val / 100, 1498789Sahrens val % 100); 1499789Sahrens break; 1500789Sahrens 1501789Sahrens case ZFS_PROP_TYPE: 1502789Sahrens switch (zhp->zfs_type) { 1503789Sahrens case ZFS_TYPE_FILESYSTEM: 1504789Sahrens str = "filesystem"; 1505789Sahrens break; 1506789Sahrens case ZFS_TYPE_VOLUME: 1507789Sahrens str = "volume"; 1508789Sahrens break; 1509789Sahrens case ZFS_TYPE_SNAPSHOT: 1510789Sahrens str = "snapshot"; 1511789Sahrens break; 1512789Sahrens default: 15132082Seschrock abort(); 1514789Sahrens } 1515789Sahrens (void) snprintf(propbuf, proplen, "%s", str); 1516789Sahrens break; 1517789Sahrens 1518789Sahrens case ZFS_PROP_MOUNTED: 1519789Sahrens /* 1520789Sahrens * The 'mounted' property is a pseudo-property that described 1521789Sahrens * whether the filesystem is currently mounted. Even though 1522789Sahrens * it's a boolean value, the typical values of "on" and "off" 1523789Sahrens * don't make sense, so we translate to "yes" and "no". 1524789Sahrens */ 15252082Seschrock if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, 15262082Seschrock src, &source, &val) != 0) 15272082Seschrock return (-1); 15282082Seschrock if (val) 1529789Sahrens (void) strlcpy(propbuf, "yes", proplen); 1530789Sahrens else 1531789Sahrens (void) strlcpy(propbuf, "no", proplen); 1532789Sahrens break; 1533789Sahrens 1534789Sahrens case ZFS_PROP_NAME: 1535789Sahrens /* 1536789Sahrens * The 'name' property is a pseudo-property derived from the 1537789Sahrens * dataset name. It is presented as a real property to simplify 1538789Sahrens * consumers. 1539789Sahrens */ 1540789Sahrens (void) strlcpy(propbuf, zhp->zfs_name, proplen); 1541789Sahrens break; 1542789Sahrens 1543789Sahrens default: 15442082Seschrock abort(); 1545789Sahrens } 1546789Sahrens 1547789Sahrens get_source(zhp, src, source, statbuf, statlen); 1548789Sahrens 1549789Sahrens return (0); 1550789Sahrens } 1551789Sahrens 1552789Sahrens /* 1553789Sahrens * Utility function to get the given numeric property. Does no validation that 1554789Sahrens * the given property is the appropriate type; should only be used with 1555789Sahrens * hard-coded property types. 1556789Sahrens */ 1557789Sahrens uint64_t 1558789Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) 1559789Sahrens { 1560789Sahrens char *source; 1561789Sahrens zfs_source_t sourcetype = ZFS_SRC_NONE; 15622082Seschrock uint64_t val; 15632082Seschrock 15642082Seschrock (void) get_numeric_property(zhp, prop, &sourcetype, &source, &val); 15652082Seschrock 15662082Seschrock return (val); 1567789Sahrens } 1568789Sahrens 1569789Sahrens /* 1570789Sahrens * Similar to zfs_prop_get(), but returns the value as an integer. 1571789Sahrens */ 1572789Sahrens int 1573789Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, 1574789Sahrens zfs_source_t *src, char *statbuf, size_t statlen) 1575789Sahrens { 1576789Sahrens char *source; 1577789Sahrens 1578789Sahrens /* 1579789Sahrens * Check to see if this property applies to our object 1580789Sahrens */ 1581789Sahrens if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) 15822082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_PROPTYPE, 15832082Seschrock dgettext(TEXT_DOMAIN, "cannot get property '%s'"), 15842082Seschrock zfs_prop_to_name(prop))); 1585789Sahrens 1586789Sahrens if (src) 1587789Sahrens *src = ZFS_SRC_NONE; 1588789Sahrens 15892082Seschrock if (get_numeric_property(zhp, prop, src, &source, value) != 0) 15902082Seschrock return (-1); 1591789Sahrens 1592789Sahrens get_source(zhp, src, source, statbuf, statlen); 1593789Sahrens 1594789Sahrens return (0); 1595789Sahrens } 1596789Sahrens 1597789Sahrens /* 1598789Sahrens * Returns the name of the given zfs handle. 1599789Sahrens */ 1600789Sahrens const char * 1601789Sahrens zfs_get_name(const zfs_handle_t *zhp) 1602789Sahrens { 1603789Sahrens return (zhp->zfs_name); 1604789Sahrens } 1605789Sahrens 1606789Sahrens /* 1607789Sahrens * Returns the type of the given zfs handle. 1608789Sahrens */ 1609789Sahrens zfs_type_t 1610789Sahrens zfs_get_type(const zfs_handle_t *zhp) 1611789Sahrens { 1612789Sahrens return (zhp->zfs_type); 1613789Sahrens } 1614789Sahrens 1615789Sahrens /* 16161356Seschrock * Iterate over all child filesystems 1617789Sahrens */ 1618789Sahrens int 16191356Seschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 1620789Sahrens { 1621789Sahrens zfs_cmd_t zc = { 0 }; 1622789Sahrens zfs_handle_t *nzhp; 1623789Sahrens int ret; 1624789Sahrens 1625789Sahrens for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 16262082Seschrock ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; 1627789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { 1628789Sahrens /* 1629789Sahrens * Ignore private dataset names. 1630789Sahrens */ 1631789Sahrens if (dataset_name_hidden(zc.zc_name)) 1632789Sahrens continue; 1633789Sahrens 1634789Sahrens /* 1635789Sahrens * Silently ignore errors, as the only plausible explanation is 1636789Sahrens * that the pool has since been removed. 1637789Sahrens */ 16382082Seschrock if ((nzhp = make_dataset_handle(zhp->zfs_hdl, 16392082Seschrock zc.zc_name)) == NULL) 1640789Sahrens continue; 1641789Sahrens 1642789Sahrens if ((ret = func(nzhp, data)) != 0) 1643789Sahrens return (ret); 1644789Sahrens } 1645789Sahrens 1646789Sahrens /* 1647789Sahrens * An errno value of ESRCH indicates normal completion. If ENOENT is 1648789Sahrens * returned, then the underlying dataset has been removed since we 1649789Sahrens * obtained the handle. 1650789Sahrens */ 1651789Sahrens if (errno != ESRCH && errno != ENOENT) 16522082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 16532082Seschrock dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); 1654789Sahrens 16551356Seschrock return (0); 16561356Seschrock } 16571356Seschrock 16581356Seschrock /* 16591356Seschrock * Iterate over all snapshots 16601356Seschrock */ 16611356Seschrock int 16621356Seschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 16631356Seschrock { 16641356Seschrock zfs_cmd_t zc = { 0 }; 16651356Seschrock zfs_handle_t *nzhp; 16661356Seschrock int ret; 1667789Sahrens 1668789Sahrens for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 16692082Seschrock ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, 16702082Seschrock &zc) == 0; 1671789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { 1672789Sahrens 16732082Seschrock if ((nzhp = make_dataset_handle(zhp->zfs_hdl, 16742082Seschrock zc.zc_name)) == NULL) 1675789Sahrens continue; 1676789Sahrens 1677789Sahrens if ((ret = func(nzhp, data)) != 0) 1678789Sahrens return (ret); 1679789Sahrens } 1680789Sahrens 1681789Sahrens /* 1682789Sahrens * An errno value of ESRCH indicates normal completion. If ENOENT is 1683789Sahrens * returned, then the underlying dataset has been removed since we 1684789Sahrens * obtained the handle. Silently ignore this case, and return success. 1685789Sahrens */ 1686789Sahrens if (errno != ESRCH && errno != ENOENT) 16872082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 16882082Seschrock dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); 1689789Sahrens 1690789Sahrens return (0); 1691789Sahrens } 1692789Sahrens 1693789Sahrens /* 16941356Seschrock * Iterate over all children, snapshots and filesystems 16951356Seschrock */ 16961356Seschrock int 16971356Seschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 16981356Seschrock { 16991356Seschrock int ret; 17001356Seschrock 17011356Seschrock if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 17021356Seschrock return (ret); 17031356Seschrock 17041356Seschrock return (zfs_iter_snapshots(zhp, func, data)); 17051356Seschrock } 17061356Seschrock 17071356Seschrock /* 1708789Sahrens * Given a complete name, return just the portion that refers to the parent. 1709789Sahrens * Can return NULL if this is a pool. 1710789Sahrens */ 1711789Sahrens static int 1712789Sahrens parent_name(const char *path, char *buf, size_t buflen) 1713789Sahrens { 1714789Sahrens char *loc; 1715789Sahrens 1716789Sahrens if ((loc = strrchr(path, '/')) == NULL) 1717789Sahrens return (-1); 1718789Sahrens 1719789Sahrens (void) strncpy(buf, path, MIN(buflen, loc - path)); 1720789Sahrens buf[loc - path] = '\0'; 1721789Sahrens 1722789Sahrens return (0); 1723789Sahrens } 1724789Sahrens 1725789Sahrens /* 1726789Sahrens * Checks to make sure that the given path has a parent, and that it exists. 1727789Sahrens */ 1728789Sahrens static int 17292082Seschrock check_parents(libzfs_handle_t *hdl, const char *path) 1730789Sahrens { 1731789Sahrens zfs_cmd_t zc = { 0 }; 1732789Sahrens char parent[ZFS_MAXNAMELEN]; 1733789Sahrens char *slash; 17341356Seschrock zfs_handle_t *zhp; 17352082Seschrock char errbuf[1024]; 17362082Seschrock 17372082Seschrock (void) snprintf(errbuf, sizeof (errbuf), "cannot create '%s'", 17382082Seschrock path); 1739789Sahrens 1740789Sahrens /* get parent, and check to see if this is just a pool */ 1741789Sahrens if (parent_name(path, parent, sizeof (parent)) != 0) { 17422082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 17432082Seschrock "missing dataset name")); 17442082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 1745789Sahrens } 1746789Sahrens 1747789Sahrens /* check to see if the pool exists */ 1748789Sahrens if ((slash = strchr(parent, '/')) == NULL) 1749789Sahrens slash = parent + strlen(parent); 1750789Sahrens (void) strncpy(zc.zc_name, parent, slash - parent); 1751789Sahrens zc.zc_name[slash - parent] = '\0'; 17522082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && 1753789Sahrens errno == ENOENT) { 17542082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 17552082Seschrock "no such pool '%s'"), zc.zc_name); 17562082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 1757789Sahrens } 1758789Sahrens 1759789Sahrens /* check to see if the parent dataset exists */ 17602082Seschrock if ((zhp = make_dataset_handle(hdl, parent)) == NULL) { 1761789Sahrens switch (errno) { 1762789Sahrens case ENOENT: 17632082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 17642082Seschrock "parent does not exist")); 17652082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 1766789Sahrens 1767789Sahrens default: 17682082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1769789Sahrens } 1770789Sahrens } 1771789Sahrens 1772789Sahrens /* we are in a non-global zone, but parent is in the global zone */ 17731356Seschrock if (getzoneid() != GLOBAL_ZONEID && 17741393Slling !zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 17752082Seschrock (void) zfs_standard_error(hdl, EPERM, errbuf); 17761356Seschrock zfs_close(zhp); 1777789Sahrens return (-1); 1778789Sahrens } 1779789Sahrens 1780789Sahrens /* make sure parent is a filesystem */ 17811356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 17822082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 17832082Seschrock "parent is not a filesystem")); 17842082Seschrock (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); 17851356Seschrock zfs_close(zhp); 1786789Sahrens return (-1); 1787789Sahrens } 1788789Sahrens 17891356Seschrock zfs_close(zhp); 1790789Sahrens return (0); 1791789Sahrens } 1792789Sahrens 1793789Sahrens /* 1794789Sahrens * Create a new filesystem or volume. 'sizestr' and 'blocksizestr' are used 1795789Sahrens * only for volumes, and indicate the size and blocksize of the volume. 1796789Sahrens */ 1797789Sahrens int 17982082Seschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, 1799789Sahrens const char *sizestr, const char *blocksizestr) 1800789Sahrens { 1801789Sahrens zfs_cmd_t zc = { 0 }; 1802789Sahrens int ret; 1803789Sahrens uint64_t size = 0; 1804789Sahrens uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); 18052082Seschrock char errbuf[1024]; 1806789Sahrens 1807789Sahrens /* convert sizestr into integer size */ 18082082Seschrock if (sizestr != NULL && nicestrtonum(hdl, sizestr, &size) != 0) 18092082Seschrock return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, 18102082Seschrock "bad volume size '%s'"), sizestr)); 1811789Sahrens 1812789Sahrens /* convert blocksizestr into integer blocksize */ 18132082Seschrock if (blocksizestr != NULL && nicestrtonum(hdl, blocksizestr, 18142082Seschrock &blocksize) != 0) 18152082Seschrock return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, 18162082Seschrock "bad volume blocksize '%s'"), blocksizestr)); 18172082Seschrock 18182082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 18192082Seschrock "cannot create '%s'"), path); 1820789Sahrens 1821789Sahrens /* validate the path, taking care to note the extended error message */ 18222082Seschrock if (!zfs_validate_name(hdl, path, type)) 18232082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 1824789Sahrens 1825789Sahrens /* validate parents exist */ 18262082Seschrock if (check_parents(hdl, path) != 0) 1827789Sahrens return (-1); 1828789Sahrens 1829789Sahrens /* 1830789Sahrens * The failure modes when creating a dataset of a different type over 1831789Sahrens * one that already exists is a little strange. In particular, if you 1832789Sahrens * try to create a dataset on top of an existing dataset, the ioctl() 1833789Sahrens * will return ENOENT, not EEXIST. To prevent this from happening, we 1834789Sahrens * first try to see if the dataset exists. 1835789Sahrens */ 1836789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 18372082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) { 18382082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 18392082Seschrock "dataset already exists")); 18402082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 1841789Sahrens } 1842789Sahrens 1843789Sahrens if (type == ZFS_TYPE_VOLUME) 1844789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 1845789Sahrens else 1846789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 1847789Sahrens 1848789Sahrens if (type == ZFS_TYPE_VOLUME) { 18491133Seschrock /* 18501133Seschrock * If we are creating a volume, the size and block size must 18511133Seschrock * satisfy a few restraints. First, the blocksize must be a 18521133Seschrock * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the 18531133Seschrock * volsize must be a multiple of the block size, and cannot be 18541133Seschrock * zero. 18551133Seschrock */ 1856789Sahrens if (size == 0) { 18572082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 18582082Seschrock "cannot be zero")); 18592082Seschrock return (zfs_error(hdl, EZFS_BADPROP, 18602082Seschrock dgettext(TEXT_DOMAIN, "bad volume size '%s'"), 18612082Seschrock sizestr)); 1862789Sahrens } 1863789Sahrens 18641133Seschrock if (blocksize < SPA_MINBLOCKSIZE || 18651133Seschrock blocksize > SPA_MAXBLOCKSIZE || !ISP2(blocksize)) { 18662082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 18671133Seschrock "must be power of 2 from %u to %uk"), 18681133Seschrock (uint_t)SPA_MINBLOCKSIZE, 18691133Seschrock (uint_t)SPA_MAXBLOCKSIZE >> 10); 18702082Seschrock return (zfs_error(hdl, EZFS_BADPROP, 18712082Seschrock dgettext(TEXT_DOMAIN, 18722082Seschrock "bad volume block size '%s'"), blocksizestr)); 18731133Seschrock } 18741133Seschrock 18751133Seschrock if (size % blocksize != 0) { 18762082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 18772082Seschrock "must be a multiple of volume block size")); 18782082Seschrock return (zfs_error(hdl, EZFS_BADPROP, 18792082Seschrock dgettext(TEXT_DOMAIN, "bad volume size '%s'"), 18802082Seschrock sizestr)); 18811133Seschrock } 18821133Seschrock 1883789Sahrens zc.zc_volsize = size; 1884789Sahrens zc.zc_volblocksize = blocksize; 1885789Sahrens } 1886789Sahrens 1887789Sahrens /* create the dataset */ 18882082Seschrock ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); 1889789Sahrens 1890789Sahrens if (ret == 0 && type == ZFS_TYPE_VOLUME) 18912082Seschrock ret = zvol_create_link(hdl, path); 1892789Sahrens 1893789Sahrens /* check for failure */ 1894789Sahrens if (ret != 0) { 1895789Sahrens char parent[ZFS_MAXNAMELEN]; 1896789Sahrens (void) parent_name(path, parent, sizeof (parent)); 1897789Sahrens 1898789Sahrens switch (errno) { 1899789Sahrens case ENOENT: 19002082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19012082Seschrock "no such parent '%s'"), parent); 19022082Seschrock return (zfs_error(hdl, EZFS_NOENT, errbuf)); 1903789Sahrens 1904789Sahrens case EINVAL: 19052082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 19062082Seschrock "parent '%s' is not a filesysem"), parent); 19072082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 1908789Sahrens 1909789Sahrens case EDOM: 19102082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1911789Sahrens "must be power of 2 from %u to %uk"), 1912789Sahrens (uint_t)SPA_MINBLOCKSIZE, 1913789Sahrens (uint_t)SPA_MAXBLOCKSIZE >> 10); 19142082Seschrock 19152082Seschrock return (zfs_error(hdl, EZFS_BADPROP, 19162082Seschrock dgettext(TEXT_DOMAIN, "bad block size '%s'"), 19172082Seschrock blocksizestr ? blocksizestr : "<unknown>")); 19182082Seschrock 1919789Sahrens #ifdef _ILP32 1920789Sahrens case EOVERFLOW: 1921789Sahrens /* 1922789Sahrens * This platform can't address a volume this big. 1923789Sahrens */ 19242082Seschrock if (type == ZFS_TYPE_VOLUME) 19252082Seschrock return (zfs_error(hdl, EZFS_VOLTOOBIG, 19262082Seschrock errbuf)); 1927789Sahrens #endif 19282082Seschrock /* FALLTHROUGH */ 1929789Sahrens default: 19302082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 1931789Sahrens } 1932789Sahrens } 1933789Sahrens 1934789Sahrens return (0); 1935789Sahrens } 1936789Sahrens 1937789Sahrens /* 1938789Sahrens * Destroys the given dataset. The caller must make sure that the filesystem 1939789Sahrens * isn't mounted, and that there are no active dependents. 1940789Sahrens */ 1941789Sahrens int 1942789Sahrens zfs_destroy(zfs_handle_t *zhp) 1943789Sahrens { 1944789Sahrens zfs_cmd_t zc = { 0 }; 1945789Sahrens int ret; 19462082Seschrock char errbuf[1024]; 1947789Sahrens 1948789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 1949789Sahrens 1950789Sahrens /* 1951789Sahrens * We use the check for 'zfs_volblocksize' instead of ZFS_TYPE_VOLUME 1952789Sahrens * so that we do the right thing for snapshots of volumes. 1953789Sahrens */ 1954789Sahrens if (zhp->zfs_volblocksize != 0) { 19552082Seschrock if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 1956789Sahrens return (-1); 1957789Sahrens 1958789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 1959789Sahrens } else { 1960789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 1961789Sahrens } 1962789Sahrens 19632082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc); 19642082Seschrock 19652082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 19662082Seschrock "cannot destroy '%s'"), zhp->zfs_name); 19672082Seschrock 19682082Seschrock if (ret != 0) 19692082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 19702082Seschrock dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), 19712082Seschrock zhp->zfs_name)); 1972789Sahrens 1973789Sahrens remove_mountpoint(zhp); 1974789Sahrens 1975789Sahrens return (0); 1976789Sahrens } 1977789Sahrens 1978789Sahrens /* 1979789Sahrens * Clones the given dataset. The target must be of the same type as the source. 1980789Sahrens */ 1981789Sahrens int 1982789Sahrens zfs_clone(zfs_handle_t *zhp, const char *target) 1983789Sahrens { 1984789Sahrens zfs_cmd_t zc = { 0 }; 1985789Sahrens char parent[ZFS_MAXNAMELEN]; 1986789Sahrens int ret; 19872082Seschrock char errbuf[1024]; 19882082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 1989789Sahrens 1990789Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 1991789Sahrens 19922082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 19932082Seschrock "cannot create '%s'"), target); 19942082Seschrock 1995789Sahrens /* validate the target name */ 19962082Seschrock if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM)) 19972082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 1998789Sahrens 1999789Sahrens /* validate parents exist */ 20002082Seschrock if (check_parents(zhp->zfs_hdl, target) != 0) 2001789Sahrens return (-1); 2002789Sahrens 2003789Sahrens (void) parent_name(target, parent, sizeof (parent)); 2004789Sahrens 2005789Sahrens /* do the clone */ 2006789Sahrens if (zhp->zfs_volblocksize != 0) 2007789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2008789Sahrens else 2009789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2010789Sahrens 2011789Sahrens (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); 2012789Sahrens (void) strlcpy(zc.zc_filename, zhp->zfs_name, sizeof (zc.zc_filename)); 20132082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); 2014789Sahrens 2015789Sahrens if (ret != 0) { 2016789Sahrens switch (errno) { 2017789Sahrens 2018789Sahrens case ENOENT: 2019789Sahrens /* 2020789Sahrens * The parent doesn't exist. We should have caught this 2021789Sahrens * above, but there may a race condition that has since 2022789Sahrens * destroyed the parent. 2023789Sahrens * 2024789Sahrens * At this point, we don't know whether it's the source 2025789Sahrens * that doesn't exist anymore, or whether the target 2026789Sahrens * dataset doesn't exist. 2027789Sahrens */ 20282082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 20292082Seschrock "no such parent '%s'"), parent); 20302082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 20312082Seschrock 20322082Seschrock case EXDEV: 20332082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 20342082Seschrock "source and target pools differ")); 20352082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, 20362082Seschrock errbuf)); 20372082Seschrock 20382082Seschrock default: 20392082Seschrock return (zfs_standard_error(zhp->zfs_hdl, errno, 20402082Seschrock errbuf)); 20412082Seschrock } 20422082Seschrock } else if (zhp->zfs_volblocksize != 0) { 20432082Seschrock ret = zvol_create_link(zhp->zfs_hdl, target); 20442082Seschrock } 20452082Seschrock 20462082Seschrock return (ret); 20472082Seschrock } 20482082Seschrock 20492082Seschrock typedef struct promote_data { 20502082Seschrock char cb_mountpoint[MAXPATHLEN]; 20512082Seschrock const char *cb_target; 20522082Seschrock const char *cb_errbuf; 20532082Seschrock uint64_t cb_pivot_txg; 20542082Seschrock } promote_data_t; 20552082Seschrock 20562082Seschrock static int 20572082Seschrock promote_snap_cb(zfs_handle_t *zhp, void *data) 20582082Seschrock { 20592082Seschrock promote_data_t *pd = data; 20602082Seschrock zfs_handle_t *szhp; 20612082Seschrock int err; 20622082Seschrock char snapname[MAXPATHLEN]; 20632082Seschrock char *cp; 20642082Seschrock 20652082Seschrock /* We don't care about snapshots after the pivot point */ 20662082Seschrock if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) 20672082Seschrock return (0); 20682082Seschrock 20692082Seschrock /* 20702082Seschrock * Unmount it. We actually need to open it to provoke it to be 20712082Seschrock * mounted first, because if it is not mounted, umount2 will 20722082Seschrock * mount it! 20732082Seschrock */ 20742082Seschrock (void) strcpy(snapname, pd->cb_mountpoint); 20752082Seschrock (void) strcat(snapname, "/.zfs/snapshot/"); 20762082Seschrock cp = strchr(zhp->zfs_name, '@'); 20772082Seschrock (void) strcat(snapname, cp+1); 20782082Seschrock err = open(snapname, O_RDONLY); 20792082Seschrock if (err != -1) 20802082Seschrock (void) close(err); 20812082Seschrock (void) umount2(snapname, MS_FORCE); 20822082Seschrock 20832082Seschrock /* Check for conflicting names */ 20842082Seschrock (void) strcpy(snapname, pd->cb_target); 20852082Seschrock (void) strcat(snapname, cp); 20862082Seschrock szhp = make_dataset_handle(zhp->zfs_hdl, snapname); 20872082Seschrock if (szhp != NULL) { 20882082Seschrock zfs_close(szhp); 20892082Seschrock zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 20902082Seschrock "snapshot name '%s' from origin \n" 20912082Seschrock "conflicts with '%s' from target"), 20922082Seschrock zhp->zfs_name, snapname); 20932082Seschrock return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf)); 20942082Seschrock } 20952082Seschrock return (0); 20962082Seschrock } 20972082Seschrock 20982082Seschrock /* 20992082Seschrock * Promotes the given clone fs to be the clone parent. 21002082Seschrock */ 21012082Seschrock int 21022082Seschrock zfs_promote(zfs_handle_t *zhp) 21032082Seschrock { 21042082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 21052082Seschrock zfs_cmd_t zc = { 0 }; 21062082Seschrock char parent[MAXPATHLEN]; 21072082Seschrock char *cp; 21082082Seschrock int ret; 21092082Seschrock zfs_handle_t *pzhp; 21102082Seschrock promote_data_t pd; 21112082Seschrock char errbuf[1024]; 21122082Seschrock 21132082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 21142082Seschrock "cannot promote '%s'"), zhp->zfs_name); 21152082Seschrock 21162082Seschrock if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 21172082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 21182082Seschrock "snapshots can not be promoted")); 21192082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 21202082Seschrock } 21212082Seschrock 21222082Seschrock (void) strcpy(parent, zhp->zfs_dmustats.dds_clone_of); 21232082Seschrock if (parent[0] == '\0') { 21242082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 21252082Seschrock "not a cloned filesystem")); 21262082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 21272082Seschrock } 21282082Seschrock cp = strchr(parent, '@'); 21292082Seschrock *cp = '\0'; 21302082Seschrock 21312082Seschrock /* Walk the snapshots we will be moving */ 21322082Seschrock pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_clone_of, ZFS_TYPE_SNAPSHOT); 21332082Seschrock if (pzhp == NULL) 21342082Seschrock return (-1); 21352082Seschrock pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); 21362082Seschrock zfs_close(pzhp); 21372082Seschrock pd.cb_target = zhp->zfs_name; 21382082Seschrock pd.cb_errbuf = errbuf; 21392082Seschrock pzhp = zfs_open(hdl, parent, ZFS_TYPE_ANY); 21402082Seschrock if (pzhp == NULL) 21412082Seschrock return (-1); 21422082Seschrock (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, 21432082Seschrock sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); 21442082Seschrock ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); 21452082Seschrock if (ret != 0) 21462082Seschrock return (-1); 21472082Seschrock 21482082Seschrock /* issue the ioctl */ 21492082Seschrock (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 21502082Seschrock ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc); 21512082Seschrock 21522082Seschrock if (ret != 0) { 21532082Seschrock switch (errno) { 2154789Sahrens 2155789Sahrens case EEXIST: 2156789Sahrens /* 21572082Seschrock * There is a conflicting snapshot name. We 21582082Seschrock * should have caught this above, but they could 21592082Seschrock * have renamed something in the mean time. 2160789Sahrens */ 21612082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 21622082Seschrock "conflicting snapshot name from parent '%s'"), 21632082Seschrock parent); 21642082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2165789Sahrens 2166789Sahrens default: 21672082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2168789Sahrens } 2169789Sahrens } 2170789Sahrens 2171789Sahrens return (ret); 2172789Sahrens } 2173789Sahrens 2174789Sahrens /* 2175789Sahrens * Takes a snapshot of the given dataset 2176789Sahrens */ 2177789Sahrens int 21782082Seschrock zfs_snapshot(libzfs_handle_t *hdl, const char *path) 2179789Sahrens { 2180789Sahrens const char *delim; 2181789Sahrens char *parent; 2182789Sahrens zfs_handle_t *zhp; 2183789Sahrens zfs_cmd_t zc = { 0 }; 2184789Sahrens int ret; 21852082Seschrock char errbuf[1024]; 21862082Seschrock 21872082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 21882082Seschrock "cannot snapshot '%s'"), path); 21892082Seschrock 21902082Seschrock /* validate the target name */ 21912082Seschrock if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT)) 21922082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2193789Sahrens 2194789Sahrens /* make sure we have a snapshot */ 2195789Sahrens if ((delim = strchr(path, '@')) == NULL) { 21962082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 21972082Seschrock "missing '@' delimeter in snapshot name")); 21982082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2199789Sahrens } 2200789Sahrens 2201789Sahrens /* make sure the parent exists and is of the appropriate type */ 22022082Seschrock if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL) 22032082Seschrock return (-1); 2204789Sahrens (void) strncpy(parent, path, delim - path); 2205789Sahrens parent[delim - path] = '\0'; 2206789Sahrens 22072082Seschrock if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | 2208789Sahrens ZFS_TYPE_VOLUME)) == NULL) { 2209789Sahrens free(parent); 2210789Sahrens return (-1); 2211789Sahrens } 2212789Sahrens 2213789Sahrens (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); 2214789Sahrens 2215789Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME) 2216789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2217789Sahrens else 2218789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2219789Sahrens 22202082Seschrock ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); 2221789Sahrens 2222789Sahrens if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { 22232082Seschrock ret = zvol_create_link(zhp->zfs_hdl, path); 2224789Sahrens if (ret != 0) 22252082Seschrock (void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, 22262082Seschrock &zc); 2227789Sahrens } 2228789Sahrens 22292082Seschrock if (ret != 0) 22302082Seschrock (void) zfs_standard_error(hdl, errno, errbuf); 2231789Sahrens 2232789Sahrens free(parent); 2233789Sahrens zfs_close(zhp); 2234789Sahrens 2235789Sahrens return (ret); 2236789Sahrens } 2237789Sahrens 2238789Sahrens /* 2239789Sahrens * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL. 2240789Sahrens */ 2241789Sahrens int 22421749Sahrens zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from) 2243789Sahrens { 2244789Sahrens zfs_cmd_t zc = { 0 }; 2245789Sahrens int ret; 22462082Seschrock char errbuf[1024]; 22472082Seschrock libzfs_handle_t *hdl = zhp_to->zfs_hdl; 22482082Seschrock 22492082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 22502082Seschrock "cannot send '%s'"), zhp_to->zfs_name); 2251789Sahrens 2252789Sahrens /* do the ioctl() */ 2253789Sahrens (void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name)); 2254789Sahrens if (zhp_from) { 2255789Sahrens (void) strlcpy(zc.zc_prop_value, zhp_from->zfs_name, 2256789Sahrens sizeof (zc.zc_name)); 2257789Sahrens } else { 2258789Sahrens zc.zc_prop_value[0] = '\0'; 2259789Sahrens } 2260789Sahrens zc.zc_cookie = STDOUT_FILENO; 2261789Sahrens 22622082Seschrock ret = ioctl(zhp_to->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc); 2263789Sahrens if (ret != 0) { 2264789Sahrens switch (errno) { 2265789Sahrens 2266789Sahrens case EXDEV: 22672082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 22682082Seschrock "not an ealier snapshot from the same fs")); 22692082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 2270789Sahrens 2271789Sahrens case EDQUOT: 2272789Sahrens case EFBIG: 2273789Sahrens case EIO: 2274789Sahrens case ENOLINK: 2275789Sahrens case ENOSPC: 2276789Sahrens case ENOSTR: 2277789Sahrens case ENXIO: 2278789Sahrens case EPIPE: 2279789Sahrens case ERANGE: 2280789Sahrens case EFAULT: 2281789Sahrens case EROFS: 22822082Seschrock zfs_error_aux(hdl, strerror(errno)); 22832082Seschrock return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); 2284789Sahrens 2285789Sahrens default: 22862082Seschrock return (zfs_standard_error(hdl, errno, errbuf)); 2287789Sahrens } 2288789Sahrens } 2289789Sahrens 2290789Sahrens return (ret); 2291789Sahrens } 2292789Sahrens 2293789Sahrens /* 2294789Sahrens * Restores a backup of tosnap from stdin. 2295789Sahrens */ 2296789Sahrens int 22972082Seschrock zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, 22982082Seschrock int verbose, int dryrun) 2299789Sahrens { 2300789Sahrens zfs_cmd_t zc = { 0 }; 2301789Sahrens time_t begin_time; 2302868Sahrens int ioctl_err, err, bytes, size; 2303789Sahrens char *cp; 2304789Sahrens dmu_replay_record_t drr; 2305789Sahrens struct drr_begin *drrb = &zc.zc_begin_record; 23062082Seschrock char errbuf[1024]; 2307789Sahrens 2308789Sahrens begin_time = time(NULL); 2309789Sahrens 23102082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 23112082Seschrock "cannot receive")); 23122082Seschrock 2313789Sahrens /* trim off snapname, if any */ 2314789Sahrens (void) strcpy(zc.zc_name, tosnap); 2315789Sahrens cp = strchr(zc.zc_name, '@'); 2316789Sahrens if (cp) 2317789Sahrens *cp = '\0'; 2318789Sahrens 2319789Sahrens /* read in the BEGIN record */ 2320789Sahrens cp = (char *)&drr; 2321789Sahrens bytes = 0; 2322789Sahrens do { 2323868Sahrens size = read(STDIN_FILENO, cp, sizeof (drr) - bytes); 2324868Sahrens cp += size; 2325868Sahrens bytes += size; 2326868Sahrens } while (size > 0); 2327868Sahrens 2328868Sahrens if (size < 0 || bytes != sizeof (drr)) { 23292082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 23302082Seschrock "stream (failed to read first record)")); 23312082Seschrock return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 2332789Sahrens } 2333789Sahrens 2334789Sahrens zc.zc_begin_record = drr.drr_u.drr_begin; 2335789Sahrens 2336789Sahrens if (drrb->drr_magic != DMU_BACKUP_MAGIC && 2337789Sahrens drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) { 23382082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 23392082Seschrock "stream (bad magic number)")); 23402082Seschrock return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 2341789Sahrens } 2342789Sahrens 2343789Sahrens if (drrb->drr_version != DMU_BACKUP_VERSION && 2344789Sahrens drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) { 23452082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only version " 23462082Seschrock "0x%llx is supported (stream is version 0x%llx)"), 2347789Sahrens DMU_BACKUP_VERSION, drrb->drr_version); 23482082Seschrock return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 2349789Sahrens } 2350789Sahrens 2351789Sahrens /* 2352789Sahrens * Determine name of destination snapshot. 2353789Sahrens */ 23541544Seschrock (void) strcpy(zc.zc_filename, tosnap); 2355789Sahrens if (isprefix) { 2356789Sahrens if (strchr(tosnap, '@') != NULL) { 23572082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 23582082Seschrock "destination must be a filesystem")); 23592082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2360789Sahrens } 2361789Sahrens 2362789Sahrens cp = strchr(drr.drr_u.drr_begin.drr_toname, '/'); 2363789Sahrens if (cp == NULL) 2364789Sahrens cp = drr.drr_u.drr_begin.drr_toname; 2365789Sahrens else 2366789Sahrens cp++; 2367789Sahrens 23681544Seschrock (void) strcat(zc.zc_filename, "/"); 23691544Seschrock (void) strcat(zc.zc_filename, cp); 2370789Sahrens } else if (strchr(tosnap, '@') == NULL) { 2371789Sahrens /* 2372789Sahrens * they specified just a filesystem; tack on the 2373789Sahrens * snapname from the backup. 2374789Sahrens */ 2375789Sahrens cp = strchr(drr.drr_u.drr_begin.drr_toname, '@'); 23762082Seschrock if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN) 23772082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 23781544Seschrock (void) strcat(zc.zc_filename, cp); 2379789Sahrens } 2380789Sahrens 2381789Sahrens if (drrb->drr_fromguid) { 2382789Sahrens zfs_handle_t *h; 2383789Sahrens /* incremental backup stream */ 2384789Sahrens 2385789Sahrens /* do the ioctl to the containing fs */ 23861544Seschrock (void) strcpy(zc.zc_name, zc.zc_filename); 2387789Sahrens cp = strchr(zc.zc_name, '@'); 2388789Sahrens *cp = '\0'; 2389789Sahrens 2390789Sahrens /* make sure destination fs exists */ 23912082Seschrock h = zfs_open(hdl, zc.zc_name, 23922082Seschrock ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 23932082Seschrock if (h == NULL) 2394789Sahrens return (-1); 2395868Sahrens if (!dryrun) { 2396868Sahrens /* unmount destination fs or remove device link. */ 2397868Sahrens if (h->zfs_type == ZFS_TYPE_FILESYSTEM) { 2398868Sahrens (void) zfs_unmount(h, NULL, 0); 2399868Sahrens } else { 24002082Seschrock (void) zvol_remove_link(hdl, h->zfs_name); 2401868Sahrens } 2402868Sahrens } 2403789Sahrens zfs_close(h); 2404789Sahrens } else { 2405789Sahrens /* full backup stream */ 2406789Sahrens 24071544Seschrock (void) strcpy(zc.zc_name, zc.zc_filename); 2408868Sahrens 24091749Sahrens /* make sure they aren't trying to receive into the root */ 2410868Sahrens if (strchr(zc.zc_name, '/') == NULL) { 2411789Sahrens cp = strchr(zc.zc_name, '@'); 2412789Sahrens if (cp) 2413789Sahrens *cp = '\0'; 24142082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 24152082Seschrock "destination '%s' already exists"), zc.zc_name); 24162082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2417789Sahrens } 2418789Sahrens 2419789Sahrens if (isprefix) { 2420868Sahrens zfs_handle_t *h; 2421868Sahrens 2422789Sahrens /* make sure prefix exists */ 24232082Seschrock h = zfs_open(hdl, tosnap, ZFS_TYPE_FILESYSTEM); 24242082Seschrock if (h == NULL) 2425789Sahrens return (-1); 24261544Seschrock zfs_close(h); 2427789Sahrens 2428789Sahrens /* create any necessary ancestors up to prefix */ 2429868Sahrens zc.zc_objset_type = DMU_OST_ZFS; 24301544Seschrock 2431868Sahrens /* 2432868Sahrens * zc.zc_name is now the full name of the snap 24331544Seschrock * we're restoring into. Attempt to create, 24341544Seschrock * mount, and share any ancestor filesystems, up 24351544Seschrock * to the one that was named. 2436868Sahrens */ 24371544Seschrock for (cp = zc.zc_name + strlen(tosnap) + 1; 24381544Seschrock cp = strchr(cp, '/'); *cp = '/', cp++) { 24391544Seschrock const char *opname; 2440789Sahrens *cp = '\0'; 24411544Seschrock 24422082Seschrock opname = dgettext(TEXT_DOMAIN, "create"); 24432082Seschrock if (zfs_create(hdl, zc.zc_name, 24442082Seschrock ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0) { 24451544Seschrock if (errno == EEXIST) 24461544Seschrock continue; 24471544Seschrock goto ancestorerr; 2448789Sahrens } 24491544Seschrock 24502082Seschrock opname = dgettext(TEXT_DOMAIN, "open"); 24512082Seschrock h = zfs_open(hdl, zc.zc_name, 24522082Seschrock ZFS_TYPE_FILESYSTEM); 24531544Seschrock if (h == NULL) 24541544Seschrock goto ancestorerr; 24551544Seschrock 24562082Seschrock opname = dgettext(TEXT_DOMAIN, "mount"); 24571544Seschrock if (zfs_mount(h, NULL, 0) != 0) 24581544Seschrock goto ancestorerr; 24591544Seschrock 24602082Seschrock opname = dgettext(TEXT_DOMAIN, "share"); 24611544Seschrock if (zfs_share(h) != 0) 24621544Seschrock goto ancestorerr; 24631544Seschrock 24641544Seschrock zfs_close(h); 24651544Seschrock 24661544Seschrock continue; 24671544Seschrock ancestorerr: 24682082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 24692082Seschrock "failed to %s ancestor '%s'"), opname, 24702082Seschrock zc.zc_name); 24712082Seschrock return (zfs_error(hdl, EZFS_BADRESTORE, 24722082Seschrock errbuf)); 2473789Sahrens } 2474789Sahrens } 2475868Sahrens 2476868Sahrens /* Make sure destination fs does not exist */ 2477868Sahrens cp = strchr(zc.zc_name, '@'); 2478868Sahrens *cp = '\0'; 24792082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) { 24802082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 24812082Seschrock "destination '%s' exists"), zc.zc_name); 24822082Seschrock return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 2483868Sahrens } 2484868Sahrens 2485868Sahrens /* Do the recvbackup ioctl to the fs's parent. */ 2486868Sahrens cp = strrchr(zc.zc_name, '/'); 2487868Sahrens *cp = '\0'; 2488789Sahrens } 2489789Sahrens 2490789Sahrens (void) strcpy(zc.zc_prop_value, tosnap); 2491789Sahrens zc.zc_cookie = STDIN_FILENO; 2492789Sahrens zc.zc_intsz = isprefix; 2493789Sahrens if (verbose) { 24941749Sahrens (void) printf("%s %s stream of %s into %s\n", 24951749Sahrens dryrun ? "would receive" : "receiving", 2496789Sahrens drrb->drr_fromguid ? "incremental" : "full", 2497789Sahrens drr.drr_u.drr_begin.drr_toname, 24981544Seschrock zc.zc_filename); 2499789Sahrens (void) fflush(stdout); 2500789Sahrens } 2501789Sahrens if (dryrun) 2502789Sahrens return (0); 25032082Seschrock err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc); 2504868Sahrens if (ioctl_err != 0) { 2505789Sahrens switch (errno) { 2506789Sahrens case ENODEV: 25072082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25082082Seschrock "most recent snapshot does not match incremental " 25092082Seschrock "source")); 25102082Seschrock (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); 2511789Sahrens break; 2512789Sahrens case ETXTBSY: 25132082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25142082Seschrock "destination has been modified since most recent " 25152082Seschrock "snapshot")); 25162082Seschrock (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); 2517789Sahrens break; 2518789Sahrens case EEXIST: 2519789Sahrens if (drrb->drr_fromguid == 0) { 2520789Sahrens /* it's the containing fs that exists */ 25211544Seschrock cp = strchr(zc.zc_filename, '@'); 2522789Sahrens *cp = '\0'; 2523789Sahrens } 25242082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25252082Seschrock "destination already exists")); 25262082Seschrock (void) zfs_error(hdl, EZFS_EXISTS, dgettext(TEXT_DOMAIN, 25272082Seschrock "cannot restore to %s"), zc.zc_filename); 2528789Sahrens break; 2529789Sahrens case EINVAL: 25302082Seschrock (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); 2531868Sahrens break; 25321544Seschrock case ECKSUM: 25332082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25342082Seschrock "invalid stream (checksum mismatch)")); 25352082Seschrock (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); 2536789Sahrens break; 2537789Sahrens default: 25382082Seschrock (void) zfs_standard_error(hdl, errno, errbuf); 2539789Sahrens } 2540789Sahrens } 2541789Sahrens 2542789Sahrens /* 2543868Sahrens * Mount or recreate the /dev links for the target filesystem 2544868Sahrens * (if created, or if we tore them down to do an incremental 2545868Sahrens * restore), and the /dev links for the new snapshot (if 2546868Sahrens * created). 2547789Sahrens */ 25481544Seschrock cp = strchr(zc.zc_filename, '@'); 2549868Sahrens if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) { 2550789Sahrens zfs_handle_t *h; 2551789Sahrens 2552789Sahrens *cp = '\0'; 25532082Seschrock h = zfs_open(hdl, zc.zc_filename, 2554789Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 2555868Sahrens *cp = '@'; 2556789Sahrens if (h) { 2557868Sahrens if (h->zfs_type == ZFS_TYPE_FILESYSTEM) { 2558789Sahrens err = zfs_mount(h, NULL, 0); 2559868Sahrens } else { 25602082Seschrock err = zvol_create_link(hdl, h->zfs_name); 25611544Seschrock if (err == 0 && ioctl_err == 0) 25622082Seschrock err = zvol_create_link(hdl, 25632082Seschrock zc.zc_filename); 2564868Sahrens } 2565789Sahrens zfs_close(h); 2566789Sahrens } 2567789Sahrens } 2568789Sahrens 2569868Sahrens if (err || ioctl_err) 2570868Sahrens return (-1); 2571789Sahrens 2572789Sahrens if (verbose) { 2573789Sahrens char buf1[64]; 2574789Sahrens char buf2[64]; 2575789Sahrens uint64_t bytes = zc.zc_cookie; 2576789Sahrens time_t delta = time(NULL) - begin_time; 2577789Sahrens if (delta == 0) 2578789Sahrens delta = 1; 2579789Sahrens zfs_nicenum(bytes, buf1, sizeof (buf1)); 2580789Sahrens zfs_nicenum(bytes/delta, buf2, sizeof (buf1)); 2581789Sahrens 25821749Sahrens (void) printf("received %sb stream in %lu seconds (%sb/sec)\n", 2583789Sahrens buf1, delta, buf2); 2584789Sahrens } 2585789Sahrens return (0); 2586789Sahrens } 2587789Sahrens 2588789Sahrens /* 25891294Slling * Destroy any more recent snapshots. We invoke this callback on any dependents 25901294Slling * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 25911294Slling * is a dependent and we should just destroy it without checking the transaction 25921294Slling * group. 2593789Sahrens */ 25941294Slling typedef struct rollback_data { 25951294Slling const char *cb_target; /* the snapshot */ 25961294Slling uint64_t cb_create; /* creation time reference */ 25971294Slling prop_changelist_t *cb_clp; /* changelist pointer */ 25981294Slling int cb_error; 25992082Seschrock boolean_t cb_dependent; 26001294Slling } rollback_data_t; 26011294Slling 26021294Slling static int 26031294Slling rollback_destroy(zfs_handle_t *zhp, void *data) 26041294Slling { 26051294Slling rollback_data_t *cbp = data; 26061294Slling 26071294Slling if (!cbp->cb_dependent) { 26081294Slling if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 && 26091294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 26101294Slling zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 26111294Slling cbp->cb_create) { 26121294Slling 26132082Seschrock cbp->cb_dependent = B_TRUE; 26141294Slling (void) zfs_iter_dependents(zhp, rollback_destroy, cbp); 26152082Seschrock cbp->cb_dependent = B_FALSE; 26161294Slling 26171294Slling if (zfs_destroy(zhp) != 0) 26181294Slling cbp->cb_error = 1; 26191294Slling else 26201294Slling changelist_remove(zhp, cbp->cb_clp); 26211294Slling } 26221294Slling } else { 26231294Slling if (zfs_destroy(zhp) != 0) 26241294Slling cbp->cb_error = 1; 26251294Slling else 26261294Slling changelist_remove(zhp, cbp->cb_clp); 26271294Slling } 26281294Slling 26291294Slling zfs_close(zhp); 26301294Slling return (0); 26311294Slling } 26321294Slling 26331294Slling /* 26341294Slling * Rollback the dataset to its latest snapshot. 26351294Slling */ 26361294Slling static int 26371294Slling do_rollback(zfs_handle_t *zhp) 2638789Sahrens { 2639789Sahrens int ret; 2640789Sahrens zfs_cmd_t zc = { 0 }; 2641789Sahrens 2642789Sahrens assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || 2643789Sahrens zhp->zfs_type == ZFS_TYPE_VOLUME); 2644789Sahrens 2645789Sahrens if (zhp->zfs_type == ZFS_TYPE_VOLUME && 26462082Seschrock zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) 2647789Sahrens return (-1); 2648789Sahrens 2649789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2650789Sahrens 2651789Sahrens if (zhp->zfs_volblocksize != 0) 2652789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2653789Sahrens else 2654789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2655789Sahrens 2656789Sahrens /* 2657789Sahrens * We rely on the consumer to verify that there are no newer snapshots 2658789Sahrens * for the given dataset. Given these constraints, we can simply pass 2659789Sahrens * the name on to the ioctl() call. There is still an unlikely race 2660789Sahrens * condition where the user has taken a snapshot since we verified that 2661789Sahrens * this was the most recent. 2662789Sahrens */ 26632082Seschrock if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK, 26642082Seschrock &zc)) != 0) { 26652082Seschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, 26662082Seschrock dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), 26672082Seschrock zhp->zfs_name); 2668789Sahrens } else if (zhp->zfs_type == ZFS_TYPE_VOLUME) { 26692082Seschrock ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); 2670789Sahrens } 2671789Sahrens 2672789Sahrens return (ret); 2673789Sahrens } 2674789Sahrens 2675789Sahrens /* 26761294Slling * Given a dataset, rollback to a specific snapshot, discarding any 26771294Slling * data changes since then and making it the active dataset. 26781294Slling * 26791294Slling * Any snapshots more recent than the target are destroyed, along with 26801294Slling * their dependents. 26811294Slling */ 26821294Slling int 26831294Slling zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, int flag) 26841294Slling { 26851294Slling int ret; 26861294Slling rollback_data_t cb = { 0 }; 26871294Slling prop_changelist_t *clp; 26881294Slling 26891294Slling /* 26901294Slling * Unmount all dependendents of the dataset and the dataset itself. 26911294Slling * The list we need to gather is the same as for doing rename 26921294Slling */ 26931294Slling clp = changelist_gather(zhp, ZFS_PROP_NAME, flag ? MS_FORCE: 0); 26941294Slling if (clp == NULL) 26951294Slling return (-1); 26961294Slling 26971294Slling if ((ret = changelist_prefix(clp)) != 0) 26981294Slling goto out; 26991294Slling 27001294Slling /* 27011294Slling * Destroy all recent snapshots and its dependends. 27021294Slling */ 27031294Slling cb.cb_target = snap->zfs_name; 27041294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 27051294Slling cb.cb_clp = clp; 27061294Slling (void) zfs_iter_children(zhp, rollback_destroy, &cb); 27071294Slling 27081294Slling if ((ret = cb.cb_error) != 0) { 27091294Slling (void) changelist_postfix(clp); 27101294Slling goto out; 27111294Slling } 27121294Slling 27131294Slling /* 27141294Slling * Now that we have verified that the snapshot is the latest, 27151294Slling * rollback to the given snapshot. 27161294Slling */ 27171294Slling ret = do_rollback(zhp); 27181294Slling 27191294Slling if (ret != 0) { 27201294Slling (void) changelist_postfix(clp); 27211294Slling goto out; 27221294Slling } 27231294Slling 27241294Slling /* 27251294Slling * We only want to re-mount the filesystem if it was mounted in the 27261294Slling * first place. 27271294Slling */ 27281294Slling ret = changelist_postfix(clp); 27291294Slling 27301294Slling out: 27311294Slling changelist_free(clp); 27321294Slling return (ret); 27331294Slling } 27341294Slling 27351294Slling /* 2736789Sahrens * Iterate over all dependents for a given dataset. This includes both 2737789Sahrens * hierarchical dependents (children) and data dependents (snapshots and 2738789Sahrens * clones). The bulk of the processing occurs in get_dependents() in 2739789Sahrens * libzfs_graph.c. 2740789Sahrens */ 2741789Sahrens int 2742789Sahrens zfs_iter_dependents(zfs_handle_t *zhp, zfs_iter_f func, void *data) 2743789Sahrens { 2744789Sahrens char **dependents; 2745789Sahrens size_t count; 2746789Sahrens int i; 2747789Sahrens zfs_handle_t *child; 2748789Sahrens int ret = 0; 2749789Sahrens 27502082Seschrock dependents = get_dependents(zhp->zfs_hdl, zhp->zfs_name, &count); 2751789Sahrens for (i = 0; i < count; i++) { 27522082Seschrock if ((child = make_dataset_handle(zhp->zfs_hdl, 27532082Seschrock dependents[i])) == NULL) 2754789Sahrens continue; 2755789Sahrens 2756789Sahrens if ((ret = func(child, data)) != 0) 2757789Sahrens break; 2758789Sahrens } 2759789Sahrens 2760789Sahrens for (i = 0; i < count; i++) 2761789Sahrens free(dependents[i]); 2762789Sahrens free(dependents); 2763789Sahrens 2764789Sahrens return (ret); 2765789Sahrens } 2766789Sahrens 2767789Sahrens /* 2768789Sahrens * Renames the given dataset. 2769789Sahrens */ 2770789Sahrens int 2771789Sahrens zfs_rename(zfs_handle_t *zhp, const char *target) 2772789Sahrens { 2773789Sahrens int ret; 2774789Sahrens zfs_cmd_t zc = { 0 }; 2775789Sahrens char *delim; 2776789Sahrens prop_changelist_t *cl; 2777789Sahrens char parent[ZFS_MAXNAMELEN]; 27782082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 27792082Seschrock char errbuf[1024]; 2780789Sahrens 2781789Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 2782789Sahrens (void) strlcpy(zc.zc_prop_value, target, sizeof (zc.zc_prop_value)); 2783789Sahrens 2784789Sahrens /* if we have the same exact name, just return success */ 2785789Sahrens if (strcmp(zhp->zfs_name, target) == 0) 2786789Sahrens return (0); 2787789Sahrens 27882082Seschrock (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 27892082Seschrock "cannot rename to '%s'"), target); 27902082Seschrock 2791789Sahrens /* 2792789Sahrens * Make sure the target name is valid 2793789Sahrens */ 27942082Seschrock if (!zfs_validate_name(hdl, target, zhp->zfs_type)) 27952082Seschrock return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 2796789Sahrens 2797789Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 27982082Seschrock 2799789Sahrens if ((delim = strchr(target, '@')) == NULL) { 28002082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28012082Seschrock "not a snapshot")); 28022082Seschrock return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 2803789Sahrens } 2804789Sahrens 2805789Sahrens /* 2806789Sahrens * Make sure we're renaming within the same dataset. 2807789Sahrens */ 2808789Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 2809789Sahrens zhp->zfs_name[delim - target] != '@') { 28102082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28112082Seschrock "snapshots must be part of same dataset")); 28122082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 2813789Sahrens } 2814789Sahrens 2815789Sahrens (void) strncpy(parent, target, delim - target); 2816789Sahrens parent[delim - target] = '\0'; 2817789Sahrens } else { 2818789Sahrens /* validate parents */ 28192082Seschrock if (check_parents(hdl, target) != 0) 2820789Sahrens return (-1); 2821789Sahrens 2822789Sahrens (void) parent_name(target, parent, sizeof (parent)); 2823789Sahrens 2824789Sahrens /* make sure we're in the same pool */ 2825789Sahrens verify((delim = strchr(target, '/')) != NULL); 2826789Sahrens if (strncmp(zhp->zfs_name, target, delim - target) != 0 || 2827789Sahrens zhp->zfs_name[delim - target] != '/') { 28282082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28292082Seschrock "datasets must be within same pool")); 28302082Seschrock return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 2831789Sahrens } 2832789Sahrens } 2833789Sahrens 28342082Seschrock (void) snprintf(errbuf, sizeof (errbuf), 28352082Seschrock dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name); 28362082Seschrock 2837789Sahrens if (getzoneid() == GLOBAL_ZONEID && 2838789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 28392082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28402082Seschrock "dataset is used in a non-global zone")); 28412082Seschrock return (zfs_error(hdl, EZFS_ZONED, errbuf)); 2842789Sahrens } 2843789Sahrens 2844789Sahrens if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL) 28452082Seschrock return (-1); 2846789Sahrens 2847789Sahrens if (changelist_haszonedchild(cl)) { 28482082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28492082Seschrock "child dataset with inherited mountpoint is used " 28502082Seschrock "in a non-global zone")); 28512082Seschrock ret = zfs_error(hdl, EZFS_ZONED, errbuf); 2852789Sahrens goto error; 2853789Sahrens } 2854789Sahrens 2855789Sahrens if ((ret = changelist_prefix(cl)) != 0) 2856789Sahrens goto error; 2857789Sahrens 2858789Sahrens if (zhp->zfs_volblocksize != 0) 2859789Sahrens zc.zc_objset_type = DMU_OST_ZVOL; 2860789Sahrens else 2861789Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2862789Sahrens 28632082Seschrock if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) { 28642082Seschrock (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); 2865789Sahrens 2866789Sahrens /* 2867789Sahrens * On failure, we still want to remount any filesystems that 2868789Sahrens * were previously mounted, so we don't alter the system state. 2869789Sahrens */ 2870789Sahrens (void) changelist_postfix(cl); 2871789Sahrens } else { 2872789Sahrens changelist_rename(cl, zfs_get_name(zhp), target); 2873789Sahrens 2874789Sahrens ret = changelist_postfix(cl); 2875789Sahrens } 2876789Sahrens 2877789Sahrens error: 2878789Sahrens changelist_free(cl); 2879789Sahrens return (ret); 2880789Sahrens } 2881789Sahrens 2882789Sahrens /* 2883789Sahrens * Given a zvol dataset, issue the ioctl to create the appropriate minor node, 2884789Sahrens * poke devfsadm to create the /dev link, and then wait for the link to appear. 2885789Sahrens */ 2886789Sahrens int 28872082Seschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset) 2888789Sahrens { 2889789Sahrens zfs_cmd_t zc = { 0 }; 28902082Seschrock di_devlink_handle_t dhdl; 2891789Sahrens 2892789Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 2893789Sahrens 2894789Sahrens /* 2895789Sahrens * Issue the appropriate ioctl. 2896789Sahrens */ 28972082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { 2898789Sahrens switch (errno) { 2899789Sahrens case EEXIST: 2900789Sahrens /* 2901789Sahrens * Silently ignore the case where the link already 2902789Sahrens * exists. This allows 'zfs volinit' to be run multiple 2903789Sahrens * times without errors. 2904789Sahrens */ 2905789Sahrens return (0); 2906789Sahrens 2907789Sahrens default: 29082082Seschrock return (zfs_standard_error(hdl, errno, 29092082Seschrock dgettext(TEXT_DOMAIN, "cannot create device links " 29102082Seschrock "for '%s'"), dataset)); 2911789Sahrens } 2912789Sahrens } 2913789Sahrens 2914789Sahrens /* 2915789Sahrens * Call devfsadm and wait for the links to magically appear. 2916789Sahrens */ 29172082Seschrock if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) { 29182082Seschrock zfs_error_aux(hdl, strerror(errno)); 29192082Seschrock (void) zfs_error(hdl, EZFS_DEVLINKS, 29202082Seschrock dgettext(TEXT_DOMAIN, "cannot create device links " 29212082Seschrock "for '%s'"), dataset); 29222082Seschrock (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); 2923789Sahrens return (-1); 2924789Sahrens } else { 29252082Seschrock (void) di_devlink_fini(&dhdl); 2926789Sahrens } 2927789Sahrens 2928789Sahrens return (0); 2929789Sahrens } 2930789Sahrens 2931789Sahrens /* 2932789Sahrens * Remove a minor node for the given zvol and the associated /dev links. 2933789Sahrens */ 2934789Sahrens int 29352082Seschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) 2936789Sahrens { 2937789Sahrens zfs_cmd_t zc = { 0 }; 2938789Sahrens 2939789Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 2940789Sahrens 29412082Seschrock if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { 2942789Sahrens switch (errno) { 2943789Sahrens case ENXIO: 2944789Sahrens /* 2945789Sahrens * Silently ignore the case where the link no longer 2946789Sahrens * exists, so that 'zfs volfini' can be run multiple 2947789Sahrens * times without errors. 2948789Sahrens */ 2949789Sahrens return (0); 2950789Sahrens 2951789Sahrens default: 29522082Seschrock return (zfs_standard_error(hdl, errno, 29532082Seschrock dgettext(TEXT_DOMAIN, "cannot remove device " 29542082Seschrock "links for '%s'"), dataset)); 2955789Sahrens } 2956789Sahrens } 2957789Sahrens 2958789Sahrens return (0); 2959789Sahrens } 2960