diff options
author | eschrock <none@none> | 2006-05-30 15:47:16 -0700 |
---|---|---|
committer | eschrock <none@none> | 2006-05-30 15:47:16 -0700 |
commit | 99653d4ee642c6528e88224f12409a5f23060994 (patch) | |
tree | 5cbcc540b8ed86b6a008f1084f9ca031368d926f /usr/src/lib | |
parent | 354a1801a85aa6b61ff4d5e290ab708ba57e56a3 (diff) | |
download | illumos-joyent-99653d4ee642c6528e88224f12409a5f23060994.tar.gz |
PSARC 2006/223 ZFS Hot Spares
PSARC 2006/303 ZFS Clone Promotion
6276916 support for "clone swap"
6288488 du reports misleading size on RAID-Z
6393490 libzfs should be a real library
6397148 fbufs debug code should be removed from buf_hash_insert()
6405966 Hot Spare support in ZFS
6409302 passing a non-root vdev via zpool_create() panics system
6415739 assertion failed: !(zio->io_flags & 0x00040)
6416759 ::dbufs does not find bonus buffers anymore
6417978 double parity RAID-Z a.k.a. RAID6
6424554 full block re-writes need not read data in
6425111 detaching an offline device can result in import confusion
Diffstat (limited to 'usr/src/lib')
20 files changed, 2490 insertions, 2048 deletions
diff --git a/usr/src/lib/libdiskmgt/common/entry.c b/usr/src/lib/libdiskmgt/common/entry.c index 860801b41d..61bc9d60d4 100644 --- a/usr/src/lib/libdiskmgt/common/entry.c +++ b/usr/src/lib/libdiskmgt/common/entry.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -966,6 +965,10 @@ dm_get_usage_string(char *what, char *how, char **usage_string) *usage_string = dgettext(TEXT_DOMAIN, "%s is part of active ZFS pool %s. Please see zpool(1M)." "\n"); + } else if (strcmp(what, DM_USE_SPARE_ZPOOL) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is reserved as a hot spare for ZFS pool %s. Please " + "see zpool(1M).\n"); } } void diff --git a/usr/src/lib/libdiskmgt/common/inuse_zpool.c b/usr/src/lib/libdiskmgt/common/inuse_zpool.c index 1637ace92d..a7cf203a2f 100644 --- a/usr/src/lib/libdiskmgt/common/inuse_zpool.c +++ b/usr/src/lib/libdiskmgt/common/inuse_zpool.c @@ -46,17 +46,21 @@ #include <ctype.h> #include <sys/fs/zfs.h> +#include <libzfs.h> #include "libdiskmgt.h" #include "disks_private.h" /* * Pointers to libzfs.so functions that we dynamically resolve. */ -static int (*zfsdl_zpool_in_use)(int fd, pool_state_t *state, char **name); +static int (*zfsdl_zpool_in_use)(libzfs_handle_t *hdl, int fd, + pool_state_t *state, char **name, boolean_t *); +static libzfs_handle_t *(*zfsdl_libzfs_init)(boolean_t); static mutex_t init_lock = DEFAULTMUTEX; static rwlock_t zpool_lock = DEFAULTRWLOCK; -static int initialized = 0; +static boolean_t initialized; +static libzfs_handle_t *zfs_hdl; static void *init_zpool(); @@ -67,6 +71,7 @@ inuse_zpool_common(char *slice, nvlist_t *attrs, int *errp, char *type) char *name; int fd; pool_state_t state; + boolean_t used; *errp = 0; if (slice == NULL) { @@ -83,15 +88,21 @@ inuse_zpool_common(char *slice, nvlist_t *attrs, int *errp, char *type) (void) mutex_unlock(&init_lock); return (found); } - initialized = 1; + initialized = B_TRUE; } (void) mutex_unlock(&init_lock); (void) rw_rdlock(&zpool_lock); if ((fd = open(slice, O_RDONLY)) > 0) { - if (zfsdl_zpool_in_use(fd, &state, &name)) { + name = NULL; + if (zfsdl_zpool_in_use(zfs_hdl, fd, &state, + &name, &used) == 0 && used) { if (strcmp(type, DM_USE_ACTIVE_ZPOOL) == 0) { - if (state == POOL_STATE_ACTIVE) + if (state == POOL_STATE_ACTIVE) { found = 1; + } else if (state == POOL_STATE_SPARE) { + found = 1; + type = DM_USE_SPARE_ZPOOL; + } } else { found = 1; } @@ -100,9 +111,11 @@ inuse_zpool_common(char *slice, nvlist_t *attrs, int *errp, char *type) libdiskmgt_add_str(attrs, DM_USED_BY, type, errp); libdiskmgt_add_str(attrs, DM_USED_NAME, - name, errp); + name, errp); } } + if (name) + free(name); (void) close(fd); } (void) rw_unlock(&zpool_lock); @@ -133,15 +146,24 @@ init_zpool() if ((lh = dlopen("libzfs.so", RTLD_NOW)) == NULL) { return (lh); } + /* * Instantiate the functions needed to get zpool configuration * data */ - if ((zfsdl_zpool_in_use = (int (*)(int, pool_state_t *, char **)) + if ((zfsdl_libzfs_init = (libzfs_handle_t *(*)(boolean_t)) + dlsym(lh, "libzfs_init")) == NULL || + (zfsdl_zpool_in_use = (int (*)(libzfs_handle_t *, int, + pool_state_t *, char **, boolean_t *)) dlsym(lh, "zpool_in_use")) == NULL) { (void) dlclose(lh); return (NULL); } + if ((zfs_hdl = (*zfsdl_libzfs_init)(B_FALSE)) == NULL) { + (void) dlclose(lh); + return (NULL); + } + return (lh); } diff --git a/usr/src/lib/libdiskmgt/common/libdiskmgt.h b/usr/src/lib/libdiskmgt/common/libdiskmgt.h index aa6df0967e..7d6fef46d4 100644 --- a/usr/src/lib/libdiskmgt/common/libdiskmgt.h +++ b/usr/src/lib/libdiskmgt/common/libdiskmgt.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -215,6 +214,7 @@ typedef enum { #define DM_USE_VFSTAB "vfstab" #define DM_USE_EXPORTED_ZPOOL "exported_zpool" #define DM_USE_ACTIVE_ZPOOL "active_zpool" +#define DM_USE_SPARE_ZPOOL "spare_zpool" /* event */ #define DM_EV_NAME "name" diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 0044ccd7c9..bf4b2874ad 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -47,16 +47,78 @@ extern "C" { #define ZFS_MAXPROPLEN MAXPATHLEN /* + * libzfs errors + */ +enum { + 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_VOLHASDATA, /* volume already contains data */ + 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_UNSHAREFAILED, /* unshare(1M) failed */ + EZFS_SHAREFAILED, /* share(1M) failed */ + EZFS_DEVLINKS, /* failed to create zvol links */ + EZFS_PERM, /* permission denied */ + EZFS_NOSPC, /* out of space */ + EZFS_IO, /* I/O error */ + EZFS_INTR, /* signal received */ + EZFS_ISSPARE, /* device is a hot spare */ + EZFS_INVALCONFIG, /* invalid vdev configuration */ + EZFS_UNKNOWN /* unknown error */ +}; + +/* * 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 int libzfs_errno(libzfs_handle_t *); +extern const char *libzfs_error_action(libzfs_handle_t *); +extern const char *libzfs_error_description(libzfs_handle_t *); /* * Basic handle functions */ -extern zpool_handle_t *zpool_open(const char *); -extern zpool_handle_t *zpool_open_canfail(const char *); +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 uint64_t zpool_get_guid(zpool_handle_t *); @@ -64,17 +126,19 @@ extern uint64_t zpool_get_space_used(zpool_handle_t *); extern uint64_t zpool_get_space_total(zpool_handle_t *); extern int zpool_get_root(zpool_handle_t *, char *, size_t); extern int zpool_get_state(zpool_handle_t *); +extern uint64_t zpool_get_version(zpool_handle_t *); /* * Iterate over all active pools in the system. */ typedef int (*zpool_iter_f)(zpool_handle_t *, void *); -extern int zpool_iter(zpool_iter_f, void *); +extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *); /* * Functions to create and destroy pools */ -extern int zpool_create(const char *, nvlist_t *, const char *); +extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *, + const char *); extern int zpool_destroy(zpool_handle_t *); extern int zpool_add(zpool_handle_t *, nvlist_t *); @@ -88,8 +152,9 @@ extern int zpool_vdev_offline(zpool_handle_t *, const char *, int); 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_clear(zpool_handle_t *, const char *); -extern uint64_t zpool_vdev_to_guid(zpool_handle_t *, const char *); +extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *); /* * Pool health statistics. @@ -143,24 +208,25 @@ extern int zpool_get_errlog(zpool_handle_t *, nvlist_t ***, size_t *); * Import and export functions */ extern int zpool_export(zpool_handle_t *); -extern int zpool_import(nvlist_t *, const char *, const char *); +extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *, + const char *); /* * Search for pools to import */ -extern nvlist_t *zpool_find_import(int, char **); +extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **); /* * Miscellaneous pool functions */ -extern char *zpool_vdev_name(zpool_handle_t *, nvlist_t *); +extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *); extern int zpool_upgrade(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(const char *, int); +extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int); 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 *); @@ -182,11 +248,11 @@ typedef enum { const char *zfs_prop_to_name(zfs_prop_t); int zfs_prop_set(zfs_handle_t *, zfs_prop_t, const char *); int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zfs_source_t *, - char *, size_t, int); + char *, size_t, boolean_t); int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, zfs_source_t *, char *, size_t); uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t); -int zfs_prop_validate(zfs_prop_t, const char *, uint64_t *); +int zfs_prop_validate(libzfs_handle_t *, zfs_prop_t, const char *, uint64_t *); int zfs_prop_inheritable(zfs_prop_t); int zfs_prop_inherit(zfs_handle_t *, zfs_prop_t); const char *zfs_prop_values(zfs_prop_t); @@ -206,7 +272,7 @@ int zfs_get_proplist(char *fields, zfs_prop_t *proplist, int max, int *count, * Iterator functions. */ typedef int (*zfs_iter_f)(zfs_handle_t *, void *); -extern int zfs_iter_root(zfs_iter_f, 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 *, zfs_iter_f, void *); extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *); @@ -215,14 +281,16 @@ extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *); /* * Functions to create and destroy datasets. */ -extern int zfs_create(const char *, zfs_type_t, const char *, const char *); +extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t, + const char *, const char *); extern int zfs_destroy(zfs_handle_t *); extern int zfs_clone(zfs_handle_t *, const char *); -extern int zfs_snapshot(const char *); +extern int zfs_snapshot(libzfs_handle_t *, const char *); extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, int); extern int zfs_rename(zfs_handle_t *, const char *); extern int zfs_send(zfs_handle_t *, zfs_handle_t *); -extern int zfs_receive(const char *, int, int, int); +extern int zfs_receive(libzfs_handle_t *, const char *, int, int, int); +extern int zfs_promote(zfs_handle_t *); /* * Miscellaneous functions. @@ -234,7 +302,7 @@ extern int zfs_name_valid(const char *, zfs_type_t); /* * Mount support functions. */ -extern int zfs_is_mounted(zfs_handle_t *, char **); +extern boolean_t zfs_is_mounted(zfs_handle_t *, char **); extern int zfs_mount(zfs_handle_t *, const char *, int); extern int zfs_unmount(zfs_handle_t *, const char *, int); extern int zfs_unmountall(zfs_handle_t *, int); @@ -242,17 +310,12 @@ extern int zfs_unmountall(zfs_handle_t *, int); /* * Share support functions. */ -extern int zfs_is_shared(zfs_handle_t *, char **); +extern boolean_t zfs_is_shared(zfs_handle_t *, char **); extern int zfs_share(zfs_handle_t *); extern int zfs_unshare(zfs_handle_t *, const char *); extern int zfs_unshareall(zfs_handle_t *); /* - * For clients that need to capture error output. - */ -extern void zfs_set_error_handler(void (*)(const char *, va_list)); - -/* * When dealing with nvlists, verify() is extremely useful */ #ifdef NDEBUG @@ -276,12 +339,13 @@ extern int zfs_remove_link(zfs_handle_t *); /* * Given a device or file, determine if it is part of a pool. */ -extern int zpool_in_use(int fd, pool_state_t *state, char **name); +extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **, + boolean_t *); /* * ftyp special. Read the label from a given device. */ -extern nvlist_t *zpool_read_label(int fd); +extern int zpool_read_label(int, nvlist_t **); /* * Create and remove zvol /dev links @@ -289,21 +353,6 @@ extern nvlist_t *zpool_read_label(int fd); extern int zpool_create_zvol_links(zpool_handle_t *); extern int zpool_remove_zvol_links(zpool_handle_t *); -/* - * zoneadmd hack - */ -extern void zfs_init(void); - -/* - * Useful defines - */ -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef FALSE -#define FALSE 0 -#endif - #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c index 57fcc1497c..04270dfe51 100644 --- a/usr/src/lib/libzfs/common/libzfs_changelist.c +++ b/usr/src/lib/libzfs/common/libzfs_changelist.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -73,11 +72,11 @@ struct prop_changelist { zfs_prop_t cl_realprop; uu_list_pool_t *cl_pool; uu_list_t *cl_list; - int cl_waslegacy; - int cl_allchildren; - int cl_alldependents; + boolean_t cl_waslegacy; + boolean_t cl_allchildren; + boolean_t cl_alldependents; int cl_flags; - int cl_haszonedchild; + boolean_t cl_haszonedchild; }; /* @@ -109,7 +108,8 @@ changelist_prefix(prop_changelist_t *clp) */ if (cn->cn_handle->zfs_volblocksize && clp->cl_realprop == ZFS_PROP_NAME) { - if (zvol_remove_link(cn->cn_handle->zfs_name) != 0) + if (zvol_remove_link(cn->cn_handle->zfs_hdl, + cn->cn_handle->zfs_name) != 0) ret = -1; } else if (zfs_unmount(cn->cn_handle, NULL, clp->cl_flags) != 0) ret = -1; @@ -167,7 +167,8 @@ changelist_postfix(prop_changelist_t *clp) */ if (cn->cn_handle->zfs_volblocksize && clp->cl_realprop == ZFS_PROP_NAME) { - if (zvol_create_link(cn->cn_handle->zfs_name) != 0) + if (zvol_create_link(cn->cn_handle->zfs_hdl, + cn->cn_handle->zfs_name) != 0) ret = -1; continue; } @@ -186,7 +187,7 @@ changelist_postfix(prop_changelist_t *clp) char shareopts[ZFS_MAXPROPLEN]; if (zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), NULL, NULL, 0, - FALSE) == 0 && strcmp(shareopts, "off") == 0) + B_FALSE) == 0 && strcmp(shareopts, "off") == 0) ret = zfs_unshare(cn->cn_handle, NULL); else ret = zfs_share(cn->cn_handle); @@ -199,22 +200,22 @@ changelist_postfix(prop_changelist_t *clp) /* * Is this "dataset" a child of "parent"? */ -static int +static boolean_t isa_child_of(char *dataset, const char *parent) { int len; /* snapshot does not have a child */ if (strchr(parent, '@')) - return (FALSE); + return (B_FALSE); len = strlen(parent); if (strncmp(dataset, parent, len) == 0 && (dataset[len] == '/' || dataset[len] == '\0')) - return (TRUE); + return (B_TRUE); else - return (FALSE); + return (B_FALSE); } @@ -326,6 +327,9 @@ changelist_free(prop_changelist_t *clp) free(cn); } + uu_list_walk_end(walk); + + uu_list_destroy(clp->cl_list); uu_list_pool_destroy(clp->cl_pool); free(clp); @@ -353,12 +357,18 @@ change_one(zfs_handle_t *zhp, void *data) if (!(zhp->zfs_volblocksize && clp->cl_realprop == ZFS_PROP_NAME) && zfs_prop_get(zhp, clp->cl_prop, property, sizeof (property), &sourcetype, where, sizeof (where), - FALSE) != 0) + B_FALSE) != 0) { + zfs_close(zhp); return (0); + } if (clp->cl_alldependents || clp->cl_allchildren || sourcetype == ZFS_SRC_DEFAULT || sourcetype == ZFS_SRC_INHERITED) { - cn = zfs_malloc(sizeof (prop_changenode_t)); + 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 = zfs_is_mounted(zhp, NULL); @@ -367,7 +377,7 @@ change_one(zfs_handle_t *zhp, void *data) /* indicate if any child is exported to a local zone */ if ((getzoneid() == GLOBAL_ZONEID) && cn->cn_zoned) - clp->cl_haszonedchild = TRUE; + clp->cl_haszonedchild = B_TRUE; uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); @@ -399,11 +409,14 @@ change_one(zfs_handle_t *zhp, void *data) prop_changelist_t * changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) { - prop_changelist_t *clp = zfs_malloc(sizeof (prop_changelist_t)); + prop_changelist_t *clp; prop_changenode_t *cn; zfs_handle_t *temp; char property[ZFS_MAXPROPLEN]; + if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) + return (NULL); + clp->cl_pool = uu_list_pool_create("changelist_pool", sizeof (prop_changenode_t), offsetof(prop_changenode_t, cn_listnode), @@ -423,10 +436,10 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) */ if (prop == ZFS_PROP_NAME) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; - clp->cl_alldependents = TRUE; + clp->cl_alldependents = B_TRUE; } else if (prop == ZFS_PROP_ZONED) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; - clp->cl_allchildren = TRUE; + clp->cl_allchildren = B_TRUE; } else { clp->cl_prop = prop; } @@ -450,8 +463,9 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) * We have to re-open ourselves because we auto-close all the handles * and can't tell the difference. */ - if ((temp = zfs_open(zfs_get_name(zhp), ZFS_TYPE_ANY)) == NULL) { - free(clp); + if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), + ZFS_TYPE_ANY)) == NULL) { + changelist_free(clp); return (NULL); } @@ -459,7 +473,13 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) * Always add ourself to the list. We add ourselves to the end so that * we're the last to be unmounted. */ - cn = zfs_malloc(sizeof (prop_changenode_t)); + 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 = zfs_is_mounted(temp, NULL); cn->cn_shared = zfs_is_shared(temp, NULL); @@ -474,10 +494,10 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) * as the behavior of changelist_postfix() will be different. */ if (zfs_prop_get(zhp, prop, property, sizeof (property), - NULL, NULL, 0, FALSE) == 0 && + NULL, NULL, 0, B_FALSE) == 0 && (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0 || strcmp(property, "off") == 0)) - clp->cl_waslegacy = TRUE; + clp->cl_waslegacy = B_TRUE; return (clp); } diff --git a/usr/src/lib/libzfs/common/libzfs_config.c b/usr/src/lib/libzfs/common/libzfs_config.c index 71801d5cba..be691f0ced 100644 --- a/usr/src/lib/libzfs/common/libzfs_config.c +++ b/usr/src/lib/libzfs/common/libzfs_config.c @@ -45,9 +45,6 @@ #include "libzfs_impl.h" -static uu_avl_t *namespace_avl; -static uint64_t namespace_generation; - typedef struct config_node { char *cn_name; nvlist_t *cn_config; @@ -73,11 +70,41 @@ config_node_compare(const void *a, const void *b, void *unused) return (0); } +void +namespace_clear(libzfs_handle_t *hdl) +{ + if (hdl->libzfs_ns_avl) { + uu_avl_walk_t *walk; + config_node_t *cn; + + if ((walk = uu_avl_walk_start(hdl->libzfs_ns_avl, + UU_WALK_ROBUST)) == NULL) + return; + + while ((cn = uu_avl_walk_next(walk)) != NULL) { + uu_avl_remove(hdl->libzfs_ns_avl, cn); + nvlist_free(cn->cn_config); + free(cn->cn_name); + free(cn); + } + + uu_avl_walk_end(walk); + + 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 void -namespace_reload() +static int +namespace_reload(libzfs_handle_t *hdl) { nvlist_t *config; config_node_t *cn; @@ -85,23 +112,21 @@ namespace_reload() zfs_cmd_t zc = { 0 }; uu_avl_walk_t *walk; - if (namespace_generation == 0) { + 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. */ - uu_avl_pool_t *pool; - - if ((pool = uu_avl_pool_create("config_pool", + 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) - no_memory(); + return (no_memory(hdl)); - if ((namespace_avl = uu_avl_create(pool, NULL, - UU_DEFAULT)) == NULL) - no_memory(); + if ((hdl->libzfs_ns_avl = uu_avl_create(hdl->libzfs_ns_avlpool, + NULL, UU_DEFAULT)) == NULL) + return (no_memory(hdl)); } /* @@ -114,68 +139,92 @@ namespace_reload() * been modified to tell us how much to allocate. */ zc.zc_config_dst_size = 1024; - zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uint64_t)(uintptr_t) + zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL) + return (-1); for (;;) { - zc.zc_cookie = namespace_generation; - if (zfs_ioctl(ZFS_IOC_POOL_CONFIGS, &zc) != 0) { + 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. */ free((void *)(uintptr_t)zc.zc_config_dst); - return; + return (0); case ENOMEM: free((void *)(uintptr_t)zc.zc_config_dst); - zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uint64_t)(uintptr_t) + zfs_alloc(hdl, zc.zc_config_dst_size)) + == NULL) + return (-1); break; default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, "failed to read " + "pool configuration"))); } } else { - namespace_generation = zc.zc_cookie; + hdl->libzfs_ns_gen = zc.zc_cookie; break; } } - verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, - zc.zc_config_dst_size, &config, 0) == 0); + if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, + zc.zc_config_dst_size, &config, 0) != 0) + return (no_memory(hdl)); free((void *)(uintptr_t)zc.zc_config_dst); /* * Clear out any existing configuration information. */ - if ((walk = uu_avl_walk_start(namespace_avl, UU_WALK_ROBUST)) == NULL) - no_memory(); + if ((walk = uu_avl_walk_start(hdl->libzfs_ns_avl, + UU_WALK_ROBUST)) == NULL) { + nvlist_free(config); + return (no_memory(hdl)); + } while ((cn = uu_avl_walk_next(walk)) != NULL) { - uu_avl_remove(namespace_avl, cn); + uu_avl_remove(hdl->libzfs_ns_avl, cn); nvlist_free(cn->cn_config); free(cn->cn_name); free(cn); } + uu_avl_walk_end(walk); + elem = NULL; while ((elem = nvlist_next_nvpair(config, elem)) != NULL) { nvlist_t *child; uu_avl_index_t where; - cn = zfs_malloc(sizeof (config_node_t)); - cn->cn_name = zfs_strdup(nvpair_name(elem)); + 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); + return (-1); + } verify(nvpair_value_nvlist(elem, &child) == 0); - verify(nvlist_dup(child, &cn->cn_config, 0) == 0); - verify(uu_avl_find(namespace_avl, cn, NULL, &where) == NULL); + if (nvlist_dup(child, &cn->cn_config, 0) != 0) { + nvlist_free(config); + return (no_memory(hdl)); + } + verify(uu_avl_find(hdl->libzfs_ns_avl, cn, NULL, &where) + == NULL); - uu_avl_insert(namespace_avl, cn, where); + uu_avl_insert(hdl->libzfs_ns_avl, cn, where); } nvlist_free(config); + return (0); } /* @@ -209,35 +258,43 @@ zpool_refresh_stats(zpool_handle_t *zhp) zhp->zpool_config_size = 1 << 16; zc.zc_config_dst_size = zhp->zpool_config_size; - zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uint64_t)(uintptr_t) + zfs_alloc(zhp->zpool_hdl, zc.zc_config_dst_size)) == NULL) + return (-1); for (;;) { - if (zfs_ioctl(ZFS_IOC_POOL_STATS, &zc) == 0) { + 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; + error = errno = zc.zc_cookie; break; } if (errno == ENOMEM) { free((void *)(uintptr_t)zc.zc_config_dst); - zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uint64_t)(uintptr_t) + zfs_alloc(zhp->zpool_hdl, + zc.zc_config_dst_size)) == NULL) + return (-1); } else { free((void *)(uintptr_t)zc.zc_config_dst); - return (errno); + return (-1); } } - verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, - zc.zc_config_dst_size, &config, 0) == 0); + if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, + zc.zc_config_dst_size, &config, 0) != 0) { + free((void *)(uintptr_t)zc.zc_config_dst); + return (no_memory(zhp->zpool_hdl)); + } zhp->zpool_config_size = zc.zc_config_dst_size; free((void *)(uintptr_t)zc.zc_config_dst); - set_pool_health(config); + if (set_pool_health(config) != 0) + return (no_memory(zhp->zpool_hdl)); if (zhp->zpool_config != NULL) { uint64_t oldtxg, newtxg; @@ -260,25 +317,26 @@ zpool_refresh_stats(zpool_handle_t *zhp) zhp->zpool_config = config; - return (error); + return (error ? -1 : 0); } /* * Iterate over all pools in the system. */ int -zpool_iter(zpool_iter_f func, void *data) +zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data) { config_node_t *cn; zpool_handle_t *zhp; int ret; - namespace_reload(); + if (namespace_reload(hdl) != 0) + return (-1); - for (cn = uu_avl_first(namespace_avl); cn != NULL; - cn = uu_avl_next(namespace_avl, cn)) { + for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL; + cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) { - if ((zhp = zpool_open_silent(cn->cn_name)) == NULL) + if ((zhp = zpool_open_silent(hdl, cn->cn_name)) == NULL) continue; if ((ret = func(zhp, data)) != 0) @@ -293,18 +351,19 @@ zpool_iter(zpool_iter_f func, void *data) * handle passed each time must be explicitly closed by the callback. */ int -zfs_iter_root(zfs_iter_f func, void *data) +zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data) { config_node_t *cn; zfs_handle_t *zhp; int ret; - namespace_reload(); + if (namespace_reload(hdl) != 0) + return (-1); - for (cn = uu_avl_first(namespace_avl); cn != NULL; - cn = uu_avl_next(namespace_avl, cn)) { + for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL; + cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) { - if ((zhp = make_dataset_handle(cn->cn_name)) == NULL) + if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL) continue; if ((ret = func(zhp, data)) != 0) diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index f23136c8aa..14ba6112ed 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -36,6 +36,7 @@ #include <strings.h> #include <unistd.h> #include <zone.h> +#include <fcntl.h> #include <sys/mntent.h> #include <sys/mnttab.h> #include <sys/mount.h> @@ -64,7 +65,6 @@ zfs_type_to_name(zfs_type_t type) return (dgettext(TEXT_DOMAIN, "volume")); } - zfs_baderror(type); return (NULL); } @@ -118,43 +118,43 @@ path_to_str(const char *path, int types) * 'buf' detailing exactly why the name was not valid. */ static int -zfs_validate_name(const char *path, int type, char *buf, size_t buflen) +zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type) { namecheck_err_t why; char what; if (dataset_namecheck(path, &why, &what) != 0) { - if (buf != NULL) { + if (hdl != NULL) { switch (why) { case NAME_ERR_TOOLONG: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "name is too long"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "name is too long")); break; case NAME_ERR_LEADING_SLASH: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "leading slash"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "leading slash in name")); break; case NAME_ERR_EMPTY_COMPONENT: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "empty component"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "empty component in name")); break; case NAME_ERR_TRAILING_SLASH: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "trailing slash"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "trailing slash in name")); break; case NAME_ERR_INVALCHAR: - (void) snprintf(buf, buflen, + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid character " - "'%c'"), what); + "'%c' in name"), what); break; case NAME_ERR_MULTIPLE_AT: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "multiple '@' delimiters"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "multiple '@' delimiters in name")); break; } } @@ -163,20 +163,19 @@ zfs_validate_name(const char *path, int type, char *buf, size_t buflen) } if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) { - if (buf != NULL) - (void) strlcpy(buf, - dgettext(TEXT_DOMAIN, - "snapshot delimiter '@'"), buflen); + if (hdl != NULL) + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "snapshot delimiter '@' in filesystem name")); return (0); } - return (1); + return (-1); } int zfs_name_valid(const char *name, zfs_type_t type) { - return (zfs_validate_name(name, type, NULL, NULL)); + return (zfs_validate_name(NULL, name, type)); } /* @@ -189,13 +188,16 @@ get_stats(zfs_handle_t *zhp) (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - zc.zc_config_src = (uint64_t)(uintptr_t)zfs_malloc(1024); + if ((zc.zc_config_src = (uint64_t)(uintptr_t)malloc(1024)) == NULL) + return (-1); zc.zc_config_src_size = 1024; - while (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) != 0) { + while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) { if (errno == ENOMEM) { - zc.zc_config_src = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_src_size); + free((void *)(uintptr_t)zc.zc_config_src); + if ((zc.zc_config_src = (uint64_t)(uintptr_t) + malloc(zc.zc_config_src_size)) == NULL) + return (-1); } else { free((void *)(uintptr_t)zc.zc_config_src); return (-1); @@ -207,12 +209,22 @@ get_stats(zfs_handle_t *zhp) (void) strcpy(zhp->zfs_root, zc.zc_root); - verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_src, - zc.zc_config_src_size, &zhp->zfs_props, 0) == 0); + if (zhp->zfs_props) { + nvlist_free(zhp->zfs_props); + zhp->zfs_props = NULL; + } + + if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_src, + zc.zc_config_src_size, &zhp->zfs_props, 0) != 0) { + free((void *)(uintptr_t)zc.zc_config_src); + return (-1); + } zhp->zfs_volsize = zc.zc_volsize; zhp->zfs_volblocksize = zc.zc_volblocksize; + free((void *)(uintptr_t)zc.zc_config_src); + return (0); } @@ -230,9 +242,14 @@ zfs_refresh_properties(zfs_handle_t *zhp) * zfs_iter_* to create child handles on the fly. */ zfs_handle_t * -make_dataset_handle(const char *path) +make_dataset_handle(libzfs_handle_t *hdl, const char *path) { - zfs_handle_t *zhp = zfs_malloc(sizeof (zfs_handle_t)); + zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1); + + if (zhp == NULL) + return (NULL); + + zhp->zfs_hdl = hdl; top: (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); @@ -263,20 +280,20 @@ top: (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); if (zhp->zfs_type == ZFS_TYPE_VOLUME) { - (void) zvol_remove_link(zhp->zfs_name); + (void) zvol_remove_link(hdl, zhp->zfs_name); zc.zc_objset_type = DMU_OST_ZVOL; } else { zc.zc_objset_type = DMU_OST_ZFS; } /* If we can successfully roll it back, reget the stats */ - if (zfs_ioctl(ZFS_IOC_ROLLBACK, &zc) == 0) + if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0) goto top; /* * If we can sucessfully destroy it, pretend that it * never existed. */ - if (zfs_ioctl(ZFS_IOC_DESTROY, &zc) == 0) { + if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) { free(zhp); errno = ENOENT; return (NULL); @@ -294,8 +311,7 @@ top: else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) zhp->zfs_type = ZFS_TYPE_FILESYSTEM; else - /* we should never see any other dataset types */ - zfs_baderror(zhp->zfs_dmustats.dds_type); + abort(); /* we should never see any other types */ return (zhp); } @@ -306,18 +322,21 @@ top: * appropriate error message and return NULL if it can't be opened. */ zfs_handle_t * -zfs_open(const char *path, int types) +zfs_open(libzfs_handle_t *hdl, const char *path, int types) { zfs_handle_t *zhp; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot open '%s'"), path); /* - * Validate the name before we even try to open it. We don't care about - * the verbose invalid messages here; just report a generic error. + * Validate the name before we even try to open it. */ - if (!zfs_validate_name(path, types, NULL, 0)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': invalid %s name"), path, - path_to_str(path, types)); + if (!zfs_validate_name(hdl, path, ZFS_TYPE_ANY)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid dataset name")); + (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); return (NULL); } @@ -325,48 +344,13 @@ zfs_open(const char *path, int types) * Try to get stats for the dataset, which will tell us if it exists. */ errno = 0; - if ((zhp = make_dataset_handle(path)) == NULL) { - switch (errno) { - case ENOENT: - /* - * The dataset doesn't exist. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': no such %s"), path, - path_to_str(path, types)); - break; - - case EBUSY: - /* - * We were able to open the dataset but couldn't - * get the stats. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': %s is busy"), path, - path_to_str(path, types)); - break; - - case ENXIO: - case EIO: - /* - * I/O error from the underlying pool. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': I/O error"), path, - path_to_str(path, types)); - break; - - default: - zfs_baderror(errno); - - } + if ((zhp = make_dataset_handle(hdl, path)) == NULL) { + (void) zfs_standard_error(hdl, errno, errbuf, path); return (NULL); } if (!(types & zhp->zfs_type)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': operation " - "not supported for %ss"), path, - zfs_type_to_name(zhp->zfs_type)); + (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); free(zhp); return (NULL); } @@ -382,6 +366,8 @@ zfs_close(zfs_handle_t *zhp) { if (zhp->zfs_mntopts) free(zhp->zfs_mntopts); + if (zhp->zfs_props) + nvlist_free(zhp->zfs_props); free(zhp); } @@ -443,7 +429,7 @@ struct { * resulting value must be shifted. */ static int -str2shift(const char *buf, char *reason, size_t len) +str2shift(libzfs_handle_t *hdl, const char *buf) { const char *ends = "BKMGTPEZ"; int i; @@ -455,8 +441,8 @@ str2shift(const char *buf, char *reason, size_t len) break; } if (i == strlen(ends)) { - (void) snprintf(reason, len, dgettext(TEXT_DOMAIN, "invalid " - "numeric suffix '%s'"), buf); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid numeric suffix '%s'"), buf); return (-1); } @@ -465,12 +451,11 @@ str2shift(const char *buf, char *reason, size_t len) * allow 'BB' - that's just weird. */ if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' && - toupper(buf[0]) != 'B')) { + toupper(buf[0]) != 'B')) return (10*i); - } - (void) snprintf(reason, len, dgettext(TEXT_DOMAIN, "invalid numeric " - "suffix '%s'"), buf); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid numeric suffix '%s'"), buf); return (-1); } @@ -480,7 +465,7 @@ str2shift(const char *buf, char *reason, size_t len) * message for the caller to use. */ static int -nicestrtonum(const char *value, uint64_t *num, char *buf, size_t buflen) +nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num) { char *end; int shift; @@ -489,8 +474,9 @@ nicestrtonum(const char *value, uint64_t *num, char *buf, size_t buflen) /* Check to see if this looks like a number. */ if ((value[0] < '0' || value[0] > '9') && value[0] != '.') { - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "must be a numeric value"), buflen); + if (hdl) + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "bad numeric value '%s'"), value); return (-1); } @@ -503,8 +489,9 @@ nicestrtonum(const char *value, uint64_t *num, char *buf, size_t buflen) * in a 64-bit value. */ if (errno == ERANGE) { - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "value is too large"), buflen); + if (hdl) + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "numeric value is too large")); return (-1); } @@ -515,26 +502,28 @@ nicestrtonum(const char *value, uint64_t *num, char *buf, size_t buflen) if (*end == '.') { double fval = strtod(value, &end); - if ((shift = str2shift(end, buf, buflen)) == -1) + if ((shift = str2shift(hdl, end)) == -1) return (-1); fval *= pow(2, shift); if (fval > UINT64_MAX) { - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "value is too large"), buflen); + if (hdl) + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "numeric value is too large")); return (-1); } *num = (uint64_t)fval; } else { - if ((shift = str2shift(end, buf, buflen)) == -1) + if ((shift = str2shift(hdl, end)) == -1) return (-1); /* Check for overflow */ if (shift >= 64 || (*num << shift) >> shift != *num) { - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "value is too large"), buflen); + if (hdl) + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "numeric value is too large")); return (-1); } @@ -547,9 +536,7 @@ nicestrtonum(const char *value, uint64_t *num, char *buf, size_t buflen) int zfs_nicestrtonum(const char *str, uint64_t *val) { - char buf[1]; - - return (nicestrtonum(str, val, buf, sizeof (buf))); + return (nicestrtonum(NULL, str, val)); } /* @@ -557,28 +544,28 @@ zfs_nicestrtonum(const char *str, uint64_t *val) * by zfs_prop_set() and some libzfs consumers. */ int -zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) +zfs_prop_validate(libzfs_handle_t *hdl, zfs_prop_t prop, const char *value, + uint64_t *intval) { const char *propname = zfs_prop_to_name(prop); uint64_t number; - char reason[64]; + char errbuf[1024]; int i; /* * Check to see if this a read-only property. */ - if (zfs_prop_readonly(prop)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s property: read-only property"), propname); - return (-1); - } + if (zfs_prop_readonly(prop)) + return (zfs_error(hdl, EZFS_PROPREADONLY, + dgettext(TEXT_DOMAIN, "cannot set %s property"), propname)); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "bad %s value '%s'"), propname, value); /* See if the property value is too long */ if (strlen(value) >= ZFS_MAXPROPLEN) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': value is too long"), propname, - value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "value is too long")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } /* Perform basic checking based on property type */ @@ -589,10 +576,9 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) } else if (strcmp(value, "off") == 0) { number = 0; } else { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be 'on' or 'off'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be 'on' or 'off'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -603,21 +589,15 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) break; } - if (nicestrtonum(value, &number, reason, - sizeof (reason)) != 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': %s"), propname, value, - reason); - return (-1); - } + if (nicestrtonum(hdl, value, &number) != 0) + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); /* don't allow 0 for quota, use 'none' instead */ if (prop == ZFS_PROP_QUOTA && number == 0 && strcmp(value, "none") != 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': use '%s=none' to disable"), - propname, value, propname); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "use 'quota=none' to disable")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */ @@ -625,13 +605,11 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) prop == ZFS_PROP_VOLBLOCKSIZE) { if (number < SPA_MINBLOCKSIZE || number > SPA_MAXBLOCKSIZE || !ISP2(number)) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': " + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "must be power of 2 from %u to %uk"), - propname, value, (uint_t)SPA_MINBLOCKSIZE, (uint_t)SPA_MAXBLOCKSIZE >> 10); - return (-1); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } } @@ -652,11 +630,10 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) break; if (value[0] != '/') { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be an absolute " - "path, 'none', or 'legacy'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be an absolute path, 'none', or " + "'legacy'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -670,11 +647,10 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) } if (checksum_table[i].name == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be 'on', 'off', " - "'fletcher2', 'fletcher4', or 'sha256'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be 'on', 'off', 'fletcher2', " + "'fletcher4', or 'sha256'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -688,11 +664,9 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) } if (compress_table[i].name == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be 'on', 'off', " - "or 'lzjb'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be 'on', 'off', or 'lzjb'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -705,11 +679,9 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) } if (snapdir_table[i].name == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be 'hidden' " - "or 'visible'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be 'hidden' or 'visible'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -723,11 +695,10 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) } if (acl_mode_table[i].name == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be 'discard', " - "'groupmask' or 'passthrough'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be 'disacard', 'groupmask', or " + "'passthrough'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -741,11 +712,10 @@ zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval) } if (acl_inherit_table[i].name == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad %s value '%s': must be 'discard', " - "'noallow', 'secure' or 'passthrough'"), - propname, value); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be 'discard, 'noallow', 'secure', " + "or 'passthrough'")); + return (zfs_error(hdl, EZFS_BADPROP, errbuf)); } break; @@ -775,19 +745,22 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) zfs_cmd_t zc = { 0 }; int ret; prop_changelist_t *cl; + char errbuf[1024]; + libzfs_handle_t *hdl = zhp->zfs_hdl; - if (zfs_prop_validate(prop, propval, &number) != 0) + if (zfs_prop_validate(zhp->zfs_hdl, prop, propval, &number) != 0) return (-1); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set %s for '%s'"), propname, + zhp->zfs_name); + /* * Check to see if the value applies to this type */ - if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': property does not apply to %ss"), - propname, zhp->zfs_name, zfs_type_to_name(zhp->zfs_type)); - return (-1); - } + if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) + return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); /* * For the mountpoint and sharenfs properties, check if it can be set @@ -804,29 +777,24 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { if (getzoneid() == GLOBAL_ZONEID) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': " - "dataset is used in a non-global zone"), - propname, zhp->zfs_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset is used in a non-global zone")); + return (zfs_error(hdl, EZFS_ZONED, errbuf)); } else if (prop == ZFS_PROP_SHARENFS) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': filesystems " - "cannot be shared in a non-global zone"), - propname, zhp->zfs_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "filesystems cannot be shared in a " + "non-global zone")); + return (zfs_error(hdl, EZFS_ZONED, errbuf)); } } else if (getzoneid() != GLOBAL_ZONEID) { /* * If zoned property is 'off', this must be in * a globle zone. If not, something is wrong. */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': dataset is " - "used in a non-global zone, but 'zoned' " - "property is not set"), - propname, zhp->zfs_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset is used in a non-global zone, but " + "'zoned' property is not set")); + return (zfs_error(hdl, EZFS_ZONED, errbuf)); } } @@ -834,11 +802,10 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) return (-1); if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s for '%s', " - "child dataset with inherited mountpoint is used " - "in a non-global zone"), - propname, zhp->zfs_name); - ret = -1; + 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; } @@ -853,11 +820,12 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) switch (prop) { case ZFS_PROP_QUOTA: zc.zc_cookie = number; - ret = zfs_ioctl(ZFS_IOC_SET_QUOTA, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_QUOTA, &zc); break; case ZFS_PROP_RESERVATION: zc.zc_cookie = number; - ret = zfs_ioctl(ZFS_IOC_SET_RESERVATION, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_RESERVATION, + &zc); break; case ZFS_PROP_MOUNTPOINT: case ZFS_PROP_SHARENFS: @@ -870,15 +838,16 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) sizeof (zc.zc_prop_value)); zc.zc_intsz = 1; zc.zc_numints = strlen(propval) + 1; - ret = zfs_ioctl(ZFS_IOC_SET_PROP, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); break; case ZFS_PROP_VOLSIZE: zc.zc_volsize = number; - ret = zfs_ioctl(ZFS_IOC_SET_VOLSIZE, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLSIZE, &zc); break; case ZFS_PROP_VOLBLOCKSIZE: zc.zc_volblocksize = number; - ret = zfs_ioctl(ZFS_IOC_SET_VOLBLOCKSIZE, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_VOLBLOCKSIZE, + &zc); break; default: (void) strlcpy(zc.zc_prop_name, propname, @@ -887,25 +856,13 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) *(uint64_t *)zc.zc_prop_value = number; zc.zc_intsz = 8; zc.zc_numints = 1; - ret = zfs_ioctl(ZFS_IOC_SET_PROP, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc); break; } if (ret != 0) { switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': permission " - "denied"), propname, zhp->zfs_name); - break; - - case ENOENT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': no such %s"), zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); - break; - case ENOSPC: /* * For quotas and reservations, ENOSPC indicates @@ -914,41 +871,33 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) */ switch (prop) { case ZFS_PROP_QUOTA: - zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s " - "for '%s': size is less than current " - "used or reserved space"), propname, - zhp->zfs_name); + 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: - zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s " - "for '%s': size is greater than available " - "space"), propname, zhp->zfs_name); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "size is greater than available space")); + (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); break; default: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': out of space"), - propname, zhp->zfs_name); + (void) zfs_standard_error(hdl, errno, errbuf); break; } break; case EBUSY: - if (prop == ZFS_PROP_VOLBLOCKSIZE) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': " - "volume already contains data"), - propname, zhp->zfs_name); - } else { - zfs_baderror(errno); - } + if (prop == ZFS_PROP_VOLBLOCKSIZE) + (void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf); + else + return (zfs_standard_error(hdl, EBUSY, errbuf)); break; case EROFS: - zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s for " - "'%s': read only %s"), propname, zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); + (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); break; case EOVERFLOW: @@ -957,16 +906,13 @@ zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval) */ #ifdef _ILP32 if (prop == ZFS_PROP_VOLSIZE) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot set %s for '%s': " - "max volume size is 1TB on 32-bit systems"), - propname, zhp->zfs_name); + (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); break; } #endif - zfs_baderror(errno); + /* FALLTHROUGH */ default: - zfs_baderror(errno); + (void) zfs_standard_error(hdl, errno, errbuf); } } else { /* @@ -994,44 +940,35 @@ zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) zfs_cmd_t zc = { 0 }; int ret; prop_changelist_t *cl; + libzfs_handle_t *hdl = zhp->zfs_hdl; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot inherit %s for '%s'"), propname, zhp->zfs_name); /* * Verify that this property is inheritable. */ - if (zfs_prop_readonly(prop)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot inherit %s for '%s': property is read-only"), - propname, zhp->zfs_name); - return (-1); - } + if (zfs_prop_readonly(prop)) + return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); - if (!zfs_prop_inheritable(prop)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot inherit %s for '%s': property is not inheritable"), - propname, zhp->zfs_name); - return (-1); - } + if (!zfs_prop_inheritable(prop)) + 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)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot inherit %s for '%s': property does " - "not apply to %ss"), propname, zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); - return (-1); - } + if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) + return (zfs_error(hdl, EZFS_PROPTYPE, errbuf)); (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); (void) strlcpy(zc.zc_prop_name, propname, sizeof (zc.zc_prop_name)); if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID && zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot inherit %s for '%s', " - "dataset is used in a non-global zone"), propname, - zhp->zfs_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset is used in a non-global zone")); + return (zfs_error(hdl, EZFS_ZONED, errbuf)); } /* @@ -1041,11 +978,10 @@ zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) return (-1); if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot inherit %s for '%s', " - "child dataset with inherited mountpoint is " - "used in a non-global zone"), - propname, zhp->zfs_name); - ret = -1; + 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; } @@ -1054,27 +990,9 @@ zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop) zc.zc_numints = 0; - if ((ret = zfs_ioctl(ZFS_IOC_SET_PROP, &zc)) != 0) { - switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot inherit %s for '%s': permission " - "denied"), propname, zhp->zfs_name); - break; - case ENOENT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': no such %s"), zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); - break; - case ENOSPC: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot inherit %s for '%s': " - "out of space"), propname, zhp->zfs_name); - break; - default: - zfs_baderror(errno); - } - + if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, + ZFS_IOC_SET_PROP, &zc)) != 0) { + return (zfs_standard_error(hdl, errno, errbuf)); } else { if ((ret = changelist_postfix(cl)) != 0) @@ -1151,11 +1069,10 @@ getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) * If they differ from the on-disk values, report the current values and mark * the source "temporary". */ -static uint64_t +static int get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, - char **source) + char **source, uint64_t *val) { - uint64_t val; struct mnttab mnt; *source = NULL; @@ -1167,86 +1084,90 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, switch (prop) { case ZFS_PROP_ATIME: - val = getprop_uint64(zhp, prop, source); + *val = getprop_uint64(zhp, prop, source); - if (hasmntopt(&mnt, MNTOPT_ATIME) && !val) { - val = TRUE; + if (hasmntopt(&mnt, MNTOPT_ATIME) && !*val) { + *val = B_TRUE; if (src) *src = ZFS_SRC_TEMPORARY; - } else if (hasmntopt(&mnt, MNTOPT_NOATIME) && val) { - val = FALSE; + } else if (hasmntopt(&mnt, MNTOPT_NOATIME) && *val) { + *val = B_FALSE; if (src) *src = ZFS_SRC_TEMPORARY; } - return (val); + break; case ZFS_PROP_AVAILABLE: - return (zhp->zfs_dmustats.dds_available); + *val = zhp->zfs_dmustats.dds_available; + break; case ZFS_PROP_DEVICES: - val = getprop_uint64(zhp, prop, source); + *val = getprop_uint64(zhp, prop, source); - if (hasmntopt(&mnt, MNTOPT_DEVICES) && !val) { - val = TRUE; + if (hasmntopt(&mnt, MNTOPT_DEVICES) && !*val) { + *val = B_TRUE; if (src) *src = ZFS_SRC_TEMPORARY; - } else if (hasmntopt(&mnt, MNTOPT_NODEVICES) && val) { - val = FALSE; + } else if (hasmntopt(&mnt, MNTOPT_NODEVICES) && *val) { + *val = B_FALSE; if (src) *src = ZFS_SRC_TEMPORARY; } - return (val); + break; case ZFS_PROP_EXEC: - val = getprop_uint64(zhp, prop, source); + *val = getprop_uint64(zhp, prop, source); - if (hasmntopt(&mnt, MNTOPT_EXEC) && !val) { - val = TRUE; + if (hasmntopt(&mnt, MNTOPT_EXEC) && !*val) { + *val = B_TRUE; if (src) *src = ZFS_SRC_TEMPORARY; - } else if (hasmntopt(&mnt, MNTOPT_NOEXEC) && val) { - val = FALSE; + } else if (hasmntopt(&mnt, MNTOPT_NOEXEC) && *val) { + *val = B_FALSE; if (src) *src = ZFS_SRC_TEMPORARY; } - return (val); + break; case ZFS_PROP_RECORDSIZE: case ZFS_PROP_COMPRESSION: case ZFS_PROP_ZONED: - val = getprop_uint64(zhp, prop, source); - return (val); + *val = getprop_uint64(zhp, prop, source); + break; case ZFS_PROP_READONLY: - val = getprop_uint64(zhp, prop, source); + *val = getprop_uint64(zhp, prop, source); - if (hasmntopt(&mnt, MNTOPT_RO) && !val) { - val = TRUE; + if (hasmntopt(&mnt, MNTOPT_RO) && !*val) { + *val = B_TRUE; if (src) *src = ZFS_SRC_TEMPORARY; - } else if (hasmntopt(&mnt, MNTOPT_RW) && val) { - val = FALSE; + } else if (hasmntopt(&mnt, MNTOPT_RW) && *val) { + *val = B_FALSE; if (src) *src = ZFS_SRC_TEMPORARY; } - return (val); + break; case ZFS_PROP_CREATION: - return (zhp->zfs_dmustats.dds_creation_time); + *val = zhp->zfs_dmustats.dds_creation_time; + break; case ZFS_PROP_QUOTA: if (zhp->zfs_dmustats.dds_quota == 0) *source = ""; /* default */ else *source = zhp->zfs_name; - return (zhp->zfs_dmustats.dds_quota); + *val = zhp->zfs_dmustats.dds_quota; + break; case ZFS_PROP_RESERVATION: if (zhp->zfs_dmustats.dds_reserved == 0) *source = ""; /* default */ else *source = zhp->zfs_name; - return (zhp->zfs_dmustats.dds_reserved); + *val = zhp->zfs_dmustats.dds_reserved; + break; case ZFS_PROP_COMPRESSRATIO: /* @@ -1255,43 +1176,50 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, * 100, so '2.5x' would be returned as 250. */ if (zhp->zfs_dmustats.dds_compressed_bytes == 0) - return (100ULL); + *val = 100ULL; else - return (zhp->zfs_dmustats.dds_uncompressed_bytes * 100 / + *val = + (zhp->zfs_dmustats.dds_uncompressed_bytes * 100 / zhp->zfs_dmustats.dds_compressed_bytes); + break; case ZFS_PROP_REFERENCED: /* * 'referenced' refers to the amount of physical space * referenced (possibly shared) by this object. */ - return (zhp->zfs_dmustats.dds_space_refd); + *val = zhp->zfs_dmustats.dds_space_refd; + break; case ZFS_PROP_SETUID: - val = getprop_uint64(zhp, prop, source); + *val = getprop_uint64(zhp, prop, source); - if (hasmntopt(&mnt, MNTOPT_SETUID) && !val) { - val = TRUE; + if (hasmntopt(&mnt, MNTOPT_SETUID) && !*val) { + *val = B_TRUE; if (src) *src = ZFS_SRC_TEMPORARY; - } else if (hasmntopt(&mnt, MNTOPT_NOSETUID) && val) { - val = FALSE; + } else if (hasmntopt(&mnt, MNTOPT_NOSETUID) && *val) { + *val = B_FALSE; if (src) *src = ZFS_SRC_TEMPORARY; } - return (val); + break; case ZFS_PROP_VOLSIZE: - return (zhp->zfs_volsize); + *val = zhp->zfs_volsize; + break; case ZFS_PROP_VOLBLOCKSIZE: - return (zhp->zfs_volblocksize); + *val = zhp->zfs_volblocksize; + break; case ZFS_PROP_USED: - return (zhp->zfs_dmustats.dds_space_used); + *val = zhp->zfs_dmustats.dds_space_used; + break; case ZFS_PROP_CREATETXG: - return (zhp->zfs_dmustats.dds_creation_txg); + *val = zhp->zfs_dmustats.dds_creation_txg; + break; case ZFS_PROP_MOUNTED: /* @@ -1306,16 +1234,22 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src, search.mnt_special = (char *)zhp->zfs_name; search.mnt_fstype = MNTTYPE_ZFS; - rewind(zfs_mnttab()); + rewind(zhp->zfs_hdl->libzfs_mnttab); - if (getmntany(zfs_mnttab(), &entry, &search) == 0) - zhp->zfs_mntopts = - zfs_strdup(entry.mnt_mntopts); + if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, + &search) == 0 && (zhp->zfs_mntopts = + zfs_strdup(zhp->zfs_hdl, + entry.mnt_mntopts)) == NULL) + return (-1); } - return (zhp->zfs_mntopts != NULL); + *val = (zhp->zfs_mntopts != NULL); + break; default: - zfs_baderror(EINVAL); + 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); @@ -1355,7 +1289,7 @@ get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source, */ int zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, - zfs_source_t *src, char *statbuf, size_t statlen, int literal) + zfs_source_t *src, char *statbuf, size_t statlen, boolean_t literal) { char *source = NULL; uint64_t val; @@ -1383,8 +1317,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, * Basic boolean values are built on top of * get_numeric_property(). */ - nicebool(get_numeric_property(zhp, prop, src, &source), - propbuf, proplen); + if (get_numeric_property(zhp, prop, src, &source, &val) != 0) + return (-1); + nicebool(val, propbuf, proplen); break; @@ -1399,7 +1334,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, * Basic numeric values are built on top of * get_numeric_property(). */ - val = get_numeric_property(zhp, prop, src, &source); + if (get_numeric_property(zhp, prop, src, &source, &val) != 0) + return (-1); if (literal) (void) snprintf(propbuf, proplen, "%llu", val); else @@ -1533,7 +1469,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, case ZFS_PROP_QUOTA: case ZFS_PROP_RESERVATION: - val = get_numeric_property(zhp, prop, src, &source); + if (get_numeric_property(zhp, prop, src, &source, &val) != 0) + return (-1); /* * If quota or reservation is 0, we translate this into 'none' @@ -1555,7 +1492,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, break; case ZFS_PROP_COMPRESSRATIO: - val = get_numeric_property(zhp, prop, src, &source); + if (get_numeric_property(zhp, prop, src, &source, &val) != 0) + return (-1); (void) snprintf(propbuf, proplen, "%lld.%02lldx", val / 100, val % 100); break; @@ -1572,7 +1510,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, str = "snapshot"; break; default: - zfs_baderror(zhp->zfs_type); + abort(); } (void) snprintf(propbuf, proplen, "%s", str); break; @@ -1584,7 +1522,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, * 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)) + 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); @@ -1600,7 +1541,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, break; default: - zfs_baderror(EINVAL); + abort(); } get_source(zhp, src, source, statbuf, statlen); @@ -1618,8 +1559,11 @@ zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop) { char *source; zfs_source_t sourcetype = ZFS_SRC_NONE; + uint64_t val; + + (void) get_numeric_property(zhp, prop, &sourcetype, &source, &val); - return (get_numeric_property(zhp, prop, &sourcetype, &source)); + return (val); } /* @@ -1635,12 +1579,15 @@ zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, * Check to see if this property applies to our object */ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) - return (-1); + return (zfs_error(zhp->zfs_hdl, EZFS_PROPTYPE, + dgettext(TEXT_DOMAIN, "cannot get property '%s'"), + zfs_prop_to_name(prop))); if (src) *src = ZFS_SRC_NONE; - *value = get_numeric_property(zhp, prop, src, &source); + if (get_numeric_property(zhp, prop, src, &source, value) != 0) + return (-1); get_source(zhp, src, source, statbuf, statlen); @@ -1676,7 +1623,7 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) int ret; for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - zfs_ioctl(ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; + ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { /* * Ignore private dataset names. @@ -1688,7 +1635,8 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) * Silently ignore errors, as the only plausible explanation is * that the pool has since been removed. */ - if ((nzhp = make_dataset_handle(zc.zc_name)) == NULL) + if ((nzhp = make_dataset_handle(zhp->zfs_hdl, + zc.zc_name)) == NULL) continue; if ((ret = func(nzhp, data)) != 0) @@ -1701,7 +1649,8 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) * obtained the handle. */ if (errno != ESRCH && errno != ENOENT) - zfs_baderror(errno); + return (zfs_standard_error(zhp->zfs_hdl, errno, + dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); return (0); } @@ -1717,10 +1666,12 @@ zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) int ret; for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - zfs_ioctl(ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0; + ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, + &zc) == 0; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) { - if ((nzhp = make_dataset_handle(zc.zc_name)) == NULL) + if ((nzhp = make_dataset_handle(zhp->zfs_hdl, + zc.zc_name)) == NULL) continue; if ((ret = func(nzhp, data)) != 0) @@ -1733,7 +1684,8 @@ zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) * obtained the handle. Silently ignore this case, and return success. */ if (errno != ESRCH && errno != ENOENT) - zfs_baderror(errno); + return (zfs_standard_error(zhp->zfs_hdl, errno, + dgettext(TEXT_DOMAIN, "cannot iterate filesystems"))); return (0); } @@ -1774,21 +1726,22 @@ parent_name(const char *path, char *buf, size_t buflen) * Checks to make sure that the given path has a parent, and that it exists. */ static int -check_parents(const char *path, zfs_type_t type) +check_parents(libzfs_handle_t *hdl, const char *path) { zfs_cmd_t zc = { 0 }; char parent[ZFS_MAXNAMELEN]; char *slash; zfs_handle_t *zhp; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), "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(dgettext(TEXT_DOMAIN, - "cannot create '%s': missing dataset name"), - path, zfs_type_to_name(type)); - zfs_error(dgettext(TEXT_DOMAIN, - "use 'zpool create' to create a storage pool")); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "missing dataset name")); + return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); } /* check to see if the pool exists */ @@ -1796,40 +1749,39 @@ check_parents(const char *path, zfs_type_t type) slash = parent + strlen(parent); (void) strncpy(zc.zc_name, parent, slash - parent); zc.zc_name[slash - parent] = '\0'; - if (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) != 0 && + if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 && errno == ENOENT) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': no such pool '%s'"), path, zc.zc_name); - return (-1); + 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 */ - if ((zhp = make_dataset_handle(parent)) == NULL) { + if ((zhp = make_dataset_handle(hdl, parent)) == NULL) { switch (errno) { case ENOENT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': parent does not exist"), path); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "parent does not exist")); + return (zfs_error(hdl, EZFS_NOENT, errbuf)); default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, errbuf)); } } /* we are in a non-global zone, but parent is in the global zone */ if (getzoneid() != GLOBAL_ZONEID && !zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': permission denied"), path); + (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(dgettext(TEXT_DOMAIN, - "cannot create '%s': parent is not a filesystem"), - path); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "parent is not a filesystem")); + (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); zfs_close(zhp); return (-1); } @@ -1843,44 +1795,35 @@ check_parents(const char *path, zfs_type_t type) * only for volumes, and indicate the size and blocksize of the volume. */ int -zfs_create(const char *path, zfs_type_t type, +zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, const char *sizestr, const char *blocksizestr) { - char reason[64]; zfs_cmd_t zc = { 0 }; int ret; uint64_t size = 0; uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); + char errbuf[1024]; /* convert sizestr into integer size */ - if (sizestr != NULL && nicestrtonum(sizestr, &size, - reason, sizeof (reason)) != 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad volume size '%s': %s"), sizestr, reason); - return (-1); - } + if (sizestr != NULL && nicestrtonum(hdl, sizestr, &size) != 0) + return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, + "bad volume size '%s'"), sizestr)); /* convert blocksizestr into integer blocksize */ - if (blocksizestr != NULL && nicestrtonum(blocksizestr, &blocksize, - reason, sizeof (reason)) != 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad volume blocksize '%s': %s"), blocksizestr, reason); - return (-1); - } + if (blocksizestr != NULL && nicestrtonum(hdl, blocksizestr, + &blocksize) != 0) + return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN, + "bad volume blocksize '%s'"), blocksizestr)); + + (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(path, type, reason, sizeof (reason))) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': %s in %s name"), path, reason, - zfs_type_to_name(type)); - if (strstr(reason, "snapshot") != NULL) - zfs_error(dgettext(TEXT_DOMAIN, - "use 'zfs snapshot' to create a snapshot")); - return (-1); - } + if (!zfs_validate_name(hdl, path, type)) + return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* validate parents exist */ - if (check_parents(path, type) != 0) + if (check_parents(hdl, path) != 0) return (-1); /* @@ -1891,10 +1834,10 @@ zfs_create(const char *path, zfs_type_t type, * first try to see if the dataset exists. */ (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); - if (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': dataset exists"), path); - return (-1); + if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset already exists")); + return (zfs_error(hdl, EZFS_EXISTS, errbuf)); } if (type == ZFS_TYPE_VOLUME) @@ -1911,30 +1854,30 @@ zfs_create(const char *path, zfs_type_t type, * zero. */ if (size == 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad volume size '%s': cannot be zero"), sizestr); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "cannot be zero")); + return (zfs_error(hdl, EZFS_BADPROP, + dgettext(TEXT_DOMAIN, "bad volume size '%s'"), + sizestr)); } if (blocksize < SPA_MINBLOCKSIZE || blocksize > SPA_MAXBLOCKSIZE || !ISP2(blocksize)) { - zfs_error(dgettext(TEXT_DOMAIN, - "bad volume block size '%s': " + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "must be power of 2 from %u to %uk"), - blocksizestr, (uint_t)SPA_MINBLOCKSIZE, (uint_t)SPA_MAXBLOCKSIZE >> 10); - return (-1); + return (zfs_error(hdl, EZFS_BADPROP, + dgettext(TEXT_DOMAIN, + "bad volume block size '%s'"), blocksizestr)); } if (size % blocksize != 0) { - char buf[64]; - zfs_nicenum(blocksize, buf, sizeof (buf)); - zfs_error(dgettext(TEXT_DOMAIN, - "bad volume size '%s': " - "must be multiple of volume block size (%s)"), - sizestr, buf); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must be a multiple of volume block size")); + return (zfs_error(hdl, EZFS_BADPROP, + dgettext(TEXT_DOMAIN, "bad volume size '%s'"), + sizestr)); } zc.zc_volsize = size; @@ -1942,10 +1885,10 @@ zfs_create(const char *path, zfs_type_t type, } /* create the dataset */ - ret = zfs_ioctl(ZFS_IOC_CREATE, &zc); + ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); if (ret == 0 && type == ZFS_TYPE_VOLUME) - ret = zvol_create_link(path); + ret = zvol_create_link(hdl, path); /* check for failure */ if (ret != 0) { @@ -1954,81 +1897,38 @@ zfs_create(const char *path, zfs_type_t type, switch (errno) { case ENOENT: - /* - * The parent dataset has been deleted since our - * previous check. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': no such parent '%s'"), - path, parent); - break; - - case EPERM: - /* - * The user doesn't have permission to create a new - * dataset here. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': permission denied"), path); - break; - - case EDQUOT: - case ENOSPC: - /* - * The parent dataset does not have enough free space - * to create a new dataset. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': not enough space in '%s'"), - path, parent); - break; - - case EEXIST: - /* - * The target dataset already exists. We should have - * caught this above, but there may be some unexplained - * race condition. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': dataset exists"), path); - break; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "no such parent '%s'"), parent); + return (zfs_error(hdl, EZFS_NOENT, errbuf)); case EINVAL: - /* - * The target dataset does not support children. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': children unsupported in '%s'"), - path, parent); - break; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "parent '%s' is not a filesysem"), parent); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); case EDOM: - zfs_error(dgettext(TEXT_DOMAIN, "bad %s value '%s': " + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "must be power of 2 from %u to %uk"), - zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), - blocksizestr ? blocksizestr : "<unknown>", (uint_t)SPA_MINBLOCKSIZE, (uint_t)SPA_MAXBLOCKSIZE >> 10); - break; + + return (zfs_error(hdl, EZFS_BADPROP, + dgettext(TEXT_DOMAIN, "bad block size '%s'"), + blocksizestr ? blocksizestr : "<unknown>")); + #ifdef _ILP32 case EOVERFLOW: /* * This platform can't address a volume this big. */ - if (type == ZFS_TYPE_VOLUME) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': " - "max volume size is 1TB on 32-bit systems"), - path); - break; - } + if (type == ZFS_TYPE_VOLUME) + return (zfs_error(hdl, EZFS_VOLTOOBIG, + errbuf)); #endif - + /* FALLTHROUGH */ default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, errbuf)); } - - return (-1); } return (0); @@ -2043,6 +1943,7 @@ zfs_destroy(zfs_handle_t *zhp) { zfs_cmd_t zc = { 0 }; int ret; + char errbuf[1024]; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); @@ -2051,7 +1952,7 @@ zfs_destroy(zfs_handle_t *zhp) * so that we do the right thing for snapshots of volumes. */ if (zhp->zfs_volblocksize != 0) { - if (zvol_remove_link(zhp->zfs_name) != 0) + if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) return (-1); zc.zc_objset_type = DMU_OST_ZVOL; @@ -2059,63 +1960,15 @@ zfs_destroy(zfs_handle_t *zhp) zc.zc_objset_type = DMU_OST_ZFS; } - ret = zfs_ioctl(ZFS_IOC_DESTROY, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc); - if (ret != 0) { - switch (errno) { - - case EPERM: - /* - * We don't have permission to destroy this dataset. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': permission denied"), - zhp->zfs_name); - break; + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot destroy '%s'"), zhp->zfs_name); - case EIO: - /* - * I/O error. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': I/O error"), - zhp->zfs_name); - break; - - case ENOENT: - /* - * We've hit a race condition where the dataset has been - * destroyed since we opened it. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': no such %s"), - zhp->zfs_name, zfs_type_to_name(zhp->zfs_type)); - break; - - case EBUSY: - /* - * Even if we destroy all children, there is a chance we - * can hit this case if: - * - * - A child dataset has since been created - * - A filesystem is mounted - * - * This error message is awful, but hopefully we've - * already caught the common cases (and aborted more - * appropriately) before calling this function. There's - * nothing else we can do at this point. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': %s is busy"), - zhp->zfs_name, zfs_type_to_name(zhp->zfs_type)); - break; - - default: - zfs_baderror(errno); - } - - return (-1); - } + if (ret != 0) + return (zfs_standard_error(zhp->zfs_hdl, errno, + dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), + zhp->zfs_name)); remove_mountpoint(zhp); @@ -2128,24 +1981,23 @@ zfs_destroy(zfs_handle_t *zhp) int zfs_clone(zfs_handle_t *zhp, const char *target) { - char reason[64]; zfs_cmd_t zc = { 0 }; char parent[ZFS_MAXNAMELEN]; int ret; + char errbuf[1024]; + libzfs_handle_t *hdl = zhp->zfs_hdl; assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot create '%s'"), target); + /* validate the target name */ - if (!zfs_validate_name(target, ZFS_TYPE_FILESYSTEM, reason, - sizeof (reason))) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': %s in filesystem name"), target, - reason, zfs_type_to_name(ZFS_TYPE_FILESYSTEM)); - return (-1); - } + if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM)) + return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* validate parents exist */ - if (check_parents(target, zhp->zfs_type) != 0) + if (check_parents(zhp->zfs_hdl, target) != 0) return (-1); (void) parent_name(target, parent, sizeof (parent)); @@ -2158,18 +2010,10 @@ zfs_clone(zfs_handle_t *zhp, const char *target) (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); (void) strlcpy(zc.zc_filename, zhp->zfs_name, sizeof (zc.zc_filename)); - ret = zfs_ioctl(ZFS_IOC_CREATE, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); if (ret != 0) { switch (errno) { - case EPERM: - /* - * The user doesn't have permission to create the clone. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': permission denied"), - target); - break; case ENOENT: /* @@ -2181,42 +2025,147 @@ zfs_clone(zfs_handle_t *zhp, const char *target) * that doesn't exist anymore, or whether the target * dataset doesn't exist. */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': no such parent '%s'"), - target, parent); - break; + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "no such parent '%s'"), parent); + return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); - case EDQUOT: - case ENOSPC: - /* - * There is not enough space in the target dataset - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': not enough space in '%s'"), - target, parent); - break; + 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)); - case EEXIST: - /* - * The target already exists. - */ - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': dataset exists"), target); - break; + default: + return (zfs_standard_error(zhp->zfs_hdl, errno, + errbuf)); + } + } else if (zhp->zfs_volblocksize != 0) { + ret = zvol_create_link(zhp->zfs_hdl, target); + } - case EXDEV: + return (ret); +} + +typedef struct promote_data { + char cb_mountpoint[MAXPATHLEN]; + const char *cb_target; + const char *cb_errbuf; + uint64_t cb_pivot_txg; +} promote_data_t; + +static int +promote_snap_cb(zfs_handle_t *zhp, void *data) +{ + promote_data_t *pd = data; + zfs_handle_t *szhp; + int err; + char snapname[MAXPATHLEN]; + char *cp; + + /* We don't care about snapshots after the pivot point */ + if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) + return (0); + + /* + * Unmount it. We actually need to open it to provoke it to be + * mounted first, because if it is not mounted, umount2 will + * mount it! + */ + (void) strcpy(snapname, pd->cb_mountpoint); + (void) strcat(snapname, "/.zfs/snapshot/"); + cp = strchr(zhp->zfs_name, '@'); + (void) strcat(snapname, cp+1); + err = open(snapname, O_RDONLY); + if (err != -1) + (void) close(err); + (void) umount2(snapname, MS_FORCE); + + /* Check for conflicting names */ + (void) strcpy(snapname, pd->cb_target); + (void) strcat(snapname, cp); + szhp = make_dataset_handle(zhp->zfs_hdl, snapname); + if (szhp != NULL) { + zfs_close(szhp); + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "snapshot name '%s' from origin \n" + "conflicts with '%s' from target"), + zhp->zfs_name, snapname); + return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf)); + } + return (0); +} + +/* + * Promotes the given clone fs to be the clone parent. + */ +int +zfs_promote(zfs_handle_t *zhp) +{ + libzfs_handle_t *hdl = zhp->zfs_hdl; + zfs_cmd_t zc = { 0 }; + char parent[MAXPATHLEN]; + char *cp; + int ret; + zfs_handle_t *pzhp; + promote_data_t pd; + 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)); + } + + (void) strcpy(parent, zhp->zfs_dmustats.dds_clone_of); + if (parent[0] == '\0') { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "not a cloned filesystem")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + } + cp = strchr(parent, '@'); + *cp = '\0'; + + /* Walk the snapshots we will be moving */ + pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_clone_of, ZFS_TYPE_SNAPSHOT); + if (pzhp == NULL) + return (-1); + pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); + zfs_close(pzhp); + pd.cb_target = zhp->zfs_name; + pd.cb_errbuf = errbuf; + pzhp = zfs_open(hdl, parent, ZFS_TYPE_ANY); + if (pzhp == NULL) + return (-1); + (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, + sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); + ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); + if (ret != 0) + return (-1); + + /* issue the ioctl */ + (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc); + + if (ret != 0) { + switch (errno) { + + case EEXIST: /* - * The source and target pools differ. + * There is a conflicting snapshot name. We + * should have caught this above, but they could + * have renamed something in the mean time. */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "source and target pools differ"), target); - break; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "conflicting snapshot name from parent '%s'"), + parent); + return (zfs_error(hdl, EZFS_EXISTS, errbuf)); default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, errbuf)); } - } else if (zhp->zfs_volblocksize != 0) { - ret = zvol_create_link(target); } return (ret); @@ -2226,40 +2175,36 @@ zfs_clone(zfs_handle_t *zhp, const char *target) * Takes a snapshot of the given dataset */ int -zfs_snapshot(const char *path) +zfs_snapshot(libzfs_handle_t *hdl, const char *path) { - char reason[64]; const char *delim; char *parent; zfs_handle_t *zhp; zfs_cmd_t zc = { 0 }; int ret; + char errbuf[1024]; - /* validate the snapshot name */ - if (!zfs_validate_name(path, ZFS_TYPE_SNAPSHOT, reason, - sizeof (reason))) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot snapshot '%s': %s in snapshot name"), path, - reason); - return (-1); - } + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot snapshot '%s'"), path); + + /* validate the target name */ + if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT)) + return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* make sure we have a snapshot */ if ((delim = strchr(path, '@')) == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot snapshot '%s': missing '@' delim in snapshot " - "name"), path); - zfs_error(dgettext(TEXT_DOMAIN, - "use 'zfs create' to create a filesystem")); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "missing '@' delimeter in snapshot name")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); } /* make sure the parent exists and is of the appropriate type */ - parent = zfs_malloc(delim - path + 1); + if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL) + return (-1); (void) strncpy(parent, path, delim - path); parent[delim - path] = '\0'; - if ((zhp = zfs_open(parent, ZFS_TYPE_FILESYSTEM | + if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) { free(parent); return (-1); @@ -2272,56 +2217,17 @@ zfs_snapshot(const char *path) else zc.zc_objset_type = DMU_OST_ZFS; - ret = zfs_ioctl(ZFS_IOC_CREATE, &zc); + ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc); if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { - ret = zvol_create_link(path); + ret = zvol_create_link(zhp->zfs_hdl, path); if (ret != 0) - (void) zfs_ioctl(ZFS_IOC_DESTROY, &zc); + (void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, + &zc); } - if (ret != 0) { - switch (errno) { - case EPERM: - /* - * User doesn't have permission to create a snapshot - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "permission denied"), path); - break; - - case EDQUOT: - case ENOSPC: - /* - * Out of space in parent. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "not enough space in '%s'"), path, parent); - break; - - case EEXIST: - /* - * Snapshot already exists. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "snapshot exists"), path); - break; - - case ENOENT: - /* - * Shouldn't happen because we verified the parent - * above. But there may be a race condition where it - * has since been removed. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': " - "no such %s"), parent, - zfs_type_to_name(zhp->zfs_type)); - break; - - default: - zfs_baderror(errno); - } - } + if (ret != 0) + (void) zfs_standard_error(hdl, errno, errbuf); free(parent); zfs_close(zhp); @@ -2337,6 +2243,11 @@ zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from) { zfs_cmd_t zc = { 0 }; int ret; + char errbuf[1024]; + libzfs_handle_t *hdl = zhp_to->zfs_hdl; + + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot send '%s'"), zhp_to->zfs_name); /* do the ioctl() */ (void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name)); @@ -2348,34 +2259,14 @@ zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from) } zc.zc_cookie = STDOUT_FILENO; - ret = zfs_ioctl(ZFS_IOC_SENDBACKUP, &zc); + ret = ioctl(zhp_to->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc); if (ret != 0) { switch (errno) { - case EPERM: - /* - * User doesn't have permission to do a send - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot send '%s': " - "permission denied"), zhp_to->zfs_name); - break; case EXDEV: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot send incremental from %s:\n" - "it is not an earlier snapshot from the " - "same fs as %s"), - zhp_from->zfs_name, zhp_to->zfs_name); - break; - - case ENOENT: - /* - * Shouldn't happen because we verified the parent - * above. But there may be a race condition where it - * has since been removed. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot open: " - "no such snapshot")); - break; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "not an ealier snapshot from the same fs")); + return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); case EDQUOT: case EFBIG: @@ -2388,18 +2279,11 @@ zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from) case ERANGE: case EFAULT: case EROFS: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot write stream: %s"), - strerror(errno)); - break; - - case EINTR: - zfs_error(dgettext(TEXT_DOMAIN, - "send failed: signal received")); - break; + zfs_error_aux(hdl, strerror(errno)); + return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, errbuf)); } } @@ -2410,7 +2294,8 @@ zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from) * Restores a backup of tosnap from stdin. */ int -zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) +zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix, + int verbose, int dryrun) { zfs_cmd_t zc = { 0 }; time_t begin_time; @@ -2418,9 +2303,13 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) char *cp; dmu_replay_record_t drr; struct drr_begin *drrb = &zc.zc_begin_record; + char errbuf[1024]; begin_time = time(NULL); + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot receive")); + /* trim off snapname, if any */ (void) strcpy(zc.zc_name, tosnap); cp = strchr(zc.zc_name, '@'); @@ -2437,31 +2326,26 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) } while (size > 0); if (size < 0 || bytes != sizeof (drr)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: invalid stream " - "(couldn't read first record)")); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " + "stream (failed to read first record)")); + return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); } zc.zc_begin_record = drr.drr_u.drr_begin; if (drrb->drr_magic != DMU_BACKUP_MAGIC && drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: invalid stream " - "(invalid magic number)")); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " + "stream (bad magic number)")); + return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); } if (drrb->drr_version != DMU_BACKUP_VERSION && drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) { - if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) - drrb->drr_version = BSWAP_64(drrb->drr_version); - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: only stream version 0x%llx is supported, " - "stream is version %llx."), + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only version " + "0x%llx is supported (stream is version 0x%llx)"), DMU_BACKUP_VERSION, drrb->drr_version); - return (-1); + return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); } /* @@ -2470,10 +2354,9 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) (void) strcpy(zc.zc_filename, tosnap); if (isprefix) { if (strchr(tosnap, '@') != NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: " - "argument to -d must be a filesystem")); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "destination must be a filesystem")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); } cp = strchr(drr.drr_u.drr_begin.drr_toname, '/'); @@ -2490,11 +2373,8 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) * snapname from the backup. */ cp = strchr(drr.drr_u.drr_begin.drr_toname, '@'); - if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: invalid snapshot name")); - return (-1); - } + if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN) + return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); (void) strcat(zc.zc_filename, cp); } @@ -2508,20 +2388,16 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) *cp = '\0'; /* make sure destination fs exists */ - h = zfs_open(zc.zc_name, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); - if (h == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive incrememtal stream: destination\n" - "filesystem %s does not exist"), - zc.zc_name); + h = zfs_open(hdl, zc.zc_name, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + if (h == NULL) return (-1); - } if (!dryrun) { /* unmount destination fs or remove device link. */ if (h->zfs_type == ZFS_TYPE_FILESYSTEM) { (void) zfs_unmount(h, NULL, 0); } else { - (void) zvol_remove_link(h->zfs_name); + (void) zvol_remove_link(hdl, h->zfs_name); } } zfs_close(h); @@ -2535,24 +2411,18 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) cp = strchr(zc.zc_name, '@'); if (cp) *cp = '\0'; - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: destination fs %s already exists"), - zc.zc_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "destination '%s' already exists"), zc.zc_name); + return (zfs_error(hdl, EZFS_EXISTS, errbuf)); } if (isprefix) { zfs_handle_t *h; /* make sure prefix exists */ - h = zfs_open(tosnap, ZFS_TYPE_FILESYSTEM); - if (h == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: " - "%s is an invalid destination"), - tosnap); + h = zfs_open(hdl, tosnap, ZFS_TYPE_FILESYSTEM); + if (h == NULL) return (-1); - } zfs_close(h); /* create any necessary ancestors up to prefix */ @@ -2569,24 +2439,25 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) const char *opname; *cp = '\0'; - opname = "create"; - if (zfs_create(zc.zc_name, ZFS_TYPE_FILESYSTEM, - NULL, NULL) != 0) { + opname = dgettext(TEXT_DOMAIN, "create"); + if (zfs_create(hdl, zc.zc_name, + ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0) { if (errno == EEXIST) continue; goto ancestorerr; } - opname = "open"; - h = zfs_open(zc.zc_name, ZFS_TYPE_FILESYSTEM); + opname = dgettext(TEXT_DOMAIN, "open"); + h = zfs_open(hdl, zc.zc_name, + ZFS_TYPE_FILESYSTEM); if (h == NULL) goto ancestorerr; - opname = "mount"; + opname = dgettext(TEXT_DOMAIN, "mount"); if (zfs_mount(h, NULL, 0) != 0) goto ancestorerr; - opname = "share"; + opname = dgettext(TEXT_DOMAIN, "share"); if (zfs_share(h) != 0) goto ancestorerr; @@ -2594,22 +2465,21 @@ zfs_receive(const char *tosnap, int isprefix, int verbose, int dryrun) continue; ancestorerr: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: couldn't %s ancestor %s"), - opname, zc.zc_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "failed to %s ancestor '%s'"), opname, + zc.zc_name); + return (zfs_error(hdl, EZFS_BADRESTORE, + errbuf)); } } /* Make sure destination fs does not exist */ cp = strchr(zc.zc_name, '@'); *cp = '\0'; - if (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive full stream: " - "destination filesystem %s already exists"), - zc.zc_name); - return (-1); + if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "destination '%s' exists"), zc.zc_name); + return (zfs_error(hdl, EZFS_EXISTS, errbuf)); } /* Do the recvbackup ioctl to the fs's parent. */ @@ -2630,21 +2500,20 @@ ancestorerr: } if (dryrun) return (0); - err = ioctl_err = zfs_ioctl(ZFS_IOC_RECVBACKUP, &zc); + err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc); if (ioctl_err != 0) { switch (errno) { case ENODEV: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: " - "most recent snapshot does not " - "match incremental source")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "most recent snapshot does not match incremental " + "source")); + (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); break; case ETXTBSY: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: " - "destination has been modified since " - "most recent snapshot --\n" - "use 'zfs rollback' to discard changes")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "destination has been modified since most recent " + "snapshot")); + (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); break; case EEXIST: if (drrb->drr_fromguid == 0) { @@ -2652,45 +2521,21 @@ ancestorerr: cp = strchr(zc.zc_filename, '@'); *cp = '\0'; } - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive to %s: destination already exists"), - zc.zc_filename); - break; - case ENOENT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: destination does not exist")); - break; - case EBUSY: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: destination is in use")); - break; - case ENOSPC: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: out of space")); - break; - case EDQUOT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: quota exceeded")); - break; - case EINTR: - zfs_error(dgettext(TEXT_DOMAIN, - "receive failed: signal received")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "destination already exists")); + (void) zfs_error(hdl, EZFS_EXISTS, dgettext(TEXT_DOMAIN, + "cannot restore to %s"), zc.zc_filename); break; case EINVAL: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: invalid stream")); + (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); break; case ECKSUM: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: invalid stream " - "(checksum mismatch)")); - break; - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot receive: permission denied")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid stream (checksum mismatch)")); + (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); break; default: - zfs_baderror(errno); + (void) zfs_standard_error(hdl, errno, errbuf); } } @@ -2705,16 +2550,17 @@ ancestorerr: zfs_handle_t *h; *cp = '\0'; - h = zfs_open(zc.zc_filename, + h = zfs_open(hdl, zc.zc_filename, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); *cp = '@'; if (h) { if (h->zfs_type == ZFS_TYPE_FILESYSTEM) { err = zfs_mount(h, NULL, 0); } else { - err = zvol_create_link(h->zfs_name); + err = zvol_create_link(hdl, h->zfs_name); if (err == 0 && ioctl_err == 0) - err = zvol_create_link(zc.zc_filename); + err = zvol_create_link(hdl, + zc.zc_filename); } zfs_close(h); } @@ -2750,7 +2596,7 @@ typedef struct rollback_data { uint64_t cb_create; /* creation time reference */ prop_changelist_t *cb_clp; /* changelist pointer */ int cb_error; - int cb_dependent; + boolean_t cb_dependent; } rollback_data_t; static int @@ -2764,9 +2610,9 @@ rollback_destroy(zfs_handle_t *zhp, void *data) zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { - cbp->cb_dependent = TRUE; + cbp->cb_dependent = B_TRUE; (void) zfs_iter_dependents(zhp, rollback_destroy, cbp); - cbp->cb_dependent = FALSE; + cbp->cb_dependent = B_FALSE; if (zfs_destroy(zhp) != 0) cbp->cb_error = 1; @@ -2797,7 +2643,7 @@ do_rollback(zfs_handle_t *zhp) zhp->zfs_type == ZFS_TYPE_VOLUME); if (zhp->zfs_type == ZFS_TYPE_VOLUME && - zvol_remove_link(zhp->zfs_name) != 0) + zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) return (-1); (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); @@ -2814,58 +2660,13 @@ do_rollback(zfs_handle_t *zhp) * condition where the user has taken a snapshot since we verified that * this was the most recent. */ - if ((ret = zfs_ioctl(ZFS_IOC_ROLLBACK, &zc)) != 0) { - switch (errno) { - case EPERM: - /* - * The user doesn't have permission to rollback the - * given dataset. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rollback '%s': " - "permission denied"), zhp->zfs_name); - break; - - case EDQUOT: - case ENOSPC: - /* - * The parent dataset doesn't have enough space to - * rollback to the last snapshot. - */ - { - char parent[ZFS_MAXNAMELEN]; - (void) parent_name(zhp->zfs_name, parent, - sizeof (parent)); - zfs_error(dgettext(TEXT_DOMAIN, "cannot " - "rollback '%s': out of space"), parent); - } - break; - - case ENOENT: - /* - * The dataset doesn't exist. This shouldn't happen - * except in race conditions. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rollback '%s': " - "no such %s"), zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); - break; - - case EBUSY: - /* - * The filesystem is busy. This should have been caught - * by the caller before getting here, but there may be - * an unexpected problem. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rollback '%s': " - "%s is busy"), zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); - break; - - default: - zfs_baderror(errno); - } + if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK, + &zc)) != 0) { + (void) zfs_standard_error(zhp->zfs_hdl, errno, + dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), + zhp->zfs_name); } else if (zhp->zfs_type == ZFS_TYPE_VOLUME) { - ret = zvol_create_link(zhp->zfs_name); + ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); } return (ret); @@ -2946,9 +2747,10 @@ zfs_iter_dependents(zfs_handle_t *zhp, zfs_iter_f func, void *data) zfs_handle_t *child; int ret = 0; - dependents = get_dependents(zhp->zfs_name, &count); + dependents = get_dependents(zhp->zfs_hdl, zhp->zfs_name, &count); for (i = 0; i < count; i++) { - if ((child = make_dataset_handle(dependents[i])) == NULL) + if ((child = make_dataset_handle(zhp->zfs_hdl, + dependents[i])) == NULL) continue; if ((ret = func(child, data)) != 0) @@ -2970,10 +2772,11 @@ zfs_rename(zfs_handle_t *zhp, const char *target) { int ret; zfs_cmd_t zc = { 0 }; - char reason[64]; char *delim; prop_changelist_t *cl; char parent[ZFS_MAXNAMELEN]; + libzfs_handle_t *hdl = zhp->zfs_hdl; + char errbuf[1024]; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); (void) strlcpy(zc.zc_prop_value, target, sizeof (zc.zc_prop_value)); @@ -2982,22 +2785,21 @@ zfs_rename(zfs_handle_t *zhp, const char *target) if (strcmp(zhp->zfs_name, target) == 0) return (0); + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot rename to '%s'"), target); + /* * Make sure the target name is valid */ - if (!zfs_validate_name(target, zhp->zfs_type, reason, - sizeof (reason))) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create '%s': %s in %s name"), target, reason, - zfs_type_to_name(zhp->zfs_type)); - return (-1); - } + if (!zfs_validate_name(hdl, target, zhp->zfs_type)) + return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { + if ((delim = strchr(target, '@')) == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot rename to '%s': not a snapshot"), target); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "not a snapshot")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); } /* @@ -3005,17 +2807,16 @@ zfs_rename(zfs_handle_t *zhp, const char *target) */ if (strncmp(zhp->zfs_name, target, delim - target) != 0 || zhp->zfs_name[delim - target] != '@') { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot rename to '%s': snapshots must be part " - "of same dataset"), target); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "snapshots must be part of same dataset")); + return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); } (void) strncpy(parent, target, delim - target); parent[delim - target] = '\0'; } else { /* validate parents */ - if (check_parents(target, zhp->zfs_type) != 0) + if (check_parents(hdl, target) != 0) return (-1); (void) parent_name(target, parent, sizeof (parent)); @@ -3024,28 +2825,30 @@ zfs_rename(zfs_handle_t *zhp, const char *target) verify((delim = strchr(target, '/')) != NULL); if (strncmp(zhp->zfs_name, target, delim - target) != 0 || zhp->zfs_name[delim - target] != '/') { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot rename to '%s': " - "datasets must be within same pool"), target); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "datasets must be within same pool")); + return (zfs_error(hdl, EZFS_CROSSTARGET, 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(dgettext(TEXT_DOMAIN, "cannot rename %s, " - "dataset is used in a non-global zone"), zhp->zfs_name); - return (-1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset is used in a non-global zone")); + return (zfs_error(hdl, EZFS_ZONED, errbuf)); } if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL) - return (1); + return (-1); if (changelist_haszonedchild(cl)) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot rename '%s': child dataset with inherited " - "mountpoint is used in a non-global zone"), zhp->zfs_name); - ret = -1; + 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; } @@ -3057,59 +2860,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target) else zc.zc_objset_type = DMU_OST_ZFS; - if ((ret = zfs_ioctl(ZFS_IOC_RENAME, &zc)) != 0) { - switch (errno) { - case EPERM: - /* - * The user doesn't have permission to rename the - * given dataset. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s': " - "permission denied"), zhp->zfs_name); - break; - - case EDQUOT: - case ENOSPC: - /* - * Not enough space in the parent dataset. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot " - "rename '%s': not enough space in '%s'"), - zhp->zfs_name, parent); - break; - - case ENOENT: - /* - * The destination doesn't exist. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s' " - "to '%s': destination doesn't exist"), - zhp->zfs_name, target); - break; - - case EEXIST: - /* - * The destination already exists. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s' " - "to '%s': destination already exists"), - zhp->zfs_name, target); - break; - - case EBUSY: - /* - * The filesystem is busy. This should have been caught - * by the caller before getting here, but there may be - * an unexpected problem. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s': " - "%s is busy"), zhp->zfs_name, - zfs_type_to_name(zhp->zfs_type)); - break; - - default: - zfs_baderror(errno); - } + if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) { + (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); /* * On failure, we still want to remount any filesystems that @@ -3132,24 +2884,18 @@ error: * poke devfsadm to create the /dev link, and then wait for the link to appear. */ int -zvol_create_link(const char *dataset) +zvol_create_link(libzfs_handle_t *hdl, const char *dataset) { zfs_cmd_t zc = { 0 }; - di_devlink_handle_t hdl; + di_devlink_handle_t dhdl; (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); /* * Issue the appropriate ioctl. */ - if (zfs_ioctl(ZFS_IOC_CREATE_MINOR, &zc) != 0) { + if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, "cannot create " - "device links for '%s': permission denied"), - dataset); - break; - case EEXIST: /* * Silently ignore the case where the link already @@ -3159,22 +2905,24 @@ zvol_create_link(const char *dataset) return (0); default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot create device links " + "for '%s'"), dataset)); } - - return (-1); } /* * Call devfsadm and wait for the links to magically appear. */ - if ((hdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot create device links for '%s'"), dataset); - (void) zfs_ioctl(ZFS_IOC_REMOVE_MINOR, &zc); + if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) { + zfs_error_aux(hdl, strerror(errno)); + (void) zfs_error(hdl, EZFS_DEVLINKS, + dgettext(TEXT_DOMAIN, "cannot create device links " + "for '%s'"), dataset); + (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc); return (-1); } else { - (void) di_devlink_fini(&hdl); + (void) di_devlink_fini(&dhdl); } return (0); @@ -3184,26 +2932,14 @@ zvol_create_link(const char *dataset) * Remove a minor node for the given zvol and the associated /dev links. */ int -zvol_remove_link(const char *dataset) +zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) { zfs_cmd_t zc = { 0 }; (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); - if (zfs_ioctl(ZFS_IOC_REMOVE_MINOR, &zc) != 0) { + if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, "cannot remove " - "device links for '%s': permission denied"), - dataset); - break; - - case EBUSY: - zfs_error(dgettext(TEXT_DOMAIN, "cannot remove " - "device links for '%s': volume is in use"), - dataset); - break; - case ENXIO: /* * Silently ignore the case where the link no longer @@ -3213,10 +2949,10 @@ zvol_remove_link(const char *dataset) return (0); default: - zfs_baderror(errno); + return (zfs_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot remove device " + "links for '%s'"), dataset)); } - - return (-1); } return (0); diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c index 4c7bb547ee..e86a6c9377 100644 --- a/usr/src/lib/libzfs/common/libzfs_graph.c +++ b/usr/src/lib/libzfs/common/libzfs_graph.c @@ -121,9 +121,12 @@ typedef struct zfs_graph { * Allocate a new edge pointing to the target vertex. */ static zfs_edge_t * -zfs_edge_create(zfs_vertex_t *dest) +zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest) { - zfs_edge_t *zep = zfs_malloc(sizeof (zfs_edge_t)); + zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t)); + + if (zep == NULL) + return (NULL); zep->ze_dest = dest; @@ -143,15 +146,23 @@ zfs_edge_destroy(zfs_edge_t *zep) * Allocate a new vertex with the given name. */ static zfs_vertex_t * -zfs_vertex_create(const char *dataset) +zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset) { - zfs_vertex_t *zvp = zfs_malloc(sizeof (zfs_vertex_t)); + zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t)); + + if (zvp == NULL) + return (NULL); assert(strlen(dataset) < ZFS_MAXNAMELEN); (void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset)); - zvp->zv_edges = zfs_malloc(MIN_EDGECOUNT * sizeof (void *)); + if ((zvp->zv_edges = zfs_alloc(hdl, + MIN_EDGECOUNT * sizeof (void *))) == NULL) { + free(zvp); + return (NULL); + } + zvp->zv_edgealloc = MIN_EDGECOUNT; return (zvp); @@ -175,15 +186,22 @@ zfs_vertex_destroy(zfs_vertex_t *zvp) /* * Given a vertex, add an edge to the destination vertex. */ -static void -zfs_vertex_add_edge(zfs_vertex_t *zvp, zfs_vertex_t *dest) +static int +zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp, + zfs_vertex_t *dest) { - zfs_edge_t *zep = zfs_edge_create(dest); + zfs_edge_t *zep = zfs_edge_create(hdl, dest); + + if (zep == NULL) + return (-1); if (zvp->zv_edgecount == zvp->zv_edgealloc) { - zfs_edge_t **newedges = zfs_malloc(zvp->zv_edgealloc * 2 * + zfs_edge_t **newedges = zfs_alloc(hdl, zvp->zv_edgealloc * 2 * sizeof (void *)); + if (newedges == NULL) + return (-1); + bcopy(zvp->zv_edges, newedges, zvp->zv_edgealloc * sizeof (void *)); @@ -193,6 +211,8 @@ zfs_vertex_add_edge(zfs_vertex_t *zvp, zfs_vertex_t *dest) } zvp->zv_edges[zvp->zv_edgecount++] = zep; + + return (0); } static int @@ -227,12 +247,19 @@ zfs_vertex_sort_edges(zfs_vertex_t *zvp) * datasets in the pool. */ static zfs_graph_t * -zfs_graph_create(size_t size) +zfs_graph_create(libzfs_handle_t *hdl, size_t size) { - zfs_graph_t *zgp = zfs_malloc(sizeof (zfs_graph_t)); + zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t)); + + if (zgp == NULL) + return (NULL); zgp->zg_size = size; - zgp->zg_hash = zfs_malloc(size * sizeof (zfs_vertex_t *)); + if ((zgp->zg_hash = zfs_alloc(hdl, + size * sizeof (zfs_vertex_t *))) == NULL) { + free(zgp); + return (NULL); + } return (zgp); } @@ -280,7 +307,8 @@ zfs_graph_hash(zfs_graph_t *zgp, const char *str) * Given a dataset name, finds the associated vertex, creating it if necessary. */ static zfs_vertex_t * -zfs_graph_lookup(zfs_graph_t *zgp, const char *dataset, uint64_t txg) +zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset, + uint64_t txg) { size_t idx = zfs_graph_hash(zgp, dataset); zfs_vertex_t *zvp; @@ -293,7 +321,9 @@ zfs_graph_lookup(zfs_graph_t *zgp, const char *dataset, uint64_t txg) } } - zvp = zfs_vertex_create(dataset); + if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL) + return (NULL); + zvp->zv_next = zgp->zg_hash[idx]; zvp->zv_txg = txg; zgp->zg_hash[idx] = zvp; @@ -308,43 +338,52 @@ zfs_graph_lookup(zfs_graph_t *zgp, const char *dataset, uint64_t txg) * created it as a destination of another edge. If 'dest' is NULL, then this * is an individual vertex (i.e. the starting vertex), so don't add an edge. */ -static void -zfs_graph_add(zfs_graph_t *zgp, const char *source, const char *dest, - uint64_t txg) +static int +zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source, + const char *dest, uint64_t txg) { zfs_vertex_t *svp, *dvp; - svp = zfs_graph_lookup(zgp, source, 0); + if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL) + return (-1); svp->zv_visited = 1; if (dest != NULL) { - dvp = zfs_graph_lookup(zgp, dest, txg); - zfs_vertex_add_edge(svp, dvp); + dvp = zfs_graph_lookup(hdl, zgp, dest, txg); + if (dvp == NULL) + return (-1); + if (zfs_vertex_add_edge(hdl, svp, dvp) != 0) + return (-1); } + + return (0); } /* * Iterate over all children of the given dataset, adding any vertices as - * necessary. Returns 0 if no cloned snapshots were seen, 1 otherwise. This is + * necessary. Returns 0 if no cloned snapshots were seen, -1 if there was an + * error, or 1 otherwise. This is * a simple recursive algorithm - the ZFS namespace typically is very flat. We * manually invoke the necessary ioctl() calls to avoid the overhead and * additional semantics of zfs_open(). */ static int -iterate_children(zfs_graph_t *zgp, const char *dataset) +iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset) { zfs_cmd_t zc = { 0 }; - int ret = 0; + int ret = 0, err; zfs_vertex_t *zvp; /* * Look up the source vertex, and avoid it if we've seen it before. */ - zvp = zfs_graph_lookup(zgp, dataset, 0); + zvp = zfs_graph_lookup(hdl, zgp, dataset, 0); + if (zvp == NULL) + return (-1); if (zvp->zv_visited) return (0); for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); - zfs_ioctl(ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; + ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) { /* @@ -358,32 +397,38 @@ iterate_children(zfs_graph_t *zgp, const char *dataset) * dataset and clone statistics. If this fails, the dataset has * since been removed, and we're pretty much screwed anyway. */ - if (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) != 0) + if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) continue; /* * Add an edge between the parent and the child. */ - zfs_graph_add(zgp, dataset, zc.zc_name, - zc.zc_objset_stats.dds_creation_txg); + if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name, + zc.zc_objset_stats.dds_creation_txg) != 0) + return (-1); /* * If this dataset has a clone parent, add an appropriate edge. */ - if (zc.zc_objset_stats.dds_clone_of[0] != '\0') - zfs_graph_add(zgp, zc.zc_objset_stats.dds_clone_of, - zc.zc_name, zc.zc_objset_stats.dds_creation_txg); + if (zc.zc_objset_stats.dds_clone_of[0] != '\0' && + zfs_graph_add(hdl, zgp, zc.zc_objset_stats.dds_clone_of, + zc.zc_name, zc.zc_objset_stats.dds_creation_txg) != 0) + return (-1); /* * Iterate over all children */ - ret |= iterate_children(zgp, zc.zc_name); + err = iterate_children(hdl, zgp, zc.zc_name); + if (err == -1) + return (-1); + else if (err == 1) + ret = 1; /* * Indicate if we found a dataset with a non-zero clone count. */ if (zc.zc_objset_stats.dds_num_clones != 0) - ret |= 1; + ret = 1; } /* @@ -392,7 +437,7 @@ iterate_children(zfs_graph_t *zgp, const char *dataset) bzero(&zc, sizeof (zc)); for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); - zfs_ioctl(ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0; + ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0; (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) { /* @@ -400,20 +445,21 @@ iterate_children(zfs_graph_t *zgp, const char *dataset) * dataset and clone statistics. If this fails, the dataset has * since been removed, and we're pretty much screwed anyway. */ - if (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) != 0) + if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) continue; /* * Add an edge between the parent and the child. */ - zfs_graph_add(zgp, dataset, zc.zc_name, - zc.zc_objset_stats.dds_creation_txg); + if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name, + zc.zc_objset_stats.dds_creation_txg) != 0) + return (-1); /* * Indicate if we found a dataset with a non-zero clone count. */ if (zc.zc_objset_stats.dds_num_clones != 0) - ret |= 1; + ret = 1; } zvp->zv_visited = 1; @@ -428,20 +474,24 @@ iterate_children(zfs_graph_t *zgp, const char *dataset) * over all datasets. */ static zfs_graph_t * -construct_graph(const char *dataset) +construct_graph(libzfs_handle_t *hdl, const char *dataset) { - zfs_graph_t *zgp = zfs_graph_create(ZFS_GRAPH_SIZE); + zfs_graph_t *zgp = zfs_graph_create(hdl, ZFS_GRAPH_SIZE); zfs_cmd_t zc = { 0 }; + int ret = 0; + + if (zgp == NULL) + return (zgp); /* * We need to explicitly check whether this dataset has clones or not, * since iterate_children() only checks the children. */ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); - (void) zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc); + (void) ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc); if (zc.zc_objset_stats.dds_num_clones != 0 || - iterate_children(zgp, dataset) != 0) { + (ret = iterate_children(hdl, zgp, dataset)) != 0) { /* * Determine pool name and try again. */ @@ -449,17 +499,29 @@ construct_graph(const char *dataset) if ((slash = strchr(dataset, '/')) != NULL || (slash = strchr(dataset, '@')) != NULL) { - pool = zfs_malloc(slash - dataset + 1); + pool = zfs_alloc(hdl, slash - dataset + 1); + if (pool == NULL) { + zfs_graph_destroy(zgp); + return (NULL); + } (void) strncpy(pool, dataset, slash - dataset); pool[slash - dataset] = '\0'; - (void) iterate_children(zgp, pool); - zfs_graph_add(zgp, pool, NULL, 0); + if (iterate_children(hdl, zgp, pool) == -1 || + zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) { + free(pool); + zfs_graph_destroy(zgp); + return (NULL); + } free(pool); } } - zfs_graph_add(zgp, dataset, NULL, 0); + + if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) { + zfs_graph_destroy(zgp); + return (NULL); + } return (zgp); } @@ -469,27 +531,33 @@ construct_graph(const char *dataset) * really just a depth first search, so that the deepest nodes appear first. * hijack the 'zv_visited' marker to avoid visiting the same vertex twice. */ -static void -topo_sort(char **result, size_t *idx, zfs_vertex_t *zgv) +static int +topo_sort(libzfs_handle_t *hdl, char **result, size_t *idx, zfs_vertex_t *zgv) { int i; /* avoid doing a search if we don't have to */ if (zgv->zv_visited == 2) - return; + return (0); zfs_vertex_sort_edges(zgv); - for (i = 0; i < zgv->zv_edgecount; i++) - topo_sort(result, idx, zgv->zv_edges[i]->ze_dest); + for (i = 0; i < zgv->zv_edgecount; i++) { + if (topo_sort(hdl, result, idx, zgv->zv_edges[i]->ze_dest) != 0) + return (-1); + } /* we may have visited this in the course of the above */ if (zgv->zv_visited == 2) - return; + return (0); + + if ((result[*idx] = zfs_alloc(hdl, + strlen(zgv->zv_dataset) + 1)) == NULL) + return (-1); - result[*idx] = zfs_malloc(strlen(zgv->zv_dataset) + 1); (void) strcpy(result[*idx], zgv->zv_dataset); *idx += 1; zgv->zv_visited = 2; + return (0); } /* @@ -498,19 +566,33 @@ topo_sort(char **result, size_t *idx, zfs_vertex_t *zgv) * sort, and then return the array of strings to the caller. */ char ** -get_dependents(const char *dataset, size_t *count) +get_dependents(libzfs_handle_t *hdl, const char *dataset, size_t *count) { char **result; zfs_graph_t *zgp; zfs_vertex_t *zvp; - zgp = construct_graph(dataset); - result = zfs_malloc(zgp->zg_nvertex * sizeof (char *)); + if ((zgp = construct_graph(hdl, dataset)) == NULL) + return (NULL); - zvp = zfs_graph_lookup(zgp, dataset, 0); + if ((result = zfs_alloc(hdl, + zgp->zg_nvertex * sizeof (char *))) == NULL) { + zfs_graph_destroy(zgp); + return (NULL); + } + + if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) { + free(result); + zfs_graph_destroy(zgp); + return (NULL); + } *count = 0; - topo_sort(result, count, zvp); + if (topo_sort(hdl, result, count, zvp) != 0) { + free(result); + zfs_graph_destroy(zgp); + return (NULL); + } /* * Get rid of the last entry, which is our starting vertex and not diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index 76bca21242..2c5e890767 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -34,13 +34,29 @@ #include <sys/zfs_acl.h> #include <sys/nvpair.h> +#include <libuutil.h> #include <libzfs.h> #ifdef __cplusplus extern "C" { #endif +struct libzfs_handle { + int libzfs_error; + int libzfs_fd; + FILE *libzfs_mnttab; + FILE *libzfs_sharetab; + 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; +}; + struct zfs_handle { + libzfs_handle_t *zfs_hdl; char zfs_name[ZFS_MAXNAMELEN]; zfs_type_t zfs_type; dmu_objset_stats_t zfs_dmustats; @@ -52,6 +68,7 @@ struct zfs_handle { }; struct zpool_handle { + libzfs_handle_t *zpool_hdl; char zpool_name[ZPOOL_MAXNAMELEN]; int zpool_state; size_t zpool_config_size; @@ -61,18 +78,16 @@ struct zpool_handle { size_t zpool_error_count; }; -void zfs_error(const char *, ...); -void zfs_fatal(const char *, ...); -void *zfs_malloc(size_t); -char *zfs_strdup(const char *); -void no_memory(void); +int zfs_error(libzfs_handle_t *, int, const char *, ...); +void zfs_error_aux(libzfs_handle_t *, const char *, ...); +void *zfs_alloc(libzfs_handle_t *, size_t); +char *zfs_strdup(libzfs_handle_t *, const char *); +int no_memory(libzfs_handle_t *); -#define zfs_baderror(err) \ - (zfs_fatal(dgettext(TEXT_DOMAIN, \ - "internal error: unexpected error %d at line %d of %s"), \ - (err), (__LINE__), (__FILE__))) +int zfs_standard_error(libzfs_handle_t *, int, const char *, ...); +int zpool_standard_error(libzfs_handle_t *, int, const char *, ...); -char **get_dependents(const char *, size_t *); +char **get_dependents(libzfs_handle_t *, const char *, size_t *); typedef struct prop_changelist prop_changelist_t; @@ -87,17 +102,15 @@ int changelist_haszonedchild(prop_changelist_t *); void remove_mountpoint(zfs_handle_t *); -zfs_handle_t *make_dataset_handle(const char *); -void set_pool_health(nvlist_t *); +zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *); +int set_pool_health(nvlist_t *); -zpool_handle_t *zpool_open_silent(const char *); +zpool_handle_t *zpool_open_silent(libzfs_handle_t *, const char *); -int zvol_create_link(const char *); -int zvol_remove_link(const char *); +int zvol_create_link(libzfs_handle_t *, const char *); +int zvol_remove_link(libzfs_handle_t *, const char *); -int zfs_ioctl(int, zfs_cmd_t *); -FILE *zfs_mnttab(void); -FILE *zfs_sharetab(void); +void namespace_clear(libzfs_handle_t *); #ifdef __cplusplus } diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c index 98519c3aae..ef34419146 100644 --- a/usr/src/lib/libzfs/common/libzfs_import.c +++ b/usr/src/lib/libzfs/common/libzfs_import.c @@ -78,7 +78,7 @@ typedef struct pool_entry { } pool_entry_t; typedef struct name_entry { - const char *ne_name; + char *ne_name; uint64_t ne_guid; struct name_entry *ne_next; } name_entry_t; @@ -117,7 +117,7 @@ get_devid(const char *path) * Go through and fix up any path and/or devid information for the given vdev * configuration. */ -static void +static int fix_paths(nvlist_t *nv, name_entry_t *names) { nvlist_t **child; @@ -130,8 +130,9 @@ fix_paths(nvlist_t *nv, name_entry_t *names) if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; c++) - fix_paths(child[c], names); - return; + if (fix_paths(child[c], names) != 0) + return (-1); + return (0); } /* @@ -182,31 +183,56 @@ fix_paths(nvlist_t *nv, name_entry_t *names) } if (best == NULL) - return; + return (0); - verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) == 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 { - verify(nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) == 0); + if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) + return (-1); devid_str_free(devid); } + + return (0); } /* * Add the given configuration to the list of known devices. */ -static void -add_config(pool_list_t *pl, const char *path, nvlist_t *config) +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; + 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, 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 && + 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 @@ -223,7 +249,7 @@ add_config(pool_list_t *pl, const char *path, nvlist_t *config) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) != 0 || txg == 0) { nvlist_free(config); - return; + return (0); } /* @@ -236,7 +262,10 @@ add_config(pool_list_t *pl, const char *path, nvlist_t *config) } if (pe == NULL) { - pe = zfs_malloc(sizeof (pool_entry_t)); + if ((pe = zfs_alloc(hdl, sizeof (pool_entry_t))) == NULL) { + nvlist_free(config); + return (-1); + } pe->pe_guid = pool_guid; pe->pe_next = pl->pools; pl->pools = pe; @@ -252,7 +281,10 @@ add_config(pool_list_t *pl, const char *path, nvlist_t *config) } if (ve == NULL) { - ve = zfs_malloc(sizeof (vdev_entry_t)); + if ((ve = zfs_alloc(hdl, sizeof (vdev_entry_t))) == NULL) { + nvlist_free(config); + return (-1); + } ve->ve_guid = top_guid; ve->ve_next = pe->pe_vdevs; pe->pe_vdevs = ve; @@ -269,7 +301,10 @@ add_config(pool_list_t *pl, const char *path, nvlist_t *config) } if (ce == NULL) { - ce = zfs_malloc(sizeof (config_entry_t)); + if ((ce = zfs_alloc(hdl, sizeof (config_entry_t))) == NULL) { + nvlist_free(config); + return (-1); + } ce->ce_txg = txg; ce->ce_config = config; ce->ce_next = ve->ve_configs; @@ -284,24 +319,31 @@ add_config(pool_list_t *pl, const char *path, nvlist_t *config) * mappings so that we can fix up the configuration as necessary before * doing the import. */ - ne = zfs_malloc(sizeof (name_entry_t)); + 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_name = zfs_strdup(path); 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. */ -boolean_t -pool_active(const char *name, uint64_t guid) +static boolean_t +pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid) { zpool_handle_t *zhp; uint64_t theguid; - if ((zhp = zpool_open_silent(name)) == NULL) + if ((zhp = zpool_open_silent(hdl, name)) == NULL) return (B_FALSE); verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID, @@ -320,41 +362,42 @@ pool_active(const char *name, uint64_t guid) * return to the user. */ static nvlist_t * -get_configs(pool_list_t *pl) +get_configs(libzfs_handle_t *hdl, pool_list_t *pl) { - pool_entry_t *pe, *penext; - vdev_entry_t *ve, *venext; - config_entry_t *ce, *cenext; - nvlist_t *ret, *config, *tmp, *nvtop, *nvroot; - int config_seen; + pool_entry_t *pe; + vdev_entry_t *ve; + config_entry_t *ce; + nvlist_t *ret = NULL, *config = NULL, *tmp, *nvtop, *nvroot; + nvlist_t **spares; + uint_t i, nspares; + boolean_t config_seen; uint64_t best_txg; char *name; zfs_cmd_t zc = { 0 }; - uint64_t guid; + uint64_t version, guid; char *packed; size_t len; int err; + uint_t children = 0; + nvlist_t **child = NULL; + uint_t c; - verify(nvlist_alloc(&ret, 0, 0) == 0); + if (nvlist_alloc(&ret, 0, 0) != 0) + goto nomem; - for (pe = pl->pools; pe != NULL; pe = penext) { - uint_t c; - uint_t children = 0; + for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { uint64_t id; - nvlist_t **child = NULL; - penext = pe->pe_next; - - verify(nvlist_alloc(&config, NV_UNIQUE_NAME, 0) == 0); - config_seen = FALSE; + 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 = venext) { - venext = ve->ve_next; + for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { /* * Determine the best configuration for this vdev by @@ -365,8 +408,10 @@ get_configs(pool_list_t *pl) for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) { - if (ce->ce_txg > best_txg) + if (ce->ce_txg > best_txg) { tmp = ce->ce_config; + best_txg = ce->ce_txg; + } } if (!config_seen) { @@ -374,6 +419,7 @@ get_configs(pool_list_t *pl) * Copy the relevant pieces of data to the pool * configuration: * + * version * pool guid * name * pool state @@ -381,19 +427,27 @@ get_configs(pool_list_t *pl) uint64_t state; verify(nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_VERSION, &version) == 0); + if (nvlist_add_uint64(config, + ZPOOL_CONFIG_VERSION, version) != 0) + goto nomem; + verify(nvlist_lookup_uint64(tmp, ZPOOL_CONFIG_POOL_GUID, &guid) == 0); - verify(nvlist_add_uint64(config, - ZPOOL_CONFIG_POOL_GUID, guid) == 0); + if (nvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_GUID, guid) != 0) + goto nomem; verify(nvlist_lookup_string(tmp, ZPOOL_CONFIG_POOL_NAME, &name) == 0); - verify(nvlist_add_string(config, - ZPOOL_CONFIG_POOL_NAME, name) == 0); + if (nvlist_add_string(config, + ZPOOL_CONFIG_POOL_NAME, name) != 0) + goto nomem; verify(nvlist_lookup_uint64(tmp, ZPOOL_CONFIG_POOL_STATE, &state) == 0); - verify(nvlist_add_uint64(config, - ZPOOL_CONFIG_POOL_STATE, state) == 0); + if (nvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_STATE, state) != 0) + goto nomem; - config_seen = TRUE; + config_seen = B_TRUE; } /* @@ -406,8 +460,10 @@ get_configs(pool_list_t *pl) if (id >= children) { nvlist_t **newchild; - newchild = zfs_malloc((id + 1) * + newchild = zfs_alloc(hdl, (id + 1) * sizeof (nvlist_t *)); + if (newchild == NULL) + goto nomem; for (c = 0; c < children; c++) newchild[c] = child[c]; @@ -416,23 +472,9 @@ get_configs(pool_list_t *pl) child = newchild; children = id + 1; } - verify(nvlist_dup(nvtop, &child[id], 0) == 0); + if (nvlist_dup(nvtop, &child[id], 0) != 0) + goto nomem; - /* - * Go through and free all config information. - */ - for (ce = ve->ve_configs; ce != NULL; ce = cenext) { - cenext = ce->ce_next; - - nvlist_free(ce->ce_config); - free(ce); - } - - /* - * Free this vdev entry, since it has now been merged - * into the main config. - */ - free(ve); } verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, @@ -448,51 +490,63 @@ get_configs(pool_list_t *pl) for (c = 0; c < children; c++) if (child[c] == NULL) { nvlist_t *missing; - verify(nvlist_alloc(&missing, NV_UNIQUE_NAME, - 0) == 0); - verify(nvlist_add_string(missing, - ZPOOL_CONFIG_TYPE, VDEV_TYPE_MISSING) == 0); - verify(nvlist_add_uint64(missing, - ZPOOL_CONFIG_ID, c) == 0); - verify(nvlist_add_uint64(missing, - ZPOOL_CONFIG_GUID, 0ULL) == 0); + 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. */ - verify(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) == 0); - verify(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, - VDEV_TYPE_ROOT) == 0); - verify(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0); - verify(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) == 0); - verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - child, children) == 0); + 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. */ - fix_paths(nvroot, pl->names); + if (fix_paths(nvroot, pl->names) != 0) { + nvlist_free(nvroot); + goto nomem; + } /* * Add the root vdev to this pool's configuration. */ - verify(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, - nvroot) == 0); + if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + nvroot) != 0) { + nvlist_free(nvroot); + goto nomem; + } nvlist_free(nvroot); /* - * Free this pool entry. - */ - free(pe); - - /* * Determine if this pool is currently active, in which case we * can't actually import it. */ @@ -501,8 +555,9 @@ get_configs(pool_list_t *pl) verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) == 0); - if (pool_active(name, guid)) { + if (pool_active(hdl, name, guid)) { nvlist_free(config); + config = NULL; continue; } @@ -510,13 +565,14 @@ get_configs(pool_list_t *pl) * Try to do the import in order to get vdev state. */ if ((err = nvlist_size(config, &len, NV_ENCODE_NATIVE)) != 0) - zfs_baderror(err); + goto nomem; - packed = zfs_malloc(len); + if ((packed = zfs_alloc(hdl, len)) == NULL) + goto nomem; if ((err = nvlist_pack(config, &packed, &len, NV_ENCODE_NATIVE, 0)) != 0) - zfs_baderror(err); + goto nomem; nvlist_free(config); config = NULL; @@ -525,37 +581,76 @@ get_configs(pool_list_t *pl) zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_dst_size = 2 * len; - zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uint64_t)(uintptr_t) + zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL) + goto nomem; - while ((err = zfs_ioctl(ZFS_IOC_POOL_TRYIMPORT, + while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT, &zc)) != 0 && errno == ENOMEM) { free((void *)(uintptr_t)zc.zc_config_dst); - zc.zc_config_dst = (uint64_t)(uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uint64_t)(uintptr_t) + zfs_alloc(hdl, zc.zc_config_dst_size)) == NULL) + goto nomem; } free(packed); - if (err) - zfs_baderror(errno); + if (err) { + (void) zpool_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot discover pools")); + free((void *)(uintptr_t)zc.zc_config_dst); + goto error; + } - verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, - zc.zc_config_dst_size, &config, 0) == 0); + if (nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst, + zc.zc_config_dst_size, &config, 0) != 0) { + free((void *)(uintptr_t)zc.zc_config_dst); + goto nomem; + } + free((void *)(uintptr_t)zc.zc_config_dst); - set_pool_health(config); + /* + * 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; + } + } + + if (set_pool_health(config) != 0) + goto nomem; /* * Add this pool to the list of configs. */ - verify(nvlist_add_nvlist(ret, name, config) == 0); + if (nvlist_add_nvlist(ret, name, config) != 0) + goto nomem; nvlist_free(config); - - free((void *)(uintptr_t)zc.zc_config_dst); + config = NULL; } return (ret); + +nomem: + (void) no_memory(hdl); +error: + if (config) + nvlist_free(config); + if (ret) + nvlist_free(ret); + for (c = 0; c < children; c++) + nvlist_free(child[c]); + if (child) + free(child); + + return (NULL); } /* @@ -572,19 +667,21 @@ label_offset(size_t size, int l) * Given a file descriptor, read the label information and return an nvlist * describing the configuration, if there is one. */ -nvlist_t * -zpool_read_label(int fd) +int +zpool_read_label(int fd, nvlist_t **config) { struct stat64 statbuf; int l; vdev_label_t *label; - nvlist_t *config; uint64_t state, txg; + *config = NULL; + if (fstat64(fd, &statbuf) == -1) - return (NULL); + return (0); - label = zfs_malloc(sizeof (vdev_label_t)); + if ((label = malloc(sizeof (vdev_label_t))) == NULL) + return (-1); for (l = 0; l < VDEV_LABELS; l++) { if (pread(fd, label, sizeof (vdev_label_t), @@ -592,27 +689,29 @@ zpool_read_label(int fd) continue; if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, - sizeof (label->vl_vdev_phys.vp_nvlist), &config, 0) != 0) + 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_DESTROYED) { - nvlist_free(config); + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, + &state) != 0 || state > POOL_STATE_SPARE) { + nvlist_free(*config); continue; } - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, - &txg) != 0 || txg == 0) { - nvlist_free(config); + if (state != POOL_STATE_SPARE && + (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0)) { + nvlist_free(*config); continue; } free(label); - return (config); + return (0); } free(label); - return (NULL); + *config = NULL; + return (0); } /* @@ -621,17 +720,22 @@ zpool_read_label(int fd) * given (argc is 0), then the default directory (/dev/dsk) is searched. */ nvlist_t * -zpool_find_import(int argc, char **argv) +zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv) { int i; DIR *dirp; struct dirent64 *dp; char path[MAXPATHLEN]; struct stat64 statbuf; - nvlist_t *ret, *config; + nvlist_t *ret = NULL, *config; static char *default_dir = "/dev/dsk"; int fd; 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; + if (argc == 0) { argc = 1; @@ -645,17 +749,18 @@ zpool_find_import(int argc, char **argv) */ for (i = 0; i < argc; i++) { if (argv[i][0] != '/') { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': must be an absolute path"), + (void) zfs_error(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), argv[i]); - return (NULL); + goto error; } if ((dirp = opendir(argv[i])) == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot open '%s': %s"), argv[i], - strerror(errno)); - return (NULL); + zfs_error_aux(hdl, strerror(errno)); + (void) zfs_error(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), + argv[i]); + goto error; } /* @@ -678,21 +783,49 @@ zpool_find_import(int argc, char **argv) if ((fd = open64(path, O_RDONLY)) < 0) continue; - config = zpool_read_label(fd); + if ((zpool_read_label(fd, &config)) != 0) { + (void) no_memory(hdl); + goto error; + } (void) close(fd); if (config != NULL) - add_config(&pools, path, config); + if (add_config(hdl, &pools, path, config) != 0) + goto error; } } - ret = get_configs(&pools); + ret = get_configs(hdl, &pools); + +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; + if (ce->ce_config) + nvlist_free(ce->ce_config); + free(ce); + } + free(ve); + } + free(pe); + } + + for (ne = pools.names; ne != NULL; ne = nenext) { + nenext = ne->ne_next; + if (ne->ne_name) + free(ne->ne_name); + free(ne); + } + return (ret); } -int +boolean_t find_guid(nvlist_t *nv, uint64_t guid) { uint64_t tmp; @@ -701,49 +834,94 @@ find_guid(nvlist_t *nv, uint64_t guid) verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &tmp) == 0); if (tmp == guid) - return (TRUE); + 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 (TRUE); + return (B_TRUE); + } + + return (B_FALSE); +} + +typedef struct spare_cbdata { + uint64_t cb_guid; + zpool_handle_t *cb_zhp; +} spare_cbdata_t; + +static int +find_spare(zpool_handle_t *zhp, void *data) +{ + spare_cbdata_t *cbp = data; + nvlist_t **spares; + uint_t i, nspares; + 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, ZPOOL_CONFIG_SPARES, + &spares, &nspares) == 0) { + for (i = 0; i < nspares; i++) { + verify(nvlist_lookup_uint64(spares[i], + ZPOOL_CONFIG_GUID, &guid) == 0); + if (guid == cbp->cb_guid) { + cbp->cb_zhp = zhp; + return (1); + } + } } - return (FALSE); + zpool_close(zhp); + return (0); } /* - * Determines if the pool is in use. If so, it returns TRUE and the state of + * 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(int fd, pool_state_t *state, char **namestr) +zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, + boolean_t *inuse) { nvlist_t *config; char *name; - int ret; + boolean_t ret; uint64_t guid, vdev_guid; zpool_handle_t *zhp; nvlist_t *pool_config; uint64_t stateval; + spare_cbdata_t cb = { 0 }; + + *inuse = B_FALSE; - if ((config = zpool_read_label(fd)) == NULL) - return (FALSE); + if (zpool_read_label(fd, &config) != 0) { + (void) no_memory(hdl); + return (-1); + } + + if (config == NULL) + return (0); - verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, - &name) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, &stateval) == 0); - verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, - &guid) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0); + if (stateval != POOL_STATE_SPARE) { + 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: - ret = TRUE; + ret = B_TRUE; break; case POOL_STATE_ACTIVE: @@ -754,14 +932,14 @@ zpool_in_use(int fd, pool_state_t *state, char **namestr) * active pool that was disconnected without being explicitly * exported. */ - if (pool_active(name, guid)) { + if (pool_active(hdl, name, guid)) { /* * 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(name)) != NULL && + if ((zhp = zpool_open_canfail(hdl, name)) != NULL && (pool_config = zpool_get_config(zhp, NULL)) != NULL) { nvlist_t *nvroot; @@ -770,24 +948,57 @@ zpool_in_use(int fd, pool_state_t *state, char **namestr) ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); ret = find_guid(nvroot, vdev_guid); } else { - ret = FALSE; + ret = B_FALSE; } + + 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; + if (zpool_iter(hdl, find_spare, &cb) == 1) { + name = (char *)zpool_get_name(cb.cb_zhp); ret = TRUE; + } else { + ret = FALSE; } break; default: - ret = FALSE; + ret = B_FALSE; } if (ret) { - *namestr = zfs_strdup(name); + if ((*namestr = zfs_strdup(hdl, name)) == NULL) { + nvlist_free(config); + return (-1); + } *state = (pool_state_t)stateval; } + if (cb.cb_zhp) + zpool_close(cb.cb_zhp); + nvlist_free(config); - return (ret); + *inuse = ret; + return (0); } diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c index ae4a9937a8..894bcc0d03 100644 --- a/usr/src/lib/libzfs/common/libzfs_mount.c +++ b/usr/src/lib/libzfs/common/libzfs_mount.c @@ -63,44 +63,44 @@ #include "libzfs_impl.h" /* - * Search the sharetab for the given mountpoint, returning TRUE if it is found. + * Search the sharetab for the given mountpoint, returning true if it is found. */ -static int -is_shared(const char *mountpoint) +static boolean_t +is_shared(libzfs_handle_t *hdl, const char *mountpoint) { char buf[MAXPATHLEN], *tab; - if (zfs_sharetab() == NULL) + if (hdl->libzfs_sharetab == NULL) return (0); - (void) fseek(zfs_sharetab(), 0, SEEK_SET); + (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); - while (fgets(buf, sizeof (buf), zfs_sharetab()) != NULL) { + while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { /* the mountpoint is the first entry on each line */ if ((tab = strchr(buf, '\t')) != NULL) { *tab = '\0'; if (strcmp(buf, mountpoint) == 0) - return (1); + return (B_TRUE); } } - return (0); + return (B_FALSE); } /* - * 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 + * 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 int +static boolean_t dir_is_empty(const char *dirname) { DIR *dirp; struct dirent64 *dp; if ((dirp = opendir(dirname)) == NULL) - return (TRUE); + return (B_TRUE); while ((dp = readdir64(dirp)) != NULL) { @@ -109,11 +109,11 @@ dir_is_empty(const char *dirname) continue; (void) closedir(dirp); - return (FALSE); + return (B_FALSE); } (void) closedir(dirp); - return (TRUE); + return (B_TRUE); } /* @@ -121,7 +121,7 @@ dir_is_empty(const char *dirname) * in 'where' with the current mountpoint, and return 1. Otherwise, we return * 0. */ -int +boolean_t zfs_is_mounted(zfs_handle_t *zhp, char **where) { struct mnttab search = { 0 }, entry; @@ -134,14 +134,14 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where) search.mnt_special = (char *)zfs_get_name(zhp); search.mnt_fstype = MNTTYPE_ZFS; - rewind(zfs_mnttab()); - if (getmntany(zfs_mnttab(), &entry, &search) != 0) - return (FALSE); + rewind(zhp->zfs_hdl->libzfs_mnttab); + if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0) + return (B_FALSE); if (where != NULL) - *where = zfs_strdup(entry.mnt_mountp); + *where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); - return (TRUE); + return (B_TRUE); } /* @@ -153,6 +153,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) struct stat buf; char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; + libzfs_handle_t *hdl = zhp->zfs_hdl; if (options == NULL) mntopts[0] = '\0'; @@ -161,7 +162,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) /* ignore non-filesystems */ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, - sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0) + sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) return (0); /* return success if there is no mountpoint set */ @@ -173,25 +174,18 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) * If the 'zoned' property is set, and we're in the global zone, simply * return success. */ - if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { - char zonename[ZONENAME_MAX]; - if (getzonenamebyid(getzoneid(), zonename, - sizeof (zonename)) < 0) { - zfs_error(dgettext(TEXT_DOMAIN, "internal error: " - "cannot determine current zone")); - return (1); - } - - if (strcmp(zonename, "global") == 0) - return (0); - } + if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && + getzoneid() == GLOBAL_ZONEID) + return (0); /* Create the directory if it doesn't already exist */ if (lstat(mountpoint, &buf) != 0) { if (mkdirp(mountpoint, 0755) != 0) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " - "unable to create mountpoint"), mountpoint); - return (1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "failed to create mountpoint")); + return (zfs_error(hdl, EZFS_MOUNTFAILED, + dgettext(TEXT_DOMAIN, "cannot mount '%s'"), + mountpoint)); } } @@ -204,11 +198,10 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) if ((flags & MS_OVERLAY) == 0 && strstr(mntopts, MNTOPT_REMOUNT) == NULL && !dir_is_empty(mountpoint)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " - "directory is not empty"), mountpoint); - zfs_error(dgettext(TEXT_DOMAIN, "use legacy mountpoint to " - "allow this behavior, or use the -O flag")); - return (1); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "directory is not empty")); + return (zfs_error(hdl, EZFS_MOUNTFAILED, + dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* perform the mount */ @@ -219,24 +212,15 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ - switch (errno) { - case EBUSY: - zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " - "mountpoint or dataset is busy"), zhp->zfs_name); - break; - case EPERM: - case EACCES: - zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " - "permission denied"), zhp->zfs_name, - mountpoint); - break; - default: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot mount '%s': %s"), - mountpoint, strerror(errno)); - break; - } - return (1); + if (errno == EBUSY) + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "mountpoint or dataset is busy")); + else + zfs_error_aux(hdl, strerror(errno)); + + return (zfs_error(hdl, EZFS_MOUNTFAILED, + dgettext(TEXT_DOMAIN, "cannot mount '%s'"), + zhp->zfs_name)); } return (0); @@ -253,9 +237,9 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) /* check to see if need to unmount the filesystem */ search.mnt_special = (char *)zfs_get_name(zhp); search.mnt_fstype = MNTTYPE_ZFS; - rewind(zfs_mnttab()); + rewind(zhp->zfs_hdl->libzfs_mnttab); if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && - getmntany(zfs_mnttab(), &entry, &search) == 0)) { + getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { if (mountpoint == NULL) mountpoint = entry.mnt_mountp; @@ -277,10 +261,10 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) * semantics from the kernel. */ if (umount2(mountpoint, flags) != 0) { - zfs_error(dgettext(TEXT_DOMAIN, - "cannot unmount '%s': %s"), - mountpoint, strerror(errno)); - return (-1); + zfs_error_aux(zhp->zfs_hdl, strerror(errno)); + return (zfs_error(zhp->zfs_hdl, EZFS_UMOUNTFAILED, + dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), + mountpoint)); } /* @@ -315,23 +299,23 @@ zfs_unmountall(zfs_handle_t *zhp, int flags) /* * Check to see if the filesystem is currently shared. */ -int +boolean_t zfs_is_shared(zfs_handle_t *zhp, char **where) { char *mountpoint; if (!zfs_is_mounted(zhp, &mountpoint)) - return (FALSE); + return (B_FALSE); - if (is_shared(mountpoint)) { + if (is_shared(zhp->zfs_hdl, mountpoint)) { if (where != NULL) *where = mountpoint; else free(mountpoint); - return (TRUE); + return (B_TRUE); } else { free(mountpoint); - return (FALSE); + return (B_FALSE); } } @@ -346,6 +330,7 @@ zfs_share(zfs_handle_t *zhp) char shareopts[ZFS_MAXPROPLEN]; char buf[MAXPATHLEN]; FILE *fp; + libzfs_handle_t *hdl = zhp->zfs_hdl; /* ignore non-filesystems */ if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) @@ -353,14 +338,14 @@ zfs_share(zfs_handle_t *zhp) /* return success if there is no mountpoint set */ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, - mountpoint, sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0 || + mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 || strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) return (0); /* return success if there are no share options */ if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), - NULL, NULL, 0, FALSE) != 0 || + NULL, NULL, 0, B_FALSE) != 0 || strcmp(shareopts, "off") == 0) return (0); @@ -386,11 +371,10 @@ zfs_share(zfs_handle_t *zhp) "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, mountpoint); - if ((fp = popen(buf, "r")) == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot share '%s': " - "share(1M) failed"), zfs_get_name(zhp)); - return (-1); - } + if ((fp = popen(buf, "r")) == NULL) + return (zfs_error(hdl, EZFS_SHAREFAILED, + dgettext(TEXT_DOMAIN, "cannot share '%s'"), + zfs_get_name(zhp))); /* * share(1M) should only produce output if there is some kind @@ -403,14 +387,11 @@ zfs_share(zfs_handle_t *zhp) while (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; - if (colon == NULL) - zfs_error(dgettext(TEXT_DOMAIN, "cannot share " - "'%s': share(1M) failed"), - zfs_get_name(zhp)); - else - zfs_error(dgettext(TEXT_DOMAIN, "cannot share " - "'%s': %s"), zfs_get_name(zhp), - colon + 2); + if (colon != NULL) + zfs_error_aux(hdl, colon + 2); + + (void) zfs_error(hdl, EZFS_SHAREFAILED, + dgettext(TEXT_DOMAIN, "cannot share '%s'")); verify(pclose(fp) != 0); return (-1); @@ -429,30 +410,29 @@ zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) { char buf[MAXPATHLEN]; struct mnttab search = { 0 }, entry; + libzfs_handle_t *hdl = zhp->zfs_hdl; /* check to see if need to unmount the filesystem */ search.mnt_special = (char *)zfs_get_name(zhp); search.mnt_fstype = MNTTYPE_ZFS; - rewind(zfs_mnttab()); + rewind(zhp->zfs_hdl->libzfs_mnttab); if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && - getmntany(zfs_mnttab(), &entry, &search) == 0)) { + getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { if (mountpoint == NULL) mountpoint = entry.mnt_mountp; - if (is_shared(mountpoint)) { + if (is_shared(zhp->zfs_hdl, mountpoint)) { FILE *fp; (void) snprintf(buf, sizeof (buf), "/usr/sbin/unshare \"%s\" 2>&1", mountpoint); - if ((fp = popen(buf, "r")) == NULL) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot " - "unshare '%s': unshare(1M) failed"), - zfs_get_name(zhp)); - return (-1); - } + if ((fp = popen(buf, "r")) == NULL) + return (zfs_error(hdl, EZFS_UNSHAREFAILED, + dgettext(TEXT_DOMAIN, + "cannot unshare '%s'"), zfs_get_name(zhp))); /* * unshare(1M) should only produce output if there is @@ -465,17 +445,14 @@ zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) while (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; - if (colon == NULL) - zfs_error(dgettext(TEXT_DOMAIN, - "cannot unshare '%s': unshare(1M) " - "failed"), zfs_get_name(zhp)); - else - zfs_error(dgettext(TEXT_DOMAIN, - "cannot unshare '%s': %s"), - zfs_get_name(zhp), colon + 2); + if (colon != NULL) + zfs_error_aux(hdl, colon + 2); verify(pclose(fp) != 0); - return (-1); + + return (zfs_error(hdl, EZFS_UNSHAREFAILED, + dgettext(TEXT_DOMAIN, + "cannot unshare '%s'"), zfs_get_name(zhp))); } verify(pclose(fp) == 0); @@ -521,24 +498,20 @@ remove_mountpoint(zfs_handle_t *zhp) char mountpoint[ZFS_MAXPROPLEN]; char source[ZFS_MAXNAMELEN]; zfs_source_t sourcetype; - char zonename[ZONENAME_MAX]; + int zoneid = getzoneid(); /* ignore non-filesystems */ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, sizeof (mountpoint), &sourcetype, source, sizeof (source), - FALSE) != 0) + B_FALSE) != 0) return; - if (getzonenamebyid(getzoneid(), zonename, sizeof (zonename)) < 0) - zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: " - "cannot determine current zone")); - if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && (sourcetype == ZFS_SRC_DEFAULT || sourcetype == ZFS_SRC_INHERITED) && (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || - strcmp(zonename, "global") != 0)) { + zoneid != GLOBAL_ZONEID)) { /* * Try to remove the directory, silently ignoring any errors. diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index 1fe6fa2d27..37c82015b9 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -18,6 +18,7 @@ * * CDDL HEADER END */ + /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -45,8 +46,8 @@ * Validate the given pool name, optionally putting an extended error message in * 'buf'. */ -static int -zpool_name_valid(const char *pool, boolean_t isopen, char *buf, size_t buflen) +static boolean_t +zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool) { namecheck_err_t why; char what; @@ -64,53 +65,52 @@ zpool_name_valid(const char *pool, boolean_t isopen, char *buf, size_t buflen) (strncmp(pool, "mirror", 6) == 0 || strncmp(pool, "raidz", 5) == 0 || strncmp(pool, "spare", 5) == 0)) { - ret = -1; - why = NAME_ERR_RESERVED; + zfs_error_aux(hdl, + dgettext(TEXT_DOMAIN, "name is reserved")); + return (B_FALSE); } if (ret != 0) { - if (buf != NULL) { + if (hdl != NULL) { switch (why) { case NAME_ERR_TOOLONG: - (void) snprintf(buf, buflen, + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "name is too long")); break; case NAME_ERR_INVALCHAR: - (void) snprintf(buf, buflen, + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid character " "'%c' in pool name"), what); break; case NAME_ERR_NOLETTER: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "name must begin with a letter"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "name must begin with a letter")); break; case NAME_ERR_RESERVED: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "name is reserved\n" - "pool name may have been omitted"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "name is reserved")); break; case NAME_ERR_DISKLIKE: - (void) strlcpy(buf, dgettext(TEXT_DOMAIN, - "pool name is reserved\n" - "pool name may have been omitted"), buflen); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "pool name is reserved")); break; } } - return (FALSE); + return (B_FALSE); } - return (TRUE); + return (B_TRUE); } /* * Set the pool-wide health based on the vdev state of the root vdev. */ -void +int set_pool_health(nvlist_t *config) { nvlist_t *nvroot; @@ -140,11 +140,10 @@ set_pool_health(nvlist_t *config) break; default: - zfs_baderror(vs->vs_state); + abort(); } - verify(nvlist_add_string(config, ZPOOL_CONFIG_POOL_HEALTH, - health) == 0); + return (nvlist_add_string(config, ZPOOL_CONFIG_POOL_HEALTH, health)); } /* @@ -152,28 +151,33 @@ set_pool_health(nvlist_t *config) * state. */ zpool_handle_t * -zpool_open_canfail(const char *pool) +zpool_open_canfail(libzfs_handle_t *hdl, const char *pool) { zpool_handle_t *zhp; - int error; /* * Make sure the pool name is valid. */ - if (!zpool_name_valid(pool, B_TRUE, NULL, 0)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': invalid " - "pool name"), pool); + if (!zpool_name_valid(hdl, B_TRUE, pool)) { + (void) zfs_error(hdl, EZFS_INVALIDNAME, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), + pool); return (NULL); } - zhp = zfs_malloc(sizeof (zpool_handle_t)); + 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 ((error = zpool_refresh_stats(zhp)) != 0) { - if (error == ENOENT || error == EINVAL) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': no " - "such pool"), pool); + if (zpool_refresh_stats(zhp) != 0) { + if (errno == ENOENT || errno == EINVAL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "no such pool")); + (void) zfs_error(hdl, EZFS_NOENT, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), + pool); free(zhp); return (NULL); } else { @@ -191,17 +195,18 @@ zpool_open_canfail(const char *pool) * the configuration cache may be out of date). */ zpool_handle_t * -zpool_open_silent(const char *pool) +zpool_open_silent(libzfs_handle_t *hdl, const char *pool) { zpool_handle_t *zhp; - int error; - zhp = zfs_malloc(sizeof (zpool_handle_t)); + if ((zhp = calloc(sizeof (zpool_handle_t), 1)) == NULL) + return (NULL); + zhp->zpool_hdl = hdl; (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name)); - if ((error = zpool_refresh_stats(zhp)) != 0) { - if (error == ENOENT || error == EINVAL) { + if (zpool_refresh_stats(zhp) != 0) { + if (errno == ENOENT || errno == EINVAL) { free(zhp); return (NULL); } else { @@ -219,18 +224,16 @@ zpool_open_silent(const char *pool) * state. */ zpool_handle_t * -zpool_open(const char *pool) +zpool_open(libzfs_handle_t *hdl, const char *pool) { zpool_handle_t *zhp; - if ((zhp = zpool_open_canfail(pool)) == NULL) + if ((zhp = zpool_open_canfail(hdl, pool)) == NULL) return (NULL); if (zhp->zpool_state == POOL_STATE_UNAVAIL) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': pool is " - "currently unavailable"), zhp->zpool_name); - zfs_error(dgettext(TEXT_DOMAIN, "run 'zpool status %s' for " - "detailed information"), zhp->zpool_name); + (void) zfs_error(hdl, EZFS_POOLUNAVAIL, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), zhp->zpool_name); zpool_close(zhp); return (NULL); } @@ -251,7 +254,7 @@ zpool_close(zpool_handle_t *zhp) if (zhp->zpool_error_log) { int i; for (i = 0; i < zhp->zpool_error_count; i++) - free(zhp->zpool_error_log[i]); + nvlist_free(zhp->zpool_error_log[i]); free(zhp->zpool_error_log); } free(zhp); @@ -280,6 +283,20 @@ zpool_get_guid(zpool_handle_t *zhp) } /* + * Return the version of the pool. + */ +uint64_t +zpool_get_version(zpool_handle_t *zhp) +{ + uint64_t version; + + verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_VERSION, + &version) == 0); + + return (version); +} + +/* * Return the amount of space currently consumed by the pool. */ uint64_t @@ -324,7 +341,7 @@ zpool_get_root(zpool_handle_t *zhp, char *buf, size_t buflen) zfs_cmd_t zc = { 0 }; (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if (zfs_ioctl(ZFS_IOC_OBJSET_STATS, &zc) != 0 || + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 || zc.zc_root[0] == '\0') return (-1); @@ -348,34 +365,35 @@ zpool_get_state(zpool_handle_t *zhp) * don't have to worry about error semantics. */ int -zpool_create(const char *pool, nvlist_t *nvroot, const char *altroot) +zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, + const char *altroot) { zfs_cmd_t zc = { 0 }; char *packed; size_t len; - int err; - char reason[64]; + char msg[1024]; - if (!zpool_name_valid(pool, B_FALSE, reason, sizeof (reason))) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': %s"), - pool, reason); - return (-1); - } + (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, + "cannot create '%s'"), pool); - if (altroot != NULL && altroot[0] != '/') { - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': alternate " - "root '%s' must be a complete path"), pool, altroot); - return (-1); - } + if (!zpool_name_valid(hdl, B_FALSE, pool)) + return (zfs_error(hdl, EZFS_INVALIDNAME, msg)); - if ((err = nvlist_size(nvroot, &len, NV_ENCODE_NATIVE)) != 0) - zfs_baderror(err); + if (altroot != NULL && altroot[0] != '/') + return (zfs_error(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), altroot)); - packed = zfs_malloc(len); + if (nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) != 0) + return (no_memory(hdl)); - if ((err = nvlist_pack(nvroot, &packed, &len, - NV_ENCODE_NATIVE, 0)) != 0) - zfs_baderror(err); + if ((packed = zfs_alloc(hdl, len)) == NULL) + return (-1); + + if (nvlist_pack(nvroot, &packed, &len, + NV_ENCODE_NATIVE, 0) != 0) { + free(packed); + return (no_memory(hdl)); + } (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name)); zc.zc_config_src = (uint64_t)(uintptr_t)packed; @@ -384,18 +402,10 @@ zpool_create(const char *pool, nvlist_t *nvroot, const char *altroot) if (altroot != NULL) (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root)); - if (zfs_ioctl(ZFS_IOC_POOL_CREATE, &zc) != 0) { - switch (errno) { - case EEXIST: - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "pool exists"), pool); - break; - - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "permission denied"), pool); - break; + if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) { + free(packed); + switch (errno) { case EBUSY: /* * This can happen if the user has specified the same @@ -403,14 +413,13 @@ zpool_create(const char *pool, nvlist_t *nvroot, const char *altroot) * until we try to add it and see we already have a * label. */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "one or more vdevs refer to the same device"), - pool); - break; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "one or more vdevs refer to the same device")); + return (zfs_error(hdl, EZFS_BADDEV, msg)); case EOVERFLOW: /* - * This occurrs when one of the devices is below + * 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. @@ -420,53 +429,20 @@ zpool_create(const char *pool, nvlist_t *nvroot, const char *altroot) zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf)); - zfs_error(dgettext(TEXT_DOMAIN, "cannot " - "create '%s': one or more devices is less " - "than the minimum size (%s)"), pool, - buf); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "one or more devices is less than the " + "minimum size (%s)"), buf); } - break; - - case ENAMETOOLONG: - /* - * One of the vdevs has exceeded VDEV_SPEC_MAX length in - * its plaintext representation. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "too many devices in a single vdev"), pool); - break; - - case EIO: - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "I/O error on one or more devices"), pool); - break; - - case ENXIO: - /* - * This is unlikely to happen since we've verified that - * all the devices can be opened from userland, but it's - * still possible in some circumstances. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "one or more devices is unavailable"), pool); - break; + return (zfs_error(hdl, EZFS_BADDEV, msg)); case ENOSPC: - /* - * This can occur if we were incapable of writing to a - * file vdev because the underlying filesystem is out of - * space. This is very similar to EOVERFLOW, but we'll - * produce a slightly different message. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': " - "one or more devices is out of space"), pool); - break; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "one or more devices is out of space")); + return (zfs_error(hdl, EZFS_BADDEV, msg)); default: - zfs_baderror(errno); + return (zpool_standard_error(hdl, errno, msg)); } - - return (-1); } free(packed); @@ -478,7 +454,7 @@ zpool_create(const char *pool, nvlist_t *nvroot, const char *altroot) if (altroot != NULL) { zfs_handle_t *zhp; - verify((zhp = zfs_open(pool, ZFS_TYPE_ANY)) != NULL); + verify((zhp = zfs_open(hdl, pool, ZFS_TYPE_ANY)) != NULL); verify(zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, "/") == 0); zfs_close(zhp); @@ -496,9 +472,12 @@ zpool_destroy(zpool_handle_t *zhp) { 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(zhp->zpool_name, ZFS_TYPE_FILESYSTEM)) == NULL) + (zfp = zfs_open(zhp->zpool_hdl, zhp->zpool_name, + ZFS_TYPE_FILESYSTEM)) == NULL) return (-1); if (zpool_remove_zvol_links(zhp) != NULL) @@ -506,35 +485,16 @@ zpool_destroy(zpool_handle_t *zhp) (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if (zfs_ioctl(ZFS_IOC_POOL_DESTROY, &zc) != 0) { - switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': permission denied"), - zhp->zpool_name); - break; - - case EBUSY: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': pool busy"), - zhp->zpool_name); - break; - - case ENOENT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': no such pool"), - zhp->zpool_name); - break; - - case EROFS: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot destroy '%s': one or more devices is " - "read only, or '/' is mounted read only"), - zhp->zpool_name); - break; + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_DESTROY, &zc) != 0) { + (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, + "cannot destroy '%s'"), zhp->zpool_name); - default: - zfs_baderror(errno); + 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) @@ -560,10 +520,27 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) char *packed; size_t len; zfs_cmd_t zc; + int ret; + libzfs_handle_t *hdl = zhp->zpool_hdl; + char msg[1024]; + nvlist_t **spares; + uint_t nspares; + + (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, + "cannot add to '%s'"), zhp->zpool_name); + + if (zpool_get_version(zhp) < ZFS_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)); + } verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); - packed = zfs_malloc(len); + if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) + return (-1); verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); @@ -571,13 +548,8 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; - if (zfs_ioctl(ZFS_IOC_VDEV_ADD, &zc) != 0) { + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) { switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': " - "permission denied"), zhp->zpool_name); - break; - case EBUSY: /* * This can happen if the user has specified the same @@ -585,30 +557,9 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) * until we try to add it and see we already have a * label. */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': " - "one or more vdevs refer to the same device"), - zhp->zpool_name); - break; - - case ENAMETOOLONG: - /* - * One of the vdevs has exceeded VDEV_SPEC_MAX length in - * its plaintext representation. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': " - "too many devices in a single vdev"), - zhp->zpool_name); - break; - - case ENXIO: - /* - * This is unlikely to happen since we've verified that - * all the devices can be opened from userland, but it's - * still possible in some circumstances. - */ - zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': " - "one or more devices is unavailable"), - zhp->zpool_name); + 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 EOVERFLOW: @@ -623,23 +574,31 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf)); - zfs_error(dgettext(TEXT_DOMAIN, "cannot " - "add to '%s': one or more devices is less " - "than the minimum size (%s)"), - zhp->zpool_name, 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 raidz2 vdevs")); + (void) zfs_error(hdl, EZFS_BADVERSION, msg); break; default: - zfs_baderror(errno); + (void) zpool_standard_error(hdl, errno, msg); } - return (-1); + ret = -1; + } else { + ret = 0; } free(packed); - return (0); + return (ret); } /* @@ -656,32 +615,10 @@ zpool_export(zpool_handle_t *zhp) (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if (zfs_ioctl(ZFS_IOC_POOL_EXPORT, &zc) != 0) { - switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot export '%s': permission denied"), - zhp->zpool_name); - break; - - case EBUSY: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot export '%s': pool is in use"), - zhp->zpool_name); - break; - - case ENOENT: - zfs_error(dgettext(TEXT_DOMAIN, - "cannot export '%s': no such pool"), - zhp->zpool_name); - break; - - default: - zfs_baderror(errno); - } - - return (-1); - } + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_EXPORT, &zc) != 0) + return (zpool_standard_error(zhp->zpool_hdl, errno, + dgettext(TEXT_DOMAIN, "cannot export '%s'"), + zhp->zpool_name)); return (0); } @@ -693,7 +630,8 @@ zpool_export(zpool_handle_t *zhp) * an alternate root, respectively. */ int -zpool_import(nvlist_t *config, const char *newname, const char *altroot) +zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, + const char *altroot) { zfs_cmd_t zc; char *packed; @@ -706,22 +644,19 @@ zpool_import(nvlist_t *config, const char *newname, const char *altroot) &origname) == 0); if (newname != NULL) { - if (!zpool_name_valid(newname, B_FALSE, NULL, 0)) { - zfs_error(dgettext(TEXT_DOMAIN, "cannot import '%s': " - "invalid pool name"), newname); - return (-1); - } + if (!zpool_name_valid(hdl, B_FALSE, newname)) + return (zfs_error(hdl, EZFS_INVALIDNAME, + dgettext(TEXT_DOMAIN, "cannot import '%s'"), + newname)); thename = (char *)newname; } else { thename = origname; } - if (altroot != NULL && altroot[0] != '/') { - zfs_error(dgettext(TEXT_DOMAIN, "cannot import '%s': alternate " - "root '%s' must be a complete path"), thename, - altroot); - return (-1); - } + if (altroot != NULL && altroot[0] != '/') + return (zfs_error(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), + altroot)); (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name)); @@ -735,7 +670,8 @@ zpool_import(nvlist_t *config, const char *newname, const char *altroot) verify(nvlist_size(config, &len, NV_ENCODE_NATIVE) == 0); - packed = zfs_malloc(len); + if ((packed = zfs_alloc(hdl, len)) == NULL) + return (-1); verify(nvlist_pack(config, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); @@ -743,7 +679,7 @@ zpool_import(nvlist_t *config, const char *newname, const char *altroot) zc.zc_config_src_size = len; ret = 0; - if (zfs_ioctl(ZFS_IOC_POOL_IMPORT, &zc) != 0) { + if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) { char desc[1024]; if (newname == NULL) (void) snprintf(desc, sizeof (desc), @@ -755,42 +691,15 @@ zpool_import(nvlist_t *config, const char *newname, const char *altroot) origname, thename); switch (errno) { - case EEXIST: - /* - * A pool with that name already exists. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: pool exists"), - desc); - break; - - case EPERM: - /* - * The user doesn't have permission to create pools. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission " - "denied"), desc); - break; - - case ENXIO: - case EDOM: - /* - * Device is unavailable, or vdev sum didn't match. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: one or more " - "devices is unavailable"), - desc); - break; - case ENOTSUP: /* * Unsupported version. */ - zfs_error(dgettext(TEXT_DOMAIN, - "%s: unsupported version"), desc); + (void) zfs_error(hdl, EZFS_BADVERSION, desc); break; default: - zfs_baderror(errno); + (void) zpool_standard_error(hdl, errno, desc); } ret = -1; @@ -799,7 +708,7 @@ zpool_import(nvlist_t *config, const char *newname, const char *altroot) /* * This should never fail, but play it safe anyway. */ - if ((zhp = zpool_open_silent(thename)) != NULL) { + if ((zhp = zpool_open_silent(hdl, thename)) != NULL) { ret = zpool_create_zvol_links(zhp); zpool_close(zhp); } @@ -817,48 +726,35 @@ zpool_scrub(zpool_handle_t *zhp, pool_scrub_type_t type) { zfs_cmd_t zc = { 0 }; char msg[1024]; + libzfs_handle_t *hdl = zhp->zpool_hdl; (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); zc.zc_cookie = type; - if (zfs_ioctl(ZFS_IOC_POOL_SCRUB, &zc) == 0) + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_SCRUB, &zc) == 0) return (0); (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot scrub %s"), zc.zc_name); - switch (errno) { - case EPERM: - /* - * No permission to scrub this pool. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg); - break; - - case EBUSY: - /* - * Resilver in progress. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: currently resilvering"), - msg); - break; - - default: - zfs_baderror(errno); - } - return (-1); + if (errno == EBUSY) + return (zfs_error(hdl, EZFS_RESILVERING, msg)); + else + return (zpool_standard_error(hdl, errno, msg)); } -static uint64_t -vdev_to_guid(nvlist_t *nv, const char *search, uint64_t guid) +static nvlist_t * +vdev_to_nvlist_iter(nvlist_t *nv, const char *search, uint64_t guid, + boolean_t *isspare) { uint_t c, children; nvlist_t **child; - uint64_t ret, present; + uint64_t theguid, present; char *path; uint64_t wholedisk = 0; + nvlist_t *ret; - verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &ret) == 0); + verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &theguid) == 0); if (search == NULL && nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &present) == 0) { @@ -866,8 +762,8 @@ vdev_to_guid(nvlist_t *nv, const char *search, uint64_t guid) * If the device has never been present since import, the only * reliable way to match the vdev is by GUID. */ - if (ret == guid) - return (ret); + if (theguid == guid) + return (nv); } else if (search != NULL && nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) { (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, @@ -879,28 +775,37 @@ vdev_to_guid(nvlist_t *nv, const char *search, uint64_t guid) */ if (strlen(search) == strlen(path) - 2 && strncmp(search, path, strlen(search)) == 0) - return (ret); + return (nv); } else if (strcmp(search, path) == 0) { - return (ret); + return (nv); } } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) - return (0); + return (NULL); for (c = 0; c < children; c++) - if ((ret = vdev_to_guid(child[c], search, guid)) != 0) + if ((ret = vdev_to_nvlist_iter(child[c], search, guid, + isspare)) != NULL) return (ret); - return (0); + 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, guid, + isspare)) != NULL) { + *isspare = B_TRUE; + return (ret); + } + } + } + + return (NULL); } -/* - * Given a string describing a vdev, returns the matching GUID, or 0 if none. - */ -uint64_t -zpool_vdev_to_guid(zpool_handle_t *zhp, const char *path) +nvlist_t * +zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *isspare) { char buf[MAXPATHLEN]; const char *search; @@ -921,7 +826,8 @@ zpool_vdev_to_guid(zpool_handle_t *zhp, const char *path) verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); - return (vdev_to_guid(nvroot, search, guid)); + *isspare = B_FALSE; + return (vdev_to_nvlist_iter(nvroot, search, guid, isspare)); } /* @@ -932,39 +838,26 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; + nvlist_t *tgt; + boolean_t isspare; + libzfs_handle_t *hdl = zhp->zpool_hdl; (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 ((zc.zc_guid = zpool_vdev_to_guid(zhp, path)) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, "%s: no such device in pool"), - msg); - return (-1); - } + if ((tgt = zpool_find_vdev(zhp, path, &isspare)) == NULL) + return (zfs_error(hdl, EZFS_NODEVICE, msg)); - if (zfs_ioctl(ZFS_IOC_VDEV_ONLINE, &zc) == 0) - return (0); + if (isspare) + return (zfs_error(hdl, EZFS_ISSPARE, msg)); - switch (errno) { - case ENODEV: - /* - * Device doesn't exist - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg); - break; + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); - case EPERM: - /* - * No permission to bring this vdev online. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg); - break; + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ONLINE, &zc) == 0) + return (0); - default: - zfs_baderror(errno); - } - return (-1); + return (zpool_standard_error(hdl, errno, msg)); } /* @@ -975,48 +868,66 @@ zpool_vdev_offline(zpool_handle_t *zhp, const char *path, int istmp) { zfs_cmd_t zc = { 0 }; char msg[1024]; + nvlist_t *tgt; + boolean_t isspare; + 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 ((zc.zc_guid = zpool_vdev_to_guid(zhp, path)) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, "%s: no such device in pool"), - msg); - return (-1); - } + if ((tgt = zpool_find_vdev(zhp, path, &isspare)) == NULL) + return (zfs_error(hdl, EZFS_NODEVICE, msg)); + + if (isspare) + return (zfs_error(hdl, EZFS_ISSPARE, msg)); + + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); zc.zc_cookie = istmp; - if (zfs_ioctl(ZFS_IOC_VDEV_OFFLINE, &zc) == 0) + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_OFFLINE, &zc) == 0) return (0); switch (errno) { - case ENODEV: - /* - * Device doesn't exist - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg); - break; - - case EPERM: - /* - * No permission to take this vdev offline. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg); - break; + case EBUSY: - case EBUSY: /* * There are no other replicas of this device. */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: no valid replicas"), msg); - break; + return (zfs_error(hdl, EZFS_NOREPLICAS, msg)); - default: - zfs_baderror(errno); + default: + return (zpool_standard_error(hdl, errno, msg)); } - return (-1); +} + +/* + * 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); } /* @@ -1032,6 +943,14 @@ zpool_vdev_attach(zpool_handle_t *zhp, char *packed; int ret; size_t len; + nvlist_t *tgt; + boolean_t isspare; + uint64_t val; + char *path; + nvlist_t **child; + uint_t children; + nvlist_t *config_root; + libzfs_handle_t *hdl = zhp->zpool_hdl; if (replacing) (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, @@ -1041,23 +960,63 @@ zpool_vdev_attach(zpool_handle_t *zhp, "cannot attach %s to %s"), new_disk, old_disk); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((zc.zc_guid = zpool_vdev_to_guid(zhp, old_disk)) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, "%s: no such device in pool"), - msg); - return (-1); - } + if ((tgt = zpool_find_vdev(zhp, old_disk, &isspare)) == 0) + return (zfs_error(hdl, EZFS_NODEVICE, msg)); + + if (isspare) + return (zfs_error(hdl, EZFS_ISSPARE, 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 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 && + nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 && + (zpool_find_vdev(zhp, path, &isspare) == NULL || !isspare) && + is_replacing_spare(config_root, tgt, 1)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "can only be replaced by another hot spare")); + return (zfs_error(hdl, EZFS_BADTARGET, msg)); + } + + /* + * If we are attempting to replace a spare, it canot be applied to an + * already spared device. + */ + if (replacing && + nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 && + zpool_find_vdev(zhp, path, &isspare) != NULL && isspare && + is_replacing_spare(config_root, tgt, 0)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "device has already been replaced with a spare")); + return (zfs_error(hdl, EZFS_BADTARGET, msg)); + } + verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); - packed = zfs_malloc(len); + if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) + return (-1); verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; - ret = zfs_ioctl(ZFS_IOC_VDEV_ATTACH, &zc); + ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc); free(packed); @@ -1065,87 +1024,65 @@ zpool_vdev_attach(zpool_handle_t *zhp, return (0); switch (errno) { - case EPERM: - /* - * No permission to mess with the config. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg); - break; - - case ENODEV: - /* - * Device doesn't exist. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: %s not in pool"), - msg, old_disk); - break; - case ENOTSUP: /* * Can't attach to or replace this type of vdev. */ if (replacing) - zfs_error(dgettext(TEXT_DOMAIN, - "%s: cannot replace a replacing device"), msg); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "cannot replace a replacing device")); else - zfs_error(dgettext(TEXT_DOMAIN, - "%s: attach is only applicable to mirrors"), msg); + 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(dgettext(TEXT_DOMAIN, - "%s: <new_device> must be a single disk"), msg); - break; - - case ENXIO: - /* - * This is unlikely to happen since we've verified that - * all the devices can be opened from userland, but it's - * still possible in some circumstances. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: %s is unavailable"), - msg, new_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: - /* - * The new device is is use. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: %s busy"), msg, new_disk); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"), + new_disk); + (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EOVERFLOW: /* * The new device is too small. */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: %s is too small"), - msg, new_disk); + 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(dgettext(TEXT_DOMAIN, - "%s: devices have different sector alignment"), msg); + 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. */ - zfs_error(dgettext(TEXT_DOMAIN, - "%s: too many devices in a single vdev"), msg); + (void) zfs_error(hdl, EZFS_DEVOVERFLOW, msg); break; default: - zfs_baderror(errno); + (void) zpool_standard_error(hdl, errno, msg); } - return (1); + return (-1); } /* @@ -1156,55 +1093,81 @@ zpool_vdev_detach(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; + nvlist_t *tgt; + boolean_t isspare; + 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 ((zc.zc_guid = zpool_vdev_to_guid(zhp, path)) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, "%s: no such device in pool"), - msg); - return (-1); - } + if ((tgt = zpool_find_vdev(zhp, path, &isspare)) == 0) + return (zfs_error(hdl, EZFS_NODEVICE, msg)); - if (zfs_ioctl(ZFS_IOC_VDEV_DETACH, &zc) == 0) + if (isspare) + return (zfs_error(hdl, EZFS_ISSPARE, msg)); + + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); + + if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_DETACH, &zc) == 0) return (0); switch (errno) { - case EPERM: - /* - * No permission to mess with the config. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg); - break; - - case ENODEV: - /* - * Device doesn't exist. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg); - break; case ENOTSUP: /* * Can't detach from this type of vdev. */ - zfs_error(dgettext(TEXT_DOMAIN, - "%s: only applicable to mirror and replacing vdevs"), msg); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only " + "applicable to mirror and replacing vdevs")); + (void) zfs_error(zhp->zpool_hdl, EZFS_BADTARGET, msg); break; case EBUSY: /* * There are no other replicas of this device. */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: no valid replicas"), msg); + (void) zfs_error(hdl, EZFS_NOREPLICAS, msg); break; default: - zfs_baderror(errno); + (void) zpool_standard_error(hdl, errno, msg); } - return (1); + return (-1); +} + +/* + * Remove the given device. Currently, this is supported only for hot spares. + */ +int +zpool_vdev_remove(zpool_handle_t *zhp, const char *path) +{ + zfs_cmd_t zc = { 0 }; + char msg[1024]; + nvlist_t *tgt; + boolean_t isspare; + libzfs_handle_t *hdl = zhp->zpool_hdl; + + (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, &isspare)) == 0) + return (zfs_error(hdl, EZFS_NODEVICE, msg)); + + if (!isspare) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "only hot spares can be removed")); + return (zfs_error(hdl, EZFS_NODEVICE, msg)); + } + + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); + + if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_REMOVE, &zc) == 0) + return (0); + + return (zpool_standard_error(hdl, errno, msg)); } /* @@ -1215,6 +1178,9 @@ zpool_clear(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; + nvlist_t *tgt; + boolean_t isspare; + libzfs_handle_t *hdl = zhp->zpool_hdl; if (path) (void) snprintf(msg, sizeof (msg), @@ -1226,35 +1192,21 @@ zpool_clear(zpool_handle_t *zhp, const char *path) zhp->zpool_name); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if (path && (zc.zc_guid = zpool_vdev_to_guid(zhp, path)) == 0) { - zfs_error(dgettext(TEXT_DOMAIN, "%s: no such device in pool"), - msg); - return (-1); - } + if (path) { + if ((tgt = zpool_find_vdev(zhp, path, &isspare)) == 0) + return (zfs_error(hdl, EZFS_NODEVICE, msg)); - if (zfs_ioctl(ZFS_IOC_CLEAR, &zc) == 0) - return (0); + if (isspare) + return (zfs_error(hdl, EZFS_ISSPARE, msg)); - switch (errno) { - case EPERM: - /* - * No permission to mess with the config. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg); - break; - - case ENODEV: - /* - * Device doesn't exist. - */ - zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg); - break; - - default: - zfs_baderror(errno); + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, + &zc.zc_guid) == 0); } - return (1); + if (ioctl(hdl->libzfs_fd, ZFS_IOC_CLEAR, &zc) == 0) + return (0); + + return (zpool_standard_error(hdl, errno, msg)); } static int @@ -1269,9 +1221,9 @@ do_zvol(zfs_handle_t *zhp, void *data) */ if (zhp->zfs_volblocksize != 0) { if (linktype) - ret = zvol_create_link(zhp->zfs_name); + ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); else - ret = zvol_remove_link(zhp->zfs_name); + ret = zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); } ret = zfs_iter_children(zhp, do_zvol, data); @@ -1292,10 +1244,11 @@ zpool_create_zvol_links(zpool_handle_t *zhp) /* * If the pool is unavailable, just return success. */ - if ((zfp = make_dataset_handle(zhp->zpool_name)) == NULL) + if ((zfp = make_dataset_handle(zhp->zpool_hdl, + zhp->zpool_name)) == NULL) return (0); - ret = zfs_iter_children(zfp, do_zvol, (void *)TRUE); + ret = zfs_iter_children(zfp, do_zvol, (void *)B_TRUE); zfs_close(zfp); return (ret); @@ -1313,10 +1266,11 @@ zpool_remove_zvol_links(zpool_handle_t *zhp) /* * If the pool is unavailable, just return success. */ - if ((zfp = make_dataset_handle(zhp->zpool_name)) == NULL) + if ((zfp = make_dataset_handle(zhp->zpool_hdl, + zhp->zpool_name)) == NULL) return (0); - ret = zfs_iter_children(zfp, do_zvol, (void *)FALSE); + ret = zfs_iter_children(zfp, do_zvol, (void *)B_FALSE); zfs_close(zfp); return (ret); @@ -1345,7 +1299,9 @@ devid_to_path(char *devid_str) if (ret != 0) return (NULL); - path = zfs_strdup(list[0].devname); + if ((path = strdup(list[0].devname)) == NULL) + return (NULL); + devid_free_nmlist(list); return (path); @@ -1393,7 +1349,7 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path) verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); - (void) zfs_ioctl(ZFS_IOC_VDEV_SETPATH, &zc); + (void) ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SETPATH, &zc); } /* @@ -1412,7 +1368,7 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path) * of these checks. */ char * -zpool_vdev_name(zpool_handle_t *zhp, nvlist_t *nv) +zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv) { char *path, *devid; uint64_t value; @@ -1442,17 +1398,17 @@ zpool_vdev_name(zpool_handle_t *zhp, nvlist_t *nv) * Update the path appropriately. */ set_path(zhp, nv, newpath); - verify(nvlist_add_string(nv, - ZPOOL_CONFIG_PATH, newpath) == 0); + if (nvlist_add_string(nv, + ZPOOL_CONFIG_PATH, newpath) == 0) + verify(nvlist_lookup_string(nv, + ZPOOL_CONFIG_PATH, + &path) == 0); free(newpath); - verify(nvlist_lookup_string(nv, - ZPOOL_CONFIG_PATH, &path) == 0); } - - if (newdevid) - devid_str_free(newdevid); } + if (newdevid) + devid_str_free(newdevid); } if (strncmp(path, "/dev/dsk/", 9) == 0) @@ -1460,15 +1416,28 @@ zpool_vdev_name(zpool_handle_t *zhp, nvlist_t *nv) if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value) == 0 && value) { - char *tmp = zfs_strdup(path); + char *tmp = zfs_strdup(hdl, path); + if (tmp == NULL) + return (NULL); tmp[strlen(path) - 2] = '\0'; return (tmp); } } 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, + value); + path = buf; + } } - return (zfs_strdup(path)); + return (zfs_strdup(hdl, path)); } static int @@ -1502,15 +1471,20 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) */ verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_ERRCOUNT, &count) == 0); - zc.zc_config_dst = (uintptr_t)zfs_malloc(count * sizeof (zbookmark_t)); + if ((zc.zc_config_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl, + count * sizeof (zbookmark_t))) == NULL) + return (-1); zc.zc_config_dst_size = count; (void) strcpy(zc.zc_name, zhp->zpool_name); for (;;) { - if (zfs_ioctl(ZFS_IOC_ERROR_LOG, &zc) != 0) { + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_ERROR_LOG, + &zc) != 0) { + free((void *)(uintptr_t)zc.zc_config_dst); if (errno == ENOMEM) { - free((void *)(uintptr_t)zc.zc_config_dst); - zc.zc_config_dst = (uintptr_t) - zfs_malloc(zc.zc_config_dst_size); + if ((zc.zc_config_dst = (uintptr_t) + zfs_alloc(zhp->zpool_hdl, + zc.zc_config_dst_size)) == NULL) + return (-1); } else { return (-1); } @@ -1549,6 +1523,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) */ if (list == NULL) { *nelem = j; + free((void *)(uintptr_t)zc.zc_config_dst); return (0); } @@ -1557,7 +1532,11 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) /* * Allocate an array of nvlists to hold the results */ - zhp->zpool_error_log = zfs_malloc(j * sizeof (nvlist_t *)); + if ((zhp->zpool_error_log = zfs_alloc(zhp->zpool_hdl, + j * sizeof (nvlist_t *))) == NULL) { + free((void *)(uintptr_t)zc.zc_config_dst); + return (-1); + } /* * Fill in the results with names from the kernel. @@ -1571,31 +1550,37 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) sizeof (zbookmark_t)) == 0) continue; - verify(nvlist_alloc(&nv, NV_UNIQUE_NAME, - 0) == 0); + if (nvlist_alloc(&nv, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; zhp->zpool_error_log[j] = nv; zc.zc_bookmark = zb[i]; - if (zfs_ioctl(ZFS_IOC_BOOKMARK_NAME, &zc) == 0) { - verify(nvlist_add_string(nv, ZPOOL_ERR_DATASET, - zc.zc_prop_name) == 0); - verify(nvlist_add_string(nv, ZPOOL_ERR_OBJECT, - zc.zc_prop_value) == 0); - verify(nvlist_add_string(nv, ZPOOL_ERR_RANGE, - zc.zc_filename) == 0); + if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_BOOKMARK_NAME, + &zc) == 0) { + if (nvlist_add_string(nv, ZPOOL_ERR_DATASET, + zc.zc_prop_name) != 0 || + nvlist_add_string(nv, ZPOOL_ERR_OBJECT, + zc.zc_prop_value) != 0 || + nvlist_add_string(nv, ZPOOL_ERR_RANGE, + zc.zc_filename) != 0) + goto nomem; } else { (void) snprintf(buf, sizeof (buf), "%llx", zb[i].zb_objset); - verify(nvlist_add_string(nv, - ZPOOL_ERR_DATASET, buf) == 0); + if (nvlist_add_string(nv, + ZPOOL_ERR_DATASET, buf) != 0) + goto nomem; (void) snprintf(buf, sizeof (buf), "%llx", zb[i].zb_object); - verify(nvlist_add_string(nv, ZPOOL_ERR_OBJECT, - buf) == 0); + if (nvlist_add_string(nv, ZPOOL_ERR_OBJECT, + buf) != 0) + goto nomem; (void) snprintf(buf, sizeof (buf), "lvl=%u blkid=%llu", (int)zb[i].zb_level, (long long)zb[i].zb_blkid); - verify(nvlist_add_string(nv, ZPOOL_ERR_RANGE, - buf) == 0); + if (nvlist_add_string(nv, ZPOOL_ERR_RANGE, + buf) != 0) + goto nomem; } j++; @@ -1607,6 +1592,16 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t ***list, size_t *nelem) free((void *)(uintptr_t)zc.zc_config_dst); return (0); + +nomem: + free((void *)(uintptr_t)zc.zc_config_dst); + for (i = 0; i < zhp->zpool_error_count; i++) { + if (zhp->zpool_error_log[i]) + free(zhp->zpool_error_log[i]); + } + free(zhp->zpool_error_log); + zhp->zpool_error_log = NULL; + return (no_memory(zhp->zpool_hdl)); } /* @@ -1616,20 +1611,13 @@ int zpool_upgrade(zpool_handle_t *zhp) { zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; (void) strcpy(zc.zc_name, zhp->zpool_name); - if (zfs_ioctl(ZFS_IOC_POOL_UPGRADE, &zc) != 0) { - switch (errno) { - case EPERM: - zfs_error(dgettext(TEXT_DOMAIN, "cannot upgrade '%s': " - "permission denied"), zhp->zpool_name); - break; - default: - zfs_baderror(errno); - } - - return (-1); - } + if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_UPGRADE, &zc) != 0) + return (zpool_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot upgrade '%s'"), + zhp->zpool_name)); return (0); } diff --git a/usr/src/lib/libzfs/common/libzfs_status.c b/usr/src/lib/libzfs/common/libzfs_status.c index 258b2e2f7d..2a4164964d 100644 --- a/usr/src/lib/libzfs/common/libzfs_status.c +++ b/usr/src/lib/libzfs/common/libzfs_status.c @@ -116,7 +116,7 @@ vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs) /* * Detect if any leaf devices that have seen errors or could not be opened. */ -static int +static boolean_t find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) { nvlist_t **child; @@ -132,13 +132,13 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) */ verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0); if (strcmp(type, VDEV_TYPE_REPLACING) == 0) - return (FALSE); + return (B_FALSE); if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; c++) if (find_vdev_problem(child[c], func)) - return (TRUE); + return (B_TRUE); } else { verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_STATS, (uint64_t **)&vs, &c) == 0); @@ -147,10 +147,10 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) vs->vs_read_errors + vs->vs_write_errors + vs->vs_checksum_errors)) - return (TRUE); + return (B_TRUE); } - return (FALSE); + return (B_FALSE); } /* @@ -171,7 +171,7 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) * only picks the most damaging of all the current errors to report. */ static zpool_status_t -check_status(nvlist_t *config, int isimport) +check_status(nvlist_t *config, boolean_t isimport) { nvlist_t *nvroot; vdev_stat_t *vs; @@ -265,7 +265,7 @@ check_status(nvlist_t *config, int isimport) zpool_status_t zpool_get_status(zpool_handle_t *zhp, char **msgid) { - zpool_status_t ret = check_status(zhp->zpool_config, FALSE); + zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE); if (ret >= NMSGID) *msgid = NULL; @@ -278,7 +278,7 @@ zpool_get_status(zpool_handle_t *zhp, char **msgid) zpool_status_t zpool_import_status(nvlist_t *config, char **msgid) { - zpool_status_t ret = check_status(config, TRUE); + zpool_status_t ret = check_status(config, B_TRUE); if (ret >= NMSGID) *msgid = NULL; diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index c7f7528491..29e99dc5b1 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -43,90 +43,320 @@ #include "libzfs_impl.h" -static int zfs_fd = -1; -static FILE *mnttab_file; -static FILE *sharetab_file; -static int sharetab_opened; +int +libzfs_errno(libzfs_handle_t *hdl) +{ + return (hdl->libzfs_error); +} -void (*error_func)(const char *, va_list); +const char * +libzfs_error_action(libzfs_handle_t *hdl) +{ + return (hdl->libzfs_action); +} -/* - * All error handling is kept within libzfs where we have the most information - * immediately available. While this may not be suitable for a general purpose - * library, it greatly simplifies our commands. This command name is used to - * prefix all error messages appropriately. - */ +const char * +libzfs_error_description(libzfs_handle_t *hdl) +{ + if (hdl->libzfs_desc[0] != '\0') + return (hdl->libzfs_desc); + + switch (hdl->libzfs_error) { + case EZFS_NOMEM: + return (dgettext(TEXT_DOMAIN, "out of memory")); + case EZFS_BADPROP: + return (dgettext(TEXT_DOMAIN, "invalid property value")); + case EZFS_PROPREADONLY: + return (dgettext(TEXT_DOMAIN, "read only property")); + case EZFS_PROPTYPE: + return (dgettext(TEXT_DOMAIN, "property doesn't apply to " + "datasets of this type")); + case EZFS_PROPNONINHERIT: + return (dgettext(TEXT_DOMAIN, "property cannot be inherited")); + case EZFS_PROPSPACE: + return (dgettext(TEXT_DOMAIN, "invalid quota or reservation")); + case EZFS_BADTYPE: + return (dgettext(TEXT_DOMAIN, "operation not applicable to " + "datasets of this type")); + case EZFS_BUSY: + return (dgettext(TEXT_DOMAIN, "pool or dataset is busy")); + case EZFS_EXISTS: + return (dgettext(TEXT_DOMAIN, "pool or dataset exists")); + case EZFS_NOENT: + return (dgettext(TEXT_DOMAIN, "no such pool or dataset")); + case EZFS_BADSTREAM: + return (dgettext(TEXT_DOMAIN, "invalid backup stream")); + case EZFS_DSREADONLY: + return (dgettext(TEXT_DOMAIN, "dataset is read only")); + case EZFS_VOLTOOBIG: + return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for " + "this system")); + case EZFS_VOLHASDATA: + return (dgettext(TEXT_DOMAIN, "volume has data")); + case EZFS_INVALIDNAME: + return (dgettext(TEXT_DOMAIN, "invalid name")); + case EZFS_BADRESTORE: + return (dgettext(TEXT_DOMAIN, "unable to restore to " + "destination")); + case EZFS_BADBACKUP: + return (dgettext(TEXT_DOMAIN, "backup failed")); + case EZFS_BADTARGET: + return (dgettext(TEXT_DOMAIN, "invalid target vdev")); + case EZFS_NODEVICE: + return (dgettext(TEXT_DOMAIN, "no such device in pool")); + case EZFS_BADDEV: + return (dgettext(TEXT_DOMAIN, "invalid device")); + case EZFS_NOREPLICAS: + return (dgettext(TEXT_DOMAIN, "no valid replicas")); + case EZFS_RESILVERING: + return (dgettext(TEXT_DOMAIN, "currently resilvering")); + case EZFS_BADVERSION: + return (dgettext(TEXT_DOMAIN, "unsupported version")); + case EZFS_POOLUNAVAIL: + return (dgettext(TEXT_DOMAIN, "pool is unavailable")); + case EZFS_DEVOVERFLOW: + return (dgettext(TEXT_DOMAIN, "too many devices in one vdev")); + case EZFS_BADPATH: + return (dgettext(TEXT_DOMAIN, "must be an absolute path")); + case EZFS_CROSSTARGET: + return (dgettext(TEXT_DOMAIN, "operation crosses datasets or " + "pools")); + case EZFS_ZONED: + return (dgettext(TEXT_DOMAIN, "dataset in use by local zone")); + case EZFS_MOUNTFAILED: + return (dgettext(TEXT_DOMAIN, "mount failed")); + case EZFS_UMOUNTFAILED: + return (dgettext(TEXT_DOMAIN, "umount failed")); + case EZFS_UNSHAREFAILED: + return (dgettext(TEXT_DOMAIN, "unshare(1M) failed")); + case EZFS_SHAREFAILED: + return (dgettext(TEXT_DOMAIN, "share(1M) failed")); + case EZFS_DEVLINKS: + return (dgettext(TEXT_DOMAIN, "failed to create /dev links")); + case EZFS_PERM: + return (dgettext(TEXT_DOMAIN, "permission denied")); + case EZFS_NOSPC: + return (dgettext(TEXT_DOMAIN, "out of space")); + case EZFS_IO: + return (dgettext(TEXT_DOMAIN, "I/O error")); + case EZFS_INTR: + return (dgettext(TEXT_DOMAIN, "signal received")); + case EZFS_ISSPARE: + return (dgettext(TEXT_DOMAIN, "device is reserved as a hot " + "spare")); + case EZFS_INVALCONFIG: + return (dgettext(TEXT_DOMAIN, "invalid vdev configuration")); + case EZFS_UNKNOWN: + return (dgettext(TEXT_DOMAIN, "unknown error")); + default: + abort(); + } + + /* NOTREACHED */ +} + +/*PRINTFLIKE2*/ void -zfs_error(const char *fmt, ...) +zfs_error_aux(libzfs_handle_t *hdl, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - if (error_func != NULL) { - error_func(fmt, ap); - } else { - (void) vfprintf(stderr, fmt, ap); - (void) fprintf(stderr, "\n"); + (void) vsnprintf(hdl->libzfs_desc, sizeof (hdl->libzfs_desc), + fmt, ap); + hdl->libzfs_desc_active = 1; + + va_end(ap); +} + +static void +zfs_verror(libzfs_handle_t *hdl, int error, const char *fmt, va_list ap) +{ + (void) vsnprintf(hdl->libzfs_action, sizeof (hdl->libzfs_action), + fmt, ap); + hdl->libzfs_error = error; + + if (hdl->libzfs_desc_active) + hdl->libzfs_desc_active = 0; + else + hdl->libzfs_desc[0] = '\0'; + + if (hdl->libzfs_printerr) { + if (error == EZFS_UNKNOWN) { + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "internal " + "error: %s\n"), libzfs_error_description(hdl)); + abort(); + } + + (void) fprintf(stderr, "%s: %s\n", hdl->libzfs_action, + libzfs_error_description(hdl)); + if (error == EZFS_NOMEM) + exit(1); } +} + +/*PRINTFLIKE3*/ +int +zfs_error(libzfs_handle_t *hdl, int error, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + zfs_verror(hdl, error, fmt, ap); va_end(ap); + + return (-1); } -/* - * An internal error is something that we cannot recover from, and should never - * happen (such as running out of memory). It should only be used in - * exceptional circumstances. - */ -void -zfs_fatal(const char *fmt, ...) +static int +zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt, + va_list ap) +{ + switch (error) { + case EPERM: + case EACCES: + zfs_verror(hdl, EZFS_PERM, fmt, ap); + return (-1); + + case EIO: + zfs_verror(hdl, EZFS_IO, fmt, ap); + return (-1); + + case EINTR: + zfs_verror(hdl, EZFS_INTR, fmt, ap); + return (-1); + } + + return (0); +} + +/*PRINTFLIKE3*/ +int +zfs_standard_error(libzfs_handle_t *hdl, int error, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - if (error_func != NULL) { - error_func(fmt, ap); - } else { - (void) vfprintf(stderr, fmt, ap); - (void) fprintf(stderr, "\n"); + if (zfs_common_error(hdl, error, fmt, ap) != 0) { + va_end(ap); + return (-1); } - va_end(ap); - exit(1); + switch (error) { + case ENXIO: + zfs_verror(hdl, EZFS_IO, fmt, ap); + break; + + case ENOENT: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset does not exist")); + zfs_verror(hdl, EZFS_NOENT, fmt, ap); + break; + + case ENOSPC: + case EDQUOT: + zfs_verror(hdl, EZFS_NOSPC, fmt, ap); + return (-1); + + case EEXIST: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset already exists")); + zfs_verror(hdl, EZFS_EXISTS, fmt, ap); + break; + + case EBUSY: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset is busy")); + zfs_verror(hdl, EZFS_BUSY, fmt, ap); + break; + + default: + zfs_error_aux(hdl, strerror(errno)); + zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap); + break; + } + + va_end(ap); + return (-1); } -/* - * Consumers (such as the JNI interface) that need to capture error output can - * override the default error handler using this function. - */ -void -zfs_set_error_handler(void (*func)(const char *, va_list)) +/*PRINTFLIKE3*/ +int +zpool_standard_error(libzfs_handle_t *hdl, int error, const char *fmt, ...) { - error_func = func; + va_list ap; + + va_start(ap, fmt); + + if (zfs_common_error(hdl, error, fmt, ap) != 0) { + va_end(ap); + return (-1); + } + + switch (error) { + case ENODEV: + zfs_verror(hdl, EZFS_NODEVICE, fmt, ap); + break; + + case ENOENT: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "no such pool")); + zfs_verror(hdl, EZFS_NOENT, fmt, ap); + break; + + case EEXIST: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "pool already exists")); + zfs_verror(hdl, EZFS_EXISTS, fmt, ap); + break; + + case EBUSY: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool is busy")); + zfs_verror(hdl, EZFS_EXISTS, fmt, ap); + break; + + case ENXIO: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "one or more devices is currently unavailable")); + zfs_verror(hdl, EZFS_BADDEV, fmt, ap); + break; + + case ENAMETOOLONG: + zfs_verror(hdl, EZFS_DEVOVERFLOW, fmt, ap); + break; + + default: + zfs_error_aux(hdl, strerror(error)); + zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap); + } + + va_end(ap); + return (-1); } /* * Display an out of memory error message and abort the current program. */ -void -no_memory(void) +int +no_memory(libzfs_handle_t *hdl) { - assert(errno == ENOMEM); - zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: out of memory\n")); + return (zfs_error(hdl, EZFS_NOMEM, "internal error")); } /* * A safe form of malloc() which will die if the allocation fails. */ void * -zfs_malloc(size_t size) +zfs_alloc(libzfs_handle_t *hdl, size_t size) { void *data; if ((data = calloc(1, size)) == NULL) - no_memory(); + (void) no_memory(hdl); return (data); } @@ -135,69 +365,17 @@ zfs_malloc(size_t size) * A safe form of strdup() which will die if the allocation fails. */ char * -zfs_strdup(const char *str) +zfs_strdup(libzfs_handle_t *hdl, const char *str) { char *ret; if ((ret = strdup(str)) == NULL) - no_memory(); + (void) no_memory(hdl); return (ret); } /* - * Utility functions around common used files - /dev/zfs, /etc/mnttab, and - * /etc/dfs/sharetab. - */ -int -zfs_ioctl(int cmd, zfs_cmd_t *zc) -{ - if (zfs_fd == -1 && - (zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) - zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: unable to " - "open ZFS device\n"), MNTTAB); - - return (ioctl(zfs_fd, cmd, zc)); -} - -FILE * -zfs_mnttab(void) -{ - if (mnttab_file == NULL && - (mnttab_file = fopen(MNTTAB, "r")) == NULL) - zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: unable to " - "open %s\n"), MNTTAB); - - return (mnttab_file); -} - -FILE * -zfs_sharetab(void) -{ - if (sharetab_opened) - return (sharetab_file); - - sharetab_opened = TRUE; - return (sharetab_file = fopen("/etc/dfs/sharetab", "r")); -} - -/* - * Cleanup function for library. Close any file descriptors that were - * opened as part of the above functions. - */ -#pragma fini(zfs_fini) -void -zfs_fini(void) -{ - if (zfs_fd != -1) - (void) close(zfs_fd); - if (sharetab_file) - (void) fclose(sharetab_file); - if (mnttab_file) - (void) fclose(mnttab_file); -} - -/* * Convert a number to an appropriately human-readable output. */ void @@ -241,3 +419,58 @@ zfs_nicenum(uint64_t num, char *buf, size_t buflen) } } } + +void +libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr) +{ + hdl->libzfs_printerr = printerr; +} + +libzfs_handle_t * +libzfs_init(void) +{ + libzfs_handle_t *hdl; + + if ((hdl = calloc(sizeof (libzfs_handle_t), 1)) == NULL) { + return (NULL); + } + + if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) == NULL) { + free(hdl); + return (NULL); + } + + if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) { + (void) close(hdl->libzfs_fd); + free(hdl); + return (NULL); + } + + hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r"); + + return (hdl); +} + +void +libzfs_fini(libzfs_handle_t *hdl) +{ + (void) close(hdl->libzfs_fd); + if (hdl->libzfs_mnttab) + (void) fclose(hdl->libzfs_mnttab); + if (hdl->libzfs_sharetab) + (void) fclose(hdl->libzfs_sharetab); + namespace_clear(hdl); + free(hdl); +} + +libzfs_handle_t * +zpool_get_handle(zpool_handle_t *zhp) +{ + return (zhp->zpool_hdl); +} + +libzfs_handle_t * +zfs_get_handle(zfs_handle_t *zhp) +{ + return (zhp->zfs_hdl); +} diff --git a/usr/src/lib/libzfs/spec/libzfs.spec b/usr/src/lib/libzfs/spec/libzfs.spec index 1789122711..6120603e18 100644 --- a/usr/src/lib/libzfs/spec/libzfs.spec +++ b/usr/src/lib/libzfs/spec/libzfs.spec @@ -24,6 +24,30 @@ # #ident "%Z%%M% %I% %E% SMI" +function libzfs_fini +version SUNWprivate_1.1 +end + +function libzfs_init +version SUNWprivate_1.1 +end + +function libzfs_errno +version SUNWprivate_1.1 +end + +function libzfs_error_action +version SUNWprivate_1.1 +end + +function libzfs_error_description +version SUNWprivate_1.1 +end + +function libzfs_print_on_error +version SUNWprivate_1.1 +end + function zfs_clone version SUNWprivate_1.1 end @@ -40,6 +64,10 @@ function zfs_destroy version SUNWprivate_1.1 end +function zfs_get_handle +version SUNWprivate_1.1 +end + function zfs_get_name version SUNWprivate_1.1 end @@ -104,6 +132,10 @@ function zfs_open version SUNWprivate_1.1 end +function zfs_promote +version SUNWprivate_1.1 +end + function zfs_prop_column_name version SUNWprivate_1.1 end @@ -188,10 +220,6 @@ function zfs_send version SUNWprivate_1.1 end -function zfs_set_error_handler -version SUNWprivate_1.1 -end - function zfs_share version SUNWprivate_1.1 end @@ -248,6 +276,10 @@ function zpool_export version SUNWprivate_1.1 end +function zpool_find_vdev +version SUNWprivate_1.1 +end + function zpool_find_import version SUNWprivate_1.1 end @@ -264,6 +296,10 @@ function zpool_get_guid version SUNWprivate_1.1 end +function zpool_get_handle +version SUNWprivate_1.1 +end + function zpool_get_name version SUNWprivate_1.1 end @@ -288,6 +324,10 @@ function zpool_get_status version SUNWprivate_1.1 end +function zpool_get_version +version SUNWprivate_1.1 +end + function zpool_import version SUNWprivate_1.1 end @@ -352,6 +392,7 @@ function zpool_vdev_name version SUNWprivate_1.1 end -function zpool_vdev_to_guid -version SUNWprivate_1.1 +function zpool_vdev_remove +version SUNWprivate_1.1 end + diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c index 64270f2cd7..2daeca32e2 100644 --- a/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c +++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c @@ -574,7 +574,7 @@ is_fs_snapshot(zfs_handle_t *zhp) zjni_get_dataset_from_snapshot( zfs_get_name(zhp), parent, sizeof (parent)); - parent_zhp = zfs_open(parent, ZFS_TYPE_ANY); + parent_zhp = zfs_open(g_zfs, parent, ZFS_TYPE_ANY); if (parent_zhp == NULL) { return (-1); } @@ -606,7 +606,8 @@ zjni_create_add_Pool(zpool_handle_t *zphp, void *data) zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list; /* Get root fs for this pool -- may be NULL if pool is faulted */ - zfs_handle_t *zhp = zfs_open(zpool_get_name(zphp), ZFS_TYPE_FILESYSTEM); + zfs_handle_t *zhp = zfs_open(g_zfs, zpool_get_name(zphp), + ZFS_TYPE_FILESYSTEM); jobject bean = create_PoolBean(env, zphp, zhp); @@ -682,7 +683,7 @@ zjni_get_Datasets_below(JNIEnv *env, jstring parentUTF, zjni_new_DatasetSet(env, list); /* Retrieve parent dataset */ - zhp = zfs_open(name, parent_typemask); + zhp = zfs_open(g_zfs, name, parent_typemask); if (zhp != NULL) { zjni_DatasetArrayCallbackData_t data = {0}; @@ -703,7 +704,7 @@ zjni_get_Datasets_below(JNIEnv *env, jstring parentUTF, /* Parent is not a dataset -- see if it's a faulted pool */ if ((parent_typemask & ZFS_TYPE_FILESYSTEM) && is_pool_name(name)) { - zpool_handle_t *zphp = zpool_open_canfail(name); + zpool_handle_t *zphp = zpool_open_canfail(g_zfs, name); if (zphp != NULL) { /* A faulted pool has no datasets */ @@ -750,7 +751,7 @@ zjni_get_Datasets_dependents(JNIEnv *env, jobjectArray paths) const char *path = (*env)->GetStringUTFChars(env, pathUTF, NULL); - zfs_handle_t *zhp = zfs_open(path, ZFS_TYPE_ANY); + zfs_handle_t *zhp = zfs_open(g_zfs, path, ZFS_TYPE_ANY); if (zhp != NULL) { /* Add all dependents of this Dataset to list */ (void) zfs_iter_dependents(zhp, @@ -762,7 +763,8 @@ zjni_get_Datasets_dependents(JNIEnv *env, jobjectArray paths) /* Path is not a dataset - see if it's a faulted pool */ if (is_pool_name(path)) { - zpool_handle_t *zphp = zpool_open_canfail(path); + zpool_handle_t *zphp = zpool_open_canfail(g_zfs, + path); if (zphp != NULL) { /* @@ -795,10 +797,10 @@ zjni_get_Dataset(JNIEnv *env, jstring nameUTF, zfs_type_t typemask) { jobject device = NULL; const char *name = (*env)->GetStringUTFChars(env, nameUTF, NULL); - zfs_handle_t *zhp = zfs_open(name, typemask); + zfs_handle_t *zhp = zfs_open(g_zfs, name, typemask); if ((typemask & ZFS_TYPE_FILESYSTEM) && is_pool_name(name)) { - zpool_handle_t *zphp = zpool_open_canfail(name); + zpool_handle_t *zphp = zpool_open_canfail(g_zfs, name); if (zphp != NULL) { device = create_PoolBean(env, zphp, zhp); diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c index 34500684d3..a699ecd7ce 100644 --- a/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c +++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c @@ -35,6 +35,8 @@ #include "libzfs_jni_diskmgt.h" #include "libzfs_jni_disk.h" +libzfs_handle_t *g_zfs; + /* * Function prototypes */ @@ -46,14 +48,14 @@ static void init(); * Static functions */ -char libzfs_err[1024]; +char libdskmgt_err[1024]; static void handle_error(const char *fmt, va_list ap) { /* Save the error message in case it's needed */ - (void) vsnprintf(libzfs_err, sizeof (libzfs_err), fmt, ap); + (void) vsnprintf(libdskmgt_err, sizeof (libdskmgt_err), fmt, ap); #ifdef DEBUG - (void) fprintf(stderr, "caught error: %s\n", libzfs_err); + (void) fprintf(stderr, "caught error: %s\n", libdskmgt_err); #endif } @@ -64,10 +66,8 @@ handle_error(const char *fmt, va_list ap) static void init() { - libzfs_err[0] = '\0'; - - /* libzfs error handler */ - zfs_set_error_handler(handle_error); + if ((g_zfs = libzfs_init()) == NULL) + abort(); /* diskmgt.o error handler */ dmgt_set_error_handler(handle_error); @@ -151,7 +151,7 @@ Java_com_sun_zfs_common_model_SystemDataModel_getPools(JNIEnv *env, jobject obj) data.env = env; data.list = (zjni_Collection_t *)list; - result = zpool_iter(zjni_create_add_Pool, &data); + result = zpool_iter(g_zfs, zjni_create_add_Pool, &data); if (result && (*env)->ExceptionOccurred(env) != NULL) { /* Must not call any more Java methods to preserve exception */ return (NULL); @@ -334,7 +334,7 @@ Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevice(JNIEnv *env, if (poolUTF != NULL) { const char *pool = (*env)->GetStringUTFChars(env, poolUTF, NULL); - zpool_handle_t *zhp = zpool_open_canfail(pool); + zpool_handle_t *zhp = zpool_open_canfail(g_zfs, pool); (*env)->ReleaseStringUTFChars(env, poolUTF, pool); if (zhp != NULL) { @@ -371,7 +371,7 @@ Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_Stri if (poolUTF != NULL) { const char *pool = (*env)->GetStringUTFChars(env, poolUTF, NULL); - zpool_handle_t *zhp = zpool_open_canfail(pool); + zpool_handle_t *zhp = zpool_open_canfail(g_zfs, pool); (*env)->ReleaseStringUTFChars(env, poolUTF, pool); /* Is the pool valid? */ @@ -408,7 +408,7 @@ Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_Stri if (poolUTF != NULL) { const char *pool = (*env)->GetStringUTFChars(env, poolUTF, NULL); - zpool_handle_t *zhp = zpool_open_canfail(pool); + zpool_handle_t *zhp = zpool_open_canfail(g_zfs, pool); (*env)->ReleaseStringUTFChars(env, poolUTF, pool); /* Is the pool valid? */ @@ -446,7 +446,7 @@ Java_com_sun_zfs_common_model_SystemDataModel_getAvailableDisks(JNIEnv *env, error = dmgt_avail_disk_iter(zjni_create_add_DiskDevice, &data); if (error) { - zjni_throw_exception(env, "%s", libzfs_err); + zjni_throw_exception(env, "%s", libdskmgt_err); } else { array = zjni_Collection_to_array( env, (zjni_Collection_t *)list, diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c index d9d09804ec..0e228460dc 100644 --- a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c +++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c @@ -26,6 +26,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" +#include "libzfs_jni_util.h" #include "libzfs_jni_pool.h" #include <strings.h> @@ -1110,7 +1111,7 @@ zjni_pool_status_to_obj(JNIEnv *env, zpool_status_t status) int zjni_ipool_iter(int argc, char **argv, zjni_ipool_iter_f func, void *data) { - nvlist_t *pools = zpool_find_import(argc, argv); + nvlist_t *pools = zpool_find_import(g_zfs, argc, argv); if (pools != NULL) { nvpair_t *elem = NULL; diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h index 1b878a4977..b6989239ac 100644 --- a/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h +++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h @@ -32,6 +32,7 @@ #include <jni.h> #include <regex.h> #include <libnvpair.h> +#include <libzfs.h> #ifdef __cplusplus extern "C" { @@ -105,6 +106,8 @@ int zjni_count_elements(void **); nvpair_t *zjni_nvlist_walk_nvpair( nvlist_t *, const char *, data_type_t, nvpair_t *); +extern libzfs_handle_t *g_zfs; + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzpool/common/util.c b/usr/src/lib/libzpool/common/util.c index 094c8b6c6f..df49adbc7a 100644 --- a/usr/src/lib/libzpool/common/util.c +++ b/usr/src/lib/libzpool/common/util.c @@ -111,11 +111,17 @@ show_vdev_stats(const char *desc, nvlist_t *nv, int indent) for (c = 0; c < children; c++) { nvlist_t *cnv = child[c]; - char *cname; + char *cname, *tname; + uint64_t np; if (nvlist_lookup_string(cnv, ZPOOL_CONFIG_PATH, &cname) && nvlist_lookup_string(cnv, ZPOOL_CONFIG_TYPE, &cname)) cname = "<unknown>"; - show_vdev_stats(cname, cnv, indent + 2); + tname = calloc(1, strlen(cname) + 2); + (void) strcpy(tname, cname); + if (nvlist_lookup_uint64(cnv, ZPOOL_CONFIG_NPARITY, &np) == 0) + tname[strlen(tname)] = '0' + np; + show_vdev_stats(tname, cnv, indent + 2); + free(tname); } } |