*
* CDDL HEADER END
*/
+
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
#include <unistd.h>
#include <ctype.h>
#include <math.h>
+#include <sys/stat.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/types.h>
+#include <wait.h>
#include <libzfs.h>
+#include <libzfs_core.h>
#include "libzfs_impl.h"
#include "zfs_prop.h"
+#include "zfeature_common.h"
int
libzfs_errno(libzfs_handle_t *hdl)
case EZFS_BADPROP:
return (dgettext(TEXT_DOMAIN, "invalid property value"));
case EZFS_PROPREADONLY:
- return (dgettext(TEXT_DOMAIN, "read only property"));
+ return (dgettext(TEXT_DOMAIN, "read-only property"));
case EZFS_PROPTYPE:
return (dgettext(TEXT_DOMAIN, "property doesn't apply to "
"datasets of this type"));
case EZFS_BADSTREAM:
return (dgettext(TEXT_DOMAIN, "invalid backup stream"));
case EZFS_DSREADONLY:
- return (dgettext(TEXT_DOMAIN, "dataset is read only"));
+ return (dgettext(TEXT_DOMAIN, "dataset is read-only"));
case EZFS_VOLTOOBIG:
return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for "
"this system"));
- case EZFS_VOLHASDATA:
- return (dgettext(TEXT_DOMAIN, "volume has data"));
case EZFS_INVALIDNAME:
return (dgettext(TEXT_DOMAIN, "invalid name"));
case EZFS_BADRESTORE:
case EZFS_RESILVERING:
return (dgettext(TEXT_DOMAIN, "currently resilvering"));
case EZFS_BADVERSION:
- return (dgettext(TEXT_DOMAIN, "unsupported version"));
+ return (dgettext(TEXT_DOMAIN, "unsupported version or "
+ "feature"));
case EZFS_POOLUNAVAIL:
return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
case EZFS_DEVOVERFLOW:
return (dgettext(TEXT_DOMAIN, "smb remove share failed"));
case EZFS_SHARESMBFAILED:
return (dgettext(TEXT_DOMAIN, "smb add share failed"));
- case EZFS_ISCSISVCUNAVAIL:
- return (dgettext(TEXT_DOMAIN,
- "iscsitgt service need to be enabled by "
- "a privileged user"));
- case EZFS_DEVLINKS:
- return (dgettext(TEXT_DOMAIN, "failed to create /dev links"));
case EZFS_PERM:
return (dgettext(TEXT_DOMAIN, "permission denied"));
case EZFS_NOSPC:
return (dgettext(TEXT_DOMAIN, "out of space"));
+ case EZFS_FAULT:
+ return (dgettext(TEXT_DOMAIN, "bad address"));
case EZFS_IO:
return (dgettext(TEXT_DOMAIN, "I/O error"));
case EZFS_INTR:
return (dgettext(TEXT_DOMAIN, "recursive dataset dependency"));
case EZFS_NOHISTORY:
return (dgettext(TEXT_DOMAIN, "no history available"));
- case EZFS_UNSHAREISCSIFAILED:
- return (dgettext(TEXT_DOMAIN,
- "iscsitgtd failed request to unshare"));
- case EZFS_SHAREISCSIFAILED:
- return (dgettext(TEXT_DOMAIN,
- "iscsitgtd failed request to share"));
case EZFS_POOLPROPS:
return (dgettext(TEXT_DOMAIN, "failed to retrieve "
"pool properties"));
case EZFS_NODELEGATION:
return (dgettext(TEXT_DOMAIN, "delegated administration is "
"disabled on pool"));
- case EZFS_PERMRDONLY:
- return (dgettext(TEXT_DOMAIN, "snapshot permissions cannot be"
- " modified"));
case EZFS_BADCACHE:
return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
case EZFS_ISL2CACHE:
case EZFS_ACTIVE_SPARE:
return (dgettext(TEXT_DOMAIN, "pool has active shared spare "
"device"));
+ case EZFS_UNPLAYED_LOGS:
+ return (dgettext(TEXT_DOMAIN, "log device has unplayed intent "
+ "logs"));
+ case EZFS_REFTAG_RELE:
+ return (dgettext(TEXT_DOMAIN, "no such tag on this dataset"));
+ case EZFS_REFTAG_HOLD:
+ return (dgettext(TEXT_DOMAIN, "tag already exists on this "
+ "dataset"));
+ case EZFS_TAGTOOLONG:
+ return (dgettext(TEXT_DOMAIN, "tag too long"));
+ case EZFS_PIPEFAILED:
+ return (dgettext(TEXT_DOMAIN, "pipe create failed"));
+ case EZFS_THREADCREATEFAILED:
+ return (dgettext(TEXT_DOMAIN, "thread create failed"));
+ case EZFS_POSTSPLIT_ONLINE:
+ return (dgettext(TEXT_DOMAIN, "disk was split from this pool "
+ "into a new one"));
+ case EZFS_SCRUBBING:
+ return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
+ "use 'zpool scrub -s' to cancel current scrub"));
+ case EZFS_NO_SCRUB:
+ return (dgettext(TEXT_DOMAIN, "there is no active scrub"));
+ case EZFS_DIFF:
+ return (dgettext(TEXT_DOMAIN, "unable to generate diffs"));
+ case EZFS_DIFFDATA:
+ return (dgettext(TEXT_DOMAIN, "invalid diff data"));
+ case EZFS_POOLREADONLY:
+ return (dgettext(TEXT_DOMAIN, "pool is read-only"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
zfs_verror(hdl, EZFS_IO, fmt, ap);
return (-1);
+ case EFAULT:
+ zfs_verror(hdl, EZFS_FAULT, fmt, ap);
+ return (-1);
+
case EINTR:
zfs_verror(hdl, EZFS_INTR, fmt, ap);
return (-1);
switch (error) {
case ENXIO:
case ENODEV:
+ case EPIPE:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
zfs_verror(hdl, EZFS_BUSY, fmt, ap);
break;
case EROFS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot permissions cannot be modified"));
- zfs_verror(hdl, EZFS_PERMRDONLY, fmt, ap);
+ zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
break;
case ENAMETOOLONG:
zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
case ENOTSUP:
zfs_verror(hdl, EZFS_BADVERSION, fmt, ap);
break;
+ case EAGAIN:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool I/O is currently suspended"));
+ zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
+ break;
default:
- zfs_error_aux(hdl, strerror(errno));
+ zfs_error_aux(hdl, strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
break;
}
zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
return (-1);
+ case EAGAIN:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool I/O is currently suspended"));
+ zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
+ break;
+
+ case EROFS:
+ zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
+ break;
+
default:
zfs_error_aux(hdl, strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
return (data);
}
+/*
+ * A safe form of asprintf() which will die if the allocation fails.
+ */
+/*PRINTFLIKE2*/
+char *
+zfs_asprintf(libzfs_handle_t *hdl, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+ int err;
+
+ va_start(ap, fmt);
+
+ err = vasprintf(&ret, fmt, ap);
+
+ va_end(ap);
+
+ if (err < 0)
+ (void) no_memory(hdl);
+
+ return (ret);
+}
+
/*
* A safe form of realloc(), which also zeroes newly allocated space.
*/
if ((ret = realloc(ptr, newsize)) == NULL) {
(void) no_memory(hdl);
- free(ptr);
return (NULL);
}
u = " KMGTPE"[index];
if (index == 0) {
- (void) snprintf(buf, buflen, "%llu", n);
+ (void) snprintf(buf, buflen, "%llu", (u_longlong_t) n);
} else if ((num & ((1ULL << 10 * index) - 1)) == 0) {
/*
* If this is an even multiple of the base, always display
* without any decimal precision.
*/
- (void) snprintf(buf, buflen, "%llu%c", n, u);
+ (void) snprintf(buf, buflen, "%llu%c", (u_longlong_t) n, u);
} else {
/*
* We want to choose a precision that reflects the best choice
hdl->libzfs_printerr = printerr;
}
+static int
+libzfs_module_loaded(const char *module)
+{
+ const char path_prefix[] = "/sys/module/";
+ char path[256];
+
+ memcpy(path, path_prefix, sizeof (path_prefix) - 1);
+ strcpy(path + sizeof (path_prefix) - 1, module);
+
+ return (access(path, F_OK) == 0);
+}
+
+int
+libzfs_run_process(const char *path, char *argv[], int flags)
+{
+ pid_t pid;
+ int rc, devnull_fd;
+
+ pid = vfork();
+ if (pid == 0) {
+ devnull_fd = open("/dev/null", O_WRONLY);
+
+ if (devnull_fd < 0)
+ _exit(-1);
+
+ if (!(flags & STDOUT_VERBOSE))
+ (void) dup2(devnull_fd, STDOUT_FILENO);
+
+ if (!(flags & STDERR_VERBOSE))
+ (void) dup2(devnull_fd, STDERR_FILENO);
+
+ close(devnull_fd);
+
+ (void) execvp(path, argv);
+ _exit(-1);
+ } else if (pid > 0) {
+ int status;
+
+ while ((rc = waitpid(pid, &status, 0)) == -1 &&
+ errno == EINTR);
+ if (rc < 0 || !WIFEXITED(status))
+ return (-1);
+
+ return (WEXITSTATUS(status));
+ }
+
+ return (-1);
+}
+
+int
+libzfs_load_module(const char *module)
+{
+ char *argv[4] = {"/sbin/modprobe", "-q", (char *)module, (char *)0};
+
+ if (libzfs_module_loaded(module))
+ return (0);
+
+ return (libzfs_run_process("/sbin/modprobe", argv, 0));
+}
+
libzfs_handle_t *
libzfs_init(void)
{
libzfs_handle_t *hdl;
- if ((hdl = calloc(sizeof (libzfs_handle_t), 1)) == NULL) {
+ if (libzfs_load_module("zfs") != 0) {
+ (void) fprintf(stderr, gettext("Failed to load ZFS module "
+ "stack.\nLoad the module manually by running "
+ "'insmod <location>/zfs.ko' as root.\n"));
+ return (NULL);
+ }
+
+ if ((hdl = calloc(1, sizeof (libzfs_handle_t))) == NULL) {
return (NULL);
}
if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
+ (void) fprintf(stderr, gettext("Unable to open %s: %s.\n"),
+ ZFS_DEV, strerror(errno));
+ if (errno == ENOENT)
+ (void) fprintf(stderr,
+ gettext("Verify the ZFS module stack is "
+ "loaded by running '/sbin/modprobe zfs'.\n"));
+
free(hdl);
return (NULL);
}
+#ifdef HAVE_SETMNTENT
+ if ((hdl->libzfs_mnttab = setmntent(MNTTAB, "r")) == NULL) {
+#else
if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) {
+#endif
(void) close(hdl->libzfs_fd);
+ (void) fprintf(stderr,
+ gettext("mtab is not present at %s.\n"), MNTTAB);
free(hdl);
return (NULL);
}
hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r");
+ if (libzfs_core_init() != 0) {
+ (void) close(hdl->libzfs_fd);
+ (void) fclose(hdl->libzfs_mnttab);
+ (void) fclose(hdl->libzfs_sharetab);
+ free(hdl);
+ return (NULL);
+ }
+
zfs_prop_init();
zpool_prop_init();
+ zpool_feature_init();
+ libzfs_mnttab_init(hdl);
return (hdl);
}
{
(void) close(hdl->libzfs_fd);
if (hdl->libzfs_mnttab)
+#ifdef HAVE_SETMNTENT
+ (void) endmntent(hdl->libzfs_mnttab);
+#else
(void) fclose(hdl->libzfs_mnttab);
+#endif
if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab);
zfs_uninit_libshare(hdl);
- if (hdl->libzfs_log_str)
- (void) free(hdl->libzfs_log_str);
zpool_free_handles(hdl);
+ libzfs_fru_clear(hdl, B_TRUE);
namespace_clear(hdl);
+ libzfs_mnttab_fini(hdl);
+ libzfs_core_fini();
free(hdl);
}
return (zfs_open(hdl, entry.mnt_special, ZFS_TYPE_FILESYSTEM));
}
+/*
+ * Append partition suffix to an otherwise fully qualified device path.
+ * This is used to generate the name the full path as its stored in
+ * ZPOOL_CONFIG_PATH for whole disk devices. On success the new length
+ * of 'path' will be returned on error a negative value is returned.
+ */
+int
+zfs_append_partition(char *path, size_t max_len)
+{
+ int len = strlen(path);
+
+ if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) {
+ if (len + 6 >= max_len)
+ return (-1);
+
+ (void) strcat(path, "-part1");
+ len += 6;
+ } else {
+ if (len + 2 >= max_len)
+ return (-1);
+
+ if (isdigit(path[len-1])) {
+ (void) strcat(path, "p1");
+ len += 2;
+ } else {
+ (void) strcat(path, "1");
+ len += 1;
+ }
+ }
+
+ return (len);
+}
+
+/*
+ * Given a shorthand device name check if a file by that name exists in any
+ * of the 'zpool_default_import_path' or ZPOOL_IMPORT_PATH directories. If
+ * one is found, store its fully qualified path in the 'path' buffer passed
+ * by the caller and return 0, otherwise return an error.
+ */
+int
+zfs_resolve_shortname(const char *name, char *path, size_t len)
+{
+ int i, error = -1;
+ char *dir, *env, *envdup;
+
+ env = getenv("ZPOOL_IMPORT_PATH");
+ errno = ENOENT;
+
+ if (env) {
+ envdup = strdup(env);
+ dir = strtok(envdup, ":");
+ while (dir && error) {
+ (void) snprintf(path, len, "%s/%s", dir, name);
+ error = access(path, F_OK);
+ dir = strtok(NULL, ":");
+ }
+ free(envdup);
+ } else {
+ for (i = 0; i < DEFAULT_IMPORT_PATH_SIZE && error < 0; i++) {
+ (void) snprintf(path, len, "%s/%s",
+ zpool_default_import_path[i], name);
+ error = access(path, F_OK);
+ }
+ }
+
+ return (error ? ENOENT : 0);
+}
+
+/*
+ * Given a shorthand device name look for a match against 'cmp_name'. This
+ * is done by checking all prefix expansions using either the default
+ * 'zpool_default_import_paths' or the ZPOOL_IMPORT_PATH environment
+ * variable. Proper partition suffixes will be appended if this is a
+ * whole disk. When a match is found 0 is returned otherwise ENOENT.
+ */
+static int
+zfs_strcmp_shortname(char *name, char *cmp_name, int wholedisk)
+{
+ int path_len, cmp_len, i = 0, error = ENOENT;
+ char *dir, *env, *envdup = NULL;
+ char path_name[MAXPATHLEN];
+
+ cmp_len = strlen(cmp_name);
+ env = getenv("ZPOOL_IMPORT_PATH");
+
+ if (env) {
+ envdup = strdup(env);
+ dir = strtok(envdup, ":");
+ } else {
+ dir = zpool_default_import_path[i];
+ }
+
+ while (dir) {
+ /* Trim trailing directory slashes from ZPOOL_IMPORT_PATH */
+ while (dir[strlen(dir)-1] == '/')
+ dir[strlen(dir)-1] = '\0';
+
+ path_len = snprintf(path_name, MAXPATHLEN, "%s/%s", dir, name);
+ if (wholedisk)
+ path_len = zfs_append_partition(path_name, MAXPATHLEN);
+
+ if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0) {
+ error = 0;
+ break;
+ }
+
+ if (env) {
+ dir = strtok(NULL, ":");
+ } else if (++i < DEFAULT_IMPORT_PATH_SIZE) {
+ dir = zpool_default_import_path[i];
+ } else {
+ dir = NULL;
+ }
+ }
+
+ if (env)
+ free(envdup);
+
+ return (error);
+}
+
+/*
+ * Given either a shorthand or fully qualified path name look for a match
+ * against 'cmp'. The passed name will be expanded as needed for comparison
+ * purposes and redundant slashes stripped to ensure an accurate match.
+ */
+int
+zfs_strcmp_pathname(char *name, char *cmp, int wholedisk)
+{
+ int path_len, cmp_len;
+ char path_name[MAXPATHLEN];
+ char cmp_name[MAXPATHLEN];
+ char *dir;
+
+ /* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
+ memset(cmp_name, 0, MAXPATHLEN);
+ dir = strtok(cmp, "/");
+ while (dir) {
+ strcat(cmp_name, "/");
+ strcat(cmp_name, dir);
+ dir = strtok(NULL, "/");
+ }
+
+ if (name[0] != '/')
+ return (zfs_strcmp_shortname(name, cmp_name, wholedisk));
+
+ strncpy(path_name, name, MAXPATHLEN);
+ path_len = strlen(path_name);
+ cmp_len = strlen(cmp_name);
+
+ if (wholedisk) {
+ path_len = zfs_append_partition(path_name, MAXPATHLEN);
+ if (path_len == -1)
+ return (ENOMEM);
+ }
+
+ if ((path_len != cmp_len) || strcmp(path_name, cmp_name))
+ return (ENOENT);
+
+ return (0);
+}
+
/*
* Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
* an ioctl().
zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
{
if (len == 0)
- len = 2048;
+ len = 16 * 1024;
zc->zc_nvlist_dst_size = len;
if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
- zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == NULL)
+ zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
return (-1);
return (0);
{
free((void *)(uintptr_t)zc->zc_nvlist_dst);
if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
- zfs_alloc(hdl, zc->zc_nvlist_dst_size))
- == NULL)
+ zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
return (-1);
return (0);
int
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
{
- int error;
-
- zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
- error = ioctl(hdl->libzfs_fd, request, zc);
- if (hdl->libzfs_log_str) {
- free(hdl->libzfs_log_str);
- hdl->libzfs_log_str = NULL;
- }
- zc->zc_history = 0;
-
- return (error);
+ return (ioctl(hdl->libzfs_fd, request, zc));
}
/*
"PROPERTY"));
cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
"VALUE"));
+ cbp->cb_colwidths[GET_COL_RECVD] = strlen(dgettext(TEXT_DOMAIN,
+ "RECEIVED"));
cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
"SOURCE"));
* inheriting from the longest name. This is acceptable because in the
* majority of cases 'SOURCE' is the last column displayed, and we don't
* use the width anyway. Note that the 'VALUE' column can be oversized,
- * if the name of the property is much longer the any values we find.
+ * if the name of the property is much longer than any values we find.
*/
for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
/*
pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
+ /* 'RECEIVED' column. */
+ if (pl != cbp->cb_proplist &&
+ pl->pl_recvd_width > cbp->cb_colwidths[GET_COL_RECVD])
+ cbp->cb_colwidths[GET_COL_RECVD] = pl->pl_recvd_width;
+
/*
* 'NAME' and 'SOURCE' columns
*/
/*
* Now go through and print the headers.
*/
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ZFS_GET_NCOLS; i++) {
switch (cbp->cb_columns[i]) {
case GET_COL_NAME:
title = dgettext(TEXT_DOMAIN, "NAME");
case GET_COL_VALUE:
title = dgettext(TEXT_DOMAIN, "VALUE");
break;
+ case GET_COL_RECVD:
+ title = dgettext(TEXT_DOMAIN, "RECEIVED");
+ break;
case GET_COL_SOURCE:
title = dgettext(TEXT_DOMAIN, "SOURCE");
break;
}
if (title != NULL) {
- if (i == 3 || cbp->cb_columns[i + 1] == 0)
+ if (i == (ZFS_GET_NCOLS - 1) ||
+ cbp->cb_columns[i + 1] == GET_COL_NONE)
(void) printf("%s", title);
else
(void) printf("%-*s ",
void
zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
const char *propname, const char *value, zprop_source_t sourcetype,
- const char *source)
+ const char *source, const char *recvd_value)
{
int i;
- const char *str;
+ const char *str = NULL;
char buf[128];
/*
if (cbp->cb_first)
zprop_print_headers(cbp, cbp->cb_type);
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ZFS_GET_NCOLS; i++) {
switch (cbp->cb_columns[i]) {
case GET_COL_NAME:
str = name;
"inherited from %s", source);
str = buf;
break;
+ case ZPROP_SRC_RECEIVED:
+ str = "received";
+ break;
}
break;
+ case GET_COL_RECVD:
+ str = (recvd_value == NULL ? "-" : recvd_value);
+ break;
+
default:
continue;
}
- if (cbp->cb_columns[i + 1] == 0)
+ if (cbp->cb_columns[i + 1] == GET_COL_NONE)
(void) printf("%s", str);
else if (cbp->cb_scripted)
(void) printf("%s\t", str);
(void) printf("%-*s ",
cbp->cb_colwidths[cbp->cb_columns[i]],
str);
-
}
(void) printf("\n");
break;
}
if (i == strlen(ends)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid numeric suffix '%s'"), buf);
+ if (hdl)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid numeric suffix '%s'"), buf);
return (-1);
}
/*
- * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't
- * allow 'BB' - that's just weird.
+ * Allow 'G' = 'GB' = 'GiB', case-insensitively.
+ * However, 'BB' and 'BiB' are disallowed.
*/
- if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
- toupper(buf[0]) != 'B'))
- return (10*i);
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid numeric suffix '%s'"), buf);
+ if (buf[1] == '\0' ||
+ (toupper(buf[0]) != 'B' &&
+ ((toupper(buf[1]) == 'B' && buf[2] == '\0') ||
+ (toupper(buf[1]) == 'I' && toupper(buf[2]) == 'B' &&
+ buf[3] == '\0'))))
+ return (10 * i);
+
+ if (hdl)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid numeric suffix '%s'"), buf);
return (-1);
}
return (-1);
}
- /* Rely on stroll() to process the numeric portion. */
+ /* Rely on strtoull() to process the numeric portion. */
errno = 0;
- *num = strtoll(value, &end, 10);
+ *num = strtoull(value, &end, 10);
/*
* Check for ERANGE, which indicates that the value is too large to fit
* this is a pool property or if this isn't a user-defined
* dataset property,
*/
- if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
- !zfs_prop_user(propname))) {
+ if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
+ !zpool_prop_feature(propname) &&
+ !zpool_prop_unsupported(propname)) ||
+ (type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
+ !zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,
entry->pl_prop = prop;
if (prop == ZPROP_INVAL) {
- if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) == NULL) {
+ if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) ==
+ NULL) {
free(entry);
return (-1);
}