static int
check_slice(const char *path, blkid_cache cache, int force, boolean_t isspare)
{
- struct stat64 statbuf;
int err;
#ifdef HAVE_LIBBLKID
char *value;
-#endif /* HAVE_LIBBLKID */
-
- if (stat64(path, &statbuf) != 0) {
- vdev_error(gettext("cannot stat %s: %s\n"),
- path, strerror(errno));
- return (-1);
- }
-#ifdef HAVE_LIBBLKID
/* No valid type detected device is safe to use */
value = blkid_get_tag_value(cache, "TYPE", path);
if (value == NULL)
* not easily decode the MBR return a failure and prompt to the
* user to use force option since we cannot check the partitions.
*/
- if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0) {
+ if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0) {
check_error(errno);
return -1;
}
efi_free(vtoc);
(void) close(fd);
- return (err);
+ return (err);
}
static int
struct dk_gpt *label;
int fd;
- if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0)
+ if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0)
return (B_FALSE);
if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
(void) close(fd);
/*
* 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, 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)
{
- char dirs[5][8] = {"by-id", "by-label", "by-path", "by-uuid", "zpool"};
- int i, err;
-
- /* /dev/<name> */
- (void) snprintf(path, MAXPATHLEN, "%s/%s", DISK_ROOT, arg);
- *wholedisk = is_whole_disk(path);
- err = stat64(path, statbuf);
- if (*wholedisk || err == 0)
- return (0);
+ int error;
- /* /dev/disk/<dirs>/<name> */
- for (i = 0; i < 5; i++) {
- (void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
- UDISK_ROOT, dirs[i], arg);
+ error = zfs_resolve_shortname(arg, path, MAXPATHLEN);
+ if (error == 0) {
*wholedisk = is_whole_disk(path);
- err = stat64(path, statbuf);
- if (*wholedisk || err == 0)
+ if (*wholedisk || (stat64(path, statbuf) == 0))
return (0);
}
memset(statbuf, 0, sizeof(*statbuf));
*wholedisk = B_FALSE;
- return (ENOENT);
+ return (error);
+}
+
+/*
+ * Determine if the given path is a hot spare within the given configuration.
+ * If no configuration is given we rely solely on the label.
+ */
+static boolean_t
+is_spare(nvlist_t *config, const char *path)
+{
+ int fd;
+ pool_state_t state;
+ char *name = NULL;
+ nvlist_t *label;
+ uint64_t guid, spareguid;
+ nvlist_t *nvroot;
+ nvlist_t **spares;
+ uint_t i, nspares;
+ boolean_t inuse;
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return (B_FALSE);
+
+ if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
+ !inuse ||
+ state != POOL_STATE_SPARE ||
+ zpool_read_label(fd, &label) != 0) {
+ free(name);
+ (void) close(fd);
+ return (B_FALSE);
+ }
+ free(name);
+ (void) close(fd);
+
+ if (config == NULL)
+ return (B_TRUE);
+
+ verify(nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) == 0);
+ nvlist_free(label);
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
+ &spares, &nspares) == 0) {
+ for (i = 0; i < nspares; i++) {
+ verify(nvlist_lookup_uint64(spares[i],
+ ZPOOL_CONFIG_GUID, &spareguid) == 0);
+ if (spareguid == guid)
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
}
/*
* 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 <zfs_vdev_paths>/xxx
*/
static nvlist_t *
-make_leaf_vdev(const char *arg, uint64_t is_log)
+make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log)
{
char path[MAXPATHLEN];
struct stat64 statbuf;
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
(uint64_t)wholedisk) == 0);
-#if defined(__sun__) || defined(__sun)
- /*
- * For a whole disk, defer getting its devid until after labeling it.
- */
- if (S_ISBLK(statbuf.st_mode) && !wholedisk) {
- /*
- * Get the devid for the device.
- */
- int fd;
- ddi_devid_t devid;
- char *minor = NULL, *devid_str = NULL;
-
- if ((fd = open(path, O_RDONLY|O_EXCL)) < 0) {
- (void) fprintf(stderr, gettext("cannot open '%s': "
- "%s\n"), path, strerror(errno));
- nvlist_free(vdev);
- return (NULL);
- }
+ if (props != NULL) {
+ uint64_t ashift = 0;
+ char *value = NULL;
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0 &&
- (devid_str = devid_str_encode(devid, minor)) !=
- NULL) {
- verify(nvlist_add_string(vdev,
- ZPOOL_CONFIG_DEVID, devid_str) == 0);
- }
- if (devid_str != NULL)
- devid_str_free(devid_str);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
+ if (nvlist_lookup_string(props,
+ zpool_prop_to_name(ZPOOL_PROP_ASHIFT), &value) == 0)
+ zfs_nicestrtonum(NULL, value, &ashift);
- (void) close(fd);
+ if (ashift > 0)
+ verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_ASHIFT,
+ ashift) == 0);
}
-#endif
return (vdev);
}
{
nvlist_t **child;
uint_t c, children;
- char *type, *path, *diskname;
- char buf[MAXPATHLEN];
+ char *type, *path;
+ char devpath[MAXPATHLEN];
+ char udevpath[MAXPATHLEN];
uint64_t wholedisk;
+ struct stat64 statbuf;
+ int is_exclusive = 0;
+ int fd;
int ret;
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
&wholedisk));
if (!wholedisk) {
- ret = zero_label(path);
- return (ret);
+ (void) zero_label(path);
+ return (0);
}
- if (realpath(path, buf) == NULL) {
+ if (realpath(path, devpath) == NULL) {
ret = errno;
(void) fprintf(stderr,
gettext("cannot resolve path '%s'\n"), path);
return (ret);
}
- diskname = strrchr(buf, '/');
- assert(diskname != NULL);
- diskname++;
- if (zpool_label_disk(g_zfs, zhp, diskname) == -1)
- return (-1);
+ /*
+ * Remove any previously existing symlink from a udev path to
+ * the device before labeling the disk. This makes
+ * zpool_label_disk_wait() truly wait for the new link to show
+ * up instead of returning if it finds an old link still in
+ * place. Otherwise there is a window between when udev
+ * deletes and recreates the link during which access attempts
+ * will fail with ENOENT.
+ */
+ strncpy(udevpath, path, MAXPATHLEN);
+ (void) zfs_append_partition(udevpath, MAXPATHLEN);
+
+ fd = open(devpath, O_RDWR|O_EXCL);
+ if (fd == -1) {
+ if (errno == EBUSY)
+ is_exclusive = 1;
+ } else {
+ (void) close(fd);
+ }
/*
- * Now the we've labeled the disk and the partitions have
- * been created. We still need to wait for udev to create
- * the symlinks to those partitions. If we are accessing
- * the devices via a udev disk path, /dev/disk, then wait
- * for *-part# to be created. Otherwise just use the normal
- * syntax for devices in /dev.
+ * If the partition exists, contains a valid spare label,
+ * and is opened exclusively there is no need to partition
+ * it. Hot spares have already been partitioned and are
+ * held open exclusively by the kernel as a safety measure.
+ *
+ * If the provided path is for a /dev/disk/ device its
+ * symbolic link will be removed, partition table created,
+ * and then block until udev creates the new link.
*/
- if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
- (void) snprintf(buf, sizeof (buf),
- "%s%s%s", path, "-part", FIRST_SLICE);
- else
- (void) snprintf(buf, sizeof (buf),
- "%s%s%s", path, isdigit(path[strlen(path)-1]) ?
- "p" : "", FIRST_SLICE);
+ if (!is_exclusive || !is_spare(NULL, udevpath)) {
+ ret = strncmp(udevpath,UDISK_ROOT,strlen(UDISK_ROOT));
+ if (ret == 0) {
+ ret = lstat64(udevpath, &statbuf);
+ if (ret == 0 && S_ISLNK(statbuf.st_mode))
+ (void) unlink(udevpath);
+ }
- if ((ret = zpool_label_disk_wait(buf, 1000)) != 0) {
- (void) fprintf(stderr,
- gettext( "cannot resolve path '%s'\n"), buf);
- return (-1);
+ if (zpool_label_disk(g_zfs, zhp,
+ strrchr(devpath, '/') + 1) == -1)
+ return (-1);
+
+ ret = zpool_label_disk_wait(udevpath, 1000);
+ if (ret) {
+ (void) fprintf(stderr, gettext("cannot "
+ "resolve path '%s': %d\n"), udevpath, ret);
+ return (-1);
+ }
+
+ (void) zero_label(udevpath);
}
/*
- * 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, buf) == 0);
-
- /* Just in case this partition already existed. */
- (void) zero_label(buf);
+ verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, udevpath) == 0);
return (0);
}
}
/*
- * Determine if the given path is a hot spare within the given configuration.
- */
-static boolean_t
-is_spare(nvlist_t *config, const char *path)
-{
- int fd;
- pool_state_t state;
- char *name = NULL;
- nvlist_t *label;
- uint64_t guid, spareguid;
- nvlist_t *nvroot;
- nvlist_t **spares;
- uint_t i, nspares;
- boolean_t inuse;
-
- if ((fd = open(path, O_RDONLY|O_EXCL)) < 0)
- return (B_FALSE);
-
- if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
- !inuse ||
- state != POOL_STATE_SPARE ||
- zpool_read_label(fd, &label) != 0) {
- free(name);
- (void) close(fd);
- return (B_FALSE);
- }
- free(name);
- (void) close(fd);
-
- verify(nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) == 0);
- nvlist_free(label);
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- verify(nvlist_lookup_uint64(spares[i],
- ZPOOL_CONFIG_GUID, &spareguid) == 0);
- if (spareguid == guid)
- return (B_TRUE);
- }
- }
-
- return (B_FALSE);
-}
-
-/*
* Go through and find any devices that are in use. We rely on libdiskmgt for
* the majority of this task.
*/
* regardless of what libblkid or zpool_in_use() says.
*/
if (replacing) {
- if (wholedisk)
- (void) snprintf(buf, sizeof (buf), "%ss0",
- path);
- else
- (void) strlcpy(buf, path, sizeof (buf));
+ (void) strlcpy(buf, path, sizeof (buf));
+ if (wholedisk) {
+ ret = zfs_append_partition(buf, sizeof (buf));
+ if (ret == -1)
+ return (-1);
+ }
if (is_spare(config, buf))
return (0);
* because the program is just going to exit anyway.
*/
nvlist_t *
-construct_spec(int argc, char **argv)
+construct_spec(nvlist_t *props, int argc, char **argv)
{
nvlist_t *nvroot, *nv, **top, **spares, **l2cache;
int t, toplevels, mindev, maxdev, nspares, nlogs, nl2cache;
children * sizeof (nvlist_t *));
if (child == NULL)
zpool_no_memory();
- if ((nv = make_leaf_vdev(argv[c], B_FALSE))
+ if ((nv = make_leaf_vdev(props, argv[c], B_FALSE))
== NULL)
return (NULL);
child[children - 1] = nv;
* We have a device. Pass off to make_leaf_vdev() to
* construct the appropriate nvlist describing the vdev.
*/
- if ((nv = make_leaf_vdev(argv[0], is_log)) == NULL)
+ if ((nv = make_leaf_vdev(props, argv[0], is_log)) == NULL)
return (NULL);
if (is_log)
nlogs++;
uint_t c, children;
if (argc > 0) {
- if ((newroot = construct_spec(argc, argv)) == NULL) {
+ if ((newroot = construct_spec(props, argc, argv)) == NULL) {
(void) fprintf(stderr, gettext("Unable to build a "
"pool from the specified devices\n"));
return (NULL);
* added, even if they appear in use.
*/
nvlist_t *
-make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
+make_root_vdev(zpool_handle_t *zhp, nvlist_t *props, int force, int check_rep,
boolean_t replacing, boolean_t dryrun, int argc, char **argv)
{
nvlist_t *newroot;
* that we have a valid specification, and that all devices can be
* opened.
*/
- if ((newroot = construct_spec(argc, argv)) == NULL)
+ if ((newroot = construct_spec(props, argc, argv)) == NULL)
return (NULL);
if (zhp && ((poolconfig = zpool_get_config(zhp, NULL)) == NULL))