Add libzutil for libzfs or libzpool consumers
Adds a libzutil for utility functions that are common to libzfs and libzpool consumers (most of what was in libzfs_import.c). This removes the need for utilities to link against both libzpool and libzfs. Reviewed-by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Don Brady <don.brady@delphix.com> Closes #8050
This commit is contained in:
parent
6644e5bb6e
commit
e89f1295d4
|
@ -31,6 +31,7 @@
|
||||||
#include <sys/mntent.h>
|
#include <sys/mntent.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
|
@ -16,5 +16,4 @@ zdb_SOURCES = \
|
||||||
|
|
||||||
zdb_LDADD = \
|
zdb_LDADD = \
|
||||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
|
||||||
$(top_builddir)/lib/libzpool/libzpool.la
|
$(top_builddir)/lib/libzpool/libzpool.la
|
||||||
|
|
|
@ -67,7 +67,9 @@
|
||||||
#include <sys/dsl_crypt.h>
|
#include <sys/dsl_crypt.h>
|
||||||
#include <sys/dsl_scan.h>
|
#include <sys/dsl_scan.h>
|
||||||
#include <zfs_comutil.h>
|
#include <zfs_comutil.h>
|
||||||
#include <libzfs.h>
|
|
||||||
|
#include <libnvpair.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
#include "zdb.h"
|
#include "zdb.h"
|
||||||
|
|
||||||
|
@ -106,7 +108,6 @@ typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
|
||||||
|
|
||||||
uint64_t *zopt_object = NULL;
|
uint64_t *zopt_object = NULL;
|
||||||
static unsigned zopt_objects = 0;
|
static unsigned zopt_objects = 0;
|
||||||
libzfs_handle_t *g_zfs;
|
|
||||||
uint64_t max_inflight = 1000;
|
uint64_t max_inflight = 1000;
|
||||||
static int leaked_objects = 0;
|
static int leaked_objects = 0;
|
||||||
static range_tree_t *mos_refd_objs;
|
static range_tree_t *mos_refd_objs;
|
||||||
|
@ -5996,10 +5997,6 @@ main(int argc, char **argv)
|
||||||
spa_load_verify_dryrun = B_TRUE;
|
spa_load_verify_dryrun = B_TRUE;
|
||||||
|
|
||||||
kernel_init(FREAD);
|
kernel_init(FREAD);
|
||||||
if ((g_zfs = libzfs_init()) == NULL) {
|
|
||||||
(void) fprintf(stderr, "%s", libzfs_error_init(errno));
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump_all)
|
if (dump_all)
|
||||||
verbose = MAX(verbose, 1);
|
verbose = MAX(verbose, 1);
|
||||||
|
@ -6078,7 +6075,8 @@ main(int argc, char **argv)
|
||||||
args.path = searchdirs;
|
args.path = searchdirs;
|
||||||
args.can_be_active = B_TRUE;
|
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) {
|
if (error == 0) {
|
||||||
|
|
||||||
|
@ -6228,7 +6226,6 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
dump_debug_buffer();
|
dump_debug_buffer();
|
||||||
|
|
||||||
libzfs_fini(g_zfs);
|
|
||||||
kernel_fini();
|
kernel_fini();
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <libnvpair.h>
|
#include <libnvpair.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <libnvpair.h>
|
#include <libnvpair.h>
|
||||||
#include <libudev.h>
|
#include <libudev.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
|
@ -66,6 +66,7 @@
|
||||||
#include <libzfs_core.h>
|
#include <libzfs_core.h>
|
||||||
#include <zfs_prop.h>
|
#include <zfs_prop.h>
|
||||||
#include <zfs_deleg.h>
|
#include <zfs_deleg.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <libuutil.h>
|
#include <libuutil.h>
|
||||||
#ifdef HAVE_IDMAP
|
#ifdef HAVE_IDMAP
|
||||||
#include <aclutils.h>
|
#include <aclutils.h>
|
||||||
|
|
|
@ -11,5 +11,4 @@ zhack_SOURCES = \
|
||||||
|
|
||||||
zhack_LDADD = \
|
zhack_LDADD = \
|
||||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
|
||||||
$(top_builddir)/lib/libzpool/libzpool.la
|
$(top_builddir)/lib/libzpool/libzpool.la
|
||||||
|
|
|
@ -48,12 +48,11 @@
|
||||||
#include <sys/zio_compress.h>
|
#include <sys/zio_compress.h>
|
||||||
#include <sys/zfeature.h>
|
#include <sys/zfeature.h>
|
||||||
#include <sys/dmu_tx.h>
|
#include <sys/dmu_tx.h>
|
||||||
#include <libzfs.h>
|
#include <libzutil.h>
|
||||||
|
|
||||||
extern boolean_t zfeature_checks_disable;
|
extern boolean_t zfeature_checks_disable;
|
||||||
|
|
||||||
const char cmdname[] = "zhack";
|
const char cmdname[] = "zhack";
|
||||||
libzfs_handle_t *g_zfs;
|
|
||||||
static importargs_t g_importargs;
|
static importargs_t g_importargs;
|
||||||
static char *g_pool;
|
static char *g_pool;
|
||||||
static boolean_t g_readonly;
|
static boolean_t g_readonly;
|
||||||
|
@ -128,20 +127,17 @@ zhack_import(char *target, boolean_t readonly)
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
kernel_init(readonly ? FREAD : (FREAD | FWRITE));
|
kernel_init(readonly ? FREAD : (FREAD | FWRITE));
|
||||||
g_zfs = libzfs_init();
|
|
||||||
ASSERT(g_zfs != NULL);
|
|
||||||
|
|
||||||
dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
|
dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
|
||||||
|
|
||||||
g_readonly = readonly;
|
g_readonly = readonly;
|
||||||
g_importargs.unique = B_TRUE;
|
|
||||||
g_importargs.can_be_active = readonly;
|
g_importargs.can_be_active = readonly;
|
||||||
g_pool = strdup(target);
|
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)
|
if (error)
|
||||||
fatal(NULL, FTAG, "cannot import '%s': %s", target,
|
fatal(NULL, FTAG, "cannot import '%s'", target);
|
||||||
libzfs_error_description(g_zfs));
|
|
||||||
|
|
||||||
props = NULL;
|
props = NULL;
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
|
@ -529,7 +525,6 @@ main(int argc, char **argv)
|
||||||
"changes may not be committed to disk\n");
|
"changes may not be committed to disk\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
libzfs_fini(g_zfs);
|
|
||||||
kernel_fini();
|
kernel_fini();
|
||||||
|
|
||||||
return (rv);
|
return (rv);
|
||||||
|
|
|
@ -13,5 +13,4 @@ zinject_SOURCES = \
|
||||||
|
|
||||||
zinject_LDADD = \
|
zinject_LDADD = \
|
||||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
$(top_builddir)/lib/libzfs/libzfs.la
|
||||||
$(top_builddir)/lib/libzpool/libzpool.la
|
|
||||||
|
|
|
@ -25,8 +25,6 @@
|
||||||
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
@ -49,9 +47,6 @@
|
||||||
|
|
||||||
#include "zinject.h"
|
#include "zinject.h"
|
||||||
|
|
||||||
extern void kernel_init(int);
|
|
||||||
extern void kernel_fini(void);
|
|
||||||
|
|
||||||
static int debug;
|
static int debug;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -161,51 +156,32 @@ parse_pathname(const char *inpath, char *dataset, char *relpath,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert from a (dataset, path) pair into a (objset, object) pair. Note that
|
* Convert from a dataset to a objset id. Note that
|
||||||
* we grab the object number from the inode number, since looking this up via
|
* we grab the object number from the inode number.
|
||||||
* libzpool is a real pain.
|
|
||||||
*/
|
*/
|
||||||
/* ARGSUSED */
|
|
||||||
static int
|
static int
|
||||||
object_from_path(const char *dataset, const char *path, struct stat64 *statbuf,
|
object_from_path(const char *dataset, uint64_t object, zinject_record_t *record)
|
||||||
zinject_record_t *record)
|
|
||||||
{
|
{
|
||||||
objset_t *os;
|
zfs_handle_t *zhp;
|
||||||
int err;
|
|
||||||
|
|
||||||
/*
|
if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
|
||||||
* Before doing any libzpool operations, call sync() to ensure that the
|
|
||||||
* on-disk state is consistent with the in-core state.
|
|
||||||
*/
|
|
||||||
sync();
|
|
||||||
|
|
||||||
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));
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
|
|
||||||
record->zi_objset = dmu_objset_id(os);
|
record->zi_objset = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
|
||||||
record->zi_object = statbuf->st_ino;
|
record->zi_object = object;
|
||||||
|
|
||||||
dmu_objset_disown(os, B_FALSE, FTAG);
|
zfs_close(zhp);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the real range based on the type, level, and range given.
|
* Intialize the range based on the type, level, and range given.
|
||||||
*/
|
*/
|
||||||
static int
|
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)
|
zinject_record_t *record)
|
||||||
{
|
{
|
||||||
objset_t *os = NULL;
|
|
||||||
dnode_t *dn = NULL;
|
|
||||||
int err;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the numeric range from the string.
|
* Determine the numeric range from the string.
|
||||||
*/
|
*/
|
||||||
|
@ -233,7 +209,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range,
|
||||||
(void) fprintf(stderr, "invalid range '%s': must be "
|
(void) fprintf(stderr, "invalid range '%s': must be "
|
||||||
"a numeric range of the form 'start[,end]'\n",
|
"a numeric range of the form 'start[,end]'\n",
|
||||||
range);
|
range);
|
||||||
goto out;
|
return (-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +229,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range,
|
||||||
if (range != NULL) {
|
if (range != NULL) {
|
||||||
(void) fprintf(stderr, "range cannot be specified when "
|
(void) fprintf(stderr, "range cannot be specified when "
|
||||||
"type is 'dnode'\n");
|
"type is 'dnode'\n");
|
||||||
goto out;
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
record->zi_start = record->zi_object * sizeof (dnode_phys_t);
|
record->zi_start = record->zi_object * sizeof (dnode_phys_t);
|
||||||
|
@ -262,76 +238,9 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range,
|
||||||
break;
|
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;
|
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) {
|
return (0);
|
||||||
(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -343,8 +252,6 @@ translate_record(err_type_t type, const char *object, const char *range,
|
||||||
struct stat64 statbuf;
|
struct stat64 statbuf;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
kernel_init(FREAD);
|
|
||||||
|
|
||||||
debug = (getenv("ZINJECT_DEBUG") != NULL);
|
debug = (getenv("ZINJECT_DEBUG") != NULL);
|
||||||
|
|
||||||
ziprintf("translating: %s\n", object);
|
ziprintf("translating: %s\n", object);
|
||||||
|
@ -396,16 +303,16 @@ translate_record(err_type_t type, const char *object, const char *range,
|
||||||
/*
|
/*
|
||||||
* Convert (dataset, file) into (objset, object)
|
* 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;
|
goto err;
|
||||||
|
|
||||||
ziprintf("raw objset: %llu\n", record->zi_objset);
|
ziprintf("raw objset: %llu\n", record->zi_objset);
|
||||||
ziprintf("raw object: %llu\n", record->zi_object);
|
ziprintf("raw object: %llu\n", record->zi_object);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the given object, calculate the real (type, level, range)
|
* For the given object, intialize 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;
|
goto err;
|
||||||
|
|
||||||
ziprintf(" objset: %llu\n", record->zi_objset);
|
ziprintf(" objset: %llu\n", record->zi_objset);
|
||||||
|
@ -427,7 +334,6 @@ translate_record(err_type_t type, const char *object, const char *range,
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
kernel_fini();
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -561,6 +561,7 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
|
||||||
|
|
||||||
if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
|
if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
|
||||||
(void) fprintf(stderr, "failed to add handler: %s\n",
|
(void) fprintf(stderr, "failed to add handler: %s\n",
|
||||||
|
errno == EDOM ? "block level exceeds max level of object" :
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
@ -853,6 +854,7 @@ main(int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
range = optarg;
|
range = optarg;
|
||||||
|
flags |= ZINJECT_CALC_RANGE;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
dur_secs = 1;
|
dur_secs = 1;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <thread_pool.h>
|
#include <thread_pool.h>
|
||||||
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
#include "zpool_util.h"
|
#include "zpool_util.h"
|
||||||
#include "zfs_comutil.h"
|
#include "zfs_comutil.h"
|
||||||
|
@ -2533,6 +2534,40 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
|
||||||
return (ret);
|
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>
|
* zpool checkpoint <pool>
|
||||||
* checkpoint --discard <pool>
|
* checkpoint --discard <pool>
|
||||||
|
@ -2685,6 +2720,7 @@ zpool_do_import(int argc, char **argv)
|
||||||
boolean_t do_rewind = B_FALSE;
|
boolean_t do_rewind = B_FALSE;
|
||||||
boolean_t xtreme_rewind = B_FALSE;
|
boolean_t xtreme_rewind = B_FALSE;
|
||||||
boolean_t do_scan = B_FALSE;
|
boolean_t do_scan = B_FALSE;
|
||||||
|
boolean_t pool_exists = B_FALSE;
|
||||||
uint64_t pool_state, txg = -1ULL;
|
uint64_t pool_state, txg = -1ULL;
|
||||||
char *cachefile = NULL;
|
char *cachefile = NULL;
|
||||||
importargs_t idata = { 0 };
|
importargs_t idata = { 0 };
|
||||||
|
@ -2892,7 +2928,8 @@ zpool_do_import(int argc, char **argv)
|
||||||
/*
|
/*
|
||||||
* User specified a name or guid. Ensure it's unique.
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2928,9 +2965,9 @@ zpool_do_import(int argc, char **argv)
|
||||||
idata.scan = do_scan;
|
idata.scan = do_scan;
|
||||||
idata.policy = policy;
|
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)) {
|
(argc == 1 || strcmp(argv[0], argv[1]) == 0)) {
|
||||||
(void) fprintf(stderr, gettext("cannot import '%s': "
|
(void) fprintf(stderr, gettext("cannot import '%s': "
|
||||||
"a pool with that name already exists\n"),
|
"a pool with that name already exists\n"),
|
||||||
|
@ -2939,7 +2976,7 @@ zpool_do_import(int argc, char **argv)
|
||||||
"<pool | id> <newpool>' to give it a new name\n"),
|
"<pool | id> <newpool>' to give it a new name\n"),
|
||||||
"zpool import");
|
"zpool import");
|
||||||
err = 1;
|
err = 1;
|
||||||
} else if (pools == NULL && idata.exists) {
|
} else if (pools == NULL && pool_exists) {
|
||||||
(void) fprintf(stderr, gettext("cannot import '%s': "
|
(void) fprintf(stderr, gettext("cannot import '%s': "
|
||||||
"a pool with that name is already created/imported,\n"),
|
"a pool with that name is already created/imported,\n"),
|
||||||
argv[0]);
|
argv[0]);
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#include <libnvpair.h>
|
#include <libnvpair.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/spa.h>
|
#include <sys/spa.h>
|
||||||
#include <scsi/scsi.h>
|
#include <scsi/scsi.h>
|
||||||
|
|
|
@ -20,7 +20,6 @@ ztest_SOURCES = \
|
||||||
|
|
||||||
ztest_LDADD = \
|
ztest_LDADD = \
|
||||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
|
||||||
$(top_builddir)/lib/libzpool/libzpool.la
|
$(top_builddir)/lib/libzpool/libzpool.la
|
||||||
|
|
||||||
ztest_LDADD += -lm
|
ztest_LDADD += -lm
|
||||||
|
|
|
@ -128,7 +128,7 @@
|
||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
#include <zfs_fletcher.h>
|
#include <zfs_fletcher.h>
|
||||||
#include <libnvpair.h>
|
#include <libnvpair.h>
|
||||||
#include <libzfs.h>
|
#include <libzutil.h>
|
||||||
#include <sys/crypto/icp.h>
|
#include <sys/crypto/icp.h>
|
||||||
#ifdef __GLIBC__
|
#ifdef __GLIBC__
|
||||||
#include <execinfo.h> /* for backtrace() */
|
#include <execinfo.h> /* for backtrace() */
|
||||||
|
@ -7065,7 +7065,6 @@ make_random_props(void)
|
||||||
static void
|
static void
|
||||||
ztest_import(ztest_shared_t *zs)
|
ztest_import(ztest_shared_t *zs)
|
||||||
{
|
{
|
||||||
libzfs_handle_t *hdl;
|
|
||||||
importargs_t args = { 0 };
|
importargs_t args = { 0 };
|
||||||
spa_t *spa;
|
spa_t *spa;
|
||||||
nvlist_t *cfg = NULL;
|
nvlist_t *cfg = NULL;
|
||||||
|
@ -7080,14 +7079,14 @@ ztest_import(ztest_shared_t *zs)
|
||||||
VERIFY0(pthread_rwlock_init(&ztest_name_lock, NULL));
|
VERIFY0(pthread_rwlock_init(&ztest_name_lock, NULL));
|
||||||
|
|
||||||
kernel_init(FREAD | FWRITE);
|
kernel_init(FREAD | FWRITE);
|
||||||
hdl = libzfs_init();
|
|
||||||
|
|
||||||
searchdirs[0] = ztest_opts.zo_dir;
|
searchdirs[0] = ztest_opts.zo_dir;
|
||||||
args.paths = nsearch;
|
args.paths = nsearch;
|
||||||
args.path = searchdirs;
|
args.path = searchdirs;
|
||||||
args.can_be_active = B_FALSE;
|
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)
|
if (error)
|
||||||
(void) fatal(0, "No pools found\n");
|
(void) fatal(0, "No pools found\n");
|
||||||
|
|
||||||
|
@ -7097,7 +7096,6 @@ ztest_import(ztest_shared_t *zs)
|
||||||
1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift;
|
1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift;
|
||||||
spa_close(spa, FTAG);
|
spa_close(spa, FTAG);
|
||||||
|
|
||||||
libzfs_fini(hdl);
|
|
||||||
kernel_fini();
|
kernel_fini();
|
||||||
|
|
||||||
if (!ztest_opts.zo_mmp_test) {
|
if (!ztest_opts.zo_mmp_test) {
|
||||||
|
|
|
@ -93,6 +93,7 @@ AC_CONFIG_FILES([
|
||||||
lib/libefi/Makefile
|
lib/libefi/Makefile
|
||||||
lib/libicp/Makefile
|
lib/libicp/Makefile
|
||||||
lib/libnvpair/Makefile
|
lib/libnvpair/Makefile
|
||||||
|
lib/libzutil/Makefile
|
||||||
lib/libtpool/Makefile
|
lib/libtpool/Makefile
|
||||||
lib/libunicode/Makefile
|
lib/libunicode/Makefile
|
||||||
lib/libuutil/Makefile
|
lib/libuutil/Makefile
|
||||||
|
|
|
@ -16,6 +16,7 @@ USER_H = \
|
||||||
$(top_srcdir)/include/libzfs.h \
|
$(top_srcdir)/include/libzfs.h \
|
||||||
$(top_srcdir)/include/libzfs_core.h \
|
$(top_srcdir)/include/libzfs_core.h \
|
||||||
$(top_srcdir)/include/libzfs_impl.h \
|
$(top_srcdir)/include/libzfs_impl.h \
|
||||||
|
$(top_srcdir)/include/libzutil.h \
|
||||||
$(top_srcdir)/include/thread_pool.h
|
$(top_srcdir)/include/thread_pool.h
|
||||||
|
|
||||||
EXTRA_DIST = $(COMMON_H) $(USER_H)
|
EXTRA_DIST = $(COMMON_H) $(USER_H)
|
||||||
|
|
102
include/libzfs.h
102
include/libzfs.h
|
@ -54,25 +54,6 @@ extern "C" {
|
||||||
#define ZFS_MAXPROPLEN MAXPATHLEN
|
#define ZFS_MAXPROPLEN MAXPATHLEN
|
||||||
#define ZPOOL_MAXPROPLEN MAXPATHLEN
|
#define ZPOOL_MAXPROPLEN MAXPATHLEN
|
||||||
|
|
||||||
/*
|
|
||||||
* Default device paths
|
|
||||||
*/
|
|
||||||
#define DISK_ROOT "/dev"
|
|
||||||
#define UDISK_ROOT "/dev/disk"
|
|
||||||
#define ZVOL_ROOT "/dev/zvol"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Default wait time for a device name to be created.
|
|
||||||
*/
|
|
||||||
#define DISK_LABEL_WAIT (30 * 1000) /* 30 seconds */
|
|
||||||
|
|
||||||
#define IMPORT_ORDER_PREFERRED_1 1
|
|
||||||
#define IMPORT_ORDER_PREFERRED_2 2
|
|
||||||
#define IMPORT_ORDER_SCAN_OFFSET 10
|
|
||||||
#define IMPORT_ORDER_DEFAULT 100
|
|
||||||
#define DEFAULT_IMPORT_PATH_SIZE 9
|
|
||||||
extern char *zpool_default_import_path[DEFAULT_IMPORT_PATH_SIZE];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* libzfs errors
|
* libzfs errors
|
||||||
*/
|
*/
|
||||||
|
@ -298,15 +279,9 @@ extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
|
||||||
boolean_t *, boolean_t *);
|
boolean_t *, boolean_t *);
|
||||||
extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
|
extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
|
||||||
boolean_t *, boolean_t *, boolean_t *);
|
boolean_t *, boolean_t *, boolean_t *);
|
||||||
extern int zpool_label_disk_wait(char *, int);
|
|
||||||
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
|
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
|
||||||
extern uint64_t zpool_vdev_path_to_guid(zpool_handle_t *zhp, const char *path);
|
extern uint64_t zpool_vdev_path_to_guid(zpool_handle_t *zhp, const char *path);
|
||||||
|
|
||||||
int zfs_dev_is_dm(char *dev_name);
|
|
||||||
int zfs_dev_is_whole_disk(char *dev_name);
|
|
||||||
char *zfs_get_underlying_path(char *dev_name);
|
|
||||||
char *zfs_get_enclosure_sysfs_path(char *dev_name);
|
|
||||||
|
|
||||||
const char *zpool_get_state_str(zpool_handle_t *);
|
const char *zpool_get_state_str(zpool_handle_t *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -386,7 +361,6 @@ extern zpool_status_t zpool_get_status(zpool_handle_t *, char **,
|
||||||
zpool_errata_t *);
|
zpool_errata_t *);
|
||||||
extern zpool_status_t zpool_import_status(nvlist_t *, char **,
|
extern zpool_status_t zpool_import_status(nvlist_t *, char **,
|
||||||
zpool_errata_t *);
|
zpool_errata_t *);
|
||||||
extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Statistics and configuration functions.
|
* Statistics and configuration functions.
|
||||||
|
@ -407,32 +381,6 @@ extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
|
||||||
nvlist_t *, int);
|
nvlist_t *, int);
|
||||||
extern void zpool_print_unsup_feat(nvlist_t *config);
|
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 */
|
|
||||||
int scan : 1; /* prefer scanning to libblkid cache */
|
|
||||||
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
|
* Miscellaneous pool functions
|
||||||
*/
|
*/
|
||||||
|
@ -451,8 +399,6 @@ extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
|
||||||
int name_flags);
|
int name_flags);
|
||||||
extern int zpool_upgrade(zpool_handle_t *, uint64_t);
|
extern int zpool_upgrade(zpool_handle_t *, uint64_t);
|
||||||
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
|
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
|
||||||
extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
|
|
||||||
nvlist_t ***, uint_t *);
|
|
||||||
extern int zpool_events_next(libzfs_handle_t *, nvlist_t **, int *, unsigned,
|
extern int zpool_events_next(libzfs_handle_t *, nvlist_t **, int *, unsigned,
|
||||||
int);
|
int);
|
||||||
extern int zpool_events_clear(libzfs_handle_t *, int *);
|
extern int zpool_events_clear(libzfs_handle_t *, int *);
|
||||||
|
@ -780,10 +726,6 @@ extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
|
||||||
zfs_type_t);
|
zfs_type_t);
|
||||||
extern int zfs_spa_version(zfs_handle_t *, int *);
|
extern int zfs_spa_version(zfs_handle_t *, int *);
|
||||||
extern boolean_t zfs_bookmark_exists(const char *path);
|
extern boolean_t zfs_bookmark_exists(const char *path);
|
||||||
extern int zfs_append_partition(char *path, size_t max_len);
|
|
||||||
extern int zfs_resolve_shortname(const char *name, char *path, size_t pathlen);
|
|
||||||
extern int zfs_strcmp_pathname(char *name, char *cmp_name, int wholedisk);
|
|
||||||
extern int zfs_path_order(char *path, int *order);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mount support functions.
|
* Mount support functions.
|
||||||
|
@ -819,33 +761,6 @@ extern int zfs_unshareall(zfs_handle_t *);
|
||||||
extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *,
|
extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *,
|
||||||
void *, void *, int, zfs_share_op_t);
|
void *, void *, int, zfs_share_op_t);
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Utility function to convert a number to a human-readable form.
|
|
||||||
*/
|
|
||||||
extern void zfs_nicenum(uint64_t, char *, size_t);
|
|
||||||
extern void zfs_nicenum_format(uint64_t num, char *buf, size_t buflen,
|
|
||||||
enum zfs_nicenum_format type);
|
|
||||||
|
|
||||||
|
|
||||||
extern void zfs_nicetime(uint64_t, char *, size_t);
|
|
||||||
extern void zfs_nicebytes(uint64_t, char *, size_t);
|
|
||||||
extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
|
extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -874,7 +789,6 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
|
||||||
/*
|
/*
|
||||||
* Label manipulation.
|
* Label manipulation.
|
||||||
*/
|
*/
|
||||||
extern int zpool_read_label(int, nvlist_t **, int *);
|
|
||||||
extern int zpool_clear_label(int);
|
extern int zpool_clear_label(int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -893,22 +807,6 @@ int zfs_smb_acl_rename(libzfs_handle_t *, char *, char *, char *, char *);
|
||||||
extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
|
extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
|
||||||
extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
|
extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
|
||||||
|
|
||||||
/*
|
|
||||||
* Support for Linux libudev derived persistent device strings
|
|
||||||
*/
|
|
||||||
extern boolean_t is_mpath_whole_disk(const char *);
|
|
||||||
extern void update_vdev_config_dev_strs(nvlist_t *);
|
|
||||||
extern char *zfs_strip_partition(char *);
|
|
||||||
extern char *zfs_strip_partition_path(char *);
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBUDEV
|
|
||||||
struct udev_device;
|
|
||||||
|
|
||||||
extern boolean_t udev_is_mpath(struct udev_device *dev);
|
|
||||||
extern int zfs_device_get_devid(struct udev_device *, char *, size_t);
|
|
||||||
extern int zfs_device_get_physical(struct udev_device *, char *, size_t);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int zfs_remap_indirects(libzfs_handle_t *hdl, const char *);
|
extern int zfs_remap_indirects(libzfs_handle_t *hdl, const char *);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 const 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 const char * const * zpool_default_search_paths(size_t *count);
|
||||||
|
extern int zpool_read_label(int, nvlist_t **, int *);
|
||||||
|
extern int zpool_label_disk_wait(const char *, int);
|
||||||
|
|
||||||
|
struct udev_device;
|
||||||
|
|
||||||
|
extern int zfs_device_get_devid(struct udev_device *, char *, size_t);
|
||||||
|
extern int zfs_device_get_physical(struct udev_device *, char *, size_t);
|
||||||
|
extern void update_vdev_config_dev_strs(nvlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default device paths
|
||||||
|
*/
|
||||||
|
#define DISK_ROOT "/dev"
|
||||||
|
#define UDISK_ROOT "/dev/disk"
|
||||||
|
#define ZVOL_ROOT "/dev/zvol"
|
||||||
|
|
||||||
|
extern int zfs_append_partition(char *path, size_t max_len);
|
||||||
|
extern int zfs_resolve_shortname(const char *name, char *path, size_t pathlen);
|
||||||
|
|
||||||
|
extern char *zfs_strip_partition(char *);
|
||||||
|
extern char *zfs_strip_partition_path(char *);
|
||||||
|
|
||||||
|
extern int zfs_strcmp_pathname(const char *, const char *, int);
|
||||||
|
|
||||||
|
extern int zfs_dev_is_dm(const char *);
|
||||||
|
extern int zfs_dev_is_whole_disk(const char *);
|
||||||
|
extern char *zfs_get_underlying_path(const char *);
|
||||||
|
extern char *zfs_get_enclosure_sysfs_path(const char *);
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBUDEV
|
||||||
|
extern boolean_t is_mpath_whole_disk(const char *);
|
||||||
|
#else
|
||||||
|
#define is_mpath_whole_disk(path) (B_FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LIBZUTIL_H */
|
|
@ -663,7 +663,6 @@ extern void random_init(void);
|
||||||
extern void random_fini(void);
|
extern void random_fini(void);
|
||||||
|
|
||||||
struct spa;
|
struct spa;
|
||||||
extern void nicenum(uint64_t num, char *buf, size_t);
|
|
||||||
extern void show_pool_stats(struct spa *);
|
extern void show_pool_stats(struct spa *);
|
||||||
extern int set_global_var(char *arg);
|
extern int set_global_var(char *arg);
|
||||||
|
|
||||||
|
|
|
@ -372,6 +372,7 @@ typedef struct zinject_record {
|
||||||
#define ZINJECT_NULL 0x1
|
#define ZINJECT_NULL 0x1
|
||||||
#define ZINJECT_FLUSH_ARC 0x2
|
#define ZINJECT_FLUSH_ARC 0x2
|
||||||
#define ZINJECT_UNLOAD_SPA 0x4
|
#define ZINJECT_UNLOAD_SPA 0x4
|
||||||
|
#define ZINJECT_CALC_RANGE 0x8
|
||||||
|
|
||||||
#define ZEVENT_NONE 0x0
|
#define ZEVENT_NONE 0x0
|
||||||
#define ZEVENT_NONBLOCK 0x1
|
#define ZEVENT_NONBLOCK 0x1
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# NB: GNU Automake Manual, Chapter 8.3.5: Libtool Convenience Libraries
|
# NB: GNU Automake Manual, Chapter 8.3.5: Libtool Convenience Libraries
|
||||||
# These six libraries are intermediary build components.
|
# These six libraries are intermediary build components.
|
||||||
SUBDIRS = libavl libefi libicp libshare libspl libtpool libunicode
|
SUBDIRS = libavl libefi libicp libshare libspl libtpool libzutil libunicode
|
||||||
|
|
||||||
# These four libraries, which are installed as the final build product,
|
# These four libraries, which are installed as the final build product,
|
||||||
# incorporate the six convenience libraries given above.
|
# incorporate the six convenience libraries given above.
|
||||||
|
|
|
@ -55,14 +55,13 @@ nodist_libzfs_la_SOURCES = \
|
||||||
$(KERNEL_C)
|
$(KERNEL_C)
|
||||||
|
|
||||||
libzfs_la_LIBADD = \
|
libzfs_la_LIBADD = \
|
||||||
$(top_builddir)/lib/libefi/libefi.la \
|
|
||||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||||
$(top_builddir)/lib/libshare/libshare.la \
|
$(top_builddir)/lib/libshare/libshare.la \
|
||||||
$(top_builddir)/lib/libtpool/libtpool.la \
|
|
||||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la
|
$(top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||||
|
$(top_builddir)/lib/libzutil/libzutil.la
|
||||||
|
|
||||||
libzfs_la_LIBADD += -lm $(LIBBLKID) $(LIBUDEV) $(LIBSSL)
|
libzfs_la_LIBADD += -lm $(LIBSSL)
|
||||||
libzfs_la_LDFLAGS = -version-info 2:0:0
|
libzfs_la_LDFLAGS = -version-info 2:0:0
|
||||||
|
|
||||||
EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C)
|
EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C)
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
@ -61,6 +60,7 @@
|
||||||
#include <sys/zap.h>
|
#include <sys/zap.h>
|
||||||
#include <sys/dsl_crypt.h>
|
#include <sys/dsl_crypt.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
#include "zfs_namecheck.h"
|
#include "zfs_namecheck.h"
|
||||||
#include "zfs_prop.h"
|
#include "zfs_prop.h"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,6 +33,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <sys/mntent.h>
|
#include <sys/mntent.h>
|
||||||
|
|
||||||
#include "libzfs_impl.h"
|
#include "libzfs_impl.h"
|
||||||
|
|
|
@ -29,10 +29,8 @@
|
||||||
* Copyright (c) 2017, Intel Corporation.
|
* Copyright (c) 2017, Intel Corporation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <devid.h>
|
#include <devid.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -47,6 +45,7 @@
|
||||||
#include <sys/zfs_ioctl.h>
|
#include <sys/zfs_ioctl.h>
|
||||||
#include <sys/vdev_disk.h>
|
#include <sys/vdev_disk.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
#include "zfs_namecheck.h"
|
#include "zfs_namecheck.h"
|
||||||
#include "zfs_prop.h"
|
#include "zfs_prop.h"
|
||||||
|
@ -3697,80 +3696,6 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
|
||||||
}
|
}
|
||||||
#endif /* sun */
|
#endif /* sun */
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove partition suffix from a vdev path. Partition suffixes may take three
|
|
||||||
* forms: "-partX", "pX", or "X", where X is a string of digits. The second
|
|
||||||
* case only occurs when the suffix is preceded by a digit, i.e. "md0p0" The
|
|
||||||
* third case only occurs when preceded by a string matching the regular
|
|
||||||
* expression "^([hsv]|xv)d[a-z]+", i.e. a scsi, ide, virtio or xen disk.
|
|
||||||
*
|
|
||||||
* caller must free the returned string
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
zfs_strip_partition(char *path)
|
|
||||||
{
|
|
||||||
char *tmp = strdup(path);
|
|
||||||
char *part = NULL, *d = NULL;
|
|
||||||
if (!tmp)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
if ((part = strstr(tmp, "-part")) && part != tmp) {
|
|
||||||
d = part + 5;
|
|
||||||
} else if ((part = strrchr(tmp, 'p')) &&
|
|
||||||
part > tmp + 1 && isdigit(*(part-1))) {
|
|
||||||
d = part + 1;
|
|
||||||
} else if ((tmp[0] == 'h' || tmp[0] == 's' || tmp[0] == 'v') &&
|
|
||||||
tmp[1] == 'd') {
|
|
||||||
for (d = &tmp[2]; isalpha(*d); part = ++d) { }
|
|
||||||
} else if (strncmp("xvd", tmp, 3) == 0) {
|
|
||||||
for (d = &tmp[3]; isalpha(*d); part = ++d) { }
|
|
||||||
}
|
|
||||||
if (part && d && *d != '\0') {
|
|
||||||
for (; isdigit(*d); d++) { }
|
|
||||||
if (*d == '\0')
|
|
||||||
*part = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Same as zfs_strip_partition, but allows "/dev/" to be in the pathname
|
|
||||||
*
|
|
||||||
* path: /dev/sda1
|
|
||||||
* returns: /dev/sda
|
|
||||||
*
|
|
||||||
* Returned string must be freed.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
zfs_strip_partition_path(char *path)
|
|
||||||
{
|
|
||||||
char *newpath = strdup(path);
|
|
||||||
char *sd_offset;
|
|
||||||
char *new_sd;
|
|
||||||
|
|
||||||
if (!newpath)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* Point to "sda1" part of "/dev/sda1" */
|
|
||||||
sd_offset = strrchr(newpath, '/') + 1;
|
|
||||||
|
|
||||||
/* Get our new name "sda" */
|
|
||||||
new_sd = zfs_strip_partition(sd_offset);
|
|
||||||
if (!new_sd) {
|
|
||||||
free(newpath);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Paste the "sda" where "sda1" was */
|
|
||||||
strlcpy(sd_offset, new_sd, strlen(sd_offset) + 1);
|
|
||||||
|
|
||||||
/* Free temporary "sda" */
|
|
||||||
free(new_sd);
|
|
||||||
|
|
||||||
return (newpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PATH_BUF_LEN 64
|
#define PATH_BUF_LEN 64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4133,54 +4058,6 @@ get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len)
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Process the buffer of nvlists, unpacking and storing each nvlist record
|
|
||||||
* into 'records'. 'leftover' is set to the number of bytes that weren't
|
|
||||||
* processed as there wasn't a complete record.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
|
|
||||||
nvlist_t ***records, uint_t *numrecords)
|
|
||||||
{
|
|
||||||
uint64_t reclen;
|
|
||||||
nvlist_t *nv;
|
|
||||||
int i;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Retrieve the command history of a pool.
|
* Retrieve the command history of a pool.
|
||||||
*/
|
*/
|
||||||
|
@ -4669,281 +4546,3 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate and return the underlying device name for a device mapper device.
|
|
||||||
* If a device mapper device maps to multiple devices, return the first device.
|
|
||||||
*
|
|
||||||
* For example, dm_name = "/dev/dm-0" could return "/dev/sda". Symlinks to a
|
|
||||||
* DM device (like /dev/disk/by-vdev/A0) are also allowed.
|
|
||||||
*
|
|
||||||
* Returns device name, or NULL on error or no match. If dm_name is not a DM
|
|
||||||
* device then return NULL.
|
|
||||||
*
|
|
||||||
* NOTE: The returned name string must be *freed*.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
dm_get_underlying_path(char *dm_name)
|
|
||||||
{
|
|
||||||
DIR *dp = NULL;
|
|
||||||
struct dirent *ep;
|
|
||||||
char *realp;
|
|
||||||
char *tmp = NULL;
|
|
||||||
char *path = NULL;
|
|
||||||
char *dev_str;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if (dm_name == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* dm name may be a symlink (like /dev/disk/by-vdev/A0) */
|
|
||||||
realp = realpath(dm_name, NULL);
|
|
||||||
if (realp == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If they preface 'dev' with a path (like "/dev") then strip it off.
|
|
||||||
* We just want the 'dm-N' part.
|
|
||||||
*/
|
|
||||||
tmp = strrchr(realp, '/');
|
|
||||||
if (tmp != NULL)
|
|
||||||
dev_str = tmp + 1; /* +1 since we want the chr after '/' */
|
|
||||||
else
|
|
||||||
dev_str = tmp;
|
|
||||||
|
|
||||||
size = asprintf(&tmp, "/sys/block/%s/slaves/", dev_str);
|
|
||||||
if (size == -1 || !tmp)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
dp = opendir(tmp);
|
|
||||||
if (dp == NULL)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/* Return first sd* entry in /sys/block/dm-N/slaves/ */
|
|
||||||
while ((ep = readdir(dp))) {
|
|
||||||
if (ep->d_type != DT_DIR) { /* skip "." and ".." dirs */
|
|
||||||
size = asprintf(&path, "/dev/%s", ep->d_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
if (dp != NULL)
|
|
||||||
closedir(dp);
|
|
||||||
free(tmp);
|
|
||||||
free(realp);
|
|
||||||
return (path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return 1 if device is a device mapper or multipath device.
|
|
||||||
* Return 0 if not.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zfs_dev_is_dm(char *dev_name)
|
|
||||||
{
|
|
||||||
|
|
||||||
char *tmp;
|
|
||||||
tmp = dm_get_underlying_path(dev_name);
|
|
||||||
if (tmp == NULL)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
free(tmp);
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By "whole disk" we mean an entire physical disk (something we can
|
|
||||||
* label, toggle the write cache on, etc.) as opposed to the full
|
|
||||||
* capacity of a pseudo-device such as lofi or did. We act as if we
|
|
||||||
* are labeling the disk, which should be a pretty good test of whether
|
|
||||||
* it's a viable device or not. Returns B_TRUE if it is and B_FALSE if
|
|
||||||
* it isn't.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zfs_dev_is_whole_disk(char *dev_name)
|
|
||||||
{
|
|
||||||
struct dk_gpt *label;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if ((fd = open(dev_name, O_RDONLY | O_DIRECT)) < 0)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
|
|
||||||
(void) close(fd);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
efi_free(label);
|
|
||||||
(void) close(fd);
|
|
||||||
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Lookup the underlying device for a device name
|
|
||||||
*
|
|
||||||
* Often you'll have a symlink to a device, a partition device,
|
|
||||||
* or a multipath device, and want to look up the underlying device.
|
|
||||||
* This function returns the underlying device name. If the device
|
|
||||||
* name is already the underlying device, then just return the same
|
|
||||||
* name. If the device is a DM device with multiple underlying devices
|
|
||||||
* then return the first one.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
*
|
|
||||||
* 1. /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001 -> ../../sda
|
|
||||||
* dev_name: /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001
|
|
||||||
* returns: /dev/sda
|
|
||||||
*
|
|
||||||
* 2. /dev/mapper/mpatha (made up of /dev/sda and /dev/sdb)
|
|
||||||
* dev_name: /dev/mapper/mpatha
|
|
||||||
* returns: /dev/sda (first device)
|
|
||||||
*
|
|
||||||
* 3. /dev/sda (already the underlying device)
|
|
||||||
* dev_name: /dev/sda
|
|
||||||
* returns: /dev/sda
|
|
||||||
*
|
|
||||||
* 4. /dev/dm-3 (mapped to /dev/sda)
|
|
||||||
* dev_name: /dev/dm-3
|
|
||||||
* returns: /dev/sda
|
|
||||||
*
|
|
||||||
* 5. /dev/disk/by-id/scsi-0QEMU_drive-scsi0-0-0-0-part9 -> ../../sdb9
|
|
||||||
* dev_name: /dev/disk/by-id/scsi-0QEMU_drive-scsi0-0-0-0-part9
|
|
||||||
* returns: /dev/sdb
|
|
||||||
*
|
|
||||||
* 6. /dev/disk/by-uuid/5df030cf-3cd9-46e4-8e99-3ccb462a4e9a -> ../dev/sda2
|
|
||||||
* dev_name: /dev/disk/by-uuid/5df030cf-3cd9-46e4-8e99-3ccb462a4e9a
|
|
||||||
* returns: /dev/sda
|
|
||||||
*
|
|
||||||
* Returns underlying device name, or NULL on error or no match.
|
|
||||||
*
|
|
||||||
* NOTE: The returned name string must be *freed*.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
zfs_get_underlying_path(char *dev_name)
|
|
||||||
{
|
|
||||||
char *name = NULL;
|
|
||||||
char *tmp;
|
|
||||||
|
|
||||||
if (dev_name == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
tmp = dm_get_underlying_path(dev_name);
|
|
||||||
|
|
||||||
/* dev_name not a DM device, so just un-symlinkize it */
|
|
||||||
if (tmp == NULL)
|
|
||||||
tmp = realpath(dev_name, NULL);
|
|
||||||
|
|
||||||
if (tmp != NULL) {
|
|
||||||
name = zfs_strip_partition_path(tmp);
|
|
||||||
free(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given a dev name like "sda", return the full enclosure sysfs path to
|
|
||||||
* the disk. You can also pass in the name with "/dev" prepended
|
|
||||||
* to it (like /dev/sda).
|
|
||||||
*
|
|
||||||
* For example, disk "sda" in enclosure slot 1:
|
|
||||||
* dev: "sda"
|
|
||||||
* returns: "/sys/class/enclosure/1:0:3:0/Slot 1"
|
|
||||||
*
|
|
||||||
* 'dev' must be a non-devicemapper device.
|
|
||||||
*
|
|
||||||
* Returned string must be freed.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
zfs_get_enclosure_sysfs_path(char *dev_name)
|
|
||||||
{
|
|
||||||
DIR *dp = NULL;
|
|
||||||
struct dirent *ep;
|
|
||||||
char buf[MAXPATHLEN];
|
|
||||||
char *tmp1 = NULL;
|
|
||||||
char *tmp2 = NULL;
|
|
||||||
char *tmp3 = NULL;
|
|
||||||
char *path = NULL;
|
|
||||||
size_t size;
|
|
||||||
int tmpsize;
|
|
||||||
|
|
||||||
if (dev_name == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* If they preface 'dev' with a path (like "/dev") then strip it off */
|
|
||||||
tmp1 = strrchr(dev_name, '/');
|
|
||||||
if (tmp1 != NULL)
|
|
||||||
dev_name = tmp1 + 1; /* +1 since we want the chr after '/' */
|
|
||||||
|
|
||||||
tmpsize = asprintf(&tmp1, "/sys/block/%s/device", dev_name);
|
|
||||||
if (tmpsize == -1 || tmp1 == NULL) {
|
|
||||||
tmp1 = NULL;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
dp = opendir(tmp1);
|
|
||||||
if (dp == NULL) {
|
|
||||||
tmp1 = NULL; /* To make free() at the end a NOP */
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Look though all sysfs entries in /sys/block/<dev>/device for
|
|
||||||
* the enclosure symlink.
|
|
||||||
*/
|
|
||||||
while ((ep = readdir(dp))) {
|
|
||||||
/* Ignore everything that's not our enclosure_device link */
|
|
||||||
if (strstr(ep->d_name, "enclosure_device") == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (asprintf(&tmp2, "%s/%s", tmp1, ep->d_name) == -1 ||
|
|
||||||
tmp2 == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
size = readlink(tmp2, buf, sizeof (buf));
|
|
||||||
|
|
||||||
/* Did readlink fail or crop the link name? */
|
|
||||||
if (size == -1 || size >= sizeof (buf)) {
|
|
||||||
free(tmp2);
|
|
||||||
tmp2 = NULL; /* To make free() at the end a NOP */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We got a valid link. readlink() doesn't terminate strings
|
|
||||||
* so we have to do it.
|
|
||||||
*/
|
|
||||||
buf[size] = '\0';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Our link will look like:
|
|
||||||
*
|
|
||||||
* "../../../../port-11:1:2/..STUFF../enclosure/1:0:3:0/SLOT 1"
|
|
||||||
*
|
|
||||||
* We want to grab the "enclosure/1:0:3:0/SLOT 1" part
|
|
||||||
*/
|
|
||||||
tmp3 = strstr(buf, "enclosure");
|
|
||||||
if (tmp3 == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (asprintf(&path, "/sys/class/%s", tmp3) == -1) {
|
|
||||||
/* If asprintf() fails, 'path' is undefined */
|
|
||||||
path = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path == NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
free(tmp2);
|
|
||||||
free(tmp1);
|
|
||||||
|
|
||||||
if (dp != NULL)
|
|
||||||
closedir(dp);
|
|
||||||
|
|
||||||
return (path);
|
|
||||||
}
|
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
#include <libzfs_core.h>
|
#include <libzfs_core.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
#include "zfs_namecheck.h"
|
#include "zfs_namecheck.h"
|
||||||
#include "zfs_prop.h"
|
#include "zfs_prop.h"
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <libzutil.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/systeminfo.h>
|
#include <sys/systeminfo.h>
|
||||||
|
@ -425,68 +426,3 @@ zpool_import_status(nvlist_t *config, char **msgid, zpool_errata_t *errata)
|
||||||
|
|
||||||
return (ret);
|
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_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");
|
|
||||||
}
|
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/mnttab.h>
|
#include <sys/mnttab.h>
|
||||||
|
@ -54,6 +53,7 @@
|
||||||
#include "zfs_prop.h"
|
#include "zfs_prop.h"
|
||||||
#include "zfeature_common.h"
|
#include "zfeature_common.h"
|
||||||
#include <zfs_fletcher.h>
|
#include <zfs_fletcher.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
libzfs_errno(libzfs_handle_t *hdl)
|
libzfs_errno(libzfs_handle_t *hdl)
|
||||||
|
@ -677,135 +677,6 @@ zfs_strdup(libzfs_handle_t *hdl, const char *str)
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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) {
|
|
||||||
snprintf(buf, buflen, "%llu", (u_longlong_t)num);
|
|
||||||
return;
|
|
||||||
} else if (format == ZFS_NICENUM_RAWTIME && num > 0) {
|
|
||||||
snprintf(buf, buflen, "%llu", (u_longlong_t)num);
|
|
||||||
return;
|
|
||||||
} else if (format == ZFS_NICENUM_RAWTIME && num == 0) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
|
libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
|
||||||
{
|
{
|
||||||
|
@ -1232,210 +1103,6 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
|
||||||
return (zfs_open(hdl, entry.mnt_special, ZFS_TYPE_FILESYSTEM));
|
return (zfs_open(hdl, entry.mnt_special, ZFS_TYPE_FILESYSTEM));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Append partition suffix to an otherwise fully qualified device path.
|
|
||||||
* This is used to generate the name the full path as its stored in
|
|
||||||
* ZPOOL_CONFIG_PATH for whole disk devices. On success the new length
|
|
||||||
* of 'path' will be returned on error a negative value is returned.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zfs_append_partition(char *path, size_t max_len)
|
|
||||||
{
|
|
||||||
int len = strlen(path);
|
|
||||||
|
|
||||||
if ((strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) ||
|
|
||||||
(strncmp(path, ZVOL_ROOT, strlen(ZVOL_ROOT)) == 0)) {
|
|
||||||
if (len + 6 >= max_len)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
(void) strcat(path, "-part1");
|
|
||||||
len += 6;
|
|
||||||
} else {
|
|
||||||
if (len + 2 >= max_len)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
if (isdigit(path[len-1])) {
|
|
||||||
(void) strcat(path, "p1");
|
|
||||||
len += 2;
|
|
||||||
} else {
|
|
||||||
(void) strcat(path, "1");
|
|
||||||
len += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given a shorthand device name check if a file by that name exists in any
|
|
||||||
* of the 'zpool_default_import_path' or ZPOOL_IMPORT_PATH directories. If
|
|
||||||
* one is found, store its fully qualified path in the 'path' buffer passed
|
|
||||||
* by the caller and return 0, otherwise return an error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zfs_resolve_shortname(const char *name, char *path, size_t len)
|
|
||||||
{
|
|
||||||
int i, error = -1;
|
|
||||||
char *dir, *env, *envdup;
|
|
||||||
|
|
||||||
env = getenv("ZPOOL_IMPORT_PATH");
|
|
||||||
errno = ENOENT;
|
|
||||||
|
|
||||||
if (env) {
|
|
||||||
envdup = strdup(env);
|
|
||||||
dir = strtok(envdup, ":");
|
|
||||||
while (dir && error) {
|
|
||||||
(void) snprintf(path, len, "%s/%s", dir, name);
|
|
||||||
error = access(path, F_OK);
|
|
||||||
dir = strtok(NULL, ":");
|
|
||||||
}
|
|
||||||
free(envdup);
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < DEFAULT_IMPORT_PATH_SIZE && error < 0; i++) {
|
|
||||||
(void) snprintf(path, len, "%s/%s",
|
|
||||||
zpool_default_import_path[i], name);
|
|
||||||
error = access(path, F_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (error ? ENOENT : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given a shorthand device name look for a match against 'cmp_name'. This
|
|
||||||
* is done by checking all prefix expansions using either the default
|
|
||||||
* 'zpool_default_import_paths' or the ZPOOL_IMPORT_PATH environment
|
|
||||||
* variable. Proper partition suffixes will be appended if this is a
|
|
||||||
* whole disk. When a match is found 0 is returned otherwise ENOENT.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
zfs_strcmp_shortname(char *name, char *cmp_name, int wholedisk)
|
|
||||||
{
|
|
||||||
int path_len, cmp_len, i = 0, error = ENOENT;
|
|
||||||
char *dir, *env, *envdup = NULL;
|
|
||||||
char path_name[MAXPATHLEN];
|
|
||||||
|
|
||||||
cmp_len = strlen(cmp_name);
|
|
||||||
env = getenv("ZPOOL_IMPORT_PATH");
|
|
||||||
|
|
||||||
if (env) {
|
|
||||||
envdup = strdup(env);
|
|
||||||
dir = strtok(envdup, ":");
|
|
||||||
} else {
|
|
||||||
dir = zpool_default_import_path[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (dir) {
|
|
||||||
/* Trim trailing directory slashes from ZPOOL_IMPORT_PATH */
|
|
||||||
while (dir[strlen(dir)-1] == '/')
|
|
||||||
dir[strlen(dir)-1] = '\0';
|
|
||||||
|
|
||||||
path_len = snprintf(path_name, MAXPATHLEN, "%s/%s", dir, name);
|
|
||||||
if (wholedisk)
|
|
||||||
path_len = zfs_append_partition(path_name, MAXPATHLEN);
|
|
||||||
|
|
||||||
if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0) {
|
|
||||||
error = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env) {
|
|
||||||
dir = strtok(NULL, ":");
|
|
||||||
} else if (++i < DEFAULT_IMPORT_PATH_SIZE) {
|
|
||||||
dir = zpool_default_import_path[i];
|
|
||||||
} else {
|
|
||||||
dir = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env)
|
|
||||||
free(envdup);
|
|
||||||
|
|
||||||
return (error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given either a shorthand or fully qualified path name look for a match
|
|
||||||
* against 'cmp'. The passed name will be expanded as needed for comparison
|
|
||||||
* purposes and redundant slashes stripped to ensure an accurate match.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zfs_strcmp_pathname(char *name, char *cmp, int wholedisk)
|
|
||||||
{
|
|
||||||
int path_len, cmp_len;
|
|
||||||
char path_name[MAXPATHLEN];
|
|
||||||
char cmp_name[MAXPATHLEN];
|
|
||||||
char *dir, *dup;
|
|
||||||
|
|
||||||
/* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
|
|
||||||
memset(cmp_name, 0, MAXPATHLEN);
|
|
||||||
dup = strdup(cmp);
|
|
||||||
dir = strtok(dup, "/");
|
|
||||||
while (dir) {
|
|
||||||
strlcat(cmp_name, "/", sizeof (cmp_name));
|
|
||||||
strlcat(cmp_name, dir, sizeof (cmp_name));
|
|
||||||
dir = strtok(NULL, "/");
|
|
||||||
}
|
|
||||||
free(dup);
|
|
||||||
|
|
||||||
if (name[0] != '/')
|
|
||||||
return (zfs_strcmp_shortname(name, cmp_name, wholedisk));
|
|
||||||
|
|
||||||
(void) strlcpy(path_name, name, MAXPATHLEN);
|
|
||||||
path_len = strlen(path_name);
|
|
||||||
cmp_len = strlen(cmp_name);
|
|
||||||
|
|
||||||
if (wholedisk) {
|
|
||||||
path_len = zfs_append_partition(path_name, MAXPATHLEN);
|
|
||||||
if (path_len == -1)
|
|
||||||
return (ENOMEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((path_len != cmp_len) || strcmp(path_name, cmp_name))
|
|
||||||
return (ENOENT);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given a full path to a device determine if that device appears in the
|
|
||||||
* import search path. If it does return the first match and store the
|
|
||||||
* index in the passed 'order' variable, otherwise return an error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zfs_path_order(char *name, int *order)
|
|
||||||
{
|
|
||||||
int i = 0, error = ENOENT;
|
|
||||||
char *dir, *env, *envdup;
|
|
||||||
|
|
||||||
env = getenv("ZPOOL_IMPORT_PATH");
|
|
||||||
if (env) {
|
|
||||||
envdup = strdup(env);
|
|
||||||
dir = strtok(envdup, ":");
|
|
||||||
while (dir) {
|
|
||||||
if (strncmp(name, dir, strlen(dir)) == 0) {
|
|
||||||
*order = i;
|
|
||||||
error = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dir = strtok(NULL, ":");
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
free(envdup);
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < DEFAULT_IMPORT_PATH_SIZE; i++) {
|
|
||||||
if (strncmp(name, zpool_default_import_path[i],
|
|
||||||
strlen(zpool_default_import_path[i])) == 0) {
|
|
||||||
*order = i;
|
|
||||||
error = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
|
* Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
|
||||||
* an ioctl().
|
* an ioctl().
|
||||||
|
|
|
@ -188,11 +188,10 @@ nodist_libzpool_la_SOURCES = \
|
||||||
$(LUA_C)
|
$(LUA_C)
|
||||||
|
|
||||||
libzpool_la_LIBADD = \
|
libzpool_la_LIBADD = \
|
||||||
$(top_builddir)/lib/libavl/libavl.la \
|
|
||||||
$(top_builddir)/lib/libicp/libicp.la \
|
$(top_builddir)/lib/libicp/libicp.la \
|
||||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||||
$(top_builddir)/lib/libspl/libspl.la \
|
$(top_builddir)/lib/libunicode/libunicode.la \
|
||||||
$(top_builddir)/lib/libunicode/libunicode.la
|
$(top_builddir)/lib/libzutil/libzutil.la
|
||||||
|
|
||||||
libzpool_la_LIBADD += $(ZLIB) -ldl
|
libzpool_la_LIBADD += $(ZLIB) -ldl
|
||||||
libzpool_la_LDFLAGS = -pthread -version-info 2:0:0
|
libzpool_la_LDFLAGS = -pthread -version-info 2:0:0
|
||||||
|
|
|
@ -34,112 +34,14 @@
|
||||||
#include <sys/spa.h>
|
#include <sys/spa.h>
|
||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
#include <sys/refcount.h>
|
#include <sys/refcount.h>
|
||||||
|
#include <sys/zfs_ioctl.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routines needed by more than one client of libzpool.
|
* Routines needed by more than one client of libzpool.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The largest suffix that can fit, aka an exabyte (2^60 / 10^18) */
|
|
||||||
#define INDEX_MAX (6)
|
|
||||||
|
|
||||||
/* Verify INDEX_MAX fits */
|
|
||||||
CTASSERT_GLOBAL(INDEX_MAX * 10 < sizeof (uint64_t) * 8);
|
|
||||||
|
|
||||||
void
|
|
||||||
nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen,
|
|
||||||
uint32_t flags)
|
|
||||||
{
|
|
||||||
uint64_t divamt = 1024;
|
|
||||||
uint64_t divisor = 1;
|
|
||||||
int index = 0;
|
|
||||||
int rc = 0;
|
|
||||||
char u;
|
|
||||||
|
|
||||||
if (units == 0)
|
|
||||||
units = 1;
|
|
||||||
|
|
||||||
if (n > 0) {
|
|
||||||
n *= units;
|
|
||||||
if (n < units)
|
|
||||||
goto overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & NN_DIVISOR_1000)
|
|
||||||
divamt = 1000;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This tries to find the suffix S(n) such that
|
|
||||||
* S(n) <= n < S(n+1), where S(n) = 2^(n*10) | 10^(3*n)
|
|
||||||
* (i.e. 1024/1000, 1,048,576/1,000,000, etc). Stop once S(n)
|
|
||||||
* is the largest prefix supported (i.e. don't bother computing
|
|
||||||
* and checking S(n+1). Since INDEX_MAX should be the largest
|
|
||||||
* suffix that fits (currently an exabyte), S(INDEX_MAX + 1) is
|
|
||||||
* never checked as it would overflow.
|
|
||||||
*/
|
|
||||||
while (index < INDEX_MAX) {
|
|
||||||
uint64_t newdiv = divisor * divamt;
|
|
||||||
|
|
||||||
/* CTASSERT() guarantee these never trip */
|
|
||||||
VERIFY3U(newdiv, >=, divamt);
|
|
||||||
VERIFY3U(newdiv, >=, divisor);
|
|
||||||
|
|
||||||
if (n < newdiv)
|
|
||||||
break;
|
|
||||||
|
|
||||||
divisor = newdiv;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
u = " KMGTPE"[index];
|
|
||||||
|
|
||||||
if (index == 0) {
|
|
||||||
rc = snprintf(buf, buflen, "%llu", (u_longlong_t)n);
|
|
||||||
} else if (n % divisor == 0) {
|
|
||||||
/*
|
|
||||||
* If this is an even multiple of the base, always display
|
|
||||||
* without any decimal precision.
|
|
||||||
*/
|
|
||||||
rc = snprintf(buf, buflen, "%llu%c",
|
|
||||||
(u_longlong_t)(n / divisor), 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--) {
|
|
||||||
if ((rc = snprintf(buf, buflen, "%.*f%c", i,
|
|
||||||
(double)n / divisor, u)) <= 5)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc + 1 > buflen || rc < 0)
|
|
||||||
goto overflow;
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
overflow:
|
|
||||||
/* prefer a more verbose message if possible */
|
|
||||||
if (buflen > 10)
|
|
||||||
(void) strlcpy(buf, "<overflow>", buflen);
|
|
||||||
else
|
|
||||||
(void) strlcpy(buf, "??", buflen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nicenum(uint64_t num, char *buf, size_t buflen)
|
|
||||||
{
|
|
||||||
nicenum_scale(num, 1, buf, buflen, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent)
|
show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent)
|
||||||
{
|
{
|
||||||
|
@ -300,3 +202,56 @@ set_global_var(char *arg)
|
||||||
|
|
||||||
return (0);
|
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,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
include $(top_srcdir)/config/Rules.am
|
||||||
|
|
||||||
|
# Suppress unused but set variable warnings often due to ASSERTs
|
||||||
|
AM_CFLAGS += $(NO_UNUSED_BUT_SET_VARIABLE)
|
||||||
|
|
||||||
|
DEFAULT_INCLUDES += \
|
||||||
|
-I$(top_srcdir)/include \
|
||||||
|
-I$(top_srcdir)/lib/libspl/include
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libzutil.la
|
||||||
|
|
||||||
|
USER_C = \
|
||||||
|
zutil_device_path.c \
|
||||||
|
zutil_import.c \
|
||||||
|
zutil_nicenum.c \
|
||||||
|
zutil_pool.c
|
||||||
|
|
||||||
|
nodist_libzutil_la_SOURCES = $(USER_C)
|
||||||
|
|
||||||
|
libzutil_la_LIBADD = \
|
||||||
|
$(top_builddir)/lib/libavl/libavl.la \
|
||||||
|
$(top_builddir)/lib/libefi/libefi.la \
|
||||||
|
$(top_builddir)/lib/libtpool/libtpool.la
|
||||||
|
|
||||||
|
libzutil_la_LIBADD += -lm $(LIBBLKID) $(LIBUDEV)
|
||||||
|
|
||||||
|
EXTRA_DIST = $(USER_C)
|
|
@ -0,0 +1,625 @@
|
||||||
|
/*
|
||||||
|
* 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 <errno.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/efi_partition.h>
|
||||||
|
|
||||||
|
#include <libzutil.h>
|
||||||
|
#ifdef HAVE_LIBUDEV
|
||||||
|
#include <libudev.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append partition suffix to an otherwise fully qualified device path.
|
||||||
|
* This is used to generate the name the full path as its stored in
|
||||||
|
* ZPOOL_CONFIG_PATH for whole disk devices. On success the new length
|
||||||
|
* of 'path' will be returned on error a negative value is returned.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zfs_append_partition(char *path, size_t max_len)
|
||||||
|
{
|
||||||
|
int len = strlen(path);
|
||||||
|
|
||||||
|
if ((strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) ||
|
||||||
|
(strncmp(path, ZVOL_ROOT, strlen(ZVOL_ROOT)) == 0)) {
|
||||||
|
if (len + 6 >= max_len)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
(void) strcat(path, "-part1");
|
||||||
|
len += 6;
|
||||||
|
} else {
|
||||||
|
if (len + 2 >= max_len)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if (isdigit(path[len-1])) {
|
||||||
|
(void) strcat(path, "p1");
|
||||||
|
len += 2;
|
||||||
|
} else {
|
||||||
|
(void) strcat(path, "1");
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a shorthand device name check if a file by that name exists in any
|
||||||
|
* of the 'zpool_default_import_path' or ZPOOL_IMPORT_PATH directories. If
|
||||||
|
* one is found, store its fully qualified path in the 'path' buffer passed
|
||||||
|
* by the caller and return 0, otherwise return an error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zfs_resolve_shortname(const char *name, char *path, size_t len)
|
||||||
|
{
|
||||||
|
int i, error = -1;
|
||||||
|
char *dir, *env, *envdup;
|
||||||
|
|
||||||
|
env = getenv("ZPOOL_IMPORT_PATH");
|
||||||
|
errno = ENOENT;
|
||||||
|
|
||||||
|
if (env) {
|
||||||
|
envdup = strdup(env);
|
||||||
|
dir = strtok(envdup, ":");
|
||||||
|
while (dir && error) {
|
||||||
|
(void) snprintf(path, len, "%s/%s", dir, name);
|
||||||
|
error = access(path, F_OK);
|
||||||
|
dir = strtok(NULL, ":");
|
||||||
|
}
|
||||||
|
free(envdup);
|
||||||
|
} else {
|
||||||
|
const char * const *zpool_default_import_path;
|
||||||
|
size_t count;
|
||||||
|
|
||||||
|
zpool_default_import_path = zpool_default_search_paths(&count);
|
||||||
|
|
||||||
|
for (i = 0; i < count && error < 0; i++) {
|
||||||
|
(void) snprintf(path, len, "%s/%s",
|
||||||
|
zpool_default_import_path[i], name);
|
||||||
|
error = access(path, F_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (error ? ENOENT : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a shorthand device name look for a match against 'cmp_name'. This
|
||||||
|
* is done by checking all prefix expansions using either the default
|
||||||
|
* 'zpool_default_import_paths' or the ZPOOL_IMPORT_PATH environment
|
||||||
|
* variable. Proper partition suffixes will be appended if this is a
|
||||||
|
* whole disk. When a match is found 0 is returned otherwise ENOENT.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
zfs_strcmp_shortname(const char *name, const char *cmp_name, int wholedisk)
|
||||||
|
{
|
||||||
|
int path_len, cmp_len, i = 0, error = ENOENT;
|
||||||
|
char *dir, *env, *envdup = NULL;
|
||||||
|
char path_name[MAXPATHLEN];
|
||||||
|
const char * const *zpool_default_import_path;
|
||||||
|
size_t count;
|
||||||
|
|
||||||
|
zpool_default_import_path = zpool_default_search_paths(&count);
|
||||||
|
|
||||||
|
cmp_len = strlen(cmp_name);
|
||||||
|
env = getenv("ZPOOL_IMPORT_PATH");
|
||||||
|
|
||||||
|
if (env) {
|
||||||
|
envdup = strdup(env);
|
||||||
|
dir = strtok(envdup, ":");
|
||||||
|
} else {
|
||||||
|
dir = (char *)zpool_default_import_path[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (dir) {
|
||||||
|
/* Trim trailing directory slashes from ZPOOL_IMPORT_PATH */
|
||||||
|
if (env) {
|
||||||
|
while (dir[strlen(dir)-1] == '/')
|
||||||
|
dir[strlen(dir)-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
path_len = snprintf(path_name, MAXPATHLEN, "%s/%s", dir, name);
|
||||||
|
if (wholedisk)
|
||||||
|
path_len = zfs_append_partition(path_name, MAXPATHLEN);
|
||||||
|
|
||||||
|
if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0) {
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env) {
|
||||||
|
dir = strtok(NULL, ":");
|
||||||
|
} else if (++i < count) {
|
||||||
|
dir = (char *)zpool_default_import_path[i];
|
||||||
|
} else {
|
||||||
|
dir = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env)
|
||||||
|
free(envdup);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given either a shorthand or fully qualified path name look for a match
|
||||||
|
* against 'cmp'. The passed name will be expanded as needed for comparison
|
||||||
|
* purposes and redundant slashes stripped to ensure an accurate match.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zfs_strcmp_pathname(const char *name, const char *cmp, int wholedisk)
|
||||||
|
{
|
||||||
|
int path_len, cmp_len;
|
||||||
|
char path_name[MAXPATHLEN];
|
||||||
|
char cmp_name[MAXPATHLEN];
|
||||||
|
char *dir, *dup;
|
||||||
|
|
||||||
|
/* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
|
||||||
|
memset(cmp_name, 0, MAXPATHLEN);
|
||||||
|
dup = strdup(cmp);
|
||||||
|
dir = strtok(dup, "/");
|
||||||
|
while (dir) {
|
||||||
|
strlcat(cmp_name, "/", sizeof (cmp_name));
|
||||||
|
strlcat(cmp_name, dir, sizeof (cmp_name));
|
||||||
|
dir = strtok(NULL, "/");
|
||||||
|
}
|
||||||
|
free(dup);
|
||||||
|
|
||||||
|
if (name[0] != '/')
|
||||||
|
return (zfs_strcmp_shortname(name, cmp_name, wholedisk));
|
||||||
|
|
||||||
|
(void) strlcpy(path_name, name, MAXPATHLEN);
|
||||||
|
path_len = strlen(path_name);
|
||||||
|
cmp_len = strlen(cmp_name);
|
||||||
|
|
||||||
|
if (wholedisk) {
|
||||||
|
path_len = zfs_append_partition(path_name, MAXPATHLEN);
|
||||||
|
if (path_len == -1)
|
||||||
|
return (ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((path_len != cmp_len) || strcmp(path_name, cmp_name))
|
||||||
|
return (ENOENT);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate and return the underlying device name for a device mapper device.
|
||||||
|
* If a device mapper device maps to multiple devices, return the first device.
|
||||||
|
*
|
||||||
|
* For example, dm_name = "/dev/dm-0" could return "/dev/sda". Symlinks to a
|
||||||
|
* DM device (like /dev/disk/by-vdev/A0) are also allowed.
|
||||||
|
*
|
||||||
|
* Returns device name, or NULL on error or no match. If dm_name is not a DM
|
||||||
|
* device then return NULL.
|
||||||
|
*
|
||||||
|
* NOTE: The returned name string must be *freed*.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
dm_get_underlying_path(const char *dm_name)
|
||||||
|
{
|
||||||
|
DIR *dp = NULL;
|
||||||
|
struct dirent *ep;
|
||||||
|
char *realp;
|
||||||
|
char *tmp = NULL;
|
||||||
|
char *path = NULL;
|
||||||
|
char *dev_str;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (dm_name == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/* dm name may be a symlink (like /dev/disk/by-vdev/A0) */
|
||||||
|
realp = realpath(dm_name, NULL);
|
||||||
|
if (realp == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If they preface 'dev' with a path (like "/dev") then strip it off.
|
||||||
|
* We just want the 'dm-N' part.
|
||||||
|
*/
|
||||||
|
tmp = strrchr(realp, '/');
|
||||||
|
if (tmp != NULL)
|
||||||
|
dev_str = tmp + 1; /* +1 since we want the chr after '/' */
|
||||||
|
else
|
||||||
|
dev_str = tmp;
|
||||||
|
|
||||||
|
size = asprintf(&tmp, "/sys/block/%s/slaves/", dev_str);
|
||||||
|
if (size == -1 || !tmp)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
dp = opendir(tmp);
|
||||||
|
if (dp == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Return first sd* entry in /sys/block/dm-N/slaves/ */
|
||||||
|
while ((ep = readdir(dp))) {
|
||||||
|
if (ep->d_type != DT_DIR) { /* skip "." and ".." dirs */
|
||||||
|
size = asprintf(&path, "/dev/%s", ep->d_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (dp != NULL)
|
||||||
|
closedir(dp);
|
||||||
|
free(tmp);
|
||||||
|
free(realp);
|
||||||
|
return (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 if device is a device mapper or multipath device.
|
||||||
|
* Return 0 if not.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zfs_dev_is_dm(const char *dev_name)
|
||||||
|
{
|
||||||
|
|
||||||
|
char *tmp;
|
||||||
|
tmp = dm_get_underlying_path(dev_name);
|
||||||
|
if (tmp == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
free(tmp);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By "whole disk" we mean an entire physical disk (something we can
|
||||||
|
* label, toggle the write cache on, etc.) as opposed to the full
|
||||||
|
* capacity of a pseudo-device such as lofi or did. We act as if we
|
||||||
|
* are labeling the disk, which should be a pretty good test of whether
|
||||||
|
* it's a viable device or not. Returns B_TRUE if it is and B_FALSE if
|
||||||
|
* it isn't.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zfs_dev_is_whole_disk(const char *dev_name)
|
||||||
|
{
|
||||||
|
struct dk_gpt *label;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if ((fd = open(dev_name, O_RDONLY | O_DIRECT)) < 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
|
||||||
|
(void) close(fd);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
efi_free(label);
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lookup the underlying device for a device name
|
||||||
|
*
|
||||||
|
* Often you'll have a symlink to a device, a partition device,
|
||||||
|
* or a multipath device, and want to look up the underlying device.
|
||||||
|
* This function returns the underlying device name. If the device
|
||||||
|
* name is already the underlying device, then just return the same
|
||||||
|
* name. If the device is a DM device with multiple underlying devices
|
||||||
|
* then return the first one.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* 1. /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001 -> ../../sda
|
||||||
|
* dev_name: /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001
|
||||||
|
* returns: /dev/sda
|
||||||
|
*
|
||||||
|
* 2. /dev/mapper/mpatha (made up of /dev/sda and /dev/sdb)
|
||||||
|
* dev_name: /dev/mapper/mpatha
|
||||||
|
* returns: /dev/sda (first device)
|
||||||
|
*
|
||||||
|
* 3. /dev/sda (already the underlying device)
|
||||||
|
* dev_name: /dev/sda
|
||||||
|
* returns: /dev/sda
|
||||||
|
*
|
||||||
|
* 4. /dev/dm-3 (mapped to /dev/sda)
|
||||||
|
* dev_name: /dev/dm-3
|
||||||
|
* returns: /dev/sda
|
||||||
|
*
|
||||||
|
* 5. /dev/disk/by-id/scsi-0QEMU_drive-scsi0-0-0-0-part9 -> ../../sdb9
|
||||||
|
* dev_name: /dev/disk/by-id/scsi-0QEMU_drive-scsi0-0-0-0-part9
|
||||||
|
* returns: /dev/sdb
|
||||||
|
*
|
||||||
|
* 6. /dev/disk/by-uuid/5df030cf-3cd9-46e4-8e99-3ccb462a4e9a -> ../dev/sda2
|
||||||
|
* dev_name: /dev/disk/by-uuid/5df030cf-3cd9-46e4-8e99-3ccb462a4e9a
|
||||||
|
* returns: /dev/sda
|
||||||
|
*
|
||||||
|
* Returns underlying device name, or NULL on error or no match.
|
||||||
|
*
|
||||||
|
* NOTE: The returned name string must be *freed*.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
zfs_get_underlying_path(const char *dev_name)
|
||||||
|
{
|
||||||
|
char *name = NULL;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
if (dev_name == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
tmp = dm_get_underlying_path(dev_name);
|
||||||
|
|
||||||
|
/* dev_name not a DM device, so just un-symlinkize it */
|
||||||
|
if (tmp == NULL)
|
||||||
|
tmp = realpath(dev_name, NULL);
|
||||||
|
|
||||||
|
if (tmp != NULL) {
|
||||||
|
name = zfs_strip_partition_path(tmp);
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a dev name like "sda", return the full enclosure sysfs path to
|
||||||
|
* the disk. You can also pass in the name with "/dev" prepended
|
||||||
|
* to it (like /dev/sda).
|
||||||
|
*
|
||||||
|
* For example, disk "sda" in enclosure slot 1:
|
||||||
|
* dev: "sda"
|
||||||
|
* returns: "/sys/class/enclosure/1:0:3:0/Slot 1"
|
||||||
|
*
|
||||||
|
* 'dev' must be a non-devicemapper device.
|
||||||
|
*
|
||||||
|
* Returned string must be freed.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
zfs_get_enclosure_sysfs_path(const char *dev_name)
|
||||||
|
{
|
||||||
|
DIR *dp = NULL;
|
||||||
|
struct dirent *ep;
|
||||||
|
char buf[MAXPATHLEN];
|
||||||
|
char *tmp1 = NULL;
|
||||||
|
char *tmp2 = NULL;
|
||||||
|
char *tmp3 = NULL;
|
||||||
|
char *path = NULL;
|
||||||
|
size_t size;
|
||||||
|
int tmpsize;
|
||||||
|
|
||||||
|
if (dev_name == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/* If they preface 'dev' with a path (like "/dev") then strip it off */
|
||||||
|
tmp1 = strrchr(dev_name, '/');
|
||||||
|
if (tmp1 != NULL)
|
||||||
|
dev_name = tmp1 + 1; /* +1 since we want the chr after '/' */
|
||||||
|
|
||||||
|
tmpsize = asprintf(&tmp1, "/sys/block/%s/device", dev_name);
|
||||||
|
if (tmpsize == -1 || tmp1 == NULL) {
|
||||||
|
tmp1 = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
dp = opendir(tmp1);
|
||||||
|
if (dp == NULL) {
|
||||||
|
tmp1 = NULL; /* To make free() at the end a NOP */
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look though all sysfs entries in /sys/block/<dev>/device for
|
||||||
|
* the enclosure symlink.
|
||||||
|
*/
|
||||||
|
while ((ep = readdir(dp))) {
|
||||||
|
/* Ignore everything that's not our enclosure_device link */
|
||||||
|
if (strstr(ep->d_name, "enclosure_device") == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (asprintf(&tmp2, "%s/%s", tmp1, ep->d_name) == -1 ||
|
||||||
|
tmp2 == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size = readlink(tmp2, buf, sizeof (buf));
|
||||||
|
|
||||||
|
/* Did readlink fail or crop the link name? */
|
||||||
|
if (size == -1 || size >= sizeof (buf)) {
|
||||||
|
free(tmp2);
|
||||||
|
tmp2 = NULL; /* To make free() at the end a NOP */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We got a valid link. readlink() doesn't terminate strings
|
||||||
|
* so we have to do it.
|
||||||
|
*/
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Our link will look like:
|
||||||
|
*
|
||||||
|
* "../../../../port-11:1:2/..STUFF../enclosure/1:0:3:0/SLOT 1"
|
||||||
|
*
|
||||||
|
* We want to grab the "enclosure/1:0:3:0/SLOT 1" part
|
||||||
|
*/
|
||||||
|
tmp3 = strstr(buf, "enclosure");
|
||||||
|
if (tmp3 == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (asprintf(&path, "/sys/class/%s", tmp3) == -1) {
|
||||||
|
/* If asprintf() fails, 'path' is undefined */
|
||||||
|
path = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
free(tmp2);
|
||||||
|
free(tmp1);
|
||||||
|
|
||||||
|
if (dp != NULL)
|
||||||
|
closedir(dp);
|
||||||
|
|
||||||
|
return (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove partition suffix from a vdev path. Partition suffixes may take three
|
||||||
|
* forms: "-partX", "pX", or "X", where X is a string of digits. The second
|
||||||
|
* case only occurs when the suffix is preceded by a digit, i.e. "md0p0" The
|
||||||
|
* third case only occurs when preceded by a string matching the regular
|
||||||
|
* expression "^([hsv]|xv)d[a-z]+", i.e. a scsi, ide, virtio or xen disk.
|
||||||
|
*
|
||||||
|
* caller must free the returned string
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
zfs_strip_partition(char *path)
|
||||||
|
{
|
||||||
|
char *tmp = strdup(path);
|
||||||
|
char *part = NULL, *d = NULL;
|
||||||
|
if (!tmp)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
if ((part = strstr(tmp, "-part")) && part != tmp) {
|
||||||
|
d = part + 5;
|
||||||
|
} else if ((part = strrchr(tmp, 'p')) &&
|
||||||
|
part > tmp + 1 && isdigit(*(part-1))) {
|
||||||
|
d = part + 1;
|
||||||
|
} else if ((tmp[0] == 'h' || tmp[0] == 's' || tmp[0] == 'v') &&
|
||||||
|
tmp[1] == 'd') {
|
||||||
|
for (d = &tmp[2]; isalpha(*d); part = ++d) { }
|
||||||
|
} else if (strncmp("xvd", tmp, 3) == 0) {
|
||||||
|
for (d = &tmp[3]; isalpha(*d); part = ++d) { }
|
||||||
|
}
|
||||||
|
if (part && d && *d != '\0') {
|
||||||
|
for (; isdigit(*d); d++) { }
|
||||||
|
if (*d == '\0')
|
||||||
|
*part = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as zfs_strip_partition, but allows "/dev/" to be in the pathname
|
||||||
|
*
|
||||||
|
* path: /dev/sda1
|
||||||
|
* returns: /dev/sda
|
||||||
|
*
|
||||||
|
* Returned string must be freed.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
zfs_strip_partition_path(char *path)
|
||||||
|
{
|
||||||
|
char *newpath = strdup(path);
|
||||||
|
char *sd_offset;
|
||||||
|
char *new_sd;
|
||||||
|
|
||||||
|
if (!newpath)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/* Point to "sda1" part of "/dev/sda1" */
|
||||||
|
sd_offset = strrchr(newpath, '/') + 1;
|
||||||
|
|
||||||
|
/* Get our new name "sda" */
|
||||||
|
new_sd = zfs_strip_partition(sd_offset);
|
||||||
|
if (!new_sd) {
|
||||||
|
free(newpath);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paste the "sda" where "sda1" was */
|
||||||
|
strlcpy(sd_offset, new_sd, strlen(sd_offset) + 1);
|
||||||
|
|
||||||
|
/* Free temporary "sda" */
|
||||||
|
free(new_sd);
|
||||||
|
|
||||||
|
return (newpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBUDEV
|
||||||
|
/*
|
||||||
|
* A disk is considered a multipath whole disk when:
|
||||||
|
* DEVNAME key value has "dm-"
|
||||||
|
* DM_NAME key value has "mpath" prefix
|
||||||
|
* DM_UUID key exists
|
||||||
|
* ID_PART_TABLE_TYPE key does not exist or is not gpt
|
||||||
|
*/
|
||||||
|
static boolean_t
|
||||||
|
udev_mpath_whole_disk(struct udev_device *dev)
|
||||||
|
{
|
||||||
|
const char *devname, *type, *uuid;
|
||||||
|
|
||||||
|
devname = udev_device_get_property_value(dev, "DEVNAME");
|
||||||
|
type = udev_device_get_property_value(dev, "ID_PART_TABLE_TYPE");
|
||||||
|
uuid = udev_device_get_property_value(dev, "DM_UUID");
|
||||||
|
|
||||||
|
if ((devname != NULL && strncmp(devname, "/dev/dm-", 8) == 0) &&
|
||||||
|
((type == NULL) || (strcmp(type, "gpt") != 0)) &&
|
||||||
|
(uuid != NULL)) {
|
||||||
|
return (B_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (B_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a disk is effectively a multipath whole disk
|
||||||
|
*/
|
||||||
|
boolean_t
|
||||||
|
is_mpath_whole_disk(const char *path)
|
||||||
|
{
|
||||||
|
struct udev *udev;
|
||||||
|
struct udev_device *dev = NULL;
|
||||||
|
char nodepath[MAXPATHLEN];
|
||||||
|
char *sysname;
|
||||||
|
boolean_t wholedisk = B_FALSE;
|
||||||
|
|
||||||
|
if (realpath(path, nodepath) == NULL)
|
||||||
|
return (B_FALSE);
|
||||||
|
sysname = strrchr(nodepath, '/') + 1;
|
||||||
|
if (strncmp(sysname, "dm-", 3) != 0)
|
||||||
|
return (B_FALSE);
|
||||||
|
if ((udev = udev_new()) == NULL)
|
||||||
|
return (B_FALSE);
|
||||||
|
if ((dev = udev_device_new_from_subsystem_sysname(udev, "block",
|
||||||
|
sysname)) == NULL) {
|
||||||
|
udev_device_unref(dev);
|
||||||
|
return (B_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
wholedisk = udev_mpath_whole_disk(dev);
|
||||||
|
|
||||||
|
udev_device_unref(dev);
|
||||||
|
return (wholedisk);
|
||||||
|
}
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <libzutil.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) {
|
||||||
|
snprintf(buf, buflen, "%llu", (u_longlong_t)num);
|
||||||
|
return;
|
||||||
|
} else if (format == ZFS_NICENUM_RAWTIME && num > 0) {
|
||||||
|
snprintf(buf, buflen, "%llu", (u_longlong_t)num);
|
||||||
|
return;
|
||||||
|
} else if (format == ZFS_NICENUM_RAWTIME && num == 0) {
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/nvpair.h>
|
||||||
|
#include <sys/fs/zfs.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);
|
||||||
|
}
|
|
@ -46,6 +46,7 @@
|
||||||
#include <sys/zfs_ioctl.h>
|
#include <sys/zfs_ioctl.h>
|
||||||
#include <sys/vdev_impl.h>
|
#include <sys/vdev_impl.h>
|
||||||
#include <sys/dmu_objset.h>
|
#include <sys/dmu_objset.h>
|
||||||
|
#include <sys/dsl_dataset.h>
|
||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
|
|
||||||
uint32_t zio_injection_enabled = 0;
|
uint32_t zio_injection_enabled = 0;
|
||||||
|
@ -659,6 +660,63 @@ zio_handle_io_delay(zio_t *zio)
|
||||||
return (min_target);
|
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
|
* 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,
|
* a reference to the spa_t in the process. We increment zio_injection_enabled,
|
||||||
|
@ -698,6 +756,15 @@ zio_inject_fault(char *name, int flags, int *id, zinject_record_t *record)
|
||||||
return (SET_ERROR(EINVAL));
|
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)) {
|
if (!(flags & ZINJECT_NULL)) {
|
||||||
/*
|
/*
|
||||||
* spa_inject_ref() will add an injection reference, which will
|
* spa_inject_ref() will add an injection reference, which will
|
||||||
|
|
Loading…
Reference in New Issue