diff options
author | Don Brady <don.brady@delphix.com> | 2020-02-05 14:01:39 -0700 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2020-02-07 07:51:26 -0700 |
commit | d8ab6e129d75d7c3f21a7909bf811a3de65faea8 (patch) | |
tree | e9eef2432a5a3e513cd5d156d0aee1b241dbb529 | |
parent | fdefee4c75361dc5ea202f7e1f7c49f8a27ea043 (diff) | |
download | illumos-joyent-d8ab6e129d75d7c3f21a7909bf811a3de65faea8.tar.gz |
12235 Add libzutil for libzfs or libzpool consumers
Portions contributed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Jason King <jason.king@joyent.com>
Approved by: Dan McDonald <danmcd@joyent.com>
52 files changed, 2652 insertions, 1709 deletions
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index c96d67f095..b979d1fe51 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -29,6 +29,7 @@ # Copyright 2017 Nexenta Systems, Inc. # Copyright 2017 RackTop Systems. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. +# Copyright 2020 Joyent, Inc. # # @@ -903,6 +904,8 @@ $(ROOT)/usr/lib/libzfs.so.1:= REALPATH=../../lib/libzfs.so.1 $(ROOT)/usr/lib/libzfs.so:= REALPATH=../../lib/libzfs.so.1 $(ROOT)/usr/lib/libzfs_core.so.1:= REALPATH=../../lib/libzfs_core.so.1 $(ROOT)/usr/lib/libzfs_core.so:= REALPATH=../../lib/libzfs_core.so.1 +$(ROOT)/usr/lib/libzutil.so.1:= REALPATH=../../lib/libzutil.so.1 +$(ROOT)/usr/lib/libzutil.so:= REALPATH=../../lib/libzutil.so.1 $(ROOT)/usr/lib/nss_compat.so.1:= REALPATH=../../lib/nss_compat.so.1 $(ROOT)/usr/lib/nss_dns.so.1:= REALPATH=../../lib/nss_dns.so.1 $(ROOT)/usr/lib/nss_files.so.1:= REALPATH=../../lib/nss_files.so.1 @@ -1171,6 +1174,10 @@ $(ROOT)/usr/lib/$(MACH64)/libzfs_core.so:= \ REALPATH=../../../lib/$(MACH64)/libzfs_core.so.1 $(ROOT)/usr/lib/$(MACH64)/libzfs_core.so.1:= \ REALPATH=../../../lib/$(MACH64)/libzfs_core.so.1 +$(ROOT)/usr/lib/$(MACH64)/libzutil.so:= \ + REALPATH=../../../lib/$(MACH64)/libzutil.so.1 +$(ROOT)/usr/lib/$(MACH64)/libzutil.so.1:= \ + REALPATH=../../../lib/$(MACH64)/libzutil.so.1 $(ROOT)/usr/lib/$(MACH64)/libfakekernel.so:= \ REALPATH=../../../lib/$(MACH64)/libfakekernel.so.1 $(ROOT)/usr/lib/$(MACH64)/libfakekernel.so.1:= \ @@ -1334,6 +1341,8 @@ SYM.USRLIB= \ /usr/lib/libzfs.so.1 \ /usr/lib/libzfs_core.so \ /usr/lib/libzfs_core.so.1 \ + /usr/lib/libzutil.so \ + /usr/lib/libzutil.so.1 \ /usr/lib/nss_compat.so.1 \ /usr/lib/nss_dns.so.1 \ /usr/lib/nss_files.so.1 \ @@ -1483,6 +1492,8 @@ SYM.USRLIB64= \ /usr/lib/$(MACH64)/libzfs.so.1 \ /usr/lib/$(MACH64)/libzfs_core.so \ /usr/lib/$(MACH64)/libzfs_core.so.1 \ + /usr/lib/$(MACH64)/libzutil.so \ + /usr/lib/$(MACH64)/libzutil.so.1 \ /usr/lib/$(MACH64)/nss_compat.so.1 \ /usr/lib/$(MACH64)/nss_dns.so.1 \ /usr/lib/$(MACH64)/nss_files.so.1 \ diff --git a/usr/src/cmd/dumpadm/Makefile b/usr/src/cmd/dumpadm/Makefile index 7818439eb4..e1303c5d0e 100644 --- a/usr/src/cmd/dumpadm/Makefile +++ b/usr/src/cmd/dumpadm/Makefile @@ -20,6 +20,7 @@ # # # Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2020 Joyent, Inc. # PROG = dumpadm @@ -35,12 +36,13 @@ lint := LINTFLAGS = -mx include ../Makefile.cmd CFLAGS += $(CCVERBOSE) +CFLAGS += -I../../lib/libzutil/common FILEMODE = 0555 ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) -LDLIBS += -ldiskmgt -lzfs -luuid +LDLIBS += -ldiskmgt -lzfs -luuid -lzutil .KEEP_STATE: diff --git a/usr/src/cmd/dumpadm/dconf.c b/usr/src/cmd/dumpadm/dconf.c index 440004eac5..dc5355ba48 100644 --- a/usr/src/cmd/dumpadm/dconf.c +++ b/usr/src/cmd/dumpadm/dconf.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Joyent, Inc. */ #include <sys/types.h> @@ -37,6 +38,7 @@ #include <errno.h> #include <libdiskmgt.h> #include <libzfs.h> +#include <libzutil.h> #include <uuid/uuid.h> #include "dconf.h" diff --git a/usr/src/cmd/fs.d/zfs/fstyp/Makefile b/usr/src/cmd/fs.d/zfs/fstyp/Makefile index ba28b9e39f..dfcdddd812 100644 --- a/usr/src/cmd/fs.d/zfs/fstyp/Makefile +++ b/usr/src/cmd/fs.d/zfs/fstyp/Makefile @@ -22,12 +22,12 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2020 Joyent, Inc. # FSTYP_VERS=1 FSTYPE= zfs -LIBPROG= fstyp.so.${FSTYP_VERS} +LIBPROG= fstyp.so.${FSTYP_VERS} include ../../../../lib/Makefile.lib include ../../Makefile.fstype @@ -36,16 +36,17 @@ include ../../Makefile.fstype MAPFILES = CFLAGS += $(C_PICFLAGS) +CFLAGS += -I../../../../lib/libzutil/common DYNLIB= $(LIBPROG) -LDLIBS += -lnvpair -lzfs -lc +LDLIBS += -lnvpair -lc -lzutil LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN -erroff=E_NAME_DEF_NOT_USED2 LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN -erroff=E_NAME_DEF_NOT_USED2 SRCS= ${LIBPROG:%.so.$(FSTYP_VERS)=%.c} -CPPFLAGS += -DFSTYP_VERS=${FSTYP_VERS} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +CPPFLAGS += -DFSTYP_VERS=${FSTYP_VERS} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 # # Override PMAP dependency @@ -76,7 +77,7 @@ lint: lint_SRCS cstyle: $(CSTYLE) $(SRCS) -clean: +clean: ${RM} $(LIBPROG) clobber: clean diff --git a/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c b/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c index 30f86375e7..d49d998404 100644 --- a/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c +++ b/usr/src/cmd/fs.d/zfs/fstyp/fstyp.c @@ -21,10 +21,10 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2020 Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * libfstyp module for zfs */ @@ -38,6 +38,7 @@ #include <string.h> #include <libnvpair.h> #include <libzfs.h> +#include <libzutil.h> #include <libfstyp_module.h> #include <errno.h> @@ -89,7 +90,7 @@ fstyp_mod_ident(fstyp_mod_handle_t handle) uint64_t u64; char buf[64]; - if (zpool_read_label(h->fd, &h->config) != 0) { + if (zpool_read_label(h->fd, &h->config, NULL) != 0) { return (FSTYP_ERR_NO_MATCH); } diff --git a/usr/src/cmd/zdb/Makefile.com b/usr/src/cmd/zdb/Makefile.com index 1834e9b2f8..42925319f4 100644 --- a/usr/src/cmd/zdb/Makefile.com +++ b/usr/src/cmd/zdb/Makefile.com @@ -23,7 +23,7 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright (c) 2012 by Delphix. All rights reserved. -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2020 Joyent, Inc. # Copyright 2017 RackTop Systems. # @@ -37,8 +37,9 @@ include ../../Makefile.ctf INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs INCS += -I../../../common/zfs +INCS += -I../../../lib/libzutil/common -LDLIBS += -lzpool -lumem -lnvpair -lzfs -lavl -lcmdutils -lfakekernel +LDLIBS += -lzpool -lumem -lnvpair -lzutil -lavl -lfakekernel CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zdb/zdb.c b/usr/src/cmd/zdb/zdb.c index 03e78ce4ba..8272bbf64f 100644 --- a/usr/src/cmd/zdb/zdb.c +++ b/usr/src/cmd/zdb/zdb.c @@ -71,6 +71,9 @@ #undef verify #include <libzfs.h> +#include <libnvpair.h> +#include <libzutil.h> + #include "zdb.h" #define ZDB_COMPRESS_NAME(idx) ((idx) < ZIO_COMPRESS_FUNCTIONS ? \ @@ -101,7 +104,6 @@ typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size); uint64_t *zopt_object = NULL; static unsigned zopt_objects = 0; -libzfs_handle_t *g_zfs; uint64_t max_inflight = 1000; static int leaked_objects = 0; @@ -5884,8 +5886,6 @@ main(int argc, char **argv) spa_load_verify_dryrun = B_TRUE; kernel_init(FREAD); - g_zfs = libzfs_init(); - ASSERT(g_zfs != NULL); if (dump_all) verbose = MAX(verbose, 1); @@ -5964,7 +5964,8 @@ main(int argc, char **argv) args.path = searchdirs; args.can_be_active = B_TRUE; - error = zpool_tryimport(g_zfs, target_pool, &cfg, &args); + error = zpool_find_config(NULL, target_pool, &cfg, &args, + &libzpool_config_ops); if (error == 0) { @@ -6094,7 +6095,6 @@ main(int argc, char **argv) dump_debug_buffer(); - libzfs_fini(g_zfs); kernel_fini(); return (error); diff --git a/usr/src/cmd/zfs/Makefile b/usr/src/cmd/zfs/Makefile index a65371609c..97642e44f9 100644 --- a/usr/src/cmd/zfs/Makefile +++ b/usr/src/cmd/zfs/Makefile @@ -24,7 +24,7 @@ # Copyright 2010 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2012, 2015 by Delphix. All rights reserved. # Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. -# Copyright 2019 Joyent, Inc. +# Copyright 2020 Joyent, Inc. # PROG= zfs @@ -41,12 +41,13 @@ LINKPROGS= mount umount ROOTETCFSTYPE= $(ROOTETC)/fs/$(FSTYPE) USRLIBFSTYPE= $(ROOTLIB)/fs/$(FSTYPE) -LDLIBS += -lzfs_core -lzfs -luutil -lumem -lnvpair -lsec -lidmap +LDLIBS += -lzfs_core -lzfs -luutil -lumem -lnvpair -lsec -lidmap -lzutil # cmdutils has list(9F) functions used by the project code. LDLIBS += -lcmdutils INCS += -I../../common/zfs INCS += -I$(SRC)/uts/common/fs/zfs +INCS += -I../../lib/libzutil/common CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index bd2f7f4cfe..ef18430bad 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -68,6 +68,7 @@ #include <libzfs_core.h> #include <zfs_prop.h> #include <zfs_deleg.h> +#include <libzutil.h> #include <libuutil.h> #include <aclutils.h> #include <directory.h> diff --git a/usr/src/cmd/zhack/Makefile.com b/usr/src/cmd/zhack/Makefile.com index 8a87de4ec9..d45962c16d 100644 --- a/usr/src/cmd/zhack/Makefile.com +++ b/usr/src/cmd/zhack/Makefile.com @@ -27,6 +27,7 @@ # # Copyright (c) 2012, 2016 by Delphix. All rights reserved. # Copyright 2017 RackTop Systems. +# Copyright 2020 Joyent, Inc. # PROG= zhack @@ -40,8 +41,9 @@ INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs INCS += -I../../../uts/common/fs/zfs/lua INCS += -I../../../common/zfs +INCS += -I../../../lib/libzutil/common -LDLIBS += -lzpool -lumem -lnvpair -lzfs +LDLIBS += -lzpool -lumem -lnvpair -lzutil CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zhack/zhack.c b/usr/src/cmd/zhack/zhack.c index 1f90f97bdf..235b1fd0ca 100644 --- a/usr/src/cmd/zhack/zhack.c +++ b/usr/src/cmd/zhack/zhack.c @@ -49,12 +49,11 @@ #include <sys/zfeature.h> #include <sys/dmu_tx.h> #undef verify -#include <libzfs.h> +#include <libzutil.h> extern boolean_t zfeature_checks_disable; const char cmdname[] = "zhack"; -libzfs_handle_t *g_zfs; static importargs_t g_importargs; static char *g_pool; static boolean_t g_readonly; @@ -128,20 +127,17 @@ zhack_import(char *target, boolean_t readonly) int error; kernel_init(readonly ? FREAD : (FREAD | FWRITE)); - g_zfs = libzfs_init(); - ASSERT(g_zfs != NULL); dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb); g_readonly = readonly; - g_importargs.unique = B_TRUE; g_importargs.can_be_active = readonly; g_pool = strdup(target); - error = zpool_tryimport(g_zfs, target, &config, &g_importargs); + error = zpool_find_config(NULL, target, &config, &g_importargs, + &libzpool_config_ops); if (error) - fatal(NULL, FTAG, "cannot import '%s': %s", target, - libzfs_error_description(g_zfs)); + fatal(NULL, FTAG, "cannot import '%s'", target); props = NULL; if (readonly) { @@ -528,7 +524,6 @@ main(int argc, char **argv) "changes may not be committed to disk\n"); } - libzfs_fini(g_zfs); kernel_fini(); return (rv); diff --git a/usr/src/cmd/zinject/Makefile.com b/usr/src/cmd/zinject/Makefile.com index 8644e8185d..220c877b42 100644 --- a/usr/src/cmd/zinject/Makefile.com +++ b/usr/src/cmd/zinject/Makefile.com @@ -24,6 +24,7 @@ # # Copyright (c) 2016 by Delphix. All rights reserved. # Copyright 2017 RackTop Systems. +# Copyright 2020 Joyent, Inc. # PROG:sh= cd ..; basename `pwd` @@ -36,7 +37,7 @@ INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs INCS += -I../../../uts/common/fs/zfs/lua -LDLIBS += -lzpool -lzfs -lnvpair +LDLIBS += -lzfs -lnvpair CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zinject/translate.c b/usr/src/cmd/zinject/translate.c index 090f2448b0..546009ab88 100644 --- a/usr/src/cmd/zinject/translate.c +++ b/usr/src/cmd/zinject/translate.c @@ -25,8 +25,6 @@ #include <libzfs.h> -#include <sys/zfs_context.h> - #include <errno.h> #include <fcntl.h> #include <stdarg.h> @@ -49,9 +47,6 @@ #include "zinject.h" -extern void kernel_init(int); -extern void kernel_fini(void); - static int debug; static void @@ -157,51 +152,32 @@ parse_pathname(const char *inpath, char *dataset, char *relpath, } /* - * Convert from a (dataset, path) pair into a (objset, object) pair. Note that - * we grab the object number from the inode number, since looking this up via - * libzpool is a real pain. + * Convert from a dataset to a objset id. Note that + * we grab the object number from the inode number. */ -/* ARGSUSED */ static int -object_from_path(const char *dataset, const char *path, struct stat64 *statbuf, - zinject_record_t *record) +object_from_path(const char *dataset, uint64_t object, zinject_record_t *record) { - objset_t *os; - int err; - - /* - * Before doing any libzpool operations, call sync() to ensure that the - * on-disk state is consistent with the in-core state. - */ - sync(); + zfs_handle_t *zhp; - err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, B_FALSE, FTAG, &os); - if (err != 0) { - (void) fprintf(stderr, "cannot open dataset '%s': %s\n", - dataset, strerror(err)); + if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL) return (-1); - } - record->zi_objset = dmu_objset_id(os); - record->zi_object = statbuf->st_ino; + record->zi_objset = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); + record->zi_object = object; - dmu_objset_disown(os, B_FALSE, FTAG); + zfs_close(zhp); return (0); } /* - * Calculate the real range based on the type, level, and range given. + * Initialize the range based on the type, level, and range given. */ static int -calculate_range(const char *dataset, err_type_t type, int level, char *range, +initialize_range(err_type_t type, int level, char *range, zinject_record_t *record) { - objset_t *os = NULL; - dnode_t *dn = NULL; - int err; - int ret = -1; - /* * Determine the numeric range from the string. */ @@ -229,7 +205,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, (void) fprintf(stderr, "invalid range '%s': must be " "a numeric range of the form 'start[,end]'\n", range); - goto out; + return (-1); } } @@ -247,7 +223,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, if (range != NULL) { (void) fprintf(stderr, "range cannot be specified when " "type is 'dnode'\n"); - goto out; + return (-1); } record->zi_start = record->zi_object * sizeof (dnode_phys_t); @@ -256,76 +232,9 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, break; } - /* - * Get the dnode associated with object, so we can calculate the block - * size. - */ - if ((err = dmu_objset_own(dataset, DMU_OST_ANY, - B_TRUE, B_FALSE, FTAG, &os)) != 0) { - (void) fprintf(stderr, "cannot open dataset '%s': %s\n", - dataset, strerror(err)); - goto out; - } - - if (record->zi_object == 0) { - dn = DMU_META_DNODE(os); - } else { - err = dnode_hold(os, record->zi_object, FTAG, &dn); - if (err != 0) { - (void) fprintf(stderr, "failed to hold dnode " - "for object %llu\n", - (u_longlong_t)record->zi_object); - goto out; - } - } - - - ziprintf("data shift: %d\n", (int)dn->dn_datablkshift); - ziprintf(" ind shift: %d\n", (int)dn->dn_indblkshift); - - /* - * Translate range into block IDs. - */ - if (record->zi_start != 0 || record->zi_end != -1ULL) { - record->zi_start >>= dn->dn_datablkshift; - record->zi_end >>= dn->dn_datablkshift; - } - - /* - * Check level, and then translate level 0 blkids into ranges - * appropriate for level of indirection. - */ record->zi_level = level; - if (level > 0) { - ziprintf("level 0 blkid range: [%llu, %llu]\n", - record->zi_start, record->zi_end); - - if (level >= dn->dn_nlevels) { - (void) fprintf(stderr, "level %d exceeds max level " - "of object (%d)\n", level, dn->dn_nlevels - 1); - goto out; - } - - if (record->zi_start != 0 || record->zi_end != 0) { - int shift = dn->dn_indblkshift - SPA_BLKPTRSHIFT; - for (; level > 0; level--) { - record->zi_start >>= shift; - record->zi_end >>= shift; - } - } - } - - ret = 0; -out: - if (dn) { - if (dn != DMU_META_DNODE(os)) - dnode_rele(dn, FTAG); - } - if (os) - dmu_objset_disown(os, B_FALSE, FTAG); - - return (ret); + return (0); } int @@ -337,8 +246,6 @@ translate_record(err_type_t type, const char *object, const char *range, struct stat64 statbuf; int ret = -1; - kernel_init(FREAD); - debug = (getenv("ZINJECT_DEBUG") != NULL); ziprintf("translating: %s\n", object); @@ -388,16 +295,16 @@ translate_record(err_type_t type, const char *object, const char *range, /* * Convert (dataset, file) into (objset, object) */ - if (object_from_path(dataset, path, &statbuf, record) != 0) + if (object_from_path(dataset, statbuf.st_ino, record) != 0) goto err; ziprintf("raw objset: %llu\n", record->zi_objset); ziprintf("raw object: %llu\n", record->zi_object); /* - * For the given object, calculate the real (type, level, range) + * For the given object, initialize the range in bytes */ - if (calculate_range(dataset, type, level, (char *)range, record) != 0) + if (initialize_range(type, level, (char *)range, record) != 0) goto err; ziprintf(" objset: %llu\n", record->zi_objset); @@ -419,7 +326,6 @@ translate_record(err_type_t type, const char *object, const char *range, ret = 0; err: - kernel_fini(); return (ret); } diff --git a/usr/src/cmd/zinject/zinject.c b/usr/src/cmd/zinject/zinject.c index fc836f11e5..16e659ca6f 100644 --- a/usr/src/cmd/zinject/zinject.c +++ b/usr/src/cmd/zinject/zinject.c @@ -564,6 +564,7 @@ register_handler(const char *pool, int flags, zinject_record_t *record, if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) { (void) fprintf(stderr, "failed to add handler: %s\n", + errno == EDOM ? "block level exceeds max level of object" : strerror(errno)); return (1); } @@ -886,6 +887,7 @@ main(int argc, char **argv) break; case 'r': range = optarg; + flags |= ZINJECT_CALC_RANGE; break; case 's': dur_secs = 1; diff --git a/usr/src/cmd/zpool/Makefile b/usr/src/cmd/zpool/Makefile index b849d9c759..55d8abc80f 100644 --- a/usr/src/cmd/zpool/Makefile +++ b/usr/src/cmd/zpool/Makefile @@ -22,7 +22,7 @@ # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. # Copyright (c) 2015 by Delphix. All rights reserved. -# Copyright 2019 Joyent, Inc. +# Copyright 2020 Joyent, Inc. # PROG= zpool @@ -40,9 +40,10 @@ STAT_COMMON_OBJS = timestamp.o STAT_COMMON_SRCS = $(STAT_COMMON_OBJS:%.o=$(STATCOMMONDIR)/%.c) SRCS += $(STAT_COMMON_SRCS) -LDLIBS += -lzfs -lnvpair -ldevid -lefi -ldiskmgt -luutil -lumem +LDLIBS += -lzfs -lnvpair -ldevid -lefi -ldiskmgt -luutil -lumem -lzutil INCS += -I../../common/zfs -I../../uts/common/fs/zfs -I$(STATCOMMONDIR) +INCS += -I../../lib/libzutil/common CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zpool/zpool_iter.c b/usr/src/cmd/zpool/zpool_iter.c index 6e77f85fa3..c05c665ada 100644 --- a/usr/src/cmd/zpool/zpool_iter.c +++ b/usr/src/cmd/zpool/zpool_iter.c @@ -34,6 +34,7 @@ #include <strings.h> #include <libzfs.h> +#include <libzutil.h> #include "zpool_util.h" diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 0a8b06db2f..23269c20d6 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -58,6 +58,7 @@ #include <sys/debug.h> #include <libzfs.h> +#include <libzutil.h> #include "zpool_util.h" #include "zfs_comutil.h" @@ -913,7 +914,7 @@ zpool_do_labelclear(int argc, char **argv) return (1); } - if (zpool_read_label(fd, &config) != 0) { + if (zpool_read_label(fd, &config, NULL) != 0) { (void) fprintf(stderr, gettext("failed to read label from %s\n"), vdev); return (1); @@ -2534,6 +2535,40 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, return (ret); } +typedef struct target_exists_args { + const char *poolname; + uint64_t poolguid; +} target_exists_args_t; + +static int +name_or_guid_exists(zpool_handle_t *zhp, void *data) +{ + target_exists_args_t *args = data; + nvlist_t *config = zpool_get_config(zhp, NULL); + int found = 0; + + if (config == NULL) + return (0); + + if (args->poolname != NULL) { + char *pool_name; + + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &pool_name) == 0); + if (strcmp(pool_name, args->poolname) == 0) + found = 1; + } else { + uint64_t pool_guid; + + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &pool_guid) == 0); + if (pool_guid == args->poolguid) + found = 1; + } + zpool_close(zhp); + + return (found); +} /* * zpool checkpoint <pool> * checkpoint --discard <pool> @@ -2684,6 +2719,7 @@ zpool_do_import(int argc, char **argv) boolean_t dryrun = B_FALSE; boolean_t do_rewind = B_FALSE; boolean_t xtreme_rewind = B_FALSE; + boolean_t pool_exists = B_FALSE; uint64_t pool_state, txg = -1ULL; char *cachefile = NULL; importargs_t idata = { 0 }; @@ -2892,10 +2928,10 @@ zpool_do_import(int argc, char **argv) /* * User specified a name or guid. Ensure it's unique. */ - idata.unique = B_TRUE; + target_exists_args_t search = {searchname, searchguid}; + pool_exists = zpool_iter(g_zfs, name_or_guid_exists, &search); } - idata.path = searchdirs; idata.paths = nsearch; idata.poolname = searchname; @@ -2903,9 +2939,9 @@ zpool_do_import(int argc, char **argv) idata.cachefile = cachefile; idata.policy = policy; - pools = zpool_search_import(g_zfs, &idata); + pools = zpool_search_import(g_zfs, &idata, &libzfs_config_ops); - if (pools != NULL && idata.exists && + if (pools != NULL && pool_exists && (argc == 1 || strcmp(argv[0], argv[1]) == 0)) { (void) fprintf(stderr, gettext("cannot import '%s': " "a pool with that name already exists\n"), @@ -2914,7 +2950,7 @@ zpool_do_import(int argc, char **argv) "[-t] <pool | id> <newpool>' to give it a new temporary " "or permanent name\n")); err = 1; - } else if (pools == NULL && idata.exists) { + } else if (pools == NULL && pool_exists) { (void) fprintf(stderr, gettext("cannot import '%s': " "a pool with that name is already created/imported,\n"), argv[0]); @@ -3386,7 +3422,7 @@ get_interval_count(int *argcp, char **argv, unsigned long *iv, /* * Determine if the last argument is an integer or a pool name */ - if (argc > 0 && isdigit(argv[argc - 1][0])) { + if (argc > 0 && zfs_isnumber(argv[argc - 1])) { char *end; errno = 0; @@ -3416,7 +3452,7 @@ get_interval_count(int *argcp, char **argv, unsigned long *iv, * If the last argument is also an integer, then we have both a count * and an interval. */ - if (argc > 0 && isdigit(argv[argc - 1][0])) { + if (argc > 0 && zfs_isnumber(argv[argc - 1])) { char *end; errno = 0; diff --git a/usr/src/cmd/zpool/zpool_vdev.c b/usr/src/cmd/zpool/zpool_vdev.c index 6e6589ab47..652bece3ab 100644 --- a/usr/src/cmd/zpool/zpool_vdev.c +++ b/usr/src/cmd/zpool/zpool_vdev.c @@ -69,6 +69,7 @@ #include <libdiskmgt.h> #include <libintl.h> #include <libnvpair.h> +#include <libzutil.h> #include <limits.h> #include <sys/spa.h> #include <stdio.h> @@ -1124,7 +1125,7 @@ is_spare(nvlist_t *config, const char *path) if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 || !inuse || state != POOL_STATE_SPARE || - zpool_read_label(fd, &label) != 0) { + zpool_read_label(fd, &label, NULL) != 0) { free(name); (void) close(fd); return (B_FALSE); diff --git a/usr/src/cmd/ztest/Makefile.com b/usr/src/cmd/ztest/Makefile.com index fb32fdd938..0db755a6f7 100644 --- a/usr/src/cmd/ztest/Makefile.com +++ b/usr/src/cmd/ztest/Makefile.com @@ -22,7 +22,7 @@ # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2012, 2016 by Delphix. All rights reserved. # Copyright 2017 RackTop Systems. -# Copyright 2019 Joyent, Inc. +# Copyright 2020 Joyent, Inc. PROG= ztest OBJS= $(PROG).o @@ -35,8 +35,9 @@ INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs INCS += -I../../../uts/common/fs/zfs/lua INCS += -I../../../common/zfs +INCS += -I../../../lib/libzutil/common -LDLIBS += -lumem -lzpool -lcmdutils -lm -lnvpair -lfakekernel -lzfs +LDLIBS += -lumem -lzpool -lcmdutils -lm -lnvpair -lfakekernel -lzutil CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/ztest/ztest.c b/usr/src/cmd/ztest/ztest.c index 83922cf376..f4f577e4d3 100644 --- a/usr/src/cmd/ztest/ztest.c +++ b/usr/src/cmd/ztest/ztest.c @@ -128,7 +128,7 @@ #include <math.h> #include <sys/fs/zfs.h> #include <libnvpair.h> -#include <libzfs.h> +#include <libzutil.h> #include <libcmdutils.h> static int ztest_fd_data = -1; @@ -6811,7 +6811,6 @@ make_random_props() static void ztest_import(ztest_shared_t *zs) { - libzfs_handle_t *hdl; importargs_t args = { 0 }; spa_t *spa; nvlist_t *cfg = NULL; @@ -6825,14 +6824,14 @@ ztest_import(ztest_shared_t *zs) rw_init(&ztest_name_lock, NULL, USYNC_THREAD, NULL); kernel_init(FREAD | FWRITE); - hdl = libzfs_init(); searchdirs[0] = ztest_opts.zo_dir; args.paths = nsearch; args.path = searchdirs; args.can_be_active = B_FALSE; - error = zpool_tryimport(hdl, name, &cfg, &args); + error = zpool_find_config(NULL, name, &cfg, &args, + &libzpool_config_ops); if (error) (void) fatal(0, "No pools found\n"); @@ -6842,7 +6841,6 @@ ztest_import(ztest_shared_t *zs) 1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift; spa_close(spa, FTAG); - libzfs_fini(hdl); kernel_fini(); if (!ztest_opts.zo_mmp_test) { diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index f361ae3d8b..ae663cdc63 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -22,7 +22,7 @@ # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2012 by Delphix. All rights reserved. -# Copyright 2019, Joyent, Inc. +# Copyright 2020 Joyent, Inc. # Copyright (c) 2013 Gary Mills # Copyright 2014 Garrett D'Amore <garrett@damore.org> # Copyright (c) 2015 Gary Mills @@ -248,6 +248,7 @@ SUBDIRS += \ libzoneinfo \ libzonestat \ libzpool \ + libzutil \ madv \ mpapi \ mpss \ @@ -686,14 +687,15 @@ libv12n: libds libuuid libvolmgt: libadm libvrrpadm: libdladm libscf libvscan: libscf libsecdb -libzfs: libdevid libgen libuutil libadm libavl libefi libidmap \ - libumem libtsol libzfs_core libcryptoutil pkcs11 libmd libcmdutils +libzfs: libdevid libgen libuutil libavl libefi libidmap \ + libumem libtsol libzfs_core libcryptoutil pkcs11 libmd libzutil libzfs_core: libnvpair -libzfs_jni: libdiskmgt libzfs +libzfs_jni: libdiskmgt libzfs libzutil libzonecfg: libuuid libsysevent libsec libbrand libpool libscf libproc \ libuutil libbsm libsecdb libzonestat: libcmdutils libumem -libzpool: libavl libumem libcmdutils libsysevent libfakekernel libzfs +libzpool: libavl libumem libcmdutils libsysevent libfakekernel libzutil +libzutil: libadm libavl libdevid libefi madv: libgen mpapi: libpthread libdevinfo libsysevent mpss: libgen diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com index ba69fc44ca..189d585456 100644 --- a/usr/src/lib/libzfs/Makefile.com +++ b/usr/src/lib/libzfs/Makefile.com @@ -22,7 +22,7 @@ # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> # Copyright (c) 2011, 2017 by Delphix. All rights reserved. -# Copyright 2019 Joyent, Inc. +# Copyright 2020 Joyent, Inc. # LIBRARY= libzfs.a @@ -69,12 +69,13 @@ INCS += -I$(SRCDIR) INCS += -I../../../uts/common/fs/zfs INCS += -I../../../common/zfs INCS += -I../../libc/inc +INCS += -I../../libzutil/common CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all LDLIBS += -lc -lm -ldevid -lgen -lnvpair -luutil -lavl -lefi \ - -ladm -lidmap -ltsol -lcryptoutil -lpkcs11 -lmd -lumem -lzfs_core \ - -lcmdutils -ldevinfo + -lidmap -ltsol -lcryptoutil -lpkcs11 -lmd -lumem -lzfs_core \ + -ldevinfo -lzutil CPPFLAGS += $(INCS) -D_LARGEFILE64_SOURCE=1 -D_REENTRANT $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index d7411f0b85..48692b1b1a 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -23,7 +23,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved. * Copyright (c) 2011, 2017 by Delphix. All rights reserved. - * Copyright 2019 Joyent, Inc. + * Copyright 2020 Joyent, Inc. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2016 Nexenta Systems, Inc. @@ -395,7 +395,6 @@ extern zpool_status_t zpool_get_status(zpool_handle_t *, char **, zpool_errata_t *); extern zpool_status_t zpool_import_status(nvlist_t *, char **, zpool_errata_t *); -extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh); /* * Statistics and configuration functions. @@ -418,31 +417,6 @@ extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *, extern void zpool_print_unsup_feat(nvlist_t *config); /* - * Search for pools to import - */ - -typedef struct importargs { - char **path; /* a list of paths to search */ - int paths; /* number of paths to search */ - char *poolname; /* name of a pool to find */ - uint64_t guid; /* guid of a pool to find */ - char *cachefile; /* cachefile to use for import */ - int can_be_active : 1; /* can the pool be active? */ - int unique : 1; /* does 'poolname' already exist? */ - int exists : 1; /* set on return if pool already exists */ - nvlist_t *policy; /* load policy (max txg, rewind, etc.) */ -} importargs_t; - -extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *); -extern int zpool_tryimport(libzfs_handle_t *hdl, char *target, - nvlist_t **configp, importargs_t *args); - -/* legacy pool search routines */ -extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **); -extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *, - char *, uint64_t); - -/* * Miscellaneous pool functions */ struct zfs_cmd; @@ -461,8 +435,6 @@ extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *, extern int zpool_upgrade(zpool_handle_t *, uint64_t); extern int zpool_get_history(zpool_handle_t *, nvlist_t **, uint64_t *, boolean_t *); -extern int zpool_history_unpack(char *, uint64_t, uint64_t *, - nvlist_t ***, uint_t *); extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *, size_t len); extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *); @@ -793,7 +765,6 @@ extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *, zfs_type_t); extern int zfs_spa_version(zfs_handle_t *, int *); extern boolean_t zfs_bookmark_exists(const char *path); -extern ulong_t get_system_hostid(void); /* * Mount support functions. @@ -837,10 +808,6 @@ extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *, #define verify(EX) assert(EX) #endif -/* - * Utility function to convert a number to a human-readable form. - */ -extern void zfs_nicenum(uint64_t, char *, size_t); extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *); /* @@ -852,7 +819,6 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **, /* * Label manipulation. */ -extern int zpool_read_label(int, nvlist_t **); extern int zpool_clear_label(int); /* is this zvol valid for use as a dump device? */ diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 6de04a541c..6810679055 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -36,7 +36,6 @@ #include <ctype.h> #include <errno.h> #include <libintl.h> -#include <math.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> @@ -61,6 +60,7 @@ #include <sys/zap.h> #include <sys/dsl_crypt.h> #include <libzfs.h> +#include <libzutil.h> #include "zfs_namecheck.h" #include "zfs_prop.h" diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c index ce5864a62b..706f08e6ec 100644 --- a/usr/src/lib/libzfs/common/libzfs_import.c +++ b/usr/src/lib/libzfs/common/libzfs_import.c @@ -59,264 +59,12 @@ #include <thread_pool.h> #include <sys/vdev_impl.h> +#include <libzutil.h> #include "libzfs.h" #include "libzfs_impl.h" /* - * Intermediate structures used to gather configuration information. - */ -typedef struct config_entry { - uint64_t ce_txg; - nvlist_t *ce_config; - struct config_entry *ce_next; -} config_entry_t; - -typedef struct vdev_entry { - uint64_t ve_guid; - config_entry_t *ve_configs; - struct vdev_entry *ve_next; -} vdev_entry_t; - -typedef struct pool_entry { - uint64_t pe_guid; - vdev_entry_t *pe_vdevs; - struct pool_entry *pe_next; -} pool_entry_t; - -typedef struct name_entry { - char *ne_name; - uint64_t ne_guid; - struct name_entry *ne_next; -} name_entry_t; - -typedef struct pool_list { - pool_entry_t *pools; - name_entry_t *names; -} pool_list_t; - -/* - * Go through and fix up any path and/or devid information for the given vdev - * configuration. - */ -static int -fix_paths(nvlist_t *nv, name_entry_t *names) -{ - nvlist_t **child; - uint_t c, children; - uint64_t guid; - name_entry_t *ne, *best; - char *path, *devid; - int matched; - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, - &child, &children) == 0) { - for (c = 0; c < children; c++) - if (fix_paths(child[c], names) != 0) - return (-1); - return (0); - } - - /* - * This is a leaf (file or disk) vdev. In either case, go through - * the name list and see if we find a matching guid. If so, replace - * the path and see if we can calculate a new devid. - * - * There may be multiple names associated with a particular guid, in - * which case we have overlapping slices or multiple paths to the same - * disk. If this is the case, then we want to pick the path that is - * the most similar to the original, where "most similar" is the number - * of matching characters starting from the end of the path. This will - * preserve slice numbers even if the disks have been reorganized, and - * will also catch preferred disk names if multiple paths exist. - */ - verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0); - if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) - path = NULL; - - matched = 0; - best = NULL; - for (ne = names; ne != NULL; ne = ne->ne_next) { - if (ne->ne_guid == guid) { - const char *src, *dst; - int count; - - if (path == NULL) { - best = ne; - break; - } - - src = ne->ne_name + strlen(ne->ne_name) - 1; - dst = path + strlen(path) - 1; - for (count = 0; src >= ne->ne_name && dst >= path; - src--, dst--, count++) - if (*src != *dst) - break; - - /* - * At this point, 'count' is the number of characters - * matched from the end. - */ - if (count > matched || best == NULL) { - best = ne; - matched = count; - } - } - } - - if (best == NULL) - return (0); - - if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0) - return (-1); - - if ((devid = devid_str_from_path(best->ne_name)) == NULL) { - (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); - } else { - if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) { - devid_str_free(devid); - return (-1); - } - devid_str_free(devid); - } - - return (0); -} - -/* - * Add the given configuration to the list of known devices. - */ -static int -add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path, - nvlist_t *config) -{ - uint64_t pool_guid, vdev_guid, top_guid, txg, state; - pool_entry_t *pe; - vdev_entry_t *ve; - config_entry_t *ce; - name_entry_t *ne; - - /* - * If this is a hot spare not currently in use or level 2 cache - * device, add it to the list of names to translate, but don't do - * anything else. - */ - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, - &state) == 0 && - (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) && - nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) { - if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL) - return (-1); - - if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) { - free(ne); - return (-1); - } - - ne->ne_guid = vdev_guid; - ne->ne_next = pl->names; - pl->names = ne; - - return (0); - } - - /* - * If we have a valid config but cannot read any of these fields, then - * it means we have a half-initialized label. In vdev_label_init() - * we write a label with txg == 0 so that we can identify the device - * in case the user refers to the same disk later on. If we fail to - * create the pool, we'll be left with a label in this state - * which should not be considered part of a valid pool. - */ - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, - &pool_guid) != 0 || - nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, - &vdev_guid) != 0 || - nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID, - &top_guid) != 0 || - nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, - &txg) != 0 || txg == 0) { - return (0); - } - - /* - * First, see if we know about this pool. If not, then add it to the - * list of known pools. - */ - for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { - if (pe->pe_guid == pool_guid) - break; - } - - if (pe == NULL) { - if ((pe = zfs_alloc(hdl, sizeof (pool_entry_t))) == NULL) { - return (-1); - } - pe->pe_guid = pool_guid; - pe->pe_next = pl->pools; - pl->pools = pe; - } - - /* - * Second, see if we know about this toplevel vdev. Add it if its - * missing. - */ - for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { - if (ve->ve_guid == top_guid) - break; - } - - if (ve == NULL) { - if ((ve = zfs_alloc(hdl, sizeof (vdev_entry_t))) == NULL) { - return (-1); - } - ve->ve_guid = top_guid; - ve->ve_next = pe->pe_vdevs; - pe->pe_vdevs = ve; - } - - /* - * Third, see if we have a config with a matching transaction group. If - * so, then we do nothing. Otherwise, add it to the list of known - * configs. - */ - for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) { - if (ce->ce_txg == txg) - break; - } - - if (ce == NULL) { - if ((ce = zfs_alloc(hdl, sizeof (config_entry_t))) == NULL) { - return (-1); - } - ce->ce_txg = txg; - ce->ce_config = fnvlist_dup(config); - ce->ce_next = ve->ve_configs; - ve->ve_configs = ce; - } - - /* - * At this point we've successfully added our config to the list of - * known configs. The last thing to do is add the vdev guid -> path - * mappings so that we can fix up the configuration as necessary before - * doing the import. - */ - if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL) - return (-1); - - if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) { - free(ne); - return (-1); - } - - ne->ne_guid = vdev_guid; - ne->ne_next = pl->names; - pl->names = ne; - - return (0); -} - -/* * Returns true if the named pool matches the given GUID. */ static int @@ -347,7 +95,7 @@ static nvlist_t * refresh_config(libzfs_handle_t *hdl, nvlist_t *config) { nvlist_t *nvl; - zfs_cmd_t zc = { 0 }; + zfs_cmd_t zc = {"\0"}; int err, dstbuf_size; if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) @@ -360,7 +108,7 @@ refresh_config(libzfs_handle_t *hdl, nvlist_t *config) return (NULL); } - while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT, + while ((err = zfs_ioctl(hdl, ZFS_IOC_POOL_TRYIMPORT, &zc)) != 0 && errno == ENOMEM) { if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { zcmd_free_nvlists(&zc); @@ -382,451 +130,24 @@ refresh_config(libzfs_handle_t *hdl, nvlist_t *config) return (nvl); } -/* - * Determine if the vdev id is a hole in the namespace. - */ -boolean_t -vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id) +static nvlist_t * +refresh_config_libzfs(void *handle, nvlist_t *tryconfig) { - for (int c = 0; c < holes; c++) { - - /* Top-level is a hole */ - if (hole_array[c] == id) - return (B_TRUE); - } - return (B_FALSE); + return (refresh_config((libzfs_handle_t *)handle, tryconfig)); } -/* - * Convert our list of pools into the definitive set of configurations. We - * start by picking the best config for each toplevel vdev. Once that's done, - * we assemble the toplevel vdevs into a full config for the pool. We make a - * pass to fix up any incorrect paths, and then add it to the main list to - * return to the user. - */ -static nvlist_t * -get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok, - nvlist_t *policy) +static int +pool_active_libzfs(void *handle, const char *name, uint64_t guid, + boolean_t *isactive) { - pool_entry_t *pe; - vdev_entry_t *ve; - config_entry_t *ce; - nvlist_t *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot; - nvlist_t **spares, **l2cache; - uint_t i, nspares, nl2cache; - boolean_t config_seen; - uint64_t best_txg; - char *name, *hostname = NULL; - uint64_t guid; - uint_t children = 0; - nvlist_t **child = NULL; - uint_t holes; - uint64_t *hole_array, max_id; - uint_t c; - boolean_t isactive; - uint64_t hostid; - nvlist_t *nvl; - boolean_t found_one = B_FALSE; - boolean_t valid_top_config = B_FALSE; - - if (nvlist_alloc(&ret, 0, 0) != 0) - goto nomem; - - for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { - uint64_t id, max_txg = 0; - - if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0) - goto nomem; - config_seen = B_FALSE; - - /* - * Iterate over all toplevel vdevs. Grab the pool configuration - * from the first one we find, and then go through the rest and - * add them as necessary to the 'vdevs' member of the config. - */ - for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { - - /* - * Determine the best configuration for this vdev by - * selecting the config with the latest transaction - * group. - */ - best_txg = 0; - for (ce = ve->ve_configs; ce != NULL; - ce = ce->ce_next) { - - if (ce->ce_txg > best_txg) { - tmp = ce->ce_config; - best_txg = ce->ce_txg; - } - } - - /* - * We rely on the fact that the max txg for the - * pool will contain the most up-to-date information - * about the valid top-levels in the vdev namespace. - */ - if (best_txg > max_txg) { - (void) nvlist_remove(config, - ZPOOL_CONFIG_VDEV_CHILDREN, - DATA_TYPE_UINT64); - (void) nvlist_remove(config, - ZPOOL_CONFIG_HOLE_ARRAY, - DATA_TYPE_UINT64_ARRAY); - - max_txg = best_txg; - hole_array = NULL; - holes = 0; - max_id = 0; - valid_top_config = B_FALSE; - - if (nvlist_lookup_uint64(tmp, - ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) { - verify(nvlist_add_uint64(config, - ZPOOL_CONFIG_VDEV_CHILDREN, - max_id) == 0); - valid_top_config = B_TRUE; - } - - if (nvlist_lookup_uint64_array(tmp, - ZPOOL_CONFIG_HOLE_ARRAY, &hole_array, - &holes) == 0) { - verify(nvlist_add_uint64_array(config, - ZPOOL_CONFIG_HOLE_ARRAY, - hole_array, holes) == 0); - } - } - - if (!config_seen) { - /* - * Copy the relevant pieces of data to the pool - * configuration: - * - * version - * pool guid - * name - * comment (if available) - * pool state - * hostid (if available) - * hostname (if available) - */ - uint64_t state, version; - char *comment = NULL; - - version = fnvlist_lookup_uint64(tmp, - ZPOOL_CONFIG_VERSION); - fnvlist_add_uint64(config, - ZPOOL_CONFIG_VERSION, version); - guid = fnvlist_lookup_uint64(tmp, - ZPOOL_CONFIG_POOL_GUID); - fnvlist_add_uint64(config, - ZPOOL_CONFIG_POOL_GUID, guid); - name = fnvlist_lookup_string(tmp, - ZPOOL_CONFIG_POOL_NAME); - fnvlist_add_string(config, - ZPOOL_CONFIG_POOL_NAME, name); - - if (nvlist_lookup_string(tmp, - ZPOOL_CONFIG_COMMENT, &comment) == 0) - fnvlist_add_string(config, - ZPOOL_CONFIG_COMMENT, comment); - - state = fnvlist_lookup_uint64(tmp, - ZPOOL_CONFIG_POOL_STATE); - fnvlist_add_uint64(config, - ZPOOL_CONFIG_POOL_STATE, state); - - hostid = 0; - if (nvlist_lookup_uint64(tmp, - ZPOOL_CONFIG_HOSTID, &hostid) == 0) { - fnvlist_add_uint64(config, - ZPOOL_CONFIG_HOSTID, hostid); - hostname = fnvlist_lookup_string(tmp, - ZPOOL_CONFIG_HOSTNAME); - fnvlist_add_string(config, - ZPOOL_CONFIG_HOSTNAME, hostname); - } - - config_seen = B_TRUE; - } - - /* - * Add this top-level vdev to the child array. - */ - verify(nvlist_lookup_nvlist(tmp, - ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0); - verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID, - &id) == 0); - - if (id >= children) { - nvlist_t **newchild; - - newchild = zfs_alloc(hdl, (id + 1) * - sizeof (nvlist_t *)); - if (newchild == NULL) - goto nomem; - - for (c = 0; c < children; c++) - newchild[c] = child[c]; - - free(child); - child = newchild; - children = id + 1; - } - if (nvlist_dup(nvtop, &child[id], 0) != 0) - goto nomem; - - } - - /* - * If we have information about all the top-levels then - * clean up the nvlist which we've constructed. This - * means removing any extraneous devices that are - * beyond the valid range or adding devices to the end - * of our array which appear to be missing. - */ - if (valid_top_config) { - if (max_id < children) { - for (c = max_id; c < children; c++) - nvlist_free(child[c]); - children = max_id; - } else if (max_id > children) { - nvlist_t **newchild; - - newchild = zfs_alloc(hdl, (max_id) * - sizeof (nvlist_t *)); - if (newchild == NULL) - goto nomem; - - for (c = 0; c < children; c++) - newchild[c] = child[c]; - - free(child); - child = newchild; - children = max_id; - } - } - - verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, - &guid) == 0); - - /* - * The vdev namespace may contain holes as a result of - * device removal. We must add them back into the vdev - * tree before we process any missing devices. - */ - if (holes > 0) { - ASSERT(valid_top_config); - - for (c = 0; c < children; c++) { - nvlist_t *holey; - - if (child[c] != NULL || - !vdev_is_hole(hole_array, holes, c)) - continue; - - if (nvlist_alloc(&holey, NV_UNIQUE_NAME, - 0) != 0) - goto nomem; - - /* - * Holes in the namespace are treated as - * "hole" top-level vdevs and have a - * special flag set on them. - */ - if (nvlist_add_string(holey, - ZPOOL_CONFIG_TYPE, - VDEV_TYPE_HOLE) != 0 || - nvlist_add_uint64(holey, - ZPOOL_CONFIG_ID, c) != 0 || - nvlist_add_uint64(holey, - ZPOOL_CONFIG_GUID, 0ULL) != 0) { - nvlist_free(holey); - goto nomem; - } - child[c] = holey; - } - } - - /* - * Look for any missing top-level vdevs. If this is the case, - * create a faked up 'missing' vdev as a placeholder. We cannot - * simply compress the child array, because the kernel performs - * certain checks to make sure the vdev IDs match their location - * in the configuration. - */ - for (c = 0; c < children; c++) { - if (child[c] == NULL) { - nvlist_t *missing; - if (nvlist_alloc(&missing, NV_UNIQUE_NAME, - 0) != 0) - goto nomem; - if (nvlist_add_string(missing, - ZPOOL_CONFIG_TYPE, - VDEV_TYPE_MISSING) != 0 || - nvlist_add_uint64(missing, - ZPOOL_CONFIG_ID, c) != 0 || - nvlist_add_uint64(missing, - ZPOOL_CONFIG_GUID, 0ULL) != 0) { - nvlist_free(missing); - goto nomem; - } - child[c] = missing; - } - } - - /* - * Put all of this pool's top-level vdevs into a root vdev. - */ - if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0) - goto nomem; - if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, - VDEV_TYPE_ROOT) != 0 || - nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 || - nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 || - nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - child, children) != 0) { - nvlist_free(nvroot); - goto nomem; - } - - for (c = 0; c < children; c++) - nvlist_free(child[c]); - free(child); - children = 0; - child = NULL; - - /* - * Go through and fix up any paths and/or devids based on our - * known list of vdev GUID -> path mappings. - */ - if (fix_paths(nvroot, pl->names) != 0) { - nvlist_free(nvroot); - goto nomem; - } - - /* - * Add the root vdev to this pool's configuration. - */ - if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, - nvroot) != 0) { - nvlist_free(nvroot); - goto nomem; - } - nvlist_free(nvroot); - - /* - * zdb uses this path to report on active pools that were - * imported or created using -R. - */ - if (active_ok) - goto add_pool; - - /* - * Determine if this pool is currently active, in which case we - * can't actually import it. - */ - verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, - &name) == 0); - verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, - &guid) == 0); - - if (pool_active(hdl, name, guid, &isactive) != 0) - goto error; - - if (isactive) { - nvlist_free(config); - config = NULL; - continue; - } - - if (policy != NULL) { - if (nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY, - policy) != 0) - goto nomem; - } - - if ((nvl = refresh_config(hdl, config)) == NULL) { - nvlist_free(config); - config = NULL; - continue; - } - - nvlist_free(config); - config = nvl; - - /* - * Go through and update the paths for spares, now that we have - * them. - */ - verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, - &nvroot) == 0); - if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, - &spares, &nspares) == 0) { - for (i = 0; i < nspares; i++) { - if (fix_paths(spares[i], pl->names) != 0) - goto nomem; - } - } - - /* - * Update the paths for l2cache devices. - */ - if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, - &l2cache, &nl2cache) == 0) { - for (i = 0; i < nl2cache; i++) { - if (fix_paths(l2cache[i], pl->names) != 0) - goto nomem; - } - } - - /* - * Restore the original information read from the actual label. - */ - (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID, - DATA_TYPE_UINT64); - (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTNAME, - DATA_TYPE_STRING); - if (hostid != 0) { - verify(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID, - hostid) == 0); - verify(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME, - hostname) == 0); - } - -add_pool: - /* - * Add this pool to the list of configs. - */ - verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, - &name) == 0); - if (nvlist_add_nvlist(ret, name, config) != 0) - goto nomem; - - found_one = B_TRUE; - nvlist_free(config); - config = NULL; - } - - if (!found_one) { - nvlist_free(ret); - ret = NULL; - } - - return (ret); - -nomem: - (void) no_memory(hdl); -error: - nvlist_free(config); - nvlist_free(ret); - for (c = 0; c < children; c++) - nvlist_free(child[c]); - free(child); - - return (NULL); + return (pool_active((libzfs_handle_t *)handle, name, guid, isactive)); } +const pool_config_ops_t libzfs_config_ops = { + .pco_refresh_config = refresh_config_libzfs, + .pco_pool_active = pool_active_libzfs, +}; + /* * Return the offset of the given label. */ @@ -839,232 +160,6 @@ label_offset(uint64_t size, int l) } /* - * Given a file descriptor, read the label information and return an nvlist - * describing the configuration, if there is one. - * Return 0 on success, or -1 on failure - */ -int -zpool_read_label(int fd, nvlist_t **config) -{ - struct stat64 statbuf; - int l; - vdev_label_t *label; - uint64_t state, txg, size; - - *config = NULL; - - if (fstat64(fd, &statbuf) == -1) - return (-1); - size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t); - - if ((label = malloc(sizeof (vdev_label_t))) == NULL) - return (-1); - - for (l = 0; l < VDEV_LABELS; l++) { - if (pread64(fd, label, sizeof (vdev_label_t), - label_offset(size, l)) != sizeof (vdev_label_t)) - continue; - - if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, - sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) - continue; - - if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, - &state) != 0 || state > POOL_STATE_L2CACHE) { - nvlist_free(*config); - continue; - } - - if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE && - (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, - &txg) != 0 || txg == 0)) { - nvlist_free(*config); - continue; - } - - free(label); - return (0); - } - - free(label); - *config = NULL; - errno = ENOENT; - return (-1); -} - -typedef struct rdsk_node { - char *rn_name; - int rn_dfd; - libzfs_handle_t *rn_hdl; - nvlist_t *rn_config; - avl_tree_t *rn_avl; - avl_node_t rn_node; - boolean_t rn_nozpool; -} rdsk_node_t; - -static int -slice_cache_compare(const void *arg1, const void *arg2) -{ - const char *nm1 = ((rdsk_node_t *)arg1)->rn_name; - const char *nm2 = ((rdsk_node_t *)arg2)->rn_name; - char *nm1slice, *nm2slice; - int rv; - - /* - * slices zero and two are the most likely to provide results, - * so put those first - */ - nm1slice = strstr(nm1, "s0"); - nm2slice = strstr(nm2, "s0"); - if (nm1slice && !nm2slice) { - return (-1); - } - if (!nm1slice && nm2slice) { - return (1); - } - nm1slice = strstr(nm1, "s2"); - nm2slice = strstr(nm2, "s2"); - if (nm1slice && !nm2slice) { - return (-1); - } - if (!nm1slice && nm2slice) { - return (1); - } - - rv = strcmp(nm1, nm2); - if (rv == 0) - return (0); - return (rv > 0 ? 1 : -1); -} - -static void -check_one_slice(avl_tree_t *r, char *diskname, uint_t partno, - diskaddr_t size, uint_t blksz) -{ - rdsk_node_t tmpnode; - rdsk_node_t *node; - char sname[MAXNAMELEN]; - - tmpnode.rn_name = &sname[0]; - (void) snprintf(tmpnode.rn_name, MAXNAMELEN, "%s%u", - diskname, partno); - /* - * protect against division by zero for disk labels that - * contain a bogus sector size - */ - if (blksz == 0) - blksz = DEV_BSIZE; - /* too small to contain a zpool? */ - if ((size < (SPA_MINDEVSIZE / blksz)) && - (node = avl_find(r, &tmpnode, NULL))) - node->rn_nozpool = B_TRUE; -} - -static void -nozpool_all_slices(avl_tree_t *r, const char *sname) -{ - char diskname[MAXNAMELEN]; - char *ptr; - int i; - - (void) strncpy(diskname, sname, MAXNAMELEN); - if (((ptr = strrchr(diskname, 's')) == NULL) && - ((ptr = strrchr(diskname, 'p')) == NULL)) - return; - ptr[0] = 's'; - ptr[1] = '\0'; - for (i = 0; i < NDKMAP; i++) - check_one_slice(r, diskname, i, 0, 1); - ptr[0] = 'p'; - for (i = 0; i <= FD_NUMPART; i++) - check_one_slice(r, diskname, i, 0, 1); -} - -static void -check_slices(avl_tree_t *r, int fd, const char *sname) -{ - struct extvtoc vtoc; - struct dk_gpt *gpt; - char diskname[MAXNAMELEN]; - char *ptr; - int i; - - (void) strncpy(diskname, sname, MAXNAMELEN); - if ((ptr = strrchr(diskname, 's')) == NULL || !isdigit(ptr[1])) - return; - ptr[1] = '\0'; - - if (read_extvtoc(fd, &vtoc) >= 0) { - for (i = 0; i < NDKMAP; i++) - check_one_slice(r, diskname, i, - vtoc.v_part[i].p_size, vtoc.v_sectorsz); - } else if (efi_alloc_and_read(fd, &gpt) >= 0) { - /* - * on x86 we'll still have leftover links that point - * to slices s[9-15], so use NDKMAP instead - */ - for (i = 0; i < NDKMAP; i++) - check_one_slice(r, diskname, i, - gpt->efi_parts[i].p_size, gpt->efi_lbasize); - /* nodes p[1-4] are never used with EFI labels */ - ptr[0] = 'p'; - for (i = 1; i <= FD_NUMPART; i++) - check_one_slice(r, diskname, i, 0, 1); - efi_free(gpt); - } -} - -static void -zpool_open_func(void *arg) -{ - rdsk_node_t *rn = arg; - struct stat64 statbuf; - nvlist_t *config; - int fd; - - if (rn->rn_nozpool) - return; - if ((fd = openat64(rn->rn_dfd, rn->rn_name, O_RDONLY)) < 0) { - /* symlink to a device that's no longer there */ - if (errno == ENOENT) - nozpool_all_slices(rn->rn_avl, rn->rn_name); - return; - } - /* - * Ignore failed stats. We only want regular - * files, character devs and block devs. - */ - if (fstat64(fd, &statbuf) != 0 || - (!S_ISREG(statbuf.st_mode) && - !S_ISCHR(statbuf.st_mode) && - !S_ISBLK(statbuf.st_mode))) { - (void) close(fd); - return; - } - /* this file is too small to hold a zpool */ - if (S_ISREG(statbuf.st_mode) && - statbuf.st_size < SPA_MINDEVSIZE) { - (void) close(fd); - return; - } else if (!S_ISREG(statbuf.st_mode)) { - /* - * Try to read the disk label first so we don't have to - * open a bunch of minor nodes that can't have a zpool. - */ - check_slices(rn->rn_avl, fd, rn->rn_name); - } - - if ((zpool_read_label(fd, &config)) != 0 && errno == ENOMEM) { - (void) close(fd); - (void) no_memory(rn->rn_hdl); - return; - } - (void) close(fd); - - rn->rn_config = config; -} - -/* * Given a file descriptor, clear (zero) the label information. */ int @@ -1094,433 +189,6 @@ zpool_clear_label(int fd) return (0); } -/* - * Given a list of directories to search, find all pools stored on disk. This - * includes partial pools which are not available to import. If no args are - * given (argc is 0), then the default directory (/dev/dsk) is searched. - * poolname or guid (but not both) are provided by the caller when trying - * to import a specific pool. - */ -static nvlist_t * -zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg) -{ - int i, dirs = iarg->paths; - struct dirent64 *dp; - char path[MAXPATHLEN]; - char *end, **dir = iarg->path; - size_t pathleft; - nvlist_t *ret = NULL; - static char *default_dir = ZFS_DISK_ROOT; - pool_list_t pools = { 0 }; - pool_entry_t *pe, *penext; - vdev_entry_t *ve, *venext; - config_entry_t *ce, *cenext; - name_entry_t *ne, *nenext; - avl_tree_t slice_cache; - rdsk_node_t *slice; - void *cookie; - - if (dirs == 0) { - dirs = 1; - dir = &default_dir; - } - - /* - * Go through and read the label configuration information from every - * possible device, organizing the information according to pool GUID - * and toplevel GUID. - */ - for (i = 0; i < dirs; i++) { - tpool_t *t; - char rdsk[MAXPATHLEN]; - int dfd; - boolean_t config_failed = B_FALSE; - DIR *dirp; - - /* use realpath to normalize the path */ - if (realpath(dir[i], path) == 0) { - (void) zfs_error_fmt(hdl, EZFS_BADPATH, - dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]); - goto error; - } - end = &path[strlen(path)]; - *end++ = '/'; - *end = 0; - pathleft = &path[sizeof (path)] - end; - - /* - * Using raw devices instead of block devices when we're - * reading the labels skips a bunch of slow operations during - * close(2) processing, so we replace /dev/dsk with /dev/rdsk. - */ - if (strcmp(path, ZFS_DISK_ROOTD) == 0) - (void) strlcpy(rdsk, ZFS_RDISK_ROOTD, sizeof (rdsk)); - else - (void) strlcpy(rdsk, path, sizeof (rdsk)); - - if ((dfd = open64(rdsk, O_RDONLY)) < 0 || - (dirp = fdopendir(dfd)) == NULL) { - if (dfd >= 0) - (void) close(dfd); - zfs_error_aux(hdl, strerror(errno)); - (void) zfs_error_fmt(hdl, EZFS_BADPATH, - dgettext(TEXT_DOMAIN, "cannot open '%s'"), - rdsk); - goto error; - } - - avl_create(&slice_cache, slice_cache_compare, - sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node)); - /* - * This is not MT-safe, but we have no MT consumers of libzfs - */ - while ((dp = readdir64(dirp)) != NULL) { - const char *name = dp->d_name; - if (name[0] == '.' && - (name[1] == 0 || (name[1] == '.' && name[2] == 0))) - continue; - - slice = zfs_alloc(hdl, sizeof (rdsk_node_t)); - slice->rn_name = zfs_strdup(hdl, name); - slice->rn_avl = &slice_cache; - slice->rn_dfd = dfd; - slice->rn_hdl = hdl; - slice->rn_nozpool = B_FALSE; - avl_add(&slice_cache, slice); - } - /* - * create a thread pool to do all of this in parallel; - * rn_nozpool is not protected, so this is racy in that - * multiple tasks could decide that the same slice can - * not hold a zpool, which is benign. Also choose - * double the number of processors; we hold a lot of - * locks in the kernel, so going beyond this doesn't - * buy us much. - */ - t = tpool_create(1, 2 * sysconf(_SC_NPROCESSORS_ONLN), - 0, NULL); - for (slice = avl_first(&slice_cache); slice; - (slice = avl_walk(&slice_cache, slice, - AVL_AFTER))) - (void) tpool_dispatch(t, zpool_open_func, slice); - tpool_wait(t); - tpool_destroy(t); - - cookie = NULL; - while ((slice = avl_destroy_nodes(&slice_cache, - &cookie)) != NULL) { - if (slice->rn_config != NULL && !config_failed) { - nvlist_t *config = slice->rn_config; - boolean_t matched = B_TRUE; - - if (iarg->poolname != NULL) { - char *pname; - - matched = nvlist_lookup_string(config, - ZPOOL_CONFIG_POOL_NAME, - &pname) == 0 && - strcmp(iarg->poolname, pname) == 0; - } else if (iarg->guid != 0) { - uint64_t this_guid; - - matched = nvlist_lookup_uint64(config, - ZPOOL_CONFIG_POOL_GUID, - &this_guid) == 0 && - iarg->guid == this_guid; - } - if (matched) { - /* - * use the non-raw path for the config - */ - (void) strlcpy(end, slice->rn_name, - pathleft); - if (add_config(hdl, &pools, path, - config) != 0) - config_failed = B_TRUE; - } - nvlist_free(config); - } - free(slice->rn_name); - free(slice); - } - avl_destroy(&slice_cache); - - (void) closedir(dirp); - - if (config_failed) - goto error; - } - - ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy); - -error: - for (pe = pools.pools; pe != NULL; pe = penext) { - penext = pe->pe_next; - for (ve = pe->pe_vdevs; ve != NULL; ve = venext) { - venext = ve->ve_next; - for (ce = ve->ve_configs; ce != NULL; ce = cenext) { - cenext = ce->ce_next; - nvlist_free(ce->ce_config); - free(ce); - } - free(ve); - } - free(pe); - } - - for (ne = pools.names; ne != NULL; ne = nenext) { - nenext = ne->ne_next; - free(ne->ne_name); - free(ne); - } - - return (ret); -} - -nvlist_t * -zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv) -{ - importargs_t iarg = { 0 }; - - iarg.paths = argc; - iarg.path = argv; - - return (zpool_find_import_impl(hdl, &iarg)); -} - -/* - * Given a cache file, return the contents as a list of importable pools. - * poolname or guid (but not both) are provided by the caller when trying - * to import a specific pool. - */ -nvlist_t * -zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile, - char *poolname, uint64_t guid) -{ - char *buf; - int fd; - struct stat64 statbuf; - nvlist_t *raw, *src, *dst; - nvlist_t *pools; - nvpair_t *elem; - char *name; - uint64_t this_guid; - boolean_t active; - - verify(poolname == NULL || guid == 0); - - if ((fd = open(cachefile, O_RDONLY)) < 0) { - zfs_error_aux(hdl, "%s", strerror(errno)); - (void) zfs_error(hdl, EZFS_BADCACHE, - dgettext(TEXT_DOMAIN, "failed to open cache file")); - return (NULL); - } - - if (fstat64(fd, &statbuf) != 0) { - zfs_error_aux(hdl, "%s", strerror(errno)); - (void) close(fd); - (void) zfs_error(hdl, EZFS_BADCACHE, - dgettext(TEXT_DOMAIN, "failed to get size of cache file")); - return (NULL); - } - - if ((buf = zfs_alloc(hdl, statbuf.st_size)) == NULL) { - (void) close(fd); - return (NULL); - } - - if (read(fd, buf, statbuf.st_size) != statbuf.st_size) { - (void) close(fd); - free(buf); - (void) zfs_error(hdl, EZFS_BADCACHE, - dgettext(TEXT_DOMAIN, - "failed to read cache file contents")); - return (NULL); - } - - (void) close(fd); - - if (nvlist_unpack(buf, statbuf.st_size, &raw, 0) != 0) { - free(buf); - (void) zfs_error(hdl, EZFS_BADCACHE, - dgettext(TEXT_DOMAIN, - "invalid or corrupt cache file contents")); - return (NULL); - } - - free(buf); - - /* - * Go through and get the current state of the pools and refresh their - * state. - */ - if (nvlist_alloc(&pools, 0, 0) != 0) { - (void) no_memory(hdl); - nvlist_free(raw); - return (NULL); - } - - elem = NULL; - while ((elem = nvlist_next_nvpair(raw, elem)) != NULL) { - src = fnvpair_value_nvlist(elem); - - name = fnvlist_lookup_string(src, ZPOOL_CONFIG_POOL_NAME); - if (poolname != NULL && strcmp(poolname, name) != 0) - continue; - - this_guid = fnvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID); - if (guid != 0 && guid != this_guid) - continue; - - if (pool_active(hdl, name, this_guid, &active) != 0) { - nvlist_free(raw); - nvlist_free(pools); - return (NULL); - } - - if (active) - continue; - - if (nvlist_add_string(src, ZPOOL_CONFIG_CACHEFILE, - cachefile) != 0) { - (void) no_memory(hdl); - nvlist_free(raw); - nvlist_free(pools); - return (NULL); - } - - if ((dst = refresh_config(hdl, src)) == NULL) { - nvlist_free(raw); - nvlist_free(pools); - return (NULL); - } - - if (nvlist_add_nvlist(pools, nvpair_name(elem), dst) != 0) { - (void) no_memory(hdl); - nvlist_free(dst); - nvlist_free(raw); - nvlist_free(pools); - return (NULL); - } - nvlist_free(dst); - } - - nvlist_free(raw); - return (pools); -} - -static int -name_or_guid_exists(zpool_handle_t *zhp, void *data) -{ - importargs_t *import = data; - int found = 0; - - if (import->poolname != NULL) { - char *pool_name; - - verify(nvlist_lookup_string(zhp->zpool_config, - ZPOOL_CONFIG_POOL_NAME, &pool_name) == 0); - if (strcmp(pool_name, import->poolname) == 0) - found = 1; - } else { - uint64_t pool_guid; - - verify(nvlist_lookup_uint64(zhp->zpool_config, - ZPOOL_CONFIG_POOL_GUID, &pool_guid) == 0); - if (pool_guid == import->guid) - found = 1; - } - - zpool_close(zhp); - return (found); -} - -nvlist_t * -zpool_search_import(libzfs_handle_t *hdl, importargs_t *import) -{ - nvlist_t *pools = NULL; - - verify(import->poolname == NULL || import->guid == 0); - - if (import->unique) - import->exists = zpool_iter(hdl, name_or_guid_exists, import); - - if (import->cachefile != NULL) - pools = zpool_find_import_cached(hdl, import->cachefile, - import->poolname, import->guid); - else - pools = zpool_find_import_impl(hdl, import); - - return (pools); -} - -static boolean_t -pool_match(nvlist_t *cfg, char *tgt) -{ - uint64_t v, guid = strtoull(tgt, NULL, 0); - char *s; - - if (guid != 0) { - if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &v) == 0) - return (v == guid); - } else { - if (nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &s) == 0) - return (strcmp(s, tgt) == 0); - } - return (B_FALSE); -} - -int -zpool_tryimport(libzfs_handle_t *hdl, char *target, nvlist_t **configp, - importargs_t *args) -{ - nvlist_t *pools; - nvlist_t *match = NULL; - nvlist_t *config = NULL; - char *sepp = NULL; - int count = 0; - char *targetdup = strdup(target); - - *configp = NULL; - - if ((sepp = strpbrk(targetdup, "/@")) != NULL) { - *sepp = '\0'; - } - - pools = zpool_search_import(hdl, args); - - if (pools != NULL) { - nvpair_t *elem = NULL; - while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { - VERIFY0(nvpair_value_nvlist(elem, &config)); - if (pool_match(config, targetdup)) { - count++; - if (match != NULL) { - /* multiple matches found */ - continue; - } else { - match = config; - } - } - } - } - - if (count == 0) { - free(targetdup); - return (ENOENT); - } - - if (count > 1) { - free(targetdup); - return (EINVAL); - } - - *configp = match; - free(targetdup); - - return (0); -} - boolean_t find_guid(nvlist_t *nv, uint64_t guid) { @@ -1597,7 +265,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, *inuse = B_FALSE; - if (zpool_read_label(fd, &config) != 0 && errno == ENOMEM) { + if (zpool_read_label(fd, &config, NULL) != 0 && errno == ENOMEM) { (void) no_memory(hdl); return (-1); } diff --git a/usr/src/lib/libzfs/common/libzfs_iter.c b/usr/src/lib/libzfs/common/libzfs_iter.c index 8fc3eea4f4..b90293fedd 100644 --- a/usr/src/lib/libzfs/common/libzfs_iter.c +++ b/usr/src/lib/libzfs/common/libzfs_iter.c @@ -33,6 +33,7 @@ #include <stddef.h> #include <libintl.h> #include <libzfs.h> +#include <libzutil.h> #include "libzfs_impl.h" diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index 20894d450a..f1784dae9c 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -43,6 +43,7 @@ #include <sys/vtoc.h> #include <sys/zfs_ioctl.h> #include <dlfcn.h> +#include <libzutil.h> #include "zfs_namecheck.h" #include "zfs_prop.h" @@ -4298,47 +4299,6 @@ get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len) } /* - * Process the buffer of nvlists, unpacking and storing each nvlist record - * into 'records'. 'leftover' is set to the number of bytes that weren't - * processed as there wasn't a complete record. - */ -int -zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover, - nvlist_t ***records, uint_t *numrecords) -{ - uint64_t reclen; - nvlist_t *nv; - int i; - - while (bytes_read > sizeof (reclen)) { - - /* get length of packed record (stored as little endian) */ - for (i = 0, reclen = 0; i < sizeof (reclen); i++) - reclen += (uint64_t)(((uchar_t *)buf)[i]) << (8*i); - - if (bytes_read < sizeof (reclen) + reclen) - break; - - /* unpack record */ - if (nvlist_unpack(buf + sizeof (reclen), reclen, &nv, 0) != 0) - return (ENOMEM); - bytes_read -= sizeof (reclen) + reclen; - buf += sizeof (reclen) + reclen; - - /* add record to nvlist array */ - (*numrecords)++; - if (ISP2(*numrecords + 1)) { - *records = realloc(*records, - *numrecords * 2 * sizeof (nvlist_t *)); - } - (*records)[*numrecords - 1] = nv; - } - - *leftover = bytes_read; - return (0); -} - -/* * Retrieve the command history of a pool. */ int diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c index 174380a777..2bfb49875d 100644 --- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c +++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c @@ -49,6 +49,7 @@ #include <libzfs.h> #include <libzfs_core.h> +#include <libzutil.h> #include "zfs_namecheck.h" #include "zfs_prop.h" diff --git a/usr/src/lib/libzfs/common/libzfs_status.c b/usr/src/lib/libzfs/common/libzfs_status.c index 46ea7f944f..c12736e4de 100644 --- a/usr/src/lib/libzfs/common/libzfs_status.c +++ b/usr/src/lib/libzfs/common/libzfs_status.c @@ -42,6 +42,7 @@ */ #include <libzfs.h> +#include <libzutil.h> #include <string.h> #include <unistd.h> #include "libzfs_impl.h" @@ -437,68 +438,3 @@ zpool_import_status(nvlist_t *config, char **msgid, zpool_errata_t *errata) return (ret); } - -static void -dump_ddt_stat(const ddt_stat_t *dds, int h) -{ - char refcnt[6]; - char blocks[6], lsize[6], psize[6], dsize[6]; - char ref_blocks[6], ref_lsize[6], ref_psize[6], ref_dsize[6]; - - if (dds == NULL || dds->dds_blocks == 0) - return; - - if (h == -1) - (void) strcpy(refcnt, "Total"); - else - zfs_nicenum(1ULL << h, refcnt, sizeof (refcnt)); - - zfs_nicenum(dds->dds_blocks, blocks, sizeof (blocks)); - zfs_nicenum(dds->dds_lsize, lsize, sizeof (lsize)); - zfs_nicenum(dds->dds_psize, psize, sizeof (psize)); - zfs_nicenum(dds->dds_dsize, dsize, sizeof (dsize)); - zfs_nicenum(dds->dds_ref_blocks, ref_blocks, sizeof (ref_blocks)); - zfs_nicenum(dds->dds_ref_lsize, ref_lsize, sizeof (ref_lsize)); - zfs_nicenum(dds->dds_ref_psize, ref_psize, sizeof (ref_psize)); - zfs_nicenum(dds->dds_ref_dsize, ref_dsize, sizeof (ref_dsize)); - - (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n", - refcnt, - blocks, lsize, psize, dsize, - ref_blocks, ref_lsize, ref_psize, ref_dsize); -} - -/* - * Print the DDT histogram and the column totals. - */ -void -zpool_dump_ddt(const ddt_stat_t *dds_total, const ddt_histogram_t *ddh) -{ - int h; - - (void) printf("\n"); - - (void) printf("bucket " - " allocated " - " referenced \n"); - (void) printf("______ " - "______________________________ " - "______________________________\n"); - - (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n", - "refcnt", - "blocks", "LSIZE", "PSIZE", "DSIZE", - "blocks", "LSIZE", "PSIZE", "DSIZE"); - - (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n", - "------", - "------", "-----", "-----", "-----", - "------", "-----", "-----", "-----"); - - for (h = 0; h < 64; h++) - dump_ddt_stat(&ddh->ddh_stat[h], h); - - dump_ddt_stat(dds_total, -1); - - (void) printf("\n"); -} diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index 75eb9722b3..3285cf7ce0 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2019 Joyent, Inc. + * Copyright 2020 Joyent, Inc. * Copyright (c) 2011, 2017 by Delphix. All rights reserved. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> * Copyright (c) 2017 Datto Inc. @@ -31,6 +31,7 @@ * Internal utility routines for the ZFS library. */ +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <libintl.h> @@ -38,8 +39,6 @@ #include <stdio.h> #include <stdlib.h> #include <strings.h> -#include <unistd.h> -#include <ctype.h> #include <math.h> #include <sys/filio.h> #include <sys/mnttab.h> @@ -54,6 +53,7 @@ #include "zfs_prop.h" #include "zfs_comutil.h" #include "zfeature_common.h" +#include <libzutil.h> int libzfs_errno(libzfs_handle_t *hdl) @@ -630,15 +630,6 @@ zfs_strdup(libzfs_handle_t *hdl, const char *str) return (ret); } -/* - * Convert a number to an appropriately human-readable output. - */ -void -zfs_nicenum(uint64_t num, char *buf, size_t buflen) -{ - nicenum(num, buf, buflen); -} - void libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr) { @@ -1622,20 +1613,3 @@ zfs_get_hole_count(const char *path, uint64_t *count, uint64_t *bs) } return (0); } - -ulong_t -get_system_hostid(void) -{ - char *env; - - /* - * Allow the hostid to be subverted for testing. - */ - env = getenv("ZFS_HOSTID"); - if (env) { - ulong_t hostid = strtoull(env, NULL, 16); - return (hostid & 0xFFFFFFFF); - } - - return (gethostid()); -} diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 95fb983e8c..49fc94a536 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -23,7 +23,7 @@ # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2011, 2017 by Delphix. All rights reserved. # Copyright 2016 Nexenta Systems, Inc. -# Copyright 2019 Joyent, Inc. +# Copyright 2020 Joyent, Inc. # # @@ -50,8 +50,8 @@ SYMBOL_VERSION SUNWprivate_1.1 { fletcher_4_byteswap; fletcher_4_incremental_native; fletcher_4_incremental_byteswap; - get_system_hostid; libzfs_add_handle; + libzfs_config_ops; libzfs_errno; libzfs_error_action; libzfs_error_description; @@ -111,7 +111,6 @@ SYMBOL_VERSION SUNWprivate_1.1 { zfs_mount; zfs_name_to_prop; zfs_name_valid; - zfs_nicenum; zfs_nicestrtonum; zfs_open; zfs_path_to_zhandle; @@ -195,14 +194,11 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_destroy; zpool_disable_datasets; zpool_discard_checkpoint; - zpool_dump_ddt; zpool_enable_datasets; zpool_expand_proplist; zpool_explain_recover; zpool_export; zpool_export_force; - zpool_find_import; - zpool_find_import_cached; zpool_find_vdev; zpool_find_vdev_by_physpath; zpool_fru_set; @@ -217,7 +213,6 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_get_prop_int; zpool_get_state; zpool_get_status; - zpool_history_unpack; zpool_import; zpool_import_props; zpool_import_status; @@ -242,18 +237,15 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_prop_to_name; zpool_prop_unsupported; zpool_prop_values; - zpool_read_label; zpool_refresh_stats; zpool_reguid; zpool_reopen; zpool_scan; - zpool_search_import; zpool_set_prop; zpool_skip_pool; zpool_state_to_name; zpool_sync_one; zpool_trim; - zpool_tryimport; zpool_unmount_datasets; zpool_upgrade; zpool_vdev_attach; diff --git a/usr/src/lib/libzfs_jni/Makefile.com b/usr/src/lib/libzfs_jni/Makefile.com index 7d2d4bca6c..9c3bc1e523 100644 --- a/usr/src/lib/libzfs_jni/Makefile.com +++ b/usr/src/lib/libzfs_jni/Makefile.com @@ -24,7 +24,7 @@ # # Copyright (c) 2015 by Delphix. All rights reserved. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2020 Joyent, Inc. # LIBRARY= libzfs_jni.a @@ -43,9 +43,10 @@ include ../../Makefile.lib LIBS= $(DYNLIB) INCS += -I$(JAVA_ROOT)/include \ - -I$(JAVA_ROOT)/include/solaris + -I$(JAVA_ROOT)/include/solaris \ + -I../../libzutil/common -LDLIBS += -lc -lnvpair -ldiskmgt -lzfs +LDLIBS += -lc -lnvpair -ldiskmgt -lzfs -lzutil CPPFLAGS += $(INCS) $(NOT_RELEASE_BUILD) CPPFLAGS += -DDEBUG CERRWARN += -_gcc=-Wno-switch 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 2eb36c0145..e4fa074f91 100644 --- a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c +++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c @@ -21,10 +21,12 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Joyent, Inc. */ #include "libzfs_jni_util.h" #include "libzfs_jni_pool.h" +#include <libzutil.h> #include <strings.h> /* @@ -753,7 +755,8 @@ create_MirrorVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp, } static char * -find_field(const zjni_field_mapping_t *mapping, int value) { +find_field(const zjni_field_mapping_t *mapping, int value) +{ int i; for (i = 0; mapping[i].name != NULL; i++) { if (value == mapping[i].value) { @@ -1046,7 +1049,8 @@ zjni_get_VirtualDevices_from_vdev(JNIEnv *env, zpool_handle_t *zhp, } int -zjni_create_add_ImportablePool(nvlist_t *config, void *data) { +zjni_create_add_ImportablePool(nvlist_t *config, void *data) +{ JNIEnv *env = ((zjni_ArrayCallbackData_t *)data)->env; zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list; @@ -1154,7 +1158,14 @@ 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(g_zfs, argc, argv); + nvlist_t *pools; + importargs_t iarg = { 0 }; + + iarg.paths = argc; + iarg.path = argv; + iarg.can_be_active = B_TRUE; + + pools = zpool_search_import(g_zfs, &iarg, &libzfs_config_ops); if (pools != NULL) { nvpair_t *elem = NULL; @@ -1173,21 +1184,25 @@ zjni_ipool_iter(int argc, char **argv, zjni_ipool_iter_f func, void *data) } char * -zjni_vdev_state_to_str(vdev_state_t state) { +zjni_vdev_state_to_str(vdev_state_t state) +{ return (find_field(vdev_state_map, state)); } char * -zjni_vdev_aux_to_str(vdev_aux_t aux) { +zjni_vdev_aux_to_str(vdev_aux_t aux) +{ return (find_field(vdev_aux_map, aux)); } char * -zjni_pool_state_to_str(pool_state_t state) { +zjni_pool_state_to_str(pool_state_t state) +{ return (find_field(pool_state_map, state)); } char * -zjni_pool_status_to_str(zpool_status_t status) { +zjni_pool_status_to_str(zpool_status_t status) +{ return (find_field(zpool_status_map, status)); } diff --git a/usr/src/lib/libzpool/Makefile.com b/usr/src/lib/libzpool/Makefile.com index 82406fe3f7..dea6d89ab8 100644 --- a/usr/src/lib/libzpool/Makefile.com +++ b/usr/src/lib/libzpool/Makefile.com @@ -21,7 +21,7 @@ # # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2013, 2016 by Delphix. All rights reserved. -# Copyright 2019, Joyent, Inc. +# Copyright 2020 Joyent, Inc. # LIBRARY= libzpool.a @@ -56,6 +56,8 @@ INCS += -I../../../uts/common/fs/zfs/lua INCS += -I../../../common/zfs INCS += -I../../../common/lz4 INCS += -I../../../common +INCS += -I../../libzutil/common + CLEANFILES += ../common/zfs.h CLEANFILES += $(EXTPICS) @@ -70,7 +72,7 @@ C99LMODE= -Xc99=%all CFLAGS += $(CCGDEBUG) $(CCVERBOSE) $(CNOGLOBAL) CFLAGS64 += $(CCGDEBUG) $(CCVERBOSE) $(CNOGLOBAL) LDLIBS += -lcmdutils -lumem -lavl -lnvpair -lz -lc -lsysevent -lmd \ - -lfakekernel -lzfs + -lfakekernel -lzutil NATIVE_LIBS += libz.so CPPFLAGS.first = -I$(SRC)/lib/libfakekernel/common CPPFLAGS += $(INCS) -DDEBUG -D_FAKE_KERNEL diff --git a/usr/src/lib/libzpool/common/kernel.c b/usr/src/lib/libzpool/common/kernel.c index 95a1c54622..0d43302861 100644 --- a/usr/src/lib/libzpool/common/kernel.c +++ b/usr/src/lib/libzpool/common/kernel.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015 by Delphix. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2020 Joyent, Inc. * Copyright 2017 RackTop Systems. */ @@ -41,7 +41,7 @@ #include <sys/zmod.h> #include <sys/utsname.h> #include <sys/systeminfo.h> -#include <libzfs.h> +#include <libzutil.h> #include <sys/crypto/common.h> #include <sys/crypto/impl.h> #include <sys/crypto/api.h> diff --git a/usr/src/lib/libzpool/common/util.c b/usr/src/lib/libzpool/common/util.c index 38b2e9e458..8525b5f299 100644 --- a/usr/src/lib/libzpool/common/util.c +++ b/usr/src/lib/libzpool/common/util.c @@ -34,7 +34,9 @@ #include <sys/spa.h> #include <sys/fs/zfs.h> #include <sys/refcount.h> +#include <sys/zfs_ioctl.h> #include <dlfcn.h> +#include <libzutil.h> extern void nicenum(uint64_t num, char *buf, size_t); @@ -197,3 +199,56 @@ set_global_var(char *arg) return (0); } + +static nvlist_t * +refresh_config(void *unused, nvlist_t *tryconfig) +{ + return (spa_tryimport(tryconfig)); +} + +static int +pool_active(void *unused, const char *name, uint64_t guid, + boolean_t *isactive) +{ + zfs_cmd_t *zcp; + nvlist_t *innvl; + char *packed = NULL; + size_t size = 0; + int fd, ret; + + /* + * Use ZFS_IOC_POOL_SYNC to confirm if a pool is active + */ + + fd = open("/dev/zfs", O_RDWR); + if (fd < 0) + return (-1); + + zcp = umem_zalloc(sizeof (zfs_cmd_t), UMEM_NOFAIL); + + innvl = fnvlist_alloc(); + fnvlist_add_boolean_value(innvl, "force", B_FALSE); + + (void) strlcpy(zcp->zc_name, name, sizeof (zcp->zc_name)); + packed = fnvlist_pack(innvl, &size); + zcp->zc_nvlist_src = (uint64_t)(uintptr_t)packed; + zcp->zc_nvlist_src_size = size; + + ret = ioctl(fd, ZFS_IOC_POOL_SYNC, zcp); + + fnvlist_pack_free(packed, size); + free((void *)(uintptr_t)zcp->zc_nvlist_dst); + nvlist_free(innvl); + umem_free(zcp, sizeof (zfs_cmd_t)); + + (void) close(fd); + + *isactive = (ret == 0); + + return (0); +} + +const pool_config_ops_t libzpool_config_ops = { + .pco_refresh_config = refresh_config, + .pco_pool_active = pool_active, +}; diff --git a/usr/src/lib/libzutil/Makefile b/usr/src/lib/libzutil/Makefile new file mode 100644 index 0000000000..745a6eab95 --- /dev/null +++ b/usr/src/lib/libzutil/Makefile @@ -0,0 +1,51 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.lib + +HDRS= libzutil.h + +HDRDIR= common + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install + +MSGFILES = `$(GREP) -l gettext $(HDRDIR)/*.[ch]` +POFILE = libzutil.po + +.KEEP_STATE: + +all clean clobber install: $(SUBDIRS) + +$(POFILE): pofile_MSGFILES + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ +include ../../Makefile.msg.targ diff --git a/usr/src/lib/libzutil/Makefile.com b/usr/src/lib/libzutil/Makefile.com new file mode 100644 index 0000000000..9ec9f67dab --- /dev/null +++ b/usr/src/lib/libzutil/Makefile.com @@ -0,0 +1,49 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY= libzutil.a +VERS= .1 + +OBJECTS= \ + zutil_import.o \ + zutil_nicenum.o \ + zutil_pool.o + +include ../../Makefile.lib + +# libzutil must be installed in the root filesystem for mount(1M) +include ../../Makefile.rootfs + +LIBS= $(DYNLIB) $(LINTLIB) + +SRCDIR = ../common + +INCS += -I$(SRCDIR) +INCS += -I../../../uts/common/fs/zfs +INCS += -I../../libc/inc + +CSTD= $(CSTD_GNU99) +C99LMODE= -Xc99=%all +LDLIBS += -lc -lm -ldevid -lnvpair -ladm -lavl -lefi +CPPFLAGS += $(INCS) -D_LARGEFILE64_SOURCE=1 -D_REENTRANT +$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG + +SRCS= $(OBJECTS:%.o=$(SRCDIR)/%.c) + +.KEEP_STATE: + +all: $(LIBS) + +include ../../Makefile.targ diff --git a/usr/src/lib/libzutil/amd64/Makefile b/usr/src/lib/libzutil/amd64/Makefile new file mode 100644 index 0000000000..5a2ea08b45 --- /dev/null +++ b/usr/src/lib/libzutil/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libzutil/common/libzutil.h b/usr/src/lib/libzutil/common/libzutil.h new file mode 100644 index 0000000000..37ab9ee125 --- /dev/null +++ b/usr/src/lib/libzutil/common/libzutil.h @@ -0,0 +1,122 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 by Delphix. All rights reserved. + * Copyright 2020 Joyent, Inc. + */ + +#ifndef _LIBZUTIL_H +#define _LIBZUTIL_H + +#include <sys/nvpair.h> +#include <sys/fs/zfs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Default wait time for a device name to be created. + */ +#define DISK_LABEL_WAIT (30 * 1000) /* 30 seconds */ + + +/* + * Pool Config Operations + * + * These are specific to the library libzfs or libzpool instance. + */ +typedef nvlist_t *refresh_config_func_t(void *, nvlist_t *); + +typedef int pool_active_func_t(void *, const char *, uint64_t, boolean_t *); + +typedef struct pool_config_ops { + refresh_config_func_t *pco_refresh_config; + pool_active_func_t *pco_pool_active; +} pool_config_ops_t; + +/* + * An instance of pool_config_ops_t is expected in the caller's binary. + */ +extern const pool_config_ops_t libzfs_config_ops; +extern const pool_config_ops_t libzpool_config_ops; + +typedef struct importargs { + char **path; /* a list of paths to search */ + int paths; /* number of paths to search */ + const char *poolname; /* name of a pool to find */ + uint64_t guid; /* guid of a pool to find */ + const char *cachefile; /* cachefile to use for import */ + boolean_t can_be_active; /* can the pool be active? */ + boolean_t scan; /* prefer scanning to libblkid cache */ + nvlist_t *policy; /* load policy (max txg, rewind, etc.) */ +} importargs_t; + +extern nvlist_t *zpool_search_import(void *, importargs_t *, + const pool_config_ops_t *); +extern int zpool_find_config(void *, const char *, nvlist_t **, importargs_t *, + const pool_config_ops_t *); + +extern int zpool_read_label(int, nvlist_t **, int *); + +extern boolean_t zfs_isnumber(const char *); + +/* + * Formats for iostat numbers. Examples: "12K", "30ms", "4B", "2321234", "-". + * + * ZFS_NICENUM_1024: Print kilo, mega, tera, peta, exa.. + * ZFS_NICENUM_BYTES: Print single bytes ("13B"), kilo, mega, tera... + * ZFS_NICENUM_TIME: Print nanosecs, microsecs, millisecs, seconds... + * ZFS_NICENUM_RAW: Print the raw number without any formatting + * ZFS_NICENUM_RAWTIME: Same as RAW, but print dashes ('-') for zero. + */ +enum zfs_nicenum_format { + ZFS_NICENUM_1024 = 0, + ZFS_NICENUM_BYTES = 1, + ZFS_NICENUM_TIME = 2, + ZFS_NICENUM_RAW = 3, + ZFS_NICENUM_RAWTIME = 4 +}; + +/* + * Convert a number to a human-readable form. + */ +extern void zfs_nicebytes(uint64_t, char *, size_t); +extern void zfs_nicenum(uint64_t, char *, size_t); +extern void zfs_nicenum_format(uint64_t, char *, size_t, + enum zfs_nicenum_format); +extern void zfs_nicetime(uint64_t, char *, size_t); + +#define nicenum(num, buf, size) zfs_nicenum(num, buf, size) + +extern void zpool_dump_ddt(const ddt_stat_t *, const ddt_histogram_t *); +extern int zpool_history_unpack(char *, uint64_t, uint64_t *, nvlist_t ***, + uint_t *); + +/* Part of SPL in OpenZFS */ +extern ulong_t get_system_hostid(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBZUTIL_H */ diff --git a/usr/src/lib/libzutil/common/mapfile-vers b/usr/src/lib/libzutil/common/mapfile-vers new file mode 100644 index 0000000000..bd73d6c951 --- /dev/null +++ b/usr/src/lib/libzutil/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate_1.1 { + global: + get_system_hostid; + zfs_isnumber; + zfs_nicebytes; + zfs_nicenum; + zfs_nicenum_format; + zfs_niceraw; + zfs_nicetime; + zpool_dump_ddt; + zpool_find_config; + zpool_history_unpack; + zpool_read_label; + zpool_search_import; + local: + *; +}; diff --git a/usr/src/lib/libzutil/common/zutil_import.c b/usr/src/lib/libzutil/common/zutil_import.c new file mode 100644 index 0000000000..961247c5c0 --- /dev/null +++ b/usr/src/lib/libzutil/common/zutil_import.c @@ -0,0 +1,1548 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2017 Nexenta Systems, Inc. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright 2015 RackTop Systems. + * Copyright (c) 2016, Intel Corporation. + */ + +/* + * Pool import support functions. + * + * Used by zpool, ztest, zdb, and zhack to locate importable configs. Since + * these commands are expected to run in the global zone, we can assume + * that the devices are all readable when called. + * + * To import a pool, we rely on reading the configuration information from the + * ZFS label of each device. If we successfully read the label, then we + * organize the configuration information in the following hierarchy: + * + * pool guid -> toplevel vdev guid -> label txg + * + * Duplicate entries matching this same tuple will be discarded. Once we have + * examined every device, we pick the best label txg config for each toplevel + * vdev. We then arrange these toplevel vdevs into a complete pool config, and + * update any paths that have changed. Finally, we attempt to import the pool + * using our derived config, and record the results. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <ctype.h> +#include <devid.h> +#include <dirent.h> +#include <errno.h> +#include <libintl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/vtoc.h> +#include <sys/dktp/fdisk.h> +#include <sys/efi_partition.h> +#include <sys/vdev_impl.h> +#include <sys/fs/zfs.h> + +#include <thread_pool.h> +#include <libzutil.h> +#include <libnvpair.h> + +#include "zutil_import.h" + +#ifdef NDEBUG +#define verify(EX) ((void)(EX)) +#else +#define verify(EX) assert(EX) +#endif + +/*PRINTFLIKE2*/ +static void +zutil_error_aux(libpc_handle_t *hdl, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + (void) vsnprintf(hdl->lpc_desc, sizeof (hdl->lpc_desc), fmt, ap); + hdl->lpc_desc_active = B_TRUE; + + va_end(ap); +} + +static void +zutil_verror(libpc_handle_t *hdl, const char *error, const char *fmt, + va_list ap) +{ + char action[1024]; + + (void) vsnprintf(action, sizeof (action), fmt, ap); + + if (hdl->lpc_desc_active) + hdl->lpc_desc_active = B_FALSE; + else + hdl->lpc_desc[0] = '\0'; + + if (hdl->lpc_printerr) { + if (hdl->lpc_desc[0] != '\0') + error = hdl->lpc_desc; + + (void) fprintf(stderr, "%s: %s\n", action, error); + } +} + +/*PRINTFLIKE3*/ +static int +zutil_error_fmt(libpc_handle_t *hdl, const char *error, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + zutil_verror(hdl, error, fmt, ap); + + va_end(ap); + + return (-1); +} + +static int +zutil_error(libpc_handle_t *hdl, const char *error, const char *msg) +{ + return (zutil_error_fmt(hdl, error, "%s", msg)); +} + +static int +zutil_no_memory(libpc_handle_t *hdl) +{ + (void) zutil_error(hdl, EZFS_NOMEM, "internal error"); + exit(1); +} + +void * +zutil_alloc(libpc_handle_t *hdl, size_t size) +{ + void *data; + + if ((data = calloc(1, size)) == NULL) + (void) zutil_no_memory(hdl); + + return (data); +} + +char * +zutil_strdup(libpc_handle_t *hdl, const char *str) +{ + char *ret; + + if ((ret = strdup(str)) == NULL) + (void) zutil_no_memory(hdl); + + return (ret); +} + +/* + * Intermediate structures used to gather configuration information. + */ +typedef struct config_entry { + uint64_t ce_txg; + nvlist_t *ce_config; + struct config_entry *ce_next; +} config_entry_t; + +typedef struct vdev_entry { + uint64_t ve_guid; + config_entry_t *ve_configs; + struct vdev_entry *ve_next; +} vdev_entry_t; + +typedef struct pool_entry { + uint64_t pe_guid; + vdev_entry_t *pe_vdevs; + struct pool_entry *pe_next; +} pool_entry_t; + +typedef struct name_entry { + char *ne_name; + uint64_t ne_guid; + struct name_entry *ne_next; +} name_entry_t; + +typedef struct pool_list { + pool_entry_t *pools; + name_entry_t *names; +} pool_list_t; + +/* + * Go through and fix up any path and/or devid information for the given vdev + * configuration. + */ +static int +fix_paths(nvlist_t *nv, name_entry_t *names) +{ + nvlist_t **child; + uint_t c, children; + uint64_t guid; + name_entry_t *ne, *best; + char *path, *devid; + int matched; + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0) { + for (c = 0; c < children; c++) + if (fix_paths(child[c], names) != 0) + return (-1); + return (0); + } + + /* + * This is a leaf (file or disk) vdev. In either case, go through + * the name list and see if we find a matching guid. If so, replace + * the path and see if we can calculate a new devid. + * + * There may be multiple names associated with a particular guid, in + * which case we have overlapping slices or multiple paths to the same + * disk. If this is the case, then we want to pick the path that is + * the most similar to the original, where "most similar" is the number + * of matching characters starting from the end of the path. This will + * preserve slice numbers even if the disks have been reorganized, and + * will also catch preferred disk names if multiple paths exist. + */ + verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0); + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) + path = NULL; + + matched = 0; + best = NULL; + for (ne = names; ne != NULL; ne = ne->ne_next) { + if (ne->ne_guid == guid) { + const char *src, *dst; + int count; + + if (path == NULL) { + best = ne; + break; + } + + src = ne->ne_name + strlen(ne->ne_name) - 1; + dst = path + strlen(path) - 1; + for (count = 0; src >= ne->ne_name && dst >= path; + src--, dst--, count++) + if (*src != *dst) + break; + + /* + * At this point, 'count' is the number of characters + * matched from the end. + */ + if (count > matched || best == NULL) { + best = ne; + matched = count; + } + } + } + + if (best == NULL) + return (0); + + if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0) + return (-1); + + if ((devid = devid_str_from_path(best->ne_name)) == NULL) { + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); + } else { + if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) { + devid_str_free(devid); + return (-1); + } + devid_str_free(devid); + } + + return (0); +} + +/* + * Add the given configuration to the list of known devices. + */ +static int +add_config(libpc_handle_t *hdl, pool_list_t *pl, const char *path, + int order, int num_labels, nvlist_t *config) +{ + uint64_t pool_guid, vdev_guid, top_guid, txg, state; + pool_entry_t *pe; + vdev_entry_t *ve; + config_entry_t *ce; + name_entry_t *ne; + + /* + * If this is a hot spare not currently in use or level 2 cache + * device, add it to the list of names to translate, but don't do + * anything else. + */ + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, + &state) == 0 && + (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) && + nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) { + if ((ne = zutil_alloc(hdl, sizeof (name_entry_t))) == NULL) + return (-1); + + if ((ne->ne_name = zutil_strdup(hdl, path)) == NULL) { + free(ne); + return (-1); + } + + ne->ne_guid = vdev_guid; + ne->ne_next = pl->names; + pl->names = ne; + + return (0); + } + + /* + * If we have a valid config but cannot read any of these fields, then + * it means we have a half-initialized label. In vdev_label_init() + * we write a label with txg == 0 so that we can identify the device + * in case the user refers to the same disk later on. If we fail to + * create the pool, we'll be left with a label in this state + * which should not be considered part of a valid pool. + */ + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &pool_guid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, + &vdev_guid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID, + &top_guid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0) { + return (0); + } + + /* + * First, see if we know about this pool. If not, then add it to the + * list of known pools. + */ + for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { + if (pe->pe_guid == pool_guid) + break; + } + + if (pe == NULL) { + if ((pe = zutil_alloc(hdl, sizeof (pool_entry_t))) == NULL) { + return (-1); + } + pe->pe_guid = pool_guid; + pe->pe_next = pl->pools; + pl->pools = pe; + } + + /* + * Second, see if we know about this toplevel vdev. Add it if its + * missing. + */ + for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { + if (ve->ve_guid == top_guid) + break; + } + + if (ve == NULL) { + if ((ve = zutil_alloc(hdl, sizeof (vdev_entry_t))) == NULL) { + return (-1); + } + ve->ve_guid = top_guid; + ve->ve_next = pe->pe_vdevs; + pe->pe_vdevs = ve; + } + + /* + * Third, see if we have a config with a matching transaction group. If + * so, then we do nothing. Otherwise, add it to the list of known + * configs. + */ + for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) { + if (ce->ce_txg == txg) + break; + } + + if (ce == NULL) { + if ((ce = zutil_alloc(hdl, sizeof (config_entry_t))) == NULL) { + return (-1); + } + ce->ce_txg = txg; + ce->ce_config = fnvlist_dup(config); + ce->ce_next = ve->ve_configs; + ve->ve_configs = ce; + } + + /* + * At this point we've successfully added our config to the list of + * known configs. The last thing to do is add the vdev guid -> path + * mappings so that we can fix up the configuration as necessary before + * doing the import. + */ + if ((ne = zutil_alloc(hdl, sizeof (name_entry_t))) == NULL) + return (-1); + + if ((ne->ne_name = zutil_strdup(hdl, path)) == NULL) { + free(ne); + return (-1); + } + + ne->ne_guid = vdev_guid; + ne->ne_next = pl->names; + pl->names = ne; + + return (0); +} + +/* + * Returns true if the named pool matches the given GUID. + */ +static int +zutil_pool_active(libpc_handle_t *hdl, const char *name, uint64_t guid, + boolean_t *isactive) +{ + ASSERT(hdl->lpc_ops->pco_pool_active != NULL); + + int error = hdl->lpc_ops->pco_pool_active(hdl->lpc_lib_handle, name, + guid, isactive); + + return (error); +} + +static nvlist_t * +zutil_refresh_config(libpc_handle_t *hdl, nvlist_t *tryconfig) +{ + ASSERT(hdl->lpc_ops->pco_refresh_config != NULL); + + return (hdl->lpc_ops->pco_refresh_config(hdl->lpc_lib_handle, + tryconfig)); +} + +/* + * Determine if the vdev id is a hole in the namespace. + */ +static boolean_t +vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id) +{ + for (int c = 0; c < holes; c++) { + + /* Top-level is a hole */ + if (hole_array[c] == id) + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Convert our list of pools into the definitive set of configurations. We + * start by picking the best config for each toplevel vdev. Once that's done, + * we assemble the toplevel vdevs into a full config for the pool. We make a + * pass to fix up any incorrect paths, and then add it to the main list to + * return to the user. + */ +static nvlist_t * +get_configs(libpc_handle_t *hdl, pool_list_t *pl, boolean_t active_ok, + nvlist_t *policy) +{ + pool_entry_t *pe; + vdev_entry_t *ve; + config_entry_t *ce; + nvlist_t *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot; + nvlist_t **spares, **l2cache; + uint_t i, nspares, nl2cache; + boolean_t config_seen; + uint64_t best_txg; + char *name, *hostname = NULL; + uint64_t guid; + uint_t children = 0; + nvlist_t **child = NULL; + uint_t holes; + uint64_t *hole_array, max_id; + uint_t c; + boolean_t isactive; + uint64_t hostid; + nvlist_t *nvl; + boolean_t found_one = B_FALSE; + boolean_t valid_top_config = B_FALSE; + + if (nvlist_alloc(&ret, 0, 0) != 0) + goto nomem; + + for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { + uint64_t id, max_txg = 0; + + if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0) + goto nomem; + config_seen = B_FALSE; + + /* + * Iterate over all toplevel vdevs. Grab the pool configuration + * from the first one we find, and then go through the rest and + * add them as necessary to the 'vdevs' member of the config. + */ + for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { + + /* + * Determine the best configuration for this vdev by + * selecting the config with the latest transaction + * group. + */ + best_txg = 0; + for (ce = ve->ve_configs; ce != NULL; + ce = ce->ce_next) { + + if (ce->ce_txg > best_txg) { + tmp = ce->ce_config; + best_txg = ce->ce_txg; + } + } + + /* + * We rely on the fact that the max txg for the + * pool will contain the most up-to-date information + * about the valid top-levels in the vdev namespace. + */ + if (best_txg > max_txg) { + (void) nvlist_remove(config, + ZPOOL_CONFIG_VDEV_CHILDREN, + DATA_TYPE_UINT64); + (void) nvlist_remove(config, + ZPOOL_CONFIG_HOLE_ARRAY, + DATA_TYPE_UINT64_ARRAY); + + max_txg = best_txg; + hole_array = NULL; + holes = 0; + max_id = 0; + valid_top_config = B_FALSE; + + if (nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) { + verify(nvlist_add_uint64(config, + ZPOOL_CONFIG_VDEV_CHILDREN, + max_id) == 0); + valid_top_config = B_TRUE; + } + + if (nvlist_lookup_uint64_array(tmp, + ZPOOL_CONFIG_HOLE_ARRAY, &hole_array, + &holes) == 0) { + verify(nvlist_add_uint64_array(config, + ZPOOL_CONFIG_HOLE_ARRAY, + hole_array, holes) == 0); + } + } + + if (!config_seen) { + /* + * Copy the relevant pieces of data to the pool + * configuration: + * + * version + * pool guid + * name + * comment (if available) + * pool state + * hostid (if available) + * hostname (if available) + */ + uint64_t state, version; + char *comment = NULL; + + version = fnvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_VERSION); + fnvlist_add_uint64(config, + ZPOOL_CONFIG_VERSION, version); + guid = fnvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_POOL_GUID); + fnvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_GUID, guid); + name = fnvlist_lookup_string(tmp, + ZPOOL_CONFIG_POOL_NAME); + fnvlist_add_string(config, + ZPOOL_CONFIG_POOL_NAME, name); + + if (nvlist_lookup_string(tmp, + ZPOOL_CONFIG_COMMENT, &comment) == 0) + fnvlist_add_string(config, + ZPOOL_CONFIG_COMMENT, comment); + + state = fnvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_POOL_STATE); + fnvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_STATE, state); + + hostid = 0; + if (nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_HOSTID, &hostid) == 0) { + fnvlist_add_uint64(config, + ZPOOL_CONFIG_HOSTID, hostid); + hostname = fnvlist_lookup_string(tmp, + ZPOOL_CONFIG_HOSTNAME); + fnvlist_add_string(config, + ZPOOL_CONFIG_HOSTNAME, hostname); + } + + config_seen = B_TRUE; + } + + /* + * Add this top-level vdev to the child array. + */ + verify(nvlist_lookup_nvlist(tmp, + ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0); + verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID, + &id) == 0); + + if (id >= children) { + nvlist_t **newchild; + + newchild = zutil_alloc(hdl, (id + 1) * + sizeof (nvlist_t *)); + if (newchild == NULL) + goto nomem; + + for (c = 0; c < children; c++) + newchild[c] = child[c]; + + free(child); + child = newchild; + children = id + 1; + } + if (nvlist_dup(nvtop, &child[id], 0) != 0) + goto nomem; + + } + + /* + * If we have information about all the top-levels then + * clean up the nvlist which we've constructed. This + * means removing any extraneous devices that are + * beyond the valid range or adding devices to the end + * of our array which appear to be missing. + */ + if (valid_top_config) { + if (max_id < children) { + for (c = max_id; c < children; c++) + nvlist_free(child[c]); + children = max_id; + } else if (max_id > children) { + nvlist_t **newchild; + + newchild = zutil_alloc(hdl, (max_id) * + sizeof (nvlist_t *)); + if (newchild == NULL) + goto nomem; + + for (c = 0; c < children; c++) + newchild[c] = child[c]; + + free(child); + child = newchild; + children = max_id; + } + } + + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &guid) == 0); + + /* + * The vdev namespace may contain holes as a result of + * device removal. We must add them back into the vdev + * tree before we process any missing devices. + */ + if (holes > 0) { + ASSERT(valid_top_config); + + for (c = 0; c < children; c++) { + nvlist_t *holey; + + if (child[c] != NULL || + !vdev_is_hole(hole_array, holes, c)) + continue; + + if (nvlist_alloc(&holey, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; + + /* + * Holes in the namespace are treated as + * "hole" top-level vdevs and have a + * special flag set on them. + */ + if (nvlist_add_string(holey, + ZPOOL_CONFIG_TYPE, + VDEV_TYPE_HOLE) != 0 || + nvlist_add_uint64(holey, + ZPOOL_CONFIG_ID, c) != 0 || + nvlist_add_uint64(holey, + ZPOOL_CONFIG_GUID, 0ULL) != 0) { + nvlist_free(holey); + goto nomem; + } + child[c] = holey; + } + } + + /* + * Look for any missing top-level vdevs. If this is the case, + * create a faked up 'missing' vdev as a placeholder. We cannot + * simply compress the child array, because the kernel performs + * certain checks to make sure the vdev IDs match their location + * in the configuration. + */ + for (c = 0; c < children; c++) { + if (child[c] == NULL) { + nvlist_t *missing; + if (nvlist_alloc(&missing, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; + if (nvlist_add_string(missing, + ZPOOL_CONFIG_TYPE, + VDEV_TYPE_MISSING) != 0 || + nvlist_add_uint64(missing, + ZPOOL_CONFIG_ID, c) != 0 || + nvlist_add_uint64(missing, + ZPOOL_CONFIG_GUID, 0ULL) != 0) { + nvlist_free(missing); + goto nomem; + } + child[c] = missing; + } + } + + /* + * Put all of this pool's top-level vdevs into a root vdev. + */ + if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0) + goto nomem; + if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, + VDEV_TYPE_ROOT) != 0 || + nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 || + nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 || + nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + child, children) != 0) { + nvlist_free(nvroot); + goto nomem; + } + + for (c = 0; c < children; c++) + nvlist_free(child[c]); + free(child); + children = 0; + child = NULL; + + /* + * Go through and fix up any paths and/or devids based on our + * known list of vdev GUID -> path mappings. + */ + if (fix_paths(nvroot, pl->names) != 0) { + nvlist_free(nvroot); + goto nomem; + } + + /* + * Add the root vdev to this pool's configuration. + */ + if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + nvroot) != 0) { + nvlist_free(nvroot); + goto nomem; + } + nvlist_free(nvroot); + + /* + * zdb uses this path to report on active pools that were + * imported or created using -R. + */ + if (active_ok) + goto add_pool; + + /* + * Determine if this pool is currently active, in which case we + * can't actually import it. + */ + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &guid) == 0); + + if (zutil_pool_active(hdl, name, guid, &isactive) != 0) + goto error; + + if (isactive) { + nvlist_free(config); + config = NULL; + continue; + } + + if (policy != NULL) { + if (nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY, + policy) != 0) + goto nomem; + } + + if ((nvl = zutil_refresh_config(hdl, config)) == NULL) { + nvlist_free(config); + config = NULL; + continue; + } + + nvlist_free(config); + config = nvl; + + /* + * Go through and update the paths for spares, now that we have + * them. + */ + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, + &spares, &nspares) == 0) { + for (i = 0; i < nspares; i++) { + if (fix_paths(spares[i], pl->names) != 0) + goto nomem; + } + } + + /* + * Update the paths for l2cache devices. + */ + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, + &l2cache, &nl2cache) == 0) { + for (i = 0; i < nl2cache; i++) { + if (fix_paths(l2cache[i], pl->names) != 0) + goto nomem; + } + } + + /* + * Restore the original information read from the actual label. + */ + (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID, + DATA_TYPE_UINT64); + (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTNAME, + DATA_TYPE_STRING); + if (hostid != 0) { + verify(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID, + hostid) == 0); + verify(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME, + hostname) == 0); + } + +add_pool: + /* + * Add this pool to the list of configs. + */ + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); + if (nvlist_add_nvlist(ret, name, config) != 0) + goto nomem; + + found_one = B_TRUE; + nvlist_free(config); + config = NULL; + } + + if (!found_one) { + nvlist_free(ret); + ret = NULL; + } + + return (ret); + +nomem: + (void) zutil_no_memory(hdl); +error: + nvlist_free(config); + nvlist_free(ret); + for (c = 0; c < children; c++) + nvlist_free(child[c]); + free(child); + + return (NULL); +} + +/* + * Return the offset of the given label. + */ +static uint64_t +label_offset(uint64_t size, int l) +{ + ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0); + return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ? + 0 : size - VDEV_LABELS * sizeof (vdev_label_t))); +} + +/* + * Given a file descriptor, read the label information and return an nvlist + * describing the configuration, if there is one. The number of valid + * labels found will be returned in num_labels when non-NULL. + */ +int +zpool_read_label(int fd, nvlist_t **config, int *num_labels) +{ + struct stat64 statbuf; + int l, count = 0; + vdev_label_t *label; + nvlist_t *expected_config = NULL; + uint64_t expected_guid = 0, size; + + *config = NULL; + + if (fstat64(fd, &statbuf) == -1) + return (-1); + size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t); + + if ((label = malloc(sizeof (vdev_label_t))) == NULL) + return (-1); + + for (l = 0; l < VDEV_LABELS; l++) { + uint64_t state, guid, txg; + + if (pread64(fd, label, sizeof (vdev_label_t), + label_offset(size, l)) != sizeof (vdev_label_t)) + continue; + + if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, + sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) + continue; + + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_GUID, + &guid) != 0 || guid == 0) { + nvlist_free(*config); + continue; + } + + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, + &state) != 0 || state > POOL_STATE_L2CACHE) { + nvlist_free(*config); + continue; + } + + if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE && + (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0)) { + nvlist_free(*config); + continue; + } + + if (expected_guid) { + if (expected_guid == guid) + count++; + + nvlist_free(*config); + } else { + expected_config = *config; + expected_guid = guid; + count++; + } + } + + if (num_labels != NULL) + *num_labels = count; + + free(label); + *config = expected_config; + + if (count == 0) { + errno = ENOENT; + return (-1); + } + + return (0); +} + +static int +slice_cache_compare(const void *arg1, const void *arg2) +{ + const char *nm1 = ((rdsk_node_t *)arg1)->rn_name; + const char *nm2 = ((rdsk_node_t *)arg2)->rn_name; + char *nm1slice, *nm2slice; + int rv; + + /* + * slices zero and two are the most likely to provide results, + * so put those first + */ + nm1slice = strstr(nm1, "s0"); + nm2slice = strstr(nm2, "s0"); + if (nm1slice && !nm2slice) { + return (-1); + } + if (!nm1slice && nm2slice) { + return (1); + } + nm1slice = strstr(nm1, "s2"); + nm2slice = strstr(nm2, "s2"); + if (nm1slice && !nm2slice) { + return (-1); + } + if (!nm1slice && nm2slice) { + return (1); + } + + rv = strcmp(nm1, nm2); + if (rv == 0) + return (0); + return (rv > 0 ? 1 : -1); +} + +static void +check_one_slice(avl_tree_t *r, char *diskname, uint_t partno, + diskaddr_t size, uint_t blksz) +{ + rdsk_node_t tmpnode; + rdsk_node_t *node; + char sname[MAXNAMELEN]; + + tmpnode.rn_name = &sname[0]; + (void) snprintf(tmpnode.rn_name, MAXNAMELEN, "%s%u", + diskname, partno); + /* + * protect against division by zero for disk labels that + * contain a bogus sector size + */ + if (blksz == 0) + blksz = DEV_BSIZE; + /* too small to contain a zpool? */ + if ((size < (SPA_MINDEVSIZE / blksz)) && + (node = avl_find(r, &tmpnode, NULL))) + node->rn_nozpool = B_TRUE; +} + +static void +nozpool_all_slices(avl_tree_t *r, const char *sname) +{ + char diskname[MAXNAMELEN]; + char *ptr; + int i; + + (void) strncpy(diskname, sname, MAXNAMELEN); + if (((ptr = strrchr(diskname, 's')) == NULL) && + ((ptr = strrchr(diskname, 'p')) == NULL)) + return; + ptr[0] = 's'; + ptr[1] = '\0'; + for (i = 0; i < NDKMAP; i++) + check_one_slice(r, diskname, i, 0, 1); + ptr[0] = 'p'; + for (i = 0; i <= FD_NUMPART; i++) + check_one_slice(r, diskname, i, 0, 1); +} + +static void +check_slices(avl_tree_t *r, int fd, const char *sname) +{ + struct extvtoc vtoc; + struct dk_gpt *gpt; + char diskname[MAXNAMELEN]; + char *ptr; + int i; + + (void) strncpy(diskname, sname, MAXNAMELEN); + if ((ptr = strrchr(diskname, 's')) == NULL || !isdigit(ptr[1])) + return; + ptr[1] = '\0'; + + if (read_extvtoc(fd, &vtoc) >= 0) { + for (i = 0; i < NDKMAP; i++) + check_one_slice(r, diskname, i, + vtoc.v_part[i].p_size, vtoc.v_sectorsz); + } else if (efi_alloc_and_read(fd, &gpt) >= 0) { + /* + * on x86 we'll still have leftover links that point + * to slices s[9-15], so use NDKMAP instead + */ + for (i = 0; i < NDKMAP; i++) + check_one_slice(r, diskname, i, + gpt->efi_parts[i].p_size, gpt->efi_lbasize); + /* nodes p[1-4] are never used with EFI labels */ + ptr[0] = 'p'; + for (i = 1; i <= FD_NUMPART; i++) + check_one_slice(r, diskname, i, 0, 1); + efi_free(gpt); + } +} + +void +zpool_open_func(void *arg) +{ + rdsk_node_t *rn = arg; + struct stat64 statbuf; + nvlist_t *config; + int error; + int num_labels = 0; + int fd; + + if (rn->rn_nozpool) + return; + if ((fd = openat64(rn->rn_dfd, rn->rn_name, O_RDONLY)) < 0) { + /* symlink to a device that's no longer there */ + if (errno == ENOENT) + nozpool_all_slices(rn->rn_avl, rn->rn_name); + return; + } + /* + * Ignore failed stats. We only want regular + * files, character devs and block devs. + */ + if (fstat64(fd, &statbuf) != 0 || + (!S_ISREG(statbuf.st_mode) && + !S_ISCHR(statbuf.st_mode) && + !S_ISBLK(statbuf.st_mode))) { + (void) close(fd); + return; + } + /* this file is too small to hold a zpool */ + if (S_ISREG(statbuf.st_mode) && + statbuf.st_size < SPA_MINDEVSIZE) { + (void) close(fd); + return; + } else if (!S_ISREG(statbuf.st_mode)) { + /* + * Try to read the disk label first so we don't have to + * open a bunch of minor nodes that can't have a zpool. + */ + check_slices(rn->rn_avl, fd, rn->rn_name); + } + + error = zpool_read_label(fd, &config, &num_labels); + if (error != 0) { + (void) close(fd); + return; + } + + if (num_labels == 0) { + (void) close(fd); + nvlist_free(config); + return; + } + + (void) close(fd); + + rn->rn_config = config; + rn->rn_num_labels = num_labels; +} + +/* + * Given a list of directories to search, find all pools stored on disk. This + * includes partial pools which are not available to import. If no args are + * given (argc is 0), then the default directory (/dev/dsk) is searched. + * poolname or guid (but not both) are provided by the caller when trying + * to import a specific pool. + */ +static nvlist_t * +zpool_find_import_impl(libpc_handle_t *hdl, importargs_t *iarg) +{ + int i, dirs = iarg->paths; + struct dirent64 *dp; + char path[MAXPATHLEN]; + char *end, **dir = iarg->path; + size_t pathleft; + nvlist_t *ret = NULL; + static char *default_dir = ZFS_DISK_ROOT; + pool_list_t pools = { 0 }; + pool_entry_t *pe, *penext; + vdev_entry_t *ve, *venext; + config_entry_t *ce, *cenext; + name_entry_t *ne, *nenext; + avl_tree_t slice_cache; + rdsk_node_t *slice; + void *cookie; + + if (dirs == 0) { + dirs = 1; + dir = &default_dir; + } + + /* + * Go through and read the label configuration information from every + * possible device, organizing the information according to pool GUID + * and toplevel GUID. + */ + for (i = 0; i < dirs; i++) { + tpool_t *t; + char rdsk[MAXPATHLEN]; + int dfd; + boolean_t config_failed = B_FALSE; + DIR *dirp; + + /* use realpath to normalize the path */ + if (realpath(dir[i], path) == 0) { + (void) zutil_error_fmt(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]); + goto error; + } + end = &path[strlen(path)]; + *end++ = '/'; + *end = 0; + pathleft = &path[sizeof (path)] - end; + + /* + * Using raw devices instead of block devices when we're + * reading the labels skips a bunch of slow operations during + * close(2) processing, so we replace /dev/dsk with /dev/rdsk. + */ + if (strcmp(path, ZFS_DISK_ROOTD) == 0) + (void) strlcpy(rdsk, ZFS_RDISK_ROOTD, sizeof (rdsk)); + else + (void) strlcpy(rdsk, path, sizeof (rdsk)); + + if ((dfd = open64(rdsk, O_RDONLY)) < 0 || + (dirp = fdopendir(dfd)) == NULL) { + if (dfd >= 0) + (void) close(dfd); + zutil_error_aux(hdl, strerror(errno)); + (void) zutil_error_fmt(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), + rdsk); + goto error; + } + + avl_create(&slice_cache, slice_cache_compare, + sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node)); + /* + * This is not MT-safe, but we have no MT consumers of libzutil + */ + while ((dp = readdir64(dirp)) != NULL) { + const char *name = dp->d_name; + if (name[0] == '.' && + (name[1] == 0 || (name[1] == '.' && name[2] == 0))) + continue; + + slice = zutil_alloc(hdl, sizeof (rdsk_node_t)); + slice->rn_name = zutil_strdup(hdl, name); + slice->rn_avl = &slice_cache; + slice->rn_dfd = dfd; + slice->rn_hdl = hdl; + slice->rn_nozpool = B_FALSE; + avl_add(&slice_cache, slice); + } + /* + * create a thread pool to do all of this in parallel; + * rn_nozpool is not protected, so this is racy in that + * multiple tasks could decide that the same slice can + * not hold a zpool, which is benign. Also choose + * double the number of processors; we hold a lot of + * locks in the kernel, so going beyond this doesn't + * buy us much. + */ + t = tpool_create(1, 2 * sysconf(_SC_NPROCESSORS_ONLN), + 0, NULL); + for (slice = avl_first(&slice_cache); slice; + (slice = avl_walk(&slice_cache, slice, + AVL_AFTER))) + (void) tpool_dispatch(t, zpool_open_func, slice); + tpool_wait(t); + tpool_destroy(t); + + cookie = NULL; + while ((slice = avl_destroy_nodes(&slice_cache, + &cookie)) != NULL) { + if (slice->rn_config != NULL && !config_failed) { + nvlist_t *config = slice->rn_config; + boolean_t matched = B_TRUE; + + if (iarg->poolname != NULL) { + char *pname; + + matched = nvlist_lookup_string(config, + ZPOOL_CONFIG_POOL_NAME, + &pname) == 0 && + strcmp(iarg->poolname, pname) == 0; + } else if (iarg->guid != 0) { + uint64_t this_guid; + + matched = nvlist_lookup_uint64(config, + ZPOOL_CONFIG_POOL_GUID, + &this_guid) == 0 && + iarg->guid == this_guid; + } + if (matched) { + /* + * use the non-raw path for the config + */ + (void) strlcpy(end, slice->rn_name, + pathleft); + (void) add_config(hdl, &pools, + path, slice->rn_order, + slice->rn_num_labels, config); + } + nvlist_free(config); + } + free(slice->rn_name); + free(slice); + } + avl_destroy(&slice_cache); + + (void) closedir(dirp); + + if (config_failed) + goto error; + } + + ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy); + +error: + for (pe = pools.pools; pe != NULL; pe = penext) { + penext = pe->pe_next; + for (ve = pe->pe_vdevs; ve != NULL; ve = venext) { + venext = ve->ve_next; + for (ce = ve->ve_configs; ce != NULL; ce = cenext) { + cenext = ce->ce_next; + nvlist_free(ce->ce_config); + free(ce); + } + free(ve); + } + free(pe); + } + + for (ne = pools.names; ne != NULL; ne = nenext) { + nenext = ne->ne_next; + free(ne->ne_name); + free(ne); + } + + return (ret); +} + +/* + * Given a cache file, return the contents as a list of importable pools. + * poolname or guid (but not both) are provided by the caller when trying + * to import a specific pool. + */ +static nvlist_t * +zpool_find_import_cached(libpc_handle_t *hdl, const char *cachefile, + const char *poolname, uint64_t guid) +{ + char *buf; + int fd; + struct stat64 statbuf; + nvlist_t *raw, *src, *dst; + nvlist_t *pools; + nvpair_t *elem; + char *name; + uint64_t this_guid; + boolean_t active; + + verify(poolname == NULL || guid == 0); + + if ((fd = open(cachefile, O_RDONLY)) < 0) { + zutil_error_aux(hdl, "%s", strerror(errno)); + (void) zutil_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "failed to open cache file")); + return (NULL); + } + + if (fstat64(fd, &statbuf) != 0) { + zutil_error_aux(hdl, "%s", strerror(errno)); + (void) close(fd); + (void) zutil_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "failed to get size of cache file")); + return (NULL); + } + + if ((buf = zutil_alloc(hdl, statbuf.st_size)) == NULL) { + (void) close(fd); + return (NULL); + } + + if (read(fd, buf, statbuf.st_size) != statbuf.st_size) { + (void) close(fd); + free(buf); + (void) zutil_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, + "failed to read cache file contents")); + return (NULL); + } + + (void) close(fd); + + if (nvlist_unpack(buf, statbuf.st_size, &raw, 0) != 0) { + free(buf); + (void) zutil_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, + "invalid or corrupt cache file contents")); + return (NULL); + } + + free(buf); + + /* + * Go through and get the current state of the pools and refresh their + * state. + */ + if (nvlist_alloc(&pools, 0, 0) != 0) { + (void) zutil_no_memory(hdl); + nvlist_free(raw); + return (NULL); + } + + elem = NULL; + while ((elem = nvlist_next_nvpair(raw, elem)) != NULL) { + src = fnvpair_value_nvlist(elem); + + name = fnvlist_lookup_string(src, ZPOOL_CONFIG_POOL_NAME); + if (poolname != NULL && strcmp(poolname, name) != 0) + continue; + + this_guid = fnvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID); + if (guid != 0 && guid != this_guid) + continue; + + if (zutil_pool_active(hdl, name, this_guid, &active) != 0) { + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + + if (active) + continue; + + if (nvlist_add_string(src, ZPOOL_CONFIG_CACHEFILE, + cachefile) != 0) { + (void) zutil_no_memory(hdl); + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + + if ((dst = zutil_refresh_config(hdl, src)) == NULL) { + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + + if (nvlist_add_nvlist(pools, nvpair_name(elem), dst) != 0) { + (void) zutil_no_memory(hdl); + nvlist_free(dst); + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + nvlist_free(dst); + } + + nvlist_free(raw); + return (pools); +} + +nvlist_t * +zpool_search_import(void *hdl, importargs_t *import, + const pool_config_ops_t *pco) +{ + libpc_handle_t handle = { 0 }; + nvlist_t *pools = NULL; + + handle.lpc_lib_handle = hdl; + handle.lpc_ops = pco; + handle.lpc_printerr = B_TRUE; + + verify(import->poolname == NULL || import->guid == 0); + + if (import->cachefile != NULL) + pools = zpool_find_import_cached(&handle, import->cachefile, + import->poolname, import->guid); + else + pools = zpool_find_import_impl(&handle, import); + + if ((pools == NULL || nvlist_empty(pools)) && + handle.lpc_open_access_error && geteuid() != 0) { + (void) zutil_error(&handle, EZFS_EACESS, dgettext(TEXT_DOMAIN, + "no pools found")); + } + + return (pools); +} + +static boolean_t +pool_match(nvlist_t *cfg, char *tgt) +{ + uint64_t v, guid = strtoull(tgt, NULL, 0); + char *s; + + if (guid != 0) { + if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &v) == 0) + return (v == guid); + } else { + if (nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &s) == 0) + return (strcmp(s, tgt) == 0); + } + return (B_FALSE); +} + +int +zpool_find_config(void *hdl, const char *target, nvlist_t **configp, + importargs_t *args, const pool_config_ops_t *pco) +{ + nvlist_t *pools; + nvlist_t *match = NULL; + nvlist_t *config = NULL; + char *sepp = NULL; + int count = 0; + char *targetdup = strdup(target); + + *configp = NULL; + + if ((sepp = strpbrk(targetdup, "/@")) != NULL) { + *sepp = '\0'; + } + + pools = zpool_search_import(hdl, args, pco); + + if (pools != NULL) { + nvpair_t *elem = NULL; + while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { + VERIFY0(nvpair_value_nvlist(elem, &config)); + if (pool_match(config, targetdup)) { + count++; + if (match != NULL) { + /* multiple matches found */ + continue; + } else { + match = config; + } + } + } + } + + if (count == 0) { + free(targetdup); + return (ENOENT); + } + + if (count > 1) { + free(targetdup); + return (EINVAL); + } + + *configp = match; + free(targetdup); + + return (0); +} diff --git a/usr/src/lib/libzutil/common/zutil_import.h b/usr/src/lib/libzutil/common/zutil_import.h new file mode 100644 index 0000000000..a82ca38688 --- /dev/null +++ b/usr/src/lib/libzutil/common/zutil_import.h @@ -0,0 +1,76 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright 2015 RackTop Systems. + * Copyright (c) 2016, Intel Corporation. + */ +#ifndef _LIBZUTIL_ZUTIL_IMPORT_H_ +#define _LIBZUTIL_ZUTIL_IMPORT_H_ + +#define EZFS_BADCACHE "invalid or missing cache file" +#define EZFS_BADPATH "must be an absolute path" +#define EZFS_NOMEM "out of memory" +#define EZFS_EACESS "some devices require root privileges" + +#define IMPORT_ORDER_PREFERRED_1 1 +#define IMPORT_ORDER_PREFERRED_2 2 +#define IMPORT_ORDER_SCAN_OFFSET 10 +#define IMPORT_ORDER_DEFAULT 100 + +typedef struct libpc_handle { + boolean_t lpc_printerr; + boolean_t lpc_open_access_error; + boolean_t lpc_desc_active; + char lpc_desc[1024]; + const pool_config_ops_t *lpc_ops; + void *lpc_lib_handle; +} libpc_handle_t; + + +int label_paths(libpc_handle_t *hdl, nvlist_t *label, char **path, + char **devid); +int zpool_find_import_blkid(libpc_handle_t *hdl, pthread_mutex_t *lock, + avl_tree_t **slice_cache); + +void * zutil_alloc(libpc_handle_t *hdl, size_t size); +char *zutil_strdup(libpc_handle_t *hdl, const char *str); + +typedef struct rdsk_node { + char *rn_name; /* Full path to device */ + int rn_dfd; + int rn_order; /* Preferred order (low to high) */ + int rn_num_labels; /* Number of valid labels */ + uint64_t rn_vdev_guid; /* Expected vdev guid when set */ + libpc_handle_t *rn_hdl; + nvlist_t *rn_config; /* Label config */ + avl_tree_t *rn_avl; + avl_node_t rn_node; + boolean_t rn_nozpool; + pthread_mutex_t *rn_lock; + boolean_t rn_labelpaths; +} rdsk_node_t; + +void zpool_open_func(void *); + +#endif /* _LIBZUTIL_ZUTIL_IMPORT_H_ */ diff --git a/usr/src/lib/libzutil/common/zutil_nicenum.c b/usr/src/lib/libzutil/common/zutil_nicenum.c new file mode 100644 index 0000000000..e11edd7d71 --- /dev/null +++ b/usr/src/lib/libzutil/common/zutil_nicenum.c @@ -0,0 +1,172 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <ctype.h> +#include <math.h> +#include <stdio.h> +#include <libzutil.h> + +/* + * Return B_TRUE if "str" is a number string, B_FALSE otherwise. + * Works for integer and floating point numbers. + */ +boolean_t +zfs_isnumber(const char *str) +{ + for (; *str; str++) + if (!(isdigit(*str) || (*str == '.'))) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * Convert a number to an appropriately human-readable output. + */ +void +zfs_nicenum_format(uint64_t num, char *buf, size_t buflen, + enum zfs_nicenum_format format) +{ + uint64_t n = num; + int index = 0; + const char *u; + const char *units[3][7] = { + [ZFS_NICENUM_1024] = {"", "K", "M", "G", "T", "P", "E"}, + [ZFS_NICENUM_BYTES] = {"B", "K", "M", "G", "T", "P", "E"}, + [ZFS_NICENUM_TIME] = {"ns", "us", "ms", "s", "?", "?", "?"} + }; + + const int units_len[] = {[ZFS_NICENUM_1024] = 6, + [ZFS_NICENUM_BYTES] = 6, + [ZFS_NICENUM_TIME] = 4}; + + const int k_unit[] = { [ZFS_NICENUM_1024] = 1024, + [ZFS_NICENUM_BYTES] = 1024, + [ZFS_NICENUM_TIME] = 1000}; + + double val; + + if (format == ZFS_NICENUM_RAW) { + (void) snprintf(buf, buflen, "%llu", (u_longlong_t)num); + return; + } else if (format == ZFS_NICENUM_RAWTIME && num > 0) { + (void) snprintf(buf, buflen, "%llu", (u_longlong_t)num); + return; + } else if (format == ZFS_NICENUM_RAWTIME && num == 0) { + (void) snprintf(buf, buflen, "%s", "-"); + return; + } + + while (n >= k_unit[format] && index < units_len[format]) { + n /= k_unit[format]; + index++; + } + + u = units[format][index]; + + /* Don't print zero latencies since they're invalid */ + if ((format == ZFS_NICENUM_TIME) && (num == 0)) { + (void) snprintf(buf, buflen, "-"); + } else if ((index == 0) || ((num % + (uint64_t)powl(k_unit[format], index)) == 0)) { + /* + * If this is an even multiple of the base, always display + * without any decimal precision. + */ + (void) snprintf(buf, buflen, "%llu%s", (u_longlong_t)n, u); + + } else { + /* + * We want to choose a precision that reflects the best choice + * for fitting in 5 characters. This can get rather tricky when + * we have numbers that are very close to an order of magnitude. + * For example, when displaying 10239 (which is really 9.999K), + * we want only a single place of precision for 10.0K. We could + * develop some complex heuristics for this, but it's much + * easier just to try each combination in turn. + */ + int i; + for (i = 2; i >= 0; i--) { + val = (double)num / + (uint64_t)powl(k_unit[format], index); + + /* + * Don't print floating point values for time. Note, + * we use floor() instead of round() here, since + * round can result in undesirable results. For + * example, if "num" is in the range of + * 999500-999999, it will print out "1000us". This + * doesn't happen if we use floor(). + */ + if (format == ZFS_NICENUM_TIME) { + if (snprintf(buf, buflen, "%d%s", + (unsigned int) floor(val), u) <= 5) + break; + + } else { + if (snprintf(buf, buflen, "%.*f%s", i, + val, u) <= 5) + break; + } + } + } +} + +/* + * Convert a number to an appropriately human-readable output. + */ +void +zfs_nicenum(uint64_t num, char *buf, size_t buflen) +{ + zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_1024); +} + +/* + * Convert a time to an appropriately human-readable output. + * @num: Time in nanoseconds + */ +void +zfs_nicetime(uint64_t num, char *buf, size_t buflen) +{ + zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_TIME); +} + +/* + * Print out a raw number with correct column spacing + */ +void +zfs_niceraw(uint64_t num, char *buf, size_t buflen) +{ + zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_RAW); +} + +/* + * Convert a number of bytes to an appropriately human-readable output. + */ +void +zfs_nicebytes(uint64_t num, char *buf, size_t buflen) +{ + zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_BYTES); +} diff --git a/usr/src/lib/libzutil/common/zutil_pool.c b/usr/src/lib/libzutil/common/zutil_pool.c new file mode 100644 index 0000000000..d7074bdc0b --- /dev/null +++ b/usr/src/lib/libzutil/common/zutil_pool.c @@ -0,0 +1,165 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Joyent, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/nvpair.h> +#include <sys/fs/zfs.h> +#include <sys/sysmacros.h> +#include <unistd.h> + +#include <libzutil.h> + +static void +dump_ddt_stat(const ddt_stat_t *dds, int h) +{ + char refcnt[6]; + char blocks[6], lsize[6], psize[6], dsize[6]; + char ref_blocks[6], ref_lsize[6], ref_psize[6], ref_dsize[6]; + + if (dds == NULL || dds->dds_blocks == 0) + return; + + if (h == -1) + (void) strcpy(refcnt, "Total"); + else + zfs_nicenum(1ULL << h, refcnt, sizeof (refcnt)); + + zfs_nicenum(dds->dds_blocks, blocks, sizeof (blocks)); + zfs_nicebytes(dds->dds_lsize, lsize, sizeof (lsize)); + zfs_nicebytes(dds->dds_psize, psize, sizeof (psize)); + zfs_nicebytes(dds->dds_dsize, dsize, sizeof (dsize)); + zfs_nicenum(dds->dds_ref_blocks, ref_blocks, sizeof (ref_blocks)); + zfs_nicebytes(dds->dds_ref_lsize, ref_lsize, sizeof (ref_lsize)); + zfs_nicebytes(dds->dds_ref_psize, ref_psize, sizeof (ref_psize)); + zfs_nicebytes(dds->dds_ref_dsize, ref_dsize, sizeof (ref_dsize)); + + (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n", + refcnt, + blocks, lsize, psize, dsize, + ref_blocks, ref_lsize, ref_psize, ref_dsize); +} + +/* + * Print the DDT histogram and the column totals. + */ +void +zpool_dump_ddt(const ddt_stat_t *dds_total, const ddt_histogram_t *ddh) +{ + int h; + + (void) printf("\n"); + + (void) printf("bucket " + " allocated " + " referenced \n"); + (void) printf("______ " + "______________________________ " + "______________________________\n"); + + (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n", + "refcnt", + "blocks", "LSIZE", "PSIZE", "DSIZE", + "blocks", "LSIZE", "PSIZE", "DSIZE"); + + (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n", + "------", + "------", "-----", "-----", "-----", + "------", "-----", "-----", "-----"); + + for (h = 0; h < 64; h++) + dump_ddt_stat(&ddh->ddh_stat[h], h); + + dump_ddt_stat(dds_total, -1); + + (void) printf("\n"); +} + +/* + * Process the buffer of nvlists, unpacking and storing each nvlist record + * into 'records'. 'leftover' is set to the number of bytes that weren't + * processed as there wasn't a complete record. + */ +int +zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover, + nvlist_t ***records, uint_t *numrecords) +{ + uint64_t reclen; + nvlist_t *nv; + int i; + void *tmp; + + while (bytes_read > sizeof (reclen)) { + + /* get length of packed record (stored as little endian) */ + for (i = 0, reclen = 0; i < sizeof (reclen); i++) + reclen += (uint64_t)(((uchar_t *)buf)[i]) << (8*i); + + if (bytes_read < sizeof (reclen) + reclen) + break; + + /* unpack record */ + if (nvlist_unpack(buf + sizeof (reclen), reclen, &nv, 0) != 0) + return (ENOMEM); + bytes_read -= sizeof (reclen) + reclen; + buf += sizeof (reclen) + reclen; + + /* add record to nvlist array */ + (*numrecords)++; + if (ISP2(*numrecords + 1)) { + tmp = realloc(*records, + *numrecords * 2 * sizeof (nvlist_t *)); + if (tmp == NULL) { + nvlist_free(nv); + (*numrecords)--; + return (ENOMEM); + } + *records = tmp; + } + (*records)[*numrecords - 1] = nv; + } + + *leftover = bytes_read; + return (0); +} + +ulong_t +get_system_hostid(void) +{ + char *env; + + /* + * Allow the hostid to be subverted for testing. + */ + env = getenv("ZFS_HOSTID"); + if (env) { + ulong_t hostid = strtoull(env, NULL, 16); + return (hostid & 0xFFFFFFFF); + } + + return (gethostid()); +} diff --git a/usr/src/lib/libzutil/i386/Makefile b/usr/src/lib/libzutil/i386/Makefile new file mode 100644 index 0000000000..ada420dc55 --- /dev/null +++ b/usr/src/lib/libzutil/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libzutil/inc.flg b/usr/src/lib/libzutil/inc.flg new file mode 100644 index 0000000000..dfb8227b21 --- /dev/null +++ b/usr/src/lib/libzutil/inc.flg @@ -0,0 +1,19 @@ +#!/bin/sh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +find_files "s.*" usr/src/common/zfs +find_files "s.*" usr/src/uts/common/fs/zfs/sys +echo_file usr/src/uts/common/sys/fs/zfs.h diff --git a/usr/src/lib/libzutil/sparc/Makefile b/usr/src/lib/libzutil/sparc/Makefile new file mode 100644 index 0000000000..ada420dc55 --- /dev/null +++ b/usr/src/lib/libzutil/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libzutil/sparcv9/Makefile b/usr/src/lib/libzutil/sparcv9/Makefile new file mode 100644 index 0000000000..e2931cd005 --- /dev/null +++ b/usr/src/lib/libzutil/sparcv9/Makefile @@ -0,0 +1,21 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +sparcv9_C_PICFLAGS= $(sparcv9_C_BIGPICFLAGS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/pkg/manifests/system-file-system-zfs.mf b/usr/src/pkg/manifests/system-file-system-zfs.mf index 3fa15b19f6..69ef355916 100644 --- a/usr/src/pkg/manifests/system-file-system-zfs.mf +++ b/usr/src/pkg/manifests/system-file-system-zfs.mf @@ -24,6 +24,7 @@ # Copyright (c) 2012, 2017 by Delphix. All rights reserved. # Copyright (c) 2013 by Saso Kiselkov. All rights reserved. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. +# Copyright 2020 Joyent, Inc. # set name=pkg.fmri value=pkg:/system/file-system/zfs@$(PKGVERS) @@ -77,8 +78,10 @@ file path=kernel/drv/zfs.conf group=sys file path=kernel/kmdb/$(ARCH64)/zfs group=sys mode=0555 file path=lib/$(ARCH64)/libzfs.so.1 file path=lib/$(ARCH64)/libzfs_core.so.1 +file path=lib/$(ARCH64)/libzutil.so.1 file path=lib/libzfs.so.1 file path=lib/libzfs_core.so.1 +file path=lib/libzutil.so.1 file path=sbin/zfs mode=0555 file path=sbin/zpool mode=0555 file path=usr/lib/$(ARCH64)/libzfs_jni.so.1 @@ -122,8 +125,10 @@ link path=etc/fs/zfs/mount target=../../../sbin/zfs link path=etc/fs/zfs/umount target=../../../sbin/zfs link path=lib/$(ARCH64)/libzfs.so target=libzfs.so.1 link path=lib/$(ARCH64)/libzfs_core.so target=libzfs_core.so.1 +link path=lib/$(ARCH64)/libzutil.so target=libzutil.so.1 link path=lib/libzfs.so target=libzfs.so.1 link path=lib/libzfs_core.so target=libzfs_core.so.1 +link path=lib/libzutil.so target=libzutil.so.1 link path=usr/lib/$(ARCH64)/libzfs.so \ target=../../../lib/$(ARCH64)/libzfs.so.1 link path=usr/lib/$(ARCH64)/libzfs.so.1 \ @@ -134,6 +139,10 @@ link path=usr/lib/$(ARCH64)/libzfs_core.so.1 \ target=../../../lib/$(ARCH64)/libzfs_core.so.1 link path=usr/lib/$(ARCH64)/libzfs_jni.so target=libzfs_jni.so.1 link path=usr/lib/$(ARCH64)/libzpool.so target=libzpool.so.1 +link path=usr/lib/$(ARCH64)/libzutil.so \ + target=../../../lib/$(ARCH64)/libzutil.so.1 +link path=usr/lib/$(ARCH64)/libzutil.so.1 \ + target=../../../lib/$(ARCH64)/libzutil.so.1 link path=usr/lib/fs/zfs/mount target=../../../../sbin/zfs link path=usr/lib/fs/zfs/umount target=../../../../sbin/zfs link path=usr/lib/libzfs.so target=../../lib/libzfs.so.1 @@ -142,6 +151,8 @@ link path=usr/lib/libzfs_core.so target=../../lib/libzfs_core.so.1 link path=usr/lib/libzfs_core.so.1 target=../../lib/libzfs_core.so.1 link path=usr/lib/libzfs_jni.so target=libzfs_jni.so.1 $(i386_ONLY)link path=usr/lib/libzpool.so target=libzpool.so.1 +link path=usr/lib/libzutil.so target=../../lib/libzutil.so.1 +link path=usr/lib/libzutil.so.1 target=../../lib/libzutil.so.1 link path=usr/sbin/zfs target=../../sbin/zfs link path=usr/sbin/zpool target=../../sbin/zpool $(python3_ONLY)depend fmri=system/library/python/zfs$(PYTHON3_PKGVERS) \ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h index 2ad4e77398..9947bedf54 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -385,6 +385,7 @@ typedef struct zinject_record { #define ZINJECT_NULL 0x1 #define ZINJECT_FLUSH_ARC 0x2 #define ZINJECT_UNLOAD_SPA 0x4 +#define ZINJECT_CALC_RANGE 0x8 #define ZI_NO_DVA (-1) diff --git a/usr/src/uts/common/fs/zfs/zio_inject.c b/usr/src/uts/common/fs/zfs/zio_inject.c index f13fb18c16..a65721d175 100644 --- a/usr/src/uts/common/fs/zfs/zio_inject.c +++ b/usr/src/uts/common/fs/zfs/zio_inject.c @@ -45,9 +45,10 @@ #include <sys/zfs_ioctl.h> #include <sys/vdev_impl.h> #include <sys/dmu_objset.h> +#include <sys/dsl_dataset.h> #include <sys/fs/zfs.h> -uint32_t zio_injection_enabled; +uint32_t zio_injection_enabled = 0; /* * Data describing each zinject handler registered on the system, and @@ -623,6 +624,63 @@ zio_handle_io_delay(zio_t *zio) return (min_target); } +static int +zio_calculate_range(const char *pool, zinject_record_t *record) +{ + dsl_pool_t *dp; + dsl_dataset_t *ds; + objset_t *os = NULL; + dnode_t *dn = NULL; + int error; + + /* + * Obtain the dnode for object using pool, objset, and object + */ + error = dsl_pool_hold(pool, FTAG, &dp); + if (error) + return (error); + + error = dsl_dataset_hold_obj(dp, record->zi_objset, FTAG, &ds); + dsl_pool_rele(dp, FTAG); + if (error) + return (error); + + error = dmu_objset_from_ds(ds, &os); + dsl_dataset_rele(ds, FTAG); + if (error) + return (error); + + error = dnode_hold(os, record->zi_object, FTAG, &dn); + if (error) + return (error); + + /* + * Translate the range into block IDs + */ + if (record->zi_start != 0 || record->zi_end != -1ULL) { + record->zi_start >>= dn->dn_datablkshift; + record->zi_end >>= dn->dn_datablkshift; + } + if (record->zi_level > 0) { + if (record->zi_level >= dn->dn_nlevels) { + dnode_rele(dn, FTAG); + return (SET_ERROR(EDOM)); + } + + if (record->zi_start != 0 || record->zi_end != 0) { + int shift = dn->dn_indblkshift - SPA_BLKPTRSHIFT; + + for (int level = record->zi_level; level > 0; level--) { + record->zi_start >>= shift; + record->zi_end >>= shift; + } + } + } + + dnode_rele(dn, FTAG); + return (0); +} + /* * Create a new handler for the given record. We add it to the list, adding * a reference to the spa_t in the process. We increment zio_injection_enabled, @@ -662,6 +720,15 @@ zio_inject_fault(char *name, int flags, int *id, zinject_record_t *record) return (SET_ERROR(EINVAL)); } + /* + * If the supplied range was in bytes -- calculate the actual blkid + */ + if (flags & ZINJECT_CALC_RANGE) { + error = zio_calculate_range(name, record); + if (error != 0) + return (error); + } + if (!(flags & ZINJECT_NULL)) { /* * spa_inject_ref() will add an injection reference, which will |