/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <ctype.h>
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_CACHEFILE:
+ case ZPOOL_PROP_COMMENT:
if (zhp->zpool_props != NULL ||
zpool_get_all_props(zhp) == 0) {
(void) strlcpy(buf,
case ZPOOL_PROP_SIZE:
case ZPOOL_PROP_ALLOCATED:
case ZPOOL_PROP_FREE:
+ case ZPOOL_PROP_ASHIFT:
(void) zfs_nicenum(intval, buf, len);
break;
zpool_prop_t prop;
char *strval;
uint64_t intval;
- char *slash;
+ char *slash, *check;
struct stat64 statbuf;
zpool_handle_t *zhp;
nvlist_t *nvroot;
}
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 > 13)) {
+ 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 (flags.create || flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+#if defined(__sun__) || defined(__sun)
/*
* bootfs property cannot be set on a disk which has
* been EFI labeled.
zpool_close(zhp);
goto error;
}
+#endif
zpool_close(zhp);
break;
*slash = '/';
break;
+ case ZPOOL_PROP_COMMENT:
+ for (check = strval; *check != '\0'; check++) {
+ if (!isprint(*check)) {
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "comment may only have printable "
+ "characters"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+ }
+ if (strlen(strval) > ZPROP_MAX_COMMENT) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "comment must not exceed %d characters"),
+ ZPROP_MAX_COMMENT);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
case ZPOOL_PROP_READONLY:
if (!flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
* Don't start the slice at the default block of 34; many storage
* 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.
+ * given 512B blocks. When the block size is larger by a power of 2
+ * we will still be 1m aligned. Some devices are sensitive to the
+ * partition ending alignment as well.
*/
-#define NEW_START_BLOCK 2048
+#define NEW_START_BLOCK 2048
+#define PARTITION_END_ALIGNMENT 2048
/*
* Validate the given pool name, optionally putting an extended error message in
(void) zpool_standard_error(hdl, error, desc);
break;
+ case EBUSY:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "one or more devices are already in use\n"));
+ (void) zfs_error(hdl, EZFS_BADDEV, desc);
+ break;
+
default:
(void) zpool_standard_error(hdl, error, desc);
zpool_explain_recover(hdl,
}
/*
- * 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
/*
* 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;
} 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", DISK_ROOT, 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 *path)
+zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
{
- char errbuf[1024];
int fd, error;
if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to open device"), path);
- return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
+ "relabel '%s': unable to open device: %d"), path, errno);
+ return (zfs_error(hdl, EZFS_OPENFAILED, msg));
}
/*
* It's possible that we might encounter an error if the device
* does not have any unallocated space left. If so, we simply
* ignore that error and continue on.
+ *
+ * Also, we don't call efi_rescan() - that would just return EBUSY.
+ * The module will do it for us in vdev_disk_open().
*/
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"), path);
- return (zfs_error(hdl, EZFS_NOCAP, errbuf));
+ return (zfs_error(hdl, EZFS_NOCAP, msg));
}
return (0);
}
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
libzfs_handle_t *hdl = zhp->zpool_hdl;
+ int error;
if (flags & ZFS_ONLINE_EXPAND) {
(void) snprintf(msg, sizeof (msg),
if (flags & ZFS_ONLINE_EXPAND ||
zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) {
- char *pathname = NULL;
uint64_t wholedisk = 0;
(void) nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
&wholedisk);
- verify(nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH,
- &pathname) == 0);
/*
* XXX - L2ARC 1.0 devices can't support expansion.
}
if (wholedisk) {
- pathname += strlen(DISK_ROOT) + 1;
- (void) zpool_relabel_disk(hdl, pathname);
+ const char *fullpath = path;
+ char buf[MAXPATHLEN];
+
+ if (path[0] != '/') {
+ error = zfs_resolve_shortname(path, buf,
+ sizeof(buf));
+ if (error != 0)
+ return (zfs_error(hdl, EZFS_NODEVICE,
+ msg));
+
+ fullpath = buf;
+ }
+
+ error = zpool_relabel_disk(hdl, fullpath, msg);
+ if (error != 0)
+ return (error);
}
}
}
/*
+ * Change the GUID for a pool.
+ */
+int
+zpool_reguid(zpool_handle_t *zhp)
+{
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
+ return (0);
+
+ return (zpool_standard_error(hdl, errno, msg));
+}
+
+/*
* Convert from a devid string to a path.
*/
static char *
(void) ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SETPATH, &zc);
}
+/*
+ * 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
/*
char *path, *devid, *type;
uint64_t value;
char buf[PATH_BUF_LEN];
+ char tmpbuf[PATH_BUF_LEN];
vdev_stat_t *vs;
uint_t vsc;
path++;
}
-#if defined(__sun__) || defined(__sun)
/*
- * The following code strips the slice from the device 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);
}
-#endif
} 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(tmpbuf, sizeof (tmpbuf), "%s%llu", path,
+ (void) snprintf(buf, sizeof (buf), "%s%llu", path,
(u_longlong_t)value);
- path = tmpbuf;
+ path = buf;
}
/*
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
&id) == 0);
- (void) snprintf(buf, sizeof (buf), "%s-%llu", path,
- (u_longlong_t)id);
- path = buf;
+ (void) snprintf(tmpbuf, sizeof (tmpbuf), "%s-%llu",
+ path, (u_longlong_t)id);
+ path = tmpbuf;
}
}
* 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 '%s': %d"), path, errno);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
+ "label '%s': unable to open device: %d"), path, errno);
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
}
(void) no_memory(hdl);
(void) close(fd);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "unable to read disk capacity"), name);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
+ "label '%s': unable to read disk capacity"), path);
return (zfs_error(hdl, EZFS_NOCAP, errbuf));
}
if (start_block == MAXOFFSET_T)
start_block = NEW_START_BLOCK;
slice_size -= start_block;
+ slice_size = P2ALIGN(slice_size, PARTITION_END_ALIGNMENT);
vtoc->efi_parts[0].p_start = start_block;
vtoc->efi_parts[0].p_size = slice_size;
vtoc->efi_parts[8].p_size = resv;
vtoc->efi_parts[8].p_tag = V_RESERVED;
- if ((rval = efi_write(fd, vtoc)) != 0) {
+ if ((rval = efi_write(fd, vtoc)) != 0 || (rval = efi_rescan(fd)) != 0) {
/*
* Some block drivers (like pcata) may not support EFI
* GPT labels. Print out a helpful error message dir-