Skip to content

Commit

Permalink
OpenZFS 6865 - want zfs-tests cases for zpool labelclear command
Browse files Browse the repository at this point in the history
Authored by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: John Kennedy <john.kennedy@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: loli10K <ezomori.nozomu@gmail.com>
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>

Porting Notes:
- Updated 'zpool labelclear' and 'zdb -l' such that they attempt
  to find a vdev given solely its short name.  This behavior is
  consistent with the upstream OpenZFS code and the test cases
  depend on it.  The actual implementation differs slightly due
  to device naming conventions on Linux.
- auto_online_001_pos, auto_replace_001_pos and add-o_ashift
  test cases updated to expect failure when no label exists.
- read_efi_label() and zpool_label_disk_check() are read-only
  operations and should use O_RDONLY at open time to enforce this.
- zpool_label_disk() and zpool_relabel_disk() write the partition
  information using O_DIRECT an fsync() and page cache invalidation
  to ensure a consistent view of the device.
- dump_label() in zdb should invalidate the page cache in order
  to get the authoritative label from disk.

OpenZFS-issue: https://www.illumos.org/issues/6865
OpenZFS-commit: openzfs/openzfs@c95076c
Closes #5981
  • Loading branch information
Yuri Pankov authored and behlendorf committed Apr 11, 2017
1 parent 047187c commit dbb38f6
Show file tree
Hide file tree
Showing 15 changed files with 350 additions and 105 deletions.
24 changes: 24 additions & 0 deletions cmd/zdb/zdb.c
Expand Up @@ -2515,13 +2515,37 @@ dump_label(const char *dev)

bzero(labels, sizeof (labels));

/*
* Check if we were given absolute path and use it as is.
* Otherwise if the provided vdev name doesn't point to a file,
* try prepending expected disk paths and partition numbers.
*/
(void) strlcpy(path, dev, sizeof (path));
if (dev[0] != '/' && stat64(path, &statbuf) != 0) {
int error;

error = zfs_resolve_shortname(dev, path, MAXPATHLEN);
if (error == 0 && zfs_dev_is_whole_disk(path)) {
if (zfs_append_partition(path, MAXPATHLEN) == -1)
error = ENOENT;
}

if (error || (stat64(path, &statbuf) != 0)) {
(void) printf("failed to find device %s, try "
"specifying absolute path instead\n", dev);
return (1);
}
}

if ((fd = open64(path, O_RDONLY)) < 0) {
(void) printf("cannot open '%s': %s\n", path, strerror(errno));
exit(1);
}

if (ioctl(fd, BLKFLSBUF) != 0)
(void) printf("failed to invalidate cache '%s' : %s\n", path,
strerror(errno));

if (fstat64_blk(fd, &statbuf) != 0) {
(void) printf("failed to stat '%s': %s\n", path,
strerror(errno));
Expand Down
151 changes: 87 additions & 64 deletions cmd/zpool/zpool_main.c
Expand Up @@ -790,16 +790,22 @@ zpool_do_remove(int argc, char **argv)
}

/*
* zpool labelclear <vdev>
* zpool labelclear [-f] <vdev>
*
* -f Force clearing the label for the vdevs which are members of
* the exported or foreign pools.
*
* Verifies that the vdev is not active and zeros out the label information
* on the device.
*/
int
zpool_do_labelclear(int argc, char **argv)
{
char *vdev, *name;
char vdev[MAXPATHLEN];
char *name = NULL;
struct stat st;
int c, fd = -1, ret = 0;
nvlist_t *config;
pool_state_t state;
boolean_t inuse = B_FALSE;
boolean_t force = B_FALSE;
Expand All @@ -822,90 +828,107 @@ zpool_do_labelclear(int argc, char **argv)

/* get vdev name */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing vdev device name\n"));
(void) fprintf(stderr, gettext("missing vdev name\n"));
usage(B_FALSE);
}

vdev = argv[0];
if ((fd = open(vdev, O_RDWR)) < 0) {
(void) fprintf(stderr, gettext("Unable to open %s\n"), vdev);
return (B_FALSE);
if (argc > 1) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
}

