aboutsummaryrefslogtreecommitdiff
path: root/cddl/contrib/opensolaris/lib/libzfs/common
diff options
context:
space:
mode:
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libzfs/common')
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h894
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c736
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c121
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h44
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c469
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c5284
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c834
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c452
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h228
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c1929
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c546
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c1734
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c4669
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c3924
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c511
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c1661
16 files changed, 0 insertions, 24036 deletions
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
deleted file mode 100644
index 1899e318d53e..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
+++ /dev/null
@@ -1,894 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
- * Copyright 2019 Joyent, Inc.
- * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2016 Nexenta Systems, Inc.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#ifndef _LIBZFS_H
-#define _LIBZFS_H
-
-#include <assert.h>
-#include <libnvpair.h>
-#include <sys/mnttab.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/varargs.h>
-#include <sys/fs/zfs.h>
-#include <sys/avl.h>
-#include <sys/zfs_ioctl.h>
-#include <libzfs_core.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Miscellaneous ZFS constants
- */
-#define ZFS_MAXPROPLEN MAXPATHLEN
-#define ZPOOL_MAXPROPLEN MAXPATHLEN
-
-/*
- * libzfs errors
- */
-typedef enum zfs_error {
- EZFS_SUCCESS = 0, /* no error -- success */
- EZFS_NOMEM = 2000, /* out of memory */
- EZFS_BADPROP, /* invalid property value */
- EZFS_PROPREADONLY, /* cannot set readonly property */
- EZFS_PROPTYPE, /* property does not apply to dataset type */
- EZFS_PROPNONINHERIT, /* property is not inheritable */
- EZFS_PROPSPACE, /* bad quota or reservation */
- EZFS_BADTYPE, /* dataset is not of appropriate type */
- EZFS_BUSY, /* pool or dataset is busy */
- EZFS_EXISTS, /* pool or dataset already exists */
- EZFS_NOENT, /* no such pool or dataset */
- EZFS_BADSTREAM, /* bad backup stream */
- EZFS_DSREADONLY, /* dataset is readonly */
- EZFS_VOLTOOBIG, /* volume is too large for 32-bit system */
- EZFS_INVALIDNAME, /* invalid dataset name */
- EZFS_BADRESTORE, /* unable to restore to destination */
- EZFS_BADBACKUP, /* backup failed */
- EZFS_BADTARGET, /* bad attach/detach/replace target */
- EZFS_NODEVICE, /* no such device in pool */
- EZFS_BADDEV, /* invalid device to add */
- EZFS_NOREPLICAS, /* no valid replicas */
- EZFS_RESILVERING, /* currently resilvering */
- EZFS_BADVERSION, /* unsupported version */
- EZFS_POOLUNAVAIL, /* pool is currently unavailable */
- EZFS_DEVOVERFLOW, /* too many devices in one vdev */
- EZFS_BADPATH, /* must be an absolute path */
- EZFS_CROSSTARGET, /* rename or clone across pool or dataset */
- EZFS_ZONED, /* used improperly in local zone */
- EZFS_MOUNTFAILED, /* failed to mount dataset */
- EZFS_UMOUNTFAILED, /* failed to unmount dataset */
- EZFS_UNSHARENFSFAILED, /* unshare(1M) failed */
- EZFS_SHARENFSFAILED, /* share(1M) failed */
- EZFS_PERM, /* permission denied */
- EZFS_NOSPC, /* out of space */
- EZFS_FAULT, /* bad address */
- EZFS_IO, /* I/O error */
- EZFS_INTR, /* signal received */
- EZFS_ISSPARE, /* device is a hot spare */
- EZFS_INVALCONFIG, /* invalid vdev configuration */
- EZFS_RECURSIVE, /* recursive dependency */
- EZFS_NOHISTORY, /* no history object */
- EZFS_POOLPROPS, /* couldn't retrieve pool props */
- EZFS_POOL_NOTSUP, /* ops not supported for this type of pool */
- EZFS_POOL_INVALARG, /* invalid argument for this pool operation */
- EZFS_NAMETOOLONG, /* dataset name is too long */
- EZFS_OPENFAILED, /* open of device failed */
- EZFS_NOCAP, /* couldn't get capacity */
- EZFS_LABELFAILED, /* write of label failed */
- EZFS_BADWHO, /* invalid permission who */
- EZFS_BADPERM, /* invalid permission */
- EZFS_BADPERMSET, /* invalid permission set name */
- EZFS_NODELEGATION, /* delegated administration is disabled */
- EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
- EZFS_SHARESMBFAILED, /* failed to share over smb */
- EZFS_BADCACHE, /* bad cache file */
- EZFS_ISL2CACHE, /* device is for the level 2 ARC */
- EZFS_VDEVNOTSUP, /* unsupported vdev type */
- EZFS_NOTSUP, /* ops not supported on this dataset */
- EZFS_ACTIVE_SPARE, /* pool has active shared spare devices */
- EZFS_UNPLAYED_LOGS, /* log device has unplayed logs */
- EZFS_REFTAG_RELE, /* snapshot release: tag not found */
- EZFS_REFTAG_HOLD, /* snapshot hold: tag already exists */
- EZFS_TAGTOOLONG, /* snapshot hold/rele: tag too long */
- EZFS_PIPEFAILED, /* pipe create failed */
- EZFS_THREADCREATEFAILED, /* thread create failed */
- EZFS_POSTSPLIT_ONLINE, /* onlining a disk after splitting it */
- EZFS_SCRUBBING, /* currently scrubbing */
- EZFS_NO_SCRUB, /* no active scrub */
- EZFS_DIFF, /* general failure of zfs diff */
- EZFS_DIFFDATA, /* bad zfs diff data */
- EZFS_POOLREADONLY, /* pool is in read-only mode */
- EZFS_SCRUB_PAUSED, /* scrub currently paused */
- EZFS_ACTIVE_POOL, /* pool is imported on a different system */
- EZFS_NO_PENDING, /* cannot cancel, no operation is pending */
- EZFS_CHECKPOINT_EXISTS, /* checkpoint exists */
- EZFS_DISCARDING_CHECKPOINT, /* currently discarding a checkpoint */
- EZFS_NO_CHECKPOINT, /* pool has no checkpoint */
- EZFS_DEVRM_IN_PROGRESS, /* a device is currently being removed */
- EZFS_VDEV_TOO_BIG, /* a device is too big to be used */
- EZFS_TOOMANY, /* argument list too long */
- EZFS_INITIALIZING, /* currently initializing */
- EZFS_NO_INITIALIZE, /* no active initialize */
- EZFS_WRONG_PARENT, /* invalid parent dataset (e.g ZVOL) */
- EZFS_IOC_NOTSUPPORTED, /* operation not supported by zfs module */
- EZFS_UNKNOWN
-} zfs_error_t;
-
-/*
- * UEFI boot support parameters. When creating whole disk boot pool,
- * zpool create should allow to create EFI System partition for UEFI boot
- * program. In case of BIOS, the EFI System partition is not used
- * even if it does exist.
- */
-typedef enum zpool_boot_label {
- ZPOOL_NO_BOOT_LABEL = 0,
- ZPOOL_CREATE_BOOT_LABEL,
- ZPOOL_COPY_BOOT_LABEL
-} zpool_boot_label_t;
-
-/*
- * The following data structures are all part
- * of the zfs_allow_t data structure which is
- * used for printing 'allow' permissions.
- * It is a linked list of zfs_allow_t's which
- * then contain avl tree's for user/group/sets/...
- * and each one of the entries in those trees have
- * avl tree's for the permissions they belong to and
- * whether they are local,descendent or local+descendent
- * permissions. The AVL trees are used primarily for
- * sorting purposes, but also so that we can quickly find
- * a given user and or permission.
- */
-typedef struct zfs_perm_node {
- avl_node_t z_node;
- char z_pname[MAXPATHLEN];
-} zfs_perm_node_t;
-
-typedef struct zfs_allow_node {
- avl_node_t z_node;
- char z_key[MAXPATHLEN]; /* name, such as joe */
- avl_tree_t z_localdescend; /* local+descendent perms */
- avl_tree_t z_local; /* local permissions */
- avl_tree_t z_descend; /* descendent permissions */
-} zfs_allow_node_t;
-
-typedef struct zfs_allow {
- struct zfs_allow *z_next;
- char z_setpoint[MAXPATHLEN];
- avl_tree_t z_sets;
- avl_tree_t z_crperms;
- avl_tree_t z_user;
- avl_tree_t z_group;
- avl_tree_t z_everyone;
-} zfs_allow_t;
-
-/*
- * Basic handle types
- */
-typedef struct zfs_handle zfs_handle_t;
-typedef struct zpool_handle zpool_handle_t;
-typedef struct libzfs_handle libzfs_handle_t;
-
-/*
- * Library initialization
- */
-extern libzfs_handle_t *libzfs_init(void);
-extern void libzfs_fini(libzfs_handle_t *);
-
-extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *);
-extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
-
-extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
-
-extern void zfs_save_arguments(int argc, char **, char *, int);
-extern int zpool_log_history(libzfs_handle_t *, const char *);
-
-extern int libzfs_errno(libzfs_handle_t *);
-extern const char *libzfs_error_action(libzfs_handle_t *);
-extern const char *libzfs_error_description(libzfs_handle_t *);
-extern int zfs_standard_error(libzfs_handle_t *, int, const char *);
-extern void libzfs_mnttab_init(libzfs_handle_t *);
-extern void libzfs_mnttab_fini(libzfs_handle_t *);
-extern void libzfs_mnttab_cache(libzfs_handle_t *, boolean_t);
-extern int libzfs_mnttab_find(libzfs_handle_t *, const char *,
- struct mnttab *);
-extern void libzfs_mnttab_add(libzfs_handle_t *, const char *,
- const char *, const char *);
-extern void libzfs_mnttab_remove(libzfs_handle_t *, const char *);
-
-/*
- * Basic handle functions
- */
-extern zpool_handle_t *zpool_open(libzfs_handle_t *, const char *);
-extern zpool_handle_t *zpool_open_canfail(libzfs_handle_t *, const char *);
-extern void zpool_close(zpool_handle_t *);
-extern const char *zpool_get_name(zpool_handle_t *);
-extern int zpool_get_state(zpool_handle_t *);
-extern const char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
-extern const char *zpool_pool_state_to_name(pool_state_t);
-extern void zpool_free_handles(libzfs_handle_t *);
-extern int zpool_nextboot(libzfs_handle_t *, uint64_t, uint64_t, const char *);
-
-/*
- * Iterate over all active pools in the system.
- */
-typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
-extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
-extern boolean_t zpool_skip_pool(const char *);
-
-/*
- * Functions to create and destroy pools
- */
-extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
- nvlist_t *, nvlist_t *);
-extern int zpool_destroy(zpool_handle_t *, const char *);
-extern int zpool_add(zpool_handle_t *, nvlist_t *);
-
-typedef struct splitflags {
- /* do not split, but return the config that would be split off */
- int dryrun : 1;
-
- /* after splitting, import the pool */
- int import : 1;
- int name_flags;
-} splitflags_t;
-
-/*
- * Functions to manipulate pool and vdev state
- */
-extern int zpool_scan(zpool_handle_t *, pool_scan_func_t, pool_scrub_cmd_t);
-extern int zpool_initialize(zpool_handle_t *, pool_initialize_func_t,
- nvlist_t *);
-extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
-extern int zpool_reguid(zpool_handle_t *);
-extern int zpool_reopen(zpool_handle_t *);
-
-extern int zpool_sync_one(zpool_handle_t *, void *);
-
-extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
- vdev_state_t *);
-extern int zpool_vdev_offline(zpool_handle_t *, const char *, boolean_t);
-extern int zpool_vdev_attach(zpool_handle_t *, const char *,
- const char *, nvlist_t *, int);
-extern int zpool_vdev_detach(zpool_handle_t *, const char *);
-extern int zpool_vdev_remove(zpool_handle_t *, const char *);
-extern int zpool_vdev_remove_cancel(zpool_handle_t *);
-extern int zpool_vdev_indirect_size(zpool_handle_t *, const char *, uint64_t *);
-extern int zpool_vdev_split(zpool_handle_t *, char *, nvlist_t **, nvlist_t *,
- splitflags_t);
-
-extern int zpool_vdev_fault(zpool_handle_t *, uint64_t, vdev_aux_t);
-extern int zpool_vdev_degrade(zpool_handle_t *, uint64_t, vdev_aux_t);
-extern int zpool_vdev_clear(zpool_handle_t *, uint64_t);
-
-extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
- boolean_t *, boolean_t *);
-extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
- boolean_t *, boolean_t *, boolean_t *);
-extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, const char *,
- zpool_boot_label_t, uint64_t, int *);
-
-/*
- * Functions to manage pool properties
- */
-extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
-extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
- size_t proplen, zprop_source_t *, boolean_t);
-extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
- zprop_source_t *);
-
-extern const char *zpool_prop_to_name(zpool_prop_t);
-extern const char *zpool_prop_values(zpool_prop_t);
-
-/*
- * Pool health statistics.
- */
-typedef enum {
- /*
- * The following correspond to faults as defined in the (fault.fs.zfs.*)
- * event namespace. Each is associated with a corresponding message ID.
- * This must be kept in sync with the zfs_msgid_table in
- * lib/libzfs/libzfs_status.c.
- */
- ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */
- ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */
- ZPOOL_STATUS_MISSING_DEV_NR, /* missing device with no replicas */
- ZPOOL_STATUS_CORRUPT_LABEL_R, /* bad device label with replicas */
- ZPOOL_STATUS_CORRUPT_LABEL_NR, /* bad device label with no replicas */
- ZPOOL_STATUS_BAD_GUID_SUM, /* sum of device guids didn't match */
- ZPOOL_STATUS_CORRUPT_POOL, /* pool metadata is corrupted */
- ZPOOL_STATUS_CORRUPT_DATA, /* data errors in user (meta)data */
- ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
- ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */
- ZPOOL_STATUS_HOSTID_MISMATCH, /* last accessed by another system */
- ZPOOL_STATUS_HOSTID_ACTIVE, /* currently active on another system */
- ZPOOL_STATUS_HOSTID_REQUIRED, /* multihost=on and hostid=0 */
- ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */
- ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
- ZPOOL_STATUS_IO_FAILURE_MMP, /* failed MMP, failmode not 'panic' */
- ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
-
- /*
- * If the pool has unsupported features but can still be opened in
- * read-only mode, its status is ZPOOL_STATUS_UNSUP_FEAT_WRITE. If the
- * pool has unsupported features but cannot be opened at all, its
- * status is ZPOOL_STATUS_UNSUP_FEAT_READ.
- */
- ZPOOL_STATUS_UNSUP_FEAT_READ, /* unsupported features for read */
- ZPOOL_STATUS_UNSUP_FEAT_WRITE, /* unsupported features for write */
-
- /*
- * These faults have no corresponding message ID. At the time we are
- * checking the status, the original reason for the FMA fault (I/O or
- * checksum errors) has been lost.
- */
- ZPOOL_STATUS_FAULTED_DEV_R, /* faulted device with replicas */
- ZPOOL_STATUS_FAULTED_DEV_NR, /* faulted device with no replicas */
-
- /*
- * The following are not faults per se, but still an error possibly
- * requiring administrative attention. There is no corresponding
- * message ID.
- */
- ZPOOL_STATUS_VERSION_OLDER, /* older legacy on-disk version */
- ZPOOL_STATUS_FEAT_DISABLED, /* supported features are disabled */
- ZPOOL_STATUS_RESILVERING, /* device being resilvered */
- ZPOOL_STATUS_OFFLINE_DEV, /* device offline */
- ZPOOL_STATUS_REMOVED_DEV, /* removed device */
- ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */
-
- /*
- * Finally, the following indicates a healthy pool.
- */
- ZPOOL_STATUS_OK
-} zpool_status_t;
-
-extern zpool_status_t zpool_get_status(zpool_handle_t *, char **);
-extern zpool_status_t zpool_import_status(nvlist_t *, char **);
-extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh);
-
-/*
- * Statistics and configuration functions.
- */
-extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
-extern nvlist_t *zpool_get_features(zpool_handle_t *);
-extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
-extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
-extern boolean_t zpool_is_bootable(zpool_handle_t *);
-
-/*
- * Import and export functions
- */
-extern int zpool_export(zpool_handle_t *, boolean_t, const char *);
-extern int zpool_export_force(zpool_handle_t *, const char *);
-extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
- char *altroot);
-extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
- nvlist_t *, int);
-extern void zpool_print_unsup_feat(nvlist_t *config);
-
-/*
- * Search for pools to import
- */
-
-typedef struct importargs {
- char **path; /* a list of paths to search */
- int paths; /* number of paths to search */
- char *poolname; /* name of a pool to find */
- uint64_t guid; /* guid of a pool to find */
- char *cachefile; /* cachefile to use for import */
- int can_be_active : 1; /* can the pool be active? */
- int unique : 1; /* does 'poolname' already exist? */
- int exists : 1; /* set on return if pool already exists */
- nvlist_t *policy; /* load policy (max txg, rewind, etc.) */
-} importargs_t;
-
-extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *);
-extern int zpool_tryimport(libzfs_handle_t *hdl, char *target,
- nvlist_t **configp, importargs_t *args);
-
-/* legacy pool search routines */
-extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
-extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *,
- char *, uint64_t);
-
-/*
- * Miscellaneous pool functions
- */
-struct zfs_cmd;
-
-extern const char *zfs_history_event_names[];
-
-typedef enum {
- VDEV_NAME_PATH = 1 << 0,
- VDEV_NAME_GUID = 1 << 1,
- VDEV_NAME_FOLLOW_LINKS = 1 << 2,
- VDEV_NAME_TYPE_ID = 1 << 3,
-} vdev_name_t;
-
-extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
- int name_flags);
-extern int zpool_upgrade(zpool_handle_t *, uint64_t);
-extern int zpool_get_history(zpool_handle_t *, nvlist_t **, uint64_t *,
- boolean_t *);
-extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
- nvlist_t ***, uint_t *);
-extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
- size_t len);
-extern int zfs_ioctl(libzfs_handle_t *, int request, struct zfs_cmd *);
-extern int zpool_get_physpath(zpool_handle_t *, char *, size_t);
-extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
- nvlist_t *);
-extern int zpool_checkpoint(zpool_handle_t *);
-extern int zpool_discard_checkpoint(zpool_handle_t *);
-
-/*
- * Basic handle manipulations. These functions do not create or destroy the
- * underlying datasets, only the references to them.
- */
-extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
-extern zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
-extern void zfs_close(zfs_handle_t *);
-extern zfs_type_t zfs_get_type(const zfs_handle_t *);
-extern const char *zfs_get_name(const zfs_handle_t *);
-extern zpool_handle_t *zfs_get_pool_handle(const zfs_handle_t *);
-extern const char *zfs_get_pool_name(const zfs_handle_t *);
-
-/*
- * Property management functions. Some functions are shared with the kernel,
- * and are found in sys/fs/zfs.h.
- */
-
-/*
- * zfs dataset property management
- */
-extern const char *zfs_prop_default_string(zfs_prop_t);
-extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
-extern const char *zfs_prop_column_name(zfs_prop_t);
-extern boolean_t zfs_prop_align_right(zfs_prop_t);
-
-extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t,
- nvlist_t *, uint64_t, zfs_handle_t *, zpool_handle_t *, const char *);
-
-extern const char *zfs_prop_to_name(zfs_prop_t);
-extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
-extern int zfs_prop_set_list(zfs_handle_t *, nvlist_t *);
-extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
- zprop_source_t *, char *, size_t, boolean_t);
-extern int zfs_prop_get_recvd(zfs_handle_t *, const char *, char *, size_t,
- boolean_t);
-extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
- zprop_source_t *, char *, size_t);
-extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue);
-extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal);
-extern int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue);
-extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal);
-extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname,
- char *buf, size_t len);
-extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
-extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
-extern const char *zfs_prop_values(zfs_prop_t);
-extern int zfs_prop_is_string(zfs_prop_t prop);
-extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
-extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
-extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
-
-
-typedef struct zprop_list {
- int pl_prop;
- char *pl_user_prop;
- struct zprop_list *pl_next;
- boolean_t pl_all;
- size_t pl_width;
- size_t pl_recvd_width;
- boolean_t pl_fixed;
-} zprop_list_t;
-
-extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **, boolean_t,
- boolean_t);
-extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *);
-
-#define ZFS_MOUNTPOINT_NONE "none"
-#define ZFS_MOUNTPOINT_LEGACY "legacy"
-
-#define ZFS_FEATURE_DISABLED "disabled"
-#define ZFS_FEATURE_ENABLED "enabled"
-#define ZFS_FEATURE_ACTIVE "active"
-
-#define ZFS_UNSUPPORTED_INACTIVE "inactive"
-#define ZFS_UNSUPPORTED_READONLY "readonly"
-
-/*
- * zpool property management
- */
-extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
-extern int zpool_prop_get_feature(zpool_handle_t *, const char *, char *,
- size_t);
-extern const char *zpool_prop_default_string(zpool_prop_t);
-extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
-extern const char *zpool_prop_column_name(zpool_prop_t);
-extern boolean_t zpool_prop_align_right(zpool_prop_t);
-
-/*
- * Functions shared by zfs and zpool property management.
- */
-extern int zprop_iter(zprop_func func, void *cb, boolean_t show_all,
- boolean_t ordered, zfs_type_t type);
-extern int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **,
- zfs_type_t);
-extern void zprop_free_list(zprop_list_t *);
-
-#define ZFS_GET_NCOLS 5
-
-typedef enum {
- GET_COL_NONE,
- GET_COL_NAME,
- GET_COL_PROPERTY,
- GET_COL_VALUE,
- GET_COL_RECVD,
- GET_COL_SOURCE
-} zfs_get_column_t;
-
-/*
- * Functions for printing zfs or zpool properties
- */
-typedef struct zprop_get_cbdata {
- int cb_sources;
- zfs_get_column_t cb_columns[ZFS_GET_NCOLS];
- int cb_colwidths[ZFS_GET_NCOLS + 1];
- boolean_t cb_scripted;
- boolean_t cb_literal;
- boolean_t cb_first;
- zprop_list_t *cb_proplist;
- zfs_type_t cb_type;
-} zprop_get_cbdata_t;
-
-void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
- const char *, const char *, zprop_source_t, const char *,
- const char *);
-
-/*
- * Iterator functions.
- */
-typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
-extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
-extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_snapshots(zfs_handle_t *, boolean_t, zfs_iter_f, void *,
- uint64_t, uint64_t);
-extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *,
- uint64_t, uint64_t);
-extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
-extern int zfs_iter_bookmarks(zfs_handle_t *, zfs_iter_f, void *);
-
-typedef struct get_all_cb {
- zfs_handle_t **cb_handles;
- size_t cb_alloc;
- size_t cb_used;
-} get_all_cb_t;
-
-void zfs_foreach_mountpoint(libzfs_handle_t *, zfs_handle_t **, size_t,
- zfs_iter_f, void*, boolean_t);
-
-void libzfs_add_handle(get_all_cb_t *, zfs_handle_t *);
-
-/*
- * Functions to create and destroy datasets.
- */
-extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
- nvlist_t *);
-extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
-extern int zfs_destroy(zfs_handle_t *, boolean_t);
-extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
-extern int zfs_destroy_snaps_nvl(libzfs_handle_t *, nvlist_t *, boolean_t);
-extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
-extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
-extern int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
- nvlist_t *props);
-extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
-
-typedef struct renameflags {
- /* recursive rename */
- int recurse : 1;
-
- /* don't unmount file systems */
- int nounmount : 1;
-
- /* force unmount file systems */
- int forceunmount : 1;
-} renameflags_t;
-
-extern int zfs_rename(zfs_handle_t *, const char *, const char *,
- renameflags_t flags);
-
-typedef struct sendflags {
- /* print informational messages (ie, -v was specified) */
- boolean_t verbose;
-
- /* recursive send (ie, -R) */
- boolean_t replicate;
-
- /* for incrementals, do all intermediate snapshots */
- boolean_t doall;
-
- /* if dataset is a clone, do incremental from its origin */
- boolean_t fromorigin;
-
- /* do deduplication */
- boolean_t dedup;
-
- /* send properties (ie, -p) */
- boolean_t props;
-
- /* do not send (no-op, ie. -n) */
- boolean_t dryrun;
-
- /* parsable verbose output (ie. -P) */
- boolean_t parsable;
-
- /* show progress (ie. -v) */
- boolean_t progress;
-
- /* large blocks (>128K) are permitted */
- boolean_t largeblock;
-
- /* WRITE_EMBEDDED records of type DATA are permitted */
- boolean_t embed_data;
-
- /* compressed WRITE records are permitted */
- boolean_t compress;
-
- /* show progress as process title(ie. -V) */
- boolean_t progressastitle;
-} sendflags_t;
-
-typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
-
-extern int zfs_send(zfs_handle_t *, const char *, const char *,
- sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
-extern int zfs_send_one(zfs_handle_t *, const char *, int, sendflags_t flags);
-extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd,
- const char *);
-extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl,
- const char *token);
-
-extern int zfs_promote(zfs_handle_t *);
-extern int zfs_hold(zfs_handle_t *, const char *, const char *,
- boolean_t, int);
-extern int zfs_hold_nvl(zfs_handle_t *, int, nvlist_t *);
-extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
-extern int zfs_get_holds(zfs_handle_t *, nvlist_t **);
-extern uint64_t zvol_volsize_to_reservation(uint64_t, nvlist_t *);
-
-typedef int (*zfs_userspace_cb_t)(void *arg, const char *domain,
- uid_t rid, uint64_t space);
-
-extern int zfs_userspace(zfs_handle_t *, zfs_userquota_prop_t,
- zfs_userspace_cb_t, void *);
-
-extern int zfs_get_fsacl(zfs_handle_t *, nvlist_t **);
-extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
-
-typedef struct recvflags {
- /* print informational messages (ie, -v was specified) */
- boolean_t verbose;
-
- /* the destination is a prefix, not the exact fs (ie, -d) */
- boolean_t isprefix;
-
- /*
- * Only the tail of the sent snapshot path is appended to the
- * destination to determine the received snapshot name (ie, -e).
- */
- boolean_t istail;
-
- /* do not actually do the recv, just check if it would work (ie, -n) */
- boolean_t dryrun;
-
- /* rollback/destroy filesystems as necessary (eg, -F) */
- boolean_t force;
-
- /* set "canmount=off" on all modified filesystems */
- boolean_t canmountoff;
-
- /*
- * Mark the file systems as "resumable" and do not destroy them if the
- * receive is interrupted
- */
- boolean_t resumable;
-
- /* byteswap flag is used internally; callers need not specify */
- boolean_t byteswap;
-
- /* do not mount file systems as they are extracted (private) */
- boolean_t nomount;
-
- /* force unmount while recv snapshot (private) */
- boolean_t forceunmount;
-} recvflags_t;
-
-extern int zfs_receive(libzfs_handle_t *, const char *, nvlist_t *,
- recvflags_t *, int, avl_tree_t *);
-
-typedef enum diff_flags {
- ZFS_DIFF_PARSEABLE = 0x1,
- ZFS_DIFF_TIMESTAMP = 0x2,
- ZFS_DIFF_CLASSIFY = 0x4
-} diff_flags_t;
-
-extern int zfs_show_diffs(zfs_handle_t *, int, const char *, const char *,
- int);
-
-/*
- * Miscellaneous functions.
- */
-extern const char *zfs_type_to_name(zfs_type_t);
-extern void zfs_refresh_properties(zfs_handle_t *);
-extern int zfs_name_valid(const char *, zfs_type_t);
-extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
-extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
- zfs_type_t);
-extern int zfs_spa_version(zfs_handle_t *, int *);
-extern boolean_t zfs_bookmark_exists(const char *path);
-extern ulong_t get_system_hostid(void);
-
-/*
- * Mount support functions.
- */
-extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
-extern boolean_t zfs_is_mounted(zfs_handle_t *, char **);
-extern int zfs_mount(zfs_handle_t *, const char *, int);
-extern int zfs_mount_at(zfs_handle_t *, const char *, int, const char *);
-extern int zfs_unmount(zfs_handle_t *, const char *, int);
-extern int zfs_unmountall(zfs_handle_t *, int);
-
-/*
- * Share support functions.
- */
-extern boolean_t zfs_is_shared(zfs_handle_t *);
-extern int zfs_share(zfs_handle_t *);
-extern int zfs_unshare(zfs_handle_t *);
-
-/*
- * Protocol-specific share support functions.
- */
-extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
-extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **);
-extern int zfs_share_nfs(zfs_handle_t *);
-extern int zfs_share_smb(zfs_handle_t *);
-extern int zfs_shareall(zfs_handle_t *);
-extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
-extern int zfs_unshare_smb(zfs_handle_t *, const char *);
-extern int zfs_unshareall_nfs(zfs_handle_t *);
-extern int zfs_unshareall_smb(zfs_handle_t *);
-extern int zfs_unshareall_bypath(zfs_handle_t *, const char *);
-extern int zfs_unshareall(zfs_handle_t *);
-extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *,
- void *, void *, int, zfs_share_op_t);
-
-/*
- * FreeBSD-specific jail support function.
- */
-extern int zfs_jail(zfs_handle_t *, int, int);
-
-/*
- * When dealing with nvlists, verify() is extremely useful
- */
-#ifndef verify
-#ifdef NDEBUG
-#define verify(EX) ((void)(EX))
-#else
-#define verify(EX) assert(EX)
-#endif
-#endif
-
-/*
- * Utility function to convert a number to a human-readable form.
- */
-extern void zfs_nicenum(uint64_t, char *, size_t);
-extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
-
-/*
- * Given a device or file, determine if it is part of a pool.
- */
-extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
- boolean_t *);
-
-/*
- * Label manipulation.
- */
-extern int zpool_read_label(int, nvlist_t **);
-extern int zpool_read_all_labels(int, nvlist_t **);
-extern int zpool_clear_label(int);
-extern int zpool_set_bootenv(zpool_handle_t *, const char *);
-extern int zpool_get_bootenv(zpool_handle_t *, char *, size_t, off_t);
-
-/* is this zvol valid for use as a dump device? */
-extern int zvol_check_dump_config(char *);
-
-/*
- * Management interfaces for SMB ACL files
- */
-
-int zfs_smb_acl_add(libzfs_handle_t *, char *, char *, char *);
-int zfs_smb_acl_remove(libzfs_handle_t *, char *, char *, char *);
-int zfs_smb_acl_purge(libzfs_handle_t *, char *, char *);
-int zfs_smb_acl_rename(libzfs_handle_t *, char *, char *, char *, char *);
-
-/*
- * Enable and disable datasets within a pool by mounting/unmounting and
- * sharing/unsharing them.
- */
-extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
-extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
-
-/*
- * Mappings between vdev and FRU.
- */
-extern void libzfs_fru_refresh(libzfs_handle_t *);
-extern const char *libzfs_fru_lookup(libzfs_handle_t *, const char *);
-extern const char *libzfs_fru_devpath(libzfs_handle_t *, const char *);
-extern boolean_t libzfs_fru_compare(libzfs_handle_t *, const char *,
- const char *);
-extern boolean_t libzfs_fru_notself(libzfs_handle_t *, const char *);
-extern int zpool_fru_set(zpool_handle_t *, uint64_t, const char *);
-
-#ifndef illumos
-extern int zmount(const char *, const char *, int, char *, char *, int, char *,
- int);
-#endif
-extern int zfs_remap_indirects(libzfs_handle_t *hdl, const char *);
-
-/* Allow consumers to initialize libshare externally for optimal performance */
-extern int zfs_init_libshare_arg(libzfs_handle_t *, int, void *);
-/*
- * For most consumers, zfs_init_libshare_arg is sufficient on its own, and
- * zfs_uninit_libshare is unnecessary. zfs_uninit_libshare should only be called
- * if the caller has already initialized libshare for one set of zfs handles,
- * and wishes to share or unshare filesystems outside of that set. In that case,
- * the caller should uninitialize libshare, and then re-initialize it with the
- * new handles being shared or unshared.
- */
-extern void zfs_uninit_libshare(libzfs_handle_t *);
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
deleted file mode 100644
index 7bbb68328f29..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
+++ /dev/null
@@ -1,736 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- *
- * Portions Copyright 2007 Ramprakash Jelari
- * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
- * All rights reserved.
- * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- */
-
-#include <libintl.h>
-#include <libuutil.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <zone.h>
-
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-
-/*
- * Structure to keep track of dataset state. Before changing the 'sharenfs' or
- * 'mountpoint' property, we record whether the filesystem was previously
- * mounted/shared. This prior state dictates whether we remount/reshare the
- * dataset after the property has been changed.
- *
- * The interface consists of the following sequence of functions:
- *
- * changelist_gather()
- * changelist_prefix()
- * < change property >
- * changelist_postfix()
- * changelist_free()
- *
- * Other interfaces:
- *
- * changelist_remove() - remove a node from a gathered list
- * changelist_rename() - renames all datasets appropriately when doing a rename
- * changelist_unshare() - unshares all the nodes in a given changelist
- * changelist_haszonedchild() - check if there is any child exported to
- * a local zone
- */
-typedef struct prop_changenode {
- zfs_handle_t *cn_handle;
- int cn_shared;
- int cn_mounted;
- int cn_zoned;
- boolean_t cn_needpost; /* is postfix() needed? */
- uu_list_node_t cn_listnode;
-} prop_changenode_t;
-
-struct prop_changelist {
- zfs_prop_t cl_prop;
- zfs_prop_t cl_realprop;
- zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
- uu_list_pool_t *cl_pool;
- uu_list_t *cl_list;
- boolean_t cl_waslegacy;
- boolean_t cl_allchildren;
- boolean_t cl_alldependents;
- int cl_mflags; /* Mount flags */
- int cl_gflags; /* Gather request flags */
- boolean_t cl_haszonedchild;
- boolean_t cl_sorted;
-};
-
-/*
- * If the property is 'mountpoint', go through and unmount filesystems as
- * necessary. We don't do the same for 'sharenfs', because we can just re-share
- * with different options without interrupting service. We do handle 'sharesmb'
- * since there may be old resource names that need to be removed.
- */
-int
-changelist_prefix(prop_changelist_t *clp)
-{
- prop_changenode_t *cn;
- int ret = 0;
-
- if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
- clp->cl_prop != ZFS_PROP_SHARESMB)
- return (0);
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
-
- /* if a previous loop failed, set the remaining to false */
- if (ret == -1) {
- cn->cn_needpost = B_FALSE;
- continue;
- }
-
- /*
- * If we are in the global zone, but this dataset is exported
- * to a local zone, do nothing.
- */
- if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
- continue;
-
- if (!ZFS_IS_VOLUME(cn->cn_handle)) {
- /*
- * Do the property specific processing.
- */
- switch (clp->cl_prop) {
- case ZFS_PROP_MOUNTPOINT:
- if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
- break;
- if (zfs_unmount(cn->cn_handle, NULL,
- clp->cl_mflags) != 0) {
- ret = -1;
- cn->cn_needpost = B_FALSE;
- }
- break;
- case ZFS_PROP_SHARESMB:
- (void) zfs_unshare_smb(cn->cn_handle, NULL);
- break;
-
- default:
- break;
- }
- }
- }
-
- if (ret == -1)
- (void) changelist_postfix(clp);
-
- return (ret);
-}
-
-/*
- * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
- * reshare the filesystems as necessary. In changelist_gather() we recorded
- * whether the filesystem was previously shared or mounted. The action we take
- * depends on the previous state, and whether the value was previously 'legacy'.
- * For non-legacy properties, we only remount/reshare the filesystem if it was
- * previously mounted/shared. Otherwise, we always remount/reshare the
- * filesystem.
- */
-int
-changelist_postfix(prop_changelist_t *clp)
-{
- prop_changenode_t *cn;
- char shareopts[ZFS_MAXPROPLEN];
- int errors = 0;
- libzfs_handle_t *hdl;
-#ifdef illumos
- size_t num_datasets = 0, i;
- zfs_handle_t **zhandle_arr;
- sa_init_selective_arg_t sharearg;
-#endif
-
- /*
- * If we're changing the mountpoint, attempt to destroy the underlying
- * mountpoint. All other datasets will have inherited from this dataset
- * (in which case their mountpoints exist in the filesystem in the new
- * location), or have explicit mountpoints set (in which case they won't
- * be in the changelist).
- */
- if ((cn = uu_list_last(clp->cl_list)) == NULL)
- return (0);
-
- if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
- !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) {
- remove_mountpoint(cn->cn_handle);
- }
-
- /*
- * It is possible that the changelist_prefix() used libshare
- * to unshare some entries. Since libshare caches data, an
- * attempt to reshare during postfix can fail unless libshare
- * is uninitialized here so that it will reinitialize later.
- */
- if (cn->cn_handle != NULL) {
- hdl = cn->cn_handle->zfs_hdl;
- assert(hdl != NULL);
- zfs_uninit_libshare(hdl);
-
-#ifdef illumos
- /*
- * For efficiencies sake, we initialize libshare for only a few
- * shares (the ones affected here). Future initializations in
- * this process should just use the cached initialization.
- */
- for (cn = uu_list_last(clp->cl_list); cn != NULL;
- cn = uu_list_prev(clp->cl_list, cn)) {
- num_datasets++;
- }
-
- zhandle_arr = zfs_alloc(hdl,
- num_datasets * sizeof (zfs_handle_t *));
- for (i = 0, cn = uu_list_last(clp->cl_list); cn != NULL;
- cn = uu_list_prev(clp->cl_list, cn)) {
- zhandle_arr[i++] = cn->cn_handle;
- zfs_refresh_properties(cn->cn_handle);
- }
- assert(i == num_datasets);
- sharearg.zhandle_arr = zhandle_arr;
- sharearg.zhandle_len = num_datasets;
- errors = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
- &sharearg);
- free(zhandle_arr);
-#endif
- }
- /*
- * We walk the datasets in reverse, because we want to mount any parent
- * datasets before mounting the children. We walk all datasets even if
- * there are errors.
- */
- for (cn = uu_list_last(clp->cl_list); cn != NULL;
- cn = uu_list_prev(clp->cl_list, cn)) {
-
- boolean_t sharenfs;
- boolean_t sharesmb;
- boolean_t mounted;
-
- /*
- * If we are in the global zone, but this dataset is exported
- * to a local zone, do nothing.
- */
- if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
- continue;
-
- /* Only do post-processing if it's required */
- if (!cn->cn_needpost)
- continue;
- cn->cn_needpost = B_FALSE;
-
-#ifndef illumos
- zfs_refresh_properties(cn->cn_handle);
-#endif
-
- if (ZFS_IS_VOLUME(cn->cn_handle))
- continue;
-
- /*
- * Remount if previously mounted or mountpoint was legacy,
- * or sharenfs or sharesmb property is set.
- */
- sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
- shareopts, sizeof (shareopts), NULL, NULL, 0,
- B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
-
- sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
- shareopts, sizeof (shareopts), NULL, NULL, 0,
- B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
-
- mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
- zfs_is_mounted(cn->cn_handle, NULL);
-
- if (!mounted && (cn->cn_mounted ||
- ((sharenfs || sharesmb || clp->cl_waslegacy) &&
- (zfs_prop_get_int(cn->cn_handle,
- ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
-
- if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
- errors++;
- else
- mounted = TRUE;
- }
-
- /*
- * If the file system is mounted we always re-share even
- * if the filesystem is currently shared, so that we can
- * adopt any new options.
- */
- if (sharenfs && mounted)
- errors += zfs_share_nfs(cn->cn_handle);
- else if (cn->cn_shared || clp->cl_waslegacy)
- errors += zfs_unshare_nfs(cn->cn_handle, NULL);
- if (sharesmb && mounted)
- errors += zfs_share_smb(cn->cn_handle);
- else if (cn->cn_shared || clp->cl_waslegacy)
- errors += zfs_unshare_smb(cn->cn_handle, NULL);
- }
-
- return (errors ? -1 : 0);
-}
-
-/*
- * Is this "dataset" a child of "parent"?
- */
-boolean_t
-isa_child_of(const char *dataset, const char *parent)
-{
- int len;
-
- len = strlen(parent);
-
- if (strncmp(dataset, parent, len) == 0 &&
- (dataset[len] == '@' || dataset[len] == '/' ||
- dataset[len] == '\0'))
- return (B_TRUE);
- else
- return (B_FALSE);
-
-}
-
-/*
- * If we rename a filesystem, child filesystem handles are no longer valid
- * since we identify each dataset by its name in the ZFS namespace. As a
- * result, we have to go through and fix up all the names appropriately. We
- * could do this automatically if libzfs kept track of all open handles, but
- * this is a lot less work.
- */
-void
-changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
-{
- prop_changenode_t *cn;
- char newname[ZFS_MAX_DATASET_NAME_LEN];
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
- /*
- * Do not rename a clone that's not in the source hierarchy.
- */
- if (!isa_child_of(cn->cn_handle->zfs_name, src))
- continue;
-
- /*
- * Destroy the previous mountpoint if needed.
- */
- remove_mountpoint(cn->cn_handle);
-
- (void) strlcpy(newname, dst, sizeof (newname));
- (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
-
- (void) strlcpy(cn->cn_handle->zfs_name, newname,
- sizeof (cn->cn_handle->zfs_name));
- }
-}
-
-/*
- * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
- * unshare all the datasets in the list.
- */
-int
-changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
-{
- prop_changenode_t *cn;
- int ret = 0;
-
- if (clp->cl_prop != ZFS_PROP_SHARENFS &&
- clp->cl_prop != ZFS_PROP_SHARESMB)
- return (0);
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
- if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
- ret = -1;
- }
-
- return (ret);
-}
-
-/*
- * Check if there is any child exported to a local zone in a given changelist.
- * This information has already been recorded while gathering the changelist
- * via changelist_gather().
- */
-int
-changelist_haszonedchild(prop_changelist_t *clp)
-{
- return (clp->cl_haszonedchild);
-}
-
-/*
- * Remove a node from a gathered list.
- */
-void
-changelist_remove(prop_changelist_t *clp, const char *name)
-{
- prop_changenode_t *cn;
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
-
- if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
- uu_list_remove(clp->cl_list, cn);
- zfs_close(cn->cn_handle);
- free(cn);
- return;
- }
- }
-}
-
-/*
- * Release any memory associated with a changelist.
- */
-void
-changelist_free(prop_changelist_t *clp)
-{
- prop_changenode_t *cn;
- void *cookie;
-
- if (clp->cl_list) {
- cookie = NULL;
- while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) {
- zfs_close(cn->cn_handle);
- free(cn);
- }
-
- uu_list_destroy(clp->cl_list);
- }
- if (clp->cl_pool)
- uu_list_pool_destroy(clp->cl_pool);
-
- free(clp);
-}
-
-static int
-change_one(zfs_handle_t *zhp, void *data)
-{
- prop_changelist_t *clp = data;
- char property[ZFS_MAXPROPLEN];
- char where[64];
- prop_changenode_t *cn;
- zprop_source_t sourcetype;
- zprop_source_t share_sourcetype;
-
- /*
- * We only want to unmount/unshare those filesystems that may inherit
- * from the target filesystem. If we find any filesystem with a
- * locally set mountpoint, we ignore any children since changing the
- * property will not affect them. If this is a rename, we iterate
- * over all children regardless, since we need them unmounted in
- * order to do the rename. Also, if this is a volume and we're doing
- * a rename, then always add it to the changelist.
- */
-
- if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
- zfs_prop_get(zhp, clp->cl_prop, property,
- sizeof (property), &sourcetype, where, sizeof (where),
- B_FALSE) != 0) {
- zfs_close(zhp);
- return (0);
- }
-
- /*
- * If we are "watching" sharenfs or sharesmb
- * then check out the companion property which is tracked
- * in cl_shareprop
- */
- if (clp->cl_shareprop != ZPROP_INVAL &&
- zfs_prop_get(zhp, clp->cl_shareprop, property,
- sizeof (property), &share_sourcetype, where, sizeof (where),
- B_FALSE) != 0) {
- zfs_close(zhp);
- return (0);
- }
-
- if (clp->cl_alldependents || clp->cl_allchildren ||
- sourcetype == ZPROP_SRC_DEFAULT ||
- sourcetype == ZPROP_SRC_INHERITED ||
- (clp->cl_shareprop != ZPROP_INVAL &&
- (share_sourcetype == ZPROP_SRC_DEFAULT ||
- share_sourcetype == ZPROP_SRC_INHERITED))) {
- if ((cn = zfs_alloc(zfs_get_handle(zhp),
- sizeof (prop_changenode_t))) == NULL) {
- zfs_close(zhp);
- return (-1);
- }
-
- cn->cn_handle = zhp;
- cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
- zfs_is_mounted(zhp, NULL);
- cn->cn_shared = zfs_is_shared(zhp);
- cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
- cn->cn_needpost = B_TRUE;
-
- /* Indicate if any child is exported to a local zone. */
- if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
- clp->cl_haszonedchild = B_TRUE;
-
- uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
-
- if (clp->cl_sorted) {
- uu_list_index_t idx;
-
- (void) uu_list_find(clp->cl_list, cn, NULL,
- &idx);
- uu_list_insert(clp->cl_list, cn, idx);
- } else {
- /*
- * Add this child to beginning of the list. Children
- * below this one in the hierarchy will get added above
- * this one in the list. This produces a list in
- * reverse dataset name order.
- * This is necessary when the original mountpoint
- * is legacy or none.
- */
- verify(uu_list_insert_before(clp->cl_list,
- uu_list_first(clp->cl_list), cn) == 0);
- }
-
- if (!clp->cl_alldependents)
- return (zfs_iter_children(zhp, change_one, data));
- } else {
- zfs_close(zhp);
- }
-
- return (0);
-}
-
-/*ARGSUSED*/
-static int
-compare_mountpoints(const void *a, const void *b, void *unused)
-{
- const prop_changenode_t *ca = a;
- const prop_changenode_t *cb = b;
-
- char mounta[MAXPATHLEN];
- char mountb[MAXPATHLEN];
-
- boolean_t hasmounta, hasmountb;
-
- /*
- * When unsharing or unmounting filesystems, we need to do it in
- * mountpoint order. This allows the user to have a mountpoint
- * hierarchy that is different from the dataset hierarchy, and still
- * allow it to be changed. However, if either dataset doesn't have a
- * mountpoint (because it is a volume or a snapshot), we place it at the
- * end of the list, because it doesn't affect our change at all.
- */
- hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
- sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
- hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
- sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
-
- if (!hasmounta && hasmountb)
- return (-1);
- else if (hasmounta && !hasmountb)
- return (1);
- else if (!hasmounta && !hasmountb)
- return (0);
- else
- return (strcmp(mountb, mounta));
-}
-
-/*
- * Given a ZFS handle and a property, construct a complete list of datasets
- * that need to be modified as part of this process. For anything but the
- * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
- * Otherwise, we iterate over all children and look for any datasets that
- * inherit the property. For each such dataset, we add it to the list and
- * mark whether it was shared beforehand.
- */
-prop_changelist_t *
-changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
- int mnt_flags)
-{
- prop_changelist_t *clp;
- prop_changenode_t *cn;
- zfs_handle_t *temp;
- char property[ZFS_MAXPROPLEN];
- uu_compare_fn_t *compare = NULL;
- boolean_t legacy = B_FALSE;
-
- if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
- return (NULL);
-
- /*
- * For mountpoint-related tasks, we want to sort everything by
- * mountpoint, so that we mount and unmount them in the appropriate
- * order, regardless of their position in the hierarchy.
- */
- if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
- prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
- prop == ZFS_PROP_SHARESMB) {
-
- if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
- property, sizeof (property),
- NULL, NULL, 0, B_FALSE) == 0 &&
- (strcmp(property, "legacy") == 0 ||
- strcmp(property, "none") == 0)) {
-
- legacy = B_TRUE;
- }
- if (!legacy) {
- compare = compare_mountpoints;
- clp->cl_sorted = B_TRUE;
- }
- }
-
- clp->cl_pool = uu_list_pool_create("changelist_pool",
- sizeof (prop_changenode_t),
- offsetof(prop_changenode_t, cn_listnode),
- compare, 0);
- if (clp->cl_pool == NULL) {
- assert(uu_error() == UU_ERROR_NO_MEMORY);
- (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
- changelist_free(clp);
- return (NULL);
- }
-
- clp->cl_list = uu_list_create(clp->cl_pool, NULL,
- clp->cl_sorted ? UU_LIST_SORTED : 0);
- clp->cl_gflags = gather_flags;
- clp->cl_mflags = mnt_flags;
-
- if (clp->cl_list == NULL) {
- assert(uu_error() == UU_ERROR_NO_MEMORY);
- (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
- changelist_free(clp);
- return (NULL);
- }
-
- /*
- * If this is a rename or the 'zoned' property, we pretend we're
- * changing the mountpoint and flag it so we can catch all children in
- * change_one().
- *
- * Flag cl_alldependents to catch all children plus the dependents
- * (clones) that are not in the hierarchy.
- */
- if (prop == ZFS_PROP_NAME) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- clp->cl_alldependents = B_TRUE;
- } else if (prop == ZFS_PROP_ZONED) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- clp->cl_allchildren = B_TRUE;
- } else if (prop == ZFS_PROP_CANMOUNT) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- } else if (prop == ZFS_PROP_VOLSIZE) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- } else {
- clp->cl_prop = prop;
- }
- clp->cl_realprop = prop;
-
- if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
- clp->cl_prop != ZFS_PROP_SHARENFS &&
- clp->cl_prop != ZFS_PROP_SHARESMB)
- return (clp);
-
- /*
- * If watching SHARENFS or SHARESMB then
- * also watch its companion property.
- */
- if (clp->cl_prop == ZFS_PROP_SHARENFS)
- clp->cl_shareprop = ZFS_PROP_SHARESMB;
- else if (clp->cl_prop == ZFS_PROP_SHARESMB)
- clp->cl_shareprop = ZFS_PROP_SHARENFS;
-
- if (clp->cl_alldependents) {
- if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
- changelist_free(clp);
- return (NULL);
- }
- } else if (zfs_iter_children(zhp, change_one, clp) != 0) {
- changelist_free(clp);
- return (NULL);
- }
-
- /*
- * We have to re-open ourselves because we auto-close all the handles
- * and can't tell the difference.
- */
- if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
- ZFS_TYPE_DATASET)) == NULL) {
- changelist_free(clp);
- return (NULL);
- }
-
- /*
- * Always add ourself to the list. We add ourselves to the end so that
- * we're the last to be unmounted.
- */
- if ((cn = zfs_alloc(zhp->zfs_hdl,
- sizeof (prop_changenode_t))) == NULL) {
- zfs_close(temp);
- changelist_free(clp);
- return (NULL);
- }
-
- cn->cn_handle = temp;
- cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
- zfs_is_mounted(temp, NULL);
- cn->cn_shared = zfs_is_shared(temp);
- cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
- cn->cn_needpost = B_TRUE;
-
- uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
- if (clp->cl_sorted) {
- uu_list_index_t idx;
- (void) uu_list_find(clp->cl_list, cn, NULL, &idx);
- uu_list_insert(clp->cl_list, cn, idx);
- } else {
- /*
- * Add the target dataset to the end of the list.
- * The list is not really unsorted. The list will be
- * in reverse dataset name order. This is necessary
- * when the original mountpoint is legacy or none.
- */
- verify(uu_list_insert_after(clp->cl_list,
- uu_list_last(clp->cl_list), cn) == 0);
- }
-
- /*
- * If the mountpoint property was previously 'legacy', or 'none',
- * record it as the behavior of changelist_postfix() will be different.
- */
- if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
- /*
- * do not automatically mount ex-legacy datasets if
- * we specifically set canmount to noauto
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
- ZFS_CANMOUNT_NOAUTO)
- clp->cl_waslegacy = B_TRUE;
- }
-
- return (clp);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
deleted file mode 100644
index 7545331b40b4..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * CDDL HEADER SART
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#include "libzfs_compat.h"
-
-int zfs_ioctl_version = ZFS_IOCVER_UNDEF;
-static int zfs_spa_version = -1;
-
-/*
- * Get zfs_ioctl_version
- */
-int
-get_zfs_ioctl_version(void)
-{
- size_t ver_size;
- int ver = ZFS_IOCVER_NONE;
-
- ver_size = sizeof(ver);
- sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0);
-
- return (ver);
-}
-
-/*
- * Get the SPA version
- */
-static int
-get_zfs_spa_version(void)
-{
- size_t ver_size;
- int ver = 0;
-
- ver_size = sizeof(ver);
- sysctlbyname("vfs.zfs.version.spa", &ver, &ver_size, NULL, 0);
-
- return (ver);
-}
-
-/*
- * This is FreeBSD version of ioctl, because Solaris' ioctl() updates
- * zc_nvlist_dst_size even if an error is returned, on FreeBSD if an
- * error is returned zc_nvlist_dst_size won't be updated.
- */
-int
-zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
-{
- size_t oldsize;
- int ret, cflag = ZFS_CMD_COMPAT_NONE;
-
- if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
- zfs_ioctl_version = get_zfs_ioctl_version();
-
- if (zfs_ioctl_version >= ZFS_IOCVER_DEADMAN) {
- switch (zfs_ioctl_version) {
- case ZFS_IOCVER_INLANES:
- cflag = ZFS_CMD_COMPAT_INLANES;
- break;
- case ZFS_IOCVER_RESUME:
- cflag = ZFS_CMD_COMPAT_RESUME;
- break;
- case ZFS_IOCVER_EDBP:
- cflag = ZFS_CMD_COMPAT_EDBP;
- break;
- case ZFS_IOCVER_ZCMD:
- cflag = ZFS_CMD_COMPAT_ZCMD;
- break;
- case ZFS_IOCVER_LZC:
- cflag = ZFS_CMD_COMPAT_LZC;
- break;
- case ZFS_IOCVER_DEADMAN:
- cflag = ZFS_CMD_COMPAT_DEADMAN;
- break;
- }
- } else {
- /*
- * If vfs.zfs.version.ioctl is not defined, assume we have v28
- * compatible binaries and use vfs.zfs.version.spa to test for v15
- */
- cflag = ZFS_CMD_COMPAT_V28;
-
- if (zfs_spa_version < 0)
- zfs_spa_version = get_zfs_spa_version();
-
- if (zfs_spa_version == SPA_VERSION_15 ||
- zfs_spa_version == SPA_VERSION_14 ||
- zfs_spa_version == SPA_VERSION_13)
- cflag = ZFS_CMD_COMPAT_V15;
- }
-
- oldsize = zc->zc_nvlist_dst_size;
- ret = zcmd_ioctl_compat(fd, request, zc, cflag);
-
- if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) {
- ret = -1;
- errno = ENOMEM;
- }
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h
deleted file mode 100644
index 37616683330a..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * CDDL HEADER SART
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#ifndef _LIBZFS_COMPAT_H
-#define _LIBZFS_COMPAT_H
-
-#include <zfs_ioctl_compat.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int get_zfs_ioctl_version(void);
-int zcmd_ioctl(int fd, int request, zfs_cmd_t *zc);
-
-#define ioctl(fd, ioc, zc) zcmd_ioctl((fd), (ioc), (zc))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_COMPAT_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c
deleted file mode 100644
index b33d86432dc5..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * Copyright (c) 2012 by Delphix. All rights reserved.
- * Copyright (c) 2015 by Syneto S.R.L. All rights reserved.
- * Copyright 2016 Nexenta Systems, Inc.
- */
-
-/*
- * The pool configuration repository is stored in /etc/zfs/zpool.cache as a
- * single packed nvlist. While it would be nice to just read in this
- * file from userland, this wouldn't work from a local zone. So we have to have
- * a zpool ioctl to return the complete configuration for all pools. In the
- * global zone, this will be identical to reading the file and unpacking it in
- * userland.
- */
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <libintl.h>
-#include <libuutil.h>
-
-#include "libzfs_impl.h"
-
-typedef struct config_node {
- char *cn_name;
- nvlist_t *cn_config;
- uu_avl_node_t cn_avl;
-} config_node_t;
-
-/* ARGSUSED */
-static int
-config_node_compare(const void *a, const void *b, void *unused)
-{
- int ret;
-
- const config_node_t *ca = (config_node_t *)a;
- const config_node_t *cb = (config_node_t *)b;
-
- ret = strcmp(ca->cn_name, cb->cn_name);
-
- if (ret < 0)
- return (-1);
- else if (ret > 0)
- return (1);
- else
- return (0);
-}
-
-void
-namespace_clear(libzfs_handle_t *hdl)
-{
- if (hdl->libzfs_ns_avl) {
- config_node_t *cn;
- void *cookie = NULL;
-
- while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl,
- &cookie)) != NULL) {
- nvlist_free(cn->cn_config);
- free(cn->cn_name);
- free(cn);
- }
-
- uu_avl_destroy(hdl->libzfs_ns_avl);
- hdl->libzfs_ns_avl = NULL;
- }
-
- if (hdl->libzfs_ns_avlpool) {
- uu_avl_pool_destroy(hdl->libzfs_ns_avlpool);
- hdl->libzfs_ns_avlpool = NULL;
- }
-}
-
-/*
- * Loads the pool namespace, or re-loads it if the cache has changed.
- */
-static int
-namespace_reload(libzfs_handle_t *hdl)
-{
- nvlist_t *config;
- config_node_t *cn;
- nvpair_t *elem;
- zfs_cmd_t zc = { 0 };
- void *cookie;
-
- if (hdl->libzfs_ns_gen == 0) {
- /*
- * This is the first time we've accessed the configuration
- * cache. Initialize the AVL tree and then fall through to the
- * common code.
- */
- if ((hdl->libzfs_ns_avlpool = uu_avl_pool_create("config_pool",
- sizeof (config_node_t),
- offsetof(config_node_t, cn_avl),
- config_node_compare, UU_DEFAULT)) == NULL)
- return (no_memory(hdl));
-
- if ((hdl->libzfs_ns_avl = uu_avl_create(hdl->libzfs_ns_avlpool,
- NULL, UU_DEFAULT)) == NULL)
- return (no_memory(hdl));
- }
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- for (;;) {
- zc.zc_cookie = hdl->libzfs_ns_gen;
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
- switch (errno) {
- case EEXIST:
- /*
- * The namespace hasn't changed.
- */
- zcmd_free_nvlists(&zc);
- return (0);
-
- case ENOMEM:
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- break;
-
- default:
- zcmd_free_nvlists(&zc);
- return (zfs_standard_error(hdl, errno,
- dgettext(TEXT_DOMAIN, "failed to read "
- "pool configuration")));
- }
- } else {
- hdl->libzfs_ns_gen = zc.zc_cookie;
- break;
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- /*
- * Clear out any existing configuration information.
- */
- cookie = NULL;
- while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl, &cookie)) != NULL) {
- nvlist_free(cn->cn_config);
- free(cn->cn_name);
- free(cn);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {
- nvlist_t *child;
- uu_avl_index_t where;
-
- if ((cn = zfs_alloc(hdl, sizeof (config_node_t))) == NULL) {
- nvlist_free(config);
- return (-1);
- }
-
- if ((cn->cn_name = zfs_strdup(hdl,
- nvpair_name(elem))) == NULL) {
- free(cn);
- nvlist_free(config);
- return (-1);
- }
-
- verify(nvpair_value_nvlist(elem, &child) == 0);
- if (nvlist_dup(child, &cn->cn_config, 0) != 0) {
- free(cn->cn_name);
- free(cn);
- nvlist_free(config);
- return (no_memory(hdl));
- }
- verify(uu_avl_find(hdl->libzfs_ns_avl, cn, NULL, &where)
- == NULL);
-
- uu_avl_insert(hdl->libzfs_ns_avl, cn, where);
- }
-
- nvlist_free(config);
- return (0);
-}
-
-/*
- * Retrieve the configuration for the given pool. The configuration is a nvlist
- * describing the vdevs, as well as the statistics associated with each one.
- */
-nvlist_t *
-zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
-{
- if (oldconfig)
- *oldconfig = zhp->zpool_old_config;
- return (zhp->zpool_config);
-}
-
-/*
- * Retrieves a list of enabled features and their refcounts and caches it in
- * the pool handle.
- */
-nvlist_t *
-zpool_get_features(zpool_handle_t *zhp)
-{
- nvlist_t *config, *features;
-
- config = zpool_get_config(zhp, NULL);
-
- if (config == NULL || !nvlist_exists(config,
- ZPOOL_CONFIG_FEATURE_STATS)) {
- int error;
- boolean_t missing = B_FALSE;
-
- error = zpool_refresh_stats(zhp, &missing);
-
- if (error != 0 || missing)
- return (NULL);
-
- config = zpool_get_config(zhp, NULL);
- }
-
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
- &features) != 0)
- return (NULL);
-
- return (features);
-}
-
-/*
- * Refresh the vdev statistics associated with the given pool. This is used in
- * iostat to show configuration changes and determine the delta from the last
- * time the function was called. This function can fail, in case the pool has
- * been destroyed.
- */
-int
-zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
-{
- zfs_cmd_t zc = { 0 };
- int error;
- nvlist_t *config;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- *missing = B_FALSE;
- (void) strcpy(zc.zc_name, zhp->zpool_name);
-
- if (zhp->zpool_config_size == 0)
- zhp->zpool_config_size = 1 << 16;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0)
- return (-1);
-
- for (;;) {
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_STATS,
- &zc) == 0) {
- /*
- * The real error is returned in the zc_cookie field.
- */
- error = zc.zc_cookie;
- break;
- }
-
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- if (errno == ENOENT || errno == EINVAL)
- *missing = B_TRUE;
- zhp->zpool_state = POOL_STATE_UNAVAIL;
- return (0);
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- zhp->zpool_config_size = zc.zc_nvlist_dst_size;
-
- if (zhp->zpool_config != NULL) {
- uint64_t oldtxg, newtxg;
-
- verify(nvlist_lookup_uint64(zhp->zpool_config,
- ZPOOL_CONFIG_POOL_TXG, &oldtxg) == 0);
- verify(nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_POOL_TXG, &newtxg) == 0);
-
- nvlist_free(zhp->zpool_old_config);
-
- if (oldtxg != newtxg) {
- nvlist_free(zhp->zpool_config);
- zhp->zpool_old_config = NULL;
- } else {
- zhp->zpool_old_config = zhp->zpool_config;
- }
- }
-
- zhp->zpool_config = config;
- if (error)
- zhp->zpool_state = POOL_STATE_UNAVAIL;
- else
- zhp->zpool_state = POOL_STATE_ACTIVE;
-
- return (0);
-}
-
-/*
- * The following environment variables are undocumented
- * and should be used for testing purposes only:
- *
- * __ZFS_POOL_EXCLUDE - don't iterate over the pools it lists
- * __ZFS_POOL_RESTRICT - iterate only over the pools it lists
- *
- * This function returns B_TRUE if the pool should be skipped
- * during iteration.
- */
-boolean_t
-zpool_skip_pool(const char *poolname)
-{
- static boolean_t initialized = B_FALSE;
- static const char *exclude = NULL;
- static const char *restricted = NULL;
-
- const char *cur, *end;
- int len;
- int namelen = strlen(poolname);
-
- if (!initialized) {
- initialized = B_TRUE;
- exclude = getenv("__ZFS_POOL_EXCLUDE");
- restricted = getenv("__ZFS_POOL_RESTRICT");
- }
-
- if (exclude != NULL) {
- cur = exclude;
- do {
- end = strchr(cur, ' ');
- len = (NULL == end) ? strlen(cur) : (end - cur);
- if (len == namelen && 0 == strncmp(cur, poolname, len))
- return (B_TRUE);
- cur += (len + 1);
- } while (NULL != end);
- }
-
- if (NULL == restricted)
- return (B_FALSE);
-
- cur = restricted;
- do {
- end = strchr(cur, ' ');
- len = (NULL == end) ? strlen(cur) : (end - cur);
-
- if (len == namelen && 0 == strncmp(cur, poolname, len)) {
- return (B_FALSE);
- }
-
- cur += (len + 1);
- } while (NULL != end);
-
- return (B_TRUE);
-}
-
-/*
- * Iterate over all pools in the system.
- */
-int
-zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
-{
- config_node_t *cn;
- zpool_handle_t *zhp;
- int ret;
-
- /*
- * If someone makes a recursive call to zpool_iter(), we want to avoid
- * refreshing the namespace because that will invalidate the parent
- * context. We allow recursive calls, but simply re-use the same
- * namespace AVL tree.
- */
- if (!hdl->libzfs_pool_iter && namespace_reload(hdl) != 0)
- return (-1);
-
- hdl->libzfs_pool_iter++;
- for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
- cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
-
- if (zpool_skip_pool(cn->cn_name))
- continue;
-
- if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) {
- hdl->libzfs_pool_iter--;
- return (-1);
- }
-
- if (zhp == NULL)
- continue;
-
- if ((ret = func(zhp, data)) != 0) {
- hdl->libzfs_pool_iter--;
- return (ret);
- }
- }
- hdl->libzfs_pool_iter--;
-
- return (0);
-}
-
-/*
- * Iterate over root datasets, calling the given function for each. The zfs
- * handle passed each time must be explicitly closed by the callback.
- */
-int
-zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
-{
- config_node_t *cn;
- zfs_handle_t *zhp;
- int ret;
-
- if (namespace_reload(hdl) != 0)
- return (-1);
-
- for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
- cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
-
- if (zpool_skip_pool(cn->cn_name))
- continue;
-
- if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
- continue;
-
- if ((ret = func(zhp, data)) != 0)
- return (ret);
- }
-
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
deleted file mode 100644
index 7075d060c78d..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
+++ /dev/null
@@ -1,5284 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
- * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
- * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2013 Martin Matuska. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2017 Nexenta Systems, Inc.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright 2017-2018 RackTop Systems.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <libintl.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <zone.h>
-#include <fcntl.h>
-#include <sys/mntent.h>
-#include <sys/mount.h>
-#include <priv.h>
-#include <pwd.h>
-#include <grp.h>
-#include <stddef.h>
-#ifdef illumos
-#include <idmap.h>
-#endif
-
-#include <sys/dnode.h>
-#include <sys/spa.h>
-#include <sys/zap.h>
-#include <sys/misc.h>
-#include <libzfs.h>
-
-#include "zfs_namecheck.h"
-#include "zfs_prop.h"
-#include "libzfs_impl.h"
-#include "zfs_deleg.h"
-
-static int userquota_propname_decode(const char *propname, boolean_t zoned,
- zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
-
-/*
- * Given a single type (not a mask of types), return the type in a human
- * readable form.
- */
-const char *
-zfs_type_to_name(zfs_type_t type)
-{
- switch (type) {
- case ZFS_TYPE_FILESYSTEM:
- return (dgettext(TEXT_DOMAIN, "filesystem"));
- case ZFS_TYPE_SNAPSHOT:
- return (dgettext(TEXT_DOMAIN, "snapshot"));
- case ZFS_TYPE_VOLUME:
- return (dgettext(TEXT_DOMAIN, "volume"));
- case ZFS_TYPE_POOL:
- return (dgettext(TEXT_DOMAIN, "pool"));
- case ZFS_TYPE_BOOKMARK:
- return (dgettext(TEXT_DOMAIN, "bookmark"));
- default:
- assert(!"unhandled zfs_type_t");
- }
-
- return (NULL);
-}
-
-/*
- * Validate a ZFS path. This is used even before trying to open the dataset, to
- * provide a more meaningful error message. We call zfs_error_aux() to
- * explain exactly why the name was not valid.
- */
-int
-zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
- boolean_t modifying)
-{
- namecheck_err_t why;
- char what;
-
- if (entity_namecheck(path, &why, &what) != 0) {
- if (hdl != NULL) {
- switch (why) {
- case NAME_ERR_TOOLONG:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name is too long"));
- break;
-
- case NAME_ERR_LEADING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "leading slash in name"));
- break;
-
- case NAME_ERR_EMPTY_COMPONENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty component in name"));
- break;
-
- case NAME_ERR_TRAILING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "trailing slash in name"));
- break;
-
- case NAME_ERR_INVALCHAR:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "invalid character "
- "'%c' in name"), what);
- break;
-
- case NAME_ERR_MULTIPLE_DELIMITERS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple '@' and/or '#' delimiters in "
- "name"));
- break;
-
- case NAME_ERR_NOLETTER:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool doesn't begin with a letter"));
- break;
-
- case NAME_ERR_RESERVED:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name is reserved"));
- break;
-
- case NAME_ERR_DISKLIKE:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "reserved disk name"));
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "(%d) not defined"), why);
- break;
- }
- }
-
- return (0);
- }
-
- if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot delimiter '@' is not expected here"));
- return (0);
- }
-
- if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing '@' delimiter in snapshot name"));
- return (0);
- }
-
- if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "bookmark delimiter '#' is not expected here"));
- return (0);
- }
-
- if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing '#' delimiter in bookmark name"));
- return (0);
- }
-
- if (modifying && strchr(path, '%') != NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid character %c in name"), '%');
- return (0);
- }
-
- return (-1);
-}
-
-int
-zfs_name_valid(const char *name, zfs_type_t type)
-{
- if (type == ZFS_TYPE_POOL)
- return (zpool_name_valid(NULL, B_FALSE, name));
- return (zfs_validate_name(NULL, name, type, B_FALSE));
-}
-
-/*
- * This function takes the raw DSL properties, and filters out the user-defined
- * properties into a separate nvlist.
- */
-static nvlist_t *
-process_user_props(zfs_handle_t *zhp, nvlist_t *props)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvpair_t *elem;
- nvlist_t *propval;
- nvlist_t *nvl;
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
- if (!zfs_prop_user(nvpair_name(elem)))
- continue;
-
- verify(nvpair_value_nvlist(elem, &propval) == 0);
- if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
- nvlist_free(nvl);
- (void) no_memory(hdl);
- return (NULL);
- }
- }
-
- return (nvl);
-}
-
-static zpool_handle_t *
-zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zpool_handle_t *zph;
-
- if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
- if (hdl->libzfs_pool_handles != NULL)
- zph->zpool_next = hdl->libzfs_pool_handles;
- hdl->libzfs_pool_handles = zph;
- }
- return (zph);
-}
-
-static zpool_handle_t *
-zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zpool_handle_t *zph = hdl->libzfs_pool_handles;
-
- while ((zph != NULL) &&
- (strncmp(pool_name, zpool_get_name(zph), len) != 0))
- zph = zph->zpool_next;
- return (zph);
-}
-
-/*
- * Returns a handle to the pool that contains the provided dataset.
- * If a handle to that pool already exists then that handle is returned.
- * Otherwise, a new handle is created and added to the list of handles.
- */
-static zpool_handle_t *
-zpool_handle(zfs_handle_t *zhp)
-{
- char *pool_name;
- int len;
- zpool_handle_t *zph;
-
- len = strcspn(zhp->zfs_name, "/@#") + 1;
- pool_name = zfs_alloc(zhp->zfs_hdl, len);
- (void) strlcpy(pool_name, zhp->zfs_name, len);
-
- zph = zpool_find_handle(zhp, pool_name, len);
- if (zph == NULL)
- zph = zpool_add_handle(zhp, pool_name);
-
- free(pool_name);
- return (zph);
-}
-
-void
-zpool_free_handles(libzfs_handle_t *hdl)
-{
- zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
-
- while (zph != NULL) {
- next = zph->zpool_next;
- zpool_close(zph);
- zph = next;
- }
- hdl->libzfs_pool_handles = NULL;
-}
-
-/*
- * Utility function to gather stats (objset and zpl) for the given object.
- */
-static int
-get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
- return (-1);
- }
- } else {
- return (-1);
- }
- }
- return (0);
-}
-
-/*
- * Utility function to get the received properties of the given object.
- */
-static int
-get_recvd_props_ioctl(zfs_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *recvdprops;
- zfs_cmd_t zc = { 0 };
- int err;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
- zcmd_free_nvlists(&zc);
- if (err != 0)
- return (-1);
-
- nvlist_free(zhp->zfs_recvd_props);
- zhp->zfs_recvd_props = recvdprops;
-
- return (0);
-}
-
-static int
-put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
-{
- nvlist_t *allprops, *userprops;
-
- zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
-
- if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
- return (-1);
- }
-
- /*
- * XXX Why do we store the user props separately, in addition to
- * storing them in zfs_props?
- */
- if ((userprops = process_user_props(zhp, allprops)) == NULL) {
- nvlist_free(allprops);
- return (-1);
- }
-
- nvlist_free(zhp->zfs_props);
- nvlist_free(zhp->zfs_user_props);
-
- zhp->zfs_props = allprops;
- zhp->zfs_user_props = userprops;
-
- return (0);
-}
-
-static int
-get_stats(zfs_handle_t *zhp)
-{
- int rc = 0;
- zfs_cmd_t zc = { 0 };
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
- if (get_stats_ioctl(zhp, &zc) != 0)
- rc = -1;
- else if (put_stats_zhdl(zhp, &zc) != 0)
- rc = -1;
- zcmd_free_nvlists(&zc);
- return (rc);
-}
-
-/*
- * Refresh the properties currently stored in the handle.
- */
-void
-zfs_refresh_properties(zfs_handle_t *zhp)
-{
- (void) get_stats(zhp);
-}
-
-/*
- * Makes a handle from the given dataset name. Used by zfs_open() and
- * zfs_iter_* to create child handles on the fly.
- */
-static int
-make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
-{
- if (put_stats_zhdl(zhp, zc) != 0)
- return (-1);
-
- /*
- * We've managed to open the dataset and gather statistics. Determine
- * the high-level type.
- */
- if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
- zhp->zfs_head_type = ZFS_TYPE_VOLUME;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
- zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER)
- return (-1);
- else
- abort();
-
- if (zhp->zfs_dmustats.dds_is_snapshot)
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
- zhp->zfs_type = ZFS_TYPE_VOLUME;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
- zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
- else
- abort(); /* we should never see any other types */
-
- if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL)
- return (-1);
-
- return (0);
-}
-
-zfs_handle_t *
-make_dataset_handle(libzfs_handle_t *hdl, const char *path)
-{
- zfs_cmd_t zc = { 0 };
-
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = hdl;
- (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
- free(zhp);
- return (NULL);
- }
- if (get_stats_ioctl(zhp, &zc) == -1) {
- zcmd_free_nvlists(&zc);
- free(zhp);
- return (NULL);
- }
- if (make_dataset_handle_common(zhp, &zc) == -1) {
- free(zhp);
- zhp = NULL;
- }
- zcmd_free_nvlists(&zc);
- return (zhp);
-}
-
-zfs_handle_t *
-make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = hdl;
- (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
- if (make_dataset_handle_common(zhp, zc) == -1) {
- free(zhp);
- return (NULL);
- }
- return (zhp);
-}
-
-zfs_handle_t *
-make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = pzhp->zfs_hdl;
- (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
- zhp->zfs_head_type = pzhp->zfs_type;
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- zhp->zpool_hdl = zpool_handle(zhp);
- return (zhp);
-}
-
-zfs_handle_t *
-zfs_handle_dup(zfs_handle_t *zhp_orig)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = zhp_orig->zfs_hdl;
- zhp->zpool_hdl = zhp_orig->zpool_hdl;
- (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
- sizeof (zhp->zfs_name));
- zhp->zfs_type = zhp_orig->zfs_type;
- zhp->zfs_head_type = zhp_orig->zfs_head_type;
- zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
- if (zhp_orig->zfs_props != NULL) {
- if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- zfs_close(zhp);
- return (NULL);
- }
- }
- if (zhp_orig->zfs_user_props != NULL) {
- if (nvlist_dup(zhp_orig->zfs_user_props,
- &zhp->zfs_user_props, 0) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- zfs_close(zhp);
- return (NULL);
- }
- }
- if (zhp_orig->zfs_recvd_props != NULL) {
- if (nvlist_dup(zhp_orig->zfs_recvd_props,
- &zhp->zfs_recvd_props, 0)) {
- (void) no_memory(zhp->zfs_hdl);
- zfs_close(zhp);
- return (NULL);
- }
- }
- zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
- if (zhp_orig->zfs_mntopts != NULL) {
- zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
- zhp_orig->zfs_mntopts);
- }
- zhp->zfs_props_table = zhp_orig->zfs_props_table;
- return (zhp);
-}
-
-boolean_t
-zfs_bookmark_exists(const char *path)
-{
- nvlist_t *bmarks;
- nvlist_t *props;
- char fsname[ZFS_MAX_DATASET_NAME_LEN];
- char *bmark_name;
- char *pound;
- int err;
- boolean_t rv;
-
-
- (void) strlcpy(fsname, path, sizeof (fsname));
- pound = strchr(fsname, '#');
- if (pound == NULL)
- return (B_FALSE);
-
- *pound = '\0';
- bmark_name = pound + 1;
- props = fnvlist_alloc();
- err = lzc_get_bookmarks(fsname, props, &bmarks);
- nvlist_free(props);
- if (err != 0) {
- nvlist_free(bmarks);
- return (B_FALSE);
- }
-
- rv = nvlist_exists(bmarks, bmark_name);
- nvlist_free(bmarks);
- return (rv);
-}
-
-zfs_handle_t *
-make_bookmark_handle(zfs_handle_t *parent, const char *path,
- nvlist_t *bmark_props)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- /* Fill in the name. */
- zhp->zfs_hdl = parent->zfs_hdl;
- (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
-
- /* Set the property lists. */
- if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) {
- free(zhp);
- return (NULL);
- }
-
- /* Set the types. */
- zhp->zfs_head_type = parent->zfs_head_type;
- zhp->zfs_type = ZFS_TYPE_BOOKMARK;
-
- if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) {
- nvlist_free(zhp->zfs_props);
- free(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-struct zfs_open_bookmarks_cb_data {
- const char *path;
- zfs_handle_t *zhp;
-};
-
-static int
-zfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data)
-{
- struct zfs_open_bookmarks_cb_data *dp = data;
-
- /*
- * Is it the one we are looking for?
- */
- if (strcmp(dp->path, zfs_get_name(zhp)) == 0) {
- /*
- * We found it. Save it and let the caller know we are done.
- */
- dp->zhp = zhp;
- return (EEXIST);
- }
-
- /*
- * Not found. Close the handle and ask for another one.
- */
- zfs_close(zhp);
- return (0);
-}
-
-/*
- * Opens the given snapshot, bookmark, filesystem, or volume. The 'types'
- * argument is a mask of acceptable types. The function will print an
- * appropriate error message and return NULL if it can't be opened.
- */
-zfs_handle_t *
-zfs_open(libzfs_handle_t *hdl, const char *path, int types)
-{
- zfs_handle_t *zhp;
- char errbuf[1024];
- char *bookp;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
-
- /*
- * Validate the name before we even try to open it.
- */
- if (!zfs_validate_name(hdl, path, types, B_FALSE)) {
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- return (NULL);
- }
-
- /*
- * Bookmarks needs to be handled separately.
- */
- bookp = strchr(path, '#');
- if (bookp == NULL) {
- /*
- * Try to get stats for the dataset, which will tell us if it
- * exists.
- */
- errno = 0;
- if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
- }
- } else {
- char dsname[ZFS_MAX_DATASET_NAME_LEN];
- zfs_handle_t *pzhp;
- struct zfs_open_bookmarks_cb_data cb_data = {path, NULL};
-
- /*
- * We need to cut out '#' and everything after '#'
- * to get the parent dataset name only.
- */
- assert(bookp - path < sizeof (dsname));
- (void) strncpy(dsname, path, bookp - path);
- dsname[bookp - path] = '\0';
-
- /*
- * Create handle for the parent dataset.
- */
- errno = 0;
- if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
- }
-
- /*
- * Iterate bookmarks to find the right one.
- */
- errno = 0;
- if ((zfs_iter_bookmarks(pzhp, zfs_open_bookmarks_cb,
- &cb_data) == 0) && (cb_data.zhp == NULL)) {
- (void) zfs_error(hdl, EZFS_NOENT, errbuf);
- zfs_close(pzhp);
- return (NULL);
- }
- if (cb_data.zhp == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- zfs_close(pzhp);
- return (NULL);
- }
- zhp = cb_data.zhp;
-
- /*
- * Cleanup.
- */
- zfs_close(pzhp);
- }
-
- if (zhp == NULL) {
- char *at = strchr(path, '@');
-
- if (at != NULL)
- *at = '\0';
- errno = 0;
- if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
- }
- if (at != NULL)
- *at = '@';
- (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- }
-
- if (!(types & zhp->zfs_type)) {
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- zfs_close(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-/*
- * Release a ZFS handle. Nothing to do but free the associated memory.
- */
-void
-zfs_close(zfs_handle_t *zhp)
-{
- if (zhp->zfs_mntopts)
- free(zhp->zfs_mntopts);
- nvlist_free(zhp->zfs_props);
- nvlist_free(zhp->zfs_user_props);
- nvlist_free(zhp->zfs_recvd_props);
- free(zhp);
-}
-
-typedef struct mnttab_node {
- struct mnttab mtn_mt;
- avl_node_t mtn_node;
-} mnttab_node_t;
-
-static int
-libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
-{
- const mnttab_node_t *mtn1 = (const mnttab_node_t *)arg1;
- const mnttab_node_t *mtn2 = (const mnttab_node_t *)arg2;
- int rv;
-
- rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
-
- return (AVL_ISIGN(rv));
-}
-
-void
-libzfs_mnttab_init(libzfs_handle_t *hdl)
-{
- pthread_mutex_init(&hdl->libzfs_mnttab_cache_lock, NULL);
- assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
- avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
- sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
-}
-
-void
-libzfs_mnttab_update(libzfs_handle_t *hdl)
-{
- struct mnttab entry;
-
- rewind(hdl->libzfs_mnttab);
- while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
- mnttab_node_t *mtn;
-
- if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
- continue;
- mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
- mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
- mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
- mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
- mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
- avl_add(&hdl->libzfs_mnttab_cache, mtn);
- }
-}
-
-void
-libzfs_mnttab_fini(libzfs_handle_t *hdl)
-{
- void *cookie = NULL;
- mnttab_node_t *mtn;
-
- while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))
- != NULL) {
- free(mtn->mtn_mt.mnt_special);
- free(mtn->mtn_mt.mnt_mountp);
- free(mtn->mtn_mt.mnt_fstype);
- free(mtn->mtn_mt.mnt_mntopts);
- free(mtn);
- }
- avl_destroy(&hdl->libzfs_mnttab_cache);
- (void) pthread_mutex_destroy(&hdl->libzfs_mnttab_cache_lock);
-}
-
-void
-libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
-{
- hdl->libzfs_mnttab_enable = enable;
-}
-
-int
-libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
- struct mnttab *entry)
-{
- mnttab_node_t find;
- mnttab_node_t *mtn;
- int ret = ENOENT;
-
- if (!hdl->libzfs_mnttab_enable) {
- struct mnttab srch = { 0 };
-
- if (avl_numnodes(&hdl->libzfs_mnttab_cache))
- libzfs_mnttab_fini(hdl);
- rewind(hdl->libzfs_mnttab);
- srch.mnt_special = (char *)fsname;
- srch.mnt_fstype = MNTTYPE_ZFS;
- if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
- return (0);
- else
- return (ENOENT);
- }
-
- pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
- if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
- libzfs_mnttab_update(hdl);
-
- find.mtn_mt.mnt_special = (char *)fsname;
- mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
- if (mtn) {
- *entry = mtn->mtn_mt;
- ret = 0;
- }
- pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
- return (ret);
-}
-
-void
-libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
- const char *mountp, const char *mntopts)
-{
- mnttab_node_t *mtn;
-
- pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
- if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) {
- mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
- mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
- mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
- mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
- mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
- avl_add(&hdl->libzfs_mnttab_cache, mtn);
- }
- pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
-}
-
-void
-libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
-{
- mnttab_node_t find;
- mnttab_node_t *ret;
-
- pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
- find.mtn_mt.mnt_special = (char *)fsname;
- if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))
- != NULL) {
- avl_remove(&hdl->libzfs_mnttab_cache, ret);
- free(ret->mtn_mt.mnt_special);
- free(ret->mtn_mt.mnt_mountp);
- free(ret->mtn_mt.mnt_fstype);
- free(ret->mtn_mt.mnt_mntopts);
- free(ret);
- }
- pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
-}
-
-int
-zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
-{
- zpool_handle_t *zpool_handle = zhp->zpool_hdl;
-
- if (zpool_handle == NULL)
- return (-1);
-
- *spa_version = zpool_get_prop_int(zpool_handle,
- ZPOOL_PROP_VERSION, NULL);
- return (0);
-}
-
-/*
- * The choice of reservation property depends on the SPA version.
- */
-static int
-zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
-{
- int spa_version;
-
- if (zfs_spa_version(zhp, &spa_version) < 0)
- return (-1);
-
- if (spa_version >= SPA_VERSION_REFRESERVATION)
- *resv_prop = ZFS_PROP_REFRESERVATION;
- else
- *resv_prop = ZFS_PROP_RESERVATION;
-
- return (0);
-}
-
-/*
- * Given an nvlist of properties to set, validates that they are correct, and
- * parses any numeric properties (index, boolean, etc) if they are specified as
- * strings.
- */
-nvlist_t *
-zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
- uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,
- const char *errbuf)
-{
- nvpair_t *elem;
- uint64_t intval;
- char *strval;
- zfs_prop_t prop;
- nvlist_t *ret;
- int chosen_normal = -1;
- int chosen_utf = -1;
-
- if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- /*
- * Make sure this property is valid and applies to this type.
- */
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
- const char *propname = nvpair_name(elem);
-
- prop = zfs_name_to_prop(propname);
- if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
- /*
- * This is a user property: make sure it's a
- * string, and that it's less than ZAP_MAXNAMELEN.
- */
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property name '%s' is too long"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- (void) nvpair_value_string(elem, &strval);
- if (nvlist_add_string(ret, propname, strval) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- continue;
- }
-
- /*
- * Currently, only user properties can be modified on
- * snapshots.
- */
- if (type == ZFS_TYPE_SNAPSHOT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "this property can not be modified for snapshots"));
- (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
- goto error;
- }
-
- if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
- zfs_userquota_prop_t uqtype;
- char newpropname[128];
- char domain[128];
- uint64_t rid;
- uint64_t valary[3];
-
- if (userquota_propname_decode(propname, zoned,
- &uqtype, domain, sizeof (domain), &rid) != 0) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "'%s' has an invalid user/group name"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (uqtype != ZFS_PROP_USERQUOTA &&
- uqtype != ZFS_PROP_GROUPQUOTA) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "'%s' is readonly"),
- propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY,
- errbuf);
- goto error;
- }
-
- if (nvpair_type(elem) == DATA_TYPE_STRING) {
- (void) nvpair_value_string(elem, &strval);
- if (strcmp(strval, "none") == 0) {
- intval = 0;
- } else if (zfs_nicestrtonum(hdl,
- strval, &intval) != 0) {
- (void) zfs_error(hdl,
- EZFS_BADPROP, errbuf);
- goto error;
- }
- } else if (nvpair_type(elem) ==
- DATA_TYPE_UINT64) {
- (void) nvpair_value_uint64(elem, &intval);
- if (intval == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "use 'none' to disable "
- "userquota/groupquota"));
- goto error;
- }
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a number"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- /*
- * Encode the prop name as
- * userquota@<hex-rid>-domain, to make it easy
- * for the kernel to decode.
- */
- (void) snprintf(newpropname, sizeof (newpropname),
- "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype],
- (longlong_t)rid, domain);
- valary[0] = uqtype;
- valary[1] = rid;
- valary[2] = intval;
- if (nvlist_add_uint64_array(ret, newpropname,
- valary, 3) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- continue;
- } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is readonly"),
- propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
- goto error;
- }
-
- if (prop == ZPROP_INVAL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (!zfs_prop_valid_for_type(prop, type)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "'%s' does not "
- "apply to datasets of this type"), propname);
- (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
- goto error;
- }
-
- if (zfs_prop_readonly(prop) &&
- (!zfs_prop_setonce(prop) || zhp != NULL)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "'%s' is readonly"),
- propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
- goto error;
- }
-
- if (zprop_parse_value(hdl, elem, prop, type, ret,
- &strval, &intval, errbuf) != 0)
- goto error;
-
- /*
- * Perform some additional checks for specific properties.
- */
- switch (prop) {
- case ZFS_PROP_VERSION:
- {
- int version;
-
- if (zhp == NULL)
- break;
- version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
- if (intval < version) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Can not downgrade; already at version %u"),
- version);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
- }
-
- case ZFS_PROP_VOLBLOCKSIZE:
- case ZFS_PROP_RECORDSIZE:
- {
- int maxbs = SPA_MAXBLOCKSIZE;
- if (zpool_hdl != NULL) {
- maxbs = zpool_get_prop_int(zpool_hdl,
- ZPOOL_PROP_MAXBLOCKSIZE, NULL);
- }
- /*
- * Volumes are limited to a volblocksize of 128KB,
- * because they typically service workloads with
- * small random writes, which incur a large performance
- * penalty with large blocks.
- */
- if (prop == ZFS_PROP_VOLBLOCKSIZE)
- maxbs = SPA_OLD_MAXBLOCKSIZE;
- /*
- * The value must be a power of two between
- * SPA_MINBLOCKSIZE and maxbs.
- */
- if (intval < SPA_MINBLOCKSIZE ||
- intval > maxbs || !ISP2(intval)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be power of 2 from 512B "
- "to %uKB"), propname, maxbs >> 10);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
- }
-
- case ZFS_PROP_SPECIAL_SMALL_BLOCKS:
- if (zpool_hdl != NULL) {
- char state[64] = "";
-
- /*
- * Issue a warning but do not fail so that
- * tests for setable properties succeed.
- */
- if (zpool_prop_get_feature(zpool_hdl,
- "feature@allocation_classes", state,
- sizeof (state)) != 0 ||
- strcmp(state, ZFS_FEATURE_ACTIVE) != 0) {
- (void) fprintf(stderr, gettext(
- "%s: property requires a special "
- "device in the pool\n"), propname);
- }
- }
- if (intval != 0 &&
- (intval < SPA_MINBLOCKSIZE ||
- intval > SPA_OLD_MAXBLOCKSIZE || !ISP2(intval))) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid '%s=%d' property: must be zero or "
- "a power of 2 from 512B to 128K"), propname,
- intval);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZFS_PROP_MLSLABEL:
- {
-#ifdef illumos
- /*
- * Verify the mlslabel string and convert to
- * internal hex label string.
- */
-
- m_label_t *new_sl;
- char *hex = NULL; /* internal label string */
-
- /* Default value is already OK. */
- if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
- break;
-
- /* Verify the label can be converted to binary form */
- if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||
- (str_to_label(strval, &new_sl, MAC_LABEL,
- L_NO_CORRECTION, NULL) == -1)) {
- goto badlabel;
- }
-
- /* Now translate to hex internal label string */
- if (label_to_str(new_sl, &hex, M_INTERNAL,
- DEF_NAMES) != 0) {
- if (hex)
- free(hex);
- goto badlabel;
- }
- m_label_free(new_sl);
-
- /* If string is already in internal form, we're done. */
- if (strcmp(strval, hex) == 0) {
- free(hex);
- break;
- }
-
- /* Replace the label string with the internal form. */
- (void) nvlist_remove(ret, zfs_prop_to_name(prop),
- DATA_TYPE_STRING);
- verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
- hex) == 0);
- free(hex);
-
- break;
-
-badlabel:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid mlslabel '%s'"), strval);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- m_label_free(new_sl); /* OK if null */
-#else /* !illumos */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "mlslabel is not supported on FreeBSD"));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-#endif /* illumos */
- goto error;
-
- }
-
- case ZFS_PROP_MOUNTPOINT:
- {
- namecheck_err_t why;
-
- if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
- strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
- break;
-
- if (mountpoint_namecheck(strval, &why)) {
- switch (why) {
- case NAME_ERR_LEADING_SLASH:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "'%s' must be an absolute path, "
- "'none', or 'legacy'"), propname);
- break;
- case NAME_ERR_TOOLONG:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "component of '%s' is too long"),
- propname);
- break;
-
- default:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "(%d) not defined"),
- why);
- break;
- }
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- }
-
- /*FALLTHRU*/
-
- case ZFS_PROP_SHARESMB:
- case ZFS_PROP_SHARENFS:
- /*
- * For the mountpoint and sharenfs or sharesmb
- * properties, check if it can be set in a
- * global/non-global zone based on
- * the zoned property value:
- *
- * global zone non-global zone
- * --------------------------------------------------
- * zoned=on mountpoint (no) mountpoint (yes)
- * sharenfs (no) sharenfs (no)
- * sharesmb (no) sharesmb (no)
- *
- * zoned=off mountpoint (yes) N/A
- * sharenfs (yes)
- * sharesmb (yes)
- */
- if (zoned) {
- if (getzoneid() == GLOBAL_ZONEID) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set on "
- "dataset in a non-global zone"),
- propname);
- (void) zfs_error(hdl, EZFS_ZONED,
- errbuf);
- goto error;
- } else if (prop == ZFS_PROP_SHARENFS ||
- prop == ZFS_PROP_SHARESMB) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set in "
- "a non-global zone"), propname);
- (void) zfs_error(hdl, EZFS_ZONED,
- errbuf);
- goto error;
- }
- } else if (getzoneid() != GLOBAL_ZONEID) {
- /*
- * If zoned property is 'off', this must be in
- * a global zone. If not, something is wrong.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set while dataset "
- "'zoned' property is set"), propname);
- (void) zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
-
- /*
- * At this point, it is legitimate to set the
- * property. Now we want to make sure that the
- * property value is valid if it is sharenfs.
- */
- if ((prop == ZFS_PROP_SHARENFS ||
- prop == ZFS_PROP_SHARESMB) &&
- strcmp(strval, "on") != 0 &&
- strcmp(strval, "off") != 0) {
- zfs_share_proto_t proto;
-
- if (prop == ZFS_PROP_SHARESMB)
- proto = PROTO_SMB;
- else
- proto = PROTO_NFS;
-
- /*
- * Must be an valid sharing protocol
- * option string so init the libshare
- * in order to enable the parser and
- * then parse the options. We use the
- * control API since we don't care about
- * the current configuration and don't
- * want the overhead of loading it
- * until we actually do something.
- */
-
- if (zfs_init_libshare(hdl,
- SA_INIT_CONTROL_API) != SA_OK) {
- /*
- * An error occurred so we can't do
- * anything
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set: problem "
- "in share initialization"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
-
- if (zfs_parse_options(strval, proto) != SA_OK) {
- /*
- * There was an error in parsing so
- * deal with it by issuing an error
- * message and leaving after
- * uninitializing the the libshare
- * interface.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set to invalid "
- "options"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- zfs_uninit_libshare(hdl);
- goto error;
- }
- zfs_uninit_libshare(hdl);
- }
-
- break;
-
- case ZFS_PROP_UTF8ONLY:
- chosen_utf = (int)intval;
- break;
-
- case ZFS_PROP_NORMALIZE:
- chosen_normal = (int)intval;
- break;
-
- default:
- break;
- }
-
- /*
- * For changes to existing volumes, we have some additional
- * checks to enforce.
- */
- if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
- uint64_t volsize = zfs_prop_get_int(zhp,
- ZFS_PROP_VOLSIZE);
- uint64_t blocksize = zfs_prop_get_int(zhp,
- ZFS_PROP_VOLBLOCKSIZE);
- char buf[64];
-
- switch (prop) {
- case ZFS_PROP_RESERVATION:
- if (intval > volsize) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is greater than current "
- "volume size"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- break;
-
- case ZFS_PROP_REFRESERVATION:
- if (intval > volsize && intval != UINT64_MAX) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is greater than current "
- "volume size"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- break;
-
- case ZFS_PROP_VOLSIZE:
- if (intval % blocksize != 0) {
- zfs_nicenum(blocksize, buf,
- sizeof (buf));
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a multiple of "
- "volume block size (%s)"),
- propname, buf);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
-
- if (intval == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be zero"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- break;
-
- default:
- break;
- }
- }
- }
-
- /*
- * If normalization was chosen, but no UTF8 choice was made,
- * enforce rejection of non-UTF8 names.
- *
- * If normalization was chosen, but rejecting non-UTF8 names
- * was explicitly not chosen, it is an error.
- */
- if (chosen_normal > 0 && chosen_utf < 0) {
- if (nvlist_add_uint64(ret,
- zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- } else if (chosen_normal > 0 && chosen_utf == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be set 'on' if normalization chosen"),
- zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- return (ret);
-
-error:
- nvlist_free(ret);
- return (NULL);
-}
-
-int
-zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
-{
- uint64_t old_volsize;
- uint64_t new_volsize;
- uint64_t old_reservation;
- uint64_t new_reservation;
- zfs_prop_t resv_prop;
- nvlist_t *props;
-
- /*
- * If this is an existing volume, and someone is setting the volsize,
- * make sure that it matches the reservation, or add it if necessary.
- */
- old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
- return (-1);
- old_reservation = zfs_prop_get_int(zhp, resv_prop);
-
- props = fnvlist_alloc();
- fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
-
- if ((zvol_volsize_to_reservation(old_volsize, props) !=
- old_reservation) || nvlist_exists(nvl,
- zfs_prop_to_name(resv_prop))) {
- fnvlist_free(props);
- return (0);
- }
- if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
- &new_volsize) != 0) {
- fnvlist_free(props);
- return (-1);
- }
- new_reservation = zvol_volsize_to_reservation(new_volsize, props);
- fnvlist_free(props);
-
- if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
- new_reservation) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- return (-1);
- }
- return (1);
-}
-
-/*
- * Helper for 'zfs {set|clone} refreservation=auto'. Must be called after
- * zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinal value.
- * Return codes must match zfs_add_synthetic_resv().
- */
-static int
-zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl)
-{
- uint64_t volsize;
- uint64_t resvsize;
- zfs_prop_t prop;
- nvlist_t *props;
-
- if (!ZFS_IS_VOLUME(zhp)) {
- return (0);
- }
-
- if (zfs_which_resv_prop(zhp, &prop) != 0) {
- return (-1);
- }
-
- if (prop != ZFS_PROP_REFRESERVATION) {
- return (0);
- }
-
- if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(prop), &resvsize) != 0) {
- /* No value being set, so it can't be "auto" */
- return (0);
- }
- if (resvsize != UINT64_MAX) {
- /* Being set to a value other than "auto" */
- return (0);
- }
-
- props = fnvlist_alloc();
-
- fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
-
- if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
- &volsize) != 0) {
- volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- }
-
- resvsize = zvol_volsize_to_reservation(volsize, props);
- fnvlist_free(props);
-
- (void) nvlist_remove_all(nvl, zfs_prop_to_name(prop));
- if (nvlist_add_uint64(nvl, zfs_prop_to_name(prop), resvsize) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- return (-1);
- }
- return (1);
-}
-
-void
-zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
- char *errbuf)
-{
- switch (err) {
-
- case ENOSPC:
- /*
- * For quotas and reservations, ENOSPC indicates
- * something different; setting a quota or reservation
- * doesn't use any disk space.
- */
- switch (prop) {
- case ZFS_PROP_QUOTA:
- case ZFS_PROP_REFQUOTA:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "size is less than current used or "
- "reserved space"));
- (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
- break;
-
- case ZFS_PROP_RESERVATION:
- case ZFS_PROP_REFRESERVATION:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "size is greater than available space"));
- (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
- break;
-
- default:
- (void) zfs_standard_error(hdl, err, errbuf);
- break;
- }
- break;
-
- case EBUSY:
- (void) zfs_standard_error(hdl, EBUSY, errbuf);
- break;
-
- case EROFS:
- (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
- break;
-
- case E2BIG:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property value too long"));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- break;
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool and or dataset must be upgraded to set this "
- "property or value"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
-
- case ERANGE:
- case EDOM:
- if (prop == ZFS_PROP_COMPRESSION ||
- prop == ZFS_PROP_RECORDSIZE) {
- (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property setting is not allowed on "
- "bootable datasets"));
- (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
- } else if (prop == ZFS_PROP_CHECKSUM ||
- prop == ZFS_PROP_DEDUP) {
- (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property setting is not allowed on "
- "root pools"));
- (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
- } else {
- (void) zfs_standard_error(hdl, err, errbuf);
- }
- break;
-
- case EINVAL:
- if (prop == ZPROP_INVAL) {
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- } else {
- (void) zfs_standard_error(hdl, err, errbuf);
- }
- break;
-
- case EOVERFLOW:
- /*
- * This platform can't address a volume this big.
- */
-#ifdef _ILP32
- if (prop == ZFS_PROP_VOLSIZE) {
- (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
- break;
- }
-#endif
- /* FALLTHROUGH */
- default:
- (void) zfs_standard_error(hdl, err, errbuf);
- }
-}
-
-/*
- * Given a property name and value, set the property for the given dataset.
- */
-int
-zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
-{
- int ret = -1;
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *nvl = NULL;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zfs_name);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
- nvlist_add_string(nvl, propname, propval) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
-
- ret = zfs_prop_set_list(zhp, nvl);
-
-error:
- nvlist_free(nvl);
- return (ret);
-}
-
-
-
-/*
- * Given an nvlist of property names and values, set the properties for the
- * given dataset.
- */
-int
-zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
-{
- zfs_cmd_t zc = { 0 };
- int ret = -1;
- prop_changelist_t **cls = NULL;
- int cl_idx;
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *nvl;
- int nvl_len;
- int added_resv = 0;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zfs_name);
-
- if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
- errbuf)) == NULL)
- goto error;
-
- /*
- * We have to check for any extra properties which need to be added
- * before computing the length of the nvlist.
- */
- for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(nvl, elem)) {
- if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE &&
- (added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) {
- goto error;
- }
- }
-
- if (added_resv != 1 &&
- (added_resv = zfs_fix_auto_resv(zhp, nvl)) == -1) {
- goto error;
- }
-
- /*
- * Check how many properties we're setting and allocate an array to
- * store changelist pointers for postfix().
- */
- nvl_len = 0;
- for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(nvl, elem))
- nvl_len++;
- if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL)
- goto error;
-
- cl_idx = 0;
- for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(nvl, elem)) {
-
- zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem));
-
- assert(cl_idx < nvl_len);
- /*
- * We don't want to unmount & remount the dataset when changing
- * its canmount property to 'on' or 'noauto'. We only use
- * the changelist logic to unmount when setting canmount=off.
- */
- if (prop != ZFS_PROP_CANMOUNT ||
- (fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF &&
- zfs_is_mounted(zhp, NULL))) {
- cls[cl_idx] = changelist_gather(zhp, prop, 0, 0);
- if (cls[cl_idx] == NULL)
- goto error;
- }
-
- if (prop == ZFS_PROP_MOUNTPOINT &&
- changelist_haszonedchild(cls[cl_idx])) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- ret = zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
-
- /* We don't support those properties on FreeBSD. */
- switch (prop) {
- case ZFS_PROP_DEVICES:
- case ZFS_PROP_ISCSIOPTIONS:
- case ZFS_PROP_XATTR:
- case ZFS_PROP_VSCAN:
- case ZFS_PROP_NBMAND:
- case ZFS_PROP_MLSLABEL:
- (void) snprintf(errbuf, sizeof (errbuf),
- "property '%s' not supported on FreeBSD",
- nvpair_name(elem));
- ret = zfs_error(hdl, EZFS_PERM, errbuf);
- goto error;
- }
-
- if (cls[cl_idx] != NULL &&
- (ret = changelist_prefix(cls[cl_idx])) != 0)
- goto error;
-
- cl_idx++;
- }
- assert(cl_idx == nvl_len);
-
- /*
- * Execute the corresponding ioctl() to set this list of properties.
- */
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 ||
- (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0)
- goto error;
-
- ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
-
- if (ret != 0) {
- if (zc.zc_nvlist_dst_filled == B_FALSE) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- goto error;
- }
-
- /* Get the list of unset properties back and report them. */
- nvlist_t *errorprops = NULL;
- if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0)
- goto error;
- for (nvpair_t *elem = nvlist_next_nvpair(errorprops, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errorprops, elem)) {
- zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem));
- zfs_setprop_error(hdl, prop, errno, errbuf);
- }
- nvlist_free(errorprops);
-
- if (added_resv && errno == ENOSPC) {
- /* clean up the volsize property we tried to set */
- uint64_t old_volsize = zfs_prop_get_int(zhp,
- ZFS_PROP_VOLSIZE);
- nvlist_free(nvl);
- nvl = NULL;
- zcmd_free_nvlists(&zc);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
- goto error;
- if (nvlist_add_uint64(nvl,
- zfs_prop_to_name(ZFS_PROP_VOLSIZE),
- old_volsize) != 0)
- goto error;
- if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
- goto error;
- (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
- }
- } else {
- for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {
- if (cls[cl_idx] != NULL) {
- int clp_err = changelist_postfix(cls[cl_idx]);
- if (clp_err != 0)
- ret = clp_err;
- }
- }
-
- /*
- * Refresh the statistics so the new property value
- * is reflected.
- */
- if (ret == 0)
- (void) get_stats(zhp);
- }
-
-error:
- nvlist_free(nvl);
- zcmd_free_nvlists(&zc);
- if (cls != NULL) {
- for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {
- if (cls[cl_idx] != NULL)
- changelist_free(cls[cl_idx]);
- }
- free(cls);
- }
- return (ret);
-}
-
-/*
- * Given a property, inherit the value from the parent dataset, or if received
- * is TRUE, revert to the received value, if any.
- */
-int
-zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
-{
- zfs_cmd_t zc = { 0 };
- int ret;
- prop_changelist_t *cl;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
- zfs_prop_t prop;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
-
- zc.zc_cookie = received;
- if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
- /*
- * For user properties, the amount of work we have to do is very
- * small, so just do it here.
- */
- if (!zfs_prop_user(propname)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
-
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
- return (zfs_standard_error(hdl, errno, errbuf));
-
- return (0);
- }
-
- /*
- * Verify that this property is inheritable.
- */
- if (zfs_prop_readonly(prop))
- return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
-
- if (!zfs_prop_inheritable(prop) && !received)
- return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
-
- /*
- * Check to see if the value applies to this type
- */
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
- return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
-
- /*
- * Normalize the name, to get rid of shorthand abbreviations.
- */
- propname = zfs_prop_to_name(prop);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
-
- if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset is used in a non-global zone"));
- return (zfs_error(hdl, EZFS_ZONED, errbuf));
- }
-
- /*
- * Determine datasets which will be affected by this change, if any.
- */
- if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
- return (-1);
-
- if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- ret = zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
-
- if ((ret = changelist_prefix(cl)) != 0)
- goto error;
-
- if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
- return (zfs_standard_error(hdl, errno, errbuf));
- } else {
-
- if ((ret = changelist_postfix(cl)) != 0)
- goto error;
-
- /*
- * Refresh the statistics so the new property is reflected.
- */
- (void) get_stats(zhp);
- }
-
-error:
- changelist_free(cl);
- return (ret);
-}
-
-/*
- * True DSL properties are stored in an nvlist. The following two functions
- * extract them appropriately.
- */
-static uint64_t
-getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
-{
- nvlist_t *nv;
- uint64_t value;
-
- *source = NULL;
- if (nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
- (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
- } else {
- verify(!zhp->zfs_props_table ||
- zhp->zfs_props_table[prop] == B_TRUE);
- value = zfs_prop_default_numeric(prop);
- *source = "";
- }
-
- return (value);
-}
-
-static const char *
-getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
-{
- nvlist_t *nv;
- const char *value;
-
- *source = NULL;
- if (nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(prop), &nv) == 0) {
- value = fnvlist_lookup_string(nv, ZPROP_VALUE);
- (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
- } else {
- verify(!zhp->zfs_props_table ||
- zhp->zfs_props_table[prop] == B_TRUE);
- value = zfs_prop_default_string(prop);
- *source = "";
- }
-
- return (value);
-}
-
-static boolean_t
-zfs_is_recvd_props_mode(zfs_handle_t *zhp)
-{
- return (zhp->zfs_props == zhp->zfs_recvd_props);
-}
-
-static void
-zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
-{
- *cookie = (uint64_t)(uintptr_t)zhp->zfs_props;
- zhp->zfs_props = zhp->zfs_recvd_props;
-}
-
-static void
-zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
-{
- zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie;
- *cookie = 0;
-}
-
-/*
- * Internal function for getting a numeric property. Both zfs_prop_get() and
- * zfs_prop_get_int() are built using this interface.
- *
- * Certain properties can be overridden using 'mount -o'. In this case, scan
- * the contents of the /etc/mnttab entry, searching for the appropriate options.
- * If they differ from the on-disk values, report the current values and mark
- * the source "temporary".
- */
-static int
-get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
- char **source, uint64_t *val)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *zplprops = NULL;
- struct mnttab mnt;
- char *mntopt_on = NULL;
- char *mntopt_off = NULL;
- boolean_t received = zfs_is_recvd_props_mode(zhp);
-
- *source = NULL;
-
- switch (prop) {
- case ZFS_PROP_ATIME:
- mntopt_on = MNTOPT_ATIME;
- mntopt_off = MNTOPT_NOATIME;
- break;
-
- case ZFS_PROP_DEVICES:
- mntopt_on = MNTOPT_DEVICES;
- mntopt_off = MNTOPT_NODEVICES;
- break;
-
- case ZFS_PROP_EXEC:
- mntopt_on = MNTOPT_EXEC;
- mntopt_off = MNTOPT_NOEXEC;
- break;
-
- case ZFS_PROP_READONLY:
- mntopt_on = MNTOPT_RO;
- mntopt_off = MNTOPT_RW;
- break;
-
- case ZFS_PROP_SETUID:
- mntopt_on = MNTOPT_SETUID;
- mntopt_off = MNTOPT_NOSETUID;
- break;
-
- case ZFS_PROP_XATTR:
- mntopt_on = MNTOPT_XATTR;
- mntopt_off = MNTOPT_NOXATTR;
- break;
-
- case ZFS_PROP_NBMAND:
- mntopt_on = MNTOPT_NBMAND;
- mntopt_off = MNTOPT_NONBMAND;
- break;
-
- default:
- break;
- }
-
- /*
- * Because looking up the mount options is potentially expensive
- * (iterating over all of /etc/mnttab), we defer its calculation until
- * we're looking up a property which requires its presence.
- */
- if (!zhp->zfs_mntcheck &&
- (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- struct mnttab entry;
-
- if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
- zhp->zfs_mntopts = zfs_strdup(hdl,
- entry.mnt_mntopts);
- if (zhp->zfs_mntopts == NULL)
- return (-1);
- }
-
- zhp->zfs_mntcheck = B_TRUE;
- }
-
- if (zhp->zfs_mntopts == NULL)
- mnt.mnt_mntopts = "";
- else
- mnt.mnt_mntopts = zhp->zfs_mntopts;
-
- switch (prop) {
- case ZFS_PROP_ATIME:
- case ZFS_PROP_DEVICES:
- case ZFS_PROP_EXEC:
- case ZFS_PROP_READONLY:
- case ZFS_PROP_SETUID:
- case ZFS_PROP_XATTR:
- case ZFS_PROP_NBMAND:
- *val = getprop_uint64(zhp, prop, source);
-
- if (received)
- break;
-
- if (hasmntopt(&mnt, mntopt_on) && !*val) {
- *val = B_TRUE;
- if (src)
- *src = ZPROP_SRC_TEMPORARY;
- } else if (hasmntopt(&mnt, mntopt_off) && *val) {
- *val = B_FALSE;
- if (src)
- *src = ZPROP_SRC_TEMPORARY;
- }
- break;
-
- case ZFS_PROP_CANMOUNT:
- case ZFS_PROP_VOLSIZE:
- case ZFS_PROP_QUOTA:
- case ZFS_PROP_REFQUOTA:
- case ZFS_PROP_RESERVATION:
- case ZFS_PROP_REFRESERVATION:
- case ZFS_PROP_FILESYSTEM_LIMIT:
- case ZFS_PROP_SNAPSHOT_LIMIT:
- case ZFS_PROP_FILESYSTEM_COUNT:
- case ZFS_PROP_SNAPSHOT_COUNT:
- *val = getprop_uint64(zhp, prop, source);
-
- if (*source == NULL) {
- /* not default, must be local */
- *source = zhp->zfs_name;
- }
- break;
-
- case ZFS_PROP_MOUNTED:
- *val = (zhp->zfs_mntopts != NULL);
- break;
-
- case ZFS_PROP_NUMCLONES:
- *val = zhp->zfs_dmustats.dds_num_clones;
- break;
-
- case ZFS_PROP_VERSION:
- case ZFS_PROP_NORMALIZE:
- case ZFS_PROP_UTF8ONLY:
- case ZFS_PROP_CASE:
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
- zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
- nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
- val) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- nvlist_free(zplprops);
- zcmd_free_nvlists(&zc);
- break;
-
- case ZFS_PROP_INCONSISTENT:
- *val = zhp->zfs_dmustats.dds_inconsistent;
- break;
-
- default:
- switch (zfs_prop_get_type(prop)) {
- case PROP_TYPE_NUMBER:
- case PROP_TYPE_INDEX:
- *val = getprop_uint64(zhp, prop, source);
- /*
- * If we tried to use a default value for a
- * readonly property, it means that it was not
- * present. Note this only applies to "truly"
- * readonly properties, not set-once properties
- * like volblocksize.
- */
- if (zfs_prop_readonly(prop) &&
- !zfs_prop_setonce(prop) &&
- *source != NULL && (*source)[0] == '\0') {
- *source = NULL;
- return (-1);
- }
- break;
-
- case PROP_TYPE_STRING:
- default:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "cannot get non-numeric property"));
- return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "internal error")));
- }
- }
-
- return (0);
-}
-
-/*
- * Calculate the source type, given the raw source string.
- */
-static void
-get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
- char *statbuf, size_t statlen)
-{
- if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
- return;
-
- if (source == NULL) {
- *srctype = ZPROP_SRC_NONE;
- } else if (source[0] == '\0') {
- *srctype = ZPROP_SRC_DEFAULT;
- } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) {
- *srctype = ZPROP_SRC_RECEIVED;
- } else {
- if (strcmp(source, zhp->zfs_name) == 0) {
- *srctype = ZPROP_SRC_LOCAL;
- } else {
- (void) strlcpy(statbuf, source, statlen);
- *srctype = ZPROP_SRC_INHERITED;
- }
- }
-
-}
-
-int
-zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
- size_t proplen, boolean_t literal)
-{
- zfs_prop_t prop;
- int err = 0;
-
- if (zhp->zfs_recvd_props == NULL)
- if (get_recvd_props_ioctl(zhp) != 0)
- return (-1);
-
- prop = zfs_name_to_prop(propname);
-
- if (prop != ZPROP_INVAL) {
- uint64_t cookie;
- if (!nvlist_exists(zhp->zfs_recvd_props, propname))
- return (-1);
- zfs_set_recvd_props_mode(zhp, &cookie);
- err = zfs_prop_get(zhp, prop, propbuf, proplen,
- NULL, NULL, 0, literal);
- zfs_unset_recvd_props_mode(zhp, &cookie);
- } else {
- nvlist_t *propval;
- char *recvdval;
- if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
- propname, &propval) != 0)
- return (-1);
- verify(nvlist_lookup_string(propval, ZPROP_VALUE,
- &recvdval) == 0);
- (void) strlcpy(propbuf, recvdval, proplen);
- }
-
- return (err == 0 ? 0 : -1);
-}
-
-static int
-get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
-{
- nvlist_t *value;
- nvpair_t *pair;
-
- value = zfs_get_clones_nvl(zhp);
- if (value == NULL)
- return (-1);
-
- propbuf[0] = '\0';
- for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
- pair = nvlist_next_nvpair(value, pair)) {
- if (propbuf[0] != '\0')
- (void) strlcat(propbuf, ",", proplen);
- (void) strlcat(propbuf, nvpair_name(pair), proplen);
- }
-
- return (0);
-}
-
-struct get_clones_arg {
- uint64_t numclones;
- nvlist_t *value;
- const char *origin;
- char buf[ZFS_MAX_DATASET_NAME_LEN];
-};
-
-int
-get_clones_cb(zfs_handle_t *zhp, void *arg)
-{
- struct get_clones_arg *gca = arg;
-
- if (gca->numclones == 0) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
- NULL, NULL, 0, B_TRUE) != 0)
- goto out;
- if (strcmp(gca->buf, gca->origin) == 0) {
- fnvlist_add_boolean(gca->value, zfs_get_name(zhp));
- gca->numclones--;
- }
-
-out:
- (void) zfs_iter_children(zhp, get_clones_cb, gca);
- zfs_close(zhp);
- return (0);
-}
-
-nvlist_t *
-zfs_get_clones_nvl(zfs_handle_t *zhp)
-{
- nvlist_t *nv, *value;
-
- if (nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
- struct get_clones_arg gca;
-
- /*
- * if this is a snapshot, then the kernel wasn't able
- * to get the clones. Do it by slowly iterating.
- */
- if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
- return (NULL);
- if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
- return (NULL);
- if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
- nvlist_free(nv);
- return (NULL);
- }
-
- gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
- gca.value = value;
- gca.origin = zhp->zfs_name;
-
- if (gca.numclones != 0) {
- zfs_handle_t *root;
- char pool[ZFS_MAX_DATASET_NAME_LEN];
- char *cp = pool;
-
- /* get the pool name */
- (void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
- (void) strsep(&cp, "/@");
- root = zfs_open(zhp->zfs_hdl, pool,
- ZFS_TYPE_FILESYSTEM);
-
- (void) get_clones_cb(root, &gca);
- }
-
- if (gca.numclones != 0 ||
- nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
- nvlist_add_nvlist(zhp->zfs_props,
- zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
- nvlist_free(nv);
- nvlist_free(value);
- return (NULL);
- }
- nvlist_free(nv);
- nvlist_free(value);
- verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
- }
-
- verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
-
- return (value);
-}
-
-/*
- * Accepts a property and value and checks that the value
- * matches the one found by the channel program. If they are
- * not equal, print both of them.
- */
-void
-zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval,
- const char *strval)
-{
- if (!zhp->zfs_hdl->libzfs_prop_debug)
- return;
- int error;
- char *poolname = zhp->zpool_hdl->zpool_name;
- const char *program =
- "args = ...\n"
- "ds = args['dataset']\n"
- "prop = args['property']\n"
- "value, setpoint = zfs.get_prop(ds, prop)\n"
- "return {value=value, setpoint=setpoint}\n";
- nvlist_t *outnvl;
- nvlist_t *retnvl;
- nvlist_t *argnvl = fnvlist_alloc();
-
- fnvlist_add_string(argnvl, "dataset", zhp->zfs_name);
- fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop));
-
- error = lzc_channel_program_nosync(poolname, program,
- 10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl);
-
- if (error == 0) {
- retnvl = fnvlist_lookup_nvlist(outnvl, "return");
- if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) {
- int64_t ans;
- error = nvlist_lookup_int64(retnvl, "value", &ans);
- if (error != 0) {
- (void) fprintf(stderr, "zcp check error: %u\n",
- error);
- return;
- }
- if (ans != intval) {
- (void) fprintf(stderr,
- "%s: zfs found %lld, but zcp found %lld\n",
- zfs_prop_to_name(prop),
- (longlong_t)intval, (longlong_t)ans);
- }
- } else {
- char *str_ans;
- error = nvlist_lookup_string(retnvl, "value", &str_ans);
- if (error != 0) {
- (void) fprintf(stderr, "zcp check error: %u\n",
- error);
- return;
- }
- if (strcmp(strval, str_ans) != 0) {
- (void) fprintf(stderr,
- "%s: zfs found %s, but zcp found %s\n",
- zfs_prop_to_name(prop),
- strval, str_ans);
- }
- }
- } else {
- (void) fprintf(stderr,
- "zcp check failed, channel program error: %u\n", error);
- }
- nvlist_free(argnvl);
- nvlist_free(outnvl);
-}
-
-/*
- * Retrieve a property from the given object. If 'literal' is specified, then
- * numbers are left as exact values. Otherwise, numbers are converted to a
- * human-readable form.
- *
- * Returns 0 on success, or -1 on error.
- */
-int
-zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
- zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
-{
- char *source = NULL;
- uint64_t val;
- const char *str;
- const char *strval;
- boolean_t received = zfs_is_recvd_props_mode(zhp);
-
- /*
- * Check to see if this property applies to our object
- */
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
- return (-1);
-
- if (received && zfs_prop_readonly(prop))
- return (-1);
-
- if (src)
- *src = ZPROP_SRC_NONE;
-
- switch (prop) {
- case ZFS_PROP_CREATION:
- /*
- * 'creation' is a time_t stored in the statistics. We convert
- * this into a string unless 'literal' is specified.
- */
- {
- val = getprop_uint64(zhp, prop, &source);
- time_t time = (time_t)val;
- struct tm t;
-
- if (literal ||
- localtime_r(&time, &t) == NULL ||
- strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
- &t) == 0)
- (void) snprintf(propbuf, proplen, "%llu", val);
- }
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_MOUNTPOINT:
- /*
- * Getting the precise mountpoint can be tricky.
- *
- * - for 'none' or 'legacy', return those values.
- * - for inherited mountpoints, we want to take everything
- * after our ancestor and append it to the inherited value.
- *
- * If the pool has an alternate root, we want to prepend that
- * root to any values we return.
- */
-
- str = getprop_string(zhp, prop, &source);
-
- if (str[0] == '/') {
- char buf[MAXPATHLEN];
- char *root = buf;
- const char *relpath;
-
- /*
- * If we inherit the mountpoint, even from a dataset
- * with a received value, the source will be the path of
- * the dataset we inherit from. If source is
- * ZPROP_SOURCE_VAL_RECVD, the received value is not
- * inherited.
- */
- if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) {
- relpath = "";
- } else {
- relpath = zhp->zfs_name + strlen(source);
- if (relpath[0] == '/')
- relpath++;
- }
-
- if ((zpool_get_prop(zhp->zpool_hdl,
- ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL,
- B_FALSE)) || (strcmp(root, "-") == 0))
- root[0] = '\0';
- /*
- * Special case an alternate root of '/'. This will
- * avoid having multiple leading slashes in the
- * mountpoint path.
- */
- if (strcmp(root, "/") == 0)
- root++;
-
- /*
- * If the mountpoint is '/' then skip over this
- * if we are obtaining either an alternate root or
- * an inherited mountpoint.
- */
- if (str[1] == '\0' && (root[0] != '\0' ||
- relpath[0] != '\0'))
- str++;
-
- if (relpath[0] == '\0')
- (void) snprintf(propbuf, proplen, "%s%s",
- root, str);
- else
- (void) snprintf(propbuf, proplen, "%s%s%s%s",
- root, str, relpath[0] == '@' ? "" : "/",
- relpath);
- } else {
- /* 'legacy' or 'none' */
- (void) strlcpy(propbuf, str, proplen);
- }
- zcp_check(zhp, prop, NULL, propbuf);
- break;
-
- case ZFS_PROP_ORIGIN:
- str = getprop_string(zhp, prop, &source);
- if (str == NULL)
- return (-1);
- (void) strlcpy(propbuf, str, proplen);
- zcp_check(zhp, prop, NULL, str);
- break;
-
- case ZFS_PROP_CLONES:
- if (get_clones_string(zhp, propbuf, proplen) != 0)
- return (-1);
- break;
-
- case ZFS_PROP_QUOTA:
- case ZFS_PROP_REFQUOTA:
- case ZFS_PROP_RESERVATION:
- case ZFS_PROP_REFRESERVATION:
-
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- /*
- * If quota or reservation is 0, we translate this into 'none'
- * (unless literal is set), and indicate that it's the default
- * value. Otherwise, we print the number nicely and indicate
- * that its set locally.
- */
- if (val == 0) {
- if (literal)
- (void) strlcpy(propbuf, "0", proplen);
- else
- (void) strlcpy(propbuf, "none", proplen);
- } else {
- if (literal)
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- else
- zfs_nicenum(val, propbuf, proplen);
- }
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_FILESYSTEM_LIMIT:
- case ZFS_PROP_SNAPSHOT_LIMIT:
- case ZFS_PROP_FILESYSTEM_COUNT:
- case ZFS_PROP_SNAPSHOT_COUNT:
-
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
-
- /*
- * If limit is UINT64_MAX, we translate this into 'none' (unless
- * literal is set), and indicate that it's the default value.
- * Otherwise, we print the number nicely and indicate that it's
- * set locally.
- */
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- } else if (val == UINT64_MAX) {
- (void) strlcpy(propbuf, "none", proplen);
- } else {
- zfs_nicenum(val, propbuf, proplen);
- }
-
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_REFRATIO:
- case ZFS_PROP_COMPRESSRATIO:
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- (void) snprintf(propbuf, proplen, "%llu.%02llux",
- (u_longlong_t)(val / 100),
- (u_longlong_t)(val % 100));
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_TYPE:
- switch (zhp->zfs_type) {
- case ZFS_TYPE_FILESYSTEM:
- str = "filesystem";
- break;
- case ZFS_TYPE_VOLUME:
- str = "volume";
- break;
- case ZFS_TYPE_SNAPSHOT:
- str = "snapshot";
- break;
- case ZFS_TYPE_BOOKMARK:
- str = "bookmark";
- break;
- default:
- abort();
- }
- (void) snprintf(propbuf, proplen, "%s", str);
- zcp_check(zhp, prop, NULL, propbuf);
- break;
-
- case ZFS_PROP_MOUNTED:
- /*
- * The 'mounted' property is a pseudo-property that described
- * whether the filesystem is currently mounted. Even though
- * it's a boolean value, the typical values of "on" and "off"
- * don't make sense, so we translate to "yes" and "no".
- */
- if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
- src, &source, &val) != 0)
- return (-1);
- if (val)
- (void) strlcpy(propbuf, "yes", proplen);
- else
- (void) strlcpy(propbuf, "no", proplen);
- break;
-
- case ZFS_PROP_NAME:
- /*
- * The 'name' property is a pseudo-property derived from the
- * dataset name. It is presented as a real property to simplify
- * consumers.
- */
- (void) strlcpy(propbuf, zhp->zfs_name, proplen);
- zcp_check(zhp, prop, NULL, propbuf);
- break;
-
- case ZFS_PROP_MLSLABEL:
- {
-#ifdef illumos
- m_label_t *new_sl = NULL;
- char *ascii = NULL; /* human readable label */
-
- (void) strlcpy(propbuf,
- getprop_string(zhp, prop, &source), proplen);
-
- if (literal || (strcasecmp(propbuf,
- ZFS_MLSLABEL_DEFAULT) == 0))
- break;
-
- /*
- * Try to translate the internal hex string to
- * human-readable output. If there are any
- * problems just use the hex string.
- */
-
- if (str_to_label(propbuf, &new_sl, MAC_LABEL,
- L_NO_CORRECTION, NULL) == -1) {
- m_label_free(new_sl);
- break;
- }
-
- if (label_to_str(new_sl, &ascii, M_LABEL,
- DEF_NAMES) != 0) {
- if (ascii)
- free(ascii);
- m_label_free(new_sl);
- break;
- }
- m_label_free(new_sl);
-
- (void) strlcpy(propbuf, ascii, proplen);
- free(ascii);
-#else /* !illumos */
- propbuf[0] = '\0';
-#endif /* illumos */
- }
- break;
-
- case ZFS_PROP_GUID:
- case ZFS_PROP_CREATETXG:
- /*
- * GUIDs are stored as numbers, but they are identifiers.
- * We don't want them to be pretty printed, because pretty
- * printing mangles the ID into a truncated and useless value.
- */
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
- zcp_check(zhp, prop, val, NULL);
- break;
-
- default:
- switch (zfs_prop_get_type(prop)) {
- case PROP_TYPE_NUMBER:
- if (get_numeric_property(zhp, prop, src,
- &source, &val) != 0) {
- return (-1);
- }
-
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- } else {
- zfs_nicenum(val, propbuf, proplen);
- }
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case PROP_TYPE_STRING:
- str = getprop_string(zhp, prop, &source);
- if (str == NULL)
- return (-1);
-
- (void) strlcpy(propbuf, str, proplen);
- zcp_check(zhp, prop, NULL, str);
- break;
-
- case PROP_TYPE_INDEX:
- if (get_numeric_property(zhp, prop, src,
- &source, &val) != 0)
- return (-1);
- if (zfs_prop_index_to_string(prop, val, &strval) != 0)
- return (-1);
-
- (void) strlcpy(propbuf, strval, proplen);
- zcp_check(zhp, prop, NULL, strval);
- break;
-
- default:
- abort();
- }
- }
-
- get_source(zhp, src, source, statbuf, statlen);
-
- return (0);
-}
-
-/*
- * Utility function to get the given numeric property. Does no validation that
- * the given property is the appropriate type; should only be used with
- * hard-coded property types.
- */
-uint64_t
-zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
-{
- char *source;
- uint64_t val;
-
- (void) get_numeric_property(zhp, prop, NULL, &source, &val);
-
- return (val);
-}
-
-int
-zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
-{
- char buf[64];
-
- (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
- return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
-}
-
-/*
- * Similar to zfs_prop_get(), but returns the value as an integer.
- */
-int
-zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
- zprop_source_t *src, char *statbuf, size_t statlen)
-{
- char *source;
-
- /*
- * Check to see if this property applies to our object
- */
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
- return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
- dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
- zfs_prop_to_name(prop)));
- }
-
- if (src)
- *src = ZPROP_SRC_NONE;
-
- if (get_numeric_property(zhp, prop, src, &source, value) != 0)
- return (-1);
-
- get_source(zhp, src, source, statbuf, statlen);
-
- return (0);
-}
-
-static int
-idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
- char **domainp, idmap_rid_t *ridp)
-{
-#ifdef illumos
- idmap_get_handle_t *get_hdl = NULL;
- idmap_stat status;
- int err = EINVAL;
-
- if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)
- goto out;
-
- if (isuser) {
- err = idmap_get_sidbyuid(get_hdl, id,
- IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
- } else {
- err = idmap_get_sidbygid(get_hdl, id,
- IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
- }
- if (err == IDMAP_SUCCESS &&
- idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
- status == IDMAP_SUCCESS)
- err = 0;
- else
- err = EINVAL;
-out:
- if (get_hdl)
- idmap_get_destroy(get_hdl);
- return (err);
-#else /* !illumos */
- assert(!"invalid code path");
- return (EINVAL); // silence compiler warning
-#endif /* illumos */
-}
-
-/*
- * convert the propname into parameters needed by kernel
- * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
- * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
- */
-static int
-userquota_propname_decode(const char *propname, boolean_t zoned,
- zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
-{
- zfs_userquota_prop_t type;
- char *cp, *end;
- char *numericsid = NULL;
- boolean_t isuser;
-
- domain[0] = '\0';
- *ridp = 0;
- /* Figure out the property type ({user|group}{quota|space}) */
- for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
- if (strncmp(propname, zfs_userquota_prop_prefixes[type],
- strlen(zfs_userquota_prop_prefixes[type])) == 0)
- break;
- }
- if (type == ZFS_NUM_USERQUOTA_PROPS)
- return (EINVAL);
- *typep = type;
-
- isuser = (type == ZFS_PROP_USERQUOTA ||
- type == ZFS_PROP_USERUSED);
-
- cp = strchr(propname, '@') + 1;
-
- if (strchr(cp, '@')) {
-#ifdef illumos
- /*
- * It's a SID name (eg "user@domain") that needs to be
- * turned into S-1-domainID-RID.
- */
- int flag = 0;
- idmap_stat stat, map_stat;
- uid_t pid;
- idmap_rid_t rid;
- idmap_get_handle_t *gh = NULL;
-
- stat = idmap_get_create(&gh);
- if (stat != IDMAP_SUCCESS) {
- idmap_get_destroy(gh);
- return (ENOMEM);
- }
- if (zoned && getzoneid() == GLOBAL_ZONEID)
- return (ENOENT);
- if (isuser) {
- stat = idmap_getuidbywinname(cp, NULL, flag, &pid);
- if (stat < 0)
- return (ENOENT);
- stat = idmap_get_sidbyuid(gh, pid, flag, &numericsid,
- &rid, &map_stat);
- } else {
- stat = idmap_getgidbywinname(cp, NULL, flag, &pid);
- if (stat < 0)
- return (ENOENT);
- stat = idmap_get_sidbygid(gh, pid, flag, &numericsid,
- &rid, &map_stat);
- }
- if (stat < 0) {
- idmap_get_destroy(gh);
- return (ENOENT);
- }
- stat = idmap_get_mappings(gh);
- idmap_get_destroy(gh);
-
- if (stat < 0) {
- return (ENOENT);
- }
- if (numericsid == NULL)
- return (ENOENT);
- cp = numericsid;
- *ridp = rid;
- /* will be further decoded below */
-#else /* !illumos */
- return (ENOENT);
-#endif /* illumos */
- }
-
- if (strncmp(cp, "S-1-", 4) == 0) {
- /* It's a numeric SID (eg "S-1-234-567-89") */
- (void) strlcpy(domain, cp, domainlen);
- errno = 0;
- if (*ridp == 0) {
- cp = strrchr(domain, '-');
- *cp = '\0';
- cp++;
- *ridp = strtoull(cp, &end, 10);
- } else {
- end = "";
- }
- if (numericsid) {
- free(numericsid);
- numericsid = NULL;
- }
- if (errno != 0 || *end != '\0')
- return (EINVAL);
- } else if (!isdigit(*cp)) {
- /*
- * It's a user/group name (eg "user") that needs to be
- * turned into a uid/gid
- */
- if (zoned && getzoneid() == GLOBAL_ZONEID)
- return (ENOENT);
- if (isuser) {
- struct passwd *pw;
- pw = getpwnam(cp);
- if (pw == NULL)
- return (ENOENT);
- *ridp = pw->pw_uid;
- } else {
- struct group *gr;
- gr = getgrnam(cp);
- if (gr == NULL)
- return (ENOENT);
- *ridp = gr->gr_gid;
- }
- } else {
- /* It's a user/group ID (eg "12345"). */
- uid_t id = strtoul(cp, &end, 10);
- idmap_rid_t rid;
- char *mapdomain;
-
- if (*end != '\0')
- return (EINVAL);
- if (id > MAXUID) {
- /* It's an ephemeral ID. */
- if (idmap_id_to_numeric_domain_rid(id, isuser,
- &mapdomain, &rid) != 0)
- return (ENOENT);
- (void) strlcpy(domain, mapdomain, domainlen);
- *ridp = rid;
- } else {
- *ridp = id;
- }
- }
-
- ASSERT3P(numericsid, ==, NULL);
- return (0);
-}
-
-static int
-zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue, zfs_userquota_prop_t *typep)
-{
- int err;
- zfs_cmd_t zc = { 0 };
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- err = userquota_propname_decode(propname,
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
- typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
- zc.zc_objset_type = *typep;
- if (err)
- return (err);
-
- err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
- if (err)
- return (err);
-
- *propvalue = zc.zc_cookie;
- return (0);
-}
-
-int
-zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue)
-{
- zfs_userquota_prop_t type;
-
- return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
- &type));
-}
-
-int
-zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal)
-{
- int err;
- uint64_t propvalue;
- zfs_userquota_prop_t type;
-
- err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,
- &type);
-
- if (err)
- return (err);
-
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu", propvalue);
- } else if (propvalue == 0 &&
- (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
- (void) strlcpy(propbuf, "none", proplen);
- } else {
- zfs_nicenum(propvalue, propbuf, proplen);
- }
- return (0);
-}
-
-int
-zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue)
-{
- int err;
- zfs_cmd_t zc = { 0 };
- const char *snapname;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- snapname = strchr(propname, '@') + 1;
- if (strchr(snapname, '@')) {
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- } else {
- /* snapname is the short name, append it to zhp's fsname */
- char *cp;
-
- (void) strlcpy(zc.zc_value, zhp->zfs_name,
- sizeof (zc.zc_value));
- cp = strchr(zc.zc_value, '@');
- if (cp != NULL)
- *cp = '\0';
- (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
- (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
- }
-
- err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
- if (err)
- return (err);
-
- *propvalue = zc.zc_cookie;
- return (0);
-}
-
-int
-zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal)
-{
- int err;
- uint64_t propvalue;
-
- err = zfs_prop_get_written_int(zhp, propname, &propvalue);
-
- if (err)
- return (err);
-
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu", propvalue);
- } else {
- zfs_nicenum(propvalue, propbuf, proplen);
- }
- return (0);
-}
-
-/*
- * Returns the name of the given zfs handle.
- */
-const char *
-zfs_get_name(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_name);
-}
-
-/*
- * Returns the name of the parent pool for the given zfs handle.
- */
-const char *
-zfs_get_pool_name(const zfs_handle_t *zhp)
-{
- return (zhp->zpool_hdl->zpool_name);
-}
-
-/*
- * Returns the type of the given zfs handle.
- */
-zfs_type_t
-zfs_get_type(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_type);
-}
-
-/*
- * Is one dataset name a child dataset of another?
- *
- * Needs to handle these cases:
- * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo"
- * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar"
- * Descendant? No. No. No. Yes.
- */
-static boolean_t
-is_descendant(const char *ds1, const char *ds2)
-{
- size_t d1len = strlen(ds1);
-
- /* ds2 can't be a descendant if it's smaller */
- if (strlen(ds2) < d1len)
- return (B_FALSE);
-
- /* otherwise, compare strings and verify that there's a '/' char */
- return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0));
-}
-
-/*
- * Given a complete name, return just the portion that refers to the parent.
- * Will return -1 if there is no parent (path is just the name of the
- * pool).
- */
-static int
-parent_name(const char *path, char *buf, size_t buflen)
-{
- char *slashp;
-
- (void) strlcpy(buf, path, buflen);
-
- if ((slashp = strrchr(buf, '/')) == NULL)
- return (-1);
- *slashp = '\0';
-
- return (0);
-}
-
-/*
- * If accept_ancestor is false, then check to make sure that the given path has
- * a parent, and that it exists. If accept_ancestor is true, then find the
- * closest existing ancestor for the given path. In prefixlen return the
- * length of already existing prefix of the given path. We also fetch the
- * 'zoned' property, which is used to validate property settings when creating
- * new datasets.
- */
-static int
-check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
- boolean_t accept_ancestor, int *prefixlen)
-{
- zfs_cmd_t zc = { 0 };
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- char *slash;
- zfs_handle_t *zhp;
- char errbuf[1024];
- uint64_t is_zoned;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
-
- /* get parent, and check to see if this is just a pool */
- if (parent_name(path, parent, sizeof (parent)) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing dataset name"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- /* check to see if the pool exists */
- if ((slash = strchr(parent, '/')) == NULL)
- slash = parent + strlen(parent);
- (void) strncpy(zc.zc_name, parent, slash - parent);
- zc.zc_name[slash - parent] = '\0';
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
- errno == ENOENT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such pool '%s'"), zc.zc_name);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
-
- /* check to see if the parent dataset exists */
- while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
- if (errno == ENOENT && accept_ancestor) {
- /*
- * Go deeper to find an ancestor, give up on top level.
- */
- if (parent_name(parent, parent, sizeof (parent)) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such pool '%s'"), zc.zc_name);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
- } else if (errno == ENOENT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "parent does not exist"));
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- } else
- return (zfs_standard_error(hdl, errno, errbuf));
- }
-
- is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
- if (zoned != NULL)
- *zoned = is_zoned;
-
- /* we are in a non-global zone, but parent is in the global zone */
- if (getzoneid() != GLOBAL_ZONEID && !is_zoned) {
- (void) zfs_standard_error(hdl, EPERM, errbuf);
- zfs_close(zhp);
- return (-1);
- }
-
- /* make sure parent is a filesystem */
- if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "parent is not a filesystem"));
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- zfs_close(zhp);
- return (-1);
- }
-
- zfs_close(zhp);
- if (prefixlen != NULL)
- *prefixlen = strlen(parent);
- return (0);
-}
-
-/*
- * Finds whether the dataset of the given type(s) exists.
- */
-boolean_t
-zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
-{
- zfs_handle_t *zhp;
-
- if (!zfs_validate_name(hdl, path, types, B_FALSE))
- return (B_FALSE);
-
- /*
- * Try to get stats for the dataset, which will tell us if it exists.
- */
- if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
- int ds_type = zhp->zfs_type;
-
- zfs_close(zhp);
- if (types & ds_type)
- return (B_TRUE);
- }
- return (B_FALSE);
-}
-
-/*
- * Given a path to 'target', create all the ancestors between
- * the prefixlen portion of the path, and the target itself.
- * Fail if the initial prefixlen-ancestor does not already exist.
- */
-int
-create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
-{
- zfs_handle_t *h;
- char *cp;
- const char *opname;
-
- /* make sure prefix exists */
- cp = target + prefixlen;
- if (*cp != '/') {
- assert(strchr(cp, '/') == NULL);
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- } else {
- *cp = '\0';
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- *cp = '/';
- }
- if (h == NULL)
- return (-1);
- zfs_close(h);
-
- /*
- * Attempt to create, mount, and share any ancestor filesystems,
- * up to the prefixlen-long one.
- */
- for (cp = target + prefixlen + 1;
- (cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) {
-
- *cp = '\0';
-
- h = make_dataset_handle(hdl, target);
- if (h) {
- /* it already exists, nothing to do here */
- zfs_close(h);
- continue;
- }
-
- if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
- NULL) != 0) {
- opname = dgettext(TEXT_DOMAIN, "create");
- goto ancestorerr;
- }
-
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- if (h == NULL) {
- opname = dgettext(TEXT_DOMAIN, "open");
- goto ancestorerr;
- }
-
- if (zfs_mount(h, NULL, 0) != 0) {
- opname = dgettext(TEXT_DOMAIN, "mount");
- goto ancestorerr;
- }
-
- if (zfs_share(h) != 0) {
- opname = dgettext(TEXT_DOMAIN, "share");
- goto ancestorerr;
- }
-
- zfs_close(h);
- }
-
- return (0);
-
-ancestorerr:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to %s ancestor '%s'"), opname, target);
- return (-1);
-}
-
-/*
- * Creates non-existing ancestors of the given path.
- */
-int
-zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
-{
- int prefix;
- char *path_copy;
- char errbuf[1024];
- int rc = 0;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), path);
-
- /*
- * Check that we are not passing the nesting limit
- * before we start creating any ancestors.
- */
- if (dataset_nestcheck(path) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "maximum name nesting depth exceeded"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
- return (-1);
-
- if ((path_copy = strdup(path)) != NULL) {
- rc = create_parents(hdl, path_copy, prefix);
- free(path_copy);
- }
- if (path_copy == NULL || rc != 0)
- return (-1);
-
- return (0);
-}
-
-/*
- * Create a new filesystem or volume.
- */
-int
-zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
- nvlist_t *props)
-{
- int ret;
- uint64_t size = 0;
- uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
- char errbuf[1024];
- uint64_t zoned;
- enum lzc_dataset_type ost;
- zpool_handle_t *zpool_handle;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), path);
-
- /* validate the path, taking care to note the extended error message */
- if (!zfs_validate_name(hdl, path, type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- if (dataset_nestcheck(path) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "maximum name nesting depth exceeded"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- /* validate parents exist */
- if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
- return (-1);
-
- /*
- * The failure modes when creating a dataset of a different type over
- * one that already exists is a little strange. In particular, if you
- * try to create a dataset on top of an existing dataset, the ioctl()
- * will return ENOENT, not EEXIST. To prevent this from happening, we
- * first try to see if the dataset exists.
- */
- if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset already exists"));
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
-
- if (type == ZFS_TYPE_VOLUME)
- ost = LZC_DATSET_TYPE_ZVOL;
- else
- ost = LZC_DATSET_TYPE_ZFS;
-
- /* open zpool handle for prop validation */
- char pool_path[ZFS_MAX_DATASET_NAME_LEN];
- (void) strlcpy(pool_path, path, sizeof (pool_path));
-
- /* truncate pool_path at first slash */
- char *p = strchr(pool_path, '/');
- if (p != NULL)
- *p = '\0';
-
- if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL)
- return (-1);
-
- if (props && (props = zfs_valid_proplist(hdl, type, props,
- zoned, NULL, zpool_handle, errbuf)) == 0) {
- zpool_close(zpool_handle);
- return (-1);
- }
- zpool_close(zpool_handle);
-
- if (type == ZFS_TYPE_VOLUME) {
- /*
- * If we are creating a volume, the size and block size must
- * satisfy a few restraints. First, the blocksize must be a
- * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the
- * volsize must be a multiple of the block size, and cannot be
- * zero.
- */
- if (props == NULL || nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing volume size"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
-
- if ((ret = nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- &blocksize)) != 0) {
- if (ret == ENOENT) {
- blocksize = zfs_prop_default_numeric(
- ZFS_PROP_VOLBLOCKSIZE);
- } else {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing volume block size"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
- }
-
- if (size == 0) {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "volume size cannot be zero"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
-
- if (size % blocksize != 0) {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "volume size must be a multiple of volume block "
- "size"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
- }
-
- /* create the dataset */
- ret = lzc_create(path, ost, props);
- nvlist_free(props);
-
- /* check for failure */
- if (ret != 0) {
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- (void) parent_name(path, parent, sizeof (parent));
-
- switch (errno) {
- case ENOENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such parent '%s'"), parent);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to set this "
- "property or value"));
- return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
- case ERANGE:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property value(s) specified"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-#ifdef _ILP32
- case EOVERFLOW:
- /*
- * This platform can't address a volume this big.
- */
- if (type == ZFS_TYPE_VOLUME)
- return (zfs_error(hdl, EZFS_VOLTOOBIG,
- errbuf));
-#endif
- /* FALLTHROUGH */
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- return (0);
-}
-
-/*
- * Destroys the given dataset. The caller must make sure that the filesystem
- * isn't mounted, and that there are no active dependents. If the file system
- * does not exist this function does nothing.
- */
-int
-zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
-{
- int error;
-
- if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT && defer)
- return (EINVAL);
-
- if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
- nvlist_t *nv = fnvlist_alloc();
- fnvlist_add_boolean(nv, zhp->zfs_name);
- error = lzc_destroy_bookmarks(nv, NULL);
- fnvlist_free(nv);
- if (error != 0) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, error,
- dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
- zhp->zfs_name));
- }
- return (0);
- }
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- nvlist_t *nv = fnvlist_alloc();
- fnvlist_add_boolean(nv, zhp->zfs_name);
- error = lzc_destroy_snaps(nv, defer, NULL);
- fnvlist_free(nv);
- } else {
- error = lzc_destroy(zhp->zfs_name);
- }
-
- if (error != 0 && error != ENOENT) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
- zhp->zfs_name));
- }
-
- remove_mountpoint(zhp);
-
- return (0);
-}
-
-struct destroydata {
- nvlist_t *nvl;
- const char *snapname;
-};
-
-static int
-zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
-{
- struct destroydata *dd = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
-
- (void) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, dd->snapname);
-
- if (lzc_exists(name))
- verify(nvlist_add_boolean(dd->nvl, name) == 0);
-
- rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
- zfs_close(zhp);
- return (rv);
-}
-
-/*
- * Destroys all snapshots with the given name in zhp & descendants.
- */
-int
-zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
-{
- int ret;
- struct destroydata dd = { 0 };
-
- dd.snapname = snapname;
- verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
- (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
-
- if (nvlist_empty(dd.nvl)) {
- ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
- dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
- zhp->zfs_name, snapname);
- } else {
- ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
- }
- nvlist_free(dd.nvl);
- return (ret);
-}
-
-/*
- * Destroys all the snapshots named in the nvlist.
- */
-int
-zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
-{
- int ret;
- nvlist_t *errlist = NULL;
-
- ret = lzc_destroy_snaps(snaps, defer, &errlist);
-
- if (ret == 0) {
- nvlist_free(errlist);
- return (0);
- }
-
- if (nvlist_empty(errlist)) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
-
- ret = zfs_standard_error(hdl, ret, errbuf);
- }
- for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
- pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
- nvpair_name(pair));
-
- switch (fnvpair_value_int32(pair)) {
- case EEXIST:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "snapshot is cloned"));
- ret = zfs_error(hdl, EZFS_EXISTS, errbuf);
- break;
- default:
- ret = zfs_standard_error(hdl, errno, errbuf);
- break;
- }
- }
-
- nvlist_free(errlist);
- return (ret);
-}
-
-/*
- * Clones the given dataset. The target must be of the same type as the source.
- */
-int
-zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
-{
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- int ret;
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- uint64_t zoned;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), target);
-
- /* validate the target/clone name */
- if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- /* validate parents exist */
- if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
- return (-1);
-
- (void) parent_name(target, parent, sizeof (parent));
-
- /* do the clone */
-
- if (props) {
- zfs_type_t type;
-
- if (ZFS_IS_VOLUME(zhp)) {
- type = ZFS_TYPE_VOLUME;
- } else {
- type = ZFS_TYPE_FILESYSTEM;
- }
- if ((props = zfs_valid_proplist(hdl, type, props, zoned,
- zhp, zhp->zpool_hdl, errbuf)) == NULL)
- return (-1);
- if (zfs_fix_auto_resv(zhp, props) == -1) {
- nvlist_free(props);
- return (-1);
- }
- }
-
- ret = lzc_clone(target, zhp->zfs_name, props);
- nvlist_free(props);
-
- if (ret != 0) {
- switch (errno) {
-
- case ENOENT:
- /*
- * The parent doesn't exist. We should have caught this
- * above, but there may a race condition that has since
- * destroyed the parent.
- *
- * At this point, we don't know whether it's the source
- * that doesn't exist anymore, or whether the target
- * dataset doesn't exist.
- */
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "no such parent '%s'"), parent);
- return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
-
- case EXDEV:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "source and target pools differ"));
- return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
- errbuf));
-
- default:
- return (zfs_standard_error(zhp->zfs_hdl, errno,
- errbuf));
- }
- }
-
- return (ret);
-}
-
-/*
- * Promotes the given clone fs to be the clone parent.
- */
-int
-zfs_promote(zfs_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- int ret;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot promote '%s'"), zhp->zfs_name);
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshots can not be promoted"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
-
- if (zhp->zfs_dmustats.dds_origin[0] == '\0') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not a cloned filesystem"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
-
- if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- ret = lzc_promote(zhp->zfs_name, snapname, sizeof (snapname));
-
- if (ret != 0) {
- switch (ret) {
- case EEXIST:
- /* There is a conflicting snapshot name. */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "conflicting snapshot '%s' from parent '%s'"),
- snapname, zhp->zfs_dmustats.dds_origin);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
-
- default:
- return (zfs_standard_error(hdl, ret, errbuf));
- }
- }
- return (ret);
-}
-
-typedef struct snapdata {
- nvlist_t *sd_nvl;
- const char *sd_snapname;
-} snapdata_t;
-
-static int
-zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
-{
- snapdata_t *sd = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) {
- (void) snprintf(name, sizeof (name),
- "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
-
- fnvlist_add_boolean(sd->sd_nvl, name);
-
- rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
- }
- zfs_close(zhp);
-
- return (rv);
-}
-
-int
-zfs_remap_indirects(libzfs_handle_t *hdl, const char *fs)
-{
- int err;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot remap dataset '%s'"), fs);
-
- err = lzc_remap(fs);
-
- if (err != 0) {
- switch (err) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl, err, errbuf);
- break;
- }
- }
-
- return (err);
-}
-
-/*
- * Creates snapshots. The keys in the snaps nvlist are the snapshots to be
- * created.
- */
-int
-zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
-{
- int ret;
- char errbuf[1024];
- nvpair_t *elem;
- nvlist_t *errors;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create snapshots "));
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
- const char *snapname = nvpair_name(elem);
-
- /* validate the target name */
- if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
- B_TRUE)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s'"), snapname);
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
- }
-
- /*
- * get pool handle for prop validation. assumes all snaps are in the
- * same pool, as does lzc_snapshot (below).
- */
- char pool[ZFS_MAX_DATASET_NAME_LEN];
- elem = nvlist_next_nvpair(snaps, NULL);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/@")] = '\0';
- zpool_handle_t *zpool_hdl = zpool_open(hdl, pool);
-
- if (props != NULL &&
- (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
- props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) {
- zpool_close(zpool_hdl);
- return (-1);
- }
- zpool_close(zpool_hdl);
-
- ret = lzc_snapshot(snaps, props, &errors);
-
- if (ret != 0) {
- boolean_t printed = B_FALSE;
- for (elem = nvlist_next_nvpair(errors, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errors, elem)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s'"), nvpair_name(elem));
- (void) zfs_standard_error(hdl,
- fnvpair_value_int32(elem), errbuf);
- printed = B_TRUE;
- }
- if (!printed) {
- switch (ret) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple snapshots of same "
- "fs not allowed"));
- (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
-
- break;
- default:
- (void) zfs_standard_error(hdl, ret, errbuf);
- }
- }
- }
-
- nvlist_free(props);
- nvlist_free(errors);
- return (ret);
-}
-
-int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
- nvlist_t *props)
-{
- int ret;
- snapdata_t sd = { 0 };
- char fsname[ZFS_MAX_DATASET_NAME_LEN];
- char *cp;
- zfs_handle_t *zhp;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot snapshot %s"), path);
-
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- (void) strlcpy(fsname, path, sizeof (fsname));
- cp = strchr(fsname, '@');
- *cp = '\0';
- sd.sd_snapname = cp + 1;
-
- if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
- ZFS_TYPE_VOLUME)) == NULL) {
- return (-1);
- }
-
- verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
- if (recursive) {
- (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
- } else {
- fnvlist_add_boolean(sd.sd_nvl, path);
- }
-
- ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
- nvlist_free(sd.sd_nvl);
- zfs_close(zhp);
- return (ret);
-}
-
-/*
- * Destroy any more recent snapshots. We invoke this callback on any dependents
- * of the snapshot first. If the 'cb_dependent' member is non-zero, then this
- * is a dependent and we should just destroy it without checking the transaction
- * group.
- */
-typedef struct rollback_data {
- const char *cb_target; /* the snapshot */
- uint64_t cb_create; /* creation time reference */
- boolean_t cb_error;
- boolean_t cb_force;
-} rollback_data_t;
-
-static int
-rollback_destroy_dependent(zfs_handle_t *zhp, void *data)
-{
- rollback_data_t *cbp = data;
- prop_changelist_t *clp;
-
- /* We must destroy this clone; first unmount it */
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- cbp->cb_force ? MS_FORCE: 0);
- if (clp == NULL || changelist_prefix(clp) != 0) {
- cbp->cb_error = B_TRUE;
- zfs_close(zhp);
- return (0);
- }
- if (zfs_destroy(zhp, B_FALSE) != 0)
- cbp->cb_error = B_TRUE;
- else
- changelist_remove(clp, zhp->zfs_name);
- (void) changelist_postfix(clp);
- changelist_free(clp);
-
- zfs_close(zhp);
- return (0);
-}
-
-static int
-rollback_destroy(zfs_handle_t *zhp, void *data)
-{
- rollback_data_t *cbp = data;
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
- cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
- rollback_destroy_dependent, cbp);
-
- cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
- }
-
- zfs_close(zhp);
- return (0);
-}
-
-/*
- * Given a dataset, rollback to a specific snapshot, discarding any
- * data changes since then and making it the active dataset.
- *
- * Any snapshots and bookmarks more recent than the target are
- * destroyed, along with their dependents (i.e. clones).
- */
-int
-zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
-{
- rollback_data_t cb = { 0 };
- int err;
- boolean_t restore_resv = 0;
- uint64_t min_txg = 0, old_volsize = 0, new_volsize;
- zfs_prop_t resv_prop;
-
- assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
- zhp->zfs_type == ZFS_TYPE_VOLUME);
-
- /*
- * Destroy all recent snapshots and their dependents.
- */
- cb.cb_force = force;
- cb.cb_target = snap->zfs_name;
- cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
-
- if (cb.cb_create > 0)
- min_txg = cb.cb_create;
-
- (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb,
- min_txg, 0);
-
- (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb);
-
- if (cb.cb_error)
- return (-1);
-
- /*
- * Now that we have verified that the snapshot is the latest,
- * rollback to the given snapshot.
- */
-
- if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
- if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
- return (-1);
- old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- restore_resv =
- (old_volsize == zfs_prop_get_int(zhp, resv_prop));
- }
-
- /*
- * Pass both the filesystem and the wanted snapshot names,
- * we would get an error back if the snapshot is destroyed or
- * a new snapshot is created before this request is processed.
- */
- err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);
- if (err != 0) {
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
- zhp->zfs_name);
- switch (err) {
- case EEXIST:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "there is a snapshot or bookmark more recent "
- "than '%s'"), snap->zfs_name);
- (void) zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf);
- break;
- case ESRCH:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "'%s' is not found among snapshots of '%s'"),
- snap->zfs_name, zhp->zfs_name);
- (void) zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(zhp->zfs_hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error(zhp->zfs_hdl, err, errbuf);
- }
- return (err);
- }
-
- /*
- * For volumes, if the pre-rollback volsize matched the pre-
- * rollback reservation and the volsize has changed then set
- * the reservation property to the post-rollback volsize.
- * Make a new handle since the rollback closed the dataset.
- */
- if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
- (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
- if (restore_resv) {
- new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- if (old_volsize != new_volsize)
- err = zfs_prop_set_int(zhp, resv_prop,
- new_volsize);
- }
- zfs_close(zhp);
- }
- return (err);
-}
-
-/*
- * Renames the given dataset.
- */
-int
-zfs_rename(zfs_handle_t *zhp, const char *source, const char *target,
- renameflags_t flags)
-{
- int ret = 0;
- zfs_cmd_t zc = { 0 };
- char *delim;
- prop_changelist_t *cl = NULL;
- zfs_handle_t *zhrp = NULL;
- char *parentname = NULL;
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- char property[ZFS_MAXPROPLEN];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
-
- /* if we have the same exact name, just return success */
- if (strcmp(zhp->zfs_name, target) == 0)
- return (0);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot rename to '%s'"), target);
-
- if (source != NULL) {
- /*
- * This is recursive snapshots rename, put snapshot name
- * (that might not exist) into zfs_name.
- */
- assert(flags.recurse);
-
- (void) strlcat(zhp->zfs_name, "@", sizeof(zhp->zfs_name));
- (void) strlcat(zhp->zfs_name, source, sizeof(zhp->zfs_name));
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- }
-
- /* make sure source name is valid */
- if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- /*
- * Make sure the target name is valid
- */
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
- zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
- const char sep = zhp->zfs_type == ZFS_TYPE_SNAPSHOT ? '@' : '#';
-
- if ((strchr(target, sep) == NULL) || *target == sep) {
- /*
- * Snapshot target name is abbreviated,
- * reconstruct full dataset name
- */
- (void) strlcpy(parent, zhp->zfs_name, sizeof (parent));
- delim = strchr(parent, sep);
- if (strchr(target, sep) == NULL)
- *(++delim) = '\0';
- else
- *delim = '\0';
- (void) strlcat(parent, target, sizeof (parent));
- target = parent;
- } else {
- /*
- * Make sure we're renaming within the same dataset.
- */
- delim = strchr(target, sep);
- if (strncmp(zhp->zfs_name, target, delim - target)
- != 0 || zhp->zfs_name[delim - target] != sep) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "%s must be part of same dataset"),
- zhp->zfs_type == ZFS_TYPE_SNAPSHOT ?
- "snapshots" : "bookmarks");
- return (zfs_error(hdl, EZFS_CROSSTARGET,
- errbuf));
- }
- }
-
- if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- } else {
- if (flags.recurse) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "recursive rename must be a snapshot"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
-
- if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- /* validate parents */
- if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0)
- return (-1);
-
- /* make sure we're in the same pool */
- verify((delim = strchr(target, '/')) != NULL);
- if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
- zhp->zfs_name[delim - target] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "datasets must be within same pool"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
- }
-
- /* new name cannot be a child of the current dataset name */
- if (is_descendant(zhp->zfs_name, target)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "New dataset name cannot be a descendant of "
- "current dataset name"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
- }
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
-
- if (getzoneid() == GLOBAL_ZONEID &&
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset is used in a non-global zone"));
- return (zfs_error(hdl, EZFS_ZONED, errbuf));
- }
-
- /*
- * Avoid unmounting file systems with mountpoint property set to
- * 'legacy' or 'none' even if -u option is not given.
- */
- if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
- !flags.recurse && !flags.nounmount &&
- zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
- sizeof (property), NULL, NULL, 0, B_FALSE) == 0 &&
- (strcmp(property, "legacy") == 0 ||
- strcmp(property, "none") == 0)) {
- flags.nounmount = B_TRUE;
- }
- if (flags.recurse) {
- parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
- if (parentname == NULL) {
- ret = -1;
- goto error;
- }
- delim = strchr(parentname, '@');
- *delim = '\0';
- zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
- if (zhrp == NULL) {
- ret = -1;
- goto error;
- }
- } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT &&
- zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
- if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
- flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0,
- flags.forceunmount ? MS_FORCE : 0)) == NULL) {
- return (-1);
- }
-
- if (changelist_haszonedchild(cl)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- (void) zfs_error(hdl, EZFS_ZONED, errbuf);
- ret = -1;
- goto error;
- }
-
- if ((ret = changelist_prefix(cl)) != 0)
- goto error;
- }
-
- if (ZFS_IS_VOLUME(zhp))
- zc.zc_objset_type = DMU_OST_ZVOL;
- else
- zc.zc_objset_type = DMU_OST_ZFS;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
-
- zc.zc_cookie = flags.recurse ? 1 : 0;
- if (flags.nounmount)
- zc.zc_cookie |= 2;
-
- if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
- /*
- * if it was recursive, the one that actually failed will
- * be in zc.zc_name
- */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot rename '%s'"), zc.zc_name);
-
- if (flags.recurse && errno == EEXIST) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "a child dataset already has a snapshot "
- "with the new name"));
- (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
- } else if (errno == EINVAL) {
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- } else {
- (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
- }
-
- /*
- * On failure, we still want to remount any filesystems that
- * were previously mounted, so we don't alter the system state.
- */
- if (cl != NULL)
- (void) changelist_postfix(cl);
- } else {
- if (cl != NULL) {
- changelist_rename(cl, zfs_get_name(zhp), target);
- ret = changelist_postfix(cl);
- }
- }
-
-error:
- if (parentname != NULL) {
- free(parentname);
- }
- if (zhrp != NULL) {
- zfs_close(zhrp);
- }
- if (cl != NULL) {
- changelist_free(cl);
- }
- return (ret);
-}
-
-nvlist_t *
-zfs_get_user_props(zfs_handle_t *zhp)
-{
- return (zhp->zfs_user_props);
-}
-
-nvlist_t *
-zfs_get_recvd_props(zfs_handle_t *zhp)
-{
- if (zhp->zfs_recvd_props == NULL)
- if (get_recvd_props_ioctl(zhp) != 0)
- return (NULL);
- return (zhp->zfs_recvd_props);
-}
-
-/*
- * This function is used by 'zfs list' to determine the exact set of columns to
- * display, and their maximum widths. This does two main things:
- *
- * - If this is a list of all properties, then expand the list to include
- * all native properties, and set a flag so that for each dataset we look
- * for new unique user properties and add them to the list.
- *
- * - For non fixed-width properties, keep track of the maximum width seen
- * so that we can size the column appropriately. If the user has
- * requested received property values, we also need to compute the width
- * of the RECEIVED column.
- */
-int
-zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
- boolean_t literal)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zprop_list_t *entry;
- zprop_list_t **last, **start;
- nvlist_t *userprops, *propval;
- nvpair_t *elem;
- char *strval;
- char buf[ZFS_MAXPROPLEN];
-
- if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
- return (-1);
-
- userprops = zfs_get_user_props(zhp);
-
- entry = *plp;
- if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
- /*
- * Go through and add any user properties as necessary. We
- * start by incrementing our list pointer to the first
- * non-native property.
- */
- start = plp;
- while (*start != NULL) {
- if ((*start)->pl_prop == ZPROP_INVAL)
- break;
- start = &(*start)->pl_next;
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
- /*
- * See if we've already found this property in our list.
- */
- for (last = start; *last != NULL;
- last = &(*last)->pl_next) {
- if (strcmp((*last)->pl_user_prop,
- nvpair_name(elem)) == 0)
- break;
- }
-
- if (*last == NULL) {
- if ((entry = zfs_alloc(hdl,
- sizeof (zprop_list_t))) == NULL ||
- ((entry->pl_user_prop = zfs_strdup(hdl,
- nvpair_name(elem)))) == NULL) {
- free(entry);
- return (-1);
- }
-
- entry->pl_prop = ZPROP_INVAL;
- entry->pl_width = strlen(nvpair_name(elem));
- entry->pl_all = B_TRUE;
- *last = entry;
- }
- }
- }
-
- /*
- * Now go through and check the width of any non-fixed columns
- */
- for (entry = *plp; entry != NULL; entry = entry->pl_next) {
- if (entry->pl_fixed && !literal)
- continue;
-
- if (entry->pl_prop != ZPROP_INVAL) {
- if (zfs_prop_get(zhp, entry->pl_prop,
- buf, sizeof (buf), NULL, NULL, 0, literal) == 0) {
- if (strlen(buf) > entry->pl_width)
- entry->pl_width = strlen(buf);
- }
- if (received && zfs_prop_get_recvd(zhp,
- zfs_prop_to_name(entry->pl_prop),
- buf, sizeof (buf), literal) == 0)
- if (strlen(buf) > entry->pl_recvd_width)
- entry->pl_recvd_width = strlen(buf);
- } else {
- if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
- &propval) == 0) {
- verify(nvlist_lookup_string(propval,
- ZPROP_VALUE, &strval) == 0);
- if (strlen(strval) > entry->pl_width)
- entry->pl_width = strlen(strval);
- }
- if (received && zfs_prop_get_recvd(zhp,
- entry->pl_user_prop,
- buf, sizeof (buf), literal) == 0)
- if (strlen(buf) > entry->pl_recvd_width)
- entry->pl_recvd_width = strlen(buf);
- }
- }
-
- return (0);
-}
-
-int
-zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
- char *resource, void *export, void *sharetab,
- int sharemax, zfs_share_op_t operation)
-{
- zfs_cmd_t zc = { 0 };
- int error;
-
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
- if (resource)
- (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
- zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
- zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
- zc.zc_share.z_sharetype = operation;
- zc.zc_share.z_sharemax = sharemax;
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
- return (error);
-}
-
-void
-zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
-{
- nvpair_t *curr;
-
- /*
- * Keep a reference to the props-table against which we prune the
- * properties.
- */
- zhp->zfs_props_table = props;
-
- curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
-
- while (curr) {
- zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
- nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
-
- /*
- * User properties will result in ZPROP_INVAL, and since we
- * only know how to prune standard ZFS properties, we always
- * leave these in the list. This can also happen if we
- * encounter an unknown DSL property (when running older
- * software, for example).
- */
- if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
- (void) nvlist_remove(zhp->zfs_props,
- nvpair_name(curr), nvpair_type(curr));
- curr = next;
- }
-}
-
-#ifdef illumos
-static int
-zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
- zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *nvlist = NULL;
- int error;
-
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
- zc.zc_cookie = (uint64_t)cmd;
-
- if (cmd == ZFS_SMB_ACL_RENAME) {
- if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (0);
- }
- }
-
- switch (cmd) {
- case ZFS_SMB_ACL_ADD:
- case ZFS_SMB_ACL_REMOVE:
- (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
- break;
- case ZFS_SMB_ACL_RENAME:
- if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
- resource1) != 0) {
- (void) no_memory(hdl);
- return (-1);
- }
- if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
- resource2) != 0) {
- (void) no_memory(hdl);
- return (-1);
- }
- if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
- nvlist_free(nvlist);
- return (-1);
- }
- break;
- case ZFS_SMB_ACL_PURGE:
- break;
- default:
- return (-1);
- }
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
- nvlist_free(nvlist);
- return (error);
-}
-
-int
-zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
- char *path, char *resource)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
- resource, NULL));
-}
-
-int
-zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
- char *path, char *resource)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
- resource, NULL));
-}
-
-int
-zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
- NULL, NULL));
-}
-
-int
-zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
- char *oldname, char *newname)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
- oldname, newname));
-}
-#endif /* illumos */
-
-int
-zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
- zfs_userspace_cb_t func, void *arg)
-{
- zfs_cmd_t zc = { 0 };
- zfs_useracct_t buf[100];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- int ret;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- zc.zc_objset_type = type;
- zc.zc_nvlist_dst = (uintptr_t)buf;
-
- for (;;) {
- zfs_useracct_t *zua = buf;
-
- zc.zc_nvlist_dst_size = sizeof (buf);
- if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot get used/quota for %s"), zc.zc_name);
- return (zfs_standard_error_fmt(hdl, errno, errbuf));
- }
- if (zc.zc_nvlist_dst_size == 0)
- break;
-
- while (zc.zc_nvlist_dst_size > 0) {
- if ((ret = func(arg, zua->zu_domain, zua->zu_rid,
- zua->zu_space)) != 0)
- return (ret);
- zua++;
- zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
- }
- }
-
- return (0);
-}
-
-struct holdarg {
- nvlist_t *nvl;
- const char *snapname;
- const char *tag;
- boolean_t recursive;
- int error;
-};
-
-static int
-zfs_hold_one(zfs_handle_t *zhp, void *arg)
-{
- struct holdarg *ha = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
-
- (void) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, ha->snapname);
-
- if (lzc_exists(name))
- fnvlist_add_string(ha->nvl, name, ha->tag);
-
- if (ha->recursive)
- rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
- zfs_close(zhp);
- return (rv);
-}
-
-int
-zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive, int cleanup_fd)
-{
- int ret;
- struct holdarg ha;
-
- ha.nvl = fnvlist_alloc();
- ha.snapname = snapname;
- ha.tag = tag;
- ha.recursive = recursive;
- (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
-
- if (nvlist_empty(ha.nvl)) {
- char errbuf[1024];
-
- fnvlist_free(ha.nvl);
- ret = ENOENT;
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot hold snapshot '%s@%s'"),
- zhp->zfs_name, snapname);
- (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);
- return (ret);
- }
-
- ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
- fnvlist_free(ha.nvl);
-
- return (ret);
-}
-
-int
-zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
-{
- int ret;
- nvlist_t *errors;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
- nvpair_t *elem;
-
- errors = NULL;
- ret = lzc_hold(holds, cleanup_fd, &errors);
-
- if (ret == 0) {
- /* There may be errors even in the success case. */
- fnvlist_free(errors);
- return (0);
- }
-
- if (nvlist_empty(errors)) {
- /* no hold-specific errors */
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot hold"));
- switch (ret) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl, ret, errbuf);
- }
- }
-
- for (elem = nvlist_next_nvpair(errors, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errors, elem)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot hold snapshot '%s'"), nvpair_name(elem));
- switch (fnvpair_value_int32(elem)) {
- case E2BIG:
- /*
- * Temporary tags wind up having the ds object id
- * prepended. So even if we passed the length check
- * above, it's still possible for the tag to wind
- * up being slightly too long.
- */
- (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case EEXIST:
- (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl,
- fnvpair_value_int32(elem), errbuf);
- }
- }
-
- fnvlist_free(errors);
- return (ret);
-}
-
-static int
-zfs_release_one(zfs_handle_t *zhp, void *arg)
-{
- struct holdarg *ha = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
- nvlist_t *existing_holds;
-
- (void) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, ha->snapname);
-
- if (lzc_get_holds(name, &existing_holds) != 0) {
- ha->error = ENOENT;
- } else if (!nvlist_exists(existing_holds, ha->tag)) {
- ha->error = ESRCH;
- } else {
- nvlist_t *torelease = fnvlist_alloc();
- fnvlist_add_boolean(torelease, ha->tag);
- fnvlist_add_nvlist(ha->nvl, name, torelease);
- fnvlist_free(torelease);
- }
-
- if (ha->recursive)
- rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
- zfs_close(zhp);
- return (rv);
-}
-
-int
-zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive)
-{
- int ret;
- struct holdarg ha;
- nvlist_t *errors = NULL;
- nvpair_t *elem;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
-
- ha.nvl = fnvlist_alloc();
- ha.snapname = snapname;
- ha.tag = tag;
- ha.recursive = recursive;
- ha.error = 0;
- (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
-
- if (nvlist_empty(ha.nvl)) {
- fnvlist_free(ha.nvl);
- ret = ha.error;
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot release hold from snapshot '%s@%s'"),
- zhp->zfs_name, snapname);
- if (ret == ESRCH) {
- (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
- } else {
- (void) zfs_standard_error(hdl, ret, errbuf);
- }
- return (ret);
- }
-
- ret = lzc_release(ha.nvl, &errors);
- fnvlist_free(ha.nvl);
-
- if (ret == 0) {
- /* There may be errors even in the success case. */
- fnvlist_free(errors);
- return (0);
- }
-
- if (nvlist_empty(errors)) {
- /* no hold-specific errors */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot release"));
- switch (errno) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- default:
- (void) zfs_standard_error_fmt(hdl, errno, errbuf);
- }
- }
-
- for (elem = nvlist_next_nvpair(errors, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errors, elem)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot release hold from snapshot '%s'"),
- nvpair_name(elem));
- switch (fnvpair_value_int32(elem)) {
- case ESRCH:
- (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error_fmt(hdl,
- fnvpair_value_int32(elem), errbuf);
- }
- }
-
- fnvlist_free(errors);
- return (ret);
-}
-
-int
-zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- int nvsz = 2048;
- void *nvbuf;
- int err = 0;
- char errbuf[1024];
-
- assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
- zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
-
-tryagain:
-
- nvbuf = malloc(nvsz);
- if (nvbuf == NULL) {
- err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
- goto out;
- }
-
- zc.zc_nvlist_dst_size = nvsz;
- zc.zc_nvlist_dst = (uintptr_t)nvbuf;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
- zc.zc_name);
- switch (errno) {
- case ENOMEM:
- free(nvbuf);
- nvsz = zc.zc_nvlist_dst_size;
- goto tryagain;
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case ENOENT:
- err = zfs_error(hdl, EZFS_NOENT, errbuf);
- break;
- default:
- err = zfs_standard_error_fmt(hdl, errno, errbuf);
- break;
- }
- } else {
- /* success */
- int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
- if (rc) {
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(
- TEXT_DOMAIN, "cannot get permissions on '%s'"),
- zc.zc_name);
- err = zfs_standard_error_fmt(hdl, rc, errbuf);
- }
- }
-
- free(nvbuf);
-out:
- return (err);
-}
-
-int
-zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char *nvbuf;
- char errbuf[1024];
- size_t nvsz;
- int err;
-
- assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
- zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
-
- err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
- assert(err == 0);
-
- nvbuf = malloc(nvsz);
-
- err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
- assert(err == 0);
-
- zc.zc_nvlist_src_size = nvsz;
- zc.zc_nvlist_src = (uintptr_t)nvbuf;
- zc.zc_perm_action = un;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
- zc.zc_name);
- switch (errno) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case ENOENT:
- err = zfs_error(hdl, EZFS_NOENT, errbuf);
- break;
- default:
- err = zfs_standard_error_fmt(hdl, errno, errbuf);
- break;
- }
- }
-
- free(nvbuf);
-
- return (err);
-}
-
-int
-zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
-{
- int err;
- char errbuf[1024];
-
- err = lzc_get_holds(zhp->zfs_name, nvl);
-
- if (err != 0) {
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
- zhp->zfs_name);
- switch (err) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case ENOENT:
- err = zfs_error(hdl, EZFS_NOENT, errbuf);
- break;
- default:
- err = zfs_standard_error_fmt(hdl, errno, errbuf);
- break;
- }
- }
-
- return (err);
-}
-
-/*
- * Convert the zvol's volume size to an appropriate reservation.
- * Note: If this routine is updated, it is necessary to update the ZFS test
- * suite's shell version in reservation.kshlib.
- */
-uint64_t
-zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
-{
- uint64_t numdb;
- uint64_t nblocks, volblocksize;
- int ncopies;
- char *strval;
-
- if (nvlist_lookup_string(props,
- zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
- ncopies = atoi(strval);
- else
- ncopies = 1;
- if (nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- &volblocksize) != 0)
- volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
- nblocks = volsize/volblocksize;
- /* start with metadnode L0-L6 */
- numdb = 7;
- /* calculate number of indirects */
- while (nblocks > 1) {
- nblocks += DNODES_PER_LEVEL - 1;
- nblocks /= DNODES_PER_LEVEL;
- numdb += nblocks;
- }
- numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
- volsize *= ncopies;
- /*
- * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
- * compressed, but in practice they compress down to about
- * 1100 bytes
- */
- numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
- volsize += numdb;
- return (volsize);
-}
-
-/*
- * Attach/detach the given filesystem to/from the given jail.
- */
-int
-zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_cmd_t zc = { 0 };
- char errbuf[1024];
- unsigned long cmd;
- int ret;
-
- if (attach) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
- } else {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name);
- }
-
- switch (zhp->zfs_type) {
- case ZFS_TYPE_VOLUME:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "volumes can not be jailed"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- case ZFS_TYPE_SNAPSHOT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshots can not be jailed"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
- assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_objset_type = DMU_OST_ZFS;
- zc.zc_jailid = jailid;
-
- cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
- if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0)
- zfs_standard_error(hdl, errno, errbuf);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c
deleted file mode 100644
index db132190154c..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c
+++ /dev/null
@@ -1,834 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2015, 2018 by Delphix. All rights reserved.
- * Copyright 2016 Joyent, Inc.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- */
-
-/*
- * zfs diff support
- */
-#include <ctype.h>
-#include <errno.h>
-#include <libintl.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/zfs_ioctl.h>
-#include <libzfs.h>
-#include "libzfs_impl.h"
-
-#define ZDIFF_SNAPDIR "/.zfs/snapshot/"
-#define ZDIFF_SHARESDIR "/.zfs/shares/"
-#define ZDIFF_PREFIX "zfs-diff-%d"
-
-#define ZDIFF_ADDED '+'
-#define ZDIFF_MODIFIED 'M'
-#define ZDIFF_REMOVED '-'
-#define ZDIFF_RENAMED 'R'
-
-typedef struct differ_info {
- zfs_handle_t *zhp;
- char *fromsnap;
- char *frommnt;
- char *tosnap;
- char *tomnt;
- char *ds;
- char *dsmnt;
- char *tmpsnap;
- char errbuf[1024];
- boolean_t isclone;
- boolean_t scripted;
- boolean_t classify;
- boolean_t timestamped;
- uint64_t shares;
- int zerr;
- int cleanupfd;
- int outputfd;
- int datafd;
-} differ_info_t;
-
-/*
- * Given a {dsname, object id}, get the object path
- */
-static int
-get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
- char *pn, int maxlen, zfs_stat_t *sb)
-{
- zfs_cmd_t zc = { 0 };
- int error;
-
- (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
- zc.zc_obj = obj;
-
- errno = 0;
- error = ioctl(di->zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc);
- di->zerr = errno;
-
- /* we can get stats even if we failed to get a path */
- (void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t));
- if (error == 0) {
- ASSERT(di->zerr == 0);
- (void) strlcpy(pn, zc.zc_value, maxlen);
- return (0);
- }
-
- if (di->zerr == ESTALE) {
- (void) snprintf(pn, maxlen, "(on_delete_queue)");
- return (0);
- } else if (di->zerr == EPERM) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "The sys_config privilege or diff delegated permission "
- "is needed\nto discover path names"));
- return (-1);
- } else {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Unable to determine path or stats for "
- "object %jd in %s"), (uintmax_t)obj, dsname);
- return (-1);
- }
-}
-
-/*
- * stream_bytes
- *
- * Prints a file name out a character at a time. If the character is
- * not in the range of what we consider "printable" ASCII, display it
- * as an escaped 3-digit octal value. ASCII values less than a space
- * are all control characters and we declare the upper end as the
- * DELete character. This also is the last 7-bit ASCII character.
- * We choose to treat all 8-bit ASCII as not printable for this
- * application.
- */
-static void
-stream_bytes(FILE *fp, const char *string)
-{
- char c;
-
- while ((c = *string++) != '\0') {
- if (c > ' ' && c != '\\' && c < '\177') {
- (void) fprintf(fp, "%c", c);
- } else {
- (void) fprintf(fp, "\\%03o", (uint8_t)c);
- }
- }
-}
-
-static void
-print_what(FILE *fp, mode_t what)
-{
- char symbol;
-
- switch (what & S_IFMT) {
- case S_IFBLK:
- symbol = 'B';
- break;
- case S_IFCHR:
- symbol = 'C';
- break;
- case S_IFDIR:
- symbol = '/';
- break;
-#ifdef S_IFDOOR
- case S_IFDOOR:
- symbol = '>';
- break;
-#endif
- case S_IFIFO:
- symbol = '|';
- break;
- case S_IFLNK:
- symbol = '@';
- break;
-#ifdef S_IFPORT
- case S_IFPORT:
- symbol = 'P';
- break;
-#endif
- case S_IFSOCK:
- symbol = '=';
- break;
- case S_IFREG:
- symbol = 'F';
- break;
- default:
- symbol = '?';
- break;
- }
- (void) fprintf(fp, "%c", symbol);
-}
-
-static void
-print_cmn(FILE *fp, differ_info_t *di, const char *file)
-{
- stream_bytes(fp, di->dsmnt);
- stream_bytes(fp, file);
-}
-
-static void
-print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new,
- zfs_stat_t *isb)
-{
- if (di->timestamped)
- (void) fprintf(fp, "%10lld.%09lld\t",
- (longlong_t)isb->zs_ctime[0],
- (longlong_t)isb->zs_ctime[1]);
- (void) fprintf(fp, "%c\t", ZDIFF_RENAMED);
- if (di->classify) {
- print_what(fp, isb->zs_mode);
- (void) fprintf(fp, "\t");
- }
- print_cmn(fp, di, old);
- if (di->scripted)
- (void) fprintf(fp, "\t");
- else
- (void) fprintf(fp, " -> ");
- print_cmn(fp, di, new);
- (void) fprintf(fp, "\n");
-}
-
-static void
-print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file,
- zfs_stat_t *isb)
-{
- if (di->timestamped)
- (void) fprintf(fp, "%10lld.%09lld\t",
- (longlong_t)isb->zs_ctime[0],
- (longlong_t)isb->zs_ctime[1]);
- (void) fprintf(fp, "%c\t", ZDIFF_MODIFIED);
- if (di->classify) {
- print_what(fp, isb->zs_mode);
- (void) fprintf(fp, "\t");
- }
- print_cmn(fp, di, file);
- (void) fprintf(fp, "\t(%+d)", delta);
- (void) fprintf(fp, "\n");
-}
-
-static void
-print_file(FILE *fp, differ_info_t *di, char type, const char *file,
- zfs_stat_t *isb)
-{
- if (di->timestamped)
- (void) fprintf(fp, "%10lld.%09lld\t",
- (longlong_t)isb->zs_ctime[0],
- (longlong_t)isb->zs_ctime[1]);
- (void) fprintf(fp, "%c\t", type);
- if (di->classify) {
- print_what(fp, isb->zs_mode);
- (void) fprintf(fp, "\t");
- }
- print_cmn(fp, di, file);
- (void) fprintf(fp, "\n");
-}
-
-static int
-write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj)
-{
- struct zfs_stat fsb, tsb;
- mode_t fmode, tmode;
- char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN];
- int fobjerr, tobjerr;
- int change;
-
- if (dobj == di->shares)
- return (0);
-
- /*
- * Check the from and to snapshots for info on the object. If
- * we get ENOENT, then the object just didn't exist in that
- * snapshot. If we get ENOTSUP, then we tried to get
- * info on a non-ZPL object, which we don't care about anyway.
- */
- fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname,
- MAXPATHLEN, &fsb);
- if (fobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
- return (-1);
-
- tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname,
- MAXPATHLEN, &tsb);
- if (tobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
- return (-1);
-
- /*
- * Unallocated object sharing the same meta dnode block
- */
- if (fobjerr && tobjerr) {
- ASSERT(di->zerr == ENOENT || di->zerr == ENOTSUP);
- di->zerr = 0;
- return (0);
- }
-
- di->zerr = 0; /* negate get_stats_for_obj() from side that failed */
- fmode = fsb.zs_mode & S_IFMT;
- tmode = tsb.zs_mode & S_IFMT;
- if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 ||
- tsb.zs_links == 0)
- change = 0;
- else
- change = tsb.zs_links - fsb.zs_links;
-
- if (fobjerr) {
- if (change) {
- print_link_change(fp, di, change, tobjname, &tsb);
- return (0);
- }
- print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
- return (0);
- } else if (tobjerr) {
- if (change) {
- print_link_change(fp, di, change, fobjname, &fsb);
- return (0);
- }
- print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
- return (0);
- }
-
- if (fmode != tmode && fsb.zs_gen == tsb.zs_gen)
- tsb.zs_gen++; /* Force a generational difference */
-
- /* Simple modification or no change */
- if (fsb.zs_gen == tsb.zs_gen) {
- /* No apparent changes. Could we assert !this? */
- if (fsb.zs_ctime[0] == tsb.zs_ctime[0] &&
- fsb.zs_ctime[1] == tsb.zs_ctime[1])
- return (0);
- if (change) {
- print_link_change(fp, di, change,
- change > 0 ? fobjname : tobjname, &tsb);
- } else if (strcmp(fobjname, tobjname) == 0) {
- print_file(fp, di, ZDIFF_MODIFIED, fobjname, &tsb);
- } else {
- print_rename(fp, di, fobjname, tobjname, &tsb);
- }
- return (0);
- } else {
- /* file re-created or object re-used */
- print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
- print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
- return (0);
- }
-}
-
-static int
-write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
-{
- uint64_t o;
- int err;
-
- for (o = dr->ddr_first; o <= dr->ddr_last; o++) {
- if ((err = write_inuse_diffs_one(fp, di, o)) != 0)
- return (err);
- }
- return (0);
-}
-
-static int
-describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf,
- int maxlen)
-{
- struct zfs_stat sb;
-
- if (get_stats_for_obj(di, di->fromsnap, object, namebuf,
- maxlen, &sb) != 0) {
- return (-1);
- }
- /* Don't print if in the delete queue on from side */
- if (di->zerr == ESTALE) {
- di->zerr = 0;
- return (0);
- }
-
- print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb);
- return (0);
-}
-
-static int
-write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *lhdl = di->zhp->zfs_hdl;
- char fobjname[MAXPATHLEN];
-
- (void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name));
- zc.zc_obj = dr->ddr_first - 1;
-
- ASSERT(di->zerr == 0);
-
- while (zc.zc_obj < dr->ddr_last) {
- int err;
-
- err = ioctl(lhdl->libzfs_fd, ZFS_IOC_NEXT_OBJ, &zc);
- if (err == 0) {
- if (zc.zc_obj == di->shares) {
- zc.zc_obj++;
- continue;
- }
- if (zc.zc_obj > dr->ddr_last) {
- break;
- }
- err = describe_free(fp, di, zc.zc_obj, fobjname,
- MAXPATHLEN);
- if (err)
- break;
- } else if (errno == ESRCH) {
- break;
- } else {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "next allocated object (> %jd) find failure"),
- (uintmax_t)zc.zc_obj);
- di->zerr = errno;
- break;
- }
- }
- if (di->zerr)
- return (-1);
- return (0);
-}
-
-static void *
-differ(void *arg)
-{
- differ_info_t *di = arg;
- dmu_diff_record_t dr;
- FILE *ofp;
- int err = 0;
-
- if ((ofp = fdopen(di->outputfd, "w")) == NULL) {
- di->zerr = errno;
- (void) strerror_r(errno, di->errbuf, sizeof (di->errbuf));
- (void) close(di->datafd);
- return ((void *)-1);
- }
-
- for (;;) {
- char *cp = (char *)&dr;
- int len = sizeof (dr);
- int rv;
-
- do {
- rv = read(di->datafd, cp, len);
- cp += rv;
- len -= rv;
- } while (len > 0 && rv > 0);
-
- if (rv < 0 || (rv == 0 && len != sizeof (dr))) {
- di->zerr = EPIPE;
- break;
- } else if (rv == 0) {
- /* end of file at a natural breaking point */
- break;
- }
-
- switch (dr.ddr_type) {
- case DDR_FREE:
- err = write_free_diffs(ofp, di, &dr);
- break;
- case DDR_INUSE:
- err = write_inuse_diffs(ofp, di, &dr);
- break;
- default:
- di->zerr = EPIPE;
- break;
- }
-
- if (err || di->zerr)
- break;
- }
-
- (void) fclose(ofp);
- (void) close(di->datafd);
- if (err)
- return ((void *)-1);
- if (di->zerr) {
- ASSERT(di->zerr == EPIPE);
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Internal error: bad data from diff IOCTL"));
- return ((void *)-1);
- }
- return ((void *)0);
-}
-
-static int
-find_shares_object(differ_info_t *di)
-{
- char fullpath[MAXPATHLEN];
- struct stat64 sb = { 0 };
-
- (void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
- (void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);
-
- if (stat64(fullpath, &sb) != 0) {
-#ifdef illumos
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
- return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
-#else
- return (0);
-#endif
- }
-
- di->shares = (uint64_t)sb.st_ino;
- return (0);
-}
-
-static int
-make_temp_snapshot(differ_info_t *di)
-{
- libzfs_handle_t *hdl = di->zhp->zfs_hdl;
- zfs_cmd_t zc = { 0 };
-
- (void) snprintf(zc.zc_value, sizeof (zc.zc_value),
- ZDIFF_PREFIX, getpid());
- (void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name));
- zc.zc_cleanup_fd = di->cleanupfd;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) {
- int err = errno;
- if (err == EPERM) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN, "The diff delegated "
- "permission is needed in order\nto create a "
- "just-in-time snapshot for diffing\n"));
- return (zfs_error(hdl, EZFS_DIFF, di->errbuf));
- } else {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN, "Cannot create just-in-time "
- "snapshot of '%s'"), zc.zc_name);
- return (zfs_standard_error(hdl, err, di->errbuf));
- }
- }
-
- di->tmpsnap = zfs_strdup(hdl, zc.zc_value);
- di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap);
- return (0);
-}
-
-static void
-teardown_differ_info(differ_info_t *di)
-{
- free(di->ds);
- free(di->dsmnt);
- free(di->fromsnap);
- free(di->frommnt);
- free(di->tosnap);
- free(di->tmpsnap);
- free(di->tomnt);
- (void) close(di->cleanupfd);
-}
-
-static int
-get_snapshot_names(differ_info_t *di, const char *fromsnap,
- const char *tosnap)
-{
- libzfs_handle_t *hdl = di->zhp->zfs_hdl;
- char *atptrf = NULL;
- char *atptrt = NULL;
- int fdslen, fsnlen;
- int tdslen, tsnlen;
-
- /*
- * Can accept
- * dataset@snap1
- * dataset@snap1 dataset@snap2
- * dataset@snap1 @snap2
- * dataset@snap1 dataset
- * @snap1 dataset@snap2
- */
- if (tosnap == NULL) {
- /* only a from snapshot given, must be valid */
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Badly formed snapshot name %s"), fromsnap);
-
- if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT,
- B_FALSE)) {
- return (zfs_error(hdl, EZFS_INVALIDNAME,
- di->errbuf));
- }
-
- atptrf = strchr(fromsnap, '@');
- ASSERT(atptrf != NULL);
- fdslen = atptrf - fromsnap;
-
- di->fromsnap = zfs_strdup(hdl, fromsnap);
- di->ds = zfs_strdup(hdl, fromsnap);
- di->ds[fdslen] = '\0';
-
- /* the to snap will be a just-in-time snap of the head */
- return (make_temp_snapshot(di));
- }
-
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Unable to determine which snapshots to compare"));
-
- atptrf = strchr(fromsnap, '@');
- atptrt = strchr(tosnap, '@');
- fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap);
- tdslen = atptrt ? atptrt - tosnap : strlen(tosnap);
- fsnlen = strlen(fromsnap) - fdslen; /* includes @ sign */
- tsnlen = strlen(tosnap) - tdslen; /* includes @ sign */
-
- if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0) ||
- (fsnlen == 0 && tsnlen == 0)) {
- return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
- } else if ((fdslen > 0 && tdslen > 0) &&
- ((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) {
- /*
- * not the same dataset name, might be okay if
- * tosnap is a clone of a fromsnap descendant.
- */
- char origin[ZFS_MAX_DATASET_NAME_LEN];
- zprop_source_t src;
- zfs_handle_t *zhp;
-
- di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1);
- (void) strncpy(di->ds, tosnap, tdslen);
- di->ds[tdslen] = '\0';
-
- zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM);
- while (zhp != NULL) {
- if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
- sizeof (origin), &src, NULL, 0, B_FALSE) != 0) {
- (void) zfs_close(zhp);
- zhp = NULL;
- break;
- }
- if (strncmp(origin, fromsnap, fsnlen) == 0)
- break;
-
- (void) zfs_close(zhp);
- zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM);
- }
-
- if (zhp == NULL) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
- } else {
- (void) zfs_close(zhp);
- }
-
- di->isclone = B_TRUE;
- di->fromsnap = zfs_strdup(hdl, fromsnap);
- if (tsnlen) {
- di->tosnap = zfs_strdup(hdl, tosnap);
- } else {
- return (make_temp_snapshot(di));
- }
- } else {
- int dslen = fdslen ? fdslen : tdslen;
-
- di->ds = zfs_alloc(hdl, dslen + 1);
- (void) strncpy(di->ds, fdslen ? fromsnap : tosnap, dslen);
- di->ds[dslen] = '\0';
-
- di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf);
- if (tsnlen) {
- di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt);
- } else {
- return (make_temp_snapshot(di));
- }
- }
- return (0);
-}
-
-static int
-get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt)
-{
- boolean_t mounted;
-
- mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt);
- if (mounted == B_FALSE) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Cannot diff an unmounted snapshot"));
- return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf));
- }
-
- /* Avoid a double slash at the beginning of root-mounted datasets */
- if (**mntpt == '/' && *(*mntpt + 1) == '\0')
- **mntpt = '\0';
- return (0);
-}
-
-static int
-get_mountpoints(differ_info_t *di)
-{
- char *strptr;
- char *frommntpt;
-
- /*
- * first get the mountpoint for the parent dataset
- */
- if (get_mountpoint(di, di->ds, &di->dsmnt) != 0)
- return (-1);
-
- strptr = strchr(di->tosnap, '@');
- ASSERT3P(strptr, !=, NULL);
- di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt,
- ZDIFF_SNAPDIR, ++strptr);
-
- strptr = strchr(di->fromsnap, '@');
- ASSERT3P(strptr, !=, NULL);
-
- frommntpt = di->dsmnt;
- if (di->isclone) {
- char *mntpt;
- int err;
-
- *strptr = '\0';
- err = get_mountpoint(di, di->fromsnap, &mntpt);
- *strptr = '@';
- if (err != 0)
- return (-1);
- frommntpt = mntpt;
- }
-
- di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt,
- ZDIFF_SNAPDIR, ++strptr);
-
- if (di->isclone)
- free(frommntpt);
-
- return (0);
-}
-
-static int
-setup_differ_info(zfs_handle_t *zhp, const char *fromsnap,
- const char *tosnap, differ_info_t *di)
-{
- di->zhp = zhp;
-
- di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL);
- VERIFY(di->cleanupfd >= 0);
-
- if (get_snapshot_names(di, fromsnap, tosnap) != 0)
- return (-1);
-
- if (get_mountpoints(di) != 0)
- return (-1);
-
- if (find_shares_object(di) != 0)
- return (-1);
-
- return (0);
-}
-
-int
-zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap,
- const char *tosnap, int flags)
-{
- zfs_cmd_t zc = { 0 };
- char errbuf[1024];
- differ_info_t di = { 0 };
- pthread_t tid;
- int pipefd[2];
- int iocerr;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "zfs diff failed"));
-
- if (setup_differ_info(zhp, fromsnap, tosnap, &di)) {
- teardown_differ_info(&di);
- return (-1);
- }
-
- if (pipe(pipefd)) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- teardown_differ_info(&di);
- return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf));
- }
-
- di.scripted = (flags & ZFS_DIFF_PARSEABLE);
- di.classify = (flags & ZFS_DIFF_CLASSIFY);
- di.timestamped = (flags & ZFS_DIFF_TIMESTAMP);
-
- di.outputfd = outfd;
- di.datafd = pipefd[0];
-
- if (pthread_create(&tid, NULL, differ, &di)) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- (void) close(pipefd[0]);
- (void) close(pipefd[1]);
- teardown_differ_info(&di);
- return (zfs_error(zhp->zfs_hdl,
- EZFS_THREADCREATEFAILED, errbuf));
- }
-
- /* do the ioctl() */
- (void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1);
- (void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1);
- zc.zc_cookie = pipefd[1];
-
- iocerr = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DIFF, &zc);
- if (iocerr != 0) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "Unable to obtain diffs"));
- if (errno == EPERM) {
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "\n The sys_mount privilege or diff delegated "
- "permission is needed\n to execute the "
- "diff ioctl"));
- } else if (errno == EXDEV) {
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "\n Not an earlier snapshot from the same fs"));
- } else if (errno != EPIPE || di.zerr == 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- }
- (void) close(pipefd[1]);
- (void) pthread_cancel(tid);
- (void) pthread_join(tid, NULL);
- teardown_differ_info(&di);
- if (di.zerr != 0 && di.zerr != EPIPE) {
- zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
- return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
- } else {
- return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf));
- }
- }
-
- (void) close(pipefd[1]);
- (void) pthread_join(tid, NULL);
-
- if (di.zerr != 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
- return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
- }
- teardown_differ_info(&di);
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c
deleted file mode 100644
index 474470c416ea..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <libintl.h>
-#include <link.h>
-#include <pthread.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include <libzfs.h>
-
-#include <fm/libtopo.h>
-#include <sys/fm/protocol.h>
-#include <sys/systeminfo.h>
-
-#include "libzfs_impl.h"
-
-/*
- * This file is responsible for determining the relationship between I/O
- * devices paths and physical locations. In the world of MPxIO and external
- * enclosures, the device path is not synonymous with the physical location.
- * If you remove a drive and insert it into a different slot, it will end up
- * with the same path under MPxIO. If you recable storage enclosures, the
- * device paths may change. All of this makes it difficult to implement the
- * 'autoreplace' property, which is supposed to automatically manage disk
- * replacement based on physical slot.
- *
- * In order to work around these limitations, we have a per-vdev FRU property
- * that is the libtopo path (minus disk-specific authority information) to the
- * physical location of the device on the system. This is an optional
- * property, and is only needed when using the 'autoreplace' property or when
- * generating FMA faults against vdevs.
- */
-
-/*
- * Because the FMA packages depend on ZFS, we have to dlopen() libtopo in case
- * it is not present. We only need this once per library instance, so it is
- * not part of the libzfs handle.
- */
-static void *_topo_dlhandle;
-static topo_hdl_t *(*_topo_open)(int, const char *, int *);
-static void (*_topo_close)(topo_hdl_t *);
-static char *(*_topo_snap_hold)(topo_hdl_t *, const char *, int *);
-static void (*_topo_snap_release)(topo_hdl_t *);
-static topo_walk_t *(*_topo_walk_init)(topo_hdl_t *, const char *,
- topo_walk_cb_t, void *, int *);
-static int (*_topo_walk_step)(topo_walk_t *, int);
-static void (*_topo_walk_fini)(topo_walk_t *);
-static void (*_topo_hdl_strfree)(topo_hdl_t *, char *);
-static char *(*_topo_node_name)(tnode_t *);
-static int (*_topo_prop_get_string)(tnode_t *, const char *, const char *,
- char **, int *);
-static int (*_topo_node_fru)(tnode_t *, nvlist_t **, nvlist_t *, int *);
-static int (*_topo_fmri_nvl2str)(topo_hdl_t *, nvlist_t *, char **, int *);
-static int (*_topo_fmri_strcmp_noauth)(topo_hdl_t *, const char *,
- const char *);
-
-#define ZFS_FRU_HASH_SIZE 257
-
-static size_t
-fru_strhash(const char *key)
-{
- ulong_t g, h = 0;
- const char *p;
-
- for (p = key; *p != '\0'; p++) {
- h = (h << 4) + *p;
-
- if ((g = (h & 0xf0000000)) != 0) {
- h ^= (g >> 24);
- h ^= g;
- }
- }
-
- return (h % ZFS_FRU_HASH_SIZE);
-}
-
-static int
-libzfs_fru_gather(topo_hdl_t *thp, tnode_t *tn, void *arg)
-{
- libzfs_handle_t *hdl = arg;
- nvlist_t *fru;
- char *devpath, *frustr;
- int err;
- libzfs_fru_t *frup;
- size_t idx;
-
- /*
- * If this is the chassis node, and we don't yet have the system
- * chassis ID, then fill in this value now.
- */
- if (hdl->libzfs_chassis_id[0] == '\0' &&
- strcmp(_topo_node_name(tn), "chassis") == 0) {
- if (_topo_prop_get_string(tn, FM_FMRI_AUTHORITY,
- FM_FMRI_AUTH_CHASSIS, &devpath, &err) == 0)
- (void) strlcpy(hdl->libzfs_chassis_id, devpath,
- sizeof (hdl->libzfs_chassis_id));
- }
-
- /*
- * Skip non-disk nodes.
- */
- if (strcmp(_topo_node_name(tn), "disk") != 0)
- return (TOPO_WALK_NEXT);
-
- /*
- * Get the devfs path and FRU.
- */
- if (_topo_prop_get_string(tn, "io", "devfs-path", &devpath, &err) != 0)
- return (TOPO_WALK_NEXT);
-
- if (libzfs_fru_lookup(hdl, devpath) != NULL) {
- _topo_hdl_strfree(thp, devpath);
- return (TOPO_WALK_NEXT);
- }
-
- if (_topo_node_fru(tn, &fru, NULL, &err) != 0) {
- _topo_hdl_strfree(thp, devpath);
- return (TOPO_WALK_NEXT);
- }
-
- /*
- * Convert the FRU into a string.
- */
- if (_topo_fmri_nvl2str(thp, fru, &frustr, &err) != 0) {
- nvlist_free(fru);
- _topo_hdl_strfree(thp, devpath);
- return (TOPO_WALK_NEXT);
- }
-
- nvlist_free(fru);
-
- /*
- * Finally, we have a FRU string and device path. Add it to the hash.
- */
- if ((frup = calloc(sizeof (libzfs_fru_t), 1)) == NULL) {
- _topo_hdl_strfree(thp, devpath);
- _topo_hdl_strfree(thp, frustr);
- return (TOPO_WALK_NEXT);
- }
-
- if ((frup->zf_device = strdup(devpath)) == NULL ||
- (frup->zf_fru = strdup(frustr)) == NULL) {
- free(frup->zf_device);
- free(frup);
- _topo_hdl_strfree(thp, devpath);
- _topo_hdl_strfree(thp, frustr);
- return (TOPO_WALK_NEXT);
- }
-
- _topo_hdl_strfree(thp, devpath);
- _topo_hdl_strfree(thp, frustr);
-
- idx = fru_strhash(frup->zf_device);
- frup->zf_chain = hdl->libzfs_fru_hash[idx];
- hdl->libzfs_fru_hash[idx] = frup;
- frup->zf_next = hdl->libzfs_fru_list;
- hdl->libzfs_fru_list = frup;
-
- return (TOPO_WALK_NEXT);
-}
-
-/*
- * Called during initialization to setup the dynamic libtopo connection.
- */
-#pragma init(libzfs_init_fru)
-static void
-libzfs_init_fru(void)
-{
- char path[MAXPATHLEN];
- char isa[257];
-
-#if defined(_LP64)
- if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
- isa[0] = '\0';
-#else
- isa[0] = '\0';
-#endif
- (void) snprintf(path, sizeof (path),
- "/usr/lib/fm/%s/libtopo.so", isa);
-
- if ((_topo_dlhandle = dlopen(path, RTLD_LAZY)) == NULL)
- return;
-
- _topo_open = (topo_hdl_t *(*)())
- dlsym(_topo_dlhandle, "topo_open");
- _topo_close = (void (*)())
- dlsym(_topo_dlhandle, "topo_close");
- _topo_snap_hold = (char *(*)())
- dlsym(_topo_dlhandle, "topo_snap_hold");
- _topo_snap_release = (void (*)())
- dlsym(_topo_dlhandle, "topo_snap_release");
- _topo_walk_init = (topo_walk_t *(*)())
- dlsym(_topo_dlhandle, "topo_walk_init");
- _topo_walk_step = (int (*)())
- dlsym(_topo_dlhandle, "topo_walk_step");
- _topo_walk_fini = (void (*)())
- dlsym(_topo_dlhandle, "topo_walk_fini");
- _topo_hdl_strfree = (void (*)())
- dlsym(_topo_dlhandle, "topo_hdl_strfree");
- _topo_node_name = (char *(*)())
- dlsym(_topo_dlhandle, "topo_node_name");
- _topo_prop_get_string = (int (*)())
- dlsym(_topo_dlhandle, "topo_prop_get_string");
- _topo_node_fru = (int (*)())
- dlsym(_topo_dlhandle, "topo_node_fru");
- _topo_fmri_nvl2str = (int (*)())
- dlsym(_topo_dlhandle, "topo_fmri_nvl2str");
- _topo_fmri_strcmp_noauth = (int (*)())
- dlsym(_topo_dlhandle, "topo_fmri_strcmp_noauth");
-
- if (_topo_open == NULL || _topo_close == NULL ||
- _topo_snap_hold == NULL || _topo_snap_release == NULL ||
- _topo_walk_init == NULL || _topo_walk_step == NULL ||
- _topo_walk_fini == NULL || _topo_hdl_strfree == NULL ||
- _topo_node_name == NULL || _topo_prop_get_string == NULL ||
- _topo_node_fru == NULL || _topo_fmri_nvl2str == NULL ||
- _topo_fmri_strcmp_noauth == NULL) {
- (void) dlclose(_topo_dlhandle);
- _topo_dlhandle = NULL;
- }
-}
-
-/*
- * Refresh the mappings from device path -> FMRI. We do this by walking the
- * hc topology looking for disk nodes, and recording the io/devfs-path and FRU.
- * Note that we strip out the disk-specific authority information (serial,
- * part, revision, etc) so that we are left with only the identifying
- * characteristics of the slot (hc path and chassis-id).
- */
-void
-libzfs_fru_refresh(libzfs_handle_t *hdl)
-{
- int err;
- char *uuid;
- topo_hdl_t *thp;
- topo_walk_t *twp;
-
- if (_topo_dlhandle == NULL)
- return;
-
- /*
- * Clear the FRU hash and initialize our basic structures.
- */
- libzfs_fru_clear(hdl, B_FALSE);
-
- if ((hdl->libzfs_topo_hdl = _topo_open(TOPO_VERSION,
- NULL, &err)) == NULL)
- return;
-
- thp = hdl->libzfs_topo_hdl;
-
- if ((uuid = _topo_snap_hold(thp, NULL, &err)) == NULL)
- return;
-
- _topo_hdl_strfree(thp, uuid);
-
- if (hdl->libzfs_fru_hash == NULL &&
- (hdl->libzfs_fru_hash =
- calloc(ZFS_FRU_HASH_SIZE, sizeof (void *))) == NULL)
- return;
-
- /*
- * We now have a topo snapshot, so iterate over the hc topology looking
- * for disks to add to the hash.
- */
- twp = _topo_walk_init(thp, FM_FMRI_SCHEME_HC,
- libzfs_fru_gather, hdl, &err);
- if (twp != NULL) {
- (void) _topo_walk_step(twp, TOPO_WALK_CHILD);
- _topo_walk_fini(twp);
- }
-}
-
-/*
- * Given a devfs path, return the FRU for the device, if known. This will
- * automatically call libzfs_fru_refresh() if it hasn't already been called by
- * the consumer. The string returned is valid until the next call to
- * libzfs_fru_refresh().
- */
-const char *
-libzfs_fru_lookup(libzfs_handle_t *hdl, const char *devpath)
-{
- size_t idx = fru_strhash(devpath);
- libzfs_fru_t *frup;
-
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_fru_hash == NULL)
- return (NULL);
-
- for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL;
- frup = frup->zf_chain) {
- if (strcmp(devpath, frup->zf_device) == 0)
- return (frup->zf_fru);
- }
-
- return (NULL);
-}
-
-/*
- * Given a fru path, return the device path. This will automatically call
- * libzfs_fru_refresh() if it hasn't already been called by the consumer. The
- * string returned is valid until the next call to libzfs_fru_refresh().
- */
-const char *
-libzfs_fru_devpath(libzfs_handle_t *hdl, const char *fru)
-{
- libzfs_fru_t *frup;
- size_t idx;
-
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_fru_hash == NULL)
- return (NULL);
-
- for (idx = 0; idx < ZFS_FRU_HASH_SIZE; idx++) {
- for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL;
- frup = frup->zf_next) {
- if (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl,
- fru, frup->zf_fru))
- return (frup->zf_device);
- }
- }
-
- return (NULL);
-}
-
-/*
- * Change the stored FRU for the given vdev.
- */
-int
-zpool_fru_set(zpool_handle_t *zhp, uint64_t vdev_guid, const char *fru)
-{
- zfs_cmd_t zc = { 0 };
-
- (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- (void) strncpy(zc.zc_value, fru, sizeof (zc.zc_value));
- zc.zc_guid = vdev_guid;
-
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SETFRU, &zc) != 0)
- return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot set FRU")));
-
- return (0);
-}
-
-/*
- * Compare to two FRUs, ignoring any authority information.
- */
-boolean_t
-libzfs_fru_compare(libzfs_handle_t *hdl, const char *a, const char *b)
-{
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_fru_hash == NULL)
- return (strcmp(a, b) == 0);
-
- return (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl, a, b));
-}
-
-/*
- * This special function checks to see whether the FRU indicates it's supposed
- * to be in the system chassis, but the chassis-id doesn't match. This can
- * happen in a clustered case, where both head nodes have the same logical
- * disk, but opening the device on the other head node is meaningless.
- */
-boolean_t
-libzfs_fru_notself(libzfs_handle_t *hdl, const char *fru)
-{
- const char *chassisid;
- size_t len;
-
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_chassis_id[0] == '\0')
- return (B_FALSE);
-
- if (strstr(fru, "/chassis=0/") == NULL)
- return (B_FALSE);
-
- if ((chassisid = strstr(fru, ":chassis-id=")) == NULL)
- return (B_FALSE);
-
- chassisid += 12;
- len = strlen(hdl->libzfs_chassis_id);
- if (strncmp(chassisid, hdl->libzfs_chassis_id, len) == 0 &&
- (chassisid[len] == '/' || chassisid[len] == ':'))
- return (B_FALSE);
-
- return (B_TRUE);
-}
-
-/*
- * Clear memory associated with the FRU hash.
- */
-void
-libzfs_fru_clear(libzfs_handle_t *hdl, boolean_t final)
-{
- libzfs_fru_t *frup;
-
- while ((frup = hdl->libzfs_fru_list) != NULL) {
- hdl->libzfs_fru_list = frup->zf_next;
- free(frup->zf_device);
- free(frup->zf_fru);
- free(frup);
- }
-
- hdl->libzfs_fru_list = NULL;
-
- if (hdl->libzfs_topo_hdl != NULL) {
- _topo_snap_release(hdl->libzfs_topo_hdl);
- _topo_close(hdl->libzfs_topo_hdl);
- hdl->libzfs_topo_hdl = NULL;
- }
-
- if (final) {
- free(hdl->libzfs_fru_hash);
- } else if (hdl->libzfs_fru_hash != NULL) {
- bzero(hdl->libzfs_fru_hash,
- ZFS_FRU_HASH_SIZE * sizeof (void *));
- }
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
deleted file mode 100644
index a0338afadb8f..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * CDDL HEADER SART
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
- * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#ifndef _LIBZFS_IMPL_H
-#define _LIBZFS_IMPL_H
-
-#include <sys/fs/zfs.h>
-#include <sys/spa.h>
-#include <sys/nvpair.h>
-#include <sys/dmu.h>
-#include <sys/zfs_ioctl.h>
-
-#include <libshare.h>
-#include <libuutil.h>
-#include <libzfs.h>
-#include <libzfs_core.h>
-#include <libzfs_compat.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef VERIFY
-#undef VERIFY
-#endif
-#define VERIFY verify
-
-typedef struct libzfs_fru {
- char *zf_device;
- char *zf_fru;
- struct libzfs_fru *zf_chain;
- struct libzfs_fru *zf_next;
-} libzfs_fru_t;
-
-struct libzfs_handle {
- int libzfs_error;
- int libzfs_fd;
- FILE *libzfs_mnttab;
- FILE *libzfs_sharetab;
- zpool_handle_t *libzfs_pool_handles;
- uu_avl_pool_t *libzfs_ns_avlpool;
- uu_avl_t *libzfs_ns_avl;
- uint64_t libzfs_ns_gen;
- int libzfs_desc_active;
- char libzfs_action[1024];
- char libzfs_desc[1024];
- int libzfs_printerr;
- int libzfs_storeerr; /* stuff error messages into buffer */
- void *libzfs_sharehdl; /* libshare handle */
- boolean_t libzfs_mnttab_enable;
- /*
- * We need a lock to handle the case where parallel mount
- * threads are populating the mnttab cache simultaneously. The
- * lock only protects the integrity of the avl tree, and does
- * not protect the contents of the mnttab entries themselves.
- */
- pthread_mutex_t libzfs_mnttab_cache_lock;
- avl_tree_t libzfs_mnttab_cache;
- int libzfs_pool_iter;
- libzfs_fru_t **libzfs_fru_hash;
- libzfs_fru_t *libzfs_fru_list;
- char libzfs_chassis_id[256];
- boolean_t libzfs_prop_debug;
-};
-
-struct zfs_handle {
- libzfs_handle_t *zfs_hdl;
- zpool_handle_t *zpool_hdl;
- char zfs_name[ZFS_MAX_DATASET_NAME_LEN];
- zfs_type_t zfs_type; /* type including snapshot */
- zfs_type_t zfs_head_type; /* type excluding snapshot */
- dmu_objset_stats_t zfs_dmustats;
- nvlist_t *zfs_props;
- nvlist_t *zfs_user_props;
- nvlist_t *zfs_recvd_props;
- boolean_t zfs_mntcheck;
- char *zfs_mntopts;
- uint8_t *zfs_props_table;
-};
-
-/*
- * This is different from checking zfs_type, because it will also catch
- * snapshots of volumes.
- */
-#define ZFS_IS_VOLUME(zhp) ((zhp)->zfs_head_type == ZFS_TYPE_VOLUME)
-
-struct zpool_handle {
- libzfs_handle_t *zpool_hdl;
- zpool_handle_t *zpool_next;
- char zpool_name[ZFS_MAX_DATASET_NAME_LEN];
- int zpool_state;
- size_t zpool_config_size;
- nvlist_t *zpool_config;
- nvlist_t *zpool_old_config;
- nvlist_t *zpool_props;
- diskaddr_t zpool_start_block;
-};
-
-typedef enum {
- PROTO_NFS = 0,
- PROTO_SMB = 1,
- PROTO_END = 2
-} zfs_share_proto_t;
-
-/*
- * The following can be used as a bitmask and any new values
- * added must preserve that capability.
- */
-typedef enum {
- SHARED_NOT_SHARED = 0x0,
- SHARED_NFS = 0x2,
- SHARED_SMB = 0x4
-} zfs_share_type_t;
-
-#define CONFIG_BUF_MINSIZE 262144
-
-int zfs_error(libzfs_handle_t *, int, const char *);
-int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
-void zfs_error_aux(libzfs_handle_t *, const char *, ...);
-void *zfs_alloc(libzfs_handle_t *, size_t);
-void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t);
-char *zfs_asprintf(libzfs_handle_t *, const char *, ...);
-char *zfs_strdup(libzfs_handle_t *, const char *);
-int no_memory(libzfs_handle_t *);
-
-int zfs_standard_error(libzfs_handle_t *, int, const char *);
-int zfs_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
-int zpool_standard_error(libzfs_handle_t *, int, const char *);
-int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
-
-int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
- size_t *);
-zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
-zfs_handle_t *make_dataset_simple_handle_zc(zfs_handle_t *, zfs_cmd_t *);
-
-int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
- nvlist_t *, char **, uint64_t *, const char *);
-int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
- zfs_type_t type);
-
-/*
- * Use this changelist_gather() flag to force attempting mounts
- * on each change node regardless of whether or not it is currently
- * mounted.
- */
-#define CL_GATHER_MOUNT_ALWAYS 0x01
-/*
- * Use this changelist_gather() flag to prevent unmounting of file systems.
- */
-#define CL_GATHER_DONT_UNMOUNT 0x02
-
-typedef struct prop_changelist prop_changelist_t;
-
-int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t);
-int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
-int zcmd_write_conf_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
-int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *);
-int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **);
-void zcmd_free_nvlists(zfs_cmd_t *);
-
-int changelist_prefix(prop_changelist_t *);
-int changelist_postfix(prop_changelist_t *);
-void changelist_rename(prop_changelist_t *, const char *, const char *);
-void changelist_remove(prop_changelist_t *, const char *);
-void changelist_free(prop_changelist_t *);
-prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int, int);
-int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *);
-int changelist_haszonedchild(prop_changelist_t *);
-
-void remove_mountpoint(zfs_handle_t *);
-int create_parents(libzfs_handle_t *, char *, int);
-boolean_t isa_child_of(const char *dataset, const char *parent);
-
-zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
-zfs_handle_t *make_bookmark_handle(zfs_handle_t *, const char *,
- nvlist_t *props);
-
-int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
-
-boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
-
-int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
- boolean_t modifying);
-
-void namespace_clear(libzfs_handle_t *);
-
-/*
- * libshare (sharemgr) interfaces used internally.
- */
-
-extern int zfs_init_libshare(libzfs_handle_t *, int);
-extern int zfs_parse_options(char *, zfs_share_proto_t);
-
-extern int zfs_unshare_proto(zfs_handle_t *,
- const char *, zfs_share_proto_t *);
-
-extern void libzfs_fru_clear(libzfs_handle_t *, boolean_t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_IMPL_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
deleted file mode 100644
index 87c8dd14898b..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
+++ /dev/null
@@ -1,1929 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
- * Copyright 2015 RackTop Systems.
- * Copyright 2016 Nexenta Systems, Inc.
- */
-
-/*
- * Pool import support functions.
- *
- * To import a pool, we rely on reading the configuration information from the
- * ZFS label of each device. If we successfully read the label, then we
- * organize the configuration information in the following hierarchy:
- *
- * pool guid -> toplevel vdev guid -> label txg
- *
- * Duplicate entries matching this same tuple will be discarded. Once we have
- * examined every device, we pick the best label txg config for each toplevel
- * vdev. We then arrange these toplevel vdevs into a complete pool config, and
- * update any paths that have changed. Finally, we attempt to import the pool
- * using our derived config, and record the results.
- */
-
-#include <aio.h>
-#include <ctype.h>
-#include <devid.h>
-#include <dirent.h>
-#include <errno.h>
-#include <libintl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <thread_pool.h>
-#include <libgeom.h>
-
-#include <sys/vdev_impl.h>
-
-#include "libzfs.h"
-#include "libzfs_impl.h"
-
-/*
- * Intermediate structures used to gather configuration information.
- */
-typedef struct config_entry {
- uint64_t ce_txg;
- nvlist_t *ce_config;
- struct config_entry *ce_next;
-} config_entry_t;
-
-typedef struct vdev_entry {
- uint64_t ve_guid;
- config_entry_t *ve_configs;
- struct vdev_entry *ve_next;
-} vdev_entry_t;
-
-typedef struct pool_entry {
- uint64_t pe_guid;
- vdev_entry_t *pe_vdevs;
- struct pool_entry *pe_next;
-} pool_entry_t;
-
-typedef struct name_entry {
- char *ne_name;
- uint64_t ne_guid;
- struct name_entry *ne_next;
-} name_entry_t;
-
-typedef struct pool_list {
- pool_entry_t *pools;
- name_entry_t *names;
-} pool_list_t;
-
-static char *
-get_devid(const char *path)
-{
-#ifdef have_devid
- int fd;
- ddi_devid_t devid;
- char *minor, *ret;
-
- if ((fd = open(path, O_RDONLY)) < 0)
- return (NULL);
-
- minor = NULL;
- ret = NULL;
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0)
- ret = devid_str_encode(devid, minor);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
- (void) close(fd);
-
- return (ret);
-#else
- return (NULL);
-#endif
-}
-
-
-/*
- * Go through and fix up any path and/or devid information for the given vdev
- * configuration.
- */
-static int
-fix_paths(nvlist_t *nv, name_entry_t *names)
-{
- nvlist_t **child;
- uint_t c, children;
- uint64_t guid;
- name_entry_t *ne, *best;
- char *path, *devid;
- int matched;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if (fix_paths(child[c], names) != 0)
- return (-1);
- return (0);
- }
-
- /*
- * This is a leaf (file or disk) vdev. In either case, go through
- * the name list and see if we find a matching guid. If so, replace
- * the path and see if we can calculate a new devid.
- *
- * There may be multiple names associated with a particular guid, in
- * which case we have overlapping slices or multiple paths to the same
- * disk. If this is the case, then we want to pick the path that is
- * the most similar to the original, where "most similar" is the number
- * of matching characters starting from the end of the path. This will
- * preserve slice numbers even if the disks have been reorganized, and
- * will also catch preferred disk names if multiple paths exist.
- */
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
- path = NULL;
-
- matched = 0;
- best = NULL;
- for (ne = names; ne != NULL; ne = ne->ne_next) {
- if (ne->ne_guid == guid) {
- const char *src, *dst;
- int count;
-
- if (path == NULL) {
- best = ne;
- break;
- }
-
- src = ne->ne_name + strlen(ne->ne_name) - 1;
- dst = path + strlen(path) - 1;
- for (count = 0; src >= ne->ne_name && dst >= path;
- src--, dst--, count++)
- if (*src != *dst)
- break;
-
- /*
- * At this point, 'count' is the number of characters
- * matched from the end.
- */
- if (count > matched || best == NULL) {
- best = ne;
- matched = count;
- }
- }
- }
-
- if (best == NULL)
- return (0);
-
- if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0)
- return (-1);
-
- if ((devid = get_devid(best->ne_name)) == NULL) {
- (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
- } else {
- if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) {
- devid_str_free(devid);
- return (-1);
- }
- devid_str_free(devid);
- }
-
- return (0);
-}
-
-/*
- * Add the given configuration to the list of known devices.
- */
-static int
-add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path,
- nvlist_t *config)
-{
- uint64_t pool_guid, vdev_guid, top_guid, txg, state;
- pool_entry_t *pe;
- vdev_entry_t *ve;
- config_entry_t *ce;
- name_entry_t *ne;
-
- /*
- * If this is a hot spare not currently in use or level 2 cache
- * device, add it to the list of names to translate, but don't do
- * anything else.
- */
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &state) == 0 &&
- (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) &&
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) {
- if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
- return (-1);
-
- if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) {
- free(ne);
- return (-1);
- }
-
- ne->ne_guid = vdev_guid;
- ne->ne_next = pl->names;
- pl->names = ne;
-
- return (0);
- }
-
- /*
- * If we have a valid config but cannot read any of these fields, then
- * it means we have a half-initialized label. In vdev_label_init()
- * we write a label with txg == 0 so that we can identify the device
- * in case the user refers to the same disk later on. If we fail to
- * create the pool, we'll be left with a label in this state
- * which should not be considered part of a valid pool.
- */
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &pool_guid) != 0 ||
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
- &vdev_guid) != 0 ||
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID,
- &top_guid) != 0 ||
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
- &txg) != 0 || txg == 0) {
- return (0);
- }
-
- /*
- * First, see if we know about this pool. If not, then add it to the
- * list of known pools.
- */
- for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
- if (pe->pe_guid == pool_guid)
- break;
- }
-
- if (pe == NULL) {
- if ((pe = zfs_alloc(hdl, sizeof (pool_entry_t))) == NULL) {
- return (-1);
- }
- pe->pe_guid = pool_guid;
- pe->pe_next = pl->pools;
- pl->pools = pe;
- }
-
- /*
- * Second, see if we know about this toplevel vdev. Add it if its
- * missing.
- */
- for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
- if (ve->ve_guid == top_guid)
- break;
- }
-
- if (ve == NULL) {
- if ((ve = zfs_alloc(hdl, sizeof (vdev_entry_t))) == NULL) {
- return (-1);
- }
- ve->ve_guid = top_guid;
- ve->ve_next = pe->pe_vdevs;
- pe->pe_vdevs = ve;
- }
-
- /*
- * Third, see if we have a config with a matching transaction group. If
- * so, then we do nothing. Otherwise, add it to the list of known
- * configs.
- */
- for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) {
- if (ce->ce_txg == txg)
- break;
- }
-
- if (ce == NULL) {
- if ((ce = zfs_alloc(hdl, sizeof (config_entry_t))) == NULL) {
- return (-1);
- }
- ce->ce_txg = txg;
- ce->ce_config = fnvlist_dup(config);
- ce->ce_next = ve->ve_configs;
- ve->ve_configs = ce;
- }
-
- /*
- * At this point we've successfully added our config to the list of
- * known configs. The last thing to do is add the vdev guid -> path
- * mappings so that we can fix up the configuration as necessary before
- * doing the import.
- */
- if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
- return (-1);
-
- if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) {
- free(ne);
- return (-1);
- }
-
- ne->ne_guid = vdev_guid;
- ne->ne_next = pl->names;
- pl->names = ne;
-
- return (0);
-}
-
-/*
- * Returns true if the named pool matches the given GUID.
- */
-static int
-pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid,
- boolean_t *isactive)
-{
- zpool_handle_t *zhp;
- uint64_t theguid;
-
- if (zpool_open_silent(hdl, name, &zhp) != 0)
- return (-1);
-
- if (zhp == NULL) {
- *isactive = B_FALSE;
- return (0);
- }
-
- verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID,
- &theguid) == 0);
-
- zpool_close(zhp);
-
- *isactive = (theguid == guid);
- return (0);
-}
-
-static nvlist_t *
-refresh_config(libzfs_handle_t *hdl, nvlist_t *config)
-{
- nvlist_t *nvl;
- zfs_cmd_t zc = { 0 };
- int err, dstbuf_size;
-
- if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0)
- return (NULL);
-
- dstbuf_size = MAX(CONFIG_BUF_MINSIZE, zc.zc_nvlist_conf_size * 4);
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, dstbuf_size) != 0) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
-
- while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT,
- &zc)) != 0 && errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
- }
-
- if (err) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &nvl) != 0) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
-
- zcmd_free_nvlists(&zc);
- return (nvl);
-}
-
-/*
- * Determine if the vdev id is a hole in the namespace.
- */
-boolean_t
-vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id)
-{
- for (int c = 0; c < holes; c++) {
-
- /* Top-level is a hole */
- if (hole_array[c] == id)
- return (B_TRUE);
- }
- return (B_FALSE);
-}
-
-/*
- * Convert our list of pools into the definitive set of configurations. We
- * start by picking the best config for each toplevel vdev. Once that's done,
- * we assemble the toplevel vdevs into a full config for the pool. We make a
- * pass to fix up any incorrect paths, and then add it to the main list to
- * return to the user.
- */
-static nvlist_t *
-get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok,
- nvlist_t *policy)
-{
- pool_entry_t *pe;
- vdev_entry_t *ve;
- config_entry_t *ce;
- nvlist_t *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot;
- nvlist_t **spares, **l2cache;
- uint_t i, nspares, nl2cache;
- boolean_t config_seen;
- uint64_t best_txg;
- char *name, *hostname = NULL;
- uint64_t guid;
- uint_t children = 0;
- nvlist_t **child = NULL;
- uint_t holes;
- uint64_t *hole_array, max_id;
- uint_t c;
- boolean_t isactive;
- uint64_t hostid;
- nvlist_t *nvl;
- boolean_t found_one = B_FALSE;
- boolean_t valid_top_config = B_FALSE;
-
- if (nvlist_alloc(&ret, 0, 0) != 0)
- goto nomem;
-
- for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
- uint64_t id, max_txg = 0;
-
- if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0)
- goto nomem;
- config_seen = B_FALSE;
-
- /*
- * Iterate over all toplevel vdevs. Grab the pool configuration
- * from the first one we find, and then go through the rest and
- * add them as necessary to the 'vdevs' member of the config.
- */
- for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
-
- /*
- * Determine the best configuration for this vdev by
- * selecting the config with the latest transaction
- * group.
- */
- best_txg = 0;
- for (ce = ve->ve_configs; ce != NULL;
- ce = ce->ce_next) {
-
- if (ce->ce_txg > best_txg) {
- tmp = ce->ce_config;
- best_txg = ce->ce_txg;
- }
- }
-
- /*
- * We rely on the fact that the max txg for the
- * pool will contain the most up-to-date information
- * about the valid top-levels in the vdev namespace.
- */
- if (best_txg > max_txg) {
- (void) nvlist_remove(config,
- ZPOOL_CONFIG_VDEV_CHILDREN,
- DATA_TYPE_UINT64);
- (void) nvlist_remove(config,
- ZPOOL_CONFIG_HOLE_ARRAY,
- DATA_TYPE_UINT64_ARRAY);
-
- max_txg = best_txg;
- hole_array = NULL;
- holes = 0;
- max_id = 0;
- valid_top_config = B_FALSE;
-
- if (nvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) {
- verify(nvlist_add_uint64(config,
- ZPOOL_CONFIG_VDEV_CHILDREN,
- max_id) == 0);
- valid_top_config = B_TRUE;
- }
-
- if (nvlist_lookup_uint64_array(tmp,
- ZPOOL_CONFIG_HOLE_ARRAY, &hole_array,
- &holes) == 0) {
- verify(nvlist_add_uint64_array(config,
- ZPOOL_CONFIG_HOLE_ARRAY,
- hole_array, holes) == 0);
- }
- }
-
- if (!config_seen) {
- /*
- * Copy the relevant pieces of data to the pool
- * configuration:
- *
- * version
- * pool guid
- * name
- * comment (if available)
- * pool state
- * hostid (if available)
- * hostname (if available)
- */
- uint64_t state, version;
- char *comment = NULL;
-
- version = fnvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_VERSION);
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_VERSION, version);
- guid = fnvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_POOL_GUID);
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_POOL_GUID, guid);
- name = fnvlist_lookup_string(tmp,
- ZPOOL_CONFIG_POOL_NAME);
- fnvlist_add_string(config,
- ZPOOL_CONFIG_POOL_NAME, name);
-
- if (nvlist_lookup_string(tmp,
- ZPOOL_CONFIG_COMMENT, &comment) == 0)
- fnvlist_add_string(config,
- ZPOOL_CONFIG_COMMENT, comment);
-
- state = fnvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_POOL_STATE);
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_POOL_STATE, state);
-
- hostid = 0;
- if (nvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_HOSTID, hostid);
- hostname = fnvlist_lookup_string(tmp,
- ZPOOL_CONFIG_HOSTNAME);
- fnvlist_add_string(config,
- ZPOOL_CONFIG_HOSTNAME, hostname);
- }
-
- config_seen = B_TRUE;
- }
-
- /*
- * Add this top-level vdev to the child array.
- */
- verify(nvlist_lookup_nvlist(tmp,
- ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0);
- verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID,
- &id) == 0);
-
- if (id >= children) {
- nvlist_t **newchild;
-
- newchild = zfs_alloc(hdl, (id + 1) *
- sizeof (nvlist_t *));
- if (newchild == NULL)
- goto nomem;
-
- for (c = 0; c < children; c++)
- newchild[c] = child[c];
-
- free(child);
- child = newchild;
- children = id + 1;
- }
- if (nvlist_dup(nvtop, &child[id], 0) != 0)
- goto nomem;
-
- }
-
- /*
- * If we have information about all the top-levels then
- * clean up the nvlist which we've constructed. This
- * means removing any extraneous devices that are
- * beyond the valid range or adding devices to the end
- * of our array which appear to be missing.
- */
- if (valid_top_config) {
- if (max_id < children) {
- for (c = max_id; c < children; c++)
- nvlist_free(child[c]);
- children = max_id;
- } else if (max_id > children) {
- nvlist_t **newchild;
-
- newchild = zfs_alloc(hdl, (max_id) *
- sizeof (nvlist_t *));
- if (newchild == NULL)
- goto nomem;
-
- for (c = 0; c < children; c++)
- newchild[c] = child[c];
-
- free(child);
- child = newchild;
- children = max_id;
- }
- }
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
-
- /*
- * The vdev namespace may contain holes as a result of
- * device removal. We must add them back into the vdev
- * tree before we process any missing devices.
- */
- if (holes > 0) {
- ASSERT(valid_top_config);
-
- for (c = 0; c < children; c++) {
- nvlist_t *holey;
-
- if (child[c] != NULL ||
- !vdev_is_hole(hole_array, holes, c))
- continue;
-
- if (nvlist_alloc(&holey, NV_UNIQUE_NAME,
- 0) != 0)
- goto nomem;
-
- /*
- * Holes in the namespace are treated as
- * "hole" top-level vdevs and have a
- * special flag set on them.
- */
- if (nvlist_add_string(holey,
- ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_HOLE) != 0 ||
- nvlist_add_uint64(holey,
- ZPOOL_CONFIG_ID, c) != 0 ||
- nvlist_add_uint64(holey,
- ZPOOL_CONFIG_GUID, 0ULL) != 0) {
- nvlist_free(holey);
- goto nomem;
- }
- child[c] = holey;
- }
- }
-
- /*
- * Look for any missing top-level vdevs. If this is the case,
- * create a faked up 'missing' vdev as a placeholder. We cannot
- * simply compress the child array, because the kernel performs
- * certain checks to make sure the vdev IDs match their location
- * in the configuration.
- */
- for (c = 0; c < children; c++) {
- if (child[c] == NULL) {
- nvlist_t *missing;
- if (nvlist_alloc(&missing, NV_UNIQUE_NAME,
- 0) != 0)
- goto nomem;
- if (nvlist_add_string(missing,
- ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_MISSING) != 0 ||
- nvlist_add_uint64(missing,
- ZPOOL_CONFIG_ID, c) != 0 ||
- nvlist_add_uint64(missing,
- ZPOOL_CONFIG_GUID, 0ULL) != 0) {
- nvlist_free(missing);
- goto nomem;
- }
- child[c] = missing;
- }
- }
-
- /*
- * Put all of this pool's top-level vdevs into a root vdev.
- */
- if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0)
- goto nomem;
- if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_ROOT) != 0 ||
- nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 ||
- nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 ||
- nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- child, children) != 0) {
- nvlist_free(nvroot);
- goto nomem;
- }
-
- for (c = 0; c < children; c++)
- nvlist_free(child[c]);
- free(child);
- children = 0;
- child = NULL;
-
- /*
- * Go through and fix up any paths and/or devids based on our
- * known list of vdev GUID -> path mappings.
- */
- if (fix_paths(nvroot, pl->names) != 0) {
- nvlist_free(nvroot);
- goto nomem;
- }
-
- /*
- * Add the root vdev to this pool's configuration.
- */
- if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- nvroot) != 0) {
- nvlist_free(nvroot);
- goto nomem;
- }
- nvlist_free(nvroot);
-
- /*
- * zdb uses this path to report on active pools that were
- * imported or created using -R.
- */
- if (active_ok)
- goto add_pool;
-
- /*
- * Determine if this pool is currently active, in which case we
- * can't actually import it.
- */
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
-
- if (pool_active(hdl, name, guid, &isactive) != 0)
- goto error;
-
- if (isactive) {
- nvlist_free(config);
- config = NULL;
- continue;
- }
-
- if (policy != NULL) {
- if (nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY,
- policy) != 0)
- goto nomem;
- }
-
- if ((nvl = refresh_config(hdl, config)) == NULL) {
- nvlist_free(config);
- config = NULL;
- continue;
- }
-
- nvlist_free(config);
- config = nvl;
-
- /*
- * Go through and update the paths for spares, now that we have
- * them.
- */
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- if (fix_paths(spares[i], pl->names) != 0)
- goto nomem;
- }
- }
-
- /*
- * Update the paths for l2cache devices.
- */
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
- &l2cache, &nl2cache) == 0) {
- for (i = 0; i < nl2cache; i++) {
- if (fix_paths(l2cache[i], pl->names) != 0)
- goto nomem;
- }
- }
-
- /*
- * Restore the original information read from the actual label.
- */
- (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID,
- DATA_TYPE_UINT64);
- (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTNAME,
- DATA_TYPE_STRING);
- if (hostid != 0) {
- verify(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID,
- hostid) == 0);
- verify(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME,
- hostname) == 0);
- }
-
-add_pool:
- /*
- * Add this pool to the list of configs.
- */
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- if (nvlist_add_nvlist(ret, name, config) != 0)
- goto nomem;
-
- found_one = B_TRUE;
- nvlist_free(config);
- config = NULL;
- }
-
- if (!found_one) {
- nvlist_free(ret);
- ret = NULL;
- }
-
- return (ret);
-
-nomem:
- (void) no_memory(hdl);
-error:
- nvlist_free(config);
- nvlist_free(ret);
- for (c = 0; c < children; c++)
- nvlist_free(child[c]);
- free(child);
-
- return (NULL);
-}
-
-/*
- * Return the offset of the given label.
- */
-static uint64_t
-label_offset(uint64_t size, int l)
-{
- ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0);
- return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ?
- 0 : size - VDEV_LABELS * sizeof (vdev_label_t)));
-}
-
-/*
- * Given a file descriptor, read the label information and return an nvlist
- * describing the configuration, if there is one.
- * Return 0 on success, or -1 on failure
- */
-int
-zpool_read_label(int fd, nvlist_t **config)
-{
- struct stat64 statbuf;
- int l;
- vdev_label_t *label;
- uint64_t state, txg, size;
-
- *config = NULL;
-
- if (fstat64(fd, &statbuf) == -1)
- return (-1);
- size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
-
- if ((label = malloc(sizeof (vdev_label_t))) == NULL)
- return (-1);
-
- for (l = 0; l < VDEV_LABELS; l++) {
- if (pread64(fd, label, sizeof (vdev_label_t),
- label_offset(size, l)) != sizeof (vdev_label_t))
- continue;
-
- if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
- sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0)
- continue;
-
- if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
- &state) != 0 || state > POOL_STATE_L2CACHE) {
- nvlist_free(*config);
- continue;
- }
-
- if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
- (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
- &txg) != 0 || txg == 0)) {
- nvlist_free(*config);
- continue;
- }
-
- free(label);
- return (0);
- }
-
- free(label);
- *config = NULL;
- errno = ENOENT;
- return (-1);
-}
-
-/*
- * Given a file descriptor, read the label information and return an nvlist
- * describing the configuration, if there is one.
- * returns the number of valid labels found
- * If a label is found, returns it via config. The caller is responsible for
- * freeing it.
- */
-int
-zpool_read_all_labels(int fd, nvlist_t **config)
-{
- struct stat64 statbuf;
- struct aiocb aiocbs[VDEV_LABELS];
- struct aiocb *aiocbps[VDEV_LABELS];
- int l;
- vdev_phys_t *labels;
- uint64_t state, txg, size;
- int nlabels = 0;
-
- *config = NULL;
-
- if (fstat64(fd, &statbuf) == -1)
- return (0);
- size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
-
- if ((labels = calloc(VDEV_LABELS, sizeof (vdev_phys_t))) == NULL)
- return (0);
-
- memset(aiocbs, 0, sizeof(aiocbs));
- for (l = 0; l < VDEV_LABELS; l++) {
- aiocbs[l].aio_fildes = fd;
- aiocbs[l].aio_offset = label_offset(size, l) + VDEV_SKIP_SIZE;
- aiocbs[l].aio_buf = &labels[l];
- aiocbs[l].aio_nbytes = sizeof(vdev_phys_t);
- aiocbs[l].aio_lio_opcode = LIO_READ;
- aiocbps[l] = &aiocbs[l];
- }
-
- if (lio_listio(LIO_WAIT, aiocbps, VDEV_LABELS, NULL) != 0) {
- if (errno == EAGAIN || errno == EINTR || errno == EIO) {
- for (l = 0; l < VDEV_LABELS; l++) {
- errno = 0;
- int r = aio_error(&aiocbs[l]);
- if (r != EINVAL)
- (void)aio_return(&aiocbs[l]);
- }
- }
- free(labels);
- return (0);
- }
-
- for (l = 0; l < VDEV_LABELS; l++) {
- nvlist_t *temp = NULL;
-
- if (aio_return(&aiocbs[l]) != sizeof(vdev_phys_t))
- continue;
-
- if (nvlist_unpack(labels[l].vp_nvlist,
- sizeof (labels[l].vp_nvlist), &temp, 0) != 0)
- continue;
-
- if (nvlist_lookup_uint64(temp, ZPOOL_CONFIG_POOL_STATE,
- &state) != 0 || state > POOL_STATE_L2CACHE) {
- nvlist_free(temp);
- temp = NULL;
- continue;
- }
-
- if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
- (nvlist_lookup_uint64(temp, ZPOOL_CONFIG_POOL_TXG,
- &txg) != 0 || txg == 0)) {
- nvlist_free(temp);
- temp = NULL;
- continue;
- }
- if (temp)
- *config = temp;
-
- nlabels++;
- }
-
- free(labels);
- return (nlabels);
-}
-
-typedef struct rdsk_node {
- char *rn_name;
- int rn_dfd;
- libzfs_handle_t *rn_hdl;
- nvlist_t *rn_config;
- avl_tree_t *rn_avl;
- avl_node_t rn_node;
- boolean_t rn_nozpool;
-} rdsk_node_t;
-
-static int
-slice_cache_compare(const void *arg1, const void *arg2)
-{
- const char *nm1 = ((rdsk_node_t *)arg1)->rn_name;
- const char *nm2 = ((rdsk_node_t *)arg2)->rn_name;
- char *nm1slice, *nm2slice;
- int rv;
-
- /*
- * slices zero and two are the most likely to provide results,
- * so put those first
- */
- nm1slice = strstr(nm1, "s0");
- nm2slice = strstr(nm2, "s0");
- if (nm1slice && !nm2slice) {
- return (-1);
- }
- if (!nm1slice && nm2slice) {
- return (1);
- }
- nm1slice = strstr(nm1, "s2");
- nm2slice = strstr(nm2, "s2");
- if (nm1slice && !nm2slice) {
- return (-1);
- }
- if (!nm1slice && nm2slice) {
- return (1);
- }
-
- rv = strcmp(nm1, nm2);
- if (rv == 0)
- return (0);
- return (rv > 0 ? 1 : -1);
-}
-
-#ifdef illumos
-static void
-check_one_slice(avl_tree_t *r, char *diskname, uint_t partno,
- diskaddr_t size, uint_t blksz)
-{
- rdsk_node_t tmpnode;
- rdsk_node_t *node;
- char sname[MAXNAMELEN];
-
- tmpnode.rn_name = &sname[0];
- (void) snprintf(tmpnode.rn_name, MAXNAMELEN, "%s%u",
- diskname, partno);
- /*
- * protect against division by zero for disk labels that
- * contain a bogus sector size
- */
- if (blksz == 0)
- blksz = DEV_BSIZE;
- /* too small to contain a zpool? */
- if ((size < (SPA_MINDEVSIZE / blksz)) &&
- (node = avl_find(r, &tmpnode, NULL)))
- node->rn_nozpool = B_TRUE;
-}
-#endif /* illumos */
-
-static void
-nozpool_all_slices(avl_tree_t *r, const char *sname)
-{
-#ifdef illumos
- char diskname[MAXNAMELEN];
- char *ptr;
- int i;
-
- (void) strncpy(diskname, sname, MAXNAMELEN);
- if (((ptr = strrchr(diskname, 's')) == NULL) &&
- ((ptr = strrchr(diskname, 'p')) == NULL))
- return;
- ptr[0] = 's';
- ptr[1] = '\0';
- for (i = 0; i < NDKMAP; i++)
- check_one_slice(r, diskname, i, 0, 1);
- ptr[0] = 'p';
- for (i = 0; i <= FD_NUMPART; i++)
- check_one_slice(r, diskname, i, 0, 1);
-#endif /* illumos */
-}
-
-#ifdef illumos
-static void
-check_slices(avl_tree_t *r, int fd, const char *sname)
-{
- struct extvtoc vtoc;
- struct dk_gpt *gpt;
- char diskname[MAXNAMELEN];
- char *ptr;
- int i;
-
- (void) strncpy(diskname, sname, MAXNAMELEN);
- if ((ptr = strrchr(diskname, 's')) == NULL || !isdigit(ptr[1]))
- return;
- ptr[1] = '\0';
-
- if (read_extvtoc(fd, &vtoc) >= 0) {
- for (i = 0; i < NDKMAP; i++)
- check_one_slice(r, diskname, i,
- vtoc.v_part[i].p_size, vtoc.v_sectorsz);
- } else if (efi_alloc_and_read(fd, &gpt) >= 0) {
- /*
- * on x86 we'll still have leftover links that point
- * to slices s[9-15], so use NDKMAP instead
- */
- for (i = 0; i < NDKMAP; i++)
- check_one_slice(r, diskname, i,
- gpt->efi_parts[i].p_size, gpt->efi_lbasize);
- /* nodes p[1-4] are never used with EFI labels */
- ptr[0] = 'p';
- for (i = 1; i <= FD_NUMPART; i++)
- check_one_slice(r, diskname, i, 0, 1);
- efi_free(gpt);
- }
-}
-#endif /* illumos */
-
-static void
-zpool_open_func(void *arg)
-{
- rdsk_node_t *rn = arg;
- struct stat64 statbuf;
- nvlist_t *config;
- int fd;
-
- if (rn->rn_nozpool)
- return;
- if ((fd = openat64(rn->rn_dfd, rn->rn_name, O_RDONLY)) < 0) {
- /* symlink to a device that's no longer there */
- if (errno == ENOENT)
- nozpool_all_slices(rn->rn_avl, rn->rn_name);
- return;
- }
- /*
- * Ignore failed stats. We only want regular
- * files, character devs and block devs.
- */
- if (fstat64(fd, &statbuf) != 0 ||
- (!S_ISREG(statbuf.st_mode) &&
- !S_ISCHR(statbuf.st_mode) &&
- !S_ISBLK(statbuf.st_mode))) {
- (void) close(fd);
- return;
- }
- /* this file is too small to hold a zpool */
-#ifdef illumos
- if (S_ISREG(statbuf.st_mode) &&
- statbuf.st_size < SPA_MINDEVSIZE) {
- (void) close(fd);
- return;
- } else if (!S_ISREG(statbuf.st_mode)) {
- /*
- * Try to read the disk label first so we don't have to
- * open a bunch of minor nodes that can't have a zpool.
- */
- check_slices(rn->rn_avl, fd, rn->rn_name);
- }
-#else /* !illumos */
- if (statbuf.st_size < SPA_MINDEVSIZE) {
- (void) close(fd);
- return;
- }
-#endif /* illumos */
-
- if ((zpool_read_label(fd, &config)) != 0 && errno == ENOMEM) {
- (void) close(fd);
- (void) no_memory(rn->rn_hdl);
- return;
- }
- (void) close(fd);
-
- rn->rn_config = config;
-}
-
-/*
- * Given a file descriptor, clear (zero) the label information.
- */
-int
-zpool_clear_label(int fd)
-{
- struct stat64 statbuf;
- int l;
- vdev_label_t *label;
- uint64_t size;
-
- if (fstat64(fd, &statbuf) == -1)
- return (0);
- size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
-
- if ((label = calloc(sizeof (vdev_label_t), 1)) == NULL)
- return (-1);
-
- for (l = 0; l < VDEV_LABELS; l++) {
- if (pwrite64(fd, label, sizeof (vdev_label_t),
- label_offset(size, l)) != sizeof (vdev_label_t)) {
- free(label);
- return (-1);
- }
- }
-
- free(label);
- return (0);
-}
-
-/*
- * Given a list of directories to search, find all pools stored on disk. This
- * includes partial pools which are not available to import. If no args are
- * given (argc is 0), then the default directory (/dev/dsk) is searched.
- * poolname or guid (but not both) are provided by the caller when trying
- * to import a specific pool.
- */
-static nvlist_t *
-zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
-{
- int i, dirs = iarg->paths;
- struct dirent64 *dp;
- char path[MAXPATHLEN];
- char *end, **dir = iarg->path;
- size_t pathleft;
- nvlist_t *ret = NULL;
- static char *default_dir = "/dev";
- pool_list_t pools = { 0 };
- pool_entry_t *pe, *penext;
- vdev_entry_t *ve, *venext;
- config_entry_t *ce, *cenext;
- name_entry_t *ne, *nenext;
- avl_tree_t slice_cache;
- rdsk_node_t *slice;
- void *cookie;
- boolean_t skip_zvols = B_FALSE;
- int value;
- size_t size = sizeof(value);
-
- if (dirs == 0) {
- dirs = 1;
- dir = &default_dir;
- }
-
- if (sysctlbyname("vfs.zfs.vol.recursive", &value, &size, NULL, 0) == 0
- && value == 0) {
- skip_zvols = B_TRUE;
- }
-
- /*
- * Go through and read the label configuration information from every
- * possible device, organizing the information according to pool GUID
- * and toplevel GUID.
- */
- for (i = 0; i < dirs; i++) {
- tpool_t *t;
- char rdsk[MAXPATHLEN];
- int dfd;
- boolean_t config_failed = B_FALSE;
- DIR *dirp;
-
- /* use realpath to normalize the path */
- if (realpath(dir[i], path) == 0) {
- (void) zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]);
- goto error;
- }
- end = &path[strlen(path)];
- *end++ = '/';
- *end = 0;
- pathleft = &path[sizeof (path)] - end;
-
-#ifdef illumos
- /*
- * Using raw devices instead of block devices when we're
- * reading the labels skips a bunch of slow operations during
- * close(2) processing, so we replace /dev/dsk with /dev/rdsk.
- */
- if (strcmp(path, ZFS_DISK_ROOTD) == 0)
- (void) strlcpy(rdsk, ZFS_RDISK_ROOTD, sizeof (rdsk));
- else
-#endif
- (void) strlcpy(rdsk, path, sizeof (rdsk));
-
- if ((dfd = open64(rdsk, O_RDONLY)) < 0 ||
- (dirp = fdopendir(dfd)) == NULL) {
- if (dfd >= 0)
- (void) close(dfd);
- zfs_error_aux(hdl, strerror(errno));
- (void) zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"),
- rdsk);
- goto error;
- }
-
- avl_create(&slice_cache, slice_cache_compare,
- sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node));
-
- if (strcmp(rdsk, "/dev/") == 0) {
- struct gmesh mesh;
- struct gclass *mp;
- struct ggeom *gp;
- struct gprovider *pp;
-
- errno = geom_gettree(&mesh);
- if (errno != 0) {
- zfs_error_aux(hdl, strerror(errno));
- (void) zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "cannot get GEOM tree"));
- goto error;
- }
-
- LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
- if (skip_zvols &&
- strcmp(mp->lg_name, "ZFS::ZVOL") == 0) {
- continue;
- }
- LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
- LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
- slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
- slice->rn_name = zfs_strdup(hdl, pp->lg_name);
- slice->rn_avl = &slice_cache;
- slice->rn_dfd = dfd;
- slice->rn_hdl = hdl;
- slice->rn_nozpool = B_FALSE;
- avl_add(&slice_cache, slice);
- }
- }
- }
-
- geom_deletetree(&mesh);
- goto skipdir;
- }
-
- /*
- * This is not MT-safe, but we have no MT consumers of libzfs
- */
- while ((dp = readdir64(dirp)) != NULL) {
- const char *name = dp->d_name;
- if (name[0] == '.' &&
- (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
- continue;
-
- slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
- slice->rn_name = zfs_strdup(hdl, name);
- slice->rn_avl = &slice_cache;
- slice->rn_dfd = dfd;
- slice->rn_hdl = hdl;
- slice->rn_nozpool = B_FALSE;
- avl_add(&slice_cache, slice);
- }
-skipdir:
- /*
- * create a thread pool to do all of this in parallel;
- * rn_nozpool is not protected, so this is racy in that
- * multiple tasks could decide that the same slice can
- * not hold a zpool, which is benign. Also choose
- * double the number of processors; we hold a lot of
- * locks in the kernel, so going beyond this doesn't
- * buy us much.
- */
- t = tpool_create(1, 2 * sysconf(_SC_NPROCESSORS_ONLN),
- 0, NULL);
- for (slice = avl_first(&slice_cache); slice;
- (slice = avl_walk(&slice_cache, slice,
- AVL_AFTER)))
- (void) tpool_dispatch(t, zpool_open_func, slice);
- tpool_wait(t);
- tpool_destroy(t);
-
- cookie = NULL;
- while ((slice = avl_destroy_nodes(&slice_cache,
- &cookie)) != NULL) {
- if (slice->rn_config != NULL && !config_failed) {
- nvlist_t *config = slice->rn_config;
- boolean_t matched = B_TRUE;
-
- if (iarg->poolname != NULL) {
- char *pname;
-
- matched = nvlist_lookup_string(config,
- ZPOOL_CONFIG_POOL_NAME,
- &pname) == 0 &&
- strcmp(iarg->poolname, pname) == 0;
- } else if (iarg->guid != 0) {
- uint64_t this_guid;
-
- matched = nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_POOL_GUID,
- &this_guid) == 0 &&
- iarg->guid == this_guid;
- }
- if (matched) {
- /*
- * use the non-raw path for the config
- */
- (void) strlcpy(end, slice->rn_name,
- pathleft);
- if (add_config(hdl, &pools, path,
- config) != 0)
- config_failed = B_TRUE;
- }
- nvlist_free(config);
- }
- free(slice->rn_name);
- free(slice);
- }
- avl_destroy(&slice_cache);
-
- (void) closedir(dirp);
-
- if (config_failed)
- goto error;
- }
-
- ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy);
-
-error:
- for (pe = pools.pools; pe != NULL; pe = penext) {
- penext = pe->pe_next;
- for (ve = pe->pe_vdevs; ve != NULL; ve = venext) {
- venext = ve->ve_next;
- for (ce = ve->ve_configs; ce != NULL; ce = cenext) {
- cenext = ce->ce_next;
- nvlist_free(ce->ce_config);
- free(ce);
- }
- free(ve);
- }
- free(pe);
- }
-
- for (ne = pools.names; ne != NULL; ne = nenext) {
- nenext = ne->ne_next;
- free(ne->ne_name);
- free(ne);
- }
-
- return (ret);
-}
-
-nvlist_t *
-zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
-{
- importargs_t iarg = { 0 };
-
- iarg.paths = argc;
- iarg.path = argv;
-
- return (zpool_find_import_impl(hdl, &iarg));
-}
-
-/*
- * Given a cache file, return the contents as a list of importable pools.
- * poolname or guid (but not both) are provided by the caller when trying
- * to import a specific pool.
- */
-nvlist_t *
-zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile,
- char *poolname, uint64_t guid)
-{
- char *buf;
- int fd;
- struct stat64 statbuf;
- nvlist_t *raw, *src, *dst;
- nvlist_t *pools;
- nvpair_t *elem;
- char *name;
- uint64_t this_guid;
- boolean_t active;
-
- verify(poolname == NULL || guid == 0);
-
- if ((fd = open(cachefile, O_RDONLY)) < 0) {
- zfs_error_aux(hdl, "%s", strerror(errno));
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN, "failed to open cache file"));
- return (NULL);
- }
-
- if (fstat64(fd, &statbuf) != 0) {
- zfs_error_aux(hdl, "%s", strerror(errno));
- (void) close(fd);
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN, "failed to get size of cache file"));
- return (NULL);
- }
-
- if ((buf = zfs_alloc(hdl, statbuf.st_size)) == NULL) {
- (void) close(fd);
- return (NULL);
- }
-
- if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
- (void) close(fd);
- free(buf);
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN,
- "failed to read cache file contents"));
- return (NULL);
- }
-
- (void) close(fd);
-
- if (nvlist_unpack(buf, statbuf.st_size, &raw, 0) != 0) {
- free(buf);
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN,
- "invalid or corrupt cache file contents"));
- return (NULL);
- }
-
- free(buf);
-
- /*
- * Go through and get the current state of the pools and refresh their
- * state.
- */
- if (nvlist_alloc(&pools, 0, 0) != 0) {
- (void) no_memory(hdl);
- nvlist_free(raw);
- return (NULL);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(raw, elem)) != NULL) {
- src = fnvpair_value_nvlist(elem);
-
- name = fnvlist_lookup_string(src, ZPOOL_CONFIG_POOL_NAME);
- if (poolname != NULL && strcmp(poolname, name) != 0)
- continue;
-
- this_guid = fnvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID);
- if (guid != 0 && guid != this_guid)
- continue;
-
- if (pool_active(hdl, name, this_guid, &active) != 0) {
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
-
- if (active)
- continue;
-
- if (nvlist_add_string(src, ZPOOL_CONFIG_CACHEFILE,
- cachefile) != 0) {
- (void) no_memory(hdl);
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
-
- if ((dst = refresh_config(hdl, src)) == NULL) {
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
-
- if (nvlist_add_nvlist(pools, nvpair_name(elem), dst) != 0) {
- (void) no_memory(hdl);
- nvlist_free(dst);
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
- nvlist_free(dst);
- }
-
- nvlist_free(raw);
- return (pools);
-}
-
-static int
-name_or_guid_exists(zpool_handle_t *zhp, void *data)
-{
- importargs_t *import = data;
- int found = 0;
-
- if (import->poolname != NULL) {
- char *pool_name;
-
- verify(nvlist_lookup_string(zhp->zpool_config,
- ZPOOL_CONFIG_POOL_NAME, &pool_name) == 0);
- if (strcmp(pool_name, import->poolname) == 0)
- found = 1;
- } else {
- uint64_t pool_guid;
-
- verify(nvlist_lookup_uint64(zhp->zpool_config,
- ZPOOL_CONFIG_POOL_GUID, &pool_guid) == 0);
- if (pool_guid == import->guid)
- found = 1;
- }
-
- zpool_close(zhp);
- return (found);
-}
-
-nvlist_t *
-zpool_search_import(libzfs_handle_t *hdl, importargs_t *import)
-{
- nvlist_t *pools = NULL;
-
- verify(import->poolname == NULL || import->guid == 0);
-
- if (import->unique)
- import->exists = zpool_iter(hdl, name_or_guid_exists, import);
-
- if (import->cachefile != NULL)
- pools = zpool_find_import_cached(hdl, import->cachefile,
- import->poolname, import->guid);
- else
- pools = zpool_find_import_impl(hdl, import);
-
- return (pools);
-}
-
-static boolean_t
-pool_match(nvlist_t *cfg, char *tgt)
-{
- uint64_t v, guid = strtoull(tgt, NULL, 0);
- char *s;
-
- if (guid != 0) {
- if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &v) == 0)
- return (v == guid);
- } else {
- if (nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &s) == 0)
- return (strcmp(s, tgt) == 0);
- }
- return (B_FALSE);
-}
-
-int
-zpool_tryimport(libzfs_handle_t *hdl, char *target, nvlist_t **configp,
- importargs_t *args)
-{
- nvlist_t *pools;
- nvlist_t *match = NULL;
- nvlist_t *config = NULL;
- char *sepp = NULL;
- int count = 0;
- char *targetdup = strdup(target);
-
- *configp = NULL;
-
- if ((sepp = strpbrk(targetdup, "/@")) != NULL) {
- *sepp = '\0';
- }
-
- pools = zpool_search_import(hdl, args);
-
- if (pools != NULL) {
- nvpair_t *elem = NULL;
- while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) {
- VERIFY0(nvpair_value_nvlist(elem, &config));
- if (pool_match(config, targetdup)) {
- count++;
- if (match != NULL) {
- /* multiple matches found */
- continue;
- } else {
- match = config;
- }
- }
- }
- }
-
- if (count == 0) {
- free(targetdup);
- return (ENOENT);
- }
-
- if (count > 1) {
- free(targetdup);
- return (EINVAL);
- }
-
- *configp = match;
- free(targetdup);
-
- return (0);
-}
-
-boolean_t
-find_guid(nvlist_t *nv, uint64_t guid)
-{
- uint64_t tmp;
- nvlist_t **child;
- uint_t c, children;
-
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &tmp) == 0);
- if (tmp == guid)
- return (B_TRUE);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if (find_guid(child[c], guid))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-typedef struct aux_cbdata {
- const char *cb_type;
- uint64_t cb_guid;
- zpool_handle_t *cb_zhp;
-} aux_cbdata_t;
-
-static int
-find_aux(zpool_handle_t *zhp, void *data)
-{
- aux_cbdata_t *cbp = data;
- nvlist_t **list;
- uint_t i, count;
- uint64_t guid;
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- if (nvlist_lookup_nvlist_array(nvroot, cbp->cb_type,
- &list, &count) == 0) {
- for (i = 0; i < count; i++) {
- verify(nvlist_lookup_uint64(list[i],
- ZPOOL_CONFIG_GUID, &guid) == 0);
- if (guid == cbp->cb_guid) {
- cbp->cb_zhp = zhp;
- return (1);
- }
- }
- }
-
- zpool_close(zhp);
- return (0);
-}
-
-/*
- * Determines if the pool is in use. If so, it returns true and the state of
- * the pool as well as the name of the pool. Both strings are allocated and
- * must be freed by the caller.
- */
-int
-zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
- boolean_t *inuse)
-{
- nvlist_t *config;
- char *name;
- boolean_t ret;
- uint64_t guid, vdev_guid;
- zpool_handle_t *zhp;
- nvlist_t *pool_config;
- uint64_t stateval, isspare;
- aux_cbdata_t cb = { 0 };
- boolean_t isactive;
-
- *inuse = B_FALSE;
-
- if (zpool_read_label(fd, &config) != 0 && errno == ENOMEM) {
- (void) no_memory(hdl);
- return (-1);
- }
-
- if (config == NULL)
- return (0);
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &stateval) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
- &vdev_guid) == 0);
-
- if (stateval != POOL_STATE_SPARE && stateval != POOL_STATE_L2CACHE) {
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
- }
-
- switch (stateval) {
- case POOL_STATE_EXPORTED:
- /*
- * A pool with an exported state may in fact be imported
- * read-only, so check the in-core state to see if it's
- * active and imported read-only. If it is, set
- * its state to active.
- */
- if (pool_active(hdl, name, guid, &isactive) == 0 && isactive &&
- (zhp = zpool_open_canfail(hdl, name)) != NULL) {
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_READONLY, NULL))
- stateval = POOL_STATE_ACTIVE;
-
- /*
- * All we needed the zpool handle for is the
- * readonly prop check.
- */
- zpool_close(zhp);
- }
-
- ret = B_TRUE;
- break;
-
- case POOL_STATE_ACTIVE:
- /*
- * For an active pool, we have to determine if it's really part
- * of a currently active pool (in which case the pool will exist
- * and the guid will be the same), or whether it's part of an
- * active pool that was disconnected without being explicitly
- * exported.
- */
- if (pool_active(hdl, name, guid, &isactive) != 0) {
- nvlist_free(config);
- return (-1);
- }
-
- if (isactive) {
- /*
- * Because the device may have been removed while
- * offlined, we only report it as active if the vdev is
- * still present in the config. Otherwise, pretend like
- * it's not in use.
- */
- if ((zhp = zpool_open_canfail(hdl, name)) != NULL &&
- (pool_config = zpool_get_config(zhp, NULL))
- != NULL) {
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(pool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- ret = find_guid(nvroot, vdev_guid);
- } else {
- ret = B_FALSE;
- }
-
- /*
- * If this is an active spare within another pool, we
- * treat it like an unused hot spare. This allows the
- * user to create a pool with a hot spare that currently
- * in use within another pool. Since we return B_TRUE,
- * libdiskmgt will continue to prevent generic consumers
- * from using the device.
- */
- if (ret && nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_IS_SPARE, &isspare) == 0 && isspare)
- stateval = POOL_STATE_SPARE;
-
- if (zhp != NULL)
- zpool_close(zhp);
- } else {
- stateval = POOL_STATE_POTENTIALLY_ACTIVE;
- ret = B_TRUE;
- }
- break;
-
- case POOL_STATE_SPARE:
- /*
- * For a hot spare, it can be either definitively in use, or
- * potentially active. To determine if it's in use, we iterate
- * over all pools in the system and search for one with a spare
- * with a matching guid.
- *
- * Due to the shared nature of spares, we don't actually report
- * the potentially active case as in use. This means the user
- * can freely create pools on the hot spares of exported pools,
- * but to do otherwise makes the resulting code complicated, and
- * we end up having to deal with this case anyway.
- */
- cb.cb_zhp = NULL;
- cb.cb_guid = vdev_guid;
- cb.cb_type = ZPOOL_CONFIG_SPARES;
- if (zpool_iter(hdl, find_aux, &cb) == 1) {
- name = (char *)zpool_get_name(cb.cb_zhp);
- ret = B_TRUE;
- } else {
- ret = B_FALSE;
- }
- break;
-
- case POOL_STATE_L2CACHE:
-
- /*
- * Check if any pool is currently using this l2cache device.
- */
- cb.cb_zhp = NULL;
- cb.cb_guid = vdev_guid;
- cb.cb_type = ZPOOL_CONFIG_L2CACHE;
- if (zpool_iter(hdl, find_aux, &cb) == 1) {
- name = (char *)zpool_get_name(cb.cb_zhp);
- ret = B_TRUE;
- } else {
- ret = B_FALSE;
- }
- break;
-
- default:
- ret = B_FALSE;
- }
-
-
- if (ret) {
- if ((*namestr = zfs_strdup(hdl, name)) == NULL) {
- if (cb.cb_zhp)
- zpool_close(cb.cb_zhp);
- nvlist_free(config);
- return (-1);
- }
- *state = (pool_state_t)stateval;
- }
-
- if (cb.cb_zhp)
- zpool_close(cb.cb_zhp);
-
- nvlist_free(config);
- *inuse = ret;
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c
deleted file mode 100644
index 36138676e7db..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <libintl.h>
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-
-int
-zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- nvlist_t *nvl = zfs_get_clones_nvl(zhp);
- nvpair_t *pair;
-
- if (nvl == NULL)
- return (0);
-
- for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
- pair = nvlist_next_nvpair(nvl, pair)) {
- zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (clone != NULL) {
- int err = func(clone, data);
- if (err != 0)
- return (err);
- }
- }
- return (0);
-}
-
-static int
-zfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
-{
- int rc;
- uint64_t orig_cookie;
-
- orig_cookie = zc->zc_cookie;
-top:
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
- rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
-
- if (rc == -1) {
- switch (errno) {
- case ENOMEM:
- /* expand nvlist memory and try again */
- if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
- zcmd_free_nvlists(zc);
- return (-1);
- }
- zc->zc_cookie = orig_cookie;
- goto top;
- /*
- * An errno value of ESRCH indicates normal completion.
- * If ENOENT is returned, then the underlying dataset
- * has been removed since we obtained the handle.
- */
- case ESRCH:
- case ENOENT:
- rc = 1;
- break;
- default:
- rc = zfs_standard_error(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot iterate filesystems"));
- break;
- }
- }
- return (rc);
-}
-
-/*
- * Iterate over all child filesystems
- */
-int
-zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
-
- if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
- return (0);
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
- &zc)) == 0) {
- /*
- * Silently ignore errors, as the only plausible explanation is
- * that the pool has since been removed.
- */
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
-
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
-}
-
-/*
- * Iterate over all snapshots
- */
-int
-zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
- void *data, uint64_t min_txg, uint64_t max_txg)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
- nvlist_t *range_nvl = NULL;
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
- zhp->zfs_type == ZFS_TYPE_BOOKMARK)
- return (0);
-
- zc.zc_simple = simple;
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- if (min_txg != 0) {
- range_nvl = fnvlist_alloc();
- fnvlist_add_uint64(range_nvl, SNAP_ITER_MIN_TXG, min_txg);
- }
- if (max_txg != 0) {
- if (range_nvl == NULL)
- range_nvl = fnvlist_alloc();
- fnvlist_add_uint64(range_nvl, SNAP_ITER_MAX_TXG, max_txg);
- }
-
- if (range_nvl != NULL &&
- zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, range_nvl) != 0) {
- zcmd_free_nvlists(&zc);
- fnvlist_free(range_nvl);
- return (-1);
- }
-
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
- &zc)) == 0) {
-
- if (simple)
- nzhp = make_dataset_simple_handle_zc(zhp, &zc);
- else
- nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
- if (nzhp == NULL)
- continue;
-
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- fnvlist_free(range_nvl);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- fnvlist_free(range_nvl);
- return ((ret < 0) ? ret : 0);
-}
-
-/*
- * Iterate over all bookmarks
- */
-int
-zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- zfs_handle_t *nzhp;
- nvlist_t *props = NULL;
- nvlist_t *bmarks = NULL;
- int err;
-
- if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0)
- return (0);
-
- /* Setup the requested properties nvlist. */
- props = fnvlist_alloc();
- fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_GUID));
- fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATETXG));
- fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATION));
-
- if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0)
- goto out;
-
- for (nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
- pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) {
- char name[ZFS_MAX_DATASET_NAME_LEN];
- char *bmark_name;
- nvlist_t *bmark_props;
-
- bmark_name = nvpair_name(pair);
- bmark_props = fnvpair_value_nvlist(pair);
-
- (void) snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name,
- bmark_name);
-
- nzhp = make_bookmark_handle(zhp, name, bmark_props);
- if (nzhp == NULL)
- continue;
-
- if ((err = func(nzhp, data)) != 0)
- goto out;
- }
-
-out:
- fnvlist_free(props);
- fnvlist_free(bmarks);
-
- return (err);
-}
-
-/*
- * Routines for dealing with the sorted snapshot functionality
- */
-typedef struct zfs_node {
- zfs_handle_t *zn_handle;
- avl_node_t zn_avlnode;
-} zfs_node_t;
-
-static int
-zfs_sort_snaps(zfs_handle_t *zhp, void *data)
-{
- avl_tree_t *avl = data;
- zfs_node_t *node;
- zfs_node_t search;
-
- search.zn_handle = zhp;
- node = avl_find(avl, &search, NULL);
- if (node) {
- /*
- * If this snapshot was renamed while we were creating the
- * AVL tree, it's possible that we already inserted it under
- * its old name. Remove the old handle before adding the new
- * one.
- */
- zfs_close(node->zn_handle);
- avl_remove(avl, node);
- free(node);
- }
-
- node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
- node->zn_handle = zhp;
- avl_add(avl, node);
-
- return (0);
-}
-
-static int
-zfs_snapshot_compare(const void *larg, const void *rarg)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- uint64_t lcreate, rcreate;
-
- /*
- * Sort them according to creation time. We use the hidden
- * CREATETXG property to get an absolute ordering of snapshots.
- */
- lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
- rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
-
- return (AVL_CMP(lcreate, rcreate));
-}
-
-int
-zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data,
- uint64_t min_txg, uint64_t max_txg)
-{
- int ret = 0;
- zfs_node_t *node;
- avl_tree_t avl;
- void *cookie = NULL;
-
- avl_create(&avl, zfs_snapshot_compare,
- sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
-
- ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl, min_txg,
- max_txg);
-
- for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
- ret |= callback(node->zn_handle, data);
-
- while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
- free(node);
-
- avl_destroy(&avl);
-
- return (ret);
-}
-
-typedef struct {
- char *ssa_first;
- char *ssa_last;
- boolean_t ssa_seenfirst;
- boolean_t ssa_seenlast;
- zfs_iter_f ssa_func;
- void *ssa_arg;
-} snapspec_arg_t;
-
-static int
-snapspec_cb(zfs_handle_t *zhp, void *arg)
-{
- snapspec_arg_t *ssa = arg;
- const char *shortsnapname;
- int err = 0;
-
- if (ssa->ssa_seenlast)
- return (0);
-
- shortsnapname = strchr(zfs_get_name(zhp), '@') + 1;
- if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
- ssa->ssa_seenfirst = B_TRUE;
- if (strcmp(shortsnapname, ssa->ssa_last) == 0)
- ssa->ssa_seenlast = B_TRUE;
-
- if (ssa->ssa_seenfirst) {
- err = ssa->ssa_func(zhp, ssa->ssa_arg);
- } else {
- zfs_close(zhp);
- }
-
- return (err);
-}
-
-/*
- * spec is a string like "A,B%C,D"
- *
- * <snaps>, where <snaps> can be:
- * <snap> (single snapshot)
- * <snap>%<snap> (range of snapshots, inclusive)
- * %<snap> (range of snapshots, starting with earliest)
- * <snap>% (range of snapshots, ending with last)
- * % (all snapshots)
- * <snaps>[,...] (comma separated list of the above)
- *
- * If a snapshot can not be opened, continue trying to open the others, but
- * return ENOENT at the end.
- */
-int
-zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
- zfs_iter_f func, void *arg)
-{
- char *buf, *comma_separated, *cp;
- int err = 0;
- int ret = 0;
-
- buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
- cp = buf;
-
- while ((comma_separated = strsep(&cp, ",")) != NULL) {
- char *pct = strchr(comma_separated, '%');
- if (pct != NULL) {
- snapspec_arg_t ssa = { 0 };
- ssa.ssa_func = func;
- ssa.ssa_arg = arg;
-
- if (pct == comma_separated)
- ssa.ssa_seenfirst = B_TRUE;
- else
- ssa.ssa_first = comma_separated;
- *pct = '\0';
- ssa.ssa_last = pct + 1;
-
- /*
- * If there is a lastname specified, make sure it
- * exists.
- */
- if (ssa.ssa_last[0] != '\0') {
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- (void) snprintf(snapname, sizeof (snapname),
- "%s@%s", zfs_get_name(fs_zhp),
- ssa.ssa_last);
- if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
- snapname, ZFS_TYPE_SNAPSHOT)) {
- ret = ENOENT;
- continue;
- }
- }
-
- err = zfs_iter_snapshots_sorted(fs_zhp,
- snapspec_cb, &ssa, 0, 0);
- if (ret == 0)
- ret = err;
- if (ret == 0 && (!ssa.ssa_seenfirst ||
- (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
- ret = ENOENT;
- }
- } else {
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- zfs_handle_t *snap_zhp;
- (void) snprintf(snapname, sizeof (snapname), "%s@%s",
- zfs_get_name(fs_zhp), comma_separated);
- snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
- snapname);
- if (snap_zhp == NULL) {
- ret = ENOENT;
- continue;
- }
- err = func(snap_zhp, arg);
- if (ret == 0)
- ret = err;
- }
- }
-
- free(buf);
- return (ret);
-}
-
-/*
- * Iterate over all children, snapshots and filesystems
- * Process snapshots before filesystems because they are nearer the input
- * handle: this is extremely important when used with zfs_iter_f functions
- * looking for data, following the logic that we would like to find it as soon
- * and as close as possible.
- */
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- int ret;
-
- if ((ret = zfs_iter_snapshots(zhp, B_FALSE, func, data, 0, 0)) != 0)
- return (ret);
-
- return (zfs_iter_filesystems(zhp, func, data));
-}
-
-
-typedef struct iter_stack_frame {
- struct iter_stack_frame *next;
- zfs_handle_t *zhp;
-} iter_stack_frame_t;
-
-typedef struct iter_dependents_arg {
- boolean_t first;
- boolean_t allowrecursion;
- iter_stack_frame_t *stack;
- zfs_iter_f func;
- void *data;
-} iter_dependents_arg_t;
-
-static int
-iter_dependents_cb(zfs_handle_t *zhp, void *arg)
-{
- iter_dependents_arg_t *ida = arg;
- int err = 0;
- boolean_t first = ida->first;
- ida->first = B_FALSE;
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
- } else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
- iter_stack_frame_t isf;
- iter_stack_frame_t *f;
-
- /*
- * check if there is a cycle by seeing if this fs is already
- * on the stack.
- */
- for (f = ida->stack; f != NULL; f = f->next) {
- if (f->zhp->zfs_dmustats.dds_guid ==
- zhp->zfs_dmustats.dds_guid) {
- if (ida->allowrecursion) {
- zfs_close(zhp);
- return (0);
- } else {
- zfs_error_aux(zhp->zfs_hdl,
- dgettext(TEXT_DOMAIN,
- "recursive dependency at '%s'"),
- zfs_get_name(zhp));
- err = zfs_error(zhp->zfs_hdl,
- EZFS_RECURSIVE,
- dgettext(TEXT_DOMAIN,
- "cannot determine dependent "
- "datasets"));
- zfs_close(zhp);
- return (err);
- }
- }
- }
-
- isf.zhp = zhp;
- isf.next = ida->stack;
- ida->stack = &isf;
- err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
- if (err == 0) {
- err = zfs_iter_snapshots(zhp, B_FALSE,
- iter_dependents_cb, ida, 0, 0);
- }
- ida->stack = isf.next;
- }
-
- if (!first && err == 0)
- err = ida->func(zhp, ida->data);
- else
- zfs_close(zhp);
-
- return (err);
-}
-
-int
-zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
- zfs_iter_f func, void *data)
-{
- iter_dependents_arg_t ida;
- ida.allowrecursion = allowrecursion;
- ida.stack = NULL;
- ida.func = func;
- ida.data = data;
- ida.first = B_TRUE;
- return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
deleted file mode 100644
index 9d4948cc7173..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
+++ /dev/null
@@ -1,1734 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright 2017 Joyent, Inc.
- * Copyright 2017 RackTop Systems.
- * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
- */
-
-/*
- * Routines to manage ZFS mounts. We separate all the nasty routines that have
- * to deal with the OS. The following functions are the main entry points --
- * they are used by mount and unmount and when changing a filesystem's
- * mountpoint.
- *
- * zfs_is_mounted()
- * zfs_mount()
- * zfs_unmount()
- * zfs_unmountall()
- *
- * This file also contains the functions used to manage sharing filesystems via
- * NFS and iSCSI:
- *
- * zfs_is_shared()
- * zfs_share()
- * zfs_unshare()
- *
- * zfs_is_shared_nfs()
- * zfs_is_shared_smb()
- * zfs_share_proto()
- * zfs_shareall();
- * zfs_unshare_nfs()
- * zfs_unshare_smb()
- * zfs_unshareall_nfs()
- * zfs_unshareall_smb()
- * zfs_unshareall()
- * zfs_unshareall_bypath()
- *
- * The following functions are available for pool consumers, and will
- * mount/unmount and share/unshare all datasets within pool:
- *
- * zpool_enable_datasets()
- * zpool_disable_datasets()
- */
-
-#include <dirent.h>
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <zone.h>
-#include <sys/mntent.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-#include <thread_pool.h>
-
-#include <libshare.h>
-#define MAXISALEN 257 /* based on sysinfo(2) man page */
-
-static int mount_tp_nthr = 512; /* tpool threads for multi-threaded mounting */
-
-static void zfs_mount_task(void *);
-static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
-zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
- zfs_share_proto_t);
-
-/*
- * The share protocols table must be in the same order as the zfs_share_proto_t
- * enum in libzfs_impl.h
- */
-typedef struct {
- zfs_prop_t p_prop;
- char *p_name;
- int p_share_err;
- int p_unshare_err;
-} proto_table_t;
-
-proto_table_t proto_table[PROTO_END] = {
- {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
- {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
-};
-
-zfs_share_proto_t nfs_only[] = {
- PROTO_NFS,
- PROTO_END
-};
-
-zfs_share_proto_t smb_only[] = {
- PROTO_SMB,
- PROTO_END
-};
-zfs_share_proto_t share_all_proto[] = {
- PROTO_NFS,
- PROTO_SMB,
- PROTO_END
-};
-
-/*
- * Search the sharetab for the given mountpoint and protocol, returning
- * a zfs_share_type_t value.
- */
-static zfs_share_type_t
-is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto)
-{
- char buf[MAXPATHLEN], *tab;
- char *ptr;
-
- if (hdl->libzfs_sharetab == NULL)
- return (SHARED_NOT_SHARED);
-
- (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
-
- while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
-
- /* the mountpoint is the first entry on each line */
- if ((tab = strchr(buf, '\t')) == NULL)
- continue;
-
- *tab = '\0';
- if (strcmp(buf, mountpoint) == 0) {
-#ifdef illumos
- /*
- * the protocol field is the third field
- * skip over second field
- */
- ptr = ++tab;
- if ((tab = strchr(ptr, '\t')) == NULL)
- continue;
- ptr = ++tab;
- if ((tab = strchr(ptr, '\t')) == NULL)
- continue;
- *tab = '\0';
- if (strcmp(ptr,
- proto_table[proto].p_name) == 0) {
- switch (proto) {
- case PROTO_NFS:
- return (SHARED_NFS);
- case PROTO_SMB:
- return (SHARED_SMB);
- default:
- return (0);
- }
- }
-#else
- if (proto == PROTO_NFS)
- return (SHARED_NFS);
-#endif
- }
- }
-
- return (SHARED_NOT_SHARED);
-}
-
-#ifdef illumos
-static boolean_t
-dir_is_empty_stat(const char *dirname)
-{
- struct stat st;
-
- /*
- * We only want to return false if the given path is a non empty
- * directory, all other errors are handled elsewhere.
- */
- if (stat(dirname, &st) < 0 || !S_ISDIR(st.st_mode)) {
- return (B_TRUE);
- }
-
- /*
- * An empty directory will still have two entries in it, one
- * entry for each of "." and "..".
- */
- if (st.st_size > 2) {
- return (B_FALSE);
- }
-
- return (B_TRUE);
-}
-
-static boolean_t
-dir_is_empty_readdir(const char *dirname)
-{
- DIR *dirp;
- struct dirent64 *dp;
- int dirfd;
-
- if ((dirfd = openat(AT_FDCWD, dirname,
- O_RDONLY | O_NDELAY | O_LARGEFILE | O_CLOEXEC, 0)) < 0) {
- return (B_TRUE);
- }
-
- if ((dirp = fdopendir(dirfd)) == NULL) {
- (void) close(dirfd);
- return (B_TRUE);
- }
-
- while ((dp = readdir64(dirp)) != NULL) {
-
- if (strcmp(dp->d_name, ".") == 0 ||
- strcmp(dp->d_name, "..") == 0)
- continue;
-
- (void) closedir(dirp);
- return (B_FALSE);
- }
-
- (void) closedir(dirp);
- return (B_TRUE);
-}
-
-/*
- * Returns true if the specified directory is empty. If we can't open the
- * directory at all, return true so that the mount can fail with a more
- * informative error message.
- */
-static boolean_t
-dir_is_empty(const char *dirname)
-{
- struct statvfs64 st;
-
- /*
- * If the statvfs call fails or the filesystem is not a ZFS
- * filesystem, fall back to the slow path which uses readdir.
- */
- if ((statvfs64(dirname, &st) != 0) ||
- (strcmp(st.f_basetype, "zfs") != 0)) {
- return (dir_is_empty_readdir(dirname));
- }
-
- /*
- * At this point, we know the provided path is on a ZFS
- * filesystem, so we can use stat instead of readdir to
- * determine if the directory is empty or not. We try to avoid
- * using readdir because that requires opening "dirname"; this
- * open file descriptor can potentially end up in a child
- * process if there's a concurrent fork, thus preventing the
- * zfs_mount() from otherwise succeeding (the open file
- * descriptor inherited by the child process will cause the
- * parent's mount to fail with EBUSY). The performance
- * implications of replacing the open, read, and close with a
- * single stat is nice; but is not the main motivation for the
- * added complexity.
- */
- return (dir_is_empty_stat(dirname));
-}
-#endif
-
-/*
- * Checks to see if the mount is active. If the filesystem is mounted, we fill
- * in 'where' with the current mountpoint, and return 1. Otherwise, we return
- * 0.
- */
-boolean_t
-is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where)
-{
- struct mnttab entry;
-
- if (libzfs_mnttab_find(zfs_hdl, special, &entry) != 0)
- return (B_FALSE);
-
- if (where != NULL)
- *where = zfs_strdup(zfs_hdl, entry.mnt_mountp);
-
- return (B_TRUE);
-}
-
-boolean_t
-zfs_is_mounted(zfs_handle_t *zhp, char **where)
-{
- return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where));
-}
-
-static boolean_t
-zfs_is_mountable_internal(zfs_handle_t *zhp, const char *mountpoint)
-{
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
- getzoneid() == GLOBAL_ZONEID)
- return (B_FALSE);
-
- return (B_TRUE);
-}
-
-/*
- * Returns true if the given dataset is mountable, false otherwise. Returns the
- * mountpoint in 'buf'.
- */
-static boolean_t
-zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
- zprop_source_t *source)
-{
- char sourceloc[MAXNAMELEN];
- zprop_source_t sourcetype;
-
- if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type))
- return (B_FALSE);
-
- verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen,
- &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0);
-
- if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 ||
- strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
- return (B_FALSE);
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
- return (B_FALSE);
-
- if (!zfs_is_mountable_internal(zhp, buf))
- return (B_FALSE);
-
- if (source)
- *source = sourcetype;
-
- return (B_TRUE);
-}
-
-/*
- * Mount the given filesystem.
- */
-int
-zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
-{
- char mountpoint[ZFS_MAXPROPLEN];
-
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
- return (0);
-
- return (zfs_mount_at(zhp, options, flags, mountpoint));
-}
-
-int
-zfs_mount_at(zfs_handle_t *zhp, const char *options, int flags,
- const char *mountpoint)
-{
- struct stat buf;
- char mntopts[MNT_LINE_MAX];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- if (options == NULL)
- mntopts[0] = '\0';
- else
- (void) strlcpy(mntopts, options, sizeof (mntopts));
-
- /*
- * If the pool is imported read-only then all mounts must be read-only
- */
- if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL))
- flags |= MS_RDONLY;
-
- if (!zfs_is_mountable_internal(zhp, mountpoint))
- return (B_FALSE);
-
- /* Create the directory if it doesn't already exist */
- if (lstat(mountpoint, &buf) != 0) {
- if (mkdirp(mountpoint, 0755) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to create mountpoint"));
- return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
- mountpoint));
- }
- }
-
-#ifdef illumos /* FreeBSD: overlay mounts are not checked. */
- /*
- * Determine if the mountpoint is empty. If so, refuse to perform the
- * mount. We don't perform this check if MS_OVERLAY is specified, which
- * would defeat the point. We also avoid this check if 'remount' is
- * specified.
- */
- if ((flags & MS_OVERLAY) == 0 &&
- strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
- !dir_is_empty(mountpoint)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "directory is not empty"));
- return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
- }
-#endif
-
- /* perform the mount */
- if (zmount(zfs_get_name(zhp), mountpoint, flags,
- MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
- /*
- * Generic errors are nasty, but there are just way too many
- * from mount(), and they're well-understood. We pick a few
- * common ones to improve upon.
- */
- if (errno == EBUSY) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "mountpoint or dataset is busy"));
- } else if (errno == EPERM) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Insufficient privileges"));
- } else if (errno == ENOTSUP) {
- char buf[256];
- int spa_version;
-
- VERIFY(zfs_spa_version(zhp, &spa_version) == 0);
- (void) snprintf(buf, sizeof (buf),
- dgettext(TEXT_DOMAIN, "Can't mount a version %lld "
- "file system on a version %d pool. Pool must be"
- " upgraded to mount this file system."),
- (u_longlong_t)zfs_prop_get_int(zhp,
- ZFS_PROP_VERSION), spa_version);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf));
- } else {
- zfs_error_aux(hdl, strerror(errno));
- }
- return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
- zhp->zfs_name));
- }
-
- /* add the mounted entry into our cache */
- libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint,
- mntopts);
- return (0);
-}
-
-/*
- * Unmount a single filesystem.
- */
-static int
-unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
-{
- if (umount2(mountpoint, flags) != 0) {
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
- mountpoint));
- }
-
- return (0);
-}
-
-/*
- * Unmount the given filesystem.
- */
-int
-zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- struct mnttab entry;
- char *mntpt = NULL;
-
- /* check to see if we need to unmount the filesystem */
- if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
- libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) {
- /*
- * mountpoint may have come from a call to
- * getmnt/getmntany if it isn't NULL. If it is NULL,
- * we know it comes from libzfs_mnttab_find which can
- * then get freed later. We strdup it to play it safe.
- */
- if (mountpoint == NULL)
- mntpt = zfs_strdup(hdl, entry.mnt_mountp);
- else
- mntpt = zfs_strdup(hdl, mountpoint);
-
- /*
- * Unshare and unmount the filesystem
- */
- if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0)
- return (-1);
-
- if (unmount_one(hdl, mntpt, flags) != 0) {
- free(mntpt);
- (void) zfs_shareall(zhp);
- return (-1);
- }
- libzfs_mnttab_remove(hdl, zhp->zfs_name);
- free(mntpt);
- }
-
- return (0);
-}
-
-/*
- * Unmount this filesystem and any children inheriting the mountpoint property.
- * To do this, just act like we're changing the mountpoint property, but don't
- * remount the filesystems afterwards.
- */
-int
-zfs_unmountall(zfs_handle_t *zhp, int flags)
-{
- prop_changelist_t *clp;
- int ret;
-
- clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 0, flags);
- if (clp == NULL)
- return (-1);
-
- ret = changelist_prefix(clp);
- changelist_free(clp);
-
- return (ret);
-}
-
-boolean_t
-zfs_is_shared(zfs_handle_t *zhp)
-{
- zfs_share_type_t rc = 0;
- zfs_share_proto_t *curr_proto;
-
- if (ZFS_IS_VOLUME(zhp))
- return (B_FALSE);
-
- for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
- curr_proto++)
- rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto);
-
- return (rc ? B_TRUE : B_FALSE);
-}
-
-int
-zfs_share(zfs_handle_t *zhp)
-{
- assert(!ZFS_IS_VOLUME(zhp));
- return (zfs_share_proto(zhp, share_all_proto));
-}
-
-int
-zfs_unshare(zfs_handle_t *zhp)
-{
- assert(!ZFS_IS_VOLUME(zhp));
- return (zfs_unshareall(zhp));
-}
-
-/*
- * Check to see if the filesystem is currently shared.
- */
-zfs_share_type_t
-zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
-{
- char *mountpoint;
- zfs_share_type_t rc;
-
- if (!zfs_is_mounted(zhp, &mountpoint))
- return (SHARED_NOT_SHARED);
-
- if ((rc = is_shared(zhp->zfs_hdl, mountpoint, proto))
- != SHARED_NOT_SHARED) {
- if (where != NULL)
- *where = mountpoint;
- else
- free(mountpoint);
- return (rc);
- } else {
- free(mountpoint);
- return (SHARED_NOT_SHARED);
- }
-}
-
-boolean_t
-zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
-{
- return (zfs_is_shared_proto(zhp, where,
- PROTO_NFS) != SHARED_NOT_SHARED);
-}
-
-boolean_t
-zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
-{
- return (zfs_is_shared_proto(zhp, where,
- PROTO_SMB) != SHARED_NOT_SHARED);
-}
-
-/*
- * Make sure things will work if libshare isn't installed by using
- * wrapper functions that check to see that the pointers to functions
- * initialized in _zfs_init_libshare() are actually present.
- */
-
-#ifdef illumos
-static sa_handle_t (*_sa_init)(int);
-static sa_handle_t (*_sa_init_arg)(int, void *);
-static void (*_sa_fini)(sa_handle_t);
-static sa_share_t (*_sa_find_share)(sa_handle_t, char *);
-static int (*_sa_enable_share)(sa_share_t, char *);
-static int (*_sa_disable_share)(sa_share_t, char *);
-static char *(*_sa_errorstr)(int);
-static int (*_sa_parse_legacy_options)(sa_group_t, char *, char *);
-static boolean_t (*_sa_needs_refresh)(sa_handle_t *);
-static libzfs_handle_t *(*_sa_get_zfs_handle)(sa_handle_t);
-static int (*_sa_zfs_process_share)(sa_handle_t, sa_group_t, sa_share_t,
- char *, char *, zprop_source_t, char *, char *, char *);
-static void (*_sa_update_sharetab_ts)(sa_handle_t);
-#endif
-
-/*
- * _zfs_init_libshare()
- *
- * Find the libshare.so.1 entry points that we use here and save the
- * values to be used later. This is triggered by the runtime loader.
- * Make sure the correct ISA version is loaded.
- */
-
-#pragma init(_zfs_init_libshare)
-static void
-_zfs_init_libshare(void)
-{
-#ifdef illumos
- void *libshare;
- char path[MAXPATHLEN];
- char isa[MAXISALEN];
-
-#if defined(_LP64)
- if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
- isa[0] = '\0';
-#else
- isa[0] = '\0';
-#endif
- (void) snprintf(path, MAXPATHLEN,
- "/usr/lib/%s/libshare.so.1", isa);
-
- if ((libshare = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)) != NULL) {
- _sa_init = (sa_handle_t (*)(int))dlsym(libshare, "sa_init");
- _sa_init_arg = (sa_handle_t (*)(int, void *))dlsym(libshare,
- "sa_init_arg");
- _sa_fini = (void (*)(sa_handle_t))dlsym(libshare, "sa_fini");
- _sa_find_share = (sa_share_t (*)(sa_handle_t, char *))
- dlsym(libshare, "sa_find_share");
- _sa_enable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
- "sa_enable_share");
- _sa_disable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
- "sa_disable_share");
- _sa_errorstr = (char *(*)(int))dlsym(libshare, "sa_errorstr");
- _sa_parse_legacy_options = (int (*)(sa_group_t, char *, char *))
- dlsym(libshare, "sa_parse_legacy_options");
- _sa_needs_refresh = (boolean_t (*)(sa_handle_t *))
- dlsym(libshare, "sa_needs_refresh");
- _sa_get_zfs_handle = (libzfs_handle_t *(*)(sa_handle_t))
- dlsym(libshare, "sa_get_zfs_handle");
- _sa_zfs_process_share = (int (*)(sa_handle_t, sa_group_t,
- sa_share_t, char *, char *, zprop_source_t, char *,
- char *, char *))dlsym(libshare, "sa_zfs_process_share");
- _sa_update_sharetab_ts = (void (*)(sa_handle_t))
- dlsym(libshare, "sa_update_sharetab_ts");
- if (_sa_init == NULL || _sa_init_arg == NULL ||
- _sa_fini == NULL || _sa_find_share == NULL ||
- _sa_enable_share == NULL || _sa_disable_share == NULL ||
- _sa_errorstr == NULL || _sa_parse_legacy_options == NULL ||
- _sa_needs_refresh == NULL || _sa_get_zfs_handle == NULL ||
- _sa_zfs_process_share == NULL ||
- _sa_update_sharetab_ts == NULL) {
- _sa_init = NULL;
- _sa_init_arg = NULL;
- _sa_fini = NULL;
- _sa_disable_share = NULL;
- _sa_enable_share = NULL;
- _sa_errorstr = NULL;
- _sa_parse_legacy_options = NULL;
- (void) dlclose(libshare);
- _sa_needs_refresh = NULL;
- _sa_get_zfs_handle = NULL;
- _sa_zfs_process_share = NULL;
- _sa_update_sharetab_ts = NULL;
- }
- }
-#endif
-}
-
-/*
- * zfs_init_libshare(zhandle, service)
- *
- * Initialize the libshare API if it hasn't already been initialized.
- * In all cases it returns 0 if it succeeded and an error if not. The
- * service value is which part(s) of the API to initialize and is a
- * direct map to the libshare sa_init(service) interface.
- */
-static int
-zfs_init_libshare_impl(libzfs_handle_t *zhandle, int service, void *arg)
-{
-#ifdef illumos
- /*
- * libshare is either not installed or we're in a branded zone. The
- * rest of the wrapper functions around the libshare calls already
- * handle NULL function pointers, but we don't want the callers of
- * zfs_init_libshare() to fail prematurely if libshare is not available.
- */
- if (_sa_init == NULL)
- return (SA_OK);
-
- /*
- * Attempt to refresh libshare. This is necessary if there was a cache
- * miss for a new ZFS dataset that was just created, or if state of the
- * sharetab file has changed since libshare was last initialized. We
- * want to make sure so check timestamps to see if a different process
- * has updated any of the configuration. If there was some non-ZFS
- * change, we need to re-initialize the internal cache.
- */
- if (_sa_needs_refresh != NULL &&
- _sa_needs_refresh(zhandle->libzfs_sharehdl)) {
- zfs_uninit_libshare(zhandle);
- zhandle->libzfs_sharehdl = _sa_init_arg(service, arg);
- }
-
- if (zhandle && zhandle->libzfs_sharehdl == NULL)
- zhandle->libzfs_sharehdl = _sa_init_arg(service, arg);
-
- if (zhandle->libzfs_sharehdl == NULL)
- return (SA_NO_MEMORY);
-#endif
-
- return (SA_OK);
-}
-int
-zfs_init_libshare(libzfs_handle_t *zhandle, int service)
-{
- return (zfs_init_libshare_impl(zhandle, service, NULL));
-}
-
-int
-zfs_init_libshare_arg(libzfs_handle_t *zhandle, int service, void *arg)
-{
- return (zfs_init_libshare_impl(zhandle, service, arg));
-}
-
-
-/*
- * zfs_uninit_libshare(zhandle)
- *
- * Uninitialize the libshare API if it hasn't already been
- * uninitialized. It is OK to call multiple times.
- */
-void
-zfs_uninit_libshare(libzfs_handle_t *zhandle)
-{
- if (zhandle != NULL && zhandle->libzfs_sharehdl != NULL) {
-#ifdef illumos
- if (_sa_fini != NULL)
- _sa_fini(zhandle->libzfs_sharehdl);
-#endif
- zhandle->libzfs_sharehdl = NULL;
- }
-}
-
-/*
- * zfs_parse_options(options, proto)
- *
- * Call the legacy parse interface to get the protocol specific
- * options using the NULL arg to indicate that this is a "parse" only.
- */
-int
-zfs_parse_options(char *options, zfs_share_proto_t proto)
-{
-#ifdef illumos
- if (_sa_parse_legacy_options != NULL) {
- return (_sa_parse_legacy_options(NULL, options,
- proto_table[proto].p_name));
- }
- return (SA_CONFIG_ERR);
-#else
- return (SA_OK);
-#endif
-}
-
-#ifdef illumos
-/*
- * zfs_sa_find_share(handle, path)
- *
- * wrapper around sa_find_share to find a share path in the
- * configuration.
- */
-static sa_share_t
-zfs_sa_find_share(sa_handle_t handle, char *path)
-{
- if (_sa_find_share != NULL)
- return (_sa_find_share(handle, path));
- return (NULL);
-}
-
-/*
- * zfs_sa_enable_share(share, proto)
- *
- * Wrapper for sa_enable_share which enables a share for a specified
- * protocol.
- */
-static int
-zfs_sa_enable_share(sa_share_t share, char *proto)
-{
- if (_sa_enable_share != NULL)
- return (_sa_enable_share(share, proto));
- return (SA_CONFIG_ERR);
-}
-
-/*
- * zfs_sa_disable_share(share, proto)
- *
- * Wrapper for sa_enable_share which disables a share for a specified
- * protocol.
- */
-static int
-zfs_sa_disable_share(sa_share_t share, char *proto)
-{
- if (_sa_disable_share != NULL)
- return (_sa_disable_share(share, proto));
- return (SA_CONFIG_ERR);
-}
-#endif /* illumos */
-
-/*
- * Share the given filesystem according to the options in the specified
- * protocol specific properties (sharenfs, sharesmb). We rely
- * on "libshare" to the dirty work for us.
- */
-static int
-zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
-{
- char mountpoint[ZFS_MAXPROPLEN];
- char shareopts[ZFS_MAXPROPLEN];
- char sourcestr[ZFS_MAXPROPLEN];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_share_proto_t *curr_proto;
- zprop_source_t sourcetype;
- int error, ret;
-
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
- return (0);
-
- for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
- /*
- * Return success if there are no share options.
- */
- if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
- shareopts, sizeof (shareopts), &sourcetype, sourcestr,
- ZFS_MAXPROPLEN, B_FALSE) != 0 ||
- strcmp(shareopts, "off") == 0)
- continue;
-#ifdef illumos
- ret = zfs_init_libshare_arg(hdl, SA_INIT_ONE_SHARE_FROM_HANDLE,
- zhp);
- if (ret != SA_OK) {
- (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
- zfs_get_name(zhp), _sa_errorstr != NULL ?
- _sa_errorstr(ret) : "");
- return (-1);
- }
-#endif
-
- /*
- * If the 'zoned' property is set, then zfs_is_mountable()
- * will have already bailed out if we are in the global zone.
- * But local zones cannot be NFS servers, so we ignore it for
- * local zones as well.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
- continue;
-
-#ifdef illumos
- share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
- if (share == NULL) {
- /*
- * This may be a new file system that was just
- * created so isn't in the internal cache
- * (second time through). Rather than
- * reloading the entire configuration, we can
- * assume ZFS has done the checking and it is
- * safe to add this to the internal
- * configuration.
- */
- if (_sa_zfs_process_share(hdl->libzfs_sharehdl,
- NULL, NULL, mountpoint,
- proto_table[*curr_proto].p_name, sourcetype,
- shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
- (void) zfs_error_fmt(hdl,
- proto_table[*curr_proto].p_share_err,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
- share = zfs_sa_find_share(hdl->libzfs_sharehdl,
- mountpoint);
- }
- if (share != NULL) {
- int err;
- err = zfs_sa_enable_share(share,
- proto_table[*curr_proto].p_name);
- if (err != SA_OK) {
- (void) zfs_error_fmt(hdl,
- proto_table[*curr_proto].p_share_err,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
- } else
-#else
- if (*curr_proto != PROTO_NFS) {
- fprintf(stderr, "Unsupported share protocol: %d.\n",
- *curr_proto);
- continue;
- }
-
- if (strcmp(shareopts, "on") == 0)
- error = fsshare(ZFS_EXPORTS_PATH, mountpoint, "");
- else
- error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts);
- if (error != 0)
-#endif
- {
- (void) zfs_error_fmt(hdl,
- proto_table[*curr_proto].p_share_err,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
-
- }
- return (0);
-}
-
-
-int
-zfs_share_nfs(zfs_handle_t *zhp)
-{
- return (zfs_share_proto(zhp, nfs_only));
-}
-
-int
-zfs_share_smb(zfs_handle_t *zhp)
-{
- return (zfs_share_proto(zhp, smb_only));
-}
-
-int
-zfs_shareall(zfs_handle_t *zhp)
-{
- return (zfs_share_proto(zhp, share_all_proto));
-}
-
-/*
- * Unshare a filesystem by mountpoint.
- */
-static int
-unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
- zfs_share_proto_t proto)
-{
-#ifdef illumos
- sa_share_t share;
- int err;
- char *mntpt;
-
- /*
- * Mountpoint could get trashed if libshare calls getmntany
- * which it does during API initialization, so strdup the
- * value.
- */
- mntpt = zfs_strdup(hdl, mountpoint);
-
- /*
- * make sure libshare initialized, initialize everything because we
- * don't know what other unsharing may happen later. Functions up the
- * stack are allowed to initialize instead a subset of shares at the
- * time the set is known.
- */
- if ((err = zfs_init_libshare_arg(hdl, SA_INIT_ONE_SHARE_FROM_NAME,
- (void *)name)) != SA_OK) {
- free(mntpt); /* don't need the copy anymore */
- return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
- dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
- name, _sa_errorstr(err)));
- }
-
- share = zfs_sa_find_share(hdl->libzfs_sharehdl, mntpt);
- free(mntpt); /* don't need the copy anymore */
-
- if (share != NULL) {
- err = zfs_sa_disable_share(share, proto_table[proto].p_name);
- if (err != SA_OK) {
- return (zfs_error_fmt(hdl,
- proto_table[proto].p_unshare_err,
- dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
- name, _sa_errorstr(err)));
- }
- } else {
- return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
- dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"),
- name));
- }
-#else
- char buf[MAXPATHLEN];
- FILE *fp;
- int err;
-
- if (proto != PROTO_NFS) {
- fprintf(stderr, "No SMB support in FreeBSD yet.\n");
- return (EOPNOTSUPP);
- }
-
- err = fsunshare(ZFS_EXPORTS_PATH, mountpoint);
- if (err != 0) {
- zfs_error_aux(hdl, "%s", strerror(err));
- return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), name));
- }
-#endif
- return (0);
-}
-
-/*
- * Unshare the given filesystem.
- */
-int
-zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
- zfs_share_proto_t *proto)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- struct mnttab entry;
- char *mntpt = NULL;
-
- /* check to see if need to unmount the filesystem */
- rewind(zhp->zfs_hdl->libzfs_mnttab);
- if (mountpoint != NULL)
- mountpoint = mntpt = zfs_strdup(hdl, mountpoint);
-
- if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
- libzfs_mnttab_find(hdl, zfs_get_name(zhp), &entry) == 0)) {
- zfs_share_proto_t *curr_proto;
-
- if (mountpoint == NULL)
- mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
-
- for (curr_proto = proto; *curr_proto != PROTO_END;
- curr_proto++) {
-
- if (is_shared(hdl, mntpt, *curr_proto) &&
- unshare_one(hdl, zhp->zfs_name,
- mntpt, *curr_proto) != 0) {
- if (mntpt != NULL)
- free(mntpt);
- return (-1);
- }
- }
- }
- if (mntpt != NULL)
- free(mntpt);
-
- return (0);
-}
-
-int
-zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
-{
- return (zfs_unshare_proto(zhp, mountpoint, nfs_only));
-}
-
-int
-zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint)
-{
- return (zfs_unshare_proto(zhp, mountpoint, smb_only));
-}
-
-/*
- * Same as zfs_unmountall(), but for NFS and SMB unshares.
- */
-int
-zfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
-{
- prop_changelist_t *clp;
- int ret;
-
- clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0, 0);
- if (clp == NULL)
- return (-1);
-
- ret = changelist_unshare(clp, proto);
- changelist_free(clp);
-
- return (ret);
-}
-
-int
-zfs_unshareall_nfs(zfs_handle_t *zhp)
-{
- return (zfs_unshareall_proto(zhp, nfs_only));
-}
-
-int
-zfs_unshareall_smb(zfs_handle_t *zhp)
-{
- return (zfs_unshareall_proto(zhp, smb_only));
-}
-
-int
-zfs_unshareall(zfs_handle_t *zhp)
-{
- return (zfs_unshareall_proto(zhp, share_all_proto));
-}
-
-int
-zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint)
-{
- return (zfs_unshare_proto(zhp, mountpoint, share_all_proto));
-}
-
-/*
- * Remove the mountpoint associated with the current dataset, if necessary.
- * We only remove the underlying directory if:
- *
- * - The mountpoint is not 'none' or 'legacy'
- * - The mountpoint is non-empty
- * - The mountpoint is the default or inherited
- * - The 'zoned' property is set, or we're in a local zone
- *
- * Any other directories we leave alone.
- */
-void
-remove_mountpoint(zfs_handle_t *zhp)
-{
- char mountpoint[ZFS_MAXPROPLEN];
- zprop_source_t source;
-
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
- &source))
- return;
-
- if (source == ZPROP_SRC_DEFAULT ||
- source == ZPROP_SRC_INHERITED) {
- /*
- * Try to remove the directory, silently ignoring any errors.
- * The filesystem may have since been removed or moved around,
- * and this error isn't really useful to the administrator in
- * any way.
- */
- (void) rmdir(mountpoint);
- }
-}
-
-/*
- * Add the given zfs handle to the cb_handles array, dynamically reallocating
- * the array if it is out of space
- */
-void
-libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp)
-{
- if (cbp->cb_alloc == cbp->cb_used) {
- size_t newsz;
- zfs_handle_t **newhandles;
-
- newsz = cbp->cb_alloc != 0 ? cbp->cb_alloc * 2 : 64;
- newhandles = zfs_realloc(zhp->zfs_hdl,
- cbp->cb_handles, cbp->cb_alloc * sizeof (zfs_handle_t *),
- newsz * sizeof (zfs_handle_t *));
- cbp->cb_handles = newhandles;
- cbp->cb_alloc = newsz;
- }
- cbp->cb_handles[cbp->cb_used++] = zhp;
-}
-
-/*
- * Recursive helper function used during file system enumeration
- */
-static int
-zfs_iter_cb(zfs_handle_t *zhp, void *data)
-{
- get_all_cb_t *cbp = data;
-
- if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) {
- zfs_close(zhp);
- return (0);
- }
-
- /*
- * If this filesystem is inconsistent and has a receive resume
- * token, we can not mount it.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
- zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
- NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
- zfs_close(zhp);
- return (0);
- }
-
- libzfs_add_handle(cbp, zhp);
- if (zfs_iter_filesystems(zhp, zfs_iter_cb, cbp) != 0) {
- zfs_close(zhp);
- return (-1);
- }
- return (0);
-}
-
-/*
- * Sort comparator that compares two mountpoint paths. We sort these paths so
- * that subdirectories immediately follow their parents. This means that we
- * effectively treat the '/' character as the lowest value non-nul char.
- * Since filesystems from non-global zones can have the same mountpoint
- * as other filesystems, the comparator sorts global zone filesystems to
- * the top of the list. This means that the global zone will traverse the
- * filesystem list in the correct order and can stop when it sees the
- * first zoned filesystem. In a non-global zone, only the delegated
- * filesystems are seen.
- *
- * An example sorted list using this comparator would look like:
- *
- * /foo
- * /foo/bar
- * /foo/bar/baz
- * /foo/baz
- * /foo.bar
- * /foo (NGZ1)
- * /foo (NGZ2)
- *
- * The mount code depend on this ordering to deterministically iterate
- * over filesystems in order to spawn parallel mount tasks.
- */
-static int
-mountpoint_cmp(const void *arga, const void *argb)
-{
- zfs_handle_t *const *zap = arga;
- zfs_handle_t *za = *zap;
- zfs_handle_t *const *zbp = argb;
- zfs_handle_t *zb = *zbp;
- char mounta[MAXPATHLEN];
- char mountb[MAXPATHLEN];
- const char *a = mounta;
- const char *b = mountb;
- boolean_t gota, gotb;
- uint64_t zoneda, zonedb;
-
- zoneda = zfs_prop_get_int(za, ZFS_PROP_ZONED);
- zonedb = zfs_prop_get_int(zb, ZFS_PROP_ZONED);
- if (zoneda && !zonedb)
- return (1);
- if (!zoneda && zonedb)
- return (-1);
- gota = (zfs_get_type(za) == ZFS_TYPE_FILESYSTEM);
- if (gota)
- verify(zfs_prop_get(za, ZFS_PROP_MOUNTPOINT, mounta,
- sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
- gotb = (zfs_get_type(zb) == ZFS_TYPE_FILESYSTEM);
- if (gotb)
- verify(zfs_prop_get(zb, ZFS_PROP_MOUNTPOINT, mountb,
- sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
-
- if (gota && gotb) {
- while (*a != '\0' && (*a == *b)) {
- a++;
- b++;
- }
- if (*a == *b)
- return (0);
- if (*a == '\0')
- return (-1);
- if (*b == '\0')
- return (1);
- if (*a == '/')
- return (-1);
- if (*b == '/')
- return (1);
- return (*a < *b ? -1 : *a > *b);
- }
-
- if (gota)
- return (-1);
- if (gotb)
- return (1);
-
- /*
- * If neither filesystem has a mountpoint, revert to sorting by
- * datset name.
- */
- return (strcmp(zfs_get_name(za), zfs_get_name(zb)));
-}
-
-/*
- * Return true if path2 is a child of path1 or path2 equals path1 or
- * path1 is "/" (path2 is always a child of "/").
- */
-static boolean_t
-libzfs_path_contains(const char *path1, const char *path2)
-{
- return (strcmp(path1, path2) == 0 || strcmp(path1, "/") == 0 ||
- (strstr(path2, path1) == path2 && path2[strlen(path1)] == '/'));
-}
-
-
-static int
-non_descendant_idx(zfs_handle_t **handles, size_t num_handles, int idx)
-{
- char parent[ZFS_MAXPROPLEN];
- char child[ZFS_MAXPROPLEN];
- int i;
-
- verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, parent,
- sizeof (parent), NULL, NULL, 0, B_FALSE) == 0);
-
- for (i = idx + 1; i < num_handles; i++) {
- verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT, child,
- sizeof (child), NULL, NULL, 0, B_FALSE) == 0);
- if (!libzfs_path_contains(parent, child))
- break;
- }
- return (i);
-}
-
-typedef struct mnt_param {
- libzfs_handle_t *mnt_hdl;
- tpool_t *mnt_tp;
- zfs_handle_t **mnt_zhps; /* filesystems to mount */
- size_t mnt_num_handles;
- int mnt_idx; /* Index of selected entry to mount */
- zfs_iter_f mnt_func;
- void *mnt_data;
-} mnt_param_t;
-
-/*
- * Allocate and populate the parameter struct for mount function, and
- * schedule mounting of the entry selected by idx.
- */
-static void
-zfs_dispatch_mount(libzfs_handle_t *hdl, zfs_handle_t **handles,
- size_t num_handles, int idx, zfs_iter_f func, void *data, tpool_t *tp)
-{
- mnt_param_t *mnt_param = zfs_alloc(hdl, sizeof (mnt_param_t));
-
- mnt_param->mnt_hdl = hdl;
- mnt_param->mnt_tp = tp;
- mnt_param->mnt_zhps = handles;
- mnt_param->mnt_num_handles = num_handles;
- mnt_param->mnt_idx = idx;
- mnt_param->mnt_func = func;
- mnt_param->mnt_data = data;
-
- (void) tpool_dispatch(tp, zfs_mount_task, (void*)mnt_param);
-}
-
-/*
- * This is the structure used to keep state of mounting or sharing operations
- * during a call to zpool_enable_datasets().
- */
-typedef struct mount_state {
- /*
- * ms_mntstatus is set to -1 if any mount fails. While multiple threads
- * could update this variable concurrently, no synchronization is
- * needed as it's only ever set to -1.
- */
- int ms_mntstatus;
- int ms_mntflags;
- const char *ms_mntopts;
-} mount_state_t;
-
-static int
-zfs_mount_one(zfs_handle_t *zhp, void *arg)
-{
- mount_state_t *ms = arg;
- int ret = 0;
-
- if (zfs_mount(zhp, ms->ms_mntopts, ms->ms_mntflags) != 0)
- ret = ms->ms_mntstatus = -1;
- return (ret);
-}
-
-static int
-zfs_share_one(zfs_handle_t *zhp, void *arg)
-{
- mount_state_t *ms = arg;
- int ret = 0;
-
- if (zfs_share(zhp) != 0)
- ret = ms->ms_mntstatus = -1;
- return (ret);
-}
-
-/*
- * Thread pool function to mount one file system. On completion, it finds and
- * schedules its children to be mounted. This depends on the sorting done in
- * zfs_foreach_mountpoint(). Note that the degenerate case (chain of entries
- * each descending from the previous) will have no parallelism since we always
- * have to wait for the parent to finish mounting before we can schedule
- * its children.
- */
-static void
-zfs_mount_task(void *arg)
-{
- mnt_param_t *mp = arg;
- int idx = mp->mnt_idx;
- zfs_handle_t **handles = mp->mnt_zhps;
- size_t num_handles = mp->mnt_num_handles;
- char mountpoint[ZFS_MAXPROPLEN];
-
- verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, mountpoint,
- sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
-
- if (mp->mnt_func(handles[idx], mp->mnt_data) != 0)
- return;
-
- /*
- * We dispatch tasks to mount filesystems with mountpoints underneath
- * this one. We do this by dispatching the next filesystem with a
- * descendant mountpoint of the one we just mounted, then skip all of
- * its descendants, dispatch the next descendant mountpoint, and so on.
- * The non_descendant_idx() function skips over filesystems that are
- * descendants of the filesystem we just dispatched.
- */
- for (int i = idx + 1; i < num_handles;
- i = non_descendant_idx(handles, num_handles, i)) {
- char child[ZFS_MAXPROPLEN];
- verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT,
- child, sizeof (child), NULL, NULL, 0, B_FALSE) == 0);
-
- if (!libzfs_path_contains(mountpoint, child))
- break; /* not a descendant, return */
- zfs_dispatch_mount(mp->mnt_hdl, handles, num_handles, i,
- mp->mnt_func, mp->mnt_data, mp->mnt_tp);
- }
- free(mp);
-}
-
-/*
- * Issue the func callback for each ZFS handle contained in the handles
- * array. This function is used to mount all datasets, and so this function
- * guarantees that filesystems for parent mountpoints are called before their
- * children. As such, before issuing any callbacks, we first sort the array
- * of handles by mountpoint.
- *
- * Callbacks are issued in one of two ways:
- *
- * 1. Sequentially: If the parallel argument is B_FALSE or the ZFS_SERIAL_MOUNT
- * environment variable is set, then we issue callbacks sequentially.
- *
- * 2. In parallel: If the parallel argument is B_TRUE and the ZFS_SERIAL_MOUNT
- * environment variable is not set, then we use a tpool to dispatch threads
- * to mount filesystems in parallel. This function dispatches tasks to mount
- * the filesystems at the top-level mountpoints, and these tasks in turn
- * are responsible for recursively mounting filesystems in their children
- * mountpoints.
- */
-void
-zfs_foreach_mountpoint(libzfs_handle_t *hdl, zfs_handle_t **handles,
- size_t num_handles, zfs_iter_f func, void *data, boolean_t parallel)
-{
- zoneid_t zoneid = getzoneid();
-
- /*
- * The ZFS_SERIAL_MOUNT environment variable is an undocumented
- * variable that can be used as a convenience to do a/b comparison
- * of serial vs. parallel mounting.
- */
- boolean_t serial_mount = !parallel ||
- (getenv("ZFS_SERIAL_MOUNT") != NULL);
-
- /*
- * Sort the datasets by mountpoint. See mountpoint_cmp for details
- * of how these are sorted.
- */
- qsort(handles, num_handles, sizeof (zfs_handle_t *), mountpoint_cmp);
-
- if (serial_mount) {
- for (int i = 0; i < num_handles; i++) {
- func(handles[i], data);
- }
- return;
- }
-
- /*
- * Issue the callback function for each dataset using a parallel
- * algorithm that uses a thread pool to manage threads.
- */
- tpool_t *tp = tpool_create(1, mount_tp_nthr, 0, NULL);
-
- /*
- * There may be multiple "top level" mountpoints outside of the pool's
- * root mountpoint, e.g.: /foo /bar. Dispatch a mount task for each of
- * these.
- */
- for (int i = 0; i < num_handles;
- i = non_descendant_idx(handles, num_handles, i)) {
- /*
- * Since the mountpoints have been sorted so that the zoned
- * filesystems are at the end, a zoned filesystem seen from
- * the global zone means that we're done.
- */
- if (zoneid == GLOBAL_ZONEID &&
- zfs_prop_get_int(handles[i], ZFS_PROP_ZONED))
- break;
- zfs_dispatch_mount(hdl, handles, num_handles, i, func, data,
- tp);
- }
-
- tpool_wait(tp); /* wait for all scheduled mounts to complete */
- tpool_destroy(tp);
-}
-
-/*
- * Mount and share all datasets within the given pool. This assumes that no
- * datasets within the pool are currently mounted.
- */
-#pragma weak zpool_mount_datasets = zpool_enable_datasets
-int
-zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
-{
- get_all_cb_t cb = { 0 };
- mount_state_t ms = { 0 };
- zfs_handle_t *zfsp;
- int ret = 0;
-
- if ((zfsp = zfs_open(zhp->zpool_hdl, zhp->zpool_name,
- ZFS_TYPE_DATASET)) == NULL)
- goto out;
-
- /*
- * Gather all non-snapshot datasets within the pool. Start by adding
- * the root filesystem for this pool to the list, and then iterate
- * over all child filesystems.
- */
- libzfs_add_handle(&cb, zfsp);
- if (zfs_iter_filesystems(zfsp, zfs_iter_cb, &cb) != 0)
- goto out;
-
- /*
- * Mount all filesystems
- */
- ms.ms_mntopts = mntopts;
- ms.ms_mntflags = flags;
- zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used,
- zfs_mount_one, &ms, B_TRUE);
- if (ms.ms_mntstatus != 0)
- ret = ms.ms_mntstatus;
-
- /*
- * Share all filesystems that need to be shared. This needs to be
- * a separate pass because libshare is not mt-safe, and so we need
- * to share serially.
- */
- ms.ms_mntstatus = 0;
- zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used,
- zfs_share_one, &ms, B_FALSE);
- if (ms.ms_mntstatus != 0)
- ret = ms.ms_mntstatus;
-
-out:
- for (int i = 0; i < cb.cb_used; i++)
- zfs_close(cb.cb_handles[i]);
- free(cb.cb_handles);
-
- return (ret);
-}
-
-static int
-mountpoint_compare(const void *a, const void *b)
-{
- const char *mounta = *((char **)a);
- const char *mountb = *((char **)b);
-
- return (strcmp(mountb, mounta));
-}
-
-/* alias for 2002/240 */
-#pragma weak zpool_unmount_datasets = zpool_disable_datasets
-/*
- * Unshare and unmount all datasets within the given pool. We don't want to
- * rely on traversing the DSL to discover the filesystems within the pool,
- * because this may be expensive (if not all of them are mounted), and can fail
- * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and
- * gather all the filesystems that are currently mounted.
- */
-int
-zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
-{
- int used, alloc;
- struct mnttab entry;
- size_t namelen;
- char **mountpoints = NULL;
- zfs_handle_t **datasets = NULL;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- int i;
- int ret = -1;
- int flags = (force ? MS_FORCE : 0);
-#ifdef illumos
- sa_init_selective_arg_t sharearg;
-#endif
-
- namelen = strlen(zhp->zpool_name);
-
- rewind(hdl->libzfs_mnttab);
- used = alloc = 0;
- while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
- /*
- * Ignore non-ZFS entries.
- */
- if (entry.mnt_fstype == NULL ||
- strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
- continue;
-
- /*
- * Ignore filesystems not within this pool.
- */
- if (entry.mnt_mountp == NULL ||
- strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
- (entry.mnt_special[namelen] != '/' &&
- entry.mnt_special[namelen] != '\0'))
- continue;
-
- /*
- * At this point we've found a filesystem within our pool. Add
- * it to our growing list.
- */
- if (used == alloc) {
- if (alloc == 0) {
- if ((mountpoints = zfs_alloc(hdl,
- 8 * sizeof (void *))) == NULL)
- goto out;
-
- if ((datasets = zfs_alloc(hdl,
- 8 * sizeof (void *))) == NULL)
- goto out;
-
- alloc = 8;
- } else {
- void *ptr;
-
- if ((ptr = zfs_realloc(hdl, mountpoints,
- alloc * sizeof (void *),
- alloc * 2 * sizeof (void *))) == NULL)
- goto out;
- mountpoints = ptr;
-
- if ((ptr = zfs_realloc(hdl, datasets,
- alloc * sizeof (void *),
- alloc * 2 * sizeof (void *))) == NULL)
- goto out;
- datasets = ptr;
-
- alloc *= 2;
- }
- }
-
- if ((mountpoints[used] = zfs_strdup(hdl,
- entry.mnt_mountp)) == NULL)
- goto out;
-
- /*
- * This is allowed to fail, in case there is some I/O error. It
- * is only used to determine if we need to remove the underlying
- * mountpoint, so failure is not fatal.
- */
- datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
-
- used++;
- }
-
- /*
- * At this point, we have the entire list of filesystems, so sort it by
- * mountpoint.
- */
-#ifdef illumos
- sharearg.zhandle_arr = datasets;
- sharearg.zhandle_len = used;
- ret = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
- &sharearg);
- if (ret != 0)
- goto out;
-#endif
- qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
-
- /*
- * Walk through and first unshare everything.
- */
- for (i = 0; i < used; i++) {
- zfs_share_proto_t *curr_proto;
- for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
- curr_proto++) {
- if (is_shared(hdl, mountpoints[i], *curr_proto) &&
- unshare_one(hdl, mountpoints[i],
- mountpoints[i], *curr_proto) != 0)
- goto out;
- }
- }
-
- /*
- * Now unmount everything, removing the underlying directories as
- * appropriate.
- */
- for (i = 0; i < used; i++) {
- if (unmount_one(hdl, mountpoints[i], flags) != 0)
- goto out;
- }
-
- for (i = 0; i < used; i++) {
- if (datasets[i])
- remove_mountpoint(datasets[i]);
- }
-
- ret = 0;
-out:
- for (i = 0; i < used; i++) {
- if (datasets[i])
- zfs_close(datasets[i]);
- free(mountpoints[i]);
- }
- free(datasets);
- free(mountpoints);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
deleted file mode 100644
index 434f77e27da9..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
+++ /dev/null
@@ -1,4669 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- * Copyright 2016 Nexenta Systems, Inc.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright (c) 2017 Datto Inc.
- * Copyright (c) 2017, Intel Corporation.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <errno.h>
-#include <devid.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <sys/zfs_ioctl.h>
-#include <dlfcn.h>
-
-#include "zfs_namecheck.h"
-#include "zfs_prop.h"
-#include "libzfs_impl.h"
-#include "zfs_comutil.h"
-#include "zfeature_common.h"
-
-static int read_efi_label(nvlist_t *, diskaddr_t *, boolean_t *);
-static boolean_t zpool_vdev_is_interior(const char *name);
-
-#define BACKUP_SLICE "s2"
-
-typedef struct prop_flags {
- int create:1; /* Validate property on creation */
- int import:1; /* Validate property on import */
-} prop_flags_t;
-
-/*
- * ====================================================================
- * zpool property functions
- * ====================================================================
- */
-
-static int
-zpool_get_all_props(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_GET_PROPS, &zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zpool_props) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- return (0);
-}
-
-static int
-zpool_props_refresh(zpool_handle_t *zhp)
-{
- nvlist_t *old_props;
-
- old_props = zhp->zpool_props;
-
- if (zpool_get_all_props(zhp) != 0)
- return (-1);
-
- nvlist_free(old_props);
- return (0);
-}
-
-static char *
-zpool_get_prop_string(zpool_handle_t *zhp, zpool_prop_t prop,
- zprop_source_t *src)
-{
- nvlist_t *nv, *nvl;
- uint64_t ival;
- char *value;
- zprop_source_t source;
-
- nvl = zhp->zpool_props;
- if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0);
- source = ival;
- verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
- } else {
- source = ZPROP_SRC_DEFAULT;
- if ((value = (char *)zpool_prop_default_string(prop)) == NULL)
- value = "-";
- }
-
- if (src)
- *src = source;
-
- return (value);
-}
-
-uint64_t
-zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *src)
-{
- nvlist_t *nv, *nvl;
- uint64_t value;
- zprop_source_t source;
-
- if (zhp->zpool_props == NULL && zpool_get_all_props(zhp)) {
- /*
- * zpool_get_all_props() has most likely failed because
- * the pool is faulted, but if all we need is the top level
- * vdev's guid then get it from the zhp config nvlist.
- */
- if ((prop == ZPOOL_PROP_GUID) &&
- (nvlist_lookup_nvlist(zhp->zpool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nv) == 0) &&
- (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value)
- == 0)) {
- return (value);
- }
- return (zpool_prop_default_numeric(prop));
- }
-
- nvl = zhp->zpool_props;
- if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, &value) == 0);
- source = value;
- verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
- } else {
- source = ZPROP_SRC_DEFAULT;
- value = zpool_prop_default_numeric(prop);
- }
-
- if (src)
- *src = source;
-
- return (value);
-}
-
-/*
- * Map VDEV STATE to printed strings.
- */
-const char *
-zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
-{
- switch (state) {
- case VDEV_STATE_CLOSED:
- case VDEV_STATE_OFFLINE:
- return (gettext("OFFLINE"));
- case VDEV_STATE_REMOVED:
- return (gettext("REMOVED"));
- case VDEV_STATE_CANT_OPEN:
- if (aux == VDEV_AUX_CORRUPT_DATA || aux == VDEV_AUX_BAD_LOG)
- return (gettext("FAULTED"));
- else if (aux == VDEV_AUX_SPLIT_POOL)
- return (gettext("SPLIT"));
- else
- return (gettext("UNAVAIL"));
- case VDEV_STATE_FAULTED:
- return (gettext("FAULTED"));
- case VDEV_STATE_DEGRADED:
- return (gettext("DEGRADED"));
- case VDEV_STATE_HEALTHY:
- return (gettext("ONLINE"));
-
- default:
- break;
- }
-
- return (gettext("UNKNOWN"));
-}
-
-/*
- * Map POOL STATE to printed strings.
- */
-const char *
-zpool_pool_state_to_name(pool_state_t state)
-{
- switch (state) {
- case POOL_STATE_ACTIVE:
- return (gettext("ACTIVE"));
- case POOL_STATE_EXPORTED:
- return (gettext("EXPORTED"));
- case POOL_STATE_DESTROYED:
- return (gettext("DESTROYED"));
- case POOL_STATE_SPARE:
- return (gettext("SPARE"));
- case POOL_STATE_L2CACHE:
- return (gettext("L2CACHE"));
- case POOL_STATE_UNINITIALIZED:
- return (gettext("UNINITIALIZED"));
- case POOL_STATE_UNAVAIL:
- return (gettext("UNAVAIL"));
- case POOL_STATE_POTENTIALLY_ACTIVE:
- return (gettext("POTENTIALLY_ACTIVE"));
- }
-
- return (gettext("UNKNOWN"));
-}
-
-/*
- * Get a zpool property value for 'prop' and return the value in
- * a pre-allocated buffer.
- */
-int
-zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
- zprop_source_t *srctype, boolean_t literal)
-{
- uint64_t intval;
- const char *strval;
- zprop_source_t src = ZPROP_SRC_NONE;
- nvlist_t *nvroot;
- vdev_stat_t *vs;
- uint_t vsc;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- switch (prop) {
- case ZPOOL_PROP_NAME:
- (void) strlcpy(buf, zpool_get_name(zhp), len);
- break;
-
- case ZPOOL_PROP_HEALTH:
- (void) strlcpy(buf,
- zpool_pool_state_to_name(POOL_STATE_UNAVAIL), len);
- break;
-
- case ZPOOL_PROP_GUID:
- intval = zpool_get_prop_int(zhp, prop, &src);
- (void) snprintf(buf, len, "%llu", intval);
- break;
-
- case ZPOOL_PROP_ALTROOT:
- case ZPOOL_PROP_CACHEFILE:
- case ZPOOL_PROP_COMMENT:
- if (zhp->zpool_props != NULL ||
- zpool_get_all_props(zhp) == 0) {
- (void) strlcpy(buf,
- zpool_get_prop_string(zhp, prop, &src),
- len);
- break;
- }
- /* FALLTHROUGH */
- default:
- (void) strlcpy(buf, "-", len);
- break;
- }
-
- if (srctype != NULL)
- *srctype = src;
- return (0);
- }
-
- if (zhp->zpool_props == NULL && zpool_get_all_props(zhp) &&
- prop != ZPOOL_PROP_NAME)
- return (-1);
-
- switch (zpool_prop_get_type(prop)) {
- case PROP_TYPE_STRING:
- (void) strlcpy(buf, zpool_get_prop_string(zhp, prop, &src),
- len);
- break;
-
- case PROP_TYPE_NUMBER:
- intval = zpool_get_prop_int(zhp, prop, &src);
-
- switch (prop) {
- case ZPOOL_PROP_SIZE:
- case ZPOOL_PROP_ALLOCATED:
- case ZPOOL_PROP_FREE:
- case ZPOOL_PROP_FREEING:
- case ZPOOL_PROP_LEAKED:
- if (literal) {
- (void) snprintf(buf, len, "%llu",
- (u_longlong_t)intval);
- } else {
- (void) zfs_nicenum(intval, buf, len);
- }
- break;
- case ZPOOL_PROP_BOOTSIZE:
- case ZPOOL_PROP_EXPANDSZ:
- case ZPOOL_PROP_CHECKPOINT:
- if (intval == 0) {
- (void) strlcpy(buf, "-", len);
- } else if (literal) {
- (void) snprintf(buf, len, "%llu",
- (u_longlong_t)intval);
- } else {
- (void) zfs_nicenum(intval, buf, len);
- }
- break;
- case ZPOOL_PROP_CAPACITY:
- if (literal) {
- (void) snprintf(buf, len, "%llu",
- (u_longlong_t)intval);
- } else {
- (void) snprintf(buf, len, "%llu%%",
- (u_longlong_t)intval);
- }
- break;
- case ZPOOL_PROP_FRAGMENTATION:
- if (intval == UINT64_MAX) {
- (void) strlcpy(buf, "-", len);
- } else {
- (void) snprintf(buf, len, "%llu%%",
- (u_longlong_t)intval);
- }
- break;
- case ZPOOL_PROP_DEDUPRATIO:
- (void) snprintf(buf, len, "%llu.%02llux",
- (u_longlong_t)(intval / 100),
- (u_longlong_t)(intval % 100));
- break;
- case ZPOOL_PROP_HEALTH:
- verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- verify(nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc)
- == 0);
-
- (void) strlcpy(buf, zpool_state_to_name(intval,
- vs->vs_aux), len);
- break;
- case ZPOOL_PROP_VERSION:
- if (intval >= SPA_VERSION_FEATURES) {
- (void) snprintf(buf, len, "-");
- break;
- }
- /* FALLTHROUGH */
- default:
- (void) snprintf(buf, len, "%llu", intval);
- }
- break;
-
- case PROP_TYPE_INDEX:
- intval = zpool_get_prop_int(zhp, prop, &src);
- if (zpool_prop_index_to_string(prop, intval, &strval)
- != 0)
- return (-1);
- (void) strlcpy(buf, strval, len);
- break;
-
- default:
- abort();
- }
-
- if (srctype)
- *srctype = src;
-
- return (0);
-}
-
-/*
- * Check if the bootfs name has the same pool name as it is set to.
- * Assuming bootfs is a valid dataset name.
- */
-static boolean_t
-bootfs_name_valid(const char *pool, const char *bootfs)
-{
- int len = strlen(pool);
-
- if (!zfs_name_valid(bootfs, ZFS_TYPE_FILESYSTEM|ZFS_TYPE_SNAPSHOT))
- return (B_FALSE);
-
- if (strncmp(pool, bootfs, len) == 0 &&
- (bootfs[len] == '/' || bootfs[len] == '\0'))
- return (B_TRUE);
-
- return (B_FALSE);
-}
-
-boolean_t
-zpool_is_bootable(zpool_handle_t *zhp)
-{
- char bootfs[ZFS_MAX_DATASET_NAME_LEN];
-
- return (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
- sizeof (bootfs), NULL, B_FALSE) == 0 && strncmp(bootfs, "-",
- sizeof (bootfs)) != 0);
-}
-
-
-/*
- * Given an nvlist of zpool properties to be set, validate that they are
- * correct, and parse any numeric properties (index, boolean, etc) if they are
- * specified as strings.
- */
-static nvlist_t *
-zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
- nvlist_t *props, uint64_t version, prop_flags_t flags, char *errbuf)
-{
- nvpair_t *elem;
- nvlist_t *retprops;
- zpool_prop_t prop;
- char *strval;
- uint64_t intval;
- char *slash, *check;
- struct stat64 statbuf;
- zpool_handle_t *zhp;
-
- if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
- const char *propname = nvpair_name(elem);
-
- prop = zpool_name_to_prop(propname);
- if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) {
- int err;
- char *fname = strchr(propname, '@') + 1;
-
- err = zfeature_lookup_name(fname, NULL);
- if (err != 0) {
- ASSERT3U(err, ==, ENOENT);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid feature '%s'"), fname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- (void) nvpair_value_string(elem, &strval);
- if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set to "
- "'enabled'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (nvlist_add_uint64(retprops, propname, 0) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- continue;
- }
-
- /*
- * Make sure this property is valid and applies to this type.
- */
- if (prop == ZPOOL_PROP_INVAL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (zpool_prop_readonly(prop)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
- "is readonly"), propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
- goto error;
- }
-
- if (zprop_parse_value(hdl, elem, prop, ZFS_TYPE_POOL, retprops,
- &strval, &intval, errbuf) != 0)
- goto error;
-
- /*
- * Perform additional checking for specific properties.
- */
- switch (prop) {
- case ZPOOL_PROP_VERSION:
- if (intval < version ||
- !SPA_VERSION_IS_SUPPORTED(intval)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' number %d is invalid."),
- propname, intval);
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_BOOTSIZE:
- if (!flags.create) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set during pool "
- "creation"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_BOOTFS:
- if (flags.create || flags.import) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' cannot be set at creation "
- "or import time"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (version < SPA_VERSION_BOOTFS) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to support "
- "'%s' property"), propname);
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- goto error;
- }
-
- /*
- * bootfs property value has to be a dataset name and
- * the dataset has to be in the same pool as it sets to.
- */
- if (strval[0] != '\0' && !bootfs_name_valid(poolname,
- strval)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
- "is an invalid name"), strval);
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- goto error;
- }
-
- if ((zhp = zpool_open_canfail(hdl, poolname)) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not open pool '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
- goto error;
- }
- zpool_close(zhp);
- break;
-
- case ZPOOL_PROP_ALTROOT:
- if (!flags.create && !flags.import) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set during pool "
- "creation or import"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (strval[0] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "bad alternate root '%s'"), strval);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_CACHEFILE:
- if (strval[0] == '\0')
- break;
-
- if (strcmp(strval, "none") == 0)
- break;
-
- if (strval[0] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' must be empty, an "
- "absolute path, or 'none'"), propname);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
-
- slash = strrchr(strval, '/');
-
- if (slash[1] == '\0' || strcmp(slash, "/.") == 0 ||
- strcmp(slash, "/..") == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is not a valid file"), strval);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
-
- *slash = '\0';
-
- if (strval[0] != '\0' &&
- (stat64(strval, &statbuf) != 0 ||
- !S_ISDIR(statbuf.st_mode))) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is not a valid directory"),
- strval);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
-
- *slash = '/';
- break;
-
- case ZPOOL_PROP_COMMENT:
- for (check = strval; *check != '\0'; check++) {
- if (!isprint(*check)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "comment may only have printable "
- "characters"));
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- }
- if (strlen(strval) > ZPROP_MAX_COMMENT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "comment must not exceed %d characters"),
- ZPROP_MAX_COMMENT);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_READONLY:
- if (!flags.import) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set at "
- "import time"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_TNAME:
- if (!flags.create) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set at "
- "creation time"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_MULTIHOST:
- if (get_system_hostid() == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "requires a non-zero system hostid"));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s'(%d) not defined"), propname, prop);
- break;
- }
- }
-
- return (retprops);
-error:
- nvlist_free(retprops);
- return (NULL);
-}
-
-/*
- * Set zpool property : propname=propval.
- */
-int
-zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
-{
- zfs_cmd_t zc = { 0 };
- int ret = -1;
- char errbuf[1024];
- nvlist_t *nvl = NULL;
- nvlist_t *realprops;
- uint64_t version;
- prop_flags_t flags = { 0 };
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zpool_name);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
- return (no_memory(zhp->zpool_hdl));
-
- if (nvlist_add_string(nvl, propname, propval) != 0) {
- nvlist_free(nvl);
- return (no_memory(zhp->zpool_hdl));
- }
-
- version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
- if ((realprops = zpool_valid_proplist(zhp->zpool_hdl,
- zhp->zpool_name, nvl, version, flags, errbuf)) == NULL) {
- nvlist_free(nvl);
- return (-1);
- }
-
- nvlist_free(nvl);
- nvl = realprops;
-
- /*
- * Execute the corresponding ioctl() to set this property.
- */
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) {
- nvlist_free(nvl);
- return (-1);
- }
-
- ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SET_PROPS, &zc);
-
- zcmd_free_nvlists(&zc);
- nvlist_free(nvl);
-
- if (ret)
- (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf);
- else
- (void) zpool_props_refresh(zhp);
-
- return (ret);
-}
-
-int
-zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
-{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- zprop_list_t *entry;
- char buf[ZFS_MAXPROPLEN];
- nvlist_t *features = NULL;
- zprop_list_t **last;
- boolean_t firstexpand = (NULL == *plp);
-
- if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
- return (-1);
-
- last = plp;
- while (*last != NULL)
- last = &(*last)->pl_next;
-
- if ((*plp)->pl_all)
- features = zpool_get_features(zhp);
-
- if ((*plp)->pl_all && firstexpand) {
- for (int i = 0; i < SPA_FEATURES; i++) {
- zprop_list_t *entry = zfs_alloc(hdl,
- sizeof (zprop_list_t));
- entry->pl_prop = ZPROP_INVAL;
- entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
- spa_feature_table[i].fi_uname);
- entry->pl_width = strlen(entry->pl_user_prop);
- entry->pl_all = B_TRUE;
-
- *last = entry;
- last = &entry->pl_next;
- }
- }
-
- /* add any unsupported features */
- for (nvpair_t *nvp = nvlist_next_nvpair(features, NULL);
- nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
- char *propname;
- boolean_t found;
- zprop_list_t *entry;
-
- if (zfeature_is_supported(nvpair_name(nvp)))
- continue;
-
- propname = zfs_asprintf(hdl, "unsupported@%s",
- nvpair_name(nvp));
-
- /*
- * Before adding the property to the list make sure that no
- * other pool already added the same property.
- */
- found = B_FALSE;
- entry = *plp;
- while (entry != NULL) {
- if (entry->pl_user_prop != NULL &&
- strcmp(propname, entry->pl_user_prop) == 0) {
- found = B_TRUE;
- break;
- }
- entry = entry->pl_next;
- }
- if (found) {
- free(propname);
- continue;
- }
-
- entry = zfs_alloc(hdl, sizeof (zprop_list_t));
- entry->pl_prop = ZPROP_INVAL;
- entry->pl_user_prop = propname;
- entry->pl_width = strlen(entry->pl_user_prop);
- entry->pl_all = B_TRUE;
-
- *last = entry;
- last = &entry->pl_next;
- }
-
- for (entry = *plp; entry != NULL; entry = entry->pl_next) {
-
- if (entry->pl_fixed)
- continue;
-
- if (entry->pl_prop != ZPROP_INVAL &&
- zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf),
- NULL, B_FALSE) == 0) {
- if (strlen(buf) > entry->pl_width)
- entry->pl_width = strlen(buf);
- }
- }
-
- return (0);
-}
-
-/*
- * Get the state for the given feature on the given ZFS pool.
- */
-int
-zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
- size_t len)
-{
- uint64_t refcount;
- boolean_t found = B_FALSE;
- nvlist_t *features = zpool_get_features(zhp);
- boolean_t supported;
- const char *feature = strchr(propname, '@') + 1;
-
- supported = zpool_prop_feature(propname);
- ASSERT(supported || zpool_prop_unsupported(propname));
-
- /*
- * Convert from feature name to feature guid. This conversion is
- * unecessary for unsupported@... properties because they already
- * use guids.
- */
- if (supported) {
- int ret;
- spa_feature_t fid;
-
- ret = zfeature_lookup_name(feature, &fid);
- if (ret != 0) {
- (void) strlcpy(buf, "-", len);
- return (ENOTSUP);
- }
- feature = spa_feature_table[fid].fi_guid;
- }
-
- if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
- found = B_TRUE;
-
- if (supported) {
- if (!found) {
- (void) strlcpy(buf, ZFS_FEATURE_DISABLED, len);
- } else {
- if (refcount == 0)
- (void) strlcpy(buf, ZFS_FEATURE_ENABLED, len);
- else
- (void) strlcpy(buf, ZFS_FEATURE_ACTIVE, len);
- }
- } else {
- if (found) {
- if (refcount == 0) {
- (void) strcpy(buf, ZFS_UNSUPPORTED_INACTIVE);
- } else {
- (void) strcpy(buf, ZFS_UNSUPPORTED_READONLY);
- }
- } else {
- (void) strlcpy(buf, "-", len);
- return (ENOTSUP);
- }
- }
-
- return (0);
-}
-
-/*
- * Don't start the slice at the default block of 34; many storage
- * devices will use a stripe width of 128k, so start there instead.
- */
-#define NEW_START_BLOCK 256
-
-/*
- * Validate the given pool name, optionally putting an extended error message in
- * 'buf'.
- */
-boolean_t
-zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
-{
- namecheck_err_t why;
- char what;
- int ret;
-
- ret = pool_namecheck(pool, &why, &what);
-
- /*
- * The rules for reserved pool names were extended at a later point.
- * But we need to support users with existing pools that may now be
- * invalid. So we only check for this expanded set of names during a
- * create (or import), and only in userland.
- */
- if (ret == 0 && !isopen &&
- (strncmp(pool, "mirror", 6) == 0 ||
- strncmp(pool, "raidz", 5) == 0 ||
- strncmp(pool, "spare", 5) == 0 ||
- strcmp(pool, "log") == 0)) {
- if (hdl != NULL)
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "name is reserved"));
- return (B_FALSE);
- }
-
-
- if (ret != 0) {
- if (hdl != NULL) {
- switch (why) {
- case NAME_ERR_TOOLONG:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "name is too long"));
- break;
-
- case NAME_ERR_INVALCHAR:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "invalid character "
- "'%c' in pool name"), what);
- break;
-
- case NAME_ERR_NOLETTER:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name must begin with a letter"));
- break;
-
- case NAME_ERR_RESERVED:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name is reserved"));
- break;
-
- case NAME_ERR_DISKLIKE:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool name is reserved"));
- break;
-
- case NAME_ERR_LEADING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "leading slash in name"));
- break;
-
- case NAME_ERR_EMPTY_COMPONENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty component in name"));
- break;
-
- case NAME_ERR_TRAILING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "trailing slash in name"));
- break;
-
- case NAME_ERR_MULTIPLE_DELIMITERS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple '@' and/or '#' delimiters in "
- "name"));
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "(%d) not defined"), why);
- break;
- }
- }
- return (B_FALSE);
- }
-
- return (B_TRUE);
-}
-
-/*
- * Open a handle to the given pool, even if the pool is currently in the FAULTED
- * state.
- */
-zpool_handle_t *
-zpool_open_canfail(libzfs_handle_t *hdl, const char *pool)
-{
- zpool_handle_t *zhp;
- boolean_t missing;
-
- /*
- * Make sure the pool name is valid.
- */
- if (!zpool_name_valid(hdl, B_TRUE, pool)) {
- (void) zfs_error_fmt(hdl, EZFS_INVALIDNAME,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"),
- pool);
- return (NULL);
- }
-
- if ((zhp = zfs_alloc(hdl, sizeof (zpool_handle_t))) == NULL)
- return (NULL);
-
- zhp->zpool_hdl = hdl;
- (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name));
-
- if (zpool_refresh_stats(zhp, &missing) != 0) {
- zpool_close(zhp);
- return (NULL);
- }
-
- if (missing) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "no such pool"));
- (void) zfs_error_fmt(hdl, EZFS_NOENT,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), pool);
- zpool_close(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-/*
- * Like the above, but silent on error. Used when iterating over pools (because
- * the configuration cache may be out of date).
- */
-int
-zpool_open_silent(libzfs_handle_t *hdl, const char *pool, zpool_handle_t **ret)
-{
- zpool_handle_t *zhp;
- boolean_t missing;
-
- if ((zhp = zfs_alloc(hdl, sizeof (zpool_handle_t))) == NULL)
- return (-1);
-
- zhp->zpool_hdl = hdl;
- (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name));
-
- if (zpool_refresh_stats(zhp, &missing) != 0) {
- zpool_close(zhp);
- return (-1);
- }
-
- if (missing) {
- zpool_close(zhp);
- *ret = NULL;
- return (0);
- }
-
- *ret = zhp;
- return (0);
-}
-
-/*
- * Similar to zpool_open_canfail(), but refuses to open pools in the faulted
- * state.
- */
-zpool_handle_t *
-zpool_open(libzfs_handle_t *hdl, const char *pool)
-{
- zpool_handle_t *zhp;
-
- if ((zhp = zpool_open_canfail(hdl, pool)) == NULL)
- return (NULL);
-
- if (zhp->zpool_state == POOL_STATE_UNAVAIL) {
- (void) zfs_error_fmt(hdl, EZFS_POOLUNAVAIL,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), zhp->zpool_name);
- zpool_close(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-/*
- * Close the handle. Simply frees the memory associated with the handle.
- */
-void
-zpool_close(zpool_handle_t *zhp)
-{
- nvlist_free(zhp->zpool_config);
- nvlist_free(zhp->zpool_old_config);
- nvlist_free(zhp->zpool_props);
- free(zhp);
-}
-
-/*
- * Return the name of the pool.
- */
-const char *
-zpool_get_name(zpool_handle_t *zhp)
-{
- return (zhp->zpool_name);
-}
-
-
-/*
- * Return the state of the pool (ACTIVE or UNAVAILABLE)
- */
-int
-zpool_get_state(zpool_handle_t *zhp)
-{
- return (zhp->zpool_state);
-}
-
-/*
- * Check if vdev list contains a special vdev
- */
-static boolean_t
-zpool_has_special_vdev(nvlist_t *nvroot)
-{
- nvlist_t **child;
- uint_t children;
-
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0) {
- for (uint_t c = 0; c < children; c++) {
- char *bias;
-
- if (nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, &bias) == 0 &&
- strcmp(bias, VDEV_ALLOC_BIAS_SPECIAL) == 0) {
- return (B_TRUE);
- }
- }
- }
- return (B_FALSE);
-}
-
-/*
- * Create the named pool, using the provided vdev list. It is assumed
- * that the consumer has already validated the contents of the nvlist, so we
- * don't have to worry about error semantics.
- */
-int
-zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
- nvlist_t *props, nvlist_t *fsprops)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *zc_fsprops = NULL;
- nvlist_t *zc_props = NULL;
- char msg[1024];
- int ret = -1;
-
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), pool);
-
- if (!zpool_name_valid(hdl, B_FALSE, pool))
- return (zfs_error(hdl, EZFS_INVALIDNAME, msg));
-
- if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
- return (-1);
-
- if (props) {
- prop_flags_t flags = { .create = B_TRUE, .import = B_FALSE };
-
- if ((zc_props = zpool_valid_proplist(hdl, pool, props,
- SPA_VERSION_1, flags, msg)) == NULL) {
- goto create_failed;
- }
- }
-
- if (fsprops) {
- uint64_t zoned;
- char *zonestr;
-
- zoned = ((nvlist_lookup_string(fsprops,
- zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) &&
- strcmp(zonestr, "on") == 0);
-
- if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
- fsprops, zoned, NULL, NULL, msg)) == NULL) {
- goto create_failed;
- }
-
- if (nvlist_exists(zc_fsprops,
- zfs_prop_to_name(ZFS_PROP_SPECIAL_SMALL_BLOCKS)) &&
- !zpool_has_special_vdev(nvroot)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "%s property requires a special vdev"),
- zfs_prop_to_name(ZFS_PROP_SPECIAL_SMALL_BLOCKS));
- (void) zfs_error(hdl, EZFS_BADPROP, msg);
- goto create_failed;
- }
-
- if (!zc_props &&
- (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) {
- goto create_failed;
- }
- if (nvlist_add_nvlist(zc_props,
- ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) {
- goto create_failed;
- }
- }
-
- if (zc_props && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
- goto create_failed;
-
- (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
-
- if ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_CREATE, &zc)) != 0) {
-
- zcmd_free_nvlists(&zc);
- nvlist_free(zc_props);
- nvlist_free(zc_fsprops);
-
- switch (errno) {
- case EBUSY:
- /*
- * This can happen if the user has specified the same
- * device multiple times. We can't reliably detect this
- * until we try to add it and see we already have a
- * label.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more vdevs refer to the same device"));
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- case ERANGE:
- /*
- * This happens if the record size is smaller or larger
- * than the allowed size range, or not a power of 2.
- *
- * NOTE: although zfs_valid_proplist is called earlier,
- * this case may have slipped through since the
- * pool does not exist yet and it is therefore
- * impossible to read properties e.g. max blocksize
- * from the pool.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "record size invalid"));
- return (zfs_error(hdl, EZFS_BADPROP, msg));
-
- case EOVERFLOW:
- /*
- * This occurs when one of the devices is below
- * SPA_MINDEVSIZE. Unfortunately, we can't detect which
- * device was the problem device since there's no
- * reliable way to determine device size from userland.
- */
- {
- char buf[64];
-
- zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is less than the "
- "minimum size (%s)"), buf);
- }
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- case ENOSPC:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is out of space"));
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- case ENOTBLK:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cache device must be a disk or disk slice"));
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- default:
- return (zpool_standard_error(hdl, errno, msg));
- }
- }
-
-create_failed:
- zcmd_free_nvlists(&zc);
- nvlist_free(zc_props);
- nvlist_free(zc_fsprops);
- return (ret);
-}
-
-/*
- * Destroy the given pool. It is up to the caller to ensure that there are no
- * datasets left in the pool.
- */
-int
-zpool_destroy(zpool_handle_t *zhp, const char *log_str)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *zfp = NULL;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
-
- if (zhp->zpool_state == POOL_STATE_ACTIVE &&
- (zfp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_FILESYSTEM)) == NULL)
- return (-1);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_history = (uint64_t)(uintptr_t)log_str;
-
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot destroy '%s'"), zhp->zpool_name);
-
- if (errno == EROFS) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is read only"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- } else {
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- if (zfp)
- zfs_close(zfp);
- return (-1);
- }
-
- if (zfp) {
- remove_mountpoint(zfp);
- zfs_close(zfp);
- }
-
- return (0);
-}
-
-/*
- * Create a checkpoint in the given pool.
- */
-int
-zpool_checkpoint(zpool_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
- int error;
-
- error = lzc_pool_checkpoint(zhp->zpool_name);
- if (error != 0) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot checkpoint '%s'"), zhp->zpool_name);
- (void) zpool_standard_error(hdl, error, msg);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Discard the checkpoint from the given pool.
- */
-int
-zpool_discard_checkpoint(zpool_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
- int error;
-
- error = lzc_pool_checkpoint_discard(zhp->zpool_name);
- if (error != 0) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot discard checkpoint in '%s'"), zhp->zpool_name);
- (void) zpool_standard_error(hdl, error, msg);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Add the given vdevs to the pool. The caller must have already performed the
- * necessary verification to ensure that the vdev specification is well-formed.
- */
-int
-zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
-{
- zfs_cmd_t zc = { 0 };
- int ret;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
- nvlist_t **spares, **l2cache;
- uint_t nspares, nl2cache;
-
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot add to '%s'"), zhp->zpool_name);
-
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
- SPA_VERSION_SPARES &&
- nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
- "upgraded to add hot spares"));
- return (zfs_error(hdl, EZFS_BADVERSION, msg));
- }
-
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
- SPA_VERSION_L2CACHE &&
- nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
- &l2cache, &nl2cache) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
- "upgraded to add cache devices"));
- return (zfs_error(hdl, EZFS_BADVERSION, msg));
- }
-
- if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
- return (-1);
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) {
- switch (errno) {
- case EBUSY:
- /*
- * This can happen if the user has specified the same
- * device multiple times. We can't reliably detect this
- * until we try to add it and see we already have a
- * label.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more vdevs refer to the same device"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EINVAL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid config; a pool with removing/removed "
- "vdevs does not support adding raidz vdevs"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EOVERFLOW:
- /*
- * This occurrs when one of the devices is below
- * SPA_MINDEVSIZE. Unfortunately, we can't detect which
- * device was the problem device since there's no
- * reliable way to determine device size from userland.
- */
- {
- char buf[64];
-
- zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "device is less than the minimum "
- "size (%s)"), buf);
- }
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to add these vdevs"));
- (void) zfs_error(hdl, EZFS_BADVERSION, msg);
- break;
-
- case EDOM:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "root pool can not have multiple vdevs"
- " or separate logs"));
- (void) zfs_error(hdl, EZFS_POOL_NOTSUP, msg);
- break;
-
- case ENOTBLK:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cache device must be a disk or disk slice"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- ret = -1;
- } else {
- ret = 0;
- }
-
- zcmd_free_nvlists(&zc);
-
- return (ret);
-}
-
-/*
- * Exports the pool from the system. The caller must ensure that there are no
- * mounted datasets in the pool.
- */
-static int
-zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce,
- const char *log_str)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
-
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot export '%s'"), zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_cookie = force;
- zc.zc_guid = hardforce;
- zc.zc_history = (uint64_t)(uintptr_t)log_str;
-
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
- switch (errno) {
- case EXDEV:
- zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN,
- "use '-f' to override the following errors:\n"
- "'%s' has an active shared spare which could be"
- " used by other pools once '%s' is exported."),
- zhp->zpool_name, zhp->zpool_name);
- return (zfs_error(zhp->zpool_hdl, EZFS_ACTIVE_SPARE,
- msg));
- default:
- return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
- msg));
- }
- }
-
- return (0);
-}
-
-int
-zpool_export(zpool_handle_t *zhp, boolean_t force, const char *log_str)
-{
- return (zpool_export_common(zhp, force, B_FALSE, log_str));
-}
-
-int
-zpool_export_force(zpool_handle_t *zhp, const char *log_str)
-{
- return (zpool_export_common(zhp, B_TRUE, B_TRUE, log_str));
-}
-
-static void
-zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun,
- nvlist_t *config)
-{
- nvlist_t *nv = NULL;
- uint64_t rewindto;
- int64_t loss = -1;
- struct tm t;
- char timestr[128];
-
- if (!hdl->libzfs_printerr || config == NULL)
- return;
-
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
- nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0) {
- return;
- }
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
- return;
- (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
-
- if (localtime_r((time_t *)&rewindto, &t) != NULL &&
- strftime(timestr, 128, 0, &t) != 0) {
- if (dryrun) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Would be able to return %s "
- "to its state as of %s.\n"),
- name, timestr);
- } else {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Pool %s returned to its state as of %s.\n"),
- name, timestr);
- }
- if (loss > 120) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "%s approximately %lld "),
- dryrun ? "Would discard" : "Discarded",
- (loss + 30) / 60);
- (void) printf(dgettext(TEXT_DOMAIN,
- "minutes of transactions.\n"));
- } else if (loss > 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "%s approximately %lld "),
- dryrun ? "Would discard" : "Discarded", loss);
- (void) printf(dgettext(TEXT_DOMAIN,
- "seconds of transactions.\n"));
- }
- }
-}
-
-void
-zpool_explain_recover(libzfs_handle_t *hdl, const char *name, int reason,
- nvlist_t *config)
-{
- nvlist_t *nv = NULL;
- int64_t loss = -1;
- uint64_t edata = UINT64_MAX;
- uint64_t rewindto;
- struct tm t;
- char timestr[128];
-
- if (!hdl->libzfs_printerr)
- return;
-
- if (reason >= 0)
- (void) printf(dgettext(TEXT_DOMAIN, "action: "));
- else
- (void) printf(dgettext(TEXT_DOMAIN, "\t"));
-
- /* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
- nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0 ||
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
- goto no_info;
-
- (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_DATA_ERRORS,
- &edata);
-
- (void) printf(dgettext(TEXT_DOMAIN,
- "Recovery is possible, but will result in some data loss.\n"));
-
- if (localtime_r((time_t *)&rewindto, &t) != NULL &&
- strftime(timestr, 128, 0, &t) != 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "\tReturning the pool to its state as of %s\n"
- "\tshould correct the problem. "),
- timestr);
- } else {
- (void) printf(dgettext(TEXT_DOMAIN,
- "\tReverting the pool to an earlier state "
- "should correct the problem.\n\t"));
- }
-
- if (loss > 120) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Approximately %lld minutes of data\n"
- "\tmust be discarded, irreversibly. "), (loss + 30) / 60);
- } else if (loss > 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Approximately %lld seconds of data\n"
- "\tmust be discarded, irreversibly. "), loss);
- }
- if (edata != 0 && edata != UINT64_MAX) {
- if (edata == 1) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "After rewind, at least\n"
- "\tone persistent user-data error will remain. "));
- } else {
- (void) printf(dgettext(TEXT_DOMAIN,
- "After rewind, several\n"
- "\tpersistent user-data errors will remain. "));
- }
- }
- (void) printf(dgettext(TEXT_DOMAIN,
- "Recovery can be attempted\n\tby executing 'zpool %s -F %s'. "),
- reason >= 0 ? "clear" : "import", name);
-
- (void) printf(dgettext(TEXT_DOMAIN,
- "A scrub of the pool\n"
- "\tis strongly recommended after recovery.\n"));
- return;
-
-no_info:
- (void) printf(dgettext(TEXT_DOMAIN,
- "Destroy and re-create the pool from\n\ta backup source.\n"));
-}
-
-/*
- * zpool_import() is a contracted interface. Should be kept the same
- * if possible.
- *
- * Applications should use zpool_import_props() to import a pool with
- * new properties value to be set.
- */
-int
-zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
- char *altroot)
-{
- nvlist_t *props = NULL;
- int ret;
-
- if (altroot != NULL) {
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
- return (zfs_error_fmt(hdl, EZFS_NOMEM,
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- newname));
- }
-
- if (nvlist_add_string(props,
- zpool_prop_to_name(ZPOOL_PROP_ALTROOT), altroot) != 0 ||
- nvlist_add_string(props,
- zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), "none") != 0) {
- nvlist_free(props);
- return (zfs_error_fmt(hdl, EZFS_NOMEM,
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- newname));
- }
- }
-
- ret = zpool_import_props(hdl, config, newname, props,
- ZFS_IMPORT_NORMAL);
- nvlist_free(props);
- return (ret);
-}
-
-static void
-print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv,
- int indent)
-{
- nvlist_t **child;
- uint_t c, children;
- char *vname;
- uint64_t is_log = 0;
-
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG,
- &is_log);
-
- if (name != NULL)
- (void) printf("\t%*s%s%s\n", indent, "", name,
- is_log ? " [log]" : "");
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return;
-
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(hdl, NULL, child[c], VDEV_NAME_TYPE_ID);
- print_vdev_tree(hdl, vname, child[c], indent + 2);
- free(vname);
- }
-}
-
-void
-zpool_print_unsup_feat(nvlist_t *config)
-{
- nvlist_t *nvinfo, *unsup_feat;
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) ==
- 0);
- verify(nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT,
- &unsup_feat) == 0);
-
- for (nvpair_t *nvp = nvlist_next_nvpair(unsup_feat, NULL); nvp != NULL;
- nvp = nvlist_next_nvpair(unsup_feat, nvp)) {
- char *desc;
-
- verify(nvpair_type(nvp) == DATA_TYPE_STRING);
- verify(nvpair_value_string(nvp, &desc) == 0);
-
- if (strlen(desc) > 0)
- (void) printf("\t%s (%s)\n", nvpair_name(nvp), desc);
- else
- (void) printf("\t%s\n", nvpair_name(nvp));
- }
-}
-
-/*
- * Import the given pool using the known configuration and a list of
- * properties to be set. The configuration should have come from
- * zpool_find_import(). The 'newname' parameters control whether the pool
- * is imported with a different name.
- */
-int
-zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
- nvlist_t *props, int flags)
-{
- zfs_cmd_t zc = { 0 };
- zpool_load_policy_t policy;
- nvlist_t *nv = NULL;
- nvlist_t *nvinfo = NULL;
- nvlist_t *missing = NULL;
- char *thename;
- char *origname;
- int ret;
- int error = 0;
- char errbuf[1024];
-
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &origname) == 0);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot import pool '%s'"), origname);
-
- if (newname != NULL) {
- if (!zpool_name_valid(hdl, B_FALSE, newname))
- return (zfs_error_fmt(hdl, EZFS_INVALIDNAME,
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- newname));
- thename = (char *)newname;
- } else {
- thename = origname;
- }
-
- if (props != NULL) {
- uint64_t version;
- prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
-
- if ((props = zpool_valid_proplist(hdl, origname,
- props, version, flags, errbuf)) == NULL)
- return (-1);
- if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
- nvlist_free(props);
- return (-1);
- }
- nvlist_free(props);
- }
-
- (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &zc.zc_guid) == 0);
-
- if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- if (zcmd_alloc_dst_nvlist(hdl, &zc, zc.zc_nvlist_conf_size * 2) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zc.zc_cookie = flags;
- while ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc)) != 0 &&
- errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
- if (ret != 0)
- error = errno;
-
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nv);
-
- zcmd_free_nvlists(&zc);
-
- zpool_get_load_policy(config, &policy);
-
- if (error) {
- char desc[1024];
- char aux[256];
-
- /*
- * Dry-run failed, but we print out what success
- * looks like if we found a best txg
- */
- if (policy.zlp_rewind & ZPOOL_TRY_REWIND) {
- zpool_rewind_exclaim(hdl, newname ? origname : thename,
- B_TRUE, nv);
- nvlist_free(nv);
- return (-1);
- }
-
- if (newname == NULL)
- (void) snprintf(desc, sizeof (desc),
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- thename);
- else
- (void) snprintf(desc, sizeof (desc),
- dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"),
- origname, thename);
-
- switch (error) {
- case ENOTSUP:
- if (nv != NULL && nvlist_lookup_nvlist(nv,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
- nvlist_exists(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT)) {
- (void) printf(dgettext(TEXT_DOMAIN, "This "
- "pool uses the following feature(s) not "
- "supported by this system:\n"));
- zpool_print_unsup_feat(nv);
- if (nvlist_exists(nvinfo,
- ZPOOL_CONFIG_CAN_RDONLY)) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "All unsupported features are only "
- "required for writing to the pool."
- "\nThe pool can be imported using "
- "'-o readonly=on'.\n"));
- }
- }
- /*
- * Unsupported version.
- */
- (void) zfs_error(hdl, EZFS_BADVERSION, desc);
- break;
-
- case EREMOTEIO:
- if (nv != NULL && nvlist_lookup_nvlist(nv,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0) {
- char *hostname = "<unknown>";
- uint64_t hostid = 0;
- mmp_state_t mmp_state;
-
- mmp_state = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_STATE);
-
- if (nvlist_exists(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTNAME))
- hostname = fnvlist_lookup_string(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTNAME);
-
- if (nvlist_exists(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTID))
- hostid = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTID);
-
- if (mmp_state == MMP_STATE_ACTIVE) {
- (void) snprintf(aux, sizeof (aux),
- dgettext(TEXT_DOMAIN, "pool is imp"
- "orted on host '%s' (hostid=%lx).\n"
- "Export the pool on the other "
- "system, then run 'zpool import'."),
- hostname, (unsigned long) hostid);
- } else if (mmp_state == MMP_STATE_NO_HOSTID) {
- (void) snprintf(aux, sizeof (aux),
- dgettext(TEXT_DOMAIN, "pool has "
- "the multihost property on and "
- "the\nsystem's hostid is not "
- "set.\n"));
- }
-
- (void) zfs_error_aux(hdl, aux);
- }
- (void) zfs_error(hdl, EZFS_ACTIVE_POOL, desc);
- break;
-
- case EINVAL:
- (void) zfs_error(hdl, EZFS_INVALCONFIG, desc);
- break;
-
- case EROFS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is read only"));
- (void) zfs_error(hdl, EZFS_BADDEV, desc);
- break;
-
- case ENXIO:
- if (nv && nvlist_lookup_nvlist(nv,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
- nvlist_lookup_nvlist(nvinfo,
- ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "The devices below are missing or "
- "corrupted, use '-m' to import the pool "
- "anyway:\n"));
- print_vdev_tree(hdl, NULL, missing, 2);
- (void) printf("\n");
- }
- (void) zpool_standard_error(hdl, error, desc);
- break;
-
- case EEXIST:
- (void) zpool_standard_error(hdl, error, desc);
- break;
- case ENAMETOOLONG:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "new name of at least one dataset is longer than "
- "the maximum allowable length"));
- (void) zfs_error(hdl, EZFS_NAMETOOLONG, desc);
- break;
- default:
- (void) zpool_standard_error(hdl, error, desc);
- zpool_explain_recover(hdl,
- newname ? origname : thename, -error, nv);
- break;
- }
-
- nvlist_free(nv);
- ret = -1;
- } else {
- zpool_handle_t *zhp;
-
- /*
- * This should never fail, but play it safe anyway.
- */
- if (zpool_open_silent(hdl, thename, &zhp) != 0)
- ret = -1;
- else if (zhp != NULL)
- zpool_close(zhp);
- if (policy.zlp_rewind &
- (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
- zpool_rewind_exclaim(hdl, newname ? origname : thename,
- ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0), nv);
- }
- nvlist_free(nv);
- return (0);
- }
-
- return (ret);
-}
-
-/*
- * Scan the pool.
- */
-int
-zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- int err;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_cookie = func;
- zc.zc_flags = cmd;
-
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
- return (0);
-
- err = errno;
-
- /* ECANCELED on a scrub means we resumed a paused scrub */
- if (err == ECANCELED && func == POOL_SCAN_SCRUB &&
- cmd == POOL_SCRUB_NORMAL)
- return (0);
-
- if (err == ENOENT && func != POOL_SCAN_NONE && cmd == POOL_SCRUB_NORMAL)
- return (0);
-
- if (func == POOL_SCAN_SCRUB) {
- if (cmd == POOL_SCRUB_PAUSE) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot pause scrubbing %s"), zc.zc_name);
- } else {
- assert(cmd == POOL_SCRUB_NORMAL);
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot scrub %s"), zc.zc_name);
- }
- } else if (func == POOL_SCAN_NONE) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot cancel scrubbing %s"),
- zc.zc_name);
- } else {
- assert(!"unexpected result");
- }
-
- if (err == EBUSY) {
- nvlist_t *nvroot;
- pool_scan_stat_t *ps = NULL;
- uint_t psc;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- (void) nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc);
- if (ps && ps->pss_func == POOL_SCAN_SCRUB) {
- if (cmd == POOL_SCRUB_PAUSE)
- return (zfs_error(hdl, EZFS_SCRUB_PAUSED, msg));
- else
- return (zfs_error(hdl, EZFS_SCRUBBING, msg));
- } else {
- return (zfs_error(hdl, EZFS_RESILVERING, msg));
- }
- } else if (err == ENOENT) {
- return (zfs_error(hdl, EZFS_NO_SCRUB, msg));
- } else {
- return (zpool_standard_error(hdl, err, msg));
- }
-}
-
-static int
-xlate_init_err(int err)
-{
- switch (err) {
- case ENODEV:
- return (EZFS_NODEVICE);
- case EINVAL:
- case EROFS:
- return (EZFS_BADDEV);
- case EBUSY:
- return (EZFS_INITIALIZING);
- case ESRCH:
- return (EZFS_NO_INITIALIZE);
- }
- return (err);
-}
-
-/*
- * Begin, suspend, or cancel the initialization (initializing of all free
- * blocks) for the given vdevs in the given pool.
- */
-int
-zpool_initialize(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
- nvlist_t *vds)
-{
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- nvlist_t *errlist;
-
- /* translate vdev names to guids */
- nvlist_t *vdev_guids = fnvlist_alloc();
- nvlist_t *guids_to_paths = fnvlist_alloc();
- boolean_t spare, cache;
- nvlist_t *tgt;
- nvpair_t *elem;
-
- for (elem = nvlist_next_nvpair(vds, NULL); elem != NULL;
- elem = nvlist_next_nvpair(vds, elem)) {
- char *vd_path = nvpair_name(elem);
- tgt = zpool_find_vdev(zhp, vd_path, &spare, &cache, NULL);
-
- if ((tgt == NULL) || cache || spare) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot initialize '%s'"),
- vd_path);
- int err = (tgt == NULL) ? EZFS_NODEVICE :
- (spare ? EZFS_ISSPARE : EZFS_ISL2CACHE);
- fnvlist_free(vdev_guids);
- fnvlist_free(guids_to_paths);
- return (zfs_error(hdl, err, msg));
- }
-
- uint64_t guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID);
- fnvlist_add_uint64(vdev_guids, vd_path, guid);
-
- (void) snprintf(msg, sizeof (msg), "%llu", guid);
- fnvlist_add_string(guids_to_paths, msg, vd_path);
- }
-
- int err = lzc_initialize(zhp->zpool_name, cmd_type, vdev_guids,
- &errlist);
- fnvlist_free(vdev_guids);
-
- if (err == 0) {
- fnvlist_free(guids_to_paths);
- return (0);
- }
-
- nvlist_t *vd_errlist = NULL;
- if (errlist != NULL) {
- vd_errlist = fnvlist_lookup_nvlist(errlist,
- ZPOOL_INITIALIZE_VDEVS);
- }
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "operation failed"));
-
- for (elem = nvlist_next_nvpair(vd_errlist, NULL); elem != NULL;
- elem = nvlist_next_nvpair(vd_errlist, elem)) {
- int64_t vd_error = xlate_init_err(fnvpair_value_int64(elem));
- char *path = fnvlist_lookup_string(guids_to_paths,
- nvpair_name(elem));
- (void) zfs_error_fmt(hdl, vd_error, "cannot initialize '%s'",
- path);
- }
-
- fnvlist_free(guids_to_paths);
- if (vd_errlist != NULL)
- return (-1);
-
- return (zpool_standard_error(hdl, err, msg));
-}
-
-#ifdef illumos
-/*
- * This provides a very minimal check whether a given string is likely a
- * c#t#d# style string. Users of this are expected to do their own
- * verification of the s# part.
- */
-#define CTD_CHECK(str) (str && str[0] == 'c' && isdigit(str[1]))
-
-/*
- * More elaborate version for ones which may start with "/dev/dsk/"
- * and the like.
- */
-static int
-ctd_check_path(char *str)
-{
- /*
- * If it starts with a slash, check the last component.
- */
- if (str && str[0] == '/') {
- char *tmp = strrchr(str, '/');
-
- /*
- * If it ends in "/old", check the second-to-last
- * component of the string instead.
- */
- if (tmp != str && strcmp(tmp, "/old") == 0) {
- for (tmp--; *tmp != '/'; tmp--)
- ;
- }
- str = tmp + 1;
- }
- return (CTD_CHECK(str));
-}
-#endif
-
-/*
- * Find a vdev that matches the search criteria specified. We use the
- * the nvpair name to determine how we should look for the device.
- * 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL
- * spare; but FALSE if its an INUSE spare.
- */
-static nvlist_t *
-vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
- boolean_t *l2cache, boolean_t *log)
-{
- uint_t c, children;
- nvlist_t **child;
- nvlist_t *ret;
- uint64_t is_log;
- char *srchkey;
- nvpair_t *pair = nvlist_next_nvpair(search, NULL);
-
- /* Nothing to look for */
- if (search == NULL || pair == NULL)
- return (NULL);
-
- /* Obtain the key we will use to search */
- srchkey = nvpair_name(pair);
-
- switch (nvpair_type(pair)) {
- case DATA_TYPE_UINT64:
- if (strcmp(srchkey, ZPOOL_CONFIG_GUID) == 0) {
- uint64_t srchval, theguid;
-
- verify(nvpair_value_uint64(pair, &srchval) == 0);
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
- &theguid) == 0);
- if (theguid == srchval)
- return (nv);
- }
- break;
-
- case DATA_TYPE_STRING: {
- char *srchval, *val;
-
- verify(nvpair_value_string(pair, &srchval) == 0);
- if (nvlist_lookup_string(nv, srchkey, &val) != 0)
- break;
-
- /*
- * Search for the requested value. Special cases:
- *
- * - ZPOOL_CONFIG_PATH for whole disk entries. To support
- * UEFI boot, these end in "s0" or "s0/old" or "s1" or
- * "s1/old". The "s0" or "s1" part is hidden from the user,
- * but included in the string, so this matches around it.
- * - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE).
- *
- * Otherwise, all other searches are simple string compares.
- */
-#ifdef illumos
- if (strcmp(srchkey, ZPOOL_CONFIG_PATH) == 0 &&
- ctd_check_path(val)) {
- uint64_t wholedisk = 0;
-
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk);
- if (wholedisk) {
- int slen = strlen(srchval);
- int vlen = strlen(val);
-
- if (slen != vlen - 2)
- break;
-
- /*
- * make_leaf_vdev() should only set
- * wholedisk for ZPOOL_CONFIG_PATHs which
- * will include "/dev/dsk/", giving plenty of
- * room for the indices used next.
- */
- ASSERT(vlen >= 6);
-
- /*
- * strings identical except trailing "s0"
- */
- if ((strcmp(&val[vlen - 2], "s0") == 0 ||
- strcmp(&val[vlen - 2], "s1") == 0) &&
- strncmp(srchval, val, slen) == 0)
- return (nv);
-
- /*
- * strings identical except trailing "s0/old"
- */
- if ((strcmp(&val[vlen - 6], "s0/old") == 0 ||
- strcmp(&val[vlen - 6], "s1/old") == 0) &&
- strcmp(&srchval[slen - 4], "/old") == 0 &&
- strncmp(srchval, val, slen - 4) == 0)
- return (nv);
-
- break;
- }
- } else if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) {
-#else
- if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) {
-#endif
- char *type, *idx, *end, *p;
- uint64_t id, vdev_id;
-
- /*
- * Determine our vdev type, keeping in mind
- * that the srchval is composed of a type and
- * vdev id pair (i.e. mirror-4).
- */
- if ((type = strdup(srchval)) == NULL)
- return (NULL);
-
- if ((p = strrchr(type, '-')) == NULL) {
- free(type);
- break;
- }
- idx = p + 1;
- *p = '\0';
-
- /*
- * If the types don't match then keep looking.
- */
- if (strncmp(val, type, strlen(val)) != 0) {
- free(type);
- break;
- }
-
- verify(zpool_vdev_is_interior(type));
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
- &id) == 0);
-
- errno = 0;
- vdev_id = strtoull(idx, &end, 10);
-
- free(type);
- if (errno != 0)
- return (NULL);
-
- /*
- * Now verify that we have the correct vdev id.
- */
- if (vdev_id == id)
- return (nv);
- }
-
- /*
- * Common case
- */
- if (strcmp(srchval, val) == 0)
- return (nv);
- break;
- }
-
- default:
- break;
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return (NULL);
-
- for (c = 0; c < children; c++) {
- if ((ret = vdev_to_nvlist_iter(child[c], search,
- avail_spare, l2cache, NULL)) != NULL) {
- /*
- * The 'is_log' value is only set for the toplevel
- * vdev, not the leaf vdevs. So we always lookup the
- * log device from the root of the vdev tree (where
- * 'log' is non-NULL).
- */
- if (log != NULL &&
- nvlist_lookup_uint64(child[c],
- ZPOOL_CONFIG_IS_LOG, &is_log) == 0 &&
- is_log) {
- *log = B_TRUE;
- }
- return (ret);
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if ((ret = vdev_to_nvlist_iter(child[c], search,
- avail_spare, l2cache, NULL)) != NULL) {
- *avail_spare = B_TRUE;
- return (ret);
- }
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if ((ret = vdev_to_nvlist_iter(child[c], search,
- avail_spare, l2cache, NULL)) != NULL) {
- *l2cache = B_TRUE;
- return (ret);
- }
- }
- }
-
- return (NULL);
-}
-
-/*
- * Given a physical path (minus the "/devices" prefix), find the
- * associated vdev.
- */
-nvlist_t *
-zpool_find_vdev_by_physpath(zpool_handle_t *zhp, const char *ppath,
- boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log)
-{
- nvlist_t *search, *nvroot, *ret;
-
- verify(nvlist_alloc(&search, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PHYS_PATH, ppath) == 0);
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- *avail_spare = B_FALSE;
- *l2cache = B_FALSE;
- if (log != NULL)
- *log = B_FALSE;
- ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log);
- nvlist_free(search);
-
- return (ret);
-}
-
-/*
- * Determine if we have an "interior" top-level vdev (i.e mirror/raidz).
- */
-static boolean_t
-zpool_vdev_is_interior(const char *name)
-{
- if (strncmp(name, VDEV_TYPE_RAIDZ, strlen(VDEV_TYPE_RAIDZ)) == 0 ||
- strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 ||
- strncmp(name,
- VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 ||
- strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0)
- return (B_TRUE);
- return (B_FALSE);
-}
-
-nvlist_t *
-zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
- boolean_t *l2cache, boolean_t *log)
-{
- char buf[MAXPATHLEN];
- char *end;
- nvlist_t *nvroot, *search, *ret;
- uint64_t guid;
-
- verify(nvlist_alloc(&search, NV_UNIQUE_NAME, KM_SLEEP) == 0);
-
- guid = strtoull(path, &end, 10);
- if (guid != 0 && *end == '\0') {
- verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0);
- } else if (zpool_vdev_is_interior(path)) {
- verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
- } else if (path[0] != '/') {
- (void) snprintf(buf, sizeof (buf), "%s%s", _PATH_DEV, path);
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0);
- } else {
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0);
- }
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- *avail_spare = B_FALSE;
- *l2cache = B_FALSE;
- if (log != NULL)
- *log = B_FALSE;
- ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log);
- nvlist_free(search);
-
- return (ret);
-}
-
-static int
-vdev_is_online(nvlist_t *nv)
-{
- uint64_t ival;
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 ||
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 ||
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0)
- return (0);
-
- return (1);
-}
-
-/*
- * Helper function for zpool_get_physpaths().
- */
-static int
-vdev_get_one_physpath(nvlist_t *config, char *physpath, size_t physpath_size,
- size_t *bytes_written)
-{
- size_t bytes_left, pos, rsz;
- char *tmppath;
- const char *format;
-
- if (nvlist_lookup_string(config, ZPOOL_CONFIG_PHYS_PATH,
- &tmppath) != 0)
- return (EZFS_NODEVICE);
-
- pos = *bytes_written;
- bytes_left = physpath_size - pos;
- format = (pos == 0) ? "%s" : " %s";
-
- rsz = snprintf(physpath + pos, bytes_left, format, tmppath);
- *bytes_written += rsz;
-
- if (rsz >= bytes_left) {
- /* if physpath was not copied properly, clear it */
- if (bytes_left != 0) {
- physpath[pos] = 0;
- }
- return (EZFS_NOSPC);
- }
- return (0);
-}
-
-static int
-vdev_get_physpaths(nvlist_t *nv, char *physpath, size_t phypath_size,
- size_t *rsz, boolean_t is_spare)
-{
- char *type;
- int ret;
-
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0)
- return (EZFS_INVALCONFIG);
-
- if (strcmp(type, VDEV_TYPE_DISK) == 0) {
- /*
- * An active spare device has ZPOOL_CONFIG_IS_SPARE set.
- * For a spare vdev, we only want to boot from the active
- * spare device.
- */
- if (is_spare) {
- uint64_t spare = 0;
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_SPARE,
- &spare);
- if (!spare)
- return (EZFS_INVALCONFIG);
- }
-
- if (vdev_is_online(nv)) {
- if ((ret = vdev_get_one_physpath(nv, physpath,
- phypath_size, rsz)) != 0)
- return (ret);
- }
- } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 ||
- strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
- strcmp(type, VDEV_TYPE_REPLACING) == 0 ||
- (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) {
- nvlist_t **child;
- uint_t count;
- int i, ret;
-
- if (nvlist_lookup_nvlist_array(nv,
- ZPOOL_CONFIG_CHILDREN, &child, &count) != 0)
- return (EZFS_INVALCONFIG);
-
- for (i = 0; i < count; i++) {
- ret = vdev_get_physpaths(child[i], physpath,
- phypath_size, rsz, is_spare);
- if (ret == EZFS_NOSPC)
- return (ret);
- }
- }
-
- return (EZFS_POOL_INVALARG);
-}
-
-/*
- * Get phys_path for a root pool config.
- * Return 0 on success; non-zero on failure.
- */
-static int
-zpool_get_config_physpath(nvlist_t *config, char *physpath, size_t phypath_size)
-{
- size_t rsz;
- nvlist_t *vdev_root;
- nvlist_t **child;
- uint_t count;
- char *type;
-
- rsz = 0;
-
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &vdev_root) != 0)
- return (EZFS_INVALCONFIG);
-
- if (nvlist_lookup_string(vdev_root, ZPOOL_CONFIG_TYPE, &type) != 0 ||
- nvlist_lookup_nvlist_array(vdev_root, ZPOOL_CONFIG_CHILDREN,
- &child, &count) != 0)
- return (EZFS_INVALCONFIG);
-
- /*
- * root pool can only have a single top-level vdev.
- */
- if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1)
- return (EZFS_POOL_INVALARG);
-
- (void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz,
- B_FALSE);
-
- /* No online devices */
- if (rsz == 0)
- return (EZFS_NODEVICE);
-
- return (0);
-}
-
-/*
- * Get phys_path for a root pool
- * Return 0 on success; non-zero on failure.
- */
-int
-zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t phypath_size)
-{
- return (zpool_get_config_physpath(zhp->zpool_config, physpath,
- phypath_size));
-}
-
-/*
- * If the device has being dynamically expanded then we need to relabel
- * the disk to use the new unallocated space.
- */
-static int
-zpool_relabel_disk(libzfs_handle_t *hdl, const char *name)
-{
-#ifdef illumos
- char path[MAXPATHLEN];
- char errbuf[1024];
- int fd, error;
- int (*_efi_use_whole_disk)(int);
-
- if ((_efi_use_whole_disk = (int (*)(int))dlsym(RTLD_DEFAULT,
- "efi_use_whole_disk")) == NULL)
- return (-1);
-
- (void) snprintf(path, sizeof (path), "%s/%s", ZFS_RDISK_ROOT, name);
-
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to open device"), name);
- return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
- }
-
- /*
- * It's possible that we might encounter an error if the device
- * does not have any unallocated space left. If so, we simply
- * ignore that error and continue on.
- */
- error = _efi_use_whole_disk(fd);
- (void) close(fd);
- if (error && error != VT_ENOSPC) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to read disk capacity"), name);
- return (zfs_error(hdl, EZFS_NOCAP, errbuf));
- }
-#endif /* illumos */
- return (0);
-}
-
-/*
- * Bring the specified vdev online. The 'flags' parameter is a set of the
- * ZFS_ONLINE_* flags.
- */
-int
-zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
- vdev_state_t *newstate)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- char *pathname;
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- if (flags & ZFS_ONLINE_EXPAND) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot expand %s"), path);
- } else {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot online %s"), path);
- }
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- if ((flags & ZFS_ONLINE_EXPAND ||
- zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) &&
- nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &pathname) == 0) {
- uint64_t wholedisk = 0;
-
- (void) nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk);
-
- /*
- * XXX - L2ARC 1.0 devices can't support expansion.
- */
- if (l2cache) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot expand cache devices"));
- return (zfs_error(hdl, EZFS_VDEVNOTSUP, msg));
- }
-
- if (wholedisk) {
- pathname += strlen(ZFS_DISK_ROOT) + 1;
- (void) zpool_relabel_disk(hdl, pathname);
- }
- }
-
- zc.zc_cookie = VDEV_STATE_ONLINE;
- zc.zc_obj = flags;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0) {
- if (errno == EINVAL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "was split "
- "from this pool into a new one. Use '%s' "
- "instead"), "zpool detach");
- return (zfs_error(hdl, EZFS_POSTSPLIT_ONLINE, msg));
- }
- return (zpool_standard_error(hdl, errno, msg));
- }
-
- *newstate = zc.zc_cookie;
- return (0);
-}
-
-/*
- * Take the specified vdev offline
- */
-int
-zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot offline %s"), path);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- NULL)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- zc.zc_cookie = VDEV_STATE_OFFLINE;
- zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- switch (errno) {
- case EBUSY:
-
- /*
- * There are no other replicas of this device.
- */
- return (zfs_error(hdl, EZFS_NOREPLICAS, msg));
-
- case EEXIST:
- /*
- * The log device has unplayed logs
- */
- return (zfs_error(hdl, EZFS_UNPLAYED_LOGS, msg));
-
- default:
- return (zpool_standard_error(hdl, errno, msg));
- }
-}
-
-/*
- * Mark the given vdev faulted.
- */
-int
-zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot fault %llu"), guid);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_guid = guid;
- zc.zc_cookie = VDEV_STATE_FAULTED;
- zc.zc_obj = aux;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- switch (errno) {
- case EBUSY:
-
- /*
- * There are no other replicas of this device.
- */
- return (zfs_error(hdl, EZFS_NOREPLICAS, msg));
-
- default:
- return (zpool_standard_error(hdl, errno, msg));
- }
-
-}
-
-/*
- * Mark the given vdev degraded.
- */
-int
-zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot degrade %llu"), guid);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_guid = guid;
- zc.zc_cookie = VDEV_STATE_DEGRADED;
- zc.zc_obj = aux;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Returns TRUE if the given nvlist is a vdev that was originally swapped in as
- * a hot spare.
- */
-static boolean_t
-is_replacing_spare(nvlist_t *search, nvlist_t *tgt, int which)
-{
- nvlist_t **child;
- uint_t c, children;
- char *type;
-
- if (nvlist_lookup_nvlist_array(search, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0) {
- verify(nvlist_lookup_string(search, ZPOOL_CONFIG_TYPE,
- &type) == 0);
-
- if (strcmp(type, VDEV_TYPE_SPARE) == 0 &&
- children == 2 && child[which] == tgt)
- return (B_TRUE);
-
- for (c = 0; c < children; c++)
- if (is_replacing_spare(child[c], tgt, which))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-/*
- * Attach new_disk (fully described by nvroot) to old_disk.
- * If 'replacing' is specified, the new disk will replace the old one.
- */
-int
-zpool_vdev_attach(zpool_handle_t *zhp,
- const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- int ret;
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- uint64_t val;
- char *newname;
- nvlist_t **child;
- uint_t children;
- nvlist_t *config_root;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- boolean_t rootpool = zpool_is_bootable(zhp);
-
- if (replacing)
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot replace %s with %s"), old_disk, new_disk);
- else
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot attach %s to %s"), new_disk, old_disk);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- if (l2cache)
- return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- zc.zc_cookie = replacing;
-
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0 || children != 1) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "new device must be a single disk"));
- return (zfs_error(hdl, EZFS_INVALCONFIG, msg));
- }
-
- verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
- ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0);
-
- if ((newname = zpool_vdev_name(NULL, NULL, child[0], 0)) == NULL)
- return (-1);
-
- /*
- * If the target is a hot spare that has been swapped in, we can only
- * replace it with another hot spare.
- */
- if (replacing &&
- nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 &&
- (zpool_find_vdev(zhp, newname, &avail_spare, &l2cache,
- NULL) == NULL || !avail_spare) &&
- is_replacing_spare(config_root, tgt, 1)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "can only be replaced by another hot spare"));
- free(newname);
- return (zfs_error(hdl, EZFS_BADTARGET, msg));
- }
-
- free(newname);
-
- if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
- return (-1);
-
- ret = zfs_ioctl(hdl, ZFS_IOC_VDEV_ATTACH, &zc);
-
- zcmd_free_nvlists(&zc);
-
- if (ret == 0) {
- if (rootpool) {
- /*
- * XXX need a better way to prevent user from
- * booting up a half-baked vdev.
- */
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Make "
- "sure to wait until resilver is done "
- "before rebooting.\n"));
- (void) fprintf(stderr, "\n");
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "If "
- "you boot from pool '%s', you may need to update\n"
- "boot code on newly attached disk '%s'.\n\n"
- "Assuming you use GPT partitioning and 'da0' is "
- "your new boot disk\n"
- "you may use the following command:\n\n"
- "\tgpart bootcode -b /boot/pmbr -p "
- "/boot/gptzfsboot -i 1 da0\n\n"),
- zhp->zpool_name, new_disk);
- }
- return (0);
- }
-
- switch (errno) {
- case ENOTSUP:
- /*
- * Can't attach to or replace this type of vdev.
- */
- if (replacing) {
- uint64_t version = zpool_get_prop_int(zhp,
- ZPOOL_PROP_VERSION, NULL);
-
- if (islog)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot replace a log with a spare"));
- else if (version >= SPA_VERSION_MULTI_REPLACE)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "already in replacing/spare config; wait "
- "for completion or use 'zpool detach'"));
- else
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot replace a replacing device"));
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "can only attach to mirrors and top-level "
- "disks"));
- }
- (void) zfs_error(hdl, EZFS_BADTARGET, msg);
- break;
-
- case EINVAL:
- /*
- * The new device must be a single disk.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "new device must be a single disk"));
- (void) zfs_error(hdl, EZFS_INVALCONFIG, msg);
- break;
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy, "
- "or device removal is in progress"),
- new_disk);
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EOVERFLOW:
- /*
- * The new device is too small.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "device is too small"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EDOM:
- /*
- * The new device has a different alignment requirement.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "devices have different sector alignment"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case ENAMETOOLONG:
- /*
- * The resulting top-level vdev spec won't fit in the label.
- */
- (void) zfs_error(hdl, EZFS_DEVOVERFLOW, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- return (-1);
-}
-
-/*
- * Detach the specified device.
- */
-int
-zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot detach %s"), path);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- NULL)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- if (l2cache)
- return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_DETACH, &zc) == 0)
- return (0);
-
- switch (errno) {
-
- case ENOTSUP:
- /*
- * Can't detach from this type of vdev.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only "
- "applicable to mirror and replacing vdevs"));
- (void) zfs_error(hdl, EZFS_BADTARGET, msg);
- break;
-
- case EBUSY:
- /*
- * There are no other replicas of this device.
- */
- (void) zfs_error(hdl, EZFS_NOREPLICAS, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- return (-1);
-}
-
-/*
- * Find a mirror vdev in the source nvlist.
- *
- * The mchild array contains a list of disks in one of the top-level mirrors
- * of the source pool. The schild array contains a list of disks that the
- * user specified on the command line. We loop over the mchild array to
- * see if any entry in the schild array matches.
- *
- * If a disk in the mchild array is found in the schild array, we return
- * the index of that entry. Otherwise we return -1.
- */
-static int
-find_vdev_entry(zpool_handle_t *zhp, nvlist_t **mchild, uint_t mchildren,
- nvlist_t **schild, uint_t schildren)
-{
- uint_t mc;
-
- for (mc = 0; mc < mchildren; mc++) {
- uint_t sc;
- char *mpath = zpool_vdev_name(zhp->zpool_hdl, zhp,
- mchild[mc], 0);
-
- for (sc = 0; sc < schildren; sc++) {
- char *spath = zpool_vdev_name(zhp->zpool_hdl, zhp,
- schild[sc], 0);
- boolean_t result = (strcmp(mpath, spath) == 0);
-
- free(spath);
- if (result) {
- free(mpath);
- return (mc);
- }
- }
-
- free(mpath);
- }
-
- return (-1);
-}
-
-/*
- * Split a mirror pool. If newroot points to null, then a new nvlist
- * is generated and it is the responsibility of the caller to free it.
- */
-int
-zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
- nvlist_t *props, splitflags_t flags)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tree, *config, **child, **newchild, *newconfig = NULL;
- nvlist_t **varray = NULL, *zc_props = NULL;
- uint_t c, children, newchildren, lastlog = 0, vcount, found = 0;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- uint64_t vers;
- boolean_t freelist = B_FALSE, memory_err = B_TRUE;
- int retval = 0;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "Unable to split %s"), zhp->zpool_name);
-
- if (!zpool_name_valid(hdl, B_FALSE, newname))
- return (zfs_error(hdl, EZFS_INVALIDNAME, msg));
-
- if ((config = zpool_get_config(zhp, NULL)) == NULL) {
- (void) fprintf(stderr, gettext("Internal error: unable to "
- "retrieve pool configuration\n"));
- return (-1);
- }
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree)
- == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &vers) == 0);
-
- if (props) {
- prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
- if ((zc_props = zpool_valid_proplist(hdl, zhp->zpool_name,
- props, vers, flags, msg)) == NULL)
- return (-1);
- }
-
- if (nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN, &child,
- &children) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Source pool is missing vdev tree"));
- nvlist_free(zc_props);
- return (-1);
- }
-
- varray = zfs_alloc(hdl, children * sizeof (nvlist_t *));
- vcount = 0;
-
- if (*newroot == NULL ||
- nvlist_lookup_nvlist_array(*newroot, ZPOOL_CONFIG_CHILDREN,
- &newchild, &newchildren) != 0)
- newchildren = 0;
-
- for (c = 0; c < children; c++) {
- uint64_t is_log = B_FALSE, is_hole = B_FALSE;
- char *type;
- nvlist_t **mchild, *vdev;
- uint_t mchildren;
- int entry;
-
- /*
- * Unlike cache & spares, slogs are stored in the
- * ZPOOL_CONFIG_CHILDREN array. We filter them out here.
- */
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &is_log);
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE,
- &is_hole);
- if (is_log || is_hole) {
- /*
- * Create a hole vdev and put it in the config.
- */
- if (nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) != 0)
- goto out;
- if (nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_HOLE) != 0)
- goto out;
- if (nvlist_add_uint64(vdev, ZPOOL_CONFIG_IS_HOLE,
- 1) != 0)
- goto out;
- if (lastlog == 0)
- lastlog = vcount;
- varray[vcount++] = vdev;
- continue;
- }
- lastlog = 0;
- verify(nvlist_lookup_string(child[c], ZPOOL_CONFIG_TYPE, &type)
- == 0);
- if (strcmp(type, VDEV_TYPE_MIRROR) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Source pool must be composed only of mirrors\n"));
- retval = zfs_error(hdl, EZFS_INVALCONFIG, msg);
- goto out;
- }
-
- verify(nvlist_lookup_nvlist_array(child[c],
- ZPOOL_CONFIG_CHILDREN, &mchild, &mchildren) == 0);
-
- /* find or add an entry for this top-level vdev */
- if (newchildren > 0 &&
- (entry = find_vdev_entry(zhp, mchild, mchildren,
- newchild, newchildren)) >= 0) {
- /* We found a disk that the user specified. */
- vdev = mchild[entry];
- ++found;
- } else {
- /* User didn't specify a disk for this vdev. */
- vdev = mchild[mchildren - 1];
- }
-
- if (nvlist_dup(vdev, &varray[vcount++], 0) != 0)
- goto out;
- }
-
- /* did we find every disk the user specified? */
- if (found != newchildren) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Device list must "
- "include at most one disk from each mirror"));
- retval = zfs_error(hdl, EZFS_INVALCONFIG, msg);
- goto out;
- }
-
- /* Prepare the nvlist for populating. */
- if (*newroot == NULL) {
- if (nvlist_alloc(newroot, NV_UNIQUE_NAME, 0) != 0)
- goto out;
- freelist = B_TRUE;
- if (nvlist_add_string(*newroot, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_ROOT) != 0)
- goto out;
- } else {
- verify(nvlist_remove_all(*newroot, ZPOOL_CONFIG_CHILDREN) == 0);
- }
-
- /* Add all the children we found */
- if (nvlist_add_nvlist_array(*newroot, ZPOOL_CONFIG_CHILDREN, varray,
- lastlog == 0 ? vcount : lastlog) != 0)
- goto out;
-
- /*
- * If we're just doing a dry run, exit now with success.
- */
- if (flags.dryrun) {
- memory_err = B_FALSE;
- freelist = B_FALSE;
- goto out;
- }
-
- /* now build up the config list & call the ioctl */
- if (nvlist_alloc(&newconfig, NV_UNIQUE_NAME, 0) != 0)
- goto out;
-
- if (nvlist_add_nvlist(newconfig,
- ZPOOL_CONFIG_VDEV_TREE, *newroot) != 0 ||
- nvlist_add_string(newconfig,
- ZPOOL_CONFIG_POOL_NAME, newname) != 0 ||
- nvlist_add_uint64(newconfig, ZPOOL_CONFIG_VERSION, vers) != 0)
- goto out;
-
- /*
- * The new pool is automatically part of the namespace unless we
- * explicitly export it.
- */
- if (!flags.import)
- zc.zc_cookie = ZPOOL_EXPORT_AFTER_SPLIT;
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_string, newname, sizeof (zc.zc_string));
- if (zcmd_write_conf_nvlist(hdl, &zc, newconfig) != 0)
- goto out;
- if (zc_props != NULL && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
- goto out;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SPLIT, &zc) != 0) {
- retval = zpool_standard_error(hdl, errno, msg);
- goto out;
- }
-
- freelist = B_FALSE;
- memory_err = B_FALSE;
-
-out:
- if (varray != NULL) {
- int v;
-
- for (v = 0; v < vcount; v++)
- nvlist_free(varray[v]);
- free(varray);
- }
- zcmd_free_nvlists(&zc);
- nvlist_free(zc_props);
- nvlist_free(newconfig);
- if (freelist) {
- nvlist_free(*newroot);
- *newroot = NULL;
- }
-
- if (retval != 0)
- return (retval);
-
- if (memory_err)
- return (no_memory(hdl));
-
- return (0);
-}
-
-/*
- * Remove the given device.
- */
-int
-zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- uint64_t version;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot remove %s"), path);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
- if (islog && version < SPA_VERSION_HOLES) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to support log removal"));
- return (zfs_error(hdl, EZFS_BADVERSION, msg));
- }
-
- zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID);
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
- return (0);
-
- switch (errno) {
-
- case EINVAL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid config; all top-level vdevs must "
- "have the same sector size and not be raidz."));
- (void) zfs_error(hdl, EZFS_INVALCONFIG, msg);
- break;
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Pool busy; removal may already be in progress"));
- (void) zfs_error(hdl, EZFS_BUSY, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
- return (-1);
-}
-
-int
-zpool_vdev_remove_cancel(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot cancel removal"));
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_cookie = 1;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-int
-zpool_vdev_indirect_size(zpool_handle_t *zhp, const char *path,
- uint64_t *sizep)
-{
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot determine indirect size of %s"),
- path);
-
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- if (avail_spare || l2cache || islog) {
- *sizep = 0;
- return (0);
- }
-
- if (nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_INDIRECT_SIZE, sizep) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "indirect size not available"));
- return (zfs_error(hdl, EINVAL, msg));
- }
- return (0);
-}
-
-/*
- * Clear the errors for the pool, or the particular device if specified.
- */
-int
-zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- zpool_load_policy_t policy;
- boolean_t avail_spare, l2cache;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- nvlist_t *nvi = NULL;
- int error;
-
- if (path)
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot clear errors for %s"),
- path);
- else
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot clear errors for %s"),
- zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (path) {
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare,
- &l2cache, NULL)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- /*
- * Don't allow error clearing for hot spares. Do allow
- * error clearing for l2cache devices.
- */
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
- &zc.zc_guid) == 0);
- }
-
- zpool_get_load_policy(rewindnvl, &policy);
- zc.zc_cookie = policy.zlp_rewind;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size * 2) != 0)
- return (-1);
-
- if (zcmd_write_src_nvlist(hdl, &zc, rewindnvl) != 0)
- return (-1);
-
- while ((error = zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc)) != 0 &&
- errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- if (!error || ((policy.zlp_rewind & ZPOOL_TRY_REWIND) &&
- errno != EPERM && errno != EACCES)) {
- if (policy.zlp_rewind &
- (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nvi);
- zpool_rewind_exclaim(hdl, zc.zc_name,
- ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0),
- nvi);
- nvlist_free(nvi);
- }
- zcmd_free_nvlists(&zc);
- return (0);
- }
-
- zcmd_free_nvlists(&zc);
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Similar to zpool_clear(), but takes a GUID (used by fmd).
- */
-int
-zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot clear errors for %llx"),
- guid);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_guid = guid;
- zc.zc_cookie = ZPOOL_NO_REWIND;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_CLEAR, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Change the GUID for a pool.
- */
-int
-zpool_reguid(zpool_handle_t *zhp)
-{
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- zfs_cmd_t zc = { 0 };
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Reopen the pool.
- */
-int
-zpool_reopen(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
- zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_REOPEN, &zc) == 0)
- return (0);
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/* call into libzfs_core to execute the sync IOCTL per pool */
-int
-zpool_sync_one(zpool_handle_t *zhp, void *data)
-{
- int ret;
- libzfs_handle_t *hdl = zpool_get_handle(zhp);
- const char *pool_name = zpool_get_name(zhp);
- boolean_t *force = data;
- nvlist_t *innvl = fnvlist_alloc();
-
- fnvlist_add_boolean_value(innvl, "force", *force);
- if ((ret = lzc_sync(pool_name, innvl, NULL)) != 0) {
- nvlist_free(innvl);
- return (zpool_standard_error_fmt(hdl, ret,
- dgettext(TEXT_DOMAIN, "sync '%s' failed"), pool_name));
- }
- nvlist_free(innvl);
-
- return (0);
-}
-
-/*
- * Convert from a devid string to a path.
- */
-static char *
-devid_to_path(char *devid_str)
-{
- ddi_devid_t devid;
- char *minor;
- char *path;
- devid_nmlist_t *list = NULL;
- int ret;
-
- if (devid_str_decode(devid_str, &devid, &minor) != 0)
- return (NULL);
-
- ret = devid_deviceid_to_nmlist("/dev", devid, minor, &list);
-
- devid_str_free(minor);
- devid_free(devid);
-
- if (ret != 0)
- return (NULL);
-
- /*
- * In a case the strdup() fails, we will just return NULL below.
- */
- path = strdup(list[0].devname);
-
- devid_free_nmlist(list);
-
- return (path);
-}
-
-/*
- * Convert from a path to a devid string.
- */
-static char *
-path_to_devid(const char *path)
-{
-#ifdef have_devid
- int fd;
- ddi_devid_t devid;
- char *minor, *ret;
-
- if ((fd = open(path, O_RDONLY)) < 0)
- return (NULL);
-
- minor = NULL;
- ret = NULL;
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0)
- ret = devid_str_encode(devid, minor);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
- (void) close(fd);
-
- return (ret);
-#else
- return (NULL);
-#endif
-}
-
-/*
- * Issue the necessary ioctl() to update the stored path value for the vdev. We
- * ignore any failure here, since a common case is for an unprivileged user to
- * type 'zpool status', and we'll display the correct information anyway.
- */
-static void
-set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
-{
- zfs_cmd_t zc = { 0 };
-
- (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- (void) strncpy(zc.zc_value, path, sizeof (zc.zc_value));
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
- &zc.zc_guid) == 0);
-
- (void) ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SETPATH, &zc);
-}
-
-/*
- * Given a vdev, return the name to display in iostat. If the vdev has a path,
- * we use that, stripping off any leading "/dev/dsk/"; if not, we use the type.
- * We also check if this is a whole disk, in which case we strip off the
- * trailing 's0' slice name.
- *
- * This routine is also responsible for identifying when disks have been
- * reconfigured in a new location. The kernel will have opened the device by
- * devid, but the path will still refer to the old location. To catch this, we
- * first do a path -> devid translation (which is fast for the common case). If
- * the devid matches, we're done. If not, we do a reverse devid -> path
- * translation and issue the appropriate ioctl() to update the path of the vdev.
- * If 'zhp' is NULL, then this is an exported pool, and we don't need to do any
- * of these checks.
- */
-char *
-zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
- int name_flags)
-{
- char *path, *devid, *env;
- uint64_t value;
- char buf[64];
- vdev_stat_t *vs;
- uint_t vsc;
- int have_stats;
- int have_path;
-
- env = getenv("ZPOOL_VDEV_NAME_PATH");
- if (env && (strtoul(env, NULL, 0) > 0 ||
- !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
- name_flags |= VDEV_NAME_PATH;
-
- env = getenv("ZPOOL_VDEV_NAME_GUID");
- if (env && (strtoul(env, NULL, 0) > 0 ||
- !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
- name_flags |= VDEV_NAME_GUID;
-
- env = getenv("ZPOOL_VDEV_NAME_FOLLOW_LINKS");
- if (env && (strtoul(env, NULL, 0) > 0 ||
- !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
- name_flags |= VDEV_NAME_FOLLOW_LINKS;
-
- have_stats = nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &vsc) == 0;
- have_path = nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0;
-
- /*
- * If the device is not currently present, assume it will not
- * come back at the same device path. Display the device by GUID.
- */
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &value) == 0 ||
- (name_flags & VDEV_NAME_GUID) != 0 ||
- have_path && have_stats && vs->vs_state <= VDEV_STATE_CANT_OPEN) {
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value);
- (void) snprintf(buf, sizeof (buf), "%llu", (u_longlong_t)value);
- path = buf;
- } else if (have_path) {
-
- /*
- * If the device is dead (faulted, offline, etc) then don't
- * bother opening it. Otherwise we may be forcing the user to
- * open a misbehaving device, which can have undesirable
- * effects.
- */
- if ((have_stats == 0 ||
- vs->vs_state >= VDEV_STATE_DEGRADED) &&
- zhp != NULL &&
- nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &devid) == 0) {
- /*
- * Determine if the current path is correct.
- */
- char *newdevid = path_to_devid(path);
-
- if (newdevid == NULL ||
- strcmp(devid, newdevid) != 0) {
- char *newpath;
-
- if ((newpath = devid_to_path(devid)) != NULL) {
- /*
- * Update the path appropriately.
- */
- set_path(zhp, nv, newpath);
- if (nvlist_add_string(nv,
- ZPOOL_CONFIG_PATH, newpath) == 0)
- verify(nvlist_lookup_string(nv,
- ZPOOL_CONFIG_PATH,
- &path) == 0);
- free(newpath);
- }
- }
-
- if (newdevid)
- devid_str_free(newdevid);
- }
-
-#ifdef illumos
- if (name_flags & VDEV_NAME_FOLLOW_LINKS) {
- char *rp = realpath(path, NULL);
- if (rp) {
- strlcpy(buf, rp, sizeof (buf));
- path = buf;
- free(rp);
- }
- }
-
- if (strncmp(path, ZFS_DISK_ROOTD, strlen(ZFS_DISK_ROOTD)) == 0)
- path += strlen(ZFS_DISK_ROOTD);
-
- /*
- * Remove the partition from the path it this is a whole disk.
- */
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value)
- == 0 && value && !(name_flags & VDEV_NAME_PATH)) {
- int pathlen = strlen(path);
- char *tmp = zfs_strdup(hdl, path);
-
- /*
- * If it starts with c#, and ends with "s0" or "s1",
- * chop the slice off, or if it ends with "s0/old" or
- * "s1/old", remove the slice from the middle.
- */
- if (CTD_CHECK(tmp)) {
- if (strcmp(&tmp[pathlen - 2], "s0") == 0 ||
- strcmp(&tmp[pathlen - 2], "s1") == 0) {
- tmp[pathlen - 2] = '\0';
- } else if (pathlen > 6 &&
- (strcmp(&tmp[pathlen - 6], "s0/old") == 0 ||
- strcmp(&tmp[pathlen - 6], "s1/old") == 0)) {
- (void) strcpy(&tmp[pathlen - 6],
- "/old");
- }
- }
- return (tmp);
- }
-#else /* !illumos */
- if (strncmp(path, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
- path += sizeof(_PATH_DEV) - 1;
-#endif /* illumos */
- } else {
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
-
- /*
- * If it's a raidz device, we need to stick in the parity level.
- */
- if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
- &value) == 0);
- (void) snprintf(buf, sizeof (buf), "%s%llu", path,
- (u_longlong_t)value);
- path = buf;
- }
-
- /*
- * We identify each top-level vdev by using a <type-id>
- * naming convention.
- */
- if (name_flags & VDEV_NAME_TYPE_ID) {
- uint64_t id;
-
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
- &id) == 0);
- (void) snprintf(buf, sizeof (buf), "%s-%llu", path,
- (u_longlong_t)id);
- path = buf;
- }
- }
-
- return (zfs_strdup(hdl, path));
-}
-
-static int
-zbookmark_mem_compare(const void *a, const void *b)
-{
- return (memcmp(a, b, sizeof (zbookmark_phys_t)));
-}
-
-/*
- * Retrieve the persistent error log, uniquify the members, and return to the
- * caller.
- */
-int
-zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
-{
- zfs_cmd_t zc = { 0 };
- uint64_t count;
- zbookmark_phys_t *zb = NULL;
- int i;
-
- /*
- * Retrieve the raw error list from the kernel. If the number of errors
- * has increased, allocate more space and continue until we get the
- * entire list.
- */
- verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_ERRCOUNT,
- &count) == 0);
- if (count == 0)
- return (0);
- if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
- count * sizeof (zbookmark_phys_t))) == (uintptr_t)NULL)
- return (-1);
- zc.zc_nvlist_dst_size = count;
- (void) strcpy(zc.zc_name, zhp->zpool_name);
- for (;;) {
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_ERROR_LOG,
- &zc) != 0) {
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- if (errno == ENOMEM) {
- void *dst;
-
- count = zc.zc_nvlist_dst_size;
- dst = zfs_alloc(zhp->zpool_hdl, count *
- sizeof (zbookmark_phys_t));
- if (dst == NULL)
- return (-1);
- zc.zc_nvlist_dst = (uintptr_t)dst;
- } else {
- return (-1);
- }
- } else {
- break;
- }
- }
-
- /*
- * Sort the resulting bookmarks. This is a little confusing due to the
- * implementation of ZFS_IOC_ERROR_LOG. The bookmarks are copied last
- * to first, and 'zc_nvlist_dst_size' indicates the number of boomarks
- * _not_ copied as part of the process. So we point the start of our
- * array appropriate and decrement the total number of elements.
- */
- zb = ((zbookmark_phys_t *)(uintptr_t)zc.zc_nvlist_dst) +
- zc.zc_nvlist_dst_size;
- count -= zc.zc_nvlist_dst_size;
-
- qsort(zb, count, sizeof (zbookmark_phys_t), zbookmark_mem_compare);
-
- verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0);
-
- /*
- * Fill in the nverrlistp with nvlist's of dataset and object numbers.
- */
- for (i = 0; i < count; i++) {
- nvlist_t *nv;
-
- /* ignoring zb_blkid and zb_level for now */
- if (i > 0 && zb[i-1].zb_objset == zb[i].zb_objset &&
- zb[i-1].zb_object == zb[i].zb_object)
- continue;
-
- if (nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) != 0)
- goto nomem;
- if (nvlist_add_uint64(nv, ZPOOL_ERR_DATASET,
- zb[i].zb_objset) != 0) {
- nvlist_free(nv);
- goto nomem;
- }
- if (nvlist_add_uint64(nv, ZPOOL_ERR_OBJECT,
- zb[i].zb_object) != 0) {
- nvlist_free(nv);
- goto nomem;
- }
- if (nvlist_add_nvlist(*nverrlistp, "ejk", nv) != 0) {
- nvlist_free(nv);
- goto nomem;
- }
- nvlist_free(nv);
- }
-
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- return (0);
-
-nomem:
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- return (no_memory(zhp->zpool_hdl));
-}
-
-/*
- * Upgrade a ZFS pool to the latest on-disk version.
- */
-int
-zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strcpy(zc.zc_name, zhp->zpool_name);
- zc.zc_cookie = new_version;
-
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_UPGRADE, &zc) != 0)
- return (zpool_standard_error_fmt(hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot upgrade '%s'"),
- zhp->zpool_name));
- return (0);
-}
-
-void
-zfs_save_arguments(int argc, char **argv, char *string, int len)
-{
- (void) strlcpy(string, basename(argv[0]), len);
- for (int i = 1; i < argc; i++) {
- (void) strlcat(string, " ", len);
- (void) strlcat(string, argv[i], len);
- }
-}
-
-int
-zpool_log_history(libzfs_handle_t *hdl, const char *message)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *args;
- int err;
-
- args = fnvlist_alloc();
- fnvlist_add_string(args, "message", message);
- err = zcmd_write_src_nvlist(hdl, &zc, args);
- if (err == 0)
- err = ioctl(hdl->libzfs_fd, ZFS_IOC_LOG_HISTORY, &zc);
- nvlist_free(args);
- zcmd_free_nvlists(&zc);
- return (err);
-}
-
-/*
- * Perform ioctl to get some command history of a pool.
- *
- * 'buf' is the buffer to fill up to 'len' bytes. 'off' is the
- * logical offset of the history buffer to start reading from.
- *
- * Upon return, 'off' is the next logical offset to read from and
- * 'len' is the actual amount of bytes read into 'buf'.
- */
-static int
-get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- zc.zc_history = (uint64_t)(uintptr_t)buf;
- zc.zc_history_len = *len;
- zc.zc_history_offset = *off;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_GET_HISTORY, &zc) != 0) {
- switch (errno) {
- case EPERM:
- return (zfs_error_fmt(hdl, EZFS_PERM,
- dgettext(TEXT_DOMAIN,
- "cannot show history for pool '%s'"),
- zhp->zpool_name));
- case ENOENT:
- return (zfs_error_fmt(hdl, EZFS_NOHISTORY,
- dgettext(TEXT_DOMAIN, "cannot get history for pool "
- "'%s'"), zhp->zpool_name));
- case ENOTSUP:
- return (zfs_error_fmt(hdl, EZFS_BADVERSION,
- dgettext(TEXT_DOMAIN, "cannot get history for pool "
- "'%s', pool must be upgraded"), zhp->zpool_name));
- default:
- return (zpool_standard_error_fmt(hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot get history for '%s'"), zhp->zpool_name));
- }
- }
-
- *len = zc.zc_history_len;
- *off = zc.zc_history_offset;
-
- return (0);
-}
-
-/*
- * Process the buffer of nvlists, unpacking and storing each nvlist record
- * into 'records'. 'leftover' is set to the number of bytes that weren't
- * processed as there wasn't a complete record.
- */
-int
-zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
- nvlist_t ***records, uint_t *numrecords)
-{
- uint64_t reclen;
- nvlist_t *nv;
- int i;
-
- while (bytes_read > sizeof (reclen)) {
-
- /* get length of packed record (stored as little endian) */
- for (i = 0, reclen = 0; i < sizeof (reclen); i++)
- reclen += (uint64_t)(((uchar_t *)buf)[i]) << (8*i);
-
- if (bytes_read < sizeof (reclen) + reclen)
- break;
-
- /* unpack record */
- if (nvlist_unpack(buf + sizeof (reclen), reclen, &nv, 0) != 0)
- return (ENOMEM);
- bytes_read -= sizeof (reclen) + reclen;
- buf += sizeof (reclen) + reclen;
-
- /* add record to nvlist array */
- (*numrecords)++;
- if (ISP2(*numrecords + 1)) {
- *records = realloc(*records,
- *numrecords * 2 * sizeof (nvlist_t *));
- }
- (*records)[*numrecords - 1] = nv;
- }
-
- *leftover = bytes_read;
- return (0);
-}
-
-/* from spa_history.c: spa_history_create_obj() */
-#define HIS_BUF_LEN_DEF (128 << 10)
-#define HIS_BUF_LEN_MAX (1 << 30)
-
-/*
- * Retrieve the command history of a pool.
- */
-int
-zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp, uint64_t *off,
- boolean_t *eof)
-{
- char *buf;
- uint64_t buflen = HIS_BUF_LEN_DEF;
- nvlist_t **records = NULL;
- uint_t numrecords = 0;
- int err, i;
- uint64_t start = *off;
-
- buf = malloc(buflen);
- if (buf == NULL)
- return (ENOMEM);
- /* process about 1MB at a time */
- while (*off - start < 1024 * 1024) {
- uint64_t bytes_read = buflen;
- uint64_t leftover;
-
- if ((err = get_history(zhp, buf, off, &bytes_read)) != 0)
- break;
-
- /* if nothing else was read in, we're at EOF, just return */
- if (bytes_read == 0) {
- *eof = B_TRUE;
- break;
- }
-
- if ((err = zpool_history_unpack(buf, bytes_read,
- &leftover, &records, &numrecords)) != 0)
- break;
- *off -= leftover;
- if (leftover == bytes_read) {
- /*
- * no progress made, because buffer is not big enough
- * to hold this record; resize and retry.
- */
- buflen *= 2;
- free(buf);
- buf = NULL;
- if ((buflen >= HIS_BUF_LEN_MAX) ||
- ((buf = malloc(buflen)) == NULL)) {
- err = ENOMEM;
- break;
- }
- }
- }
-
- free(buf);
-
- if (!err) {
- verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
- verify(nvlist_add_nvlist_array(*nvhisp, ZPOOL_HIST_RECORD,
- records, numrecords) == 0);
- }
- for (i = 0; i < numrecords; i++)
- nvlist_free(records[i]);
- free(records);
-
- return (err);
-}
-
-void
-zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
- char *pathname, size_t len)
-{
- zfs_cmd_t zc = { 0 };
- boolean_t mounted = B_FALSE;
- char *mntpnt = NULL;
- char dsname[ZFS_MAX_DATASET_NAME_LEN];
-
- if (dsobj == 0) {
- /* special case for the MOS */
- (void) snprintf(pathname, len, "<metadata>:<0x%llx>", obj);
- return;
- }
-
- /* get the dataset's name */
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_obj = dsobj;
- if (ioctl(zhp->zpool_hdl->libzfs_fd,
- ZFS_IOC_DSOBJ_TO_DSNAME, &zc) != 0) {
- /* just write out a path of two object numbers */
- (void) snprintf(pathname, len, "<0x%llx>:<0x%llx>",
- dsobj, obj);
- return;
- }
- (void) strlcpy(dsname, zc.zc_value, sizeof (dsname));
-
- /* find out if the dataset is mounted */
- mounted = is_mounted(zhp->zpool_hdl, dsname, &mntpnt);
-
- /* get the corrupted object's path */
- (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
- zc.zc_obj = obj;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_PATH,
- &zc) == 0) {
- if (mounted) {
- (void) snprintf(pathname, len, "%s%s", mntpnt,
- zc.zc_value);
- } else {
- (void) snprintf(pathname, len, "%s:%s",
- dsname, zc.zc_value);
- }
- } else {
- (void) snprintf(pathname, len, "%s:<0x%llx>", dsname, obj);
- }
- free(mntpnt);
-}
-
-int
-zpool_set_bootenv(zpool_handle_t *zhp, const char *envmap)
-{
- int error = lzc_set_bootenv(zhp->zpool_name, envmap);
- if (error != 0) {
- (void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
- dgettext(TEXT_DOMAIN,
- "error setting bootenv in pool '%s'"), zhp->zpool_name);
- }
-
- return (error);
-}
-
-int
-zpool_get_bootenv(zpool_handle_t *zhp, char *outbuf, size_t size, off_t offset)
-{
- nvlist_t *nvl;
- int error = lzc_get_bootenv(zhp->zpool_name, &nvl);;
- if (error != 0) {
- (void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
- dgettext(TEXT_DOMAIN,
- "error getting bootenv in pool '%s'"), zhp->zpool_name);
- return (-1);
- }
- char *envmap = fnvlist_lookup_string(nvl, "envmap");
- if (offset >= strlen(envmap)) {
- fnvlist_free(nvl);
- return (0);
- }
-
- strlcpy(outbuf, envmap + offset, size);
- int bytes = MIN(strlen(envmap + offset), size);
- fnvlist_free(nvl);
- return (bytes);
-}
-
-#ifdef illumos
-/*
- * Read the EFI label from the config, if a label does not exist then
- * pass back the error to the caller. If the caller has passed a non-NULL
- * diskaddr argument then we set it to the starting address of the EFI
- * partition. If the caller has passed a non-NULL boolean argument, then
- * we set it to indicate if the disk does have efi system partition.
- */
-static int
-read_efi_label(nvlist_t *config, diskaddr_t *sb, boolean_t *system)
-{
- char *path;
- int fd;
- char diskname[MAXPATHLEN];
- boolean_t boot = B_FALSE;
- int err = -1;
- int slice;
-
- if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
- return (err);
-
- (void) snprintf(diskname, sizeof (diskname), "%s%s", ZFS_RDISK_ROOT,
- strrchr(path, '/'));
- if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
- struct dk_gpt *vtoc;
-
- if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
- for (slice = 0; slice < vtoc->efi_nparts; slice++) {
- if (vtoc->efi_parts[slice].p_tag == V_SYSTEM)
- boot = B_TRUE;
- if (vtoc->efi_parts[slice].p_tag == V_USR)
- break;
- }
- if (sb != NULL && vtoc->efi_parts[slice].p_tag == V_USR)
- *sb = vtoc->efi_parts[slice].p_start;
- if (system != NULL)
- *system = boot;
- efi_free(vtoc);
- }
- (void) close(fd);
- }
- return (err);
-}
-
-/*
- * determine where a partition starts on a disk in the current
- * configuration
- */
-static diskaddr_t
-find_start_block(nvlist_t *config)
-{
- nvlist_t **child;
- uint_t c, children;
- diskaddr_t sb = MAXOFFSET_T;
- uint64_t wholedisk;
-
- if (nvlist_lookup_nvlist_array(config,
- ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) {
- if (nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk) != 0 || !wholedisk) {
- return (MAXOFFSET_T);
- }
- if (read_efi_label(config, &sb, NULL) < 0)
- sb = MAXOFFSET_T;
- return (sb);
- }
-
- for (c = 0; c < children; c++) {
- sb = find_start_block(child[c]);
- if (sb != MAXOFFSET_T) {
- return (sb);
- }
- }
- return (MAXOFFSET_T);
-}
-#endif /* illumos */
-
-/*
- * Label an individual disk. The name provided is the short name,
- * stripped of any leading /dev path.
- */
-int
-zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name,
- zpool_boot_label_t boot_type, uint64_t boot_size, int *slice)
-{
-#ifdef illumos
- char path[MAXPATHLEN];
- struct dk_gpt *vtoc;
- int fd;
- size_t resv = EFI_MIN_RESV_SIZE;
- uint64_t slice_size;
- diskaddr_t start_block;
- char errbuf[1024];
-
- /* prepare an error message just in case */
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot label '%s'"), name);
-
- if (zhp) {
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
-
- if (zhp->zpool_start_block == 0)
- start_block = find_start_block(nvroot);
- else
- start_block = zhp->zpool_start_block;
- zhp->zpool_start_block = start_block;
- } else {
- /* new pool */
- start_block = NEW_START_BLOCK;
- }
-
- (void) snprintf(path, sizeof (path), "%s/%s%s", ZFS_RDISK_ROOT, name,
- BACKUP_SLICE);
-
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
- /*
- * This shouldn't happen. We've long since verified that this
- * is a valid device.
- */
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "unable to open device"));
- return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
- }
-
- if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) {
- /*
- * The only way this can fail is if we run out of memory, or we
- * were unable to read the disk's capacity
- */
- if (errno == ENOMEM)
- (void) no_memory(hdl);
-
- (void) close(fd);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "unable to read disk capacity"), name);
-
- return (zfs_error(hdl, EZFS_NOCAP, errbuf));
- }
-
- /*
- * Why we use V_USR: V_BACKUP confuses users, and is considered
- * disposable by some EFI utilities (since EFI doesn't have a backup
- * slice). V_UNASSIGNED is supposed to be used only for zero size
- * partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT,
- * etc. were all pretty specific. V_USR is as close to reality as we
- * can get, in the absence of V_OTHER.
- */
- /* first fix the partition start block */
- if (start_block == MAXOFFSET_T)
- start_block = NEW_START_BLOCK;
-
- /*
- * EFI System partition is using slice 0.
- * ZFS is on slice 1 and slice 8 is reserved.
- * We assume the GPT partition table without system
- * partition has zfs p_start == NEW_START_BLOCK.
- * If start_block != NEW_START_BLOCK, it means we have
- * system partition. Correct solution would be to query/cache vtoc
- * from existing vdev member.
- */
- if (boot_type == ZPOOL_CREATE_BOOT_LABEL) {
- if (boot_size % vtoc->efi_lbasize != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "boot partition size must be a multiple of %d"),
- vtoc->efi_lbasize);
- (void) close(fd);
- efi_free(vtoc);
- return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
- }
- /*
- * System partition size checks.
- * Note the 1MB is quite arbitrary value, since we
- * are creating dedicated pool, it should be enough
- * to hold fat + efi bootloader. May need to be
- * adjusted if the bootloader size will grow.
- */
- if (boot_size < 1024 * 1024) {
- char buf[64];
- zfs_nicenum(boot_size, buf, sizeof (buf));
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Specified size %s for EFI System partition is too "
- "small, the minimum size is 1MB."), buf);
- (void) close(fd);
- efi_free(vtoc);
- return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
- }
- /* 33MB is tested with mkfs -F pcfs */
- if (hdl->libzfs_printerr &&
- ((vtoc->efi_lbasize == 512 &&
- boot_size < 33 * 1024 * 1024) ||
- (vtoc->efi_lbasize == 4096 &&
- boot_size < 256 * 1024 * 1024))) {
- char buf[64];
- zfs_nicenum(boot_size, buf, sizeof (buf));
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "Warning: EFI System partition size %s is "
- "not allowing to create FAT32 file\nsystem, which "
- "may result in unbootable system.\n"), buf);
- }
- /* Adjust zfs partition start by size of system partition. */
- start_block += boot_size / vtoc->efi_lbasize;
- }
-
- if (start_block == NEW_START_BLOCK) {
- /*
- * Use default layout.
- * ZFS is on slice 0 and slice 8 is reserved.
- */
- slice_size = vtoc->efi_last_u_lba + 1;
- slice_size -= EFI_MIN_RESV_SIZE;
- slice_size -= start_block;
- if (slice != NULL)
- *slice = 0;
-
- vtoc->efi_parts[0].p_start = start_block;
- vtoc->efi_parts[0].p_size = slice_size;
-
- vtoc->efi_parts[0].p_tag = V_USR;
- (void) strcpy(vtoc->efi_parts[0].p_name, "zfs");
-
- vtoc->efi_parts[8].p_start = slice_size + start_block;
- vtoc->efi_parts[8].p_size = resv;
- vtoc->efi_parts[8].p_tag = V_RESERVED;
- } else {
- slice_size = start_block - NEW_START_BLOCK;
- vtoc->efi_parts[0].p_start = NEW_START_BLOCK;
- vtoc->efi_parts[0].p_size = slice_size;
- vtoc->efi_parts[0].p_tag = V_SYSTEM;
- (void) strcpy(vtoc->efi_parts[0].p_name, "loader");
- if (slice != NULL)
- *slice = 1;
- /* prepare slice 1 */
- slice_size = vtoc->efi_last_u_lba + 1 - slice_size;
- slice_size -= resv;
- slice_size -= NEW_START_BLOCK;
- vtoc->efi_parts[1].p_start = start_block;
- vtoc->efi_parts[1].p_size = slice_size;
- vtoc->efi_parts[1].p_tag = V_USR;
- (void) strcpy(vtoc->efi_parts[1].p_name, "zfs");
-
- vtoc->efi_parts[8].p_start = slice_size + start_block;
- vtoc->efi_parts[8].p_size = resv;
- vtoc->efi_parts[8].p_tag = V_RESERVED;
- }
-
- if (efi_write(fd, vtoc) != 0) {
- /*
- * Some block drivers (like pcata) may not support EFI
- * GPT labels. Print out a helpful error message dir-
- * ecting the user to manually label the disk and give
- * a specific slice.
- */
- (void) close(fd);
- efi_free(vtoc);
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "try using fdisk(1M) and then provide a specific slice"));
- return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
- }
-
- (void) close(fd);
- efi_free(vtoc);
-#endif /* illumos */
- return (0);
-}
-
-static boolean_t
-supported_dump_vdev_type(libzfs_handle_t *hdl, nvlist_t *config, char *errbuf)
-{
- char *type;
- nvlist_t **child;
- uint_t children, c;
-
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_TYPE, &type) == 0);
- if (strcmp(type, VDEV_TYPE_FILE) == 0 ||
- strcmp(type, VDEV_TYPE_HOLE) == 0 ||
- strcmp(type, VDEV_TYPE_MISSING) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "vdev type '%s' is not supported"), type);
- (void) zfs_error(hdl, EZFS_VDEVNOTSUP, errbuf);
- return (B_FALSE);
- }
- if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if (!supported_dump_vdev_type(hdl, child[c], errbuf))
- return (B_FALSE);
- }
- }
- return (B_TRUE);
-}
-
-/*
- * Check if this zvol is allowable for use as a dump device; zero if
- * it is, > 0 if it isn't, < 0 if it isn't a zvol.
- *
- * Allowable storage configurations include mirrors, all raidz variants, and
- * pools with log, cache, and spare devices. Pools which are backed by files or
- * have missing/hole vdevs are not suitable.
- */
-int
-zvol_check_dump_config(char *arg)
-{
- zpool_handle_t *zhp = NULL;
- nvlist_t *config, *nvroot;
- char *p, *volname;
- nvlist_t **top;
- uint_t toplevels;
- libzfs_handle_t *hdl;
- char errbuf[1024];
- char poolname[ZFS_MAX_DATASET_NAME_LEN];
- int pathlen = strlen(ZVOL_FULL_DEV_DIR);
- int ret = 1;
-
- if (strncmp(arg, ZVOL_FULL_DEV_DIR, pathlen)) {
- return (-1);
- }
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "dump is not supported on device '%s'"), arg);
-
- if ((hdl = libzfs_init()) == NULL)
- return (1);
- libzfs_print_on_error(hdl, B_TRUE);
-
- volname = arg + pathlen;
-
- /* check the configuration of the pool */
- if ((p = strchr(volname, '/')) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "malformed dataset name"));
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- return (1);
- } else if (p - volname >= ZFS_MAX_DATASET_NAME_LEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset name is too long"));
- (void) zfs_error(hdl, EZFS_NAMETOOLONG, errbuf);
- return (1);
- } else {
- (void) strncpy(poolname, volname, p - volname);
- poolname[p - volname] = '\0';
- }
-
- if ((zhp = zpool_open(hdl, poolname)) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not open pool '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
- goto out;
- }
- config = zpool_get_config(zhp, NULL);
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not obtain vdev configuration for '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf);
- goto out;
- }
-
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &top, &toplevels) == 0);
-
- if (!supported_dump_vdev_type(hdl, top[0], errbuf)) {
- goto out;
- }
- ret = 0;
-
-out:
- if (zhp)
- zpool_close(zhp);
- libzfs_fini(hdl);
- return (ret);
-}
-
-int
-zpool_nextboot(libzfs_handle_t *hdl, uint64_t pool_guid, uint64_t dev_guid,
- const char *command)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *args;
- char *packed;
- size_t size;
- int error;
-
- args = fnvlist_alloc();
- fnvlist_add_uint64(args, ZPOOL_CONFIG_POOL_GUID, pool_guid);
- fnvlist_add_uint64(args, ZPOOL_CONFIG_GUID, dev_guid);
- fnvlist_add_string(args, "command", command);
- error = zcmd_write_src_nvlist(hdl, &zc, args);
- if (error == 0)
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_NEXTBOOT, &zc);
- zcmd_free_nvlists(&zc);
- nvlist_free(args);
- return (error);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
deleted file mode 100644
index 2e2e1020ad8a..000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
+++ /dev/null
@@ -1,3924 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
- * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <fcntl.h>
-#include <sys/param.h>
-#include <sys/mount.h>
-#include <pthread.h>
-#include <umem.h>
-#include <time.h>
-
-#include <libzfs.h>
-#include <libzfs_core.h>
-
-#include "zfs_namecheck.h"
-#include "zfs_prop.h"
-#include "zfs_fletcher.h"
-#include "libzfs_impl.h"
-#include <zlib.h>
-#include <sha2.h>
-#include <sys/zio_checksum.h>
-#include <sys/ddt.h>
-
-#ifdef __FreeBSD__
-extern int zfs_ioctl_version;
-#endif
-
-/* in libzfs_dataset.c */
-extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
-/* We need to use something for ENODATA. */
-#define ENODATA EIDRM
-
-static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
- recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
- uint64_t *, const char *);
-static int guid_to_name(libzfs_handle_t *, const char *,
- uint64_t, boolean_t, char *);
-
-static const zio_cksum_t zero_cksum = { 0 };
-
-typedef struct dedup_arg {
- int inputfd;
- int outputfd;
- libzfs_handle_t *dedup_hdl;
-} dedup_arg_t;
-
-typedef struct progress_arg {
- zfs_handle_t *pa_zhp;
- int pa_fd;
- boolean_t pa_parsable;
- boolean_t pa_astitle;
- uint64_t pa_size;
-} progress_arg_t;
-
-typedef struct dataref {
- uint64_t ref_guid;
- uint64_t ref_object;
- uint64_t ref_offset;
-} dataref_t;
-
-typedef struct dedup_entry {
- struct dedup_entry *dde_next;
- zio_cksum_t dde_chksum;
- uint64_t dde_prop;
- dataref_t dde_ref;
-} dedup_entry_t;
-
-#define MAX_DDT_PHYSMEM_PERCENT 20
-#define SMALLEST_POSSIBLE_MAX_DDT_MB 128
-
-typedef struct dedup_table {
- dedup_entry_t **dedup_hash_array;
- umem_cache_t *ddecache;
- uint64_t max_ddt_size; /* max dedup table size in bytes */
- uint64_t cur_ddt_size; /* current dedup table size in bytes */
- uint64_t ddt_count;
- int numhashbits;
- boolean_t ddt_full;
-} dedup_table_t;
-
-static int
-high_order_bit(uint64_t n)
-{
- int count;
-
- for (count = 0; n != 0; count++)
- n >>= 1;
- return (count);
-}
-
-static size_t
-ssread(void *buf, size_t len, FILE *stream)
-{
- size_t outlen;
-
- if ((outlen = fread(buf, len, 1, stream)) == 0)
- return (0);
-
- return (outlen);
-}
-
-static void
-ddt_hash_append(libzfs_handle_t *hdl, dedup_table_t *ddt, dedup_entry_t **ddepp,
- zio_cksum_t *cs, uint64_t prop, dataref_t *dr)
-{
- dedup_entry_t *dde;
-
- if (ddt->cur_ddt_size >= ddt->max_ddt_size) {
- if (ddt->ddt_full == B_FALSE) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Dedup table full. Deduplication will continue "
- "with existing table entries"));
- ddt->ddt_full = B_TRUE;
- }
- return;
- }
-
- if ((dde = umem_cache_alloc(ddt->ddecache, UMEM_DEFAULT))
- != NULL) {
- assert(*ddepp == NULL);
- dde->dde_next = NULL;
- dde->dde_chksum = *cs;
- dde->dde_prop = prop;
- dde->dde_ref = *dr;
- *ddepp = dde;
- ddt->cur_ddt_size += sizeof (dedup_entry_t);
- ddt->ddt_count++;
- }
-}
-
-/*
- * Using the specified dedup table, do a lookup for an entry with
- * the checksum cs. If found, return the block's reference info
- * in *dr. Otherwise, insert a new entry in the dedup table, using
- * the reference information specified by *dr.
- *
- * return value: true - entry was found
- * false - entry was not found
- */
-static boolean_t
-ddt_update(libzfs_handle_t *hdl, dedup_table_t *ddt, zio_cksum_t *cs,
- uint64_t prop, dataref_t *dr)
-{
- uint32_t hashcode;
- dedup_entry_t **ddepp;
-
- hashcode = BF64_GET(cs->zc_word[0], 0, ddt->numhashbits);
-
- for (ddepp = &(ddt->dedup_hash_array[hashcode]); *ddepp != NULL;
- ddepp = &((*ddepp)->dde_next)) {
- if (ZIO_CHECKSUM_EQUAL(((*ddepp)->dde_chksum), *cs) &&
- (*ddepp)->dde_prop == prop) {
- *dr = (*ddepp)->dde_ref;
- return (B_TRUE);
- }
- }
- ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr);
- return (B_FALSE);
-}
-
-static int
-dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
- zio_cksum_t *zc, int outfd)
-{
- ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
- ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
- (void) fletcher_4_incremental_native(drr,
- offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
- if (drr->drr_type != DRR_BEGIN) {
- ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
- drr_checksum.drr_checksum));
- drr->drr_u.drr_checksum.drr_checksum = *zc;
- }
- (void) fletcher_4_incremental_native(
- &drr->drr_u.drr_checksum.drr_checksum, sizeof (zio_cksum_t), zc);
- if (write(outfd, drr, sizeof (*drr)) == -1)
- return (errno);
- if (payload_len != 0) {
- (void) fletcher_4_incremental_native(payload, payload_len, zc);
- if (write(outfd, payload, payload_len) == -1)
- return (errno);
- }
- return (0);
-}
-
-/*
- * This function is started in a separate thread when the dedup option
- * has been requested. The main send thread determines the list of
- * snapshots to be included in the send stream and makes the ioctl calls
- * for each one. But instead of having the ioctl send the output to the
- * the output fd specified by the caller of zfs_send()), the
- * ioctl is told to direct the output to a pipe, which is read by the
- * alternate thread running THIS function. This function does the
- * dedup'ing by:
- * 1. building a dedup table (the DDT)
- * 2. doing checksums on each data block and inserting a record in the DDT
- * 3. looking for matching checksums, and
- * 4. sending a DRR_WRITE_BYREF record instead of a write record whenever
- * a duplicate block is found.
- * The output of this function then goes to the output fd requested
- * by the caller of zfs_send().
- */
-static void *
-cksummer(void *arg)
-{
- dedup_arg_t *dda = arg;
- char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE);
- dmu_replay_record_t thedrr;
- dmu_replay_record_t *drr = &thedrr;
- FILE *ofp;
- int outfd;
- dedup_table_t ddt;
- zio_cksum_t stream_cksum;
- uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
- uint64_t numbuckets;
-
- ddt.max_ddt_size =
- MAX((physmem * MAX_DDT_PHYSMEM_PERCENT) / 100,
- SMALLEST_POSSIBLE_MAX_DDT_MB << 20);
-
- numbuckets = ddt.max_ddt_size / (sizeof (dedup_entry_t));
-
- /*
- * numbuckets must be a power of 2. Increase number to
- * a power of 2 if necessary.
- */
- if (!ISP2(numbuckets))
- numbuckets = 1 << high_order_bit(numbuckets);
-
- ddt.dedup_hash_array = calloc(numbuckets, sizeof (dedup_entry_t *));
- ddt.ddecache = umem_cache_create("dde", sizeof (dedup_entry_t), 0,
- NULL, NULL, NULL, NULL, NULL, 0);
- ddt.cur_ddt_size = numbuckets * sizeof (dedup_entry_t *);
- ddt.numhashbits = high_order_bit(numbuckets) - 1;
- ddt.ddt_full = B_FALSE;
-
- outfd = dda->outputfd;
- ofp = fdopen(dda->inputfd, "r");
- while (ssread(drr, sizeof (*drr), ofp) != 0) {
-
- /*
- * kernel filled in checksum, we are going to write same
- * record, but need to regenerate checksum.
- */
- if (drr->drr_type != DRR_BEGIN) {
- bzero(&drr->drr_u.drr_checksum.drr_checksum,
- sizeof (drr->drr_u.drr_checksum.drr_checksum));
- }
-
- switch (drr->drr_type) {
- case DRR_BEGIN:
- {
- struct drr_begin *drrb = &drr->drr_u.drr_begin;
- int fflags;
- int sz = 0;
- ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
-
- ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC);
-
- /* set the DEDUP feature flag for this stream */
- fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
- fflags |= (DMU_BACKUP_FEATURE_DEDUP |
- DMU_BACKUP_FEATURE_DEDUPPROPS);
- DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags);
-
- if (drr->drr_payloadlen != 0) {
- sz = drr->drr_payloadlen;
-
- if (sz > SPA_MAXBLOCKSIZE) {
- buf = zfs_realloc(dda->dedup_hdl, buf,
- SPA_MAXBLOCKSIZE, sz);
- }
- (void) ssread(buf, sz, ofp);
- if (ferror(stdin))
- perror("fread");
- }
- if (dump_record(drr, buf, sz, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_END:
- {
- struct drr_end *drre = &drr->drr_u.drr_end;
- /* use the recalculated checksum */
- drre->drr_checksum = stream_cksum;
- if (dump_record(drr, NULL, 0, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_OBJECT:
- {
- struct drr_object *drro = &drr->drr_u.drr_object;
- if (drro->drr_bonuslen > 0) {
- (void) ssread(buf,
- P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
- ofp);
- }
- if (dump_record(drr, buf,
- P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
- &stream_cksum, outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_SPILL:
- {
- struct drr_spill *drrs = &drr->drr_u.drr_spill;
- (void) ssread(buf, drrs->drr_length, ofp);
- if (dump_record(drr, buf, drrs->drr_length,
- &stream_cksum, outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_FREEOBJECTS:
- {
- if (dump_record(drr, NULL, 0, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_WRITE:
- {
- struct drr_write *drrw = &drr->drr_u.drr_write;
- dataref_t dataref;
- uint64_t payload_size;
-
- payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
- (void) ssread(buf, payload_size, ofp);
-
- /*
- * Use the existing checksum if it's dedup-capable,
- * else calculate a SHA256 checksum for it.
- */
-
- if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum,
- zero_cksum) ||
- !DRR_IS_DEDUP_CAPABLE(drrw->drr_checksumflags)) {
- SHA256_CTX ctx;
- zio_cksum_t tmpsha256;
-
- SHA256Init(&ctx);
- SHA256Update(&ctx, buf, payload_size);
- SHA256Final(&tmpsha256, &ctx);
- drrw->drr_key.ddk_cksum.zc_word[0] =
- BE_64(tmpsha256.zc_word[0]);
- drrw->drr_key.ddk_cksum.zc_word[1] =
- BE_64(tmpsha256.zc_word[1]);
- drrw->drr_key.ddk_cksum.zc_word[2] =
- BE_64(tmpsha256.zc_word[2]);
- drrw->drr_key.ddk_cksum.zc_word[3] =
- BE_64(tmpsha256.zc_word[3]);
- drrw->drr_checksumtype = ZIO_CHECKSUM_SHA256;
- drrw->drr_checksumflags = DRR_CHECKSUM_DEDUP;
- }
-
- dataref.ref_guid = drrw->drr_toguid;
- dataref.ref_object = drrw->drr_object;
- dataref.ref_offset = drrw->drr_offset;
-
- if (ddt_update(dda->dedup_hdl, &ddt,
- &drrw->drr_key.ddk_cksum, drrw->drr_key.ddk_prop,
- &dataref)) {
- dmu_replay_record_t wbr_drr = {0};
- struct drr_write_byref *wbr_drrr =
- &wbr_drr.drr_u.drr_write_byref;
-
- /* block already present in stream */
- wbr_drr.drr_type = DRR_WRITE_BYREF;
-
- wbr_drrr->drr_object = drrw->drr_object;
- wbr_drrr->drr_offset = drrw->drr_offset;
- wbr_drrr->drr_length = drrw->drr_logical_size;
- wbr_drrr->drr_toguid = drrw->drr_toguid;
- wbr_drrr->drr_refguid = dataref.ref_guid;
- wbr_drrr->drr_refobject =
- dataref.ref_object;
- wbr_drrr->drr_refoffset =
- dataref.ref_offset;
-
- wbr_drrr->drr_checksumtype =
- drrw->drr_checksumtype;
- wbr_drrr->drr_checksumflags =
- drrw->drr_checksumtype;
- wbr_drrr->drr_key.ddk_cksum =
- drrw->drr_key.ddk_cksum;
- wbr_drrr->drr_key.ddk_prop =
- drrw->drr_key.ddk_prop;
-
- if (dump_record(&wbr_drr, NULL, 0,
- &stream_cksum, outfd) != 0)
- goto out;
- } else {
- /* block not previously seen */
- if (dump_record(drr, buf, payload_size,
- &stream_cksum, outfd) != 0)
- goto out;
- }
- break;
- }
-
- case DRR_WRITE_EMBEDDED:
- {
- struct drr_write_embedded *drrwe =
- &drr->drr_u.drr_write_embedded;
- (void) ssread(buf,
- P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
- if (dump_record(drr, buf,
- P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
- &stream_cksum, outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_FREE:
- {
- if (dump_record(drr, NULL, 0, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- default:
- (void) fprintf(stderr, "INVALID record type 0x%x\n",
- drr->drr_type);
- /* should never happen, so assert */
- assert(B_FALSE);
- }
- }
-out:
- umem_cache_destroy(ddt.ddecache);
- free(ddt.dedup_hash_array);
- free(buf);
- (void) fclose(ofp);
-
- return (NULL);
-}
-
-/*
- * Routines for dealing with the AVL tree of fs-nvlists
- */
-typedef struct fsavl_node {
- avl_node_t fn_node;
- nvlist_t *fn_nvfs;
- char *fn_snapname;
- uint64_t fn_guid;
-} fsavl_node_t;
-
-static int
-fsavl_compare(const void *arg1, const void *arg2)
-{
- const fsavl_node_t *fn1 = (const fsavl_node_t *)arg1;
- const fsavl_node_t *fn2 = (const fsavl_node_t *)arg2;
-
- return (AVL_CMP(fn1->fn_guid, fn2->fn_guid));
-}
-
-/*
- * Given the GUID of a snapshot, find its containing filesystem and
- * (optionally) name.
- */
-static nvlist_t *
-fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
-{
- fsavl_node_t fn_find;
- fsavl_node_t *fn;
-
- fn_find.fn_guid = snapguid;
-
- fn = avl_find(avl, &fn_find, NULL);
- if (fn) {
- if (snapname)
- *snapname = fn->fn_snapname;
- return (fn->fn_nvfs);
- }
- return (NULL);
-}
-
-static void
-fsavl_destroy(avl_tree_t *avl)
-{
- fsavl_node_t *fn;
- void *cookie;
-
- if (avl == NULL)
- return;
-
- cookie = NULL;
- while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
- free(fn);
- avl_destroy(avl);
- free(avl);
-}
-
-/*
- * Given an nvlist, produce an avl tree of snapshots, ordered by guid
- */
-static avl_tree_t *
-fsavl_create(nvlist_t *fss)
-{
- avl_tree_t *fsavl;
- nvpair_t *fselem = NULL;
-
- if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
- return (NULL);
-
- avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
- offsetof(fsavl_node_t, fn_node));
-
- while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
- nvlist_t *nvfs, *snaps;
- nvpair_t *snapelem = NULL;
-
- VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
- VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
-
- while ((snapelem =
- nvlist_next_nvpair(snaps, snapelem)) != NULL) {
- fsavl_node_t *fn;
- uint64_t guid;
-
- VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
- if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
- fsavl_destroy(fsavl);
- return (NULL);
- }
- fn->fn_nvfs = nvfs;
- fn->fn_snapname = nvpair_name(snapelem);
- fn->fn_guid = guid;
-
- /*
- * Note: if there are multiple snaps with the
- * same GUID, we ignore all but one.
- */
- if (avl_find(fsavl, fn, NULL) == NULL)
- avl_add(fsavl, fn);
- else
- free(fn);
- }
- }
-
- return (fsavl);
-}
-
-/*
- * Routines for dealing with the giant nvlist of fs-nvlists, etc.
- */
-typedef struct send_data {
- /*
- * assigned inside every recursive call,
- * restored from *_save on return:
- *
- * guid of fromsnap snapshot in parent dataset
- * txg of fromsnap snapshot in current dataset
- * txg of tosnap snapshot in current dataset
- */
-
- uint64_t parent_fromsnap_guid;
- uint64_t fromsnap_txg;
- uint64_t tosnap_txg;
-
- /* the nvlists get accumulated during depth-first traversal */
- nvlist_t *parent_snaps;
- nvlist_t *fss;
- nvlist_t *snapprops;
-
- /* send-receive configuration, does not change during traversal */
- const char *fsname;
- const char *fromsnap;
- const char *tosnap;
- boolean_t recursive;
- boolean_t verbose;
- boolean_t replicate;
-
- /*
- * The header nvlist is of the following format:
- * {
- * "tosnap" -> string
- * "fromsnap" -> string (if incremental)
- * "fss" -> {
- * id -> {
- *
- * "name" -> string (full name; for debugging)
- * "parentfromsnap" -> number (guid of fromsnap in parent)
- *
- * "props" -> { name -> value (only if set here) }
- * "snaps" -> { name (lastname) -> number (guid) }
- * "snapprops" -> { name (lastname) -> { name -> value } }
- *
- * "origin" -> number (guid) (if clone)
- * "sent" -> boolean (not on-disk)
- * }
- * }
- * }
- *
- */
-} send_data_t;
-
-static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
-
-static int
-send_iterate_snap(zfs_handle_t *zhp, void *arg)
-{
- send_data_t *sd = arg;
- uint64_t guid = zhp->zfs_dmustats.dds_guid;
- uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
- char *snapname;
- nvlist_t *nv;
-
- snapname = strrchr(zhp->zfs_name, '@')+1;
-
- if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
- if (sd->verbose) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "skipping snapshot %s because it was created "
- "after the destination snapshot (%s)\n"),
- zhp->zfs_name, sd->tosnap);
- }
- zfs_close(zhp);
- return (0);
- }
-
- VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
- /*
- * NB: if there is no fromsnap here (it's a newly created fs in
- * an incremental replication), we will substitute the tosnap.
- */
- if ((sd->fromsnap && strcmp(snapname, sd->fromsnap) == 0) ||
- (sd->parent_fromsnap_guid == 0 && sd->tosnap &&
- strcmp(snapname, sd->tosnap) == 0)) {
- sd->parent_fromsnap_guid = guid;
- }
-
- VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
- send_iterate_prop(zhp, nv);
- VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
- nvlist_free(nv);
-
- zfs_close(zhp);
- return (0);
-}
-
-static void
-send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
-{
- nvpair_t *elem = NULL;
-
- while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
- char *propname = nvpair_name(elem);
- zfs_prop_t prop = zfs_name_to_prop(propname);
- nvlist_t *propnv;
-
- if (!zfs_prop_user(propname)) {
- /*
- * Realistically, this should never happen. However,
- * we want the ability to add DSL properties without
- * needing to make incompatible version changes. We
- * need to ignore unknown properties to allow older
- * software to still send datasets containing these
- * properties, with the unknown properties elided.
- */
- if (prop == ZPROP_INVAL)
- continue;
-
- if (zfs_prop_readonly(prop))
- continue;
- }
-
- verify(nvpair_value_nvlist(elem, &propnv) == 0);
- if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION ||
- prop == ZFS_PROP_REFQUOTA ||
- prop == ZFS_PROP_REFRESERVATION) {
- char *source;
- uint64_t value;
- verify(nvlist_lookup_uint64(propnv,
- ZPROP_VALUE, &value) == 0);
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
- continue;
- /*
- * May have no source before SPA_VERSION_RECVD_PROPS,
- * but is still modifiable.
- */
- if (nvlist_lookup_string(propnv,
- ZPROP_SOURCE, &source) == 0) {
- if ((strcmp(source, zhp->zfs_name) != 0) &&
- (strcmp(source,
- ZPROP_SOURCE_VAL_RECVD) != 0))
- continue;
- }
- } else {
- char *source;
- if (nvlist_lookup_string(propnv,
- ZPROP_SOURCE, &source) != 0)
- continue;
- if ((strcmp(source, zhp->zfs_name) != 0) &&
- (strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0))
- continue;
- }
-
- if (zfs_prop_user(propname) ||
- zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
- char *value;
- verify(nvlist_lookup_string(propnv,
- ZPROP_VALUE, &value) == 0);
- VERIFY(0 == nvlist_add_string(nv, propname, value));
- } else {
- uint64_t value;
- verify(nvlist_lookup_uint64(propnv,
- ZPROP_VALUE, &value) == 0);
- VERIFY(0 == nvlist_add_uint64(nv, propname, value));
- }
- }
-}
-
-/*
- * returns snapshot creation txg
- * and returns 0 if the snapshot does not exist
- */
-static uint64_t
-get_snap_txg(libzfs_handle_t *hdl, const char *fs, const char *snap)
-{
- char name[ZFS_MAX_DATASET_NAME_LEN];
- uint64_t txg = 0;
-
- if (fs == NULL || fs[0] == '\0' || snap == NULL || snap[0] == '\0')
- return (txg);
-
- (void) snprintf(name, sizeof (name), "%s@%s", fs, snap);
- if (zfs_dataset_exists(hdl, name, ZFS_TYPE_SNAPSHOT)) {
- zfs_handle_t *zhp = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT);
- if (zhp != NULL) {
- txg = zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG);
- zfs_close(zhp);
- }
- }
-
- return (txg);
-}
-
-/*
- * recursively generate nvlists describing datasets. See comment
- * for the data structure send_data_t above for description of contents
- * of the nvlist.
- */
-static int
-send_iterate_fs(zfs_handle_t *zhp, void *arg)
-{
- send_data_t *sd = arg;
- nvlist_t *nvfs, *nv;
- int rv = 0;
- uint64_t min_txg = 0, max_txg = 0;
- uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
- uint64_t fromsnap_txg_save = sd->fromsnap_txg;
- uint64_t tosnap_txg_save = sd->tosnap_txg;
- uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
- uint64_t guid = zhp->zfs_dmustats.dds_guid;
- uint64_t fromsnap_txg, tosnap_txg;
- char guidstring[64];
-
- fromsnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->fromsnap);
- if (fromsnap_txg != 0)
- sd->fromsnap_txg = fromsnap_txg;
-
- tosnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->tosnap);
- if (tosnap_txg != 0)
- sd->tosnap_txg = tosnap_txg;
-
- /*
- * on the send side, if the current dataset does not have tosnap,
- * perform two additional checks:
- *
- * - skip sending the current dataset if it was created later than
- * the parent tosnap
- * - return error if the current dataset was created earlier than
- * the parent tosnap
- */
- if (sd->tosnap != NULL && tosnap_txg == 0) {
- if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
- if (sd->verbose) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "skipping dataset %s: snapshot %s does "
- "not exist\n"), zhp->zfs_name, sd->tosnap);
- }
- } else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "cannot send %s@%s%s: snapshot %s@%s does not "
- "exist\n"), sd->fsname, sd->tosnap, sd->recursive ?
- dgettext(TEXT_DOMAIN, " recursively") : "",
- zhp->zfs_name, sd->tosnap);
- rv = -1;
- }
- goto out;
- }
-
- nvfs = fnvlist_alloc();
- fnvlist_add_string(nvfs, "name", zhp->zfs_name);
- fnvlist_add_uint64(nvfs, "parentfromsnap",
- sd->parent_fromsnap_guid);
-
- if (zhp->zfs_dmustats.dds_origin[0]) {
- zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
- zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
- if (origin == NULL) {
- rv = -1;
- goto out;
- }
- VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
- origin->zfs_dmustats.dds_guid));
- }
-
- /* iterate over props */
- VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
- send_iterate_prop(zhp, nv);
- VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
- nvlist_free(nv);
-
- /* iterate over snaps, and set sd->parent_fromsnap_guid */
- if (!sd->replicate && fromsnap_txg != 0)
- min_txg = fromsnap_txg;
- if (!sd->replicate && tosnap_txg != 0)
- max_txg = tosnap_txg;
- sd->parent_fromsnap_guid = 0;
- VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
- VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
- (void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd,
- min_txg, max_txg);
- VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
- VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
- fnvlist_free(sd->parent_snaps);
- fnvlist_free(sd->snapprops);
-
- /* add this fs to nvlist */
- (void) snprintf(guidstring, sizeof (guidstring),
- "0x%llx", (longlong_t)guid);
- VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
- nvlist_free(nvfs);
-
- /* iterate over children */
- if (sd->recursive)
- rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
-
-out:
- sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
- sd->fromsnap_txg = fromsnap_txg_save;
- sd->tosnap_txg = tosnap_txg_save;
-
- zfs_close(zhp);
- return (rv);
-}
-
-static int
-gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
- const char *tosnap, boolean_t recursive, boolean_t verbose,
- boolean_t replicate, nvlist_t **nvlp, avl_tree_t **avlp)
-{
- zfs_handle_t *zhp;
- int error;
- uint64_t min_txg = 0, max_txg = 0;
- send_data_t sd = { 0 };
-
- zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- return (EZFS_BADTYPE);
-
- VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
- sd.fsname = fsname;
- sd.fromsnap = fromsnap;
- sd.tosnap = tosnap;
- sd.recursive = recursive;
- sd.verbose = verbose;
- sd.replicate = replicate;
-
- if ((error = send_iterate_fs(zhp, &sd)) != 0) {
- nvlist_free(sd.fss);
- if (avlp != NULL)
- *avlp = NULL;
- *nvlp = NULL;
- return (error);
- }
-
- if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
- nvlist_free(sd.fss);
- *nvlp = NULL;
- return (EZFS_NOMEM);
- }
-
- *nvlp = sd.fss;
- return (0);
-}
-
-/*
- * Routines specific to "zfs send"
- */
-typedef struct send_dump_data {
- /* these are all just the short snapname (the part after the @) */
- const char *fromsnap;
- const char *tosnap;
- char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
- uint64_t prevsnap_obj;
- boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
- boolean_t progressastitle;
- boolean_t large_block, compress;
- int outfd;
- boolean_t err;
- nvlist_t *fss;
- nvlist_t *snapholds;
- avl_tree_t *fsavl;
- snapfilter_cb_t *filter_cb;
- void *filter_cb_arg;
- nvlist_t *debugnv;
- char holdtag[ZFS_MAX_DATASET_NAME_LEN];
- int cleanup_fd;
- uint64_t size;
-} send_dump_data_t;
-
-static int
-zfs_send_space(zfs_handle_t *zhp, const char *snapname, const char *from,
- enum lzc_send_flags flags, uint64_t *spacep)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- int error;
-
- assert(snapname != NULL);
- error = lzc_send_space(snapname, from, flags, spacep);
-
- if (error != 0) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot estimate space for '%s'"), snapname);
-
- switch (error) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- if (zfs_dataset_exists(hdl, snapname,
- ZFS_TYPE_SNAPSHOT)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (%s) does not exist"),
- snapname);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- case EINVAL:
- zfs_error_aux(hdl, strerror(error));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, error, errbuf));
- }
- }
-
- return (0);
-}
-
-/*
- * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
- * NULL) to the file descriptor specified by outfd.
- */
-static int
-dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
- boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
- nvlist_t *debugnv)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *thisdbg;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- assert(fromsnap_obj == 0 || !fromorigin);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_cookie = outfd;
- zc.zc_obj = fromorigin;
- zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zc.zc_fromobj = fromsnap_obj;
- zc.zc_flags = flags;
-
- VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
- if (fromsnap && fromsnap[0] != '\0') {
- VERIFY(0 == nvlist_add_string(thisdbg,
- "fromsnap", fromsnap));
- }
-
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot send '%s'"), zhp->zfs_name);
-
- VERIFY(0 == nvlist_add_uint64(thisdbg, "error", errno));
- if (debugnv) {
- VERIFY(0 == nvlist_add_nvlist(debugnv,
- zhp->zfs_name, thisdbg));
- }
- nvlist_free(thisdbg);
-
- switch (errno) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- if (zfs_dataset_exists(hdl, zc.zc_name,
- ZFS_TYPE_SNAPSHOT)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (@%s) does not exist"),
- zc.zc_value);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
-#ifdef illumos
- case ENOSTR:
-#endif
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- if (debugnv)
- VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg));
- nvlist_free(thisdbg);
-
- return (0);
-}
-
-static void
-gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd)
-{
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
-
- /*
- * zfs_send() only sets snapholds for sends that need them,
- * e.g. replication and doall.
- */
- if (sdd->snapholds == NULL)
- return;
-
- fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag);
-}
-
-static void *
-send_progress_thread(void *arg)
-{
- progress_arg_t *pa = arg;
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *zhp = pa->pa_zhp;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- unsigned long long bytes, total;
- char buf[16];
- time_t t;
- struct tm *tm;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (!pa->pa_parsable && !pa->pa_astitle)
- (void) fprintf(stderr, "TIME SENT SNAPSHOT\n");
-
- /*
- * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
- */
- for (;;) {
- (void) sleep(1);
-
- zc.zc_cookie = pa->pa_fd;
- if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
- return ((void *)-1);
-
- (void) time(&t);
- tm = localtime(&t);
- bytes = zc.zc_cookie;
-
- if (pa->pa_astitle) {
- int pct;
- if (pa->pa_size > bytes)
- pct = 100 * bytes / pa->pa_size;
- else
- pct = 100;
-
- setproctitle("sending %s (%d%%: %llu/%llu)",
- zhp->zfs_name, pct, bytes, pa->pa_size);
- } else if (pa->pa_parsable) {
- (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
- tm->tm_hour, tm->tm_min, tm->tm_sec,
- bytes, zhp->zfs_name);
- } else {
- zfs_nicenum(bytes, buf, sizeof (buf));
- (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n",
- tm->tm_hour, tm->tm_min, tm->tm_sec,
- buf, zhp->zfs_name);
- }
- }
-}
-
-static void
-send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap,
- uint64_t size, boolean_t parsable)
-{
- if (parsable) {
- if (fromsnap != NULL) {
- (void) fprintf(fout, "incremental\t%s\t%s",
- fromsnap, tosnap);
- } else {
- (void) fprintf(fout, "full\t%s",
- tosnap);
- }
- } else {
- if (fromsnap != NULL) {
- if (strchr(fromsnap, '@') == NULL &&
- strchr(fromsnap, '#') == NULL) {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "send from @%s to %s"),
- fromsnap, tosnap);
- } else {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "send from %s to %s"),
- fromsnap, tosnap);
- }
- } else {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "full send of %s"),
- tosnap);
- }
- }
-
- if (parsable) {
- (void) fprintf(fout, "\t%llu",
- (longlong_t)size);
- } else if (size != 0) {
- char buf[16];
- zfs_nicenum(size, buf, sizeof (buf));
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- " estimated size is %s"), buf);
- }
- (void) fprintf(fout, "\n");
-}
-
-static int
-dump_snapshot(zfs_handle_t *zhp, void *arg)
-{
- send_dump_data_t *sdd = arg;
- progress_arg_t pa = { 0 };
- pthread_t tid;
- char *thissnap;
- enum lzc_send_flags flags = 0;
- int err;
- boolean_t isfromsnap, istosnap, fromorigin;
- boolean_t exclude = B_FALSE;
- FILE *fout = sdd->std_out ? stdout : stderr;
-
- err = 0;
- thissnap = strchr(zhp->zfs_name, '@') + 1;
- isfromsnap = (sdd->fromsnap != NULL &&
- strcmp(sdd->fromsnap, thissnap) == 0);
-
- if (!sdd->seenfrom && isfromsnap) {
- gather_holds(zhp, sdd);
- sdd->seenfrom = B_TRUE;
- (void) strcpy(sdd->prevsnap, thissnap);
- sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zfs_close(zhp);
- return (0);
- }
-
- if (sdd->seento || !sdd->seenfrom) {
- zfs_close(zhp);
- return (0);
- }
-
- istosnap = (strcmp(sdd->tosnap, thissnap) == 0);
- if (istosnap)
- sdd->seento = B_TRUE;
-
- if (sdd->large_block)
- flags |= LZC_SEND_FLAG_LARGE_BLOCK;
- if (sdd->embed_data)
- flags |= LZC_SEND_FLAG_EMBED_DATA;
- if (sdd->compress)
- flags |= LZC_SEND_FLAG_COMPRESS;
-
- if (!sdd->doall && !isfromsnap && !istosnap) {
- if (sdd->replicate) {
- char *snapname;
- nvlist_t *snapprops;
- /*
- * Filter out all intermediate snapshots except origin
- * snapshots needed to replicate clones.
- */
- nvlist_t *nvfs = fsavl_find(sdd->fsavl,
- zhp->zfs_dmustats.dds_guid, &snapname);
-
- VERIFY(0 == nvlist_lookup_nvlist(nvfs,
- "snapprops", &snapprops));
- VERIFY(0 == nvlist_lookup_nvlist(snapprops,
- thissnap, &snapprops));
- exclude = !nvlist_exists(snapprops, "is_clone_origin");
- } else {
- exclude = B_TRUE;
- }
- }
-
- /*
- * If a filter function exists, call it to determine whether
- * this snapshot will be sent.
- */
- if (exclude || (sdd->filter_cb != NULL &&
- sdd->filter_cb(zhp, sdd->filter_cb_arg) == B_FALSE)) {
- /*
- * This snapshot is filtered out. Don't send it, and don't
- * set prevsnap_obj, so it will be as if this snapshot didn't
- * exist, and the next accepted snapshot will be sent as
- * an incremental from the last accepted one, or as the
- * first (and full) snapshot in the case of a replication,
- * non-incremental send.
- */
- zfs_close(zhp);
- return (0);
- }
-
- gather_holds(zhp, sdd);
- fromorigin = sdd->prevsnap[0] == '\0' &&
- (sdd->fromorigin || sdd->replicate);
-
- if (sdd->verbose || sdd->progress) {
- uint64_t size = 0;
- char fromds[ZFS_MAX_DATASET_NAME_LEN];
-
- if (sdd->prevsnap[0] != '\0') {
- (void) strlcpy(fromds, zhp->zfs_name, sizeof (fromds));
- *(strchr(fromds, '@') + 1) = '\0';
- (void) strlcat(fromds, sdd->prevsnap, sizeof (fromds));
- }
- if (zfs_send_space(zhp, zhp->zfs_name,
- sdd->prevsnap[0] ? fromds : NULL, flags, &size) != 0) {
- size = 0; /* cannot estimate send space */
- } else {
- send_print_verbose(fout, zhp->zfs_name,
- sdd->prevsnap[0] ? sdd->prevsnap : NULL,
- size, sdd->parsable);
- }
- sdd->size += size;
- }
-
- if (!sdd->dryrun) {
- /*
- * If progress reporting is requested, spawn a new thread to
- * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
- */
- if (sdd->progress) {
- pa.pa_zhp = zhp;
- pa.pa_fd = sdd->outfd;
- pa.pa_parsable = sdd->parsable;
- pa.pa_size = sdd->size;
- pa.pa_astitle = sdd->progressastitle;
-
- if ((err = pthread_create(&tid, NULL,
- send_progress_thread, &pa)) != 0) {
- zfs_close(zhp);
- return (err);
- }
- }
-
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- fromorigin, sdd->outfd, flags, sdd->debugnv);
-
- if (sdd->progress) {
- (void) pthread_cancel(tid);
- (void) pthread_join(tid, NULL);
- }
- }
-
- (void) strcpy(sdd->prevsnap, thissnap);
- sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zfs_close(zhp);
- return (err);
-}
-
-static int
-dump_filesystem(zfs_handle_t *zhp, void *arg)
-{
- int rv = 0;
- uint64_t min_txg = 0, max_txg = 0;
- send_dump_data_t *sdd = arg;
- boolean_t missingfrom = B_FALSE;
- zfs_cmd_t zc = { 0 };
-
- (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
- zhp->zfs_name, sdd->tosnap);
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s: does not exist\n"),
- zhp->zfs_name, sdd->tosnap);
- sdd->err = B_TRUE;
- return (0);
- }
-
- if (sdd->replicate && sdd->fromsnap) {
- /*
- * If this fs does not have fromsnap, and we're doing
- * recursive, we need to send a full stream from the
- * beginning (or an incremental from the origin if this
- * is a clone). If we're doing non-recursive, then let
- * them get the error.
- */
- (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
- zhp->zfs_name, sdd->fromsnap);
- if (ioctl(zhp->zfs_hdl->libzfs_fd,
- ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- missingfrom = B_TRUE;
- }
- }
-
- sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0;
- sdd->prevsnap_obj = 0;
- if (sdd->fromsnap == NULL || missingfrom)
- sdd->seenfrom = B_TRUE;
-
- if (!sdd->replicate && sdd->fromsnap != NULL)
- min_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
- sdd->fromsnap);
- if (!sdd->replicate && sdd->tosnap != NULL)
- max_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
- sdd->tosnap);
-
- rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg,
- min_txg, max_txg);
- if (!sdd->seenfrom) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) does not exist\n"),
- zhp->zfs_name, sdd->tosnap,
- zhp->zfs_name, sdd->fromsnap);
- sdd->err = B_TRUE;
- } else if (!sdd->seento) {
- if (sdd->fromsnap) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) "
- "is not earlier than it\n"),
- zhp->zfs_name, sdd->tosnap,
- zhp->zfs_name, sdd->fromsnap);
- } else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: "
- "could not send %s@%s: does not exist\n"),
- zhp->zfs_name, sdd->tosnap);
- }
- sdd->err = B_TRUE;
- }
-
- return (rv);
-}
-
-static int
-dump_filesystems(zfs_handle_t *rzhp, void *arg)
-{
- send_dump_data_t *sdd = arg;
- nvpair_t *fspair;
- boolean_t needagain, progress;
-
- if (!sdd->replicate)
- return (dump_filesystem(rzhp, sdd));
-
- /* Mark the clone origin snapshots. */
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *nvfs;
- uint64_t origin_guid = 0;
-
- VERIFY(0 == nvpair_value_nvlist(fspair, &nvfs));
- (void) nvlist_lookup_uint64(nvfs, "origin", &origin_guid);
- if (origin_guid != 0) {
- char *snapname;
- nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
- origin_guid, &snapname);
- if (origin_nv != NULL) {
- nvlist_t *snapprops;
- VERIFY(0 == nvlist_lookup_nvlist(origin_nv,
- "snapprops", &snapprops));
- VERIFY(0 == nvlist_lookup_nvlist(snapprops,
- snapname, &snapprops));
- VERIFY(0 == nvlist_add_boolean(
- snapprops, "is_clone_origin"));
- }
- }
- }
-again:
- needagain = progress = B_FALSE;
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist, *parent_nv;
- char *fsname;
- zfs_handle_t *zhp;
- int err;
- uint64_t origin_guid = 0;
- uint64_t parent_guid = 0;
-
- VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
- if (nvlist_lookup_boolean(fslist, "sent") == 0)
- continue;
-
- VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
- (void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
- (void) nvlist_lookup_uint64(fslist, "parentfromsnap",
- &parent_guid);
-
- if (parent_guid != 0) {
- parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
- if (!nvlist_exists(parent_nv, "sent")) {
- /* parent has not been sent; skip this one */
- needagain = B_TRUE;
- continue;
- }
- }
-
- if (origin_guid != 0) {
- nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
- origin_guid, NULL);
- if (origin_nv != NULL &&
- !nvlist_exists(origin_nv, "sent")) {
- /*
- * origin has not been sent yet;
- * skip this clone.
- */
- needagain = B_TRUE;
- continue;
- }
- }
-
- zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (-1);
- err = dump_filesystem(zhp, sdd);
- VERIFY(nvlist_add_boolean(fslist, "sent") == 0);
- progress = B_TRUE;
- zfs_close(zhp);
- if (err)
- return (err);
- }
- if (needagain) {
- assert(progress);
- goto again;
- }
-
- /* clean out the sent flags in case we reuse this fss */
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
-
- VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
- (void) nvlist_remove_all(fslist, "sent");
- }
-
- return (0);
-}
-
-nvlist_t *
-zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
-{
- unsigned int version;
- int nread;
- unsigned long long checksum, packed_len;
-
- /*
- * Decode token header, which is:
- * <token version>-<checksum of payload>-<uncompressed payload length>
- * Note that the only supported token version is 1.
- */
- nread = sscanf(token, "%u-%llx-%llx-",
- &version, &checksum, &packed_len);
- if (nread != 3) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (invalid format)"));
- return (NULL);
- }
-
- if (version != ZFS_SEND_RESUME_TOKEN_VERSION) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (invalid version %u)"),
- version);
- return (NULL);
- }
-
- /* convert hexadecimal representation to binary */
- token = strrchr(token, '-') + 1;
- int len = strlen(token) / 2;
- unsigned char *compressed = zfs_alloc(hdl, len);
- for (int i = 0; i < len; i++) {
- nread = sscanf(token + i * 2, "%2hhx", compressed + i);
- if (nread != 1) {
- free(compressed);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt "
- "(payload is not hex-encoded)"));
- return (NULL);
- }
- }
-
- /* verify checksum */
- zio_cksum_t cksum;
- fletcher_4_native(compressed, len, NULL, &cksum);
- if (cksum.zc_word[0] != checksum) {
- free(compressed);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (incorrect checksum)"));
- return (NULL);
- }
-
- /* uncompress */
- void *packed = zfs_alloc(hdl, packed_len);
- uLongf packed_len_long = packed_len;
- if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
- packed_len_long != packed_len) {
- free(packed);
- free(compressed);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (decompression failed)"));
- return (NULL);
- }
-
- /* unpack nvlist */
- nvlist_t *nv;
- int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
- free(packed);
- free(compressed);
- if (error != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (nvlist_unpack failed)"));
- return (NULL);
- }
- return (nv);
-}
-
-int
-zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
- const char *resume_token)
-{
- char errbuf[1024];
- char *toname;
- char *fromname = NULL;
- uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
- zfs_handle_t *zhp;
- int error = 0;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- enum lzc_send_flags lzc_flags = 0;
- uint64_t size = 0;
- FILE *fout = (flags->verbose && flags->dryrun) ? stdout : stderr;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot resume send"));
-
- nvlist_t *resume_nvl =
- zfs_send_resume_token_to_nvlist(hdl, resume_token);
- if (resume_nvl == NULL) {
- /*
- * zfs_error_aux has already been set by
- * zfs_send_resume_token_to_nvlist
- */
- return (zfs_error(hdl, EZFS_FAULT, errbuf));
- }
- if (flags->verbose) {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "resume token contents:\n"));
- nvlist_print(fout, resume_nvl);
- }
-
- if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt"));
- return (zfs_error(hdl, EZFS_FAULT, errbuf));
- }
- fromguid = 0;
- (void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid);
-
- if (flags->largeblock || nvlist_exists(resume_nvl, "largeblockok"))
- lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
- if (flags->embed_data || nvlist_exists(resume_nvl, "embedok"))
- lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
- if (flags->compress || nvlist_exists(resume_nvl, "compressok"))
- lzc_flags |= LZC_SEND_FLAG_COMPRESS;
-
- if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
- if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is no longer the same snapshot used in "
- "the initial send"), toname);
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' used in the initial send no longer exists"),
- toname);
- }
- return (zfs_error(hdl, EZFS_BADPATH, errbuf));
- }
- zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
- if (zhp == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "unable to access '%s'"), name);
- return (zfs_error(hdl, EZFS_BADPATH, errbuf));
- }
-
- if (fromguid != 0) {
- if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source %#llx no longer exists"),
- (longlong_t)fromguid);
- return (zfs_error(hdl, EZFS_BADPATH, errbuf));
- }
- fromname = name;
- }
-
- if (flags->progress || flags->verbose) {
- error = lzc_send_space(zhp->zfs_name, fromname,
- lzc_flags, &size);
- if (error == 0)
- size = MAX(0, (int64_t)(size - bytes));
- }
- if (flags->verbose) {
- send_print_verbose(fout, zhp->zfs_name, fromname,
- size, flags->parsable);
- }
-
- if (!flags->dryrun) {
- progress_arg_t pa = { 0 };
- pthread_t tid;
- /*
- * If progress reporting is requested, spawn a new thread to
- * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
- */
- if (flags->progress) {
- pa.pa_zhp = zhp;
- pa.pa_fd = outfd;
- pa.pa_parsable = flags->parsable;
- pa.pa_size = size;
- pa.pa_astitle = flags->progressastitle;
-
- error = pthread_create(&tid, NULL,
- send_progress_thread, &pa);
- if (error != 0) {
- zfs_close(zhp);
- return (error);
- }
- }
-
- error = lzc_send_resume(zhp->zfs_name, fromname, outfd,
- lzc_flags, resumeobj, resumeoff);
-
- if (flags->progress) {
- (void) pthread_cancel(tid);
- (void) pthread_join(tid, NULL);
- }
-
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot send '%s'"), zhp->zfs_name);
-
- zfs_close(zhp);
-
- switch (error) {
- case 0:
- return (0);
- case EXDEV:
- case ENOENT:
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
-#ifdef illumos
- case ENOSTR:
-#endif
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
-
- zfs_close(zhp);
-
- return (error);
-}
-
-/*
- * Generate a send stream for the dataset identified by the argument zhp.
- *
- * The content of the send stream is the snapshot identified by
- * 'tosnap'. Incremental streams are requested in two ways:
- * - from the snapshot identified by "fromsnap" (if non-null) or
- * - from the origin of the dataset identified by zhp, which must
- * be a clone. In this case, "fromsnap" is null and "fromorigin"
- * is TRUE.
- *
- * The send stream is recursive (i.e. dumps a hierarchy of snapshots) and
- * uses a special header (with a hdrtype field of DMU_COMPOUNDSTREAM)
- * if "replicate" is set. If "doall" is set, dump all the intermediate
- * snapshots. The DMU_COMPOUNDSTREAM header is used in the "doall"
- * case too. If "props" is set, send properties.
- */
-int
-zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
- void *cb_arg, nvlist_t **debugnvp)
-{
- char errbuf[1024];
- send_dump_data_t sdd = { 0 };
- int err = 0;
- nvlist_t *fss = NULL;
- avl_tree_t *fsavl = NULL;
- static uint64_t holdseq;
- int spa_version;
- pthread_t tid = 0;
- int pipefd[2];
- dedup_arg_t dda = { 0 };
- int featureflags = 0;
- FILE *fout;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot send '%s'"), zhp->zfs_name);
-
- if (fromsnap && fromsnap[0] == '\0') {
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "zero-length incremental source"));
- return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
- }
-
- if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) {
- uint64_t version;
- version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
- if (version >= ZPL_VERSION_SA) {
- featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
- }
- }
-
- if (flags->dedup && !flags->dryrun) {
- featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
- DMU_BACKUP_FEATURE_DEDUPPROPS);
- if ((err = pipe(pipefd)) != 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED,
- errbuf));
- }
- dda.outputfd = outfd;
- dda.inputfd = pipefd[1];
- dda.dedup_hdl = zhp->zfs_hdl;
- if ((err = pthread_create(&tid, NULL, cksummer, &dda)) != 0) {
- (void) close(pipefd[0]);
- (void) close(pipefd[1]);
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- return (zfs_error(zhp->zfs_hdl,
- EZFS_THREADCREATEFAILED, errbuf));
- }
- }
-
- if (flags->replicate || flags->doall || flags->props) {
- dmu_replay_record_t drr = { 0 };
- char *packbuf = NULL;
- size_t buflen = 0;
- zio_cksum_t zc = { 0 };
-
- if (flags->replicate || flags->props) {
- nvlist_t *hdrnv;
-
- VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
- if (fromsnap) {
- VERIFY(0 == nvlist_add_string(hdrnv,
- "fromsnap", fromsnap));
- }
- VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
- if (!flags->replicate) {
- VERIFY(0 == nvlist_add_boolean(hdrnv,
- "not_recursive"));
- }
-
- err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
- fromsnap, tosnap, flags->replicate, flags->verbose,
- flags->replicate, &fss, &fsavl);
- if (err)
- goto err_out;
- VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
- err = nvlist_pack(hdrnv, &packbuf, &buflen,
- NV_ENCODE_XDR, 0);
- if (debugnvp)
- *debugnvp = hdrnv;
- else
- nvlist_free(hdrnv);
- if (err)
- goto stderr_out;
- }
-
- if (!flags->dryrun) {
- /* write first begin record */
- drr.drr_type = DRR_BEGIN;
- drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
- drr_versioninfo, DMU_COMPOUNDSTREAM);
- DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
- drr_versioninfo, featureflags);
- (void) snprintf(drr.drr_u.drr_begin.drr_toname,
- sizeof (drr.drr_u.drr_begin.drr_toname),
- "%s@%s", zhp->zfs_name, tosnap);
- drr.drr_payloadlen = buflen;
-
- err = dump_record(&drr, packbuf, buflen, &zc, outfd);
- free(packbuf);
- if (err != 0)
- goto stderr_out;
-
- /* write end record */
- bzero(&drr, sizeof (drr));
- drr.drr_type = DRR_END;
- drr.drr_u.drr_end.drr_checksum = zc;
- err = write(outfd, &drr, sizeof (drr));
- if (err == -1) {
- err = errno;
- goto stderr_out;
- }
-
- err = 0;
- }
- }
-
- /* dump each stream */
- sdd.fromsnap = fromsnap;
- sdd.tosnap = tosnap;
- if (tid != 0)
- sdd.outfd = pipefd[0];
- else
- sdd.outfd = outfd;
- sdd.replicate = flags->replicate;
- sdd.doall = flags->doall;
- sdd.fromorigin = flags->fromorigin;
- sdd.fss = fss;
- sdd.fsavl = fsavl;
- sdd.verbose = flags->verbose;
- sdd.parsable = flags->parsable;
- sdd.progress = flags->progress;
- sdd.progressastitle = flags->progressastitle;
- sdd.dryrun = flags->dryrun;
- sdd.large_block = flags->largeblock;
- sdd.embed_data = flags->embed_data;
- sdd.compress = flags->compress;
- sdd.filter_cb = filter_func;
- sdd.filter_cb_arg = cb_arg;
- if (debugnvp)
- sdd.debugnv = *debugnvp;
- if (sdd.verbose && sdd.dryrun)
- sdd.std_out = B_TRUE;
- fout = sdd.std_out ? stdout : stderr;
-
- /*
- * Some flags require that we place user holds on the datasets that are
- * being sent so they don't get destroyed during the send. We can skip
- * this step if the pool is imported read-only since the datasets cannot
- * be destroyed.
- */
- if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp),
- ZPOOL_PROP_READONLY, NULL) &&
- zfs_spa_version(zhp, &spa_version) == 0 &&
- spa_version >= SPA_VERSION_USERREFS &&
- (flags->doall || flags->replicate)) {
- ++holdseq;
- (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
- ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
- sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
- if (sdd.cleanup_fd < 0) {
- err = errno;
- goto stderr_out;
- }
- sdd.snapholds = fnvlist_alloc();
- } else {
- sdd.cleanup_fd = -1;
- sdd.snapholds = NULL;
- }
- if (flags->progress || flags->verbose || sdd.snapholds != NULL) {
- /*
- * Do a verbose no-op dry run to get all the verbose output
- * or to gather snapshot hold's before generating any data,
- * then do a non-verbose real run to generate the streams.
- */
- sdd.dryrun = B_TRUE;
- err = dump_filesystems(zhp, &sdd);
-
- if (err != 0)
- goto stderr_out;
-
- if (flags->verbose) {
- if (flags->parsable) {
- (void) fprintf(fout, "size\t%llu\n",
- (longlong_t)sdd.size);
- } else {
- char buf[16];
- zfs_nicenum(sdd.size, buf, sizeof (buf));
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "total estimated size is %s\n"), buf);
- }
- }
-
- /* Ensure no snaps found is treated as an error. */
- if (!sdd.seento) {
- err = ENOENT;
- goto err_out;
- }
-
- /* Skip the second run if dryrun was requested. */
- if (flags->dryrun)
- goto err_out;
-
- if (sdd.snapholds != NULL) {
- err = zfs_hold_nvl(zhp, sdd.cleanup_fd, sdd.snapholds);
- if (err != 0)
- goto stderr_out;
-
- fnvlist_free(sdd.snapholds);
- sdd.snapholds = NULL;
- }
-
- sdd.dryrun = B_FALSE;
- sdd.verbose = B_FALSE;
- }
-
- err = dump_filesystems(zhp, &sdd);
- fsavl_destroy(fsavl);
- nvlist_free(fss);
-
- /* Ensure no snaps found is treated as an error. */
- if (err == 0 && !sdd.seento)
- err = ENOENT;
-
- if (tid != 0) {
- if (err != 0)
- (void) pthread_cancel(tid);
- (void) close(pipefd[0]);
- (void) pthread_join(tid, NULL);
- }
-
- if (sdd.cleanup_fd != -1) {
- VERIFY(0 == close(sdd.cleanup_fd));
- sdd.cleanup_fd = -1;
- }
-
- if (!flags->dryrun && (flags->replicate || flags->doall ||
- flags->props)) {
- /*
- * write final end record. NB: want to do this even if
- * there was some error, because it might not be totally
- * failed.
- */
- dmu_replay_record_t drr = { 0 };
- drr.drr_type = DRR_END;
- if (write(outfd, &drr, sizeof (drr)) == -1) {
- return (zfs_standard_error(zhp->zfs_hdl,
- errno, errbuf));
- }
- }
-
- return (err || sdd.err);
-
-stderr_out:
- err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);
-err_out:
- fsavl_destroy(fsavl);
- nvlist_free(fss);
- fnvlist_free(sdd.snapholds);
-
- if (sdd.cleanup_fd != -1)
- VERIFY(0 == close(sdd.cleanup_fd));
- if (tid != 0) {
- (void) pthread_cancel(tid);
- (void) close(pipefd[0]);
- (void) pthread_join(tid, NULL);
- }
- return (err);
-}
-
-int
-zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t flags)
-{
- int err = 0;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- enum lzc_send_flags lzc_flags = 0;
- FILE *fout = (flags.verbose && flags.dryrun) ? stdout : stderr;
- char errbuf[1024];
-
- if (flags.largeblock)
- lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
- if (flags.embed_data)
- lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
- if (flags.compress)
- lzc_flags |= LZC_SEND_FLAG_COMPRESS;
-
- if (flags.verbose) {
- uint64_t size = 0;
- err = lzc_send_space(zhp->zfs_name, from, lzc_flags, &size);
- if (err == 0) {
- send_print_verbose(fout, zhp->zfs_name, from, size,
- flags.parsable);
- if (flags.parsable) {
- (void) fprintf(fout, "size\t%llu\n",
- (longlong_t)size);
- } else {
- char buf[16];
- zfs_nicenum(size, buf, sizeof (buf));
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "total estimated size is %s\n"), buf);
- }
- } else {
- (void) fprintf(stderr, "Cannot estimate send size: "
- "%s\n", strerror(errno));
- }
- }
-
- if (flags.dryrun)
- return (err);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot send '%s'"), zhp->zfs_name);
-
- err = lzc_send(zhp->zfs_name, from, fd, lzc_flags);
- if (err != 0) {
- switch (errno) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- case ESRCH:
- if (lzc_exists(zhp->zfs_name)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (%s) does not exist"),
- from);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "target is busy; if a filesystem, "
- "it must not be mounted"));
- return (zfs_error(hdl, EZFS_BUSY, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
-#ifdef illumos
- case ENOSTR:
-#endif
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
- return (err != 0);
-}
-
-/*
- * Routines specific to "zfs recv"
- */
-
-static int
-recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
- boolean_t byteswap, zio_cksum_t *zc)
-{
- char *cp = buf;
- int rv;
- int len = ilen;
-
- assert(ilen <= SPA_MAXBLOCKSIZE);
-
- do {
- rv = read(fd, cp, len);
- cp += rv;
- len -= rv;
- } while (rv > 0);
-
- if (rv < 0 || len != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to read from stream"));
- return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN,
- "cannot receive")));
- }
-
- if (zc) {
- if (byteswap)
- (void) fletcher_4_incremental_byteswap(buf, ilen, zc);
- else
- (void) fletcher_4_incremental_native(buf, ilen, zc);
- }
- return (0);
-}
-
-static int
-recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
- boolean_t byteswap, zio_cksum_t *zc)
-{
- char *buf;
- int err;
-
- buf = zfs_alloc(hdl, len);
- if (buf == NULL)
- return (ENOMEM);
-
- err = recv_read(hdl, fd, buf, len, byteswap, zc);
- if (err != 0) {
- free(buf);
- return (err);
- }
-
- err = nvlist_unpack(buf, len, nvp, 0);
- free(buf);
- if (err != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (malformed nvlist)"));
- return (EINVAL);
- }
- return (0);
-}
-
-static int
-recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
- int baselen, char *newname, recvflags_t *flags)
-{
- static int seq;
- int err;
- prop_changelist_t *clp;
- zfs_handle_t *zhp;
-
- zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (-1);
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags->force ? MS_FORCE : 0);
- zfs_close(zhp);
- if (clp == NULL)
- return (-1);
- err = changelist_prefix(clp);
- if (err)
- return (err);
-
- if (tryname) {
- (void) strcpy(newname, tryname);
- if (flags->verbose) {
- (void) printf("attempting rename %s to %s\n",
- name, newname);
- }
- err = lzc_rename(name, newname);
- if (err == 0)
- changelist_rename(clp, name, tryname);
- } else {
- err = ENOENT;
- }
-
- if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
- seq++;
-
- (void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
- "%.*srecv-%u-%u", baselen, name, getpid(), seq);
- if (flags->verbose) {
- (void) printf("failed - trying rename %s to %s\n",
- name, newname);
- }
- err = lzc_rename(name, newname);
- if (err == 0)
- changelist_rename(clp, name, newname);
- if (err && flags->verbose) {
- (void) printf("failed (%u) - "
- "will try again on next pass\n", errno);
- }
- err = EAGAIN;
- } else if (flags->verbose) {
- if (err == 0)
- (void) printf("success\n");
- else
- (void) printf("failed (%u)\n", errno);
- }
-
- (void) changelist_postfix(clp);
- changelist_free(clp);
-
- return (err);
-}
-
-static int
-recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
- char *newname, recvflags_t *flags)
-{
- int err = 0;
- prop_changelist_t *clp;
- zfs_handle_t *zhp;
- boolean_t defer = B_FALSE;
- int spa_version;
-
- zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (-1);
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags->force ? MS_FORCE : 0);
- if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
- zfs_spa_version(zhp, &spa_version) == 0 &&
- spa_version >= SPA_VERSION_USERREFS)
- defer = B_TRUE;
- zfs_close(zhp);
- if (clp == NULL)
- return (-1);
- err = changelist_prefix(clp);
- if (err)
- return (err);
-
- if (flags->verbose)
- (void) printf("attempting destroy %s\n", name);
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- nvlist_t *nv = fnvlist_alloc();
- fnvlist_add_boolean(nv, name);
- err = lzc_destroy_snaps(nv, defer, NULL);
- fnvlist_free(nv);
- } else {
- err = lzc_destroy(name);
- }
- if (err == 0) {
- if (flags->verbose)
- (void) printf("success\n");
- changelist_remove(clp, name);
- }
-
- (void) changelist_postfix(clp);
- changelist_free(clp);
-
- /*
- * Deferred destroy might destroy the snapshot or only mark it to be
- * destroyed later, and it returns success in either case.
- */
- if (err != 0 || (defer && zfs_dataset_exists(hdl, name,
- ZFS_TYPE_SNAPSHOT))) {
- err = recv_rename(hdl, name, NULL, baselen, newname, flags);
- }
-
- return (err);
-}
-
-typedef struct guid_to_name_data {
- uint64_t guid;
- boolean_t bookmark_ok;
- char *name;
- char *skip;
-} guid_to_name_data_t;
-
-static int
-guid_to_name_cb(zfs_handle_t *zhp, void *arg)
-{
- guid_to_name_data_t *gtnd = arg;
- const char *slash;
- int err;
-
- if (gtnd->skip != NULL &&
- (slash = strrchr(zhp->zfs_name, '/')) != NULL &&
- strcmp(slash + 1, gtnd->skip) == 0) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) {
- (void) strcpy(gtnd->name, zhp->zfs_name);
- zfs_close(zhp);
- return (EEXIST);
- }
-
- err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
- if (err != EEXIST && gtnd->bookmark_ok)
- err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd);
- zfs_close(zhp);
- return (err);
-}
-
-/*
- * Attempt to find the local dataset associated with this guid. In the case of
- * multiple matches, we attempt to find the "best" match by searching
- * progressively larger portions of the hierarchy. This allows one to send a
- * tree of datasets individually and guarantee that we will find the source
- * guid within that hierarchy, even if there are multiple matches elsewhere.
- */
-static int
-guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
- boolean_t bookmark_ok, char *name)
-{
- char pname[ZFS_MAX_DATASET_NAME_LEN];
- guid_to_name_data_t gtnd;
-
- gtnd.guid = guid;
- gtnd.bookmark_ok = bookmark_ok;
- gtnd.name = name;
- gtnd.skip = NULL;
-
- /*
- * Search progressively larger portions of the hierarchy, starting
- * with the filesystem specified by 'parent'. This will
- * select the "most local" version of the origin snapshot in the case
- * that there are multiple matching snapshots in the system.
- */
- (void) strlcpy(pname, parent, sizeof (pname));
- char *cp = strrchr(pname, '@');
- if (cp == NULL)
- cp = strchr(pname, '\0');
- for (; cp != NULL; cp = strrchr(pname, '/')) {
- /* Chop off the last component and open the parent */
- *cp = '\0';
- zfs_handle_t *zhp = make_dataset_handle(hdl, pname);
-
- if (zhp == NULL)
- continue;
- int err = guid_to_name_cb(zfs_handle_dup(zhp), &gtnd);
- if (err != EEXIST)
- err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
- if (err != EEXIST && bookmark_ok)
- err = zfs_iter_bookmarks(zhp, guid_to_name_cb, &gtnd);
- zfs_close(zhp);
- if (err == EEXIST)
- return (0);
-
- /*
- * Remember the last portion of the dataset so we skip it next
- * time through (as we've already searched that portion of the
- * hierarchy).
- */
- gtnd.skip = strrchr(pname, '/') + 1;
- }
-
- return (ENOENT);
-}
-
-/*
- * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
- * guid1 is after guid2.
- */
-static int
-created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
- uint64_t guid1, uint64_t guid2)
-{
- nvlist_t *nvfs;
- char *fsname, *snapname;
- char buf[ZFS_MAX_DATASET_NAME_LEN];
- int rv;
- zfs_handle_t *guid1hdl, *guid2hdl;
- uint64_t create1, create2;
-
- if (guid2 == 0)
- return (0);
- if (guid1 == 0)
- return (1);
-
- nvfs = fsavl_find(avl, guid1, &snapname);
- VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
- (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid1hdl == NULL)
- return (-1);
-
- nvfs = fsavl_find(avl, guid2, &snapname);
- VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
- (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid2hdl == NULL) {
- zfs_close(guid1hdl);
- return (-1);
- }
-
- create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
- create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
-
- if (create1 < create2)
- rv = -1;
- else if (create1 > create2)
- rv = +1;
- else
- rv = 0;
-
- zfs_close(guid1hdl);
- zfs_close(guid2hdl);
-
- return (rv);
-}
-
-static int
-recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
- recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
- nvlist_t *renamed)
-{
- nvlist_t *local_nv, *deleted = NULL;