*/
/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
*/
#include <ctype.h>
return (gettext("UNKNOWN"));
}
-/*
- * API compatibility wrapper around zpool_get_prop_literal
- */
-int
-zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
- zprop_source_t *srctype)
-{
- return zpool_get_prop_literal(zhp, prop, buf, len, srctype, B_FALSE);
-}
-
/*
* Get a zpool property value for 'prop' and return the value in
* a pre-allocated buffer.
*/
int
-zpool_get_prop_literal(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
- zprop_source_t *srctype, boolean_t literal)
+zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
+ size_t len, zprop_source_t *srctype, boolean_t literal)
{
uint64_t intval;
const char *strval;
(void) strlcpy(buf,
zpool_get_prop_string(zhp, prop, &src),
len);
- if (srctype != NULL)
- *srctype = src;
- return (0);
+ break;
}
/* FALLTHROUGH */
default:
case ZPOOL_PROP_ALLOCATED:
case ZPOOL_PROP_FREE:
case ZPOOL_PROP_FREEING:
- case ZPOOL_PROP_EXPANDSZ:
+ case ZPOOL_PROP_LEAKED:
case ZPOOL_PROP_ASHIFT:
if (literal)
(void) snprintf(buf, len, "%llu",
(void) zfs_nicenum(intval, buf, len);
break;
+ case ZPOOL_PROP_EXPANDSZ:
+ if (intval == 0) {
+ (void) strlcpy(buf, "-", len);
+ } else if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) zfs_nicenum(intval, buf, len);
+ }
+ break;
+
case ZPOOL_PROP_CAPACITY:
- (void) snprintf(buf, len, "%llu%%",
- (u_longlong_t)intval);
+ if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) snprintf(buf, len, "%llu%%",
+ (u_longlong_t)intval);
+ }
+ break;
+
+ case ZPOOL_PROP_FRAGMENTATION:
+ if (intval == UINT64_MAX) {
+ (void) strlcpy(buf, "-", len);
+ } else if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) snprintf(buf, len, "%llu%%",
+ (u_longlong_t)intval);
+ }
break;
case ZPOOL_PROP_DEDUPRATIO:
- (void) snprintf(buf, len, "%llu.%02llux",
- (u_longlong_t)(intval / 100),
- (u_longlong_t)(intval % 100));
+ if (literal)
+ (void) snprintf(buf, len, "%llu.%02llu",
+ (u_longlong_t)(intval / 100),
+ (u_longlong_t)(intval % 100));
+ else
+ (void) snprintf(buf, len, "%llu.%02llux",
+ (u_longlong_t)(intval / 100),
+ (u_longlong_t)(intval % 100));
break;
case ZPOOL_PROP_HEALTH:
boolean_t
zpool_is_bootable(zpool_handle_t *zhp)
{
- char bootfs[ZPOOL_MAXNAMELEN];
+ char bootfs[ZFS_MAX_DATASET_NAME_LEN];
return (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
- sizeof (bootfs), NULL) == 0 && strncmp(bootfs, "-",
+ sizeof (bootfs), NULL, B_FALSE) == 0 && strncmp(bootfs, "-",
sizeof (bootfs)) != 0);
}
prop = zpool_name_to_prop(propname);
if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
int err;
- zfeature_info_t *feature;
char *fname = strchr(propname, '@') + 1;
- err = zfeature_lookup_name(fname, &feature);
+ err = zfeature_lookup_name(fname, NULL);
if (err != 0) {
ASSERT3U(err, ==, ENOENT);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
goto error;
}
break;
+ case ZPOOL_PROP_TNAME:
+ 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;
+ }
+ break;
}
}
if (entry->pl_prop != ZPROP_INVAL &&
zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf),
- NULL) == 0) {
+ NULL, B_FALSE) == 0) {
if (strlen(buf) > entry->pl_width)
entry->pl_width = strlen(buf);
}
*/
if (supported) {
int ret;
- zfeature_info_t *fi;
+ spa_feature_t fid;
- ret = zfeature_lookup_name(feature, &fi);
+ ret = zfeature_lookup_name(feature, &fid);
if (ret != 0) {
(void) strlcpy(buf, "-", len);
return (ENOTSUP);
}
- feature = fi->fi_guid;
+ feature = spa_feature_table[fid].fi_guid;
}
if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
void
zpool_close(zpool_handle_t *zhp)
{
- if (zhp->zpool_config)
- nvlist_free(zhp->zpool_config);
- if (zhp->zpool_old_config)
- nvlist_free(zhp->zpool_old_config);
- if (zhp->zpool_props)
- nvlist_free(zhp->zpool_props);
+ nvlist_free(zhp->zpool_config);
+ nvlist_free(zhp->zpool_old_config);
+ nvlist_free(zhp->zpool_props);
free(zhp);
}
zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) &&
strcmp(zonestr, "on") == 0);
- if ((zc_fsprops = zfs_valid_proplist(hdl,
- ZFS_TYPE_FILESYSTEM, fsprops, zoned, NULL, msg)) == NULL) {
+ if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
+ fsprops, zoned, NULL, NULL, msg)) == NULL) {
goto create_failed;
}
if (!zc_props &&
* part of an active md or lvm device.
*/
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more vdevs refer to the same device, or one of\n"
- "the devices is part of an active md or lvm device"));
+ "one or more vdevs refer to the same device, or "
+ "one of\nthe devices is part of an active md or "
+ "lvm device"));
return (zfs_error(hdl, EZFS_BADDEV, msg));
+ case ERANGE:
+ /*
+ * This happens if the record size is smaller or larger
+ * than the allowed size range, or not a power of 2.
+ *
+ * NOTE: although zfs_valid_proplist is called earlier,
+ * this case may have slipped through since the
+ * pool does not exist yet and it is therefore
+ * impossible to read properties e.g. max blocksize
+ * from the pool.
+ */
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "record size invalid"));
+ return (zfs_error(hdl, EZFS_BADPROP, msg));
+
case EOVERFLOW:
/*
* This occurs when one of the devices is below
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"device '%s' contains an EFI label and "
"cannot be used on root pools."),
- zpool_vdev_name(hdl, NULL, spares[s],
- B_FALSE));
+ zpool_vdev_name(hdl, NULL, spares[s], 0));
return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg));
}
}
ret = zpool_import_props(hdl, config, newname, props,
ZFS_IMPORT_NORMAL);
- if (props)
- nvlist_free(props);
+ nvlist_free(props);
return (ret);
}
return;
for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(hdl, NULL, child[c], B_TRUE);
+ vname = zpool_vdev_name(hdl, NULL, child[c], VDEV_NAME_TYPE_ID);
print_vdev_tree(hdl, vname, child[c], indent + 2);
free(vname);
}
thename = origname;
}
- if (props) {
+ if (props != NULL) {
uint64_t version;
prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
&version) == 0);
if ((props = zpool_valid_proplist(hdl, origname,
- props, version, flags, errbuf)) == NULL) {
+ props, version, flags, errbuf)) == NULL)
return (-1);
- } else if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
+ if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
nvlist_free(props);
return (-1);
}
+ nvlist_free(props);
}
(void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
&zc.zc_guid) == 0);
if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) {
- nvlist_free(props);
+ zcmd_free_nvlists(&zc);
return (-1);
}
if (zcmd_alloc_dst_nvlist(hdl, &zc, zc.zc_nvlist_conf_size * 2) != 0) {
- nvlist_free(props);
+ zcmd_free_nvlists(&zc);
return (-1);
}
error = errno;
(void) zcmd_read_dst_nvlist(hdl, &zc, &nv);
+
+ zcmd_free_nvlists(&zc);
+
zpool_get_rewind_policy(config, &policy);
if (error) {
"one or more devices are already in use\n"));
(void) zfs_error(hdl, EZFS_BADDEV, desc);
break;
-
+ case ENAMETOOLONG:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "new name of at least one dataset is longer than "
+ "the maximum allowable length"));
+ (void) zfs_error(hdl, EZFS_NAMETOOLONG, desc);
+ break;
default:
(void) zpool_standard_error(hdl, error, desc);
zpool_explain_recover(hdl,
return (0);
}
- zcmd_free_nvlists(&zc);
- nvlist_free(props);
-
return (ret);
}
verify(nvlist_alloc(&search, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- guid = strtoull(path, &end, 10);
+ guid = strtoull(path, &end, 0);
if (guid != 0 && *end == '\0') {
verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0);
} else if (zpool_vdev_is_interior(path)) {
if (path[0] != '/') {
error = zfs_resolve_shortname(path, buf,
- sizeof(buf));
+ sizeof (buf));
if (error != 0)
return (zfs_error(hdl, EZFS_NODEVICE,
msg));
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot fault %llu"), (u_longlong_t)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;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot degrade %llu"), (u_longlong_t)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;
verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0);
- if ((newname = zpool_vdev_name(NULL, NULL, child[0], B_FALSE)) == NULL)
+ if ((newname = zpool_vdev_name(NULL, NULL, child[0], 0)) == NULL)
return (-1);
/*
case EDOM:
/*
- * The new device has a different alignment requirement.
+ * The new device has a different optimal sector size.
*/
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "devices have different sector alignment"));
+ "new device has a different optimal sector size; use the "
+ "option '-o ashift=N' to override the optimal size"));
(void) zfs_error(hdl, EZFS_BADDEV, msg);
break;
for (mc = 0; mc < mchildren; mc++) {
uint_t sc;
char *mpath = zpool_vdev_name(zhp->zpool_hdl, zhp,
- mchild[mc], B_FALSE);
+ mchild[mc], 0);
for (sc = 0; sc < schildren; sc++) {
char *spath = zpool_vdev_name(zhp->zpool_hdl, zhp,
- schild[sc], B_FALSE);
+ schild[sc], 0);
boolean_t result = (strcmp(mpath, spath) == 0);
free(spath);
&children) != 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"Source pool is missing vdev tree"));
- if (zc_props)
- nvlist_free(zc_props);
+ nvlist_free(zc_props);
return (-1);
}
free(varray);
}
zcmd_free_nvlists(&zc);
- if (zc_props)
- nvlist_free(zc_props);
- if (newconfig)
- nvlist_free(newconfig);
+ nvlist_free(zc_props);
+ nvlist_free(newconfig);
if (freelist) {
nvlist_free(*newroot);
*newroot = NULL;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot clear errors for %llx"),
- (u_longlong_t)guid);
+ (u_longlong_t)guid);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_guid = guid;
return (zpool_standard_error(hdl, errno, msg));
}
+#if defined(__sun__) || defined(__sun)
/*
* Convert from a devid string to a path.
*/
if (ret != 0)
return (NULL);
- if ((path = strdup(list[0].devname)) == NULL)
- return (NULL);
+ /*
+ * In a case the strdup() fails, we will just return NULL below.
+ */
+ path = strdup(list[0].devname);
devid_free_nmlist(list);
(void) ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SETPATH, &zc);
}
+#endif /* sun */
/*
* 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.
+ * expression "^([hsv]|xv)d[a-z]+", i.e. a scsi, ide, virtio or xen disk.
*/
static char *
strip_partition(libzfs_handle_t *hdl, char *path)
} 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') {
+ } else if ((tmp[0] == 'h' || tmp[0] == 's' || tmp[0] == 'v') &&
+ tmp[1] == 'd') {
for (d = &tmp[2]; isalpha(*d); part = ++d);
+ } else if (strncmp("xvd", tmp, 3) == 0) {
+ for (d = &tmp[3]; isalpha(*d); part = ++d);
}
if (part && d && *d != '\0') {
for (; isdigit(*d); d++);
*/
char *
zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
- boolean_t verbose)
+ int name_flags)
{
- char *path, *devid, *type;
+ char *path, *type, *env;
uint64_t value;
char buf[PATH_BUF_LEN];
char tmpbuf[PATH_BUF_LEN];
- vdev_stat_t *vs;
- uint_t vsc;
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
- &value) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
- &value) == 0);
- (void) snprintf(buf, sizeof (buf), "%llu",
- (u_longlong_t)value);
+ env = getenv("ZPOOL_VDEV_NAME_PATH");
+ if (env && (strtoul(env, NULL, 0) > 0 ||
+ !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
+ name_flags |= VDEV_NAME_PATH;
+
+ env = getenv("ZPOOL_VDEV_NAME_GUID");
+ if (env && (strtoul(env, NULL, 0) > 0 ||
+ !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
+ name_flags |= VDEV_NAME_GUID;
+
+ env = getenv("ZPOOL_VDEV_NAME_FOLLOW_LINKS");
+ if (env && (strtoul(env, NULL, 0) > 0 ||
+ !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
+ name_flags |= VDEV_NAME_FOLLOW_LINKS;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &value) == 0 ||
+ name_flags & VDEV_NAME_GUID) {
+ nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value);
+ (void) snprintf(buf, sizeof (buf), "%llu", (u_longlong_t)value);
path = buf;
} else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
+#if defined(__sun__) || defined(__sun)
+ /*
+ * Live VDEV path updates to a kernel VDEV during a
+ * zpool_vdev_name lookup are not supported on Linux.
+ */
+ char *devid;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
/*
* If the device is dead (faulted, offline, etc) then don't
* bother opening it. Otherwise we may be forcing the user to
if (newdevid)
devid_str_free(newdevid);
}
+#endif /* sun */
+
+ if (name_flags & VDEV_NAME_FOLLOW_LINKS) {
+ char *rp = realpath(path, NULL);
+ if (rp) {
+ strlcpy(buf, rp, sizeof (buf));
+ path = buf;
+ free(rp);
+ }
+ }
/*
* 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) {
+ if ((strcmp(type, VDEV_TYPE_DISK) == 0) &&
+ !(name_flags & VDEV_NAME_PATH)) {
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) {
- return strip_partition(hdl, path);
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value)
+ == 0 && value && !(name_flags & VDEV_NAME_PATH)) {
+ 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) {
-
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
&value) == 0);
(void) snprintf(buf, sizeof (buf), "%s%llu", path,
* We identify each top-level vdev by using a <type-id>
* naming convention.
*/
- if (verbose) {
+ if (name_flags & VDEV_NAME_TYPE_ID) {
uint64_t id;
-
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
&id) == 0);
(void) snprintf(tmpbuf, sizeof (tmpbuf), "%s-%llu",
}
static int
-zbookmark_compare(const void *a, const void *b)
+zbookmark_mem_compare(const void *a, const void *b)
{
- return (memcmp(a, b, sizeof (zbookmark_t)));
+ return (memcmp(a, b, sizeof (zbookmark_phys_t)));
}
/*
{
zfs_cmd_t zc = {"\0"};
uint64_t count;
- zbookmark_t *zb = NULL;
+ zbookmark_phys_t *zb = NULL;
int i;
/*
if (count == 0)
return (0);
if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
- count * sizeof (zbookmark_t))) == (uintptr_t)NULL)
+ count * sizeof (zbookmark_phys_t))) == (uintptr_t)NULL)
return (-1);
zc.zc_nvlist_dst_size = count;
(void) strcpy(zc.zc_name, zhp->zpool_name);
&zc) != 0) {
free((void *)(uintptr_t)zc.zc_nvlist_dst);
if (errno == ENOMEM) {
+ void *dst;
+
count = zc.zc_nvlist_dst_size;
- if ((zc.zc_nvlist_dst = (uintptr_t)
- zfs_alloc(zhp->zpool_hdl, count *
- sizeof (zbookmark_t))) == (uintptr_t)NULL)
+ dst = zfs_alloc(zhp->zpool_hdl, count *
+ sizeof (zbookmark_phys_t));
+ if (dst == NULL)
return (-1);
+ zc.zc_nvlist_dst = (uintptr_t)dst;
} else {
return (-1);
}
* _not_ copied as part of the process. So we point the start of our
* array appropriate and decrement the total number of elements.
*/
- zb = ((zbookmark_t *)(uintptr_t)zc.zc_nvlist_dst) +
+ zb = ((zbookmark_phys_t *)(uintptr_t)zc.zc_nvlist_dst) +
zc.zc_nvlist_dst_size;
count -= zc.zc_nvlist_dst_size;
- qsort(zb, count, sizeof (zbookmark_t), zbookmark_compare);
+ qsort(zb, count, sizeof (zbookmark_phys_t), zbookmark_mem_compare);
verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0);
return (0);
}
-#define HIS_BUF_LEN (128*1024)
-
/*
* Retrieve the command history of a pool.
*/
int
zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
{
- char buf[HIS_BUF_LEN];
+ char *buf;
+ int buflen = 128 * 1024;
uint64_t off = 0;
nvlist_t **records = NULL;
uint_t numrecords = 0;
int err, i;
+ buf = malloc(buflen);
+ if (buf == NULL)
+ return (ENOMEM);
do {
- uint64_t bytes_read = sizeof (buf);
+ uint64_t bytes_read = buflen;
uint64_t leftover;
if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0)
&leftover, &records, &numrecords)) != 0)
break;
off -= leftover;
+ if (leftover == bytes_read) {
+ /*
+ * no progress made, because buffer is not big enough
+ * to hold this record; resize and retry.
+ */
+ buflen *= 2;
+ free(buf);
+ buf = malloc(buflen);
+ if (buf == NULL)
+ return (ENOMEM);
+ }
/* CONSTCOND */
} while (1);
+ free(buf);
+
if (!err) {
verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
verify(nvlist_add_nvlist_array(*nvhisp, ZPOOL_HIST_RECORD,
}
/*
- * 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.
+ * Retrieve the next event given the passed 'zevent_fd' file descriptor.
+ * 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 (the default, unless the ZEVENT_NONBLOCK flag is passed),
+ * 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)
+ int *dropped, unsigned flags, int zevent_fd)
{
zfs_cmd_t zc = {"\0"};
int error = 0;
*nvp = NULL;
*dropped = 0;
- zc.zc_cleanup_fd = cleanup_fd;
+ zc.zc_cleanup_fd = zevent_fd;
- if (!block)
+ if (flags & ZEVENT_NONBLOCK)
zc.zc_guid = ZEVENT_NONBLOCK;
if (zcmd_alloc_dst_nvlist(hdl, &zc, ZEVENT_SIZE) != 0)
goto out;
case ENOENT:
/* Blocking error case should not occur */
- if (block)
+ if (!(flags & ZEVENT_NONBLOCK))
error = zpool_standard_error_fmt(hdl, errno,
dgettext(TEXT_DOMAIN, "cannot get event"));
return (0);
}
+/*
+ * Seek to a specific EID, ZEVENT_SEEK_START, or ZEVENT_SEEK_END for
+ * the passed zevent_fd file handle. On success zero is returned,
+ * otherwise -1 is returned and hdl->libzfs_error is set to the errno.
+ */
+int
+zpool_events_seek(libzfs_handle_t *hdl, uint64_t eid, int zevent_fd)
+{
+ zfs_cmd_t zc = {"\0"};
+ int error = 0;
+
+ zc.zc_guid = eid;
+ zc.zc_cleanup_fd = zevent_fd;
+
+ if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_SEEK, &zc) != 0) {
+ switch (errno) {
+ case ENOENT:
+ error = zfs_error_fmt(hdl, EZFS_NOENT,
+ dgettext(TEXT_DOMAIN, "cannot get event"));
+ break;
+
+ case ENOMEM:
+ error = zfs_error_fmt(hdl, EZFS_NOMEM,
+ dgettext(TEXT_DOMAIN, "cannot get event"));
+ break;
+
+ default:
+ error = zpool_standard_error_fmt(hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot get event"));
+ break;
+ }
+ }
+
+ return (error);
+}
+
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"};
boolean_t mounted = B_FALSE;
char *mntpnt = NULL;
- char dsname[MAXNAMELEN];
+ char dsname[ZFS_MAX_DATASET_NAME_LEN];
if (dsobj == 0) {
/* special case for the MOS */
- (void) snprintf(pathname, len, "<metadata>:<0x%llx>", (longlong_t)obj);
+ (void) snprintf(pathname, len, "<metadata>:<0x%llx>",
+ (longlong_t)obj);
return;
}
dsname, zc.zc_value);
}
} else {
- (void) snprintf(pathname, len, "%s:<0x%llx>", dsname, (longlong_t)obj);
+ (void) snprintf(pathname, len, "%s:<0x%llx>", dsname,
+ (longlong_t)obj);
}
free(mntpnt);
}
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
+static 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;
+ return (errno);
if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
(void) close(fd);
- return err;
+ return (err);
}
if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
efi_free(vtoc);
(void) close(fd);
- return EIDRM;
+ return (EIDRM);
}
efi_free(vtoc);
(void) close(fd);
- return 0;
+ return (0);
+}
+
+/*
+ * Generate a unique partition name for the ZFS member. Partitions must
+ * have unique names to ensure udev will be able to create symlinks under
+ * /dev/disk/by-partlabel/ for all pool members. The partition names are
+ * of the form <pool>-<unique-id>.
+ */
+static void
+zpool_label_name(char *label_name, int label_size)
+{
+ uint64_t id = 0;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd > 0) {
+ if (read(fd, &id, sizeof (id)) != sizeof (id))
+ id = 0;
+
+ close(fd);
+ }
+
+ if (id == 0)
+ id = (((uint64_t)rand()) << 32) | (uint64_t)rand();
+
+ snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t) id);
}
/*
* can get, in the absence of V_OTHER.
*/
vtoc->efi_parts[0].p_tag = V_USR;
- (void) strcpy(vtoc->efi_parts[0].p_name, "zfs");
+ zpool_label_name(vtoc->efi_parts[0].p_name, EFI_PART_NAME_LEN);
vtoc->efi_parts[8].p_start = slice_size + start_block;
vtoc->efi_parts[8].p_size = resv;
(void) close(fd);
efi_free(vtoc);
- /* 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);
+ /* Wait to udev to signal use the device has settled. */
+ rval = zpool_label_disk_wait(path, DISK_LABEL_WAIT);
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));
}
- return 0;
+ return (0);
}