#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
+#include <zone.h>
+#include <sys/stat.h>
#include <sys/efi_partition.h>
#include <sys/vtoc.h>
#include <sys/zfs_ioctl.h>
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
-#if defined(__i386) || defined(__amd64)
-#define BOOTCMD "installgrub(1M)"
-#else
-#define BOOTCMD "installboot(1M)"
-#endif
-
-#define DISK_ROOT "/dev/dsk"
-#define RDISK_ROOT "/dev/rdsk"
-#define BACKUP_SLICE "s2"
+typedef struct prop_flags {
+ int create:1; /* Validate property on creation */
+ int import:1; /* Validate property on import */
+} prop_flags_t;
/*
* ====================================================================
static int
zpool_get_all_props(zpool_handle_t *zhp)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
{
switch (state) {
+ default:
+ break;
case VDEV_STATE_CLOSED:
case VDEV_STATE_OFFLINE:
return (gettext("OFFLINE"));
case ZPOOL_PROP_GUID:
intval = zpool_get_prop_int(zhp, prop, &src);
- (void) snprintf(buf, len, "%llu", intval);
+ (void) snprintf(buf, len, "%llu", (u_longlong_t)intval);
break;
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_SIZE:
case ZPOOL_PROP_ALLOCATED:
case ZPOOL_PROP_FREE:
+ case ZPOOL_PROP_ASHIFT:
(void) zfs_nicenum(intval, buf, len);
break;
vs->vs_aux), len);
break;
default:
- (void) snprintf(buf, len, "%llu", intval);
+ (void) snprintf(buf, len, "%llu", (u_longlong_t)intval);
}
break;
*/
static nvlist_t *
zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
- nvlist_t *props, uint64_t version, boolean_t create_or_import, char *errbuf)
+ nvlist_t *props, uint64_t version, prop_flags_t flags, char *errbuf)
{
nvpair_t *elem;
nvlist_t *retprops;
* Perform additional checking for specific properties.
*/
switch (prop) {
+ default:
+ break;
case ZPOOL_PROP_VERSION:
if (intval < version || intval > SPA_VERSION) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
}
break;
+ case ZPOOL_PROP_ASHIFT:
+ if (!flags.create) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' can only be set at "
+ "creation time"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (intval != 0 && (intval < 9 || intval > 17)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' number %d is invalid."),
+ propname, intval);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
+
case ZPOOL_PROP_BOOTFS:
- if (create_or_import) {
+ if (flags.create || flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property '%s' cannot be set at creation "
"or import time"), propname);
break;
case ZPOOL_PROP_ALTROOT:
- if (!create_or_import) {
+ if (!flags.create && !flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property '%s' can only be set during pool "
"creation or import"), propname);
*slash = '/';
break;
+
+ case ZPOOL_PROP_READONLY:
+ if (!flags.import) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' can only be set at "
+ "import time"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
}
}
int
zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret = -1;
char errbuf[1024];
nvlist_t *nvl = NULL;
nvlist_t *realprops;
uint64_t version;
+ prop_flags_t flags = { 0 };
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
if ((realprops = zpool_valid_proplist(zhp->zpool_hdl,
- zhp->zpool_name, nvl, version, B_FALSE, errbuf)) == NULL) {
+ zhp->zpool_name, nvl, version, flags, errbuf)) == NULL) {
nvlist_free(nvl);
return (-1);
}
/*
* Don't start the slice at the default block of 34; many storage
- * devices will use a stripe width of 128k, so start there instead.
+ * devices will use a stripe width of 128k, other vendors prefer a 1m
+ * alignment. It is best to play it safe and ensure a 1m alignment
+ * give 512b blocks. When the block size is larger by a power of 2
+ * we will still be 1m aligned.
*/
-#define NEW_START_BLOCK 256
+#define NEW_START_BLOCK 2048
/*
* Validate the given pool name, optionally putting an extended error message in
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"multiple '@' delimiters in name"));
break;
-
+ case NAME_ERR_NO_AT:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "permission set is missing '@'"));
+ break;
}
}
return (B_FALSE);
zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
nvlist_t *props, nvlist_t *fsprops)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
nvlist_t *zc_fsprops = NULL;
nvlist_t *zc_props = NULL;
char msg[1024];
return (-1);
if (props) {
+ prop_flags_t flags = { .create = B_TRUE, .import = B_FALSE };
+
if ((zc_props = zpool_valid_proplist(hdl, pool, props,
- SPA_VERSION_1, B_TRUE, msg)) == NULL) {
+ SPA_VERSION_1, flags, msg)) == NULL) {
goto create_failed;
}
}
* This can happen if the user has specified the same
* device multiple times. We can't reliably detect this
* until we try to add it and see we already have a
- * label.
+ * label. This can also happen under if the device is
+ * part of an active md or lvm device.
*/
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more vdevs refer to the same device"));
+ "one or more vdevs refer to the same device, or one of\n"
+ "the devices is part of an active md or lvm device"));
return (zfs_error(hdl, EZFS_BADDEV, msg));
case EOVERFLOW:
int
zpool_destroy(zpool_handle_t *zhp)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
zfs_handle_t *zfp = NULL;
libzfs_handle_t *hdl = zhp->zpool_hdl;
char msg[1024];
if (zhp->zpool_state == POOL_STATE_ACTIVE &&
- (zfp = zfs_open(zhp->zpool_hdl, zhp->zpool_name,
- ZFS_TYPE_FILESYSTEM)) == NULL)
+ (zfp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_FILESYSTEM)) == NULL)
return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
"cannot destroy '%s'"), zhp->zpool_name);
int
zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret;
libzfs_handle_t *hdl = zhp->zpool_hdl;
char msg[1024];
return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) {
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) {
switch (errno) {
case EBUSY:
/*
int
zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
static void
zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun,
- nvlist_t *rbi)
+ nvlist_t *config)
{
+ nvlist_t *nv = NULL;
uint64_t rewindto;
int64_t loss = -1;
struct tm t;
char timestr[128];
- if (!hdl->libzfs_printerr || rbi == NULL)
+ if (!hdl->libzfs_printerr || config == NULL)
+ return;
+
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0)
return;
- if (nvlist_lookup_uint64(rbi, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
return;
- (void) nvlist_lookup_int64(rbi, ZPOOL_CONFIG_REWIND_TIME, &loss);
+ (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
if (localtime_r((time_t *)&rewindto, &t) != NULL &&
- strftime(timestr, 128, 0, &t) != 0) {
+ strftime(timestr, 128, "%c", &t) != 0) {
if (dryrun) {
(void) printf(dgettext(TEXT_DOMAIN,
"Would be able to return %s "
(void) printf(dgettext(TEXT_DOMAIN,
"%s approximately %lld "),
dryrun ? "Would discard" : "Discarded",
- (loss + 30) / 60);
+ ((longlong_t)loss + 30) / 60);
(void) printf(dgettext(TEXT_DOMAIN,
"minutes of transactions.\n"));
} else if (loss > 0) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s approximately %lld "),
- dryrun ? "Would discard" : "Discarded", loss);
+ dryrun ? "Would discard" : "Discarded",
+ (longlong_t)loss);
(void) printf(dgettext(TEXT_DOMAIN,
"seconds of transactions.\n"));
}
zpool_explain_recover(libzfs_handle_t *hdl, const char *name, int reason,
nvlist_t *config)
{
+ nvlist_t *nv = NULL;
int64_t loss = -1;
uint64_t edata = UINT64_MAX;
uint64_t rewindto;
(void) printf(dgettext(TEXT_DOMAIN, "\t"));
/* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
- if (nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
+ nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
goto no_info;
- (void) nvlist_lookup_int64(config, ZPOOL_CONFIG_REWIND_TIME, &loss);
- (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_LOAD_DATA_ERRORS,
+ (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_DATA_ERRORS,
&edata);
(void) printf(dgettext(TEXT_DOMAIN,
"Recovery is possible, but will result in some data loss.\n"));
if (localtime_r((time_t *)&rewindto, &t) != NULL &&
- strftime(timestr, 128, 0, &t) != 0) {
+ strftime(timestr, 128, "%c", &t) != 0) {
(void) printf(dgettext(TEXT_DOMAIN,
"\tReturning the pool to its state as of %s\n"
"\tshould correct the problem. "),
if (loss > 120) {
(void) printf(dgettext(TEXT_DOMAIN,
"Approximately %lld minutes of data\n"
- "\tmust be discarded, irreversibly. "), (loss + 30) / 60);
+ "\tmust be discarded, irreversibly. "),
+ ((longlong_t)loss + 30) / 60);
} else if (loss > 0) {
(void) printf(dgettext(TEXT_DOMAIN,
"Approximately %lld seconds of data\n"
- "\tmust be discarded, irreversibly. "), loss);
+ "\tmust be discarded, irreversibly. "),
+ (longlong_t)loss);
}
if (edata != 0 && edata != UINT64_MAX) {
if (edata == 1) {
}
}
- ret = zpool_import_props(hdl, config, newname, props, B_FALSE);
+ ret = zpool_import_props(hdl, config, newname, props,
+ ZFS_IMPORT_NORMAL);
if (props)
nvlist_free(props);
return (ret);
}
+static void
+print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv,
+ int indent)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ char *vname;
+ uint64_t is_log = 0;
+
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG,
+ &is_log);
+
+ if (name != NULL)
+ (void) printf("\t%*s%s%s\n", indent, "", name,
+ is_log ? " [log]" : "");
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(hdl, NULL, child[c], B_TRUE);
+ print_vdev_tree(hdl, vname, child[c], indent + 2);
+ free(vname);
+ }
+}
+
/*
* Import the given pool using the known configuration and a list of
* properties to be set. The configuration should have come from
*/
int
zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
- nvlist_t *props, boolean_t importfaulted)
+ nvlist_t *props, int flags)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
zpool_rewind_policy_t policy;
- nvlist_t *nvi = NULL;
+ nvlist_t *nv = NULL;
+ nvlist_t *nvinfo = NULL;
+ nvlist_t *missing = NULL;
char *thename;
char *origname;
- uint64_t returned_size;
int ret;
+ int error = 0;
char errbuf[1024];
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
if (props) {
uint64_t version;
+ prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
&version) == 0);
if ((props = zpool_valid_proplist(hdl, origname,
- props, version, B_TRUE, errbuf)) == NULL) {
+ props, version, flags, errbuf)) == NULL) {
return (-1);
} else if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
nvlist_free(props);
nvlist_free(props);
return (-1);
}
- returned_size = zc.zc_nvlist_conf_size + 512;
- if (zcmd_alloc_dst_nvlist(hdl, &zc, returned_size) != 0) {
+ if (zcmd_alloc_dst_nvlist(hdl, &zc, zc.zc_nvlist_conf_size * 2) != 0) {
nvlist_free(props);
return (-1);
}
- zc.zc_cookie = (uint64_t)importfaulted;
- ret = 0;
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
+ zc.zc_cookie = flags;
+ while ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc)) != 0 &&
+ errno == ENOMEM) {
+ if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ }
+ if (ret != 0)
+ error = errno;
+
+ (void) zcmd_read_dst_nvlist(hdl, &zc, &nv);
+ zpool_get_rewind_policy(config, &policy);
+
+ if (error) {
char desc[1024];
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nvi);
- zpool_get_rewind_policy(config, &policy);
/*
* Dry-run failed, but we print out what success
* looks like if we found a best txg
*/
- if ((policy.zrp_request & ZPOOL_TRY_REWIND) && nvi) {
+ if (policy.zrp_request & ZPOOL_TRY_REWIND) {
zpool_rewind_exclaim(hdl, newname ? origname : thename,
- B_TRUE, nvi);
- nvlist_free(nvi);
+ B_TRUE, nv);
+ nvlist_free(nv);
return (-1);
}
dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"),
origname, thename);
- switch (errno) {
+ switch (error) {
case ENOTSUP:
/*
* Unsupported version.
(void) zfs_error(hdl, EZFS_BADDEV, desc);
break;
+ case ENXIO:
+ if (nv && nvlist_lookup_nvlist(nv,
+ ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
+ nvlist_lookup_nvlist(nvinfo,
+ ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "The devices below are missing, use "
+ "'-m' to import the pool anyway:\n"));
+ print_vdev_tree(hdl, NULL, missing, 2);
+ (void) printf("\n");
+ }
+ (void) zpool_standard_error(hdl, error, desc);
+ break;
+
+ case EEXIST:
+ (void) zpool_standard_error(hdl, error, desc);
+ break;
+
default:
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nvi);
- (void) zpool_standard_error(hdl, errno, desc);
+ (void) zpool_standard_error(hdl, error, desc);
zpool_explain_recover(hdl,
- newname ? origname : thename, -errno, nvi);
- nvlist_free(nvi);
+ newname ? origname : thename, -error, nv);
break;
}
+ nvlist_free(nv);
ret = -1;
} else {
zpool_handle_t *zhp;
ret = -1;
else if (zhp != NULL)
zpool_close(zhp);
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nvi);
- zpool_get_rewind_policy(config, &policy);
if (policy.zrp_request &
(ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
zpool_rewind_exclaim(hdl, newname ? origname : thename,
- ((policy.zrp_request & ZPOOL_TRY_REWIND) != 0),
- nvi);
+ ((policy.zrp_request & ZPOOL_TRY_REWIND) != 0), nv);
}
- nvlist_free(nvi);
+ nvlist_free(nv);
return (0);
}
int
zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_cookie = func;
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SCAN, &zc) == 0 ||
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0 ||
(errno == ENOENT && func != POOL_SCAN_NONE))
return (0);
}
/*
- * This provides a very minimal check whether a given string is likely a
- * c#t#d# style string. Users of this are expected to do their own
- * verification of the s# part.
- */
-#define CTD_CHECK(str) (str && str[0] == 'c' && isdigit(str[1]))
-
-/*
- * More elaborate version for ones which may start with "/dev/dsk/"
- * and the like.
- */
-static int
-ctd_check_path(char *str) {
- /*
- * If it starts with a slash, check the last component.
- */
- if (str && str[0] == '/') {
- char *tmp = strrchr(str, '/');
-
- /*
- * If it ends in "/old", check the second-to-last
- * component of the string instead.
- */
- if (tmp != str && strcmp(tmp, "/old") == 0) {
- for (tmp--; *tmp != '/'; tmp--)
- ;
- }
- str = tmp + 1;
- }
- return (CTD_CHECK(str));
-}
-
-/*
* Find a vdev that matches the search criteria specified. We use the
* the nvpair name to determine how we should look for the device.
* 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL
srchkey = nvpair_name(pair);
switch (nvpair_type(pair)) {
- case DATA_TYPE_UINT64: {
- uint64_t srchval, theguid, present;
-
- verify(nvpair_value_uint64(pair, &srchval) == 0);
+ case DATA_TYPE_UINT64:
if (strcmp(srchkey, ZPOOL_CONFIG_GUID) == 0) {
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
- &present) == 0) {
- /*
- * If the device has never been present since
- * import, the only reliable way to match the
- * vdev is by GUID.
- */
- verify(nvlist_lookup_uint64(nv,
- ZPOOL_CONFIG_GUID, &theguid) == 0);
- if (theguid == srchval)
- return (nv);
- }
+ uint64_t srchval, theguid;
+
+ verify(nvpair_value_uint64(pair, &srchval) == 0);
+ verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
+ &theguid) == 0);
+ if (theguid == srchval)
+ return (nv);
}
break;
- }
case DATA_TYPE_STRING: {
char *srchval, *val;
/*
* Search for the requested value. Special cases:
*
- * - ZPOOL_CONFIG_PATH for whole disk entries. These end in
- * "s0" or "s0/old". The "s0" part 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 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.
* - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE).
*
* Otherwise, all other searches are simple string compares.
*/
- if (strcmp(srchkey, ZPOOL_CONFIG_PATH) == 0 &&
- ctd_check_path(val)) {
+ if (strcmp(srchkey, ZPOOL_CONFIG_PATH) == 0) {
uint64_t wholedisk = 0;
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
&wholedisk);
if (wholedisk) {
- int slen = strlen(srchval);
- int vlen = strlen(val);
-
- if (slen != vlen - 2)
- break;
-
- /*
- * make_leaf_vdev() should only set
- * wholedisk for ZPOOL_CONFIG_PATHs which
- * will include "/dev/dsk/", giving plenty of
- * room for the indices used next.
- */
- ASSERT(vlen >= 6);
-
- /*
- * strings identical except trailing "s0"
- */
- if (strcmp(&val[vlen - 2], "s0") == 0 &&
- strncmp(srchval, val, slen) == 0)
- return (nv);
+ char buf[MAXPATHLEN];
- /*
- * strings identical except trailing "s0/old"
- */
- if (strcmp(&val[vlen - 6], "s0/old") == 0 &&
- strcmp(&srchval[slen - 4], "/old") == 0 &&
- strncmp(srchval, val, slen - 4) == 0)
+ zfs_append_partition(srchval, buf, sizeof (buf));
+ if (strcmp(val, buf) == 0)
return (nv);
break;
&nvroot) == 0);
*avail_spare = B_FALSE;
+ *l2cache = B_FALSE;
+ if (log != NULL)
+ *log = B_FALSE;
ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log);
nvlist_free(search);
} else if (zpool_vdev_is_interior(path)) {
verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
} else if (path[0] != '/') {
- (void) snprintf(buf, sizeof (buf), "%s%s", "/dev/dsk/", path);
+ 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);
* the disk to use the new unallocated space.
*/
static int
-zpool_relabel_disk(libzfs_handle_t *hdl, const char *name)
+zpool_relabel_disk(libzfs_handle_t *hdl, const char *path)
{
- char path[MAXPATHLEN];
char errbuf[1024];
int fd, error;
- int (*_efi_use_whole_disk)(int);
-
- if ((_efi_use_whole_disk = (int (*)(int))dlsym(RTLD_DEFAULT,
- "efi_use_whole_disk")) == NULL)
- return (-1);
-
- (void) snprintf(path, sizeof (path), "%s/%s", RDISK_ROOT, name);
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
+ if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to open device"), name);
+ "relabel '%s': unable to open device"), path);
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
}
* does not have any unallocated space left. If so, we simply
* ignore that error and continue on.
*/
- error = _efi_use_whole_disk(fd);
+ error = efi_use_whole_disk(fd);
(void) close(fd);
if (error && error != VT_ENOSPC) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to read disk capacity"), name);
+ "relabel '%s': unable to read disk capacity"), path);
return (zfs_error(hdl, EZFS_NOCAP, errbuf));
}
return (0);
zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
vdev_state_t *newstate)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
if (wholedisk) {
pathname += strlen(DISK_ROOT) + 1;
- (void) zpool_relabel_disk(zhp->zpool_hdl, pathname);
+ (void) zpool_relabel_disk(hdl, pathname);
}
}
zc.zc_cookie = VDEV_STATE_ONLINE;
zc.zc_obj = flags;
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0) {
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0) {
if (errno == EINVAL) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "was split "
"from this pool into a new one. Use '%s' "
int
zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache;
zc.zc_cookie = VDEV_STATE_OFFLINE;
zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0;
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
return (0);
switch (errno) {
int
zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot fault %llu"), guid);
+ dgettext(TEXT_DOMAIN, "cannot fault %llu"), (u_longlong_t)guid);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_guid = guid;
zc.zc_cookie = VDEV_STATE_FAULTED;
zc.zc_obj = aux;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
return (0);
switch (errno) {
int
zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot degrade %llu"), guid);
+ dgettext(TEXT_DOMAIN, "cannot degrade %llu"), (u_longlong_t)guid);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_guid = guid;
zc.zc_cookie = VDEV_STATE_DEGRADED;
zc.zc_obj = aux;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
return (0);
return (zpool_standard_error(hdl, errno, msg));
zpool_vdev_attach(zpool_handle_t *zhp,
const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
int ret;
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
uint64_t val;
- char *path, *newname;
+ char *newname;
nvlist_t **child;
uint_t children;
nvlist_t *config_root;
return (zfs_error(hdl, EZFS_BADTARGET, msg));
}
- /*
- * If we are attempting to replace a spare, it canot be applied to an
- * already spared device.
- */
- if (replacing &&
- nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 &&
- zpool_find_vdev(zhp, newname, &avail_spare,
- &l2cache, NULL) != NULL && avail_spare &&
- is_replacing_spare(config_root, tgt, 0)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "device has already been replaced with a spare"));
- free(newname);
- return (zfs_error(hdl, EZFS_BADTARGET, msg));
- }
-
free(newname);
if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
return (-1);
- ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_ATTACH, &zc);
+ ret = zfs_ioctl(hdl, ZFS_IOC_VDEV_ATTACH, &zc);
zcmd_free_nvlists(&zc);
if (ret == 0) {
if (rootpool) {
/*
- * XXX - This should be removed once we can
- * automatically install the bootblocks on the
- * newly attached disk.
- */
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Please "
- "be sure to invoke %s to make '%s' bootable.\n"),
- BOOTCMD, new_disk);
-
- /*
* XXX need a better way to prevent user from
* booting up a half-baked vdev.
*/
* Can't attach to or replace this type of vdev.
*/
if (replacing) {
+ uint64_t version = zpool_get_prop_int(zhp,
+ ZPOOL_PROP_VERSION, NULL);
+
if (islog)
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"cannot replace a log with a spare"));
+ else if (version >= SPA_VERSION_MULTI_REPLACE)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "already in replacing/spare config; wait "
+ "for completion or use 'zpool detach'"));
else
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"cannot replace a replacing device"));
int
zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache;
*/
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only "
"applicable to mirror and replacing vdevs"));
- (void) zfs_error(zhp->zpool_hdl, EZFS_BADTARGET, msg);
+ (void) zfs_error(hdl, EZFS_BADTARGET, msg);
break;
case EBUSY:
zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
nvlist_t *props, splitflags_t flags)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
nvlist_t *tree, *config, **child, **newchild, *newconfig = NULL;
nvlist_t **varray = NULL, *zc_props = NULL;
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &vers) == 0);
if (props) {
+ prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
if ((zc_props = zpool_valid_proplist(hdl, zhp->zpool_name,
- props, vers, B_TRUE, msg)) == NULL)
+ props, vers, flags, msg)) == NULL)
return (-1);
}
int
zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
int
zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
nvlist_t *tgt;
zpool_rewind_policy_t policy;
boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
nvlist_t *nvi = NULL;
+ int error;
if (path)
(void) snprintf(msg, sizeof (msg),
zpool_get_rewind_policy(rewindnvl, &policy);
zc.zc_cookie = policy.zrp_request;
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 8192) != 0)
+ if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size * 2) != 0)
return (-1);
- if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, rewindnvl) != 0)
+ if (zcmd_write_src_nvlist(hdl, &zc, rewindnvl) != 0)
return (-1);
- if (zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc) == 0 ||
- ((policy.zrp_request & ZPOOL_TRY_REWIND) &&
+ while ((error = zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc)) != 0 &&
+ errno == ENOMEM) {
+ if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ }
+
+ if (!error || ((policy.zrp_request & ZPOOL_TRY_REWIND) &&
errno != EPERM && errno != EACCES)) {
if (policy.zrp_request &
(ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
int
zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot clear errors for %llx"),
- guid);
+ (u_longlong_t)guid);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_guid = guid;
static void
set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
(void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
(void) strncpy(zc.zc_value, path, sizeof (zc.zc_value));
}
/*
+ * 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 "^[hs]d[a-z]+", i.e. a scsi or ide disk.
+ */
+static char *
+strip_partition(libzfs_handle_t *hdl, char *path)
+{
+ char *tmp = zfs_strdup(hdl, path);
+ char *part = NULL, *d = 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[1] == 'd') {
+ for (d = &tmp[2]; isalpha(*d); part = ++d);
+ }
+ if (part && d && *d != '\0') {
+ for (; isdigit(*d); d++);
+ if (*d == '\0')
+ *part = '\0';
+ }
+ return (tmp);
+}
+
+#define PATH_BUF_LEN 64
+
+/*
* Given a vdev, return the name to display in iostat. If the vdev has a path,
* we use that, stripping off any leading "/dev/dsk/"; if not, we use the type.
* We also check if this is a whole disk, in which case we strip off the
zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
boolean_t verbose)
{
- char *path, *devid;
+ char *path, *devid, *type;
uint64_t value;
- char buf[64];
+ char buf[PATH_BUF_LEN];
vdev_stat_t *vs;
uint_t vsc;
(u_longlong_t)value);
path = buf;
} else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
-
/*
* If the device is dead (faulted, offline, etc) then don't
* bother opening it. Otherwise we may be forcing the user to
devid_str_free(newdevid);
}
- if (strncmp(path, "/dev/dsk/", 9) == 0)
- path += 9;
+ /*
+ * For a block device only use the name.
+ */
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
+ if (strcmp(type, VDEV_TYPE_DISK) == 0) {
+ path = strrchr(path, '/');
+ path++;
+ }
+ /*
+ * Remove the partition from the path it this is a whole disk.
+ */
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
&value) == 0 && value) {
- int pathlen = strlen(path);
- char *tmp = zfs_strdup(hdl, path);
-
- /*
- * If it starts with c#, and ends with "s0", chop
- * the "s0" off, or if it ends with "s0/old", remove
- * the "s0" from the middle.
- */
- if (CTD_CHECK(tmp)) {
- if (strcmp(&tmp[pathlen - 2], "s0") == 0) {
- tmp[pathlen - 2] = '\0';
- } else if (pathlen > 6 &&
- strcmp(&tmp[pathlen - 6], "s0/old") == 0) {
- (void) strcpy(&tmp[pathlen - 6],
- "/old");
- }
- }
- return (tmp);
+ return strip_partition(hdl, path);
}
} else {
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
* If it's a raidz device, we need to stick in the parity level.
*/
if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) {
+ char tmpbuf[PATH_BUF_LEN];
+
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
&value) == 0);
- (void) snprintf(buf, sizeof (buf), "%s%llu", path,
+ (void) snprintf(tmpbuf, sizeof (tmpbuf), "%s%llu", path,
(u_longlong_t)value);
- path = buf;
+ path = tmpbuf;
}
/*
int
zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
uint64_t count;
zbookmark_t *zb = NULL;
int i;
int
zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strcpy(zc.zc_name, zhp->zpool_name);
static int
get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
return (err);
}
+/*
+ * Retrieve the next event. If there is a new event available 'nvp' will
+ * contain a newly allocated nvlist and 'dropped' will be set to the number
+ * of missed events since the last call to this function. When 'nvp' is
+ * set to NULL it indicates no new events are available. In either case
+ * the function returns 0 and it is up to the caller to free 'nvp'. In
+ * the case of a fatal error the function will return a non-zero value.
+ * When the function is called in blocking mode it will not return until
+ * a new event is available.
+ */
+int
+zpool_events_next(libzfs_handle_t *hdl, nvlist_t **nvp,
+ int *dropped, int block, int cleanup_fd)
+{
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ int error = 0;
+
+ *nvp = NULL;
+ *dropped = 0;
+ zc.zc_cleanup_fd = cleanup_fd;
+
+ if (!block)
+ zc.zc_guid = ZEVENT_NONBLOCK;
+
+ if (zcmd_alloc_dst_nvlist(hdl, &zc, ZEVENT_SIZE) != 0)
+ return (-1);
+
+retry:
+ if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_NEXT, &zc) != 0) {
+ switch (errno) {
+ case ESHUTDOWN:
+ error = zfs_error_fmt(hdl, EZFS_POOLUNAVAIL,
+ dgettext(TEXT_DOMAIN, "zfs shutdown"));
+ goto out;
+ case ENOENT:
+ /* Blocking error case should not occur */
+ if (block)
+ error = zpool_standard_error_fmt(hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot get event"));
+
+ goto out;
+ case ENOMEM:
+ if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+ error = zfs_error_fmt(hdl, EZFS_NOMEM,
+ dgettext(TEXT_DOMAIN, "cannot get event"));
+ goto out;
+ } else {
+ goto retry;
+ }
+ default:
+ error = zpool_standard_error_fmt(hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot get event"));
+ goto out;
+ }
+ }
+
+ error = zcmd_read_dst_nvlist(hdl, &zc, nvp);
+ if (error != 0)
+ goto out;
+
+ *dropped = (int)zc.zc_cookie;
+out:
+ zcmd_free_nvlists(&zc);
+
+ return (error);
+}
+
+/*
+ * Clear all events.
+ */
+int
+zpool_events_clear(libzfs_handle_t *hdl, int *count)
+{
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ char msg[1024];
+
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot clear events"));
+
+ if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_CLEAR, &zc) != 0)
+ return (zpool_standard_error_fmt(hdl, errno, msg));
+
+ if (count != NULL)
+ *count = (int)zc.zc_cookie; /* # of events cleared */
+
+ return (0);
+}
+
void
zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
char *pathname, size_t len)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
boolean_t mounted = B_FALSE;
char *mntpnt = NULL;
char dsname[MAXNAMELEN];
if (dsobj == 0) {
/* special case for the MOS */
- (void) snprintf(pathname, len, "<metadata>:<0x%llx>", obj);
+ (void) snprintf(pathname, len, "<metadata>:<0x%llx>", (longlong_t)obj);
return;
}
ZFS_IOC_DSOBJ_TO_DSNAME, &zc) != 0) {
/* just write out a path of two object numbers */
(void) snprintf(pathname, len, "<0x%llx>:<0x%llx>",
- dsobj, obj);
+ (longlong_t)dsobj, (longlong_t)obj);
return;
}
(void) strlcpy(dsname, zc.zc_value, sizeof (dsname));
dsname, zc.zc_value);
}
} else {
- (void) snprintf(pathname, len, "%s:<0x%llx>", dsname, obj);
+ (void) snprintf(pathname, len, "%s:<0x%llx>", dsname, (longlong_t)obj);
}
free(mntpnt);
}
(void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
strrchr(path, '/'));
- if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
+ if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) {
struct dk_gpt *vtoc;
if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
return (MAXOFFSET_T);
}
+int
+zpool_label_disk_wait(char *path, int timeout)
+{
+ struct stat64 statbuf;
+ int i;
+
+ /*
+ * Wait timeout miliseconds for a newly created device to be available
+ * from the given path. There is a small window when a /dev/ device
+ * will exist and the udev link will not, so we must wait for the
+ * symlink. Depending on the udev rules this may take a few seconds.
+ */
+ for (i = 0; i < timeout; i++) {
+ usleep(1000);
+
+ errno = 0;
+ if ((stat64(path, &statbuf) == 0) && (errno == 0))
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+int
+zpool_label_disk_check(char *path)
+{
+ struct dk_gpt *vtoc;
+ int fd, err;
+
+ if ((fd = open(path, O_RDWR|O_DIRECT)) < 0)
+ return errno;
+
+ if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
+ (void) close(fd);
+ return err;
+ }
+
+ if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
+ efi_free(vtoc);
+ (void) close(fd);
+ return EIDRM;
+ }
+
+ efi_free(vtoc);
+ (void) close(fd);
+ return 0;
+}
+
/*
* Label an individual disk. The name provided is the short name,
* stripped of any leading /dev path.
{
char path[MAXPATHLEN];
struct dk_gpt *vtoc;
- int fd;
+ int rval, fd;
size_t resv = EFI_MIN_RESV_SIZE;
uint64_t slice_size;
diskaddr_t start_block;
(void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name,
BACKUP_SLICE);
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
+ if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
/*
* This shouldn't happen. We've long since verified that this
* is a valid device.
*/
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "unable to open device"));
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "unable to open device '%s': %d"), path, errno);
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
}
vtoc->efi_parts[8].p_size = resv;
vtoc->efi_parts[8].p_tag = V_RESERVED;
- if (efi_write(fd, vtoc) != 0) {
+ if ((rval = efi_write(fd, vtoc)) != 0) {
/*
* Some block drivers (like pcata) may not support EFI
* GPT labels. Print out a helpful error message dir-
(void) close(fd);
efi_free(vtoc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "try using fdisk(1M) and then provide a specific slice"));
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "try using "
+ "parted(8) and then provide a specific slice: %d"), rval);
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
}
(void) close(fd);
efi_free(vtoc);
- return (0);
-}
-
-static boolean_t
-supported_dump_vdev_type(libzfs_handle_t *hdl, nvlist_t *config, char *errbuf)
-{
- char *type;
- nvlist_t **child;
- uint_t children, c;
-
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_TYPE, &type) == 0);
- if (strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
- strcmp(type, VDEV_TYPE_FILE) == 0 ||
- strcmp(type, VDEV_TYPE_LOG) == 0 ||
- strcmp(type, VDEV_TYPE_HOLE) == 0 ||
- strcmp(type, VDEV_TYPE_MISSING) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "vdev type '%s' is not supported"), type);
- (void) zfs_error(hdl, EZFS_VDEVNOTSUP, errbuf);
- return (B_FALSE);
- }
- if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if (!supported_dump_vdev_type(hdl, child[c], errbuf))
- return (B_FALSE);
- }
- }
- return (B_TRUE);
-}
-/*
- * check if this zvol is allowable for use as a dump device; zero if
- * it is, > 0 if it isn't, < 0 if it isn't a zvol
- */
-int
-zvol_check_dump_config(char *arg)
-{
- zpool_handle_t *zhp = NULL;
- nvlist_t *config, *nvroot;
- char *p, *volname;
- nvlist_t **top;
- uint_t toplevels;
- libzfs_handle_t *hdl;
- char errbuf[1024];
- char poolname[ZPOOL_MAXNAMELEN];
- int pathlen = strlen(ZVOL_FULL_DEV_DIR);
- int ret = 1;
-
- if (strncmp(arg, ZVOL_FULL_DEV_DIR, pathlen)) {
- return (-1);
- }
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "dump is not supported on device '%s'"), arg);
-
- if ((hdl = libzfs_init()) == NULL)
- return (1);
- libzfs_print_on_error(hdl, B_TRUE);
-
- volname = arg + pathlen;
-
- /* check the configuration of the pool */
- if ((p = strchr(volname, '/')) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "malformed dataset name"));
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- return (1);
- } else if (p - volname >= ZFS_MAXNAMELEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset name is too long"));
- (void) zfs_error(hdl, EZFS_NAMETOOLONG, errbuf);
- return (1);
- } else {
- (void) strncpy(poolname, volname, p - volname);
- poolname[p - volname] = '\0';
- }
-
- if ((zhp = zpool_open(hdl, poolname)) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not open pool '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
- goto out;
- }
- config = zpool_get_config(zhp, NULL);
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not obtain vdev configuration for '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf);
- goto out;
- }
-
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &top, &toplevels) == 0);
- if (toplevels != 1) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' has multiple top level vdevs"), poolname);
- (void) zfs_error(hdl, EZFS_DEVOVERFLOW, errbuf);
- goto out;
+ /* 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);
+ rval = zpool_label_disk_wait(path, 3000);
+ if (rval) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "
+ "detect device partitions on '%s': %d"), path, rval);
+ return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
}
- if (!supported_dump_vdev_type(hdl, top[0], errbuf)) {
- goto out;
+ /* We can't be to paranoid. Read the label back and verify it. */
+ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
+ rval = zpool_label_disk_check(path);
+ if (rval) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "freshly written "
+ "EFI label on '%s' is damaged. Ensure\nthis device "
+ "is not in in use, and is functioning properly: %d"),
+ path, rval);
+ return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
}
- ret = 0;
-out:
- if (zhp)
- zpool_close(zhp);
- libzfs_fini(hdl);
- return (ret);
+ return 0;
}