Showing with 95 additions and 34 deletions.
  1. +26 −17 cmd/zpool/os/macos/zpool_vdev_os.c
  2. +16 −2 cmd/zpool/zpool_vdev.c
  3. +4 −0 include/libzutil.h
  4. +25 −12 lib/libzutil/os/macos/zutil_device_path_os.c
  5. +24 −3 lib/libzutil/zutil_device_path.c
@@ -88,25 +88,41 @@ check_slice(const char *path, int force, boolean_t isspare)
return (0);
}

/*
* Validate that a disk including all partitions are safe to use.
*
* For EFI labeled disks this can done relatively easily with the libefi
* library. The partition numbers are extracted from the label and used
* to generate the expected /dev/ paths. Each partition can then be
* checked for conflicts.
*
* For non-EFI labeled disks (MBR/EBR/etc) the same process is possible
* but due to the lack of a readily available libraries this scanning is
* not implemented. Instead only the device path as given is checked.
*/
static int
check_disk(const char *path, int force,
boolean_t isspare, boolean_t iswholedisk)
{
struct dk_gpt *vtoc;
char slice_path[MAXPATHLEN];
int err = 0;
int slice_err = 0;
int fd, i;
int flags = O_RDONLY|O_DIRECT;

if (!iswholedisk)
return (check_slice(path, force, isspare));

if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0) {
check_error(errno);
/* only spares can be shared, other devices require exclusive access */
if (!isspare)
flags |= O_EXCL;

if ((fd = open(path, flags)) < 0) {
return (-1);
}

/*
* Expected to fail for non-EFI labled disks. Just check the device
* Expected to fail for non-EFI labeled disks. Just check the device
* as given and do not attempt to detect and scan partitions.
*/
err = efi_alloc_and_read(fd, &vtoc);
@@ -125,25 +141,25 @@ check_disk(const char *path, int force,
(void) close(fd);

if (force) {
/* Partitions will no be created using the backup */
/* Partitions will now be created using the backup */
return (0);
} else {
vdev_error(gettext("%s contains a corrupt primary "
"EFI label.\n"), path);
return (-1);
}
}

for (i = 0; i < vtoc->efi_nparts; i++) {
if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED ||
uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid))
continue;
(void) snprintf(slice_path, sizeof (slice_path),
"%ss%d", path, i+1);
slice_err = check_slice(slice_path, force, isspare);

// Latch the first error that occurs
if (err == 0)
err = slice_err;
err = check_slice(slice_path, force, isspare);
if (err)
break;
}

efi_free(vtoc);
@@ -152,19 +168,12 @@ check_disk(const char *path, int force,
return (err);
}


int
check_device(const char *name, boolean_t force,
check_device(const char *path, boolean_t force,
boolean_t isspare, boolean_t iswholedisk)
{
char path[MAXPATHLEN];
int error;

if (strncmp(name, _PATH_DEV, sizeof (_PATH_DEV) - 1) != 0)
snprintf(path, sizeof (path), "%s%s", _PATH_DEV, name);
else
strlcpy(path, name, sizeof (path));

error = check_disk(path, force, isspare, iswholedisk);
if (error != 0)
return (error);
@@ -66,6 +66,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libzutil.h>
@@ -273,6 +274,8 @@ static nvlist_t *
make_leaf_vdev(nvlist_t *props, const char *arg, boolean_t is_primary)
{
char path[MAXPATHLEN];
char *d, *b;
char *dpath, *bname;
struct stat64 statbuf;
nvlist_t *vdev = NULL;
char *type = NULL;
@@ -308,8 +311,19 @@ make_leaf_vdev(nvlist_t *props, const char *arg, boolean_t is_primary)
return (NULL);
}

/* After whole disk check restore original passed path */
strlcpy(path, arg, sizeof (path));
/*
* After whole disk check restore original passed path and use
* the realpath of the directory.
*/
d = strdup(arg);
b = strdup(arg);
dpath = dirname(d);
bname = basename(b);
realpath(dpath, path);
strlcat(path, "/", sizeof (path));
strlcat(path, bname, sizeof (path));
free(d);
free(b);
} else if (zpool_is_draid_spare(arg)) {
if (!is_primary) {
(void) fprintf(stderr,
@@ -90,7 +90,11 @@ extern void update_vdev_config_dev_strs(nvlist_t *);
* Default device paths
*/
#define DISK_ROOT "/dev"
#ifdef __APPLE__
#define UDISK_ROOT "/private/var/run/disk"
#else
#define UDISK_ROOT "/dev/disk"
#endif
#define ZVOL_ROOT "/dev/zvol"

extern int zfs_append_partition(char *path, size_t max_len);
@@ -31,10 +31,6 @@

#include <libzutil.h>

/*
* We don't strip/append partitions on FreeBSD.
*/

/*
* Note: The caller must free the returned string.
*/
@@ -43,6 +39,7 @@ zfs_strip_partition(char *dev)
{
unsigned int disk, slice;
char *partless;
char whole_disk[MAXPATHLEN];

partless = strdup(dev);

@@ -52,6 +49,16 @@ zfs_strip_partition(char *dev)
r = strrchr(partless, 's');
if (r != NULL)
*r = 0;
} else if ((sscanf(partless, "%[^:]:%u", whole_disk, &slice)) == 2) {
char *r;
r = strrchr(partless, ':');
if (r != NULL) {
if (strchr(partless, '@')) { // by-path
if (slice == 1)
r[1] = '0';
} else // by-serial
*r = 0;
}
}

return (partless);
@@ -61,23 +68,29 @@ int
zfs_append_partition(char *path, size_t max_len)
{
int len = strlen(path);
char dpath[max_len];
if (strncmp(path, "/var/", 5) == 0) {
(void) strlcpy(dpath, "/private", max_len);
(void) strlcat(dpath, path, max_len);
} else
strlcpy(dpath, path, max_len);

if (strncmp(path, "/private/var/run/disk/by-id", 27) == 0) {

if (strncmp(dpath, "/private/var/run/disk/by-id", 27) == 0) {
return (len);
} else if (strncmp(path, "/private/var/run/disk/by-path", 29) == 0) {
} else if (strncmp(dpath, "/private/var/run/disk/by-path", 29) == 0) {
if (path[len - 1] == '0' &&
path[len - 2] == ':')
path[len - 1] = '1';
else
return (-1); /* should have ended with ":0" */

} else if (strncmp(path, "/private/var/run/disk/by-serial", 31) == 0) {
} else if (strncmp(dpath, "/private/var/run/disk/by-serial", 31) == 0) {
if (len + 2 >= max_len)
return (-1);

(void) strcat(path, ":1");
len += 2;

if (strchr(path, ':') == NULL) {
(void) strcat(path, ":1");
len += 2;
}
} else {

if (len + 2 >= max_len)
@@ -24,6 +24,7 @@
*/

#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -142,6 +143,8 @@ zfs_strcmp_pathname(const char *name, const char *cmp, int wholedisk)
char path_name[MAXPATHLEN];
char cmp_name[MAXPATHLEN];
char *dir, *dup;
char *d, *b;
char *dpath, *bname;

/* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
memset(cmp_name, 0, MAXPATHLEN);
@@ -167,8 +170,26 @@ zfs_strcmp_pathname(const char *name, const char *cmp, int wholedisk)
return (ENOMEM);
}

if ((path_len != cmp_len) || strcmp(path_name, cmp_name))
return (ENOENT);
if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0)
return (0);
else {
d = strdup(path_name);
b = strdup(path_name);
dpath = dirname(d);
bname = basename(b);
realpath(dpath, path_name);

if (strcmp(dpath, path_name) == 0)
return (ENOENT); // We already tried this path

strlcat(path_name, "/", sizeof (path_name));
path_len = strlcat(path_name, bname, sizeof (path_name));
free(d);
free(b);

if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0)
return (0);
}

return (0);
return (ENOENT);
}