name = NULL;
if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0) {
if (force)
goto wipe_label;
/*
* Check if we were given absolute path and use it as is.
* Otherwise if the provided vdev name doesn't point to a file,
* try prepending expected disk paths and partition numbers.
*/
(void) strlcpy(vdev, argv[0], sizeof (vdev));
if (vdev[0] != '/' && stat(vdev, &st) != 0) {
int error;

error = zfs_resolve_shortname(argv[0], vdev, MAXPATHLEN);
if (error == 0 && zfs_dev_is_whole_disk(vdev)) {
if (zfs_append_partition(vdev, MAXPATHLEN) == -1)
error = ENOENT;
}

(void) fprintf(stderr,
gettext("Unable to determine pool state for %s\n"
"Use -f to force the clearing any label data\n"), vdev);
if (error || (stat(vdev, &st) != 0)) {
(void) fprintf(stderr, gettext(
"failed to find device %s, try specifying absolute "
"path instead\n"), argv[0]);
return (1);
}
}

if ((fd = open(vdev, O_RDWR)) < 0) {
(void) fprintf(stderr, gettext("failed to open %s: %s\n"),
vdev, strerror(errno));
return (1);
}

if (inuse) {
switch (state) {
default:
case POOL_STATE_ACTIVE:
case POOL_STATE_SPARE:
case POOL_STATE_L2CACHE:
(void) fprintf(stderr,
gettext("labelclear operation failed.\n"
"\tVdev %s is a member (%s), of pool \"%s\".\n"
"\tTo remove label information from this device, "
"export or destroy\n\tthe pool, or remove %s from "
"the configuration of this pool\n\tand retry the "
"labelclear operation.\n"),
vdev, zpool_pool_state_to_name(state), name, vdev);
ret = 1;
goto errout;
if (zpool_read_label(fd, &config, NULL) != 0 || config == NULL) {
(void) fprintf(stderr,
gettext("failed to check state for %s\n"), vdev);
return (1);
}
nvlist_free(config);

case POOL_STATE_EXPORTED:
if (force)
break;
ret = zpool_in_use(g_zfs, fd, &state, &name, &inuse);
if (ret != 0) {
(void) fprintf(stderr,
gettext("failed to check state for %s\n"), vdev);
return (1);
}

(void) fprintf(stderr,
gettext("labelclear operation failed.\n\tVdev "
"%s is a member of the exported pool \"%s\".\n"
"\tUse \"zpool labelclear -f %s\" to force the "
"removal of label\n\tinformation.\n"),
vdev, name, vdev);
ret = 1;
goto errout;
if (!inuse)
goto wipe_label;

case POOL_STATE_POTENTIALLY_ACTIVE:
if (force)
break;
switch (state) {
default:
case POOL_STATE_ACTIVE:
case POOL_STATE_SPARE:
case POOL_STATE_L2CACHE:
(void) fprintf(stderr, gettext(
"%s is a member (%s) of pool \"%s\"\n"),
vdev, zpool_pool_state_to_name(state), name);
ret = 1;
goto errout;

(void) fprintf(stderr,
gettext("labelclear operation failed.\n"
"\tVdev %s is a member of the pool \"%s\".\n"
"\tThis pool is unknown to this system, but may "
"be active on\n\tanother system. Use "
"\'zpool labelclear -f %s\' to force the\n"
"\tremoval of label information.\n"),
vdev, name, vdev);
ret = 1;
goto errout;
case POOL_STATE_EXPORTED:
if (force)
break;
(void) fprintf(stderr, gettext(
"use '-f' to override the following error:\n"
"%s is a member of exported pool \"%s\"\n"),
vdev, name);
ret = 1;
goto errout;

case POOL_STATE_DESTROYED:
/* inuse should never be set for a destroyed pool... */
case POOL_STATE_POTENTIALLY_ACTIVE:
if (force)
break;
}
(void) fprintf(stderr, gettext(
"use '-f' to override the following error:\n"
"%s is a member of potentially active pool \"%s\"\n"),
vdev, name);
ret = 1;
goto errout;

case POOL_STATE_DESTROYED:
/* inuse should never be set for a destroyed pool */
assert(0);
break;
}

wipe_label:
if (zpool_clear_label(fd) != 0) {
ret = zpool_clear_label(fd);
if (ret != 0) {
(void) fprintf(stderr,
gettext("Label clear failed on vdev %s\n"), vdev);
ret = 1;
gettext("failed to clear label for %s\n"), vdev);
}

errout:
close(fd);
if (name != NULL)
free(name);
free(name);
(void) close(fd);

return (ret);
}
Expand Down
33 changes: 4 additions & 29 deletions cmd/zpool/zpool_vdev.c
Expand Up @@ -506,31 +506,6 @@ check_device(const char *path, boolean_t force,
return (error);
}

/*
* 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.
*/
static boolean_t
is_whole_disk(const char *path)
{
struct dk_gpt *label;
int fd;

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);
return (B_FALSE);
}
efi_free(label);
(void) close(fd);
return (B_TRUE);
}

/*
* This may be a shorthand device path or it could be total gibberish.
* Check to see if it is a known device available in zfs_vdev_paths.
Expand All @@ -545,7 +520,7 @@ is_shorthand_path(const char *arg, char *path, size_t path_size,

error = zfs_resolve_shortname(arg, path, path_size);
if (error == 0) {
*wholedisk = is_whole_disk(path);
*wholedisk = zfs_dev_is_whole_disk(path);
if (*wholedisk || (stat64(path, statbuf) == 0))
return (0);
}
Expand Down Expand Up @@ -640,7 +615,7 @@ make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log)
/*
* Complete device or file path. Exact type is determined by
* examining the file descriptor afterwards. Symbolic links
* are resolved to their real paths for the is_whole_disk()
* are resolved to their real paths to determine whole disk
* and S_ISBLK/S_ISREG type checks. However, we are careful
* to store the given path as ZPOOL_CONFIG_PATH to ensure we
* can leverage udev's persistent device labels.
Expand All @@ -651,15 +626,15 @@ make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log)
return (NULL);
}

wholedisk = is_whole_disk(path);
wholedisk = zfs_dev_is_whole_disk(path);
if (!wholedisk && (stat64(path, &statbuf) != 0)) {
(void) fprintf(stderr,
gettext("cannot open '%s': %s\n"),
path, strerror(errno));
return (NULL);
}

/* After is_whole_disk() check restore original passed path */
/* After whole disk check restore original passed path */
strlcpy(path, arg, sizeof (path));
} else {
err = is_shorthand_path(arg, path, sizeof (path),
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Expand Up @@ -213,6 +213,7 @@ AC_CONFIG_FILES([
tests/zfs-tests/tests/functional/cli_root/zpool_get/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_history/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_import/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_labelclear/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_offline/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_online/Makefile
Expand Down
1 change: 1 addition & 0 deletions include/libzfs.h
Expand Up @@ -286,6 +286,7 @@ extern int zpool_label_disk_wait(char *, int);
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);

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);

Expand Down

0 comments on commit dbb38f6

Please sign in to comment.