mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
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:
committed by
Brian Behlendorf
parent
6644e5bb6e
commit
e89f1295d4
@@ -55,14 +55,13 @@ nodist_libzfs_la_SOURCES = \
|
||||
$(KERNEL_C)
|
||||
|
||||
libzfs_la_LIBADD = \
|
||||
$(top_builddir)/lib/libefi/libefi.la \
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libshare/libshare.la \
|
||||
$(top_builddir)/lib/libtpool/libtpool.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
|
||||
|
||||
EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C)
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <libintl.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
@@ -61,6 +60,7 @@
|
||||
#include <sys/zap.h>
|
||||
#include <sys/dsl_crypt.h>
|
||||
#include <libzfs.h>
|
||||
#include <libzutil.h>
|
||||
|
||||
#include "zfs_namecheck.h"
|
||||
#include "zfs_prop.h"
|
||||
|
||||
+17
-2242
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
||||
#include <stddef.h>
|
||||
#include <libintl.h>
|
||||
#include <libzfs.h>
|
||||
#include <libzutil.h>
|
||||
#include <sys/mntent.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
+1
-402
@@ -29,10 +29,8 @@
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <devid.h>
|
||||
#include <fcntl.h>
|
||||
#include <libintl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -47,6 +45,7 @@
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/vdev_disk.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libzutil.h>
|
||||
|
||||
#include "zfs_namecheck.h"
|
||||
#include "zfs_prop.h"
|
||||
@@ -3697,80 +3696,6 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
|
||||
}
|
||||
#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
|
||||
|
||||
/*
|
||||
@@ -4133,54 +4058,6 @@ get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len)
|
||||
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.
|
||||
*/
|
||||
@@ -4669,281 +4546,3 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
||||
|
||||
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_core.h>
|
||||
#include <libzutil.h>
|
||||
|
||||
#include "zfs_namecheck.h"
|
||||
#include "zfs_prop.h"
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
*/
|
||||
|
||||
#include <libzfs.h>
|
||||
#include <libzutil.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/systeminfo.h>
|
||||
@@ -425,68 +426,3 @@ zpool_import_status(nvlist_t *config, char **msgid, zpool_errata_t *errata)
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_ddt_stat(const ddt_stat_t *dds, int h)
|
||||
{
|
||||
char refcnt[6];
|
||||
char blocks[6], lsize[6], psize[6], dsize[6];
|
||||
char ref_blocks[6], ref_lsize[6], ref_psize[6], ref_dsize[6];
|
||||
|
||||
if (dds == NULL || dds->dds_blocks == 0)
|
||||
return;
|
||||
|
||||
if (h == -1)
|
||||
(void) strcpy(refcnt, "Total");
|
||||
else
|
||||
zfs_nicenum(1ULL << h, refcnt, sizeof (refcnt));
|
||||
|
||||
zfs_nicenum(dds->dds_blocks, blocks, sizeof (blocks));
|
||||
zfs_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");
|
||||
}
|
||||
|
||||
+1
-334
@@ -39,7 +39,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mnttab.h>
|
||||
@@ -54,6 +53,7 @@
|
||||
#include "zfs_prop.h"
|
||||
#include "zfeature_common.h"
|
||||
#include <zfs_fletcher.h>
|
||||
#include <libzutil.h>
|
||||
|
||||
int
|
||||
libzfs_errno(libzfs_handle_t *hdl)
|
||||
@@ -677,135 +677,6 @@ zfs_strdup(libzfs_handle_t *hdl, const char *str)
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* an ioctl().
|
||||
|
||||
Reference in New Issue
Block a user