*
* CDDL HEADER END
*/
+
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
#include "libzfs_impl.h"
#include "zfs_prop.h"
+#include "zfeature_common.h"
int
libzfs_errno(libzfs_handle_t *hdl)
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:
switch (error) {
case ENXIO:
case ENODEV:
+ case EPIPE:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
static int
libzfs_module_loaded(const char *module)
{
- FILE *f;
- int result = 0;
- char name[256];
-
- f = fopen("/proc/modules", "r");
- if (f == NULL)
- return -1;
+ const char path_prefix[] = "/sys/module/";
+ char path[256];
- while (fgets(name, sizeof(name), f)) {
- char *c = strchr(name, ' ');
- if (!c)
- continue;
- *c = 0;
- if (strcmp(module, name) == 0) {
- result = 1;
- break;
- }
- }
- fclose(f);
+ memcpy(path, path_prefix, sizeof(path_prefix) - 1);
+ strcpy(path + sizeof(path_prefix) - 1, module);
- return result;
+ return (access(path, F_OK) == 0);
}
int
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);
}
zfs_prop_init();
zpool_prop_init();
+ zpool_feature_init();
libzfs_mnttab_init(hdl);
return (hdl);
}
/*
- * Given a shorthand device name, check if a file by that name exists in a list
- * of directories under /dev. If one is found, store its full path in the
- * buffer pointed to by the path argument and return 0, else return -1. The
- * path buffer must be allocated by the caller.
+ * 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_resolve_shortname(const char *name, char *path, size_t pathlen)
+zfs_append_partition(char *path, size_t max_len)
{
- int i, err;
- char dirs[6][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool",
- "by-vdev"};
-
- /* /dev/ */
- (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name);
- err = access(path, F_OK);
- if (err == 0)
- return (err);
-
- /* /dev/mapper/ */
- (void) snprintf(path, pathlen, "%s/mapper/%s", DISK_ROOT, name);
- err = access(path, F_OK);
- if (err == 0)
- return (err);
-
- /* /dev/disk/<dirs>/ */
- for (i = 0; i < 6 && err < 0; i++) {
- (void) snprintf(path, pathlen, "%s/%s/%s",
- UDISK_ROOT, dirs[i], name);
- err = access(path, F_OK);
+ 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 (err);
+ return (len);
}
/*
- * Append partition suffix to a device path. This should be used to generate
- * the name of a whole disk as it is stored in the vdev label. The
- * user-visible names of whole disks do not contain the partition information.
- * Modifies buf which must be allocated by the caller.
+ * 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.
*/
-void
-zfs_append_partition(const char *path, char *buf, size_t buflen)
+int
+zfs_resolve_shortname(const char *name, char *path, size_t len)
{
- if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
- (void) snprintf(buf, buflen, "%s%s%s", path, "-part",
- FIRST_SLICE);
- else
- (void) snprintf(buf, buflen, "%s%s%s", path,
- isdigit(path[strlen(path)-1]) ? "p" : "",
- FIRST_SLICE);
+ 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)) {
+ 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);
}
/*
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);
}
buf[3] == '\0'))))
return (10*i);
- 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);
}
* 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) && !zfs_prop_userquota(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);
}