X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=lib%2Flibzfs%2Flibzfs_util.c;h=5bb88e94677bd07f18bb4f5f3f5eddff3fe148a3;hb=bbb75c11908d1009b6749b797b3a763558bbaaaf;hp=01995f802e38c08624fb7b64cc0b94273999dabb;hpb=79e7242a91c17f50c857b53d2a7313cf363ea535;p=zfs.git diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index 01995f8..5bb88e9 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -18,8 +18,10 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. */ /* @@ -46,6 +48,7 @@ #include "libzfs_impl.h" #include "zfs_prop.h" +#include "zfeature_common.h" int libzfs_errno(libzfs_handle_t *hdl) @@ -113,7 +116,8 @@ libzfs_error_description(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: @@ -346,6 +350,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) switch (error) { case ENXIO: case ENODEV: + case EPIPE: zfs_verror(hdl, EZFS_IO, fmt, ap); break; @@ -608,39 +613,36 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr) 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); } -static int -libzfs_run_process(const char *path, char *argv[]) +int +libzfs_run_process(const char *path, char *argv[], int flags) { pid_t pid; - int rc; + int rc, devnull_fd; pid = vfork(); if (pid == 0) { - close(1); - close(2); + 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) { @@ -657,14 +659,15 @@ libzfs_run_process(const char *path, char *argv[]) return -1; } -static int +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("modprobe", argv); + + return libzfs_run_process("/sbin/modprobe", argv, 0); } libzfs_handle_t * @@ -701,6 +704,8 @@ libzfs_init(void) 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); } @@ -709,6 +714,7 @@ libzfs_init(void) zfs_prop_init(); zpool_prop_init(); + zpool_feature_init(); libzfs_mnttab_init(hdl); return (hdl); @@ -726,9 +732,7 @@ libzfs_fini(libzfs_handle_t *hdl) #endif if (hdl->libzfs_sharetab) (void) fclose(hdl->libzfs_sharetab); -#ifdef HAVE_ZPL zfs_uninit_libshare(hdl); -#endif if (hdl->libzfs_log_str) (void) free(hdl->libzfs_log_str); zpool_free_handles(hdl); @@ -802,43 +806,165 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype) } /* - * 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[5][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool"}; - - (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name); - err = access(path, F_OK); - for (i = 0; i < 5 && 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); } /* @@ -1177,21 +1303,26 @@ str2shift(libzfs_handle_t *hdl, const char *buf) 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')) + 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); - 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); } @@ -1407,8 +1538,11 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp, * 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, @@ -1420,7 +1554,8 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp, 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); }