diff --git a/cmd/zpool/zpool_vdev.c b/cmd/zpool/zpool_vdev.c index 8c4fadebd..da121f06f 100644 --- a/cmd/zpool/zpool_vdev.c +++ b/cmd/zpool/zpool_vdev.c @@ -366,16 +366,18 @@ is_whole_disk(const char *path) /* * This may be a shorthand device path or it could be total gibberish. - * Check to see if it's a known device in /dev/, /dev/disk/by-id, - * /dev/disk/by-label, /dev/disk/by-path, /dev/disk/by-uuid, - * /dev/disk/by-vdev, or /dev/disk/zpool/. As part of this check, see - * if we've been given an entire disk (minus the slice number). + * Check to see if it is a known device available in zfs_vdev_paths. + * As part of this check, see if we've been given an entire disk + * (minus the slice number). */ static int is_shorthand_path(const char *arg, char *path, struct stat64 *statbuf, boolean_t *wholedisk) { - if (zfs_resolve_shortname(arg, path, MAXPATHLEN) == 0) { + int error; + + error = zfs_resolve_shortname(arg, path, MAXPATHLEN); + if (error == 0) { *wholedisk = is_whole_disk(path); if (*wholedisk || (stat64(path, statbuf) == 0)) return (0); @@ -385,7 +387,7 @@ is_shorthand_path(const char *arg, char *path, memset(statbuf, 0, sizeof(*statbuf)); *wholedisk = B_FALSE; - return (ENOENT); + return (error); } /* @@ -393,9 +395,9 @@ is_shorthand_path(const char *arg, char *path, * device, fill in the device id to make a complete nvlist. Valid forms for a * leaf vdev are: * - * /dev/xxx Complete disk path - * /xxx Full path to file - * xxx Shorthand for /dev/disk/yyy/xxx + * /dev/xxx Complete disk path + * /xxx Full path to file + * xxx Shorthand for /xxx */ static nvlist_t * make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log) @@ -959,7 +961,9 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv) * deletes and recreates the link during which access attempts * will fail with ENOENT. */ - zfs_append_partition(path, udevpath, sizeof (udevpath)); + strncpy(udevpath, path, MAXPATHLEN); + (void) zfs_append_partition(udevpath, MAXPATHLEN); + if ((strncmp(udevpath, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) && (lstat64(udevpath, &statbuf) == 0) && S_ISLNK(statbuf.st_mode)) @@ -983,9 +987,9 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv) } /* - * Update the path to refer to FIRST_SLICE. The presence of + * Update the path to refer to the partition. The presence of * the 'whole_disk' field indicates to the CLI that we should - * chop off the slice number when displaying the device in + * chop off the partition number when displaying the device in * future output. */ verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, udevpath) == 0); diff --git a/include/libzfs.h b/include/libzfs.h index 75e149334..e59350c9d 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -54,22 +54,11 @@ extern "C" { /* * Default device paths */ +#define DISK_ROOT "/dev" +#define UDISK_ROOT "/dev/disk" -#if defined(__sun__) || defined(__sun) -#define DISK_ROOT "/dev/dsk" -#define RDISK_ROOT "/dev/rdsk" -#define UDISK_ROOT RDISK_ROOT -#define FIRST_SLICE "s0" -#define BACKUP_SLICE "s2" -#endif - -#ifdef __linux__ -#define DISK_ROOT "/dev" -#define RDISK_ROOT DISK_ROOT -#define UDISK_ROOT "/dev/disk" -#define FIRST_SLICE "1" -#define BACKUP_SLICE "" -#endif +#define DEFAULT_IMPORT_PATH_SIZE 8 +extern char *zpool_default_import_path[DEFAULT_IMPORT_PATH_SIZE]; /* * libzfs errors @@ -658,8 +647,9 @@ extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t); extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *, zfs_type_t); extern int zfs_spa_version(zfs_handle_t *, int *); -extern void zfs_append_partition(const char *path, char *buf, size_t buflen); +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); /* * Mount support functions. diff --git a/lib/libzfs/libzfs_import.c b/lib/libzfs/libzfs_import.c index b5cac6094..98dc8788b 100644 --- a/lib/libzfs/libzfs_import.c +++ b/lib/libzfs/libzfs_import.c @@ -984,9 +984,7 @@ err_blkid1: } #endif /* HAVE_LIBBLKID */ -#define DEFAULT_IMPORT_PATH_SIZE 8 - -static char * +char * zpool_default_import_path[DEFAULT_IMPORT_PATH_SIZE] = { "/dev/disk/by-vdev", /* Custom rules, use first if they exist */ "/dev/disk/zpool", /* Custom rules, use first if they exist */ diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 2198a8285..75e883e4f 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -1744,10 +1744,11 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, /* * Search for the requested value. Special cases: * - * - ZPOOL_CONFIG_PATH for whole disk entries. These end in with a - * partition suffix "1", "-part1", or "p1". The suffix is hidden - * from the user, but included in the string, so this matches around - * it. + * - ZPOOL_CONFIG_PATH for whole disk entries. These end in + * "-part1", or "p1". The suffix is hidden from the user, + * but included in the string, so this matches around it. + * - ZPOOL_CONFIG_PATH for short names zfs_strcmp_shortname() + * is used to check all possible expanded paths. * - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE). * * Otherwise, all other searches are simple string compares. @@ -1757,15 +1758,9 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk); - if (wholedisk) { - char buf[MAXPATHLEN]; + if (zfs_strcmp_pathname(srchval, val, wholedisk) == 0) + return (nv); - zfs_append_partition(srchval, buf, sizeof (buf)); - if (strcmp(val, buf) == 0) - return (nv); - - break; - } } else if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) { char *type, *idx, *end, *p; uint64_t id, vdev_id; @@ -1916,7 +1911,6 @@ nvlist_t * zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log) { - char buf[MAXPATHLEN]; char *end; nvlist_t *nvroot, *search, *ret; uint64_t guid; @@ -1928,12 +1922,6 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0); } else if (zpool_vdev_is_interior(path)) { verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0); - } else if (path[0] != '/') { - if (zfs_resolve_shortname(path, buf, sizeof (buf)) < 0) { - nvlist_free(search); - return (NULL); - } - verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0); } else { verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0); } @@ -3701,7 +3689,7 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb) if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0) return (err); - (void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT, + (void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT, strrchr(path, '/')); if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) { struct dk_gpt *vtoc; @@ -3839,8 +3827,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) start_block = NEW_START_BLOCK; } - (void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name, - BACKUP_SLICE); + (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name); if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) { /* @@ -3910,9 +3897,11 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) (void) close(fd); efi_free(vtoc); - /* Wait for the first expected slice to appear. */ - (void) snprintf(path, sizeof (path), "%s/%s%s%s", DISK_ROOT, name, - isdigit(name[strlen(name)-1]) ? "p" : "", FIRST_SLICE); + /* Wait for the first expected partition to appear. */ + + (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name); + (void) zfs_append_partition(path, MAXPATHLEN); + rval = zpool_label_disk_wait(path, 3000); if (rval) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to " diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index de4bb72de..16affd1ce 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -800,56 +800,165 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype) } /* - * Given a shorthand device name, check if a file by that name exists in a list - * of directories under /dev. If one is found, store its full path in the - * buffer pointed to by the path argument and return 0, else return -1. The - * path buffer must be allocated by the caller. + * 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_resolve_shortname(const char *name, char *path, size_t pathlen) +zfs_append_partition(char *path, size_t max_len) { - int i, err; - char dirs[6][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool", - "by-vdev"}; + int len = strlen(path); - /* /dev/ */ - (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name); - err = access(path, F_OK); - if (err == 0) - return (err); + if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) { + if (len + 6 >= max_len) + return (-1); - /* /dev/mapper/ */ - (void) snprintf(path, pathlen, "%s/mapper/%s", DISK_ROOT, name); - err = access(path, F_OK); - if (err == 0) - return (err); + (void) strcat(path, "-part1"); + len += 6; + } else { + if (len + 2 >= max_len) + return (-1); - /* /dev/disk// */ - for (i = 0; i < 6 && err < 0; i++) { - (void) snprintf(path, pathlen, "%s/%s/%s", - UDISK_ROOT, dirs[i], name); - err = access(path, F_OK); + if (isdigit(path[len-1])) { + (void) strcat(path, "p1"); + len += 2; + } else { + (void) strcat(path, "1"); + len += 1; + } } - return (err); + return (len); } /* - * Append partition suffix to a device path. This should be used to generate - * the name of a whole disk as it is stored in the vdev label. The - * user-visible names of whole disks do not contain the partition information. - * Modifies buf which must be allocated by the caller. + * 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. */ -void -zfs_append_partition(const char *path, char *buf, size_t buflen) +int +zfs_resolve_shortname(const char *name, char *path, size_t len) { - if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) - (void) snprintf(buf, buflen, "%s%s%s", path, "-part", - FIRST_SLICE); - else - (void) snprintf(buf, buflen, "%s%s%s", path, - isdigit(path[strlen(path)-1]) ? "p" : "", - FIRST_SLICE); + 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)) { + 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; + + /* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */ + memset(cmp_name, 0, MAXPATHLEN); + dir = strtok(cmp, "/"); + while (dir) { + strcat(cmp_name, "/"); + strcat(cmp_name, dir); + dir = strtok(NULL, "/"); + } + + if (name[0] != '/') + return zfs_strcmp_shortname(name, cmp_name, wholedisk); + + strncpy(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); } /*