diff --git a/cmd/zed/zed.c b/cmd/zed/zed.c index e56b45fa72a9..0aa03fded468 100644 --- a/cmd/zed/zed.c +++ b/cmd/zed/zed.c @@ -1,308 +1,308 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include #include #include "zed.h" #include "zed_conf.h" #include "zed_event.h" #include "zed_file.h" #include "zed_log.h" static volatile sig_atomic_t _got_exit = 0; static volatile sig_atomic_t _got_hup = 0; /* * Signal handler for SIGINT & SIGTERM. */ static void _exit_handler(int signum) { _got_exit = 1; } /* * Signal handler for SIGHUP. */ static void _hup_handler(int signum) { _got_hup = 1; } /* * Register signal handlers. */ static void _setup_sig_handlers(void) { struct sigaction sa; if (sigemptyset(&sa.sa_mask) < 0) zed_log_die("Failed to initialize sigset"); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) < 0) zed_log_die("Failed to ignore SIGPIPE"); sa.sa_handler = _exit_handler; if (sigaction(SIGINT, &sa, NULL) < 0) zed_log_die("Failed to register SIGINT handler"); if (sigaction(SIGTERM, &sa, NULL) < 0) zed_log_die("Failed to register SIGTERM handler"); sa.sa_handler = _hup_handler; if (sigaction(SIGHUP, &sa, NULL) < 0) zed_log_die("Failed to register SIGHUP handler"); (void) sigaddset(&sa.sa_mask, SIGCHLD); if (pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL) < 0) zed_log_die("Failed to block SIGCHLD"); } /* * Lock all current and future pages in the virtual memory address space. * Access to locked pages will never be delayed by a page fault. * * EAGAIN is tested up to max_tries in case this is a transient error. * * Note that memory locks are not inherited by a child created via fork() * and are automatically removed during an execve(). As such, this must * be called after the daemon fork()s (when running in the background). */ static void _lock_memory(void) { #if HAVE_MLOCKALL int i = 0; const int max_tries = 10; for (i = 0; i < max_tries; i++) { if (mlockall(MCL_CURRENT | MCL_FUTURE) == 0) { zed_log_msg(LOG_INFO, "Locked all pages in memory"); return; } if (errno != EAGAIN) break; } zed_log_die("Failed to lock memory pages: %s", strerror(errno)); #else /* HAVE_MLOCKALL */ zed_log_die("Failed to lock memory pages: mlockall() not supported"); #endif /* HAVE_MLOCKALL */ } /* * Start daemonization of the process including the double fork(). * * The parent process will block here until _finish_daemonize() is called * (in the grandchild process), at which point the parent process will exit. * This prevents the parent process from exiting until initialization is * complete. */ static void _start_daemonize(void) { pid_t pid; struct sigaction sa; /* Create pipe for communicating with child during daemonization. */ zed_log_pipe_open(); /* Background process and ensure child is not process group leader. */ pid = fork(); if (pid < 0) { zed_log_die("Failed to create child process: %s", strerror(errno)); } else if (pid > 0) { /* Close writes since parent will only read from pipe. */ zed_log_pipe_close_writes(); /* Wait for notification that daemonization is complete. */ zed_log_pipe_wait(); zed_log_pipe_close_reads(); _exit(EXIT_SUCCESS); } /* Close reads since child will only write to pipe. */ zed_log_pipe_close_reads(); /* Create independent session and detach from terminal. */ if (setsid() < 0) zed_log_die("Failed to create new session: %s", strerror(errno)); /* Prevent child from terminating on HUP when session leader exits. */ if (sigemptyset(&sa.sa_mask) < 0) zed_log_die("Failed to initialize sigset"); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; if (sigaction(SIGHUP, &sa, NULL) < 0) zed_log_die("Failed to ignore SIGHUP"); /* Ensure process cannot re-acquire terminal. */ pid = fork(); if (pid < 0) { zed_log_die("Failed to create grandchild process: %s", strerror(errno)); } else if (pid > 0) { _exit(EXIT_SUCCESS); } } /* * Finish daemonization of the process by closing stdin/stdout/stderr. * * This must be called at the end of initialization after all external * communication channels are established and accessible. */ static void _finish_daemonize(void) { int devnull; /* Preserve fd 0/1/2, but discard data to/from stdin/stdout/stderr. */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) zed_log_die("Failed to open /dev/null: %s", strerror(errno)); if (dup2(devnull, STDIN_FILENO) < 0) zed_log_die("Failed to dup /dev/null onto stdin: %s", strerror(errno)); if (dup2(devnull, STDOUT_FILENO) < 0) zed_log_die("Failed to dup /dev/null onto stdout: %s", strerror(errno)); if (dup2(devnull, STDERR_FILENO) < 0) zed_log_die("Failed to dup /dev/null onto stderr: %s", strerror(errno)); if ((devnull > STDERR_FILENO) && (close(devnull) < 0)) zed_log_die("Failed to close /dev/null: %s", strerror(errno)); /* Notify parent that daemonization is complete. */ zed_log_pipe_close_writes(); } /* * ZFS Event Daemon (ZED). */ int main(int argc, char *argv[]) { struct zed_conf zcp; uint64_t saved_eid; int64_t saved_etime[2]; zed_log_init(argv[0]); zed_log_stderr_open(LOG_NOTICE); zed_conf_init(&zcp); zed_conf_parse_opts(&zcp, argc, argv); if (zcp.do_verbose) zed_log_stderr_open(LOG_INFO); if (geteuid() != 0) zed_log_die("Must be run as root"); zed_file_close_from(STDERR_FILENO + 1); (void) umask(0); if (chdir("/") < 0) zed_log_die("Failed to change to root directory"); if (zed_conf_scan_dir(&zcp) < 0) exit(EXIT_FAILURE); if (!zcp.do_foreground) { _start_daemonize(); zed_log_syslog_open(LOG_DAEMON); } _setup_sig_handlers(); if (zcp.do_memlock) _lock_memory(); if ((zed_conf_write_pid(&zcp) < 0) && (!zcp.do_force)) exit(EXIT_FAILURE); if (!zcp.do_foreground) _finish_daemonize(); zed_log_msg(LOG_NOTICE, "ZFS Event Daemon %s-%s (PID %d)", ZFS_META_VERSION, ZFS_META_RELEASE, (int)getpid()); if (zed_conf_open_state(&zcp) < 0) exit(EXIT_FAILURE); if (zed_conf_read_state(&zcp, &saved_eid, saved_etime) < 0) exit(EXIT_FAILURE); idle: /* * If -I is specified, attempt to open /dev/zfs repeatedly until * successful. */ do { if (!zed_event_init(&zcp)) break; /* Wait for some time and try again. tunable? */ sleep(30); } while (!_got_exit && zcp.do_idle); if (_got_exit) goto out; zed_event_seek(&zcp, saved_eid, saved_etime); while (!_got_exit) { int rv; if (_got_hup) { _got_hup = 0; (void) zed_conf_scan_dir(&zcp); } rv = zed_event_service(&zcp); /* ENODEV: When kernel module is unloaded (osx) */ if (rv == ENODEV) break; } zed_log_msg(LOG_NOTICE, "Exiting"); zed_event_fini(&zcp); if (zcp.do_idle && !_got_exit) goto idle; out: zed_conf_destroy(&zcp); zed_log_fini(); exit(EXIT_SUCCESS); } diff --git a/cmd/zed/zed.h b/cmd/zed/zed.h index 8f2767d74d6d..94f13c2c9da7 100644 --- a/cmd/zed/zed.h +++ b/cmd/zed/zed.h @@ -1,43 +1,43 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_H #define ZED_H /* * Absolute path for the default zed pid file. */ #define ZED_PID_FILE RUNSTATEDIR "/zed.pid" /* * Absolute path for the default zed state file. */ #define ZED_STATE_FILE RUNSTATEDIR "/zed.state" /* * Absolute path for the default zed zedlet directory. */ #define ZED_ZEDLET_DIR SYSCONFDIR "/zfs/zed.d" /* * String prefix for ZED variables passed via environment variables. */ #define ZED_VAR_PREFIX "ZED_" /* * String prefix for ZFS event names passed via environment variables. */ #define ZEVENT_VAR_PREFIX "ZEVENT_" #endif /* !ZED_H */ diff --git a/cmd/zed/zed_conf.c b/cmd/zed/zed_conf.c index b95108fd2f8c..2cf2311dbb42 100644 --- a/cmd/zed/zed_conf.c +++ b/cmd/zed/zed_conf.c @@ -1,705 +1,705 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zed.h" #include "zed_conf.h" #include "zed_file.h" #include "zed_log.h" #include "zed_strings.h" /* * Initialise the configuration with default values. */ void zed_conf_init(struct zed_conf *zcp) { memset(zcp, 0, sizeof (*zcp)); /* zcp->zfs_hdl opened in zed_event_init() */ /* zcp->zedlets created in zed_conf_scan_dir() */ zcp->pid_fd = -1; /* opened in zed_conf_write_pid() */ zcp->state_fd = -1; /* opened in zed_conf_open_state() */ zcp->zevent_fd = -1; /* opened in zed_event_init() */ zcp->max_jobs = 16; if (!(zcp->pid_file = strdup(ZED_PID_FILE)) || !(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)) || !(zcp->state_file = strdup(ZED_STATE_FILE))) zed_log_die("Failed to create conf: %s", strerror(errno)); } /* * Destroy the configuration [zcp]. * * Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini(). */ void zed_conf_destroy(struct zed_conf *zcp) { if (zcp->state_fd >= 0) { if (close(zcp->state_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to close state file \"%s\": %s", zcp->state_file, strerror(errno)); zcp->state_fd = -1; } if (zcp->pid_file) { if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT)) zed_log_msg(LOG_WARNING, "Failed to remove PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } if (zcp->pid_fd >= 0) { if (close(zcp->pid_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to close PID file \"%s\": %s", zcp->pid_file, strerror(errno)); zcp->pid_fd = -1; } if (zcp->pid_file) { free(zcp->pid_file); zcp->pid_file = NULL; } if (zcp->zedlet_dir) { free(zcp->zedlet_dir); zcp->zedlet_dir = NULL; } if (zcp->state_file) { free(zcp->state_file); zcp->state_file = NULL; } if (zcp->zedlets) { zed_strings_destroy(zcp->zedlets); zcp->zedlets = NULL; } } /* * Display command-line help and exit. * * If [got_err] is 0, output to stdout and exit normally; * otherwise, output to stderr and exit with a failure status. */ static void _zed_conf_display_help(const char *prog, boolean_t got_err) { struct opt { const char *o, *d, *v; }; FILE *fp = got_err ? stderr : stdout; struct opt *oo; struct opt iopts[] = { { .o = "-h", .d = "Display help" }, { .o = "-L", .d = "Display license information" }, { .o = "-V", .d = "Display version information" }, {}, }; struct opt nopts[] = { { .o = "-v", .d = "Be verbose" }, { .o = "-f", .d = "Force daemon to run" }, { .o = "-F", .d = "Run daemon in the foreground" }, { .o = "-I", .d = "Idle daemon until kernel module is (re)loaded" }, { .o = "-M", .d = "Lock all pages in memory" }, { .o = "-P", .d = "$PATH for ZED to use (only used by ZTS)" }, { .o = "-Z", .d = "Zero state file" }, {}, }; struct opt vopts[] = { { .o = "-d DIR", .d = "Read enabled ZEDLETs from DIR.", .v = ZED_ZEDLET_DIR }, { .o = "-p FILE", .d = "Write daemon's PID to FILE.", .v = ZED_PID_FILE }, { .o = "-s FILE", .d = "Write daemon's state to FILE.", .v = ZED_STATE_FILE }, { .o = "-j JOBS", .d = "Start at most JOBS at once.", .v = "16" }, {}, }; fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed")); fprintf(fp, "\n"); for (oo = iopts; oo->o; ++oo) fprintf(fp, " %*s %s\n", -8, oo->o, oo->d); fprintf(fp, "\n"); for (oo = nopts; oo->o; ++oo) fprintf(fp, " %*s %s\n", -8, oo->o, oo->d); fprintf(fp, "\n"); for (oo = vopts; oo->o; ++oo) fprintf(fp, " %*s %s [%s]\n", -8, oo->o, oo->d, oo->v); fprintf(fp, "\n"); exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS); } /* * Display license information to stdout and exit. */ static void _zed_conf_display_license(void) { printf( "The ZFS Event Daemon (ZED) is distributed under the terms of the\n" " Common Development and Distribution License (CDDL-1.0)\n" " .\n" "\n" "Developed at Lawrence Livermore National Laboratory" " (LLNL-CODE-403049).\n" "\n"); exit(EXIT_SUCCESS); } /* * Display version information to stdout and exit. */ static void _zed_conf_display_version(void) { printf("%s-%s-%s\n", ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE); exit(EXIT_SUCCESS); } /* * Copy the [path] string to the [resultp] ptr. * If [path] is not an absolute path, prefix it with the current working dir. * If [resultp] is non-null, free its existing string before assignment. */ static void _zed_conf_parse_path(char **resultp, const char *path) { char buf[PATH_MAX]; assert(resultp != NULL); assert(path != NULL); if (*resultp) free(*resultp); if (path[0] == '/') { *resultp = strdup(path); } else { if (!getcwd(buf, sizeof (buf))) zed_log_die("Failed to get current working dir: %s", strerror(errno)); if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf) || strlcat(buf, path, sizeof (buf)) >= sizeof (buf)) zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG)); *resultp = strdup(buf); } if (!*resultp) zed_log_die("Failed to copy path: %s", strerror(ENOMEM)); } /* * Parse the command-line options into the configuration [zcp]. */ void zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv) { const char * const opts = ":hLVd:p:P:s:vfFMZIj:"; int opt; unsigned long raw; if (!zcp || !argv || !argv[0]) zed_log_die("Failed to parse options: Internal error"); opterr = 0; /* suppress default getopt err msgs */ while ((opt = getopt(argc, argv, opts)) != -1) { switch (opt) { case 'h': _zed_conf_display_help(argv[0], B_FALSE); break; case 'L': _zed_conf_display_license(); break; case 'V': _zed_conf_display_version(); break; case 'd': _zed_conf_parse_path(&zcp->zedlet_dir, optarg); break; case 'I': zcp->do_idle = 1; break; case 'p': _zed_conf_parse_path(&zcp->pid_file, optarg); break; case 'P': _zed_conf_parse_path(&zcp->path, optarg); break; case 's': _zed_conf_parse_path(&zcp->state_file, optarg); break; case 'v': zcp->do_verbose = 1; break; case 'f': zcp->do_force = 1; break; case 'F': zcp->do_foreground = 1; break; case 'M': zcp->do_memlock = 1; break; case 'Z': zcp->do_zero = 1; break; case 'j': errno = 0; raw = strtoul(optarg, NULL, 0); if (errno == ERANGE || raw > INT16_MAX) { zed_log_die("%lu is too many jobs", raw); } if (raw == 0) { zed_log_die("0 jobs makes no sense"); } else { zcp->max_jobs = raw; } break; case '?': default: if (optopt == '?') _zed_conf_display_help(argv[0], B_FALSE); fprintf(stderr, "%s: Invalid option '-%c'\n\n", argv[0], optopt); _zed_conf_display_help(argv[0], B_TRUE); break; } } } /* * Scan the [zcp] zedlet_dir for files to exec based on the event class. * Files must be executable by user, but not writable by group or other. * Dotfiles are ignored. * * Return 0 on success with an updated set of zedlets, * or -1 on error with errno set. */ int zed_conf_scan_dir(struct zed_conf *zcp) { zed_strings_t *zedlets; DIR *dirp; struct dirent *direntp; char pathname[PATH_MAX]; struct stat st; int n; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to scan zedlet dir: %s", strerror(errno)); return (-1); } zedlets = zed_strings_create(); if (!zedlets) { errno = ENOMEM; zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s", zcp->zedlet_dir, strerror(errno)); return (-1); } dirp = opendir(zcp->zedlet_dir); if (!dirp) { int errno_bak = errno; zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s", zcp->zedlet_dir, strerror(errno)); zed_strings_destroy(zedlets); errno = errno_bak; return (-1); } while ((direntp = readdir(dirp))) { if (direntp->d_name[0] == '.') continue; n = snprintf(pathname, sizeof (pathname), "%s/%s", zcp->zedlet_dir, direntp->d_name); if ((n < 0) || (n >= sizeof (pathname))) { zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s", direntp->d_name, strerror(ENAMETOOLONG)); continue; } if (stat(pathname, &st) < 0) { zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s", pathname, strerror(errno)); continue; } if (!S_ISREG(st.st_mode)) { zed_log_msg(LOG_INFO, "Ignoring \"%s\": not a regular file", direntp->d_name); continue; } if ((st.st_uid != 0) && !zcp->do_force) { zed_log_msg(LOG_NOTICE, "Ignoring \"%s\": not owned by root", direntp->d_name); continue; } if (!(st.st_mode & S_IXUSR)) { zed_log_msg(LOG_INFO, "Ignoring \"%s\": not executable by user", direntp->d_name); continue; } if ((st.st_mode & S_IWGRP) && !zcp->do_force) { zed_log_msg(LOG_NOTICE, "Ignoring \"%s\": writable by group", direntp->d_name); continue; } if ((st.st_mode & S_IWOTH) && !zcp->do_force) { zed_log_msg(LOG_NOTICE, "Ignoring \"%s\": writable by other", direntp->d_name); continue; } if (zed_strings_add(zedlets, NULL, direntp->d_name) < 0) { zed_log_msg(LOG_WARNING, "Failed to register \"%s\": %s", direntp->d_name, strerror(errno)); continue; } if (zcp->do_verbose) zed_log_msg(LOG_INFO, "Registered zedlet \"%s\"", direntp->d_name); } if (closedir(dirp) < 0) { int errno_bak = errno; zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s", zcp->zedlet_dir, strerror(errno)); zed_strings_destroy(zedlets); errno = errno_bak; return (-1); } if (zcp->zedlets) zed_strings_destroy(zcp->zedlets); zcp->zedlets = zedlets; return (0); } /* * Write the PID file specified in [zcp]. * Return 0 on success, -1 on error. * * This must be called after fork()ing to become a daemon (so the correct PID * is recorded), but before daemonization is complete and the parent process * exits (for synchronization with systemd). */ int zed_conf_write_pid(struct zed_conf *zcp) { char buf[PATH_MAX]; int n; char *p; mode_t mask; int rv; if (!zcp || !zcp->pid_file) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to create PID file: %s", strerror(errno)); return (-1); } assert(zcp->pid_fd == -1); /* * Create PID file directory if needed. */ n = strlcpy(buf, zcp->pid_file, sizeof (buf)); if (n >= sizeof (buf)) { errno = ENAMETOOLONG; zed_log_msg(LOG_ERR, "Failed to create PID file: %s", strerror(errno)); goto err; } p = strrchr(buf, '/'); if (p) *p = '\0'; if ((mkdirp(buf, 0755) < 0) && (errno != EEXIST)) { zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s", buf, strerror(errno)); goto err; } /* * Obtain PID file lock. */ mask = umask(0); umask(mask | 022); zcp->pid_fd = open(zcp->pid_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644); umask(mask); if (zcp->pid_fd < 0) { zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s", zcp->pid_file, strerror(errno)); goto err; } rv = zed_file_lock(zcp->pid_fd); if (rv < 0) { zed_log_msg(LOG_ERR, "Failed to lock PID file \"%s\": %s", zcp->pid_file, strerror(errno)); goto err; } else if (rv > 0) { pid_t pid = zed_file_is_locked(zcp->pid_fd); if (pid < 0) { zed_log_msg(LOG_ERR, "Failed to test lock on PID file \"%s\"", zcp->pid_file); } else if (pid > 0) { zed_log_msg(LOG_ERR, "Found PID %d bound to PID file \"%s\"", pid, zcp->pid_file); } else { zed_log_msg(LOG_ERR, "Inconsistent lock state on PID file \"%s\"", zcp->pid_file); } goto err; } /* * Write PID file. */ n = snprintf(buf, sizeof (buf), "%d\n", (int)getpid()); if ((n < 0) || (n >= sizeof (buf))) { errno = ERANGE; zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else if (write(zcp->pid_fd, buf, n) != n) { zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else if (fdatasync(zcp->pid_fd) < 0) { zed_log_msg(LOG_ERR, "Failed to sync PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else { return (0); } err: if (zcp->pid_fd >= 0) { (void) close(zcp->pid_fd); zcp->pid_fd = -1; } return (-1); } /* * Open and lock the [zcp] state_file. * Return 0 on success, -1 on error. * * FIXME: Move state information into kernel. */ int zed_conf_open_state(struct zed_conf *zcp) { char dirbuf[PATH_MAX]; int n; char *p; int rv; if (!zcp || !zcp->state_file) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to open state file: %s", strerror(errno)); return (-1); } n = strlcpy(dirbuf, zcp->state_file, sizeof (dirbuf)); if (n >= sizeof (dirbuf)) { errno = ENAMETOOLONG; zed_log_msg(LOG_WARNING, "Failed to open state file: %s", strerror(errno)); return (-1); } p = strrchr(dirbuf, '/'); if (p) *p = '\0'; if ((mkdirp(dirbuf, 0755) < 0) && (errno != EEXIST)) { zed_log_msg(LOG_WARNING, "Failed to create directory \"%s\": %s", dirbuf, strerror(errno)); return (-1); } if (zcp->state_fd >= 0) { if (close(zcp->state_fd) < 0) { zed_log_msg(LOG_WARNING, "Failed to close state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } } if (zcp->do_zero) (void) unlink(zcp->state_file); zcp->state_fd = open(zcp->state_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644); if (zcp->state_fd < 0) { zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } rv = zed_file_lock(zcp->state_fd); if (rv < 0) { zed_log_msg(LOG_WARNING, "Failed to lock state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } if (rv > 0) { pid_t pid = zed_file_is_locked(zcp->state_fd); if (pid < 0) { zed_log_msg(LOG_WARNING, "Failed to test lock on state file \"%s\"", zcp->state_file); } else if (pid > 0) { zed_log_msg(LOG_WARNING, "Found PID %d bound to state file \"%s\"", pid, zcp->state_file); } else { zed_log_msg(LOG_WARNING, "Inconsistent lock state on state file \"%s\"", zcp->state_file); } return (-1); } return (0); } /* * Read the opened [zcp] state_file to obtain the eid & etime of the last event * processed. Write the state from the last event to the [eidp] & [etime] args * passed by reference. Note that etime[] is an array of size 2. * Return 0 on success, -1 on error. */ int zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]) { ssize_t len; struct iovec iov[3]; ssize_t n; if (!zcp || !eidp || !etime) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to read state file: %s", strerror(errno)); return (-1); } if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) { zed_log_msg(LOG_WARNING, "Failed to reposition state file offset: %s", strerror(errno)); return (-1); } len = 0; iov[0].iov_base = eidp; len += iov[0].iov_len = sizeof (*eidp); iov[1].iov_base = &etime[0]; len += iov[1].iov_len = sizeof (etime[0]); iov[2].iov_base = &etime[1]; len += iov[2].iov_len = sizeof (etime[1]); n = readv(zcp->state_fd, iov, 3); if (n == 0) { *eidp = 0; } else if (n < 0) { zed_log_msg(LOG_WARNING, "Failed to read state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } else if (n != len) { errno = EIO; zed_log_msg(LOG_WARNING, "Failed to read state file \"%s\": Read %d of %d bytes", zcp->state_file, n, len); return (-1); } return (0); } /* * Write the [eid] & [etime] of the last processed event to the opened * [zcp] state_file. Note that etime[] is an array of size 2. * Return 0 on success, -1 on error. */ int zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]) { ssize_t len; struct iovec iov[3]; ssize_t n; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to write state file: %s", strerror(errno)); return (-1); } if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) { zed_log_msg(LOG_WARNING, "Failed to reposition state file offset: %s", strerror(errno)); return (-1); } len = 0; iov[0].iov_base = &eid; len += iov[0].iov_len = sizeof (eid); iov[1].iov_base = &etime[0]; len += iov[1].iov_len = sizeof (etime[0]); iov[2].iov_base = &etime[1]; len += iov[2].iov_len = sizeof (etime[1]); n = writev(zcp->state_fd, iov, 3); if (n < 0) { zed_log_msg(LOG_WARNING, "Failed to write state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } if (n != len) { errno = EIO; zed_log_msg(LOG_WARNING, "Failed to write state file \"%s\": Wrote %d of %d bytes", zcp->state_file, n, len); return (-1); } if (fdatasync(zcp->state_fd) < 0) { zed_log_msg(LOG_WARNING, "Failed to sync state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } return (0); } diff --git a/cmd/zed/zed_conf.h b/cmd/zed/zed_conf.h index b2dc09c51a69..0b30a1503c52 100644 --- a/cmd/zed/zed_conf.h +++ b/cmd/zed/zed_conf.h @@ -1,58 +1,58 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_CONF_H #define ZED_CONF_H #include #include #include "zed_strings.h" struct zed_conf { char *pid_file; /* abs path to pid file */ char *zedlet_dir; /* abs path to zedlet dir */ char *state_file; /* abs path to state file */ libzfs_handle_t *zfs_hdl; /* handle to libzfs */ zed_strings_t *zedlets; /* names of enabled zedlets */ char *path; /* custom $PATH for zedlets to use */ int pid_fd; /* fd to pid file for lock */ int state_fd; /* fd to state file */ int zevent_fd; /* fd for access to zevents */ int16_t max_jobs; /* max zedlets to run at one time */ boolean_t do_force:1; /* true if force enabled */ boolean_t do_foreground:1; /* true if run in foreground */ boolean_t do_memlock:1; /* true if locking memory */ boolean_t do_verbose:1; /* true if verbosity enabled */ boolean_t do_zero:1; /* true if zeroing state */ boolean_t do_idle:1; /* true if idle enabled */ }; void zed_conf_init(struct zed_conf *zcp); void zed_conf_destroy(struct zed_conf *zcp); void zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv); int zed_conf_scan_dir(struct zed_conf *zcp); int zed_conf_write_pid(struct zed_conf *zcp); int zed_conf_open_state(struct zed_conf *zcp); int zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]); int zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]); #endif /* !ZED_CONF_H */ diff --git a/cmd/zed/zed_event.c b/cmd/zed/zed_event.c index 232b88a13840..9eaad0e92fbb 100644 --- a/cmd/zed/zed_event.c +++ b/cmd/zed/zed_event.c @@ -1,987 +1,987 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zed.h" #include "zed_conf.h" #include "zed_disk_event.h" #include "zed_event.h" #include "zed_exec.h" #include "zed_file.h" #include "zed_log.h" #include "zed_strings.h" #include "agents/zfs_agents.h" #define MAXBUF 4096 /* * Open the libzfs interface. */ int zed_event_init(struct zed_conf *zcp) { if (!zcp) zed_log_die("Failed zed_event_init: %s", strerror(EINVAL)); zcp->zfs_hdl = libzfs_init(); if (!zcp->zfs_hdl) { if (zcp->do_idle) return (-1); zed_log_die("Failed to initialize libzfs"); } zcp->zevent_fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC); if (zcp->zevent_fd < 0) { if (zcp->do_idle) return (-1); zed_log_die("Failed to open \"%s\": %s", ZFS_DEV, strerror(errno)); } zfs_agent_init(zcp->zfs_hdl); if (zed_disk_event_init() != 0) { if (zcp->do_idle) return (-1); zed_log_die("Failed to initialize disk events"); } return (0); } /* * Close the libzfs interface. */ void zed_event_fini(struct zed_conf *zcp) { if (!zcp) zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL)); zed_disk_event_fini(); zfs_agent_fini(); if (zcp->zevent_fd >= 0) { if (close(zcp->zevent_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s", ZFS_DEV, strerror(errno)); zcp->zevent_fd = -1; } if (zcp->zfs_hdl) { libzfs_fini(zcp->zfs_hdl); zcp->zfs_hdl = NULL; } zed_exec_fini(); } static void _bump_event_queue_length(void) { int zzlm = -1, wr; char qlen_buf[12] = {0}; /* parameter is int => max "-2147483647\n" */ long int qlen; zzlm = open("/sys/module/zfs/parameters/zfs_zevent_len_max", O_RDWR); if (zzlm < 0) goto done; if (read(zzlm, qlen_buf, sizeof (qlen_buf)) < 0) goto done; qlen_buf[sizeof (qlen_buf) - 1] = '\0'; errno = 0; qlen = strtol(qlen_buf, NULL, 10); if (errno == ERANGE) goto done; if (qlen <= 0) qlen = 512; /* default zfs_zevent_len_max value */ else qlen *= 2; if (qlen > INT_MAX) qlen = INT_MAX; wr = snprintf(qlen_buf, sizeof (qlen_buf), "%ld", qlen); if (pwrite(zzlm, qlen_buf, wr, 0) < 0) goto done; zed_log_msg(LOG_WARNING, "Bumping queue length to %ld", qlen); done: if (zzlm > -1) (void) close(zzlm); } /* * Seek to the event specified by [saved_eid] and [saved_etime]. * This protects against processing a given event more than once. * Return 0 upon a successful seek to the specified event, or -1 otherwise. * * A zevent is considered to be uniquely specified by its (eid,time) tuple. * The unsigned 64b eid is set to 1 when the kernel module is loaded, and * incremented by 1 for each new event. Since the state file can persist * across a kernel module reload, the time must be checked to ensure a match. */ int zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[]) { uint64_t eid; int found; nvlist_t *nvl; int n_dropped; int64_t *etime; uint_t nelem; int rv; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to seek zevent: %s", strerror(errno)); return (-1); } eid = 0; found = 0; while ((eid < saved_eid) && !found) { rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONBLOCK, zcp->zevent_fd); if ((rv != 0) || !nvl) break; if (n_dropped > 0) { zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped); _bump_event_queue_length(); } if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid"); } else if (nvlist_lookup_int64_array(nvl, "time", &etime, &nelem) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu)", eid); } else if (nelem != 2) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu, nelem=%u)", eid, nelem); } else if ((eid != saved_eid) || (etime[0] != saved_etime[0]) || (etime[1] != saved_etime[1])) { /* no-op */ } else { found = 1; } free(nvl); } if (!found && (saved_eid > 0)) { if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START, zcp->zevent_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to seek to eid=0"); else eid = 0; } zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid); return (found ? 0 : -1); } /* * Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0. */ static int _zed_event_value_is_hex(const char *name) { const char *hex_suffix[] = { "_guid", "_guids", NULL }; const char **pp; char *p; if (!name) return (0); for (pp = hex_suffix; *pp; pp++) { p = strstr(name, *pp); if (p && strlen(p) == strlen(*pp)) return (1); } return (0); } /* * Add an environment variable for [eid] to the container [zsp]. * * The variable name is the concatenation of [prefix] and [name] converted to * uppercase with non-alphanumeric characters converted to underscores; * [prefix] is optional, and [name] must begin with an alphabetic character. * If the converted variable name already exists within the container [zsp], * its existing value will be replaced with the new value. * * The variable value is specified by the format string [fmt]. * * Returns 0 on success, and -1 on error (with errno set). * * All environment variables in [zsp] should be added through this function. */ static __attribute__((format(printf, 5, 6))) int _zed_event_add_var(uint64_t eid, zed_strings_t *zsp, const char *prefix, const char *name, const char *fmt, ...) { char keybuf[MAXBUF]; char valbuf[MAXBUF]; char *dstp; const char *srcp; const char *lastp; int n; int buflen; va_list vargs; assert(zsp != NULL); assert(fmt != NULL); if (!name) { errno = EINVAL; zed_log_msg(LOG_WARNING, "Failed to add variable for eid=%llu: Name is empty", eid); return (-1); } else if (!isalpha(name[0])) { errno = EINVAL; zed_log_msg(LOG_WARNING, "Failed to add variable for eid=%llu: " "Name \"%s\" is invalid", eid, name); return (-1); } /* * Construct the string key by converting PREFIX (if present) and NAME. */ dstp = keybuf; lastp = keybuf + sizeof (keybuf); if (prefix) { for (srcp = prefix; *srcp && (dstp < lastp); srcp++) *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_'; } for (srcp = name; *srcp && (dstp < lastp); srcp++) *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_'; if (dstp == lastp) { errno = ENAMETOOLONG; zed_log_msg(LOG_WARNING, "Failed to add variable for eid=%llu: Name too long", eid); return (-1); } *dstp = '\0'; /* * Construct the string specified by "[PREFIX][NAME]=[FMT]". */ dstp = valbuf; buflen = sizeof (valbuf); n = strlcpy(dstp, keybuf, buflen); if (n >= sizeof (valbuf)) { errno = EMSGSIZE; zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", keybuf, eid, "Exceeded buffer size"); return (-1); } dstp += n; buflen -= n; *dstp++ = '='; buflen--; if (buflen <= 0) { errno = EMSGSIZE; zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", keybuf, eid, "Exceeded buffer size"); return (-1); } va_start(vargs, fmt); n = vsnprintf(dstp, buflen, fmt, vargs); va_end(vargs); if ((n < 0) || (n >= buflen)) { errno = EMSGSIZE; zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", keybuf, eid, "Exceeded buffer size"); return (-1); } else if (zed_strings_add(zsp, keybuf, valbuf) < 0) { zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", keybuf, eid, strerror(errno)); return (-1); } return (0); } static int _zed_event_add_array_err(uint64_t eid, const char *name) { errno = EMSGSIZE; zed_log_msg(LOG_WARNING, "Failed to convert nvpair \"%s\" for eid=%llu: " "Exceeded buffer size", name, eid); return (-1); } static int _zed_event_add_int8_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; int8_t *i8p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_int8_array(nvp, &i8p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%d ", i8p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; uint8_t *u8p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_uint8_array(nvp, &u8p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%u ", u8p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; int16_t *i16p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_int16_array(nvp, &i16p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%d ", i16p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; uint16_t *u16p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_uint16_array(nvp, &u16p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%u ", u16p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; int32_t *i32p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_int32_array(nvp, &i32p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%d ", i32p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; uint32_t *u32p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_uint32_array(nvp, &u32p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%u ", u32p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; int64_t *i64p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_int64_array(nvp, &i64p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; const char *fmt; uint64_t *u64p; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY)); name = nvpair_name(nvp); fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu "; (void) nvpair_value_uint64_array(nvp, &u64p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } static int _zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp, const char *prefix, nvpair_t *nvp) { char buf[MAXBUF]; int buflen = sizeof (buf); const char *name; char **strp; uint_t nelem; uint_t i; char *p; int n; assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY)); name = nvpair_name(nvp); (void) nvpair_value_string_array(nvp, &strp, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : ""); if ((n < 0) || (n >= buflen)) return (_zed_event_add_array_err(eid, name)); p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf)); } /* * Convert the nvpair [nvp] to a string which is added to the environment * of the child process. * Return 0 on success, -1 on error. */ static void _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp) { const char *name; data_type_t type; const char *prefix = ZEVENT_VAR_PREFIX; boolean_t b; double d; uint8_t i8; uint16_t i16; uint32_t i32; uint64_t i64; char *str; assert(zsp != NULL); assert(nvp != NULL); name = nvpair_name(nvp); type = nvpair_type(nvp); switch (type) { case DATA_TYPE_BOOLEAN: _zed_event_add_var(eid, zsp, prefix, name, "%s", "1"); break; case DATA_TYPE_BOOLEAN_VALUE: (void) nvpair_value_boolean_value(nvp, &b); _zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0"); break; case DATA_TYPE_BYTE: (void) nvpair_value_byte(nvp, &i8); _zed_event_add_var(eid, zsp, prefix, name, "%d", i8); break; case DATA_TYPE_INT8: (void) nvpair_value_int8(nvp, (int8_t *)&i8); _zed_event_add_var(eid, zsp, prefix, name, "%d", i8); break; case DATA_TYPE_UINT8: (void) nvpair_value_uint8(nvp, &i8); _zed_event_add_var(eid, zsp, prefix, name, "%u", i8); break; case DATA_TYPE_INT16: (void) nvpair_value_int16(nvp, (int16_t *)&i16); _zed_event_add_var(eid, zsp, prefix, name, "%d", i16); break; case DATA_TYPE_UINT16: (void) nvpair_value_uint16(nvp, &i16); _zed_event_add_var(eid, zsp, prefix, name, "%u", i16); break; case DATA_TYPE_INT32: (void) nvpair_value_int32(nvp, (int32_t *)&i32); _zed_event_add_var(eid, zsp, prefix, name, "%d", i32); break; case DATA_TYPE_UINT32: (void) nvpair_value_uint32(nvp, &i32); _zed_event_add_var(eid, zsp, prefix, name, "%u", i32); break; case DATA_TYPE_INT64: (void) nvpair_value_int64(nvp, (int64_t *)&i64); _zed_event_add_var(eid, zsp, prefix, name, "%lld", (longlong_t)i64); break; case DATA_TYPE_UINT64: (void) nvpair_value_uint64(nvp, &i64); _zed_event_add_var(eid, zsp, prefix, name, (_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"), (u_longlong_t)i64); /* * shadow readable strings for vdev state pairs */ if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE) == 0 || strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_LASTSTATE) == 0) { char alt[32]; (void) snprintf(alt, sizeof (alt), "%s_str", name); _zed_event_add_var(eid, zsp, prefix, alt, "%s", zpool_state_to_name(i64, VDEV_AUX_NONE)); } else /* * shadow readable strings for pool state */ if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_POOL_STATE) == 0) { char alt[32]; (void) snprintf(alt, sizeof (alt), "%s_str", name); _zed_event_add_var(eid, zsp, prefix, alt, "%s", zpool_pool_state_to_name(i64)); } break; case DATA_TYPE_DOUBLE: (void) nvpair_value_double(nvp, &d); _zed_event_add_var(eid, zsp, prefix, name, "%g", d); break; case DATA_TYPE_HRTIME: (void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64); _zed_event_add_var(eid, zsp, prefix, name, "%llu", (u_longlong_t)i64); break; case DATA_TYPE_STRING: (void) nvpair_value_string(nvp, &str); _zed_event_add_var(eid, zsp, prefix, name, "%s", (str ? str : "")); break; case DATA_TYPE_INT8_ARRAY: _zed_event_add_int8_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_UINT8_ARRAY: _zed_event_add_uint8_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_INT16_ARRAY: _zed_event_add_int16_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_UINT16_ARRAY: _zed_event_add_uint16_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_INT32_ARRAY: _zed_event_add_int32_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_UINT32_ARRAY: _zed_event_add_uint32_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_INT64_ARRAY: _zed_event_add_int64_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_UINT64_ARRAY: _zed_event_add_uint64_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_STRING_ARRAY: _zed_event_add_string_array(eid, zsp, prefix, nvp); break; case DATA_TYPE_NVLIST: case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_BYTE_ARRAY: case DATA_TYPE_NVLIST_ARRAY: _zed_event_add_var(eid, zsp, prefix, name, "_NOT_IMPLEMENTED_"); break; default: errno = EINVAL; zed_log_msg(LOG_WARNING, "Failed to convert nvpair \"%s\" for eid=%llu: " "Unrecognized type=%u", name, eid, (unsigned int) type); break; } } /* * Restrict various environment variables to safe and sane values * when constructing the environment for the child process, unless * we're running with a custom $PATH (like under the ZFS test suite). * * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1. */ static void _zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp, const char *path) { const char *env_restrict[][2] = { { "IFS", " \t\n" }, { "PATH", _PATH_STDPATH }, { "ZDB", SBINDIR "/zdb" }, { "ZED", SBINDIR "/zed" }, { "ZFS", SBINDIR "/zfs" }, { "ZINJECT", SBINDIR "/zinject" }, { "ZPOOL", SBINDIR "/zpool" }, { "ZFS_ALIAS", ZFS_META_ALIAS }, { "ZFS_VERSION", ZFS_META_VERSION }, { "ZFS_RELEASE", ZFS_META_RELEASE }, { NULL, NULL } }; /* * If we have a custom $PATH, use the default ZFS binary locations * instead of the hard-coded ones. */ const char *env_path[][2] = { { "IFS", " \t\n" }, { "PATH", NULL }, /* $PATH copied in later on */ { "ZDB", "zdb" }, { "ZED", "zed" }, { "ZFS", "zfs" }, { "ZINJECT", "zinject" }, { "ZPOOL", "zpool" }, { "ZFS_ALIAS", ZFS_META_ALIAS }, { "ZFS_VERSION", ZFS_META_VERSION }, { "ZFS_RELEASE", ZFS_META_RELEASE }, { NULL, NULL } }; const char *(*pa)[2]; assert(zsp != NULL); pa = path != NULL ? env_path : env_restrict; for (; *(*pa); pa++) { /* Use our custom $PATH if we have one */ if (path != NULL && strcmp((*pa)[0], "PATH") == 0) (*pa)[1] = path; _zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]); } } /* * Preserve specified variables from the parent environment * when constructing the environment for the child process. * * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1. */ static void _zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp) { const char *env_preserve[] = { "TZ", NULL }; const char **keyp; const char *val; assert(zsp != NULL); for (keyp = env_preserve; *keyp; keyp++) { if ((val = getenv(*keyp))) _zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val); } } /* * Compute the "subclass" by removing the first 3 components of [class] * (which will always be of the form "*.fs.zfs"). Return a pointer inside * the string [class], or NULL if insufficient components exist. */ static const char * _zed_event_get_subclass(const char *class) { const char *p; int i; if (!class) return (NULL); p = class; for (i = 0; i < 3; i++) { p = strchr(p, '.'); if (!p) break; p++; } return (p); } /* * Convert the zevent time from a 2-element array of 64b integers * into a more convenient form: * - TIME_SECS is the second component of the time. * - TIME_NSECS is the nanosecond component of the time. * - TIME_STRING is an almost-RFC3339-compliant string representation. */ static void _zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[]) { struct tm *stp; char buf[32]; assert(zsp != NULL); assert(etime != NULL); _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS", "%lld", (long long int) etime[0]); _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS", "%lld", (long long int) etime[1]); if (!(stp = localtime((const time_t *) &etime[0]))) { zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s", ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error"); } else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", stp)) { zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s", ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error"); } else { _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING", "%s", buf); } } /* * Service the next zevent, blocking until one is available. */ int zed_event_service(struct zed_conf *zcp) { nvlist_t *nvl; nvpair_t *nvp; int n_dropped; zed_strings_t *zsp; uint64_t eid; int64_t *etime; uint_t nelem; char *class; const char *subclass; int rv; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to service zevent: %s", strerror(errno)); return (EINVAL); } rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE, zcp->zevent_fd); if ((rv != 0) || !nvl) return (errno); if (n_dropped > 0) { zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped); _bump_event_queue_length(); } if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid"); } else if (nvlist_lookup_int64_array( nvl, "time", &etime, &nelem) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu)", eid); } else if (nelem != 2) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu, nelem=%u)", eid, nelem); } else if (nvlist_lookup_string(nvl, "class", &class) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent class (eid=%llu)", eid); } else { /* let internal modules see this event first */ zfs_agent_post_event(class, NULL, nvl); zsp = zed_strings_create(); nvp = NULL; while ((nvp = nvlist_next_nvpair(nvl, nvp))) _zed_event_add_nvpair(eid, zsp, nvp); _zed_event_add_env_restrict(eid, zsp, zcp->path); _zed_event_add_env_preserve(eid, zsp); _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID", "%d", (int)getpid()); _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR", "%s", zcp->zedlet_dir); subclass = _zed_event_get_subclass(class); _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS", "%s", (subclass ? subclass : class)); _zed_event_add_time_strings(eid, zsp, etime); zed_exec_process(eid, class, subclass, zcp, zsp); zed_conf_write_state(zcp, eid, etime); zed_strings_destroy(zsp); } nvlist_free(nvl); return (0); } diff --git a/cmd/zed/zed_event.h b/cmd/zed/zed_event.h index 264c377ed91a..5606f14a21a0 100644 --- a/cmd/zed/zed_event.h +++ b/cmd/zed/zed_event.h @@ -1,29 +1,29 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_EVENT_H #define ZED_EVENT_H #include int zed_event_init(struct zed_conf *zcp); void zed_event_fini(struct zed_conf *zcp); int zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[]); int zed_event_service(struct zed_conf *zcp); #endif /* !ZED_EVENT_H */ diff --git a/cmd/zed/zed_exec.c b/cmd/zed/zed_exec.c index 0a6782f0205e..1eecfa0a92c4 100644 --- a/cmd/zed/zed_exec.c +++ b/cmd/zed/zed_exec.c @@ -1,368 +1,368 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zed_exec.h" #include "zed_log.h" #include "zed_strings.h" #define ZEVENT_FILENO 3 struct launched_process_node { avl_node_t node; pid_t pid; uint64_t eid; char *name; }; static int _launched_process_node_compare(const void *x1, const void *x2) { pid_t p1; pid_t p2; assert(x1 != NULL); assert(x2 != NULL); p1 = ((const struct launched_process_node *) x1)->pid; p2 = ((const struct launched_process_node *) x2)->pid; if (p1 < p2) return (-1); else if (p1 == p2) return (0); else return (1); } static pthread_t _reap_children_tid = (pthread_t)-1; static volatile boolean_t _reap_children_stop; static avl_tree_t _launched_processes; static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER; static int16_t _launched_processes_limit; /* * Create an environment string array for passing to execve() using the * NAME=VALUE strings in container [zsp]. * Return a newly-allocated environment, or NULL on error. */ static char ** _zed_exec_create_env(zed_strings_t *zsp) { int num_ptrs; int buflen; char *buf; char **pp; char *p; const char *q; int i; int len; num_ptrs = zed_strings_count(zsp) + 1; buflen = num_ptrs * sizeof (char *); for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) buflen += strlen(q) + 1; buf = calloc(1, buflen); if (!buf) return (NULL); pp = (char **)buf; p = buf + (num_ptrs * sizeof (char *)); i = 0; for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) { pp[i] = p; len = strlen(q) + 1; memcpy(p, q, len); p += len; i++; } pp[i] = NULL; assert(buf + buflen == p); return ((char **)buf); } /* * Fork a child process to handle event [eid]. The program [prog] * in directory [dir] is executed with the environment [env]. * * The file descriptor [zfd] is the zevent_fd used to track the * current cursor location within the zevent nvlist. */ static void _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, char *env[], int zfd, boolean_t in_foreground) { char path[PATH_MAX]; int n; pid_t pid; int fd; struct launched_process_node *node; sigset_t mask; struct timespec launch_timeout = { .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, }; assert(dir != NULL); assert(prog != NULL); assert(env != NULL); assert(zfd >= 0); while (__atomic_load_n(&_launched_processes_limit, __ATOMIC_SEQ_CST) <= 0) (void) nanosleep(&launch_timeout, NULL); n = snprintf(path, sizeof (path), "%s/%s", dir, prog); if ((n < 0) || (n >= sizeof (path))) { zed_log_msg(LOG_WARNING, "Failed to fork \"%s\" for eid=%llu: %s", prog, eid, strerror(ENAMETOOLONG)); return; } (void) pthread_mutex_lock(&_launched_processes_lock); pid = fork(); if (pid < 0) { (void) pthread_mutex_unlock(&_launched_processes_lock); zed_log_msg(LOG_WARNING, "Failed to fork \"%s\" for eid=%llu: %s", prog, eid, strerror(errno)); return; } else if (pid == 0) { (void) sigemptyset(&mask); (void) sigprocmask(SIG_SETMASK, &mask, NULL); (void) umask(022); if (in_foreground && /* we're already devnulled if daemonised */ (fd = open("/dev/null", O_RDWR | O_CLOEXEC)) != -1) { (void) dup2(fd, STDIN_FILENO); (void) dup2(fd, STDOUT_FILENO); (void) dup2(fd, STDERR_FILENO); } (void) dup2(zfd, ZEVENT_FILENO); execle(path, prog, NULL, env); _exit(127); } /* parent process */ node = calloc(1, sizeof (*node)); if (node) { node->pid = pid; node->eid = eid; node->name = strdup(prog); avl_add(&_launched_processes, node); } (void) pthread_mutex_unlock(&_launched_processes_lock); __atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST); zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", prog, eid, pid); } static void _nop(int sig) {} static void * _reap_children(void *arg) { struct launched_process_node node, *pnode; pid_t pid; int status; struct rusage usage; struct sigaction sa = {}; (void) sigfillset(&sa.sa_mask); (void) sigdelset(&sa.sa_mask, SIGCHLD); (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL); (void) sigemptyset(&sa.sa_mask); sa.sa_handler = _nop; sa.sa_flags = SA_NOCLDSTOP; (void) sigaction(SIGCHLD, &sa, NULL); for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) { (void) pthread_mutex_lock(&_launched_processes_lock); pid = wait4(0, &status, WNOHANG, &usage); if (pid == 0 || pid == (pid_t)-1) { (void) pthread_mutex_unlock(&_launched_processes_lock); if (pid == 0 || errno == ECHILD) pause(); else if (errno != EINTR) zed_log_msg(LOG_WARNING, "Failed to wait for children: %s", strerror(errno)); } else { memset(&node, 0, sizeof (node)); node.pid = pid; pnode = avl_find(&_launched_processes, &node, NULL); if (pnode) { memcpy(&node, pnode, sizeof (node)); avl_remove(&_launched_processes, pnode); free(pnode); } (void) pthread_mutex_unlock(&_launched_processes_lock); __atomic_add_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST); usage.ru_utime.tv_sec += usage.ru_stime.tv_sec; usage.ru_utime.tv_usec += usage.ru_stime.tv_usec; usage.ru_utime.tv_sec += usage.ru_utime.tv_usec / (1000 * 1000); usage.ru_utime.tv_usec %= 1000 * 1000; if (WIFEXITED(status)) { zed_log_msg(LOG_INFO, "Finished \"%s\" eid=%llu pid=%d " "time=%llu.%06us exit=%d", node.name, node.eid, pid, (unsigned long long) usage.ru_utime.tv_sec, (unsigned int) usage.ru_utime.tv_usec, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { zed_log_msg(LOG_INFO, "Finished \"%s\" eid=%llu pid=%d " "time=%llu.%06us sig=%d/%s", node.name, node.eid, pid, (unsigned long long) usage.ru_utime.tv_sec, (unsigned int) usage.ru_utime.tv_usec, WTERMSIG(status), strsignal(WTERMSIG(status))); } else { zed_log_msg(LOG_INFO, "Finished \"%s\" eid=%llu pid=%d " "time=%llu.%06us status=0x%X", node.name, node.eid, (unsigned long long) usage.ru_utime.tv_sec, (unsigned int) usage.ru_utime.tv_usec, (unsigned int) status); } free(node.name); } } return (NULL); } void zed_exec_fini(void) { struct launched_process_node *node; void *ck = NULL; if (_reap_children_tid == (pthread_t)-1) return; _reap_children_stop = B_TRUE; (void) pthread_kill(_reap_children_tid, SIGCHLD); (void) pthread_join(_reap_children_tid, NULL); while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) { free(node->name); free(node); } avl_destroy(&_launched_processes); (void) pthread_mutex_destroy(&_launched_processes_lock); (void) pthread_mutex_init(&_launched_processes_lock, NULL); _reap_children_tid = (pthread_t)-1; } /* * Process the event [eid] by synchronously invoking all zedlets with a * matching class prefix. * * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir] * is matched against the event's [class], [subclass], and the "all" class * (which matches all events). * Every zedlet with a matching class prefix is invoked. * The NAME=VALUE strings in [envs] will be passed to the zedlet as * environment variables. * * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the * current cursor location within the zevent nvlist. * * Return 0 on success, -1 on error. */ int zed_exec_process(uint64_t eid, const char *class, const char *subclass, struct zed_conf *zcp, zed_strings_t *envs) { const char *class_strings[4]; const char *allclass = "all"; const char **csp; const char *z; char **e; int n; if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0) return (-1); if (_reap_children_tid == (pthread_t)-1) { _launched_processes_limit = zcp->max_jobs; if (pthread_create(&_reap_children_tid, NULL, _reap_children, NULL) != 0) return (-1); pthread_setname_np(_reap_children_tid, "reap ZEDLETs"); avl_create(&_launched_processes, _launched_process_node_compare, sizeof (struct launched_process_node), offsetof(struct launched_process_node, node)); } csp = class_strings; if (class) *csp++ = class; if (subclass) *csp++ = subclass; if (allclass) *csp++ = allclass; *csp = NULL; e = _zed_exec_create_env(envs); for (z = zed_strings_first(zcp->zedlets); z; z = zed_strings_next(zcp->zedlets)) { for (csp = class_strings; *csp; csp++) { n = strlen(*csp); if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) _zed_exec_fork_child(eid, zcp->zedlet_dir, z, e, zcp->zevent_fd, zcp->do_foreground); } } free(e); return (0); } diff --git a/cmd/zed/zed_exec.h b/cmd/zed/zed_exec.h index 2a7f721f0030..e4c8d86335b8 100644 --- a/cmd/zed/zed_exec.h +++ b/cmd/zed/zed_exec.h @@ -1,27 +1,27 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_EXEC_H #define ZED_EXEC_H #include #include "zed_strings.h" #include "zed_conf.h" void zed_exec_fini(void); int zed_exec_process(uint64_t eid, const char *class, const char *subclass, struct zed_conf *zcp, zed_strings_t *envs); #endif /* !ZED_EXEC_H */ diff --git a/cmd/zed/zed_file.c b/cmd/zed/zed_file.c index 0e7086d9e8ca..b62f68b2610f 100644 --- a/cmd/zed/zed_file.c +++ b/cmd/zed/zed_file.c @@ -1,141 +1,141 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include #include "zed_file.h" #include "zed_log.h" /* * Set an exclusive advisory lock on the open file descriptor [fd]. * Return 0 on success, 1 if a conflicting lock is held by another process, * or -1 on error (with errno set). */ int zed_file_lock(int fd) { struct flock lock; if (fd < 0) { errno = EBADF; return (-1); } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { if ((errno == EACCES) || (errno == EAGAIN)) return (1); return (-1); } return (0); } /* * Release an advisory lock held on the open file descriptor [fd]. * Return 0 on success, or -1 on error (with errno set). */ int zed_file_unlock(int fd) { struct flock lock; if (fd < 0) { errno = EBADF; return (-1); } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) return (-1); return (0); } /* * Test whether an exclusive advisory lock could be obtained for the open * file descriptor [fd]. * Return 0 if the file is not locked, >0 for the PID of another process * holding a conflicting lock, or -1 on error (with errno set). */ pid_t zed_file_is_locked(int fd) { struct flock lock; if (fd < 0) { errno = EBADF; return (-1); } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_GETLK, &lock) < 0) return (-1); if (lock.l_type == F_UNLCK) return (0); return (lock.l_pid); } #if __APPLE__ #define PROC_SELF_FD "/dev/fd" #else /* Linux-compatible layout */ #define PROC_SELF_FD "/proc/self/fd" #endif /* * Close all open file descriptors greater than or equal to [lowfd]. * Any errors encountered while closing file descriptors are ignored. */ void zed_file_close_from(int lowfd) { int errno_bak = errno; int maxfd = 0; int fd; DIR *fddir; struct dirent *fdent; if ((fddir = opendir(PROC_SELF_FD)) != NULL) { while ((fdent = readdir(fddir)) != NULL) { fd = atoi(fdent->d_name); if (fd > maxfd && fd != dirfd(fddir)) maxfd = fd; } (void) closedir(fddir); } else { maxfd = sysconf(_SC_OPEN_MAX); } for (fd = lowfd; fd < maxfd; fd++) (void) close(fd); errno = errno_bak; } diff --git a/cmd/zed/zed_file.h b/cmd/zed/zed_file.h index 5cb5e88047e0..7e3a0efcaf37 100644 --- a/cmd/zed/zed_file.h +++ b/cmd/zed/zed_file.h @@ -1,29 +1,29 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_FILE_H #define ZED_FILE_H #include #include int zed_file_lock(int fd); int zed_file_unlock(int fd); pid_t zed_file_is_locked(int fd); void zed_file_close_from(int fd); #endif /* !ZED_FILE_H */ diff --git a/cmd/zed/zed_log.c b/cmd/zed/zed_log.c index 948dad52adb8..0c4ab6f47db7 100644 --- a/cmd/zed/zed_log.c +++ b/cmd/zed/zed_log.c @@ -1,256 +1,256 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include #include #include "zed_log.h" #define ZED_LOG_MAX_LOG_LEN 1024 static struct { unsigned do_stderr:1; unsigned do_syslog:1; const char *identity; int priority; int pipe_fd[2]; } _ctx; /* * Initialize the logging subsystem. */ void zed_log_init(const char *identity) { if (identity) { const char *p = strrchr(identity, '/'); _ctx.identity = (p != NULL) ? p + 1 : identity; } else { _ctx.identity = NULL; } _ctx.pipe_fd[0] = -1; _ctx.pipe_fd[1] = -1; } /* * Shutdown the logging subsystem. */ void zed_log_fini(void) { zed_log_stderr_close(); zed_log_syslog_close(); } /* * Create pipe for communicating daemonization status between the parent and * child processes across the double-fork(). */ void zed_log_pipe_open(void) { if ((_ctx.pipe_fd[0] != -1) || (_ctx.pipe_fd[1] != -1)) zed_log_die("Invalid use of zed_log_pipe_open in PID %d", (int)getpid()); if (pipe(_ctx.pipe_fd) < 0) zed_log_die("Failed to create daemonize pipe in PID %d: %s", (int)getpid(), strerror(errno)); } /* * Close the read-half of the daemonize pipe. * * This should be called by the child after fork()ing from the parent since * the child will never read from this pipe. */ void zed_log_pipe_close_reads(void) { if (_ctx.pipe_fd[0] < 0) zed_log_die( "Invalid use of zed_log_pipe_close_reads in PID %d", (int)getpid()); if (close(_ctx.pipe_fd[0]) < 0) zed_log_die( "Failed to close reads on daemonize pipe in PID %d: %s", (int)getpid(), strerror(errno)); _ctx.pipe_fd[0] = -1; } /* * Close the write-half of the daemonize pipe. * * This should be called by the parent after fork()ing its child since the * parent will never write to this pipe. * * This should also be called by the child once initialization is complete * in order to signal the parent that it can safely exit. */ void zed_log_pipe_close_writes(void) { if (_ctx.pipe_fd[1] < 0) zed_log_die( "Invalid use of zed_log_pipe_close_writes in PID %d", (int)getpid()); if (close(_ctx.pipe_fd[1]) < 0) zed_log_die( "Failed to close writes on daemonize pipe in PID %d: %s", (int)getpid(), strerror(errno)); _ctx.pipe_fd[1] = -1; } /* * Block on reading from the daemonize pipe until signaled by the child * (via zed_log_pipe_close_writes()) that initialization is complete. * * This should only be called by the parent while waiting to exit after * fork()ing the child. */ void zed_log_pipe_wait(void) { ssize_t n; char c; if (_ctx.pipe_fd[0] < 0) zed_log_die("Invalid use of zed_log_pipe_wait in PID %d", (int)getpid()); for (;;) { n = read(_ctx.pipe_fd[0], &c, sizeof (c)); if (n < 0) { if (errno == EINTR) continue; zed_log_die( "Failed to read from daemonize pipe in PID %d: %s", (int)getpid(), strerror(errno)); } if (n == 0) { break; } } } /* * Start logging messages at the syslog [priority] level or higher to stderr. * Refer to syslog(3) for valid priority values. */ void zed_log_stderr_open(int priority) { _ctx.do_stderr = 1; _ctx.priority = priority; } /* * Stop logging messages to stderr. */ void zed_log_stderr_close(void) { if (_ctx.do_stderr) _ctx.do_stderr = 0; } /* * Start logging messages to syslog. * Refer to syslog(3) for valid option/facility values. */ void zed_log_syslog_open(int facility) { _ctx.do_syslog = 1; openlog(_ctx.identity, LOG_NDELAY | LOG_PID, facility); } /* * Stop logging messages to syslog. */ void zed_log_syslog_close(void) { if (_ctx.do_syslog) { _ctx.do_syslog = 0; closelog(); } } /* * Auxiliary function to log a message to syslog and/or stderr. */ static void _zed_log_aux(int priority, const char *fmt, va_list vargs) { char buf[ZED_LOG_MAX_LOG_LEN]; int n; if (!fmt) return; n = vsnprintf(buf, sizeof (buf), fmt, vargs); if ((n < 0) || (n >= sizeof (buf))) { buf[sizeof (buf) - 2] = '+'; buf[sizeof (buf) - 1] = '\0'; } if (_ctx.do_syslog) syslog(priority, "%s", buf); if (_ctx.do_stderr && (priority <= _ctx.priority)) fprintf(stderr, "%s\n", buf); } /* * Log a message at the given [priority] level specified by the printf-style * format string [fmt]. */ void zed_log_msg(int priority, const char *fmt, ...) { va_list vargs; if (fmt) { va_start(vargs, fmt); _zed_log_aux(priority, fmt, vargs); va_end(vargs); } } /* * Log a fatal error message specified by the printf-style format string [fmt]. */ void zed_log_die(const char *fmt, ...) { va_list vargs; if (fmt) { va_start(vargs, fmt); _zed_log_aux(LOG_ERR, fmt, vargs); va_end(vargs); } exit(EXIT_FAILURE); } diff --git a/cmd/zed/zed_log.h b/cmd/zed/zed_log.h index 0daaad11df5c..ed88ad41d7e2 100644 --- a/cmd/zed/zed_log.h +++ b/cmd/zed/zed_log.h @@ -1,44 +1,44 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_LOG_H #define ZED_LOG_H #include void zed_log_init(const char *identity); void zed_log_fini(void); void zed_log_pipe_open(void); void zed_log_pipe_close_reads(void); void zed_log_pipe_close_writes(void); void zed_log_pipe_wait(void); void zed_log_stderr_open(int priority); void zed_log_stderr_close(void); void zed_log_syslog_open(int facility); void zed_log_syslog_close(void); void zed_log_msg(int priority, const char *fmt, ...); void zed_log_die(const char *fmt, ...); #endif /* !ZED_LOG_H */ diff --git a/cmd/zed/zed_strings.c b/cmd/zed/zed_strings.c index 89964317e48c..52a86e9296fe 100644 --- a/cmd/zed/zed_strings.c +++ b/cmd/zed/zed_strings.c @@ -1,247 +1,247 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #include #include #include #include #include #include #include #include "zed_strings.h" struct zed_strings { avl_tree_t tree; avl_node_t *iteratorp; }; struct zed_strings_node { avl_node_t node; char *key; char *val; }; typedef struct zed_strings_node zed_strings_node_t; /* * Compare zed_strings_node_t nodes [x1] and [x2]. * As required for the AVL tree, return -1 for <, 0 for ==, and +1 for >. */ static int _zed_strings_node_compare(const void *x1, const void *x2) { const char *s1; const char *s2; int rv; assert(x1 != NULL); assert(x2 != NULL); s1 = ((const zed_strings_node_t *) x1)->key; assert(s1 != NULL); s2 = ((const zed_strings_node_t *) x2)->key; assert(s2 != NULL); rv = strcmp(s1, s2); if (rv < 0) return (-1); if (rv > 0) return (1); return (0); } /* * Return a new string container, or NULL on error. */ zed_strings_t * zed_strings_create(void) { zed_strings_t *zsp; zsp = calloc(1, sizeof (*zsp)); if (!zsp) return (NULL); avl_create(&zsp->tree, _zed_strings_node_compare, sizeof (zed_strings_node_t), offsetof(zed_strings_node_t, node)); zsp->iteratorp = NULL; return (zsp); } /* * Destroy the string node [np]. */ static void _zed_strings_node_destroy(zed_strings_node_t *np) { if (!np) return; if (np->key) { if (np->key != np->val) free(np->key); np->key = NULL; } if (np->val) { free(np->val); np->val = NULL; } free(np); } /* * Return a new string node for storing the string [val], or NULL on error. * If [key] is specified, it will be used to index the node; otherwise, * the string [val] will be used. */ static zed_strings_node_t * _zed_strings_node_create(const char *key, const char *val) { zed_strings_node_t *np; assert(val != NULL); np = calloc(1, sizeof (*np)); if (!np) return (NULL); np->val = strdup(val); if (!np->val) goto nomem; if (key) { np->key = strdup(key); if (!np->key) goto nomem; } else { np->key = np->val; } return (np); nomem: _zed_strings_node_destroy(np); return (NULL); } /* * Destroy the string container [zsp] and all nodes within. */ void zed_strings_destroy(zed_strings_t *zsp) { void *cookie; zed_strings_node_t *np; if (!zsp) return; cookie = NULL; while ((np = avl_destroy_nodes(&zsp->tree, &cookie))) _zed_strings_node_destroy(np); avl_destroy(&zsp->tree); free(zsp); } /* * Add a copy of the string [s] indexed by [key] to the container [zsp]. * If [key] already exists within the container [zsp], it will be replaced * with the new string [s]. * If [key] is NULL, the string [s] will be used as the key. * Return 0 on success, or -1 on error. */ int zed_strings_add(zed_strings_t *zsp, const char *key, const char *s) { zed_strings_node_t *newp, *oldp; if (!zsp || !s) { errno = EINVAL; return (-1); } if (key == s) key = NULL; newp = _zed_strings_node_create(key, s); if (!newp) return (-1); oldp = avl_find(&zsp->tree, newp, NULL); if (oldp) { avl_remove(&zsp->tree, oldp); _zed_strings_node_destroy(oldp); } avl_add(&zsp->tree, newp); return (0); } /* * Return the first string in container [zsp]. * Return NULL if there are no strings, or on error. * This can be called multiple times to re-traverse [zsp]. * XXX: Not thread-safe. */ const char * zed_strings_first(zed_strings_t *zsp) { if (!zsp) { errno = EINVAL; return (NULL); } zsp->iteratorp = avl_first(&zsp->tree); if (!zsp->iteratorp) return (NULL); return (((zed_strings_node_t *)zsp->iteratorp)->val); } /* * Return the next string in container [zsp]. * Return NULL after the last string, or on error. * This must be called after zed_strings_first(). * XXX: Not thread-safe. */ const char * zed_strings_next(zed_strings_t *zsp) { if (!zsp) { errno = EINVAL; return (NULL); } if (!zsp->iteratorp) return (NULL); zsp->iteratorp = AVL_NEXT(&zsp->tree, zsp->iteratorp); if (!zsp->iteratorp) return (NULL); return (((zed_strings_node_t *)zsp->iteratorp)->val); } /* * Return the number of strings in container [zsp], or -1 on error. */ int zed_strings_count(zed_strings_t *zsp) { if (!zsp) { errno = EINVAL; return (-1); } return (avl_numnodes(&zsp->tree)); } diff --git a/cmd/zed/zed_strings.h b/cmd/zed/zed_strings.h index 63d776f9b48f..804639592fe5 100644 --- a/cmd/zed/zed_strings.h +++ b/cmd/zed/zed_strings.h @@ -1,27 +1,27 @@ /* * This file is part of the ZFS Event Daemon (ZED). * * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. - * Refer to the ZoL git commit log for authoritative copyright attribution. + * Refer to the OpenZFS git commit log for authoritative copyright attribution. * * The contents of this file are subject to the terms of the * Common Development and Distribution License Version 1.0 (CDDL-1.0). * You can obtain a copy of the license from the top-level file * "OPENSOLARIS.LICENSE" or at . * You may not use this file except in compliance with the license. */ #ifndef ZED_STRINGS_H #define ZED_STRINGS_H typedef struct zed_strings zed_strings_t; zed_strings_t *zed_strings_create(void); void zed_strings_destroy(zed_strings_t *zsp); int zed_strings_add(zed_strings_t *zsp, const char *key, const char *s); const char *zed_strings_first(zed_strings_t *zsp); const char *zed_strings_next(zed_strings_t *zsp); int zed_strings_count(zed_strings_t *zsp); #endif /* !ZED_STRINGS_H */ diff --git a/contrib/initramfs/hooks/zfs.in b/contrib/initramfs/hooks/zfs.in index 67d27a7649b2..0a9cc87720ad 100755 --- a/contrib/initramfs/hooks/zfs.in +++ b/contrib/initramfs/hooks/zfs.in @@ -1,109 +1,109 @@ #!/bin/sh # -# Add ZoL filesystem capabilities to an initrd, usually for a native ZFS root. +# Add OpenZFS filesystem capabilities to an initrd, usually for a native ZFS root. # -# This hook installs udev rules for ZoL. +# This hook installs udev rules for OpenZFS. PREREQ="udev" # These prerequisites are provided by the zfsutils package. The zdb utility is # not strictly required, but it can be useful at the initramfs recovery prompt. COPY_EXEC_LIST="@sbindir@/zdb @sbindir@/zpool @sbindir@/zfs" COPY_EXEC_LIST="$COPY_EXEC_LIST @mounthelperdir@/mount.zfs @udevdir@/vdev_id" COPY_EXEC_LIST="$COPY_EXEC_LIST @udevdir@/zvol_id" COPY_FILE_LIST="/etc/hostid @sysconfdir@/zfs/zpool.cache" COPY_FILE_LIST="$COPY_FILE_LIST @initconfdir@/zfs" COPY_FILE_LIST="$COPY_FILE_LIST @sysconfdir@/zfs/zfs-functions" COPY_FILE_LIST="$COPY_FILE_LIST @sysconfdir@/zfs/vdev_id.conf" COPY_FILE_LIST="$COPY_FILE_LIST @udevruledir@/60-zvol.rules" COPY_FILE_LIST="$COPY_FILE_LIST @udevruledir@/69-vdev.rules" # These prerequisites are provided by the base system. COPY_EXEC_LIST="$COPY_EXEC_LIST /usr/bin/dirname /bin/hostname /sbin/blkid" COPY_EXEC_LIST="$COPY_EXEC_LIST /usr/bin/env" COPY_EXEC_LIST="$COPY_EXEC_LIST $(which systemd-ask-password)" # Explicitly specify all kernel modules because automatic dependency resolution # is unreliable on many systems. BASE_MODULES="zlib_deflate spl zavl zcommon znvpair zunicode zlua zfs icp" CRPT_MODULES="sun-ccm sun-gcm sun-ctr" MANUAL_ADD_MODULES_LIST="$BASE_MODULES" # Generic result code. RC=0 case $1 in prereqs) echo "$PREREQ" exit 0 ;; esac for ii in $COPY_EXEC_LIST do if [ ! -x "$ii" ] then echo "Error: $ii is not executable." RC=2 fi done if [ "$RC" -ne 0 ] then exit "$RC" fi . /usr/share/initramfs-tools/hook-functions mkdir -p "$DESTDIR/etc/" # ZDB uses pthreads for some functions, but the library dependency is not # automatically detected. The `find` utility and extended `cp` options are # used here because libgcc_s.so could be in a subdirectory of /lib for # multi-arch installations. cp --target-directory="$DESTDIR" --parents $(find /lib/ -type f -name libgcc_s.so.1) for ii in $COPY_EXEC_LIST do copy_exec "$ii" done for ii in $COPY_FILE_LIST do dir=$(dirname "$ii") [ -d "$dir" ] && mkdir -p "$DESTDIR/$dir" [ -f "$ii" ] && cp -p "$ii" "$DESTDIR/$ii" done for ii in $MANUAL_ADD_MODULES_LIST do manual_add_modules "$ii" done if [ -f "/etc/hostname" ] then cp -p "/etc/hostname" "$DESTDIR/etc/" else hostname >"$DESTDIR/etc/hostname" fi for ii in zfs zfs.conf spl spl.conf do if [ -f "/etc/modprobe.d/$ii" ]; then if [ ! -d "$DESTDIR/etc/modprobe.d" ]; then mkdir -p $DESTDIR/etc/modprobe.d fi cp -p "/etc/modprobe.d/$ii" $DESTDIR/etc/modprobe.d/ fi done # With pull request #1476 (not yet merged) comes a verbose warning # if /usr/bin/net doesn't exist or isn't executable. Just create # a dummy... [ ! -d "$DESTDIR/usr/bin" ] && mkdir -p "$DESTDIR/usr/bin" if [ ! -x "$DESTDIR/usr/bin/net" ]; then touch "$DESTDIR/usr/bin/net" chmod +x "$DESTDIR/usr/bin/net" fi exit 0 diff --git a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py index 87138b305f73..d949d88d5a1e 100644 --- a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py @@ -1,4380 +1,4380 @@ # # Copyright 2015 ClusterHQ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """ Tests for `libzfs_core` operations. These are mostly functional and conformance tests that validate that the operations produce expected effects or fail with expected exceptions. """ from __future__ import absolute_import, division, print_function import unittest import contextlib import errno import filecmp import os import platform import resource import shutil import stat import subprocess import sys import tempfile import time import uuid import itertools import zlib from .. import _libzfs_core as lzc from .. import exceptions as lzc_exc from .._nvlist import packed_nvlist_out def _print(*args): for arg in args: print(arg, end=' ') print() @contextlib.contextmanager def suppress(exceptions=None): try: yield except BaseException as e: if exceptions is None or isinstance(e, exceptions): pass else: raise @contextlib.contextmanager def _zfs_mount(fs): mntdir = tempfile.mkdtemp() if platform.system() == 'SunOS': mount_cmd = ['mount', '-F', 'zfs', fs, mntdir] else: mount_cmd = ['mount', '-t', 'zfs', fs, mntdir] unmount_cmd = ['umount', '-f', mntdir] try: subprocess.check_output(mount_cmd, stderr=subprocess.STDOUT) try: yield mntdir finally: with suppress(): subprocess.check_output(unmount_cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print('failed to mount %s @ %s : %s' % (fs, mntdir, e.output)) raise finally: os.rmdir(mntdir) # XXX On illumos it is impossible to explicitly mount a snapshot. # So, either we need to implicitly mount it using .zfs/snapshot/ # or we need to create a clone and mount it readonly (and discard # it afterwards). # At the moment the former approach is implemented. # This dictionary is used to keep track of mounted filesystems # (not snapshots), so that we do not try to mount a filesystem # more than once in the case more than one snapshot of the # filesystem is accessed from the same context or the filesystem # and its snapshot are accessed. _mnttab = {} @contextlib.contextmanager def _illumos_mount_fs(fs): if fs in _mnttab: yield _mnttab[fs] else: with _zfs_mount(fs) as mntdir: _mnttab[fs] = mntdir try: yield mntdir finally: _mnttab.pop(fs, None) @contextlib.contextmanager def _illumos_mount_snap(fs): (base, snap) = fs.split('@', 1) with _illumos_mount_fs(base) as mntdir: yield os.path.join(mntdir, '.zfs', 'snapshot', snap) @contextlib.contextmanager def _zfs_mount_illumos(fs): if '@' not in fs: with _illumos_mount_fs(fs) as mntdir: yield mntdir else: with _illumos_mount_snap(fs) as mntdir: yield mntdir if platform.system() == 'SunOS': zfs_mount = _zfs_mount_illumos else: zfs_mount = _zfs_mount @contextlib.contextmanager def cleanup_fd(): fd = os.open('/dev/zfs', os.O_EXCL) try: yield fd finally: os.close(fd) @contextlib.contextmanager def os_open(name, mode): fd = os.open(name, mode) try: yield fd finally: os.close(fd) @contextlib.contextmanager def dev_null(): with tempfile.TemporaryFile(suffix='.zstream') as fd: yield fd.fileno() @contextlib.contextmanager def dev_zero(): with os_open('/dev/zero', os.O_RDONLY) as fd: yield fd @contextlib.contextmanager def temp_file_in_fs(fs): with zfs_mount(fs) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() yield f.name def make_snapshots(fs, before, modified, after): def _maybe_snap(snap): if snap is not None: if not snap.startswith(fs): snap = fs + b'@' + snap lzc.lzc_snapshot([snap]) return snap before = _maybe_snap(before) with temp_file_in_fs(fs) as name: modified = _maybe_snap(modified) after = _maybe_snap(after) return (name, (before, modified, after)) @contextlib.contextmanager def streams(fs, first, second): (filename, snaps) = make_snapshots(fs, None, first, second) with tempfile.TemporaryFile(suffix='.zstream') as full: lzc.lzc_send(snaps[1], None, full.fileno()) full.seek(0) if snaps[2] is not None: with tempfile.TemporaryFile(suffix='.zstream') as incremental: lzc.lzc_send(snaps[2], snaps[1], incremental.fileno()) incremental.seek(0) yield (filename, (full, incremental)) else: yield (filename, (full, None)) @contextlib.contextmanager def encrypted_filesystem(): fs = ZFSTest.pool.getFilesystem(b"encrypted") name = fs.getName() filename = None key = os.urandom(lzc.WRAPPING_KEY_LEN) with tempfile.NamedTemporaryFile() as f: filename = "file://" + f.name props = { b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM, b"keylocation": filename.encode(), b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW, } lzc.lzc_create(name, 'zfs', props=props, key=key) yield (name, key) def runtimeSkipIf(check_method, message): def _decorator(f): def _f(_self, *args, **kwargs): if check_method(_self): return _self.skipTest(message) else: return f(_self, *args, **kwargs) _f.__name__ = f.__name__ return _f return _decorator def skipIfFeatureAvailable(feature, message): return runtimeSkipIf( lambda _self: _self.__class__.pool.isPoolFeatureAvailable(feature), message) def skipUnlessFeatureEnabled(feature, message): return runtimeSkipIf( lambda _self: not _self.__class__.pool.isPoolFeatureEnabled(feature), message) def skipUnlessBookmarksSupported(f): return skipUnlessFeatureEnabled( 'bookmarks', 'bookmarks are not enabled')(f) def snap_always_unmounted_before_destruction(): - # Apparently ZoL automatically unmounts the snapshot + # Apparently OpenZFS automatically unmounts the snapshot # only if it is mounted at its default .zfs/snapshot - # mountpoint. + # mountpoint under Linux. return ( platform.system() != 'Linux', 'snapshot is not auto-unmounted') def illumos_bug_6379(): # zfs_ioc_hold() panics on a bad cleanup fd return ( platform.system() == 'SunOS', 'see https://www.illumos.org/issues/6379') def needs_support(function): return unittest.skipUnless( lzc.is_supported(function), '{} not available'.format(function.__name__)) class ZFSTest(unittest.TestCase): POOL_FILE_SIZE = 128 * 1024 * 1024 FILESYSTEMS = [b'fs1', b'fs2', b'fs1/fs'] pool = None misc_pool = None readonly_pool = None @classmethod def setUpClass(cls): try: cls.pool = _TempPool(filesystems=cls.FILESYSTEMS) cls.misc_pool = _TempPool() cls.readonly_pool = _TempPool( filesystems=cls.FILESYSTEMS, readonly=True) cls.pools = [cls.pool, cls.misc_pool, cls.readonly_pool] except Exception: cls._cleanUp() raise @classmethod def tearDownClass(cls): cls._cleanUp() @classmethod def _cleanUp(cls): for pool in [cls.pool, cls.misc_pool, cls.readonly_pool]: if pool is not None: pool.cleanUp() def setUp(self): pass def tearDown(self): for pool in ZFSTest.pools: pool.reset() def assertExists(self, name): self.assertTrue( lzc.lzc_exists(name), 'ZFS dataset %s does not exist' % (name, )) def assertNotExists(self, name): self.assertFalse( lzc.lzc_exists(name), 'ZFS dataset %s exists' % (name, )) def test_exists(self): self.assertExists(ZFSTest.pool.makeName()) def test_exists_in_ro_pool(self): self.assertExists(ZFSTest.readonly_pool.makeName()) def test_exists_failure(self): self.assertNotExists(ZFSTest.pool.makeName(b'nonexistent')) def test_create_fs(self): name = ZFSTest.pool.makeName(b"fs1/fs/test1") lzc.lzc_create(name) self.assertExists(name) def test_create_zvol(self): name = ZFSTest.pool.makeName(b"fs1/fs/zvol") props = {b"volsize": 1024 * 1024} lzc.lzc_create(name, ds_type='zvol', props=props) self.assertExists(name) # On Gentoo with ZFS 0.6.5.4 the volume is busy # and can not be destroyed right after its creation. # A reason for this is unknown at the moment. # Because of that the post-test clean up could fail. time.sleep(0.1) def test_create_fs_with_prop(self): name = ZFSTest.pool.makeName(b"fs1/fs/test2") props = {b"atime": 0} lzc.lzc_create(name, props=props) self.assertExists(name) def test_create_fs_wrong_ds_type(self): name = ZFSTest.pool.makeName(b"fs1/fs/test1") with self.assertRaises(lzc_exc.DatasetTypeInvalid): lzc.lzc_create(name, ds_type='wrong') def test_create_fs_below_zvol(self): name = ZFSTest.pool.makeName(b"fs1/fs/zvol") props = {b"volsize": 1024 * 1024} lzc.lzc_create(name, ds_type='zvol', props=props) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_create(name + b'/fs') def test_create_zvol_below_zvol(self): name = ZFSTest.pool.makeName(b"fs1/fs/zvol") props = {b"volsize": 1024 * 1024} lzc.lzc_create(name, ds_type='zvol', props=props) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_create(name + b'/zvol', ds_type='zvol', props=props) def test_create_fs_duplicate(self): name = ZFSTest.pool.makeName(b"fs1/fs/test6") lzc.lzc_create(name) with self.assertRaises(lzc_exc.FilesystemExists): lzc.lzc_create(name) def test_create_fs_in_ro_pool(self): name = ZFSTest.readonly_pool.makeName(b"fs") with self.assertRaises(lzc_exc.ReadOnlyPool): lzc.lzc_create(name) def test_create_fs_without_parent(self): name = ZFSTest.pool.makeName(b"fs1/nonexistent/test") with self.assertRaises(lzc_exc.ParentNotFound): lzc.lzc_create(name) self.assertNotExists(name) def test_create_fs_in_nonexistent_pool(self): name = b"no-such-pool/fs" with self.assertRaises(lzc_exc.ParentNotFound): lzc.lzc_create(name) self.assertNotExists(name) def test_create_fs_with_invalid_prop(self): name = ZFSTest.pool.makeName(b"fs1/fs/test3") props = {b"BOGUS": 0} with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_create(name, 'zfs', props) self.assertNotExists(name) def test_create_fs_with_invalid_prop_type(self): name = ZFSTest.pool.makeName(b"fs1/fs/test4") props = {b"recordsize": b"128k"} with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_create(name, 'zfs', props) self.assertNotExists(name) def test_create_fs_with_invalid_prop_val(self): name = ZFSTest.pool.makeName(b"fs1/fs/test5") props = {b"atime": 20} with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_create(name, 'zfs', props) self.assertNotExists(name) def test_create_fs_with_invalid_name(self): name = ZFSTest.pool.makeName(b"@badname") with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_create(name) self.assertNotExists(name) def test_create_fs_with_invalid_pool_name(self): name = b"bad!pool/fs" with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_create(name) self.assertNotExists(name) def test_create_encrypted_fs(self): fs = ZFSTest.pool.getFilesystem(b"encrypted") name = fs.getName() filename = None with tempfile.NamedTemporaryFile() as f: filename = "file://" + f.name props = { b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM, b"keylocation": filename.encode(), b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW, } key = os.urandom(lzc.WRAPPING_KEY_LEN) lzc.lzc_create(name, 'zfs', props=props, key=key) self.assertEqual(fs.getProperty("encryption"), b"aes-256-ccm") self.assertEqual(fs.getProperty("encryptionroot"), name) self.assertEqual(fs.getProperty("keylocation"), filename.encode()) self.assertEqual(fs.getProperty("keyformat"), b"raw") def test_snapshot(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] lzc.lzc_snapshot(snaps) self.assertExists(snapname) def test_snapshot_empty_list(self): lzc.lzc_snapshot([]) def test_snapshot_user_props(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] props = {b"user:foo": b"bar"} lzc.lzc_snapshot(snaps, props) self.assertExists(snapname) def test_snapshot_invalid_props(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] props = {b"foo": b"bar"} with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps, props) self.assertEqual(len(ctx.exception.errors), len(snaps)) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PropertyInvalid) self.assertNotExists(snapname) def test_snapshot_ro_pool(self): snapname1 = ZFSTest.readonly_pool.makeName(b"@snap") snapname2 = ZFSTest.readonly_pool.makeName(b"fs1@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.ReadOnlyPool) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_nonexistent_pool(self): snapname = b"no-such-pool@snap" snaps = [snapname] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) def test_snapshot_nonexistent_fs(self): snapname = ZFSTest.pool.makeName(b"nonexistent@snap") snaps = [snapname] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) def test_snapshot_nonexistent_and_existent_fs(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_multiple_snapshots_nonexistent_fs(self): snapname1 = ZFSTest.pool.makeName(b"nonexistent@snap1") snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap2") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # XXX two errors should be reported but alas self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.DuplicateSnapshots) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_multiple_snapshots_multiple_nonexistent_fs(self): snapname1 = ZFSTest.pool.makeName(b"nonexistent1@snap") snapname2 = ZFSTest.pool.makeName(b"nonexistent2@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.FilesystemNotFound) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_already_exists(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotExists) def test_multiple_snapshots_for_same_fs(self): snapname1 = ZFSTest.pool.makeName(b"@snap1") snapname2 = ZFSTest.pool.makeName(b"@snap2") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.DuplicateSnapshots) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_multiple_snapshots(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) self.assertExists(snapname1) self.assertExists(snapname2) def test_multiple_existing_snapshots(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotExists) def test_multiple_new_and_existing_snapshots(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") snapname3 = ZFSTest.pool.makeName(b"fs2@snap") snaps = [snapname1, snapname2] more_snaps = snaps + [snapname3] lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(more_snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotExists) self.assertNotExists(snapname3) def test_snapshot_multiple_errors(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap") snapname3 = ZFSTest.pool.makeName(b"fs1@snap") snaps = [snapname1] more_snaps = [snapname1, snapname2, snapname3] # create 'snapname1' snapshot lzc.lzc_snapshot(snaps) # attempt to create 3 snapshots: # 1. duplicate snapshot name # 2. refers to filesystem that doesn't exist # 3. could have succeeded if not for 1 and 2 with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(more_snaps) # It seems that FilesystemNotFound overrides the other error, # but it doesn't have to. self.assertGreater(len(ctx.exception.errors), 0) for e in ctx.exception.errors: self.assertIsInstance( e, (lzc_exc.SnapshotExists, lzc_exc.FilesystemNotFound)) self.assertNotExists(snapname2) self.assertNotExists(snapname3) def test_snapshot_different_pools(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.misc_pool.makeName(b"@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_different_pools_ro_pool(self): snapname1 = ZFSTest.pool.makeName(b"@snap") snapname2 = ZFSTest.readonly_pool.makeName(b"@snap") snaps = [snapname1, snapname2] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: # NB: depending on whether the first attempted snapshot is # for the read-only pool a different error is reported. self.assertIsInstance( e, (lzc_exc.PoolsDiffer, lzc_exc.ReadOnlyPool)) self.assertNotExists(snapname1) self.assertNotExists(snapname2) def test_snapshot_invalid_name(self): snapname1 = ZFSTest.pool.makeName(b"@bad&name") snapname2 = ZFSTest.pool.makeName(b"fs1@bad*name") snapname3 = ZFSTest.pool.makeName(b"fs2@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertIsNone(e.name) def test_snapshot_too_long_complete_name(self): snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@") snapname2 = ZFSTest.pool.makeTooLongName(b"fs2@") snapname3 = ZFSTest.pool.makeName(b"@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) self.assertEqual(len(ctx.exception.errors), 2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertIsNotNone(e.name) def test_snapshot_too_long_snap_name(self): snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@") snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@") snapname3 = ZFSTest.pool.makeName(b"@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotFailure) as ctx: lzc.lzc_snapshot(snaps) # NB: one common error is reported. self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertIsNone(e.name) def test_destroy_nonexistent_snapshot(self): lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], False) lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], True) def test_destroy_snapshot_of_nonexistent_pool(self): with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps([b"no-such-pool@snap"], False) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolNotFound) with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps([b"no-such-pool@snap"], True) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolNotFound) # NB: note the difference from the nonexistent pool test. def test_destroy_snapshot_of_nonexistent_fs(self): lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"nonexistent@snap")], False) lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"nonexistent@snap")], True) # Apparently the name is not checked for validity. @unittest.expectedFailure def test_destroy_invalid_snap_name(self): with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"@non$&*existent")], False) with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps( [ZFSTest.pool.makeName(b"@non$&*existent")], True) # Apparently the full name is not checked for length. @unittest.expectedFailure def test_destroy_too_long_full_snap_name(self): snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@") snaps = [snapname1] with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps(snaps, False) with self.assertRaises(lzc_exc.SnapshotDestructionFailure): lzc.lzc_destroy_snaps(snaps, True) def test_destroy_too_long_short_snap_name(self): snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@") snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@") snapname3 = ZFSTest.pool.makeName(b"@snap") snaps = [snapname1, snapname2, snapname3] with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps(snaps, False) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) @unittest.skipUnless(*snap_always_unmounted_before_destruction()) def test_destroy_mounted_snap(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with zfs_mount(snap): # the snapshot should be force-unmounted lzc.lzc_destroy_snaps([snap], defer=False) self.assertNotExists(snap) def test_clone(self): # NB: note the special name for the snapshot. # Since currently we can not destroy filesystems, # it would be impossible to destroy the snapshot, # so no point in attempting to clean it up. snapname = ZFSTest.pool.makeName(b"fs2@origin1") name = ZFSTest.pool.makeName(b"fs1/fs/clone1") lzc.lzc_snapshot([snapname]) lzc.lzc_clone(name, snapname) self.assertExists(name) def test_clone_nonexistent_snapshot(self): snapname = ZFSTest.pool.makeName(b"fs2@nonexistent") name = ZFSTest.pool.makeName(b"fs1/fs/clone2") # XXX The error should be SnapshotNotFound # but limitations of C interface do not allow # to differentiate between the errors. with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_nonexistent_parent_fs(self): snapname = ZFSTest.pool.makeName(b"fs2@origin3") name = ZFSTest.pool.makeName(b"fs1/nonexistent/clone3") lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_to_nonexistent_pool(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = b"no-such-pool/fs" lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_snap_name(self): # Use a valid filesystem name of filesystem that # exists as a snapshot name snapname = ZFSTest.pool.makeName(b"fs1/fs") name = ZFSTest.pool.makeName(b"fs2/clone") with self.assertRaises(lzc_exc.SnapshotNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_snap_name_2(self): # Use a valid filesystem name of filesystem that # doesn't exist as a snapshot name snapname = ZFSTest.pool.makeName(b"fs1/nonexistent") name = ZFSTest.pool.makeName(b"fs2/clone") with self.assertRaises(lzc_exc.SnapshotNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_name(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = ZFSTest.pool.makeName(b"fs1/bad#name") lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.FilesystemNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_invalid_pool_name(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = b"bad!pool/fs1" lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.FilesystemNameInvalid): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_across_pools(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = ZFSTest.misc_pool.makeName(b"clone1") lzc.lzc_snapshot([snapname]) with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_clone_across_pools_to_ro_pool(self): snapname = ZFSTest.pool.makeName(b"fs2@snap") name = ZFSTest.readonly_pool.makeName(b"fs1/clone1") lzc.lzc_snapshot([snapname]) # it's legal to report either of the conditions with self.assertRaises((lzc_exc.ReadOnlyPool, lzc_exc.PoolsDiffer)): lzc.lzc_clone(name, snapname) self.assertNotExists(name) def test_destroy_cloned_fs(self): snapname1 = ZFSTest.pool.makeName(b"fs2@origin4") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") clonename = ZFSTest.pool.makeName(b"fs1/fs/clone4") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) lzc.lzc_clone(clonename, snapname1) with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps(snaps, False) self.assertEqual(len(ctx.exception.errors), 1) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotIsCloned) for snap in snaps: self.assertExists(snap) def test_deferred_destroy_cloned_fs(self): snapname1 = ZFSTest.pool.makeName(b"fs2@origin5") snapname2 = ZFSTest.pool.makeName(b"fs1@snap") clonename = ZFSTest.pool.makeName(b"fs1/fs/clone5") snaps = [snapname1, snapname2] lzc.lzc_snapshot(snaps) lzc.lzc_clone(clonename, snapname1) lzc.lzc_destroy_snaps(snaps, defer=True) self.assertExists(snapname1) self.assertNotExists(snapname2) def test_rollback(self): name = ZFSTest.pool.makeName(b"fs1") snapname = name + b"@snap" lzc.lzc_snapshot([snapname]) ret = lzc.lzc_rollback(name) self.assertEqual(ret, snapname) def test_rollback_2(self): name = ZFSTest.pool.makeName(b"fs1") snapname1 = name + b"@snap1" snapname2 = name + b"@snap2" lzc.lzc_snapshot([snapname1]) lzc.lzc_snapshot([snapname2]) ret = lzc.lzc_rollback(name) self.assertEqual(ret, snapname2) def test_rollback_no_snaps(self): name = ZFSTest.pool.makeName(b"fs1") with self.assertRaises(lzc_exc.SnapshotNotFound): lzc.lzc_rollback(name) def test_rollback_non_existent_fs(self): name = ZFSTest.pool.makeName(b"nonexistent") with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_rollback(name) def test_rollback_invalid_fs_name(self): name = ZFSTest.pool.makeName(b"bad~name") with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_rollback(name) def test_rollback_snap_name(self): name = ZFSTest.pool.makeName(b"fs1@snap") with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_rollback(name) def test_rollback_snap_name_2(self): name = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([name]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_rollback(name) def test_rollback_too_long_fs_name(self): name = ZFSTest.pool.makeTooLongName() with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_rollback(name) def test_rollback_to_snap_name(self): name = ZFSTest.pool.makeName(b"fs1") snap = name + b"@snap" lzc.lzc_snapshot([snap]) lzc.lzc_rollback_to(name, snap) def test_rollback_to_not_latest(self): fsname = ZFSTest.pool.makeName(b'fs1') snap1 = fsname + b"@snap1" snap2 = fsname + b"@snap2" lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotNotLatest): lzc.lzc_rollback_to(fsname, fsname + b"@snap1") @skipUnlessBookmarksSupported def test_bookmarks(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) lzc.lzc_bookmark(bmark_dict) @skipUnlessBookmarksSupported def test_bookmarks_2(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) lzc.lzc_bookmark(bmark_dict) lzc.lzc_destroy_snaps(snaps, defer=False) @skipUnlessBookmarksSupported def test_bookmark_copying(self): snaps = [ZFSTest.pool.makeName(s) for s in [ b'fs1@snap1', b'fs1@snap2', b'fs2@snap1']] bmarks = [ZFSTest.pool.makeName(x) for x in [ b'fs1#bmark1', b'fs1#bmark2', b'fs2#bmark1']] bmarks_copies = [ZFSTest.pool.makeName(x) for x in [ b'fs1#bmark1_copy', b'fs1#bmark2_copy', b'fs2#bmark1_copy']] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} bmark_copies_dict = {x: y for x, y in zip(bmarks_copies, bmarks)} for snap in snaps: lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) lzc.lzc_bookmark(bmark_copies_dict) lzc.lzc_destroy_bookmarks(bmarks_copies) lzc.lzc_destroy_bookmarks(bmarks) lzc.lzc_destroy_snaps(snaps, defer=False) @skipUnlessBookmarksSupported def test_bookmarks_empty(self): lzc.lzc_bookmark({}) @skipUnlessBookmarksSupported def test_bookmarks_foreign_source(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkMismatch) @skipUnlessBookmarksSupported def test_bookmarks_invalid_name(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeName(b'fs1#bmark!')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) @skipUnlessBookmarksSupported def test_bookmarks_invalid_name_2(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeName(b'fs1@bmark')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) @skipUnlessBookmarksSupported def test_bookmarks_too_long_name(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeTooLongName(b'fs1#')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) @skipUnlessBookmarksSupported def test_bookmarks_too_long_name_2(self): snaps = [ZFSTest.pool.makeName(b'fs1@snap1')] bmarks = [ZFSTest.pool.makeTooLongComponent(b'fs1#')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) @skipUnlessBookmarksSupported def test_bookmarks_foreign_sources(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs2#bmark1'), ZFSTest.pool.makeName(b'fs1#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkMismatch) @skipUnlessBookmarksSupported def test_bookmarks_partially_foreign_sources(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs2#bmark'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkMismatch) @skipUnlessBookmarksSupported def test_bookmarks_cross_pool(self): snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.misc_pool.makeName(b'@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.misc_pool.makeName(b'#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps[0:1]) lzc.lzc_snapshot(snaps[1:2]) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) @skipUnlessBookmarksSupported def test_bookmarks_missing_snap(self): fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')] snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} lzc.lzc_snapshot(snaps[0:1]) # only create fs1@snap1 with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotNotFound) # no new bookmarks are created if one or more sources do not exist for fs in fss: fsbmarks = lzc.lzc_get_bookmarks(fs) self.assertEqual(len(fsbmarks), 0) @skipUnlessBookmarksSupported def test_bookmarks_missing_snaps(self): fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')] snaps = [ZFSTest.pool.makeName( b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')] bmarks = [ZFSTest.pool.makeName( b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')] bmark_dict = {x: y for x, y in zip(bmarks, snaps)} # do not create any snapshots with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotNotFound) # no new bookmarks are created if one or more sources do not exist for fs in fss: fsbmarks = lzc.lzc_get_bookmarks(fs) self.assertEqual(len(fsbmarks), 0) @skipUnlessBookmarksSupported def test_bookmarks_for_the_same_snap(self): snap = ZFSTest.pool.makeName(b'fs1@snap1') bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') bmark_dict = {bmark1: snap, bmark2: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) @skipUnlessBookmarksSupported def test_bookmarks_for_the_same_snap_2(self): snap = ZFSTest.pool.makeName(b'fs1@snap1') bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') bmark_dict1 = {bmark1: snap} bmark_dict2 = {bmark2: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict1) lzc.lzc_bookmark(bmark_dict2) @skipUnlessBookmarksSupported def test_bookmarks_duplicate_name(self): snap1 = ZFSTest.pool.makeName(b'fs1@snap1') snap2 = ZFSTest.pool.makeName(b'fs1@snap2') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict1 = {bmark: snap1} bmark_dict2 = {bmark: snap2} lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark(bmark_dict1) with self.assertRaises(lzc_exc.BookmarkFailure) as ctx: lzc.lzc_bookmark(bmark_dict2) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.BookmarkExists) @skipUnlessBookmarksSupported def test_get_bookmarks(self): snap1 = ZFSTest.pool.makeName(b'fs1@snap1') snap2 = ZFSTest.pool.makeName(b'fs1@snap2') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1') bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2') bmark_dict1 = {bmark1: snap1, bmark2: snap2} bmark_dict2 = {bmark: snap2} lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark(bmark_dict1) lzc.lzc_bookmark(bmark_dict2) lzc.lzc_destroy_snaps([snap1, snap2], defer=False) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) self.assertEqual(len(bmarks), 3) for b in b'bmark', b'bmark1', b'bmark2': self.assertIn(b, bmarks) self.assertIsInstance(bmarks[b], dict) self.assertEqual(len(bmarks[b]), 0) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'), [b'guid', b'createtxg', b'creation']) self.assertEqual(len(bmarks), 3) for b in b'bmark', b'bmark1', b'bmark2': self.assertIn(b, bmarks) self.assertIsInstance(bmarks[b], dict) self.assertEqual(len(bmarks[b]), 3) @skipUnlessBookmarksSupported def test_get_bookmarks_invalid_property(self): snap = ZFSTest.pool.makeName(b'fs1@snap') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict = {bmark: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) bmarks = lzc.lzc_get_bookmarks( ZFSTest.pool.makeName(b'fs1'), [b'badprop']) self.assertEqual(len(bmarks), 1) for b in (b'bmark', ): self.assertIn(b, bmarks) self.assertIsInstance(bmarks[b], dict) self.assertEqual(len(bmarks[b]), 0) @skipUnlessBookmarksSupported def test_get_bookmarks_nonexistent_fs(self): with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'nonexistent')) @skipUnlessBookmarksSupported def test_destroy_bookmarks(self): snap = ZFSTest.pool.makeName(b'fs1@snap') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict = {bmark: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) lzc.lzc_destroy_bookmarks( [bmark, ZFSTest.pool.makeName(b'fs1#nonexistent')]) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) self.assertEqual(len(bmarks), 0) @skipUnlessBookmarksSupported def test_destroy_bookmarks_invalid_name(self): snap = ZFSTest.pool.makeName(b'fs1@snap') bmark = ZFSTest.pool.makeName(b'fs1#bmark') bmark_dict = {bmark: snap} lzc.lzc_snapshot([snap]) lzc.lzc_bookmark(bmark_dict) with self.assertRaises(lzc_exc.BookmarkDestructionFailure) as ctx: lzc.lzc_destroy_bookmarks( [bmark, ZFSTest.pool.makeName(b'fs1/nonexistent')]) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1')) self.assertEqual(len(bmarks), 1) self.assertIn(b'bmark', bmarks) @skipUnlessBookmarksSupported def test_destroy_bookmark_nonexistent_fs(self): lzc.lzc_destroy_bookmarks( [ZFSTest.pool.makeName(b'nonexistent#bmark')]) @skipUnlessBookmarksSupported def test_destroy_bookmarks_empty(self): lzc.lzc_bookmark({}) def test_snaprange_space(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_snaprange_space(snap1, snap2) self.assertIsInstance(space, (int, int)) space = lzc.lzc_snaprange_space(snap2, snap3) self.assertIsInstance(space, (int, int)) space = lzc.lzc_snaprange_space(snap1, snap3) self.assertIsInstance(space, (int, int)) def test_snaprange_space_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_snaprange_space(snap1, snap2) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_snaprange_space(snap2, snap3) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_snaprange_space(snap1, snap3) self.assertGreater(space, 1024 * 1024) def test_snaprange_space_same_snap(self): snap = ZFSTest.pool.makeName(b"fs1@snap") with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap]) space = lzc.lzc_snaprange_space(snap, snap) self.assertGreater(space, 1024 * 1024) self.assertAlmostEqual(space, 1024 * 1024, delta=1024 * 1024 // 20) def test_snaprange_space_wrong_order(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_snaprange_space(snap2, snap1) def test_snaprange_space_unrelated(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_snaprange_space(snap1, snap2) def test_snaprange_space_across_pools(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.misc_pool.makeName(b"@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_snaprange_space(snap1, snap2) def test_snaprange_space_nonexistent(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_snaprange_space(snap1, snap2) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_snaprange_space(snap2, snap1) self.assertEqual(ctx.exception.name, snap1) def test_snaprange_space_invalid_name(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@sn#p") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap1, snap2) def test_snaprange_space_not_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap1, snap2) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap2, snap1) def test_snaprange_space_not_snap_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap1, snap2) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_snaprange_space(snap2, snap1) def test_send_space(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_send_space(snap2, snap1) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap3, snap2) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap3, snap1) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap1) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap2) self.assertIsInstance(space, (int, int)) space = lzc.lzc_send_space(snap3) self.assertIsInstance(space, (int, int)) def test_send_space_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") snap3 = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap1]) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap2]) lzc.lzc_snapshot([snap3]) space = lzc.lzc_send_space(snap2, snap1) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_send_space(snap3, snap2) space = lzc.lzc_send_space(snap3, snap1) space_empty = lzc.lzc_send_space(snap1) space = lzc.lzc_send_space(snap2) self.assertGreater(space, 1024 * 1024) space = lzc.lzc_send_space(snap3) self.assertEqual(space, space_empty) def test_send_space_same_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send_space(snap1, snap1) def test_send_space_wrong_order(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send_space(snap1, snap2) def test_send_space_unrelated(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send_space(snap1, snap2) def test_send_space_across_pools(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.misc_pool.makeName(b"@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_send_space(snap1, snap2) def test_send_space_nonexistent(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send_space(snap1, snap2) self.assertEqual(ctx.exception.name, snap1) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send_space(snap2, snap1) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send_space(snap2) self.assertEqual(ctx.exception.name, snap2) def test_send_space_invalid_name(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@sn!p") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send_space(snap2, snap1) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send_space(snap2) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send_space(snap1, snap2) self.assertEqual(ctx.exception.name, snap2) def test_send_space_not_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap1, snap2) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2, snap1) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2) def test_send_space_not_snap_2(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2, snap1) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send_space(snap2) def test_send_full(self): snap = ZFSTest.pool.makeName(b"fs1@snap") with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as output: estimate = lzc.lzc_send_space(snap) fd = output.fileno() lzc.lzc_send(snap, None, fd) st = os.fstat(fd) # 5%, arbitrary. self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20) def test_send_incremental(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: estimate = lzc.lzc_send_space(snap2, snap1) fd = output.fileno() lzc.lzc_send(snap2, snap1, fd) st = os.fstat(fd) # 5%, arbitrary. self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20) def test_send_flags(self): flags = ['embedded_data', 'large_blocks', 'compress', 'raw'] snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) for c in range(len(flags)): for flag in itertools.permutations(flags, c + 1): with dev_null() as fd: lzc.lzc_send(snap, None, fd, list(flag)) def test_send_unknown_flags(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with dev_null() as fd: with self.assertRaises(lzc_exc.UnknownStreamFeature): lzc.lzc_send(snap, None, fd, ['embedded_data', 'UNKNOWN']) def test_send_same_snap(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") lzc.lzc_snapshot([snap1]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send(snap1, snap1, fd) def test_send_wrong_order(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send(snap1, snap2, fd) def test_send_unrelated(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotMismatch): lzc.lzc_send(snap1, snap2, fd) def test_send_across_pools(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.misc_pool.makeName(b"@snap2") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.PoolsDiffer): lzc.lzc_send(snap1, snap2, fd) def test_send_nonexistent(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") lzc.lzc_snapshot([snap1]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send(snap1, snap2, fd) self.assertEqual(ctx.exception.name, snap1) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send(snap2, snap1, fd) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx: lzc.lzc_send(snap2, None, fd) self.assertEqual(ctx.exception.name, snap2) def test_send_invalid_name(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@sn!p") lzc.lzc_snapshot([snap1]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send(snap2, snap1, fd) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send(snap2, None, fd) self.assertEqual(ctx.exception.name, snap2) with self.assertRaises(lzc_exc.NameInvalid) as ctx: lzc.lzc_send(snap1, snap2, fd) self.assertEqual(ctx.exception.name, snap2) # XXX Although undocumented the API allows to create an incremental # or full stream for a filesystem as if a temporary unnamed snapshot # is taken at some time after the call is made and before the stream # starts being produced. def test_send_filesystem(self): snap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() lzc.lzc_send(fs, snap, fd) lzc.lzc_send(fs, None, fd) def test_send_from_filesystem(self): snap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.makeName(b"fs1") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send(snap, fs, fd) @skipUnlessBookmarksSupported def test_send_bookmark(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") bmark = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark({bmark: snap2}) lzc.lzc_destroy_snaps([snap2], defer=False) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send(bmark, snap1, fd) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_send(bmark, None, fd) @skipUnlessBookmarksSupported def test_send_from_bookmark(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") bmark = ZFSTest.pool.makeName(b"fs1#bmark") lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) lzc.lzc_bookmark({bmark: snap1}) lzc.lzc_destroy_snaps([snap1], defer=False) with tempfile.TemporaryFile(suffix='.zstream') as output: fd = output.fileno() lzc.lzc_send(snap2, bmark, fd) def test_send_bad_fd(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: bad_fd = tmp.fileno() with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, bad_fd) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_send_bad_fd_2(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, -2) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_send_bad_fd_3(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: bad_fd = tmp.fileno() (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) bad_fd = hard + 1 with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, bad_fd) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_send_to_broken_pipe(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) if sys.version_info < (3, 0): proc = subprocess.Popen(['true'], stdin=subprocess.PIPE) proc.wait() with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, proc.stdin.fileno()) self.assertEqual(ctx.exception.errno, errno.EPIPE) else: with subprocess.Popen(['true'], stdin=subprocess.PIPE) as proc: proc.wait() with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, proc.stdin.fileno()) self.assertEqual(ctx.exception.errno, errno.EPIPE) def test_send_to_broken_pipe_2(self): snap = ZFSTest.pool.makeName(b"fs1@snap") with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: with tempfile.NamedTemporaryFile(dir=mntdir) as f: for i in range(1024): f.write(b'x' * 1024) f.flush() lzc.lzc_snapshot([snap]) if sys.version_info < (3, 0): p = subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, p.stdin.fileno()) self.assertTrue(ctx.exception.errno == errno.EPIPE or ctx.exception.errno == errno.EINTR) else: with subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) as p: with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, p.stdin.fileno()) self.assertTrue(ctx.exception.errno == errno.EPIPE or ctx.exception.errno == errno.EINTR) def test_send_to_ro_file(self): snap = ZFSTest.pool.makeName(b"fs1@snap") lzc.lzc_snapshot([snap]) with tempfile.NamedTemporaryFile( suffix='.zstream', delete=False) as output: # tempfile always opens a temporary file in read-write mode # regardless of the specified mode, so we have to open it again. os.chmod(output.name, stat.S_IRUSR) fd = os.open(output.name, os.O_RDONLY) with self.assertRaises(lzc_exc.StreamIOError) as ctx: lzc.lzc_send(snap, None, fd) os.close(fd) self.assertEqual(ctx.exception.errno, errno.EBADF) def test_recv_full(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/received-1@snap") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst, stream.fileno()) name = os.path.basename(name) with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: self.assertTrue( filecmp.cmp( os.path.join(mnt1, name), os.path.join(mnt2, name), False)) def test_recv_incremental(self): src1 = ZFSTest.pool.makeName(b"fs1@snap1") src2 = ZFSTest.pool.makeName(b"fs1@snap2") dst1 = ZFSTest.pool.makeName(b"fs2/received-2@snap1") dst2 = ZFSTest.pool.makeName(b"fs2/received-2@snap2") lzc.lzc_snapshot([src1]) with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: lzc.lzc_snapshot([src2]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src1, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src2, src1, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst2, stream.fileno()) name = os.path.basename(name) with zfs_mount(src2) as mnt1, zfs_mount(dst2) as mnt2: self.assertTrue( filecmp.cmp( os.path.join(mnt1, name), os.path.join(mnt2, name), False)) # This test case fails unless a patch from # https://clusterhq.atlassian.net/browse/ZFS-20 # is applied to libzfs_core, otherwise it succeeds. @unittest.skip("fails with unpatched libzfs_core") def test_recv_without_explicit_snap_name(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-100") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dstfs, full.fileno()) lzc.lzc_receive(dstfs, incr.fileno()) self.assertExists(dst1) self.assertExists(dst2) def test_recv_clone(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) lzc.lzc_receive(clone_dst, stream.fileno(), origin=orig_dst) def test_recv_full_already_existing_empty_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-3") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_into_root_empty_pool(self): empty_pool = None try: srcfs = ZFSTest.pool.makeName(b"fs1") empty_pool = _TempPool() dst = empty_pool.makeName(b'@snap') with streams(srcfs, b"snap", None) as (_, (stream, _)): with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) finally: if empty_pool is not None: empty_pool.cleanUp() def test_recv_full_into_ro_pool(self): srcfs = ZFSTest.pool.makeName(b"fs1") dst = ZFSTest.readonly_pool.makeName(b'fs2/received@snap') with streams(srcfs, b"snap", None) as (_, (stream, _)): with self.assertRaises(lzc_exc.ReadOnlyPool): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_already_existing_modified_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-5") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_already_existing_with_snapshots(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-4") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) lzc.lzc_snapshot([dstfs + b"@snap1"]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(( lzc_exc.StreamMismatch, lzc_exc.DatasetExists)): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_already_existing_snapshot(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-6") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) lzc.lzc_snapshot([dst]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_missing_parent_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive(dst, stream.fileno()) def test_recv_full_but_specify_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src = srcfs + b"@snap" dstfs = ZFSTest.pool.makeName(b"fs2/received-30") dst = dstfs + b'@snap' origin1 = ZFSTest.pool.makeName(b"fs2@snap1") origin2 = ZFSTest.pool.makeName(b"fs2@snap2") lzc.lzc_snapshot([origin1]) with streams(srcfs, src, None) as (_, (stream, _)): lzc.lzc_receive(dst, stream.fileno(), origin=origin1) origin = ZFSTest.pool.getFilesystem( b"fs2/received-30").getProperty('origin') self.assertEqual(origin, origin1) stream.seek(0) # because origin snap does not exist can't receive as a clone of it with self.assertRaises(( lzc_exc.DatasetNotFound, lzc_exc.BadStream)): lzc.lzc_receive(dst, stream.fileno(), origin=origin2) def test_recv_full_existing_empty_fs_and_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src = srcfs + b"@snap" dstfs = ZFSTest.pool.makeName(b"fs2/received-31") dst = dstfs + b'@snap' origin = dstfs + b'@dummy' lzc.lzc_create(dstfs) with streams(srcfs, src, None) as (_, (stream, _)): # because the destination fs already exists and has no snaps with self.assertRaises(( lzc_exc.DestinationModified, lzc_exc.DatasetExists, lzc_exc.BadStream)): lzc.lzc_receive(dst, stream.fileno(), origin=origin) lzc.lzc_snapshot([origin]) stream.seek(0) # because the destination fs already exists and has the snap with self.assertRaises(( lzc_exc.StreamMismatch, lzc_exc.DatasetExists, lzc_exc.BadStream)): lzc.lzc_receive(dst, stream.fileno(), origin=origin) def test_recv_incremental_mounted_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-7") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with zfs_mount(dstfs): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_modified_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-15") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_snapname_used(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-8") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst2]) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_more_recent_snap_with_no_changes(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-9") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst_snap]) lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_non_clone_but_set_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-20") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst_snap]) # because cannot receive incremental and set origin on a non-clone with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(dst2, incr.fileno(), origin=dst1) def test_recv_incremental_non_clone_but_set_random_origin(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-21") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_snapshot([dst_snap]) # because origin snap does not exist can't receive as a clone of it with self.assertRaises(( lzc_exc.DatasetNotFound, lzc_exc.BadStream)): lzc.lzc_receive( dst2, incr.fileno(), origin=ZFSTest.pool.makeName(b"fs2/fs@snap")) def test_recv_incremental_more_recent_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-10") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): lzc.lzc_snapshot([dst_snap]) with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(dst2, incr.fileno()) def test_recv_incremental_duplicate(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-11") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_receive(dst2, incr.fileno()) incr.seek(0) with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(dst_snap, incr.fileno()) def test_recv_incremental_unrelated_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-12") dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (_, incr)): lzc.lzc_create(dstfs) with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive(dst_snap, incr.fileno()) def test_recv_incremental_nonexistent_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-13") dst_snap = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (_, incr)): with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive(dst_snap, incr.fileno()) def test_recv_incremental_same_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" src_snap = srcfs + b'@snap' with streams(srcfs, src1, src2) as (_, (_, incr)): with self.assertRaises(lzc_exc.DestinationModified): lzc.lzc_receive(src_snap, incr.fileno()) def test_recv_clone_without_specifying_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-2") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-2") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-2@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-2@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(clone_dst, stream.fileno()) def test_recv_clone_invalid_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-3") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-3") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-3@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-3@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_receive( clone_dst, stream.fileno(), origin=ZFSTest.pool.makeName(b"fs1/fs")) def test_recv_clone_wrong_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-4") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-4") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-4@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-4@snap") wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) lzc.lzc_snapshot([wrong_origin]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive( clone_dst, stream.fileno(), origin=wrong_origin) def test_recv_clone_nonexistent_origin(self): orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-5") clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-5") clone_snap = clone + b"@snap" orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-5@snap") clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-5@snap") wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap") lzc.lzc_snapshot([orig_src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(orig_src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(orig_dst, stream.fileno()) lzc.lzc_clone(clone, orig_src) lzc.lzc_snapshot([clone_snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(clone_snap, orig_src, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive( clone_dst, stream.fileno(), origin=wrong_origin) def test_force_recv_full_existing_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-50") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): pass # enough to taint the fs with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_full_existing_modified_mounted_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-53") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with zfs_mount(dstfs) as mntdir: f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) for i in range(1024): f.write(b'x' * 1024) lzc.lzc_receive(dst, stream.fileno(), force=True) # The temporary file disappears and any access, even close(), # results in EIO. self.assertFalse(os.path.exists(f.name)) with self.assertRaises(IOError): f.close() # This test-case expects the behavior that should be there, # at the moment it may fail with DatasetExists or StreamMismatch # depending on the implementation. def test_force_recv_full_already_existing_with_snapshots(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-51") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dstfs + b"@snap1"]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_full_already_existing_with_same_snap(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.makeName(b"fs2/received-52") dst = dstfs + b'@snap' with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) lzc.lzc_create(dstfs) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_full_missing_parent_fs(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")): lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_receive(dst, stream.fileno(), force=True) def test_force_recv_incremental_modified_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-60") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_receive(dst2, incr.fileno(), force=True) def test_force_recv_incremental_modified_mounted_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-64") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with zfs_mount(dstfs) as mntdir: f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False) for i in range(1024): f.write(b'x' * 1024) lzc.lzc_receive(dst2, incr.fileno(), force=True) # The temporary file disappears and any access, even close(), # results in EIO. self.assertFalse(os.path.exists(f.name)) with self.assertRaises(IOError): f.close() def test_force_recv_incremental_modified_fs_plus_later_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-61") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst3 = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst3]) lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertExists(dst2) self.assertNotExists(dst3) def test_force_recv_incremental_modified_fs_plus_same_name_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-62") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst2]) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst2, incr.fileno(), force=True) def test_force_recv_incremental_modified_fs_plus_held_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-63") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst3 = dstfs + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst3]) with cleanup_fd() as cfd: lzc.lzc_hold({dst3: b'tag'}, cfd) with self.assertRaises(lzc_exc.DatasetBusy): lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertNotExists(dst2) self.assertExists(dst3) def test_force_recv_incremental_modified_fs_plus_cloned_snap(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-70") dst1 = dstfs + b'@snap1' dst2 = dstfs + b'@snap2' dst3 = dstfs + b'@snap' cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-70") with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) with temp_file_in_fs(dstfs): pass # enough to taint the fs lzc.lzc_snapshot([dst3]) lzc.lzc_clone(cloned, dst3) with self.assertRaises(lzc_exc.DatasetExists): lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertNotExists(dst2) self.assertExists(dst3) def test_recv_incremental_into_cloned_fs(self): srcfs = ZFSTest.pool.makeName(b"fs1") src1 = srcfs + b"@snap1" src2 = srcfs + b"@snap2" dstfs = ZFSTest.pool.makeName(b"fs2/received-71") dst1 = dstfs + b'@snap1' cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-71") dst2 = cloned + b'@snap' with streams(srcfs, src1, src2) as (_, (full, incr)): lzc.lzc_receive(dst1, full.fileno()) lzc.lzc_clone(cloned, dst1) # test both graceful and with-force attempts with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive(dst2, incr.fileno()) incr.seek(0) with self.assertRaises(lzc_exc.StreamMismatch): lzc.lzc_receive(dst2, incr.fileno(), force=True) self.assertExists(dst1) self.assertNotExists(dst2) def test_recv_with_header_full(self): src = ZFSTest.pool.makeName(b"fs1@snap") dst = ZFSTest.pool.makeName(b"fs2/received") with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name: lzc.lzc_snapshot([src]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) self.assertEqual(src, header['drr_toname']) snap = header['drr_toname'].split(b'@', 1)[1] lzc.lzc_receive_with_header( dst + b'@' + snap, stream.fileno(), c_header) name = os.path.basename(name) with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2: self.assertTrue( filecmp.cmp( os.path.join(mnt1, name), os.path.join(mnt2, name), False)) def test_recv_fs_below_zvol(self): send = ZFSTest.pool.makeName(b"fs1@snap") zvol = ZFSTest.pool.makeName(b"fs1/zvol") dest = zvol + b"/fs@snap" props = {b"volsize": 1024 * 1024} lzc.lzc_snapshot([send]) lzc.lzc_create(zvol, ds_type='zvol', props=props) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(send, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_receive(dest, stream.fileno()) def test_recv_zvol_over_fs_with_children(self): parent = ZFSTest.pool.makeName(b"fs1") child = parent + b"subfs" zvol = ZFSTest.pool.makeName(b"fs1/zvol") send = zvol + b"@snap" props = {b"volsize": 1024 * 1024} lzc.lzc_create(child) lzc.lzc_create(zvol, ds_type='zvol', props=props) lzc.lzc_snapshot([send]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(send, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_receive(parent + b"@snap", stream.fileno(), force=True) def test_recv_zvol_overwrite_rootds(self): zvol = ZFSTest.pool.makeName(b"fs1/zvol") snap = zvol + b"@snap" rootds = ZFSTest.pool.getRoot().getName() props = {b"volsize": 1024 * 1024} lzc.lzc_create(zvol, ds_type='zvol', props=props) lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(snap, None, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_receive(rootds + b"@snap", stream.fileno(), force=True) def test_send_full_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-20", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-20") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, None, stream.fileno()) def test_send_incr_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-21", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-21") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) def test_send_resume_token_full(self): src = ZFSTest.pool.makeName(b"fs1@snap") dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") dst = dstfs.getSnap() with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: for i in range(1, 10): with tempfile.NamedTemporaryFile(dir=mntdir) as f: f.write(b'x' * 1024 * i) f.flush() lzc.lzc_snapshot([src]) with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) stream.truncate(1024 * 3) with self.assertRaises(lzc_exc.StreamTruncated): lzc.lzc_receive_resumable(dst, stream.fileno()) # Resume token code from zfs_send_resume_token_to_nvlist() # XXX: if used more than twice move this code into an external func # format: --- token = dstfs.getProperty("receive_resume_token") self.assertNotEqual(token, b'-') tokens = token.split(b'-') self.assertEqual(len(tokens), 4) version = tokens[0] packed_size = int(tokens[2], 16) compressed_nvs = tokens[3] # Validate resume token self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION if sys.version_info < (3, 0): payload = ( zlib.decompress(str(bytearray.fromhex(compressed_nvs))) ) else: payload = ( zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) ) self.assertEqual(len(payload), packed_size) # Unpack resume_values = packed_nvlist_out(payload, packed_size) resumeobj = resume_values.get(b'object') resumeoff = resume_values.get(b'offset') with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: lzc.lzc_send_resume( src, None, rstream.fileno(), None, resumeobj, resumeoff) rstream.seek(0) lzc.lzc_receive_resumable(dst, rstream.fileno()) def test_send_resume_token_incremental(self): snap1 = ZFSTest.pool.makeName(b"fs1@snap1") snap2 = ZFSTest.pool.makeName(b"fs1@snap2") dstfs = ZFSTest.pool.getFilesystem(b"fs2/received") dst1 = dstfs.getSnap() dst2 = dstfs.getSnap() lzc.lzc_snapshot([snap1]) with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(snap1, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(dst1, stream.fileno()) with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir: for i in range(1, 10): with tempfile.NamedTemporaryFile(dir=mntdir) as f: f.write(b'x' * 1024 * i) f.flush() lzc.lzc_snapshot([snap2]) with tempfile.NamedTemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(snap2, snap1, stream.fileno()) stream.seek(0) stream.truncate(1024 * 3) with self.assertRaises(lzc_exc.StreamTruncated): lzc.lzc_receive_resumable(dst2, stream.fileno()) # Resume token code from zfs_send_resume_token_to_nvlist() # format: --- token = dstfs.getProperty("receive_resume_token") self.assertNotEqual(token, '-') tokens = token.split(b'-') self.assertEqual(len(tokens), 4) version = tokens[0] packed_size = int(tokens[2], 16) compressed_nvs = tokens[3] # Validate resume token self.assertEqual(version, b'1') # ZFS_SEND_RESUME_TOKEN_VERSION if sys.version_info < (3, 0): payload = ( zlib.decompress(str(bytearray.fromhex(compressed_nvs))) ) else: payload = ( zlib.decompress(bytearray.fromhex(compressed_nvs.decode())) ) self.assertEqual(len(payload), packed_size) # Unpack resume_values = packed_nvlist_out(payload, packed_size) resumeobj = resume_values.get(b'object') resumeoff = resume_values.get(b'offset') with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream: lzc.lzc_send_resume( snap2, snap1, rstream.fileno(), None, resumeobj, resumeoff) rstream.seek(0) lzc.lzc_receive_resumable(dst2, rstream.fileno()) def test_recv_full_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-30", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-30") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-30") recvsnap = recvfs + b"@snap" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap, stream.fileno()) def test_recv_one(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") tosnap = ZFSTest.pool.makeName(b"recv@snap1") lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) def test_recv_one_size(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") tosnap = ZFSTest.pool.makeName(b"recv@snap1") lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) size = os.fstat(stream.fileno()).st_size stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) (read, _) = lzc.lzc_receive_one(tosnap, stream.fileno(), c_header) self.assertAlmostEqual(read, size, delta=read * 0.05) def test_recv_one_props(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = { b"compression": 0x01, b"ns:prop": b"val" } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_one(tosnap, stream.fileno(), c_header, props=props) self.assertExists(tosnap) self.assertEqual(fs.getProperty("compression", "received"), b"on") self.assertEqual(fs.getProperty("ns:prop", "received"), b"val") def test_recv_one_invalid_prop(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = { b"exec": 0xff, b"atime": 0x00 } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) with self.assertRaises(lzc_exc.ReceivePropertyFailure) as ctx: lzc.lzc_receive_one( tosnap, stream.fileno(), c_header, props=props) self.assertExists(tosnap) self.assertEqual(fs.getProperty("atime", "received"), b"off") for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PropertyInvalid) self.assertEqual(e.name, b"exec") def test_recv_with_cmdprops(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = {} cmdprops = { b"compression": 0x01, b"ns:prop": b"val" } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_with_cmdprops( tosnap, stream.fileno(), c_header, props=props, cmdprops=cmdprops) self.assertExists(tosnap) self.assertEqual(fs.getProperty("compression"), b"on") self.assertEqual(fs.getProperty("ns:prop"), b"val") def test_recv_with_cmdprops_and_recvprops(self): fromsnap = ZFSTest.pool.makeName(b"fs1@snap1") fs = ZFSTest.pool.getFilesystem(b"recv") tosnap = fs.getName() + b"@snap1" props = { b"atime": 0x01, b"exec": 0x00, b"ns:prop": b"abc" } cmdprops = { b"compression": 0x01, b"ns:prop": b"def", b"exec": None, } lzc.lzc_snapshot([fromsnap]) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) (header, c_header) = lzc.receive_header(stream.fileno()) lzc.lzc_receive_with_cmdprops( tosnap, stream.fileno(), c_header, props=props, cmdprops=cmdprops) self.assertExists(tosnap) self.assertEqual(fs.getProperty("atime", True), b"on") self.assertEqual(fs.getProperty("exec", True), b"off") self.assertEqual(fs.getProperty("ns:prop", True), b"abc") self.assertEqual(fs.getProperty("compression"), b"on") self.assertEqual(fs.getProperty("ns:prop"), b"def") self.assertEqual(fs.getProperty("exec"), b"on") def test_recv_incr_across_clone_branch_point_no_origin(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-32", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-32") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-32") recvsnap1 = recvfs + b"@snap1" recvsnap2 = recvfs + b"@snap2" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(recvsnap2, stream.fileno()) def test_recv_incr_across_clone_branch_point(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-31", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-31") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-31") recvsnap1 = recvfs + b"@snap1" recvsnap2 = recvfs + b"@snap2" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) stream.seek(0) with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) def test_recv_incr_across_clone_branch_point_new_fs(self): origfs = ZFSTest.pool.makeName(b"fs2") (_, (fromsnap, origsnap, _)) = make_snapshots( origfs, b"snap1", b"send-origin-33", None) clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-33") lzc.lzc_clone(clonefs, origsnap) (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None) recvfs1 = ZFSTest.pool.makeName(b"fs1/recv-clone-33") recvsnap1 = recvfs1 + b"@snap" recvfs2 = ZFSTest.pool.makeName(b"fs1/recv-clone-33_2") recvsnap2 = recvfs2 + b"@snap" with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(fromsnap, None, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap1, stream.fileno()) with tempfile.TemporaryFile(suffix='.zstream') as stream: lzc.lzc_send(tosnap, fromsnap, stream.fileno()) stream.seek(0) lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1) def test_recv_bad_stream(self): dstfs = ZFSTest.pool.makeName(b"fs2/received") dst_snap = dstfs + b'@snap' with dev_zero() as fd: with self.assertRaises(lzc_exc.BadStream): lzc.lzc_receive(dst_snap, fd) @needs_support(lzc.lzc_promote) def test_promote(self): origfs = ZFSTest.pool.makeName(b"fs2") snap = b"@promote-snap-1" origsnap = origfs + snap lzc.lzc_snap([origsnap]) clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-1") lzc.lzc_clone(clonefs, origsnap) lzc.lzc_promote(clonefs) # the snapshot now should belong to the promoted fs self.assertExists(clonefs + snap) @needs_support(lzc.lzc_promote) def test_promote_too_long_snapname(self): # origfs name must be shorter than clonefs name origfs = ZFSTest.pool.makeName(b"fs2") clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-2") snapprefix = b"@promote-snap-2-" pad_len = 1 + lzc.MAXNAMELEN - len(clonefs) - len(snapprefix) snap = snapprefix + b'x' * pad_len origsnap = origfs + snap lzc.lzc_snap([origsnap]) lzc.lzc_clone(clonefs, origsnap) # This may fail on older buggy systems. # See: https://www.illumos.org/issues/5909 with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_promote(clonefs) @needs_support(lzc.lzc_promote) def test_promote_not_cloned(self): fs = ZFSTest.pool.makeName(b"fs2") with self.assertRaises(lzc_exc.NotClone): lzc.lzc_promote(fs) @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: bad_fd = tmp.fileno() with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, bad_fd) @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd_2(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, -2) @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd_3(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) bad_fd = hard + 1 with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, bad_fd) @unittest.skipIf(*illumos_bug_6379()) def test_hold_wrong_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with tempfile.TemporaryFile() as tmp: fd = tmp.fileno() with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: b'tag'}, fd) def test_hold_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) def test_hold_empty(self): with cleanup_fd() as fd: lzc.lzc_hold({}, fd) def test_hold_empty_2(self): lzc.lzc_hold({}) def test_hold_vs_snap_destroy(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx: lzc.lzc_destroy_snaps([snap], defer=False) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.SnapshotIsHeld) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) # after automatic hold cleanup and deferred destruction self.assertNotExists(snap) def test_hold_many_tags(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) def test_hold_many_snaps(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with cleanup_fd() as fd: lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) def test_hold_many_with_one_missing(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) with cleanup_fd() as fd: missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) self.assertEqual(len(missing), 1) self.assertEqual(missing[0], snap2) def test_hold_many_with_all_missing(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() with cleanup_fd() as fd: missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) self.assertEqual(len(missing), 2) self.assertEqual(sorted(missing), sorted([snap1, snap2])) def test_hold_missing_fs(self): # XXX skip pre-created filesystems ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() snaps = lzc.lzc_hold({snap: b'tag'}) self.assertEqual([snap], snaps) def test_hold_missing_fs_auto_cleanup(self): # XXX skip pre-created filesystems ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() ZFSTest.pool.getRoot().getFilesystem() snap = ZFSTest.pool.getRoot().getFilesystem().getSnap() with cleanup_fd() as fd: snaps = lzc.lzc_hold({snap: b'tag'}, fd) self.assertEqual([snap], snaps) def test_hold_duplicate(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.HoldExists) def test_hold_across_pools(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.misc_pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) def test_hold_too_long_tag(self): snap = ZFSTest.pool.getRoot().getSnap() tag = b't' * 256 lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: tag}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, tag) # Apparently the full snapshot name is not checked for length # and this snapshot is treated as simply missing. @unittest.expectedFailure def test_hold_too_long_snap_name(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(False) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, snap) def test_hold_too_long_snap_name_2(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(True) with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, snap) def test_hold_invalid_snap_name(self): snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_hold_invalid_snap_name_2(self): snap = ZFSTest.pool.getRoot().getFilesystem().getName() with cleanup_fd() as fd: with self.assertRaises(lzc_exc.HoldFailure) as ctx: lzc.lzc_hold({snap: b'tag'}, fd) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_get_holds(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) holds = lzc.lzc_get_holds(snap) self.assertEqual(len(holds), 2) self.assertIn(b'tag1', holds) self.assertIn(b'tag2', holds) self.assertIsInstance(holds[b'tag1'], (int, int)) def test_get_holds_after_auto_cleanup(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) holds = lzc.lzc_get_holds(snap) self.assertEqual(len(holds), 0) self.assertIsInstance(holds, dict) def test_get_holds_nonexistent_snap(self): snap = ZFSTest.pool.getRoot().getSnap() with self.assertRaises(lzc_exc.SnapshotNotFound): lzc.lzc_get_holds(snap) def test_get_holds_too_long_snap_name(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(False) with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_get_holds(snap) def test_get_holds_too_long_snap_name_2(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(True) with self.assertRaises(lzc_exc.NameTooLong): lzc.lzc_get_holds(snap) def test_get_holds_invalid_snap_name(self): snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_get_holds(snap) # A filesystem-like snapshot name is not recognized as # an invalid name. @unittest.expectedFailure def test_get_holds_invalid_snap_name_2(self): snap = ZFSTest.pool.getRoot().getFilesystem().getName() with self.assertRaises(lzc_exc.NameInvalid): lzc.lzc_get_holds(snap) def test_release_hold(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) lzc.lzc_hold({snap: b'tag'}) ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 0) def test_release_hold_empty(self): ret = lzc.lzc_release({}) self.assertEqual(len(ret), 0) def test_release_hold_complex(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.pool.getRoot().getSnap() snap3 = ZFSTest.pool.getRoot().getFilesystem().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2, snap3]) lzc.lzc_hold({snap1: b'tag1'}) lzc.lzc_hold({snap1: b'tag2'}) lzc.lzc_hold({snap2: b'tag'}) lzc.lzc_hold({snap3: b'tag1'}) lzc.lzc_hold({snap3: b'tag2'}) holds = lzc.lzc_get_holds(snap1) self.assertEqual(len(holds), 2) holds = lzc.lzc_get_holds(snap2) self.assertEqual(len(holds), 1) holds = lzc.lzc_get_holds(snap3) self.assertEqual(len(holds), 2) release = { snap1: [b'tag1', b'tag2'], snap2: [b'tag'], snap3: [b'tag2'], } ret = lzc.lzc_release(release) self.assertEqual(len(ret), 0) holds = lzc.lzc_get_holds(snap1) self.assertEqual(len(holds), 0) holds = lzc.lzc_get_holds(snap2) self.assertEqual(len(holds), 0) holds = lzc.lzc_get_holds(snap3) self.assertEqual(len(holds), 1) ret = lzc.lzc_release({snap3: [b'tag1']}) self.assertEqual(len(ret), 0) holds = lzc.lzc_get_holds(snap3) self.assertEqual(len(holds), 0) def test_release_hold_before_auto_cleanup(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 0) def test_release_hold_and_snap_destruction(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag1'}, fd) lzc.lzc_hold({snap: b'tag2'}, fd) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) lzc.lzc_release({snap: [b'tag1']}) self.assertExists(snap) lzc.lzc_release({snap: [b'tag2']}) self.assertNotExists(snap) def test_release_hold_and_multiple_snap_destruction(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) with cleanup_fd() as fd: lzc.lzc_hold({snap: b'tag'}, fd) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) lzc.lzc_destroy_snaps([snap], defer=True) self.assertExists(snap) lzc.lzc_release({snap: [b'tag']}) self.assertNotExists(snap) def test_release_hold_missing_tag(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], snap + b'#tag') def test_release_hold_missing_snap(self): snap = ZFSTest.pool.getRoot().getSnap() ret = lzc.lzc_release({snap: [b'tag']}) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], snap) def test_release_hold_missing_snap_2(self): snap = ZFSTest.pool.getRoot().getSnap() ret = lzc.lzc_release({snap: [b'tag', b'another']}) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], snap) def test_release_hold_across_pools(self): snap1 = ZFSTest.pool.getRoot().getSnap() snap2 = ZFSTest.misc_pool.getRoot().getSnap() lzc.lzc_snapshot([snap1]) lzc.lzc_snapshot([snap2]) with cleanup_fd() as fd: lzc.lzc_hold({snap1: b'tag'}, fd) lzc.lzc_hold({snap2: b'tag'}, fd) with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap1: [b'tag'], snap2: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.PoolsDiffer) # Apparently the tag name is not verified, # only its existence is checked. @unittest.expectedFailure def test_release_hold_too_long_tag(self): snap = ZFSTest.pool.getRoot().getSnap() tag = b't' * 256 lzc.lzc_snapshot([snap]) with self.assertRaises(lzc_exc.HoldReleaseFailure): lzc.lzc_release({snap: [tag]}) # Apparently the full snapshot name is not checked for length # and this snapshot is treated as simply missing. @unittest.expectedFailure def test_release_hold_too_long_snap_name(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(False) with self.assertRaises(lzc_exc.HoldReleaseFailure): lzc.lzc_release({snap: [b'tag']}) def test_release_hold_too_long_snap_name_2(self): snap = ZFSTest.pool.getRoot().getTooLongSnap(True) with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameTooLong) self.assertEqual(e.name, snap) def test_release_hold_invalid_snap_name(self): snap = ZFSTest.pool.getRoot().getSnap() + b'@bad' with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_release_hold_invalid_snap_name_2(self): snap = ZFSTest.pool.getRoot().getFilesystem().getName() with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx: lzc.lzc_release({snap: [b'tag']}) for e in ctx.exception.errors: self.assertIsInstance(e, lzc_exc.NameInvalid) self.assertEqual(e.name, snap) def test_sync_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_sync(pool) def test_sync_pool_forced(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_sync(pool, True) def test_reopen_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_reopen(pool) def test_reopen_pool_no_restart(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_reopen(pool, False) def test_channel_program_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_channel_program(pool, b"return {}") def test_channel_program_timeout(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" for i = 1,10000 do zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) end """ with self.assertRaises(lzc_exc.ZCPTimeout): lzc.lzc_channel_program(pool, zcp, instrlimit=1) def test_channel_program_memory_limit(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" for i = 1,10000 do zfs.sync.snapshot('""" + pool + b"""@zcp' .. i) end """ with self.assertRaises(lzc_exc.ZCPSpaceError): lzc.lzc_channel_program(pool, zcp, memlimit=1) def test_channel_program_invalid_limits(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" return {} """ with self.assertRaises(lzc_exc.ZCPLimitInvalid): lzc.lzc_channel_program(pool, zcp, instrlimit=0) with self.assertRaises(lzc_exc.ZCPLimitInvalid): lzc.lzc_channel_program(pool, zcp, memlimit=0) def test_channel_program_syntax_error(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" inv+val:id """ with self.assertRaises(lzc_exc.ZCPSyntaxError) as ctx: lzc.lzc_channel_program(pool, zcp) self.assertTrue(b"syntax error" in ctx.exception.details) def test_channel_program_sync_snapshot(self): pool = ZFSTest.pool.getRoot().getName() snapname = ZFSTest.pool.makeName(b"@zcp") zcp = b""" zfs.sync.snapshot('""" + snapname + b"""') """ lzc.lzc_channel_program(pool, zcp) self.assertExists(snapname) def test_channel_program_runtime_error(self): pool = ZFSTest.pool.getRoot().getName() # failing an assertion raises a runtime error with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: lzc.lzc_channel_program(pool, b"assert(1 == 2)") self.assertTrue( b"assertion failed" in ctx.exception.details) # invoking the error() function raises a runtime error with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: lzc.lzc_channel_program(pool, b"error()") def test_channel_program_nosync_runtime_error(self): pool = ZFSTest.pool.getRoot().getName() zcp = b""" zfs.sync.snapshot('""" + pool + b"""@zcp') """ # lzc_channel_program_nosync() allows only "read-only" operations with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx: lzc.lzc_channel_program_nosync(pool, zcp) self.assertTrue( b"running functions from the zfs.sync" in ctx.exception.details) def test_change_key_new(self): with encrypted_filesystem() as (fs, _): lzc.lzc_change_key( fs, 'new_key', props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_change_key_missing_fs(self): name = b"nonexistent" with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_change_key( name, 'new_key', props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_change_key_not_loaded(self): with encrypted_filesystem() as (fs, _): lzc.lzc_unload_key(fs) with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): lzc.lzc_change_key( fs, 'new_key', props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW}, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_change_key_invalid_property(self): with encrypted_filesystem() as (fs, _): with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_change_key(fs, 'new_key', props={b"invalid": b"prop"}) def test_change_key_invalid_crypt_command(self): with encrypted_filesystem() as (fs, _): with self.assertRaises(lzc_exc.UnknownCryptCommand): lzc.lzc_change_key(fs, 'duplicate_key') def test_load_key(self): with encrypted_filesystem() as (fs, key): lzc.lzc_unload_key(fs) lzc.lzc_load_key(fs, False, key) def test_load_key_invalid(self): with encrypted_filesystem() as (fs, key): lzc.lzc_unload_key(fs) with self.assertRaises(lzc_exc.EncryptionKeyInvalid): lzc.lzc_load_key(fs, False, os.urandom(lzc.WRAPPING_KEY_LEN)) def test_load_key_already_loaded(self): with encrypted_filesystem() as (fs, key): lzc.lzc_unload_key(fs) lzc.lzc_load_key(fs, False, key) with self.assertRaises(lzc_exc.EncryptionKeyAlreadyLoaded): lzc.lzc_load_key(fs, False, key) def test_load_key_missing_fs(self): name = b"nonexistent" with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_load_key(name, False, key=os.urandom(lzc.WRAPPING_KEY_LEN)) def test_unload_key(self): with encrypted_filesystem() as (fs, _): lzc.lzc_unload_key(fs) def test_unload_key_missing_fs(self): name = b"nonexistent" with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_unload_key(name) def test_unload_key_busy(self): with encrypted_filesystem() as (fs, _): with zfs_mount(fs): with self.assertRaises(lzc_exc.DatasetBusy): lzc.lzc_unload_key(fs) def test_unload_key_not_loaded(self): with encrypted_filesystem() as (fs, _): lzc.lzc_unload_key(fs) with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded): lzc.lzc_unload_key(fs) def test_checkpoint(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_pool_checkpoint(pool) def test_checkpoint_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_pool_checkpoint(pool) def test_checkpoint_already_exists(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_pool_checkpoint(pool) with self.assertRaises(lzc_exc.CheckpointExists): lzc.lzc_pool_checkpoint(pool) def test_checkpoint_discard(self): pool = ZFSTest.pool.getRoot().getName() lzc.lzc_pool_checkpoint(pool) lzc.lzc_pool_checkpoint_discard(pool) def test_checkpoint_discard_missing_pool(self): pool = b"nonexistent" with self.assertRaises(lzc_exc.PoolNotFound): lzc.lzc_pool_checkpoint_discard(pool) def test_checkpoint_discard_missing_checkpoint(self): pool = ZFSTest.pool.getRoot().getName() with self.assertRaises(lzc_exc.CheckpointNotFound): lzc.lzc_pool_checkpoint_discard(pool) @needs_support(lzc.lzc_list_children) def test_list_children(self): name = ZFSTest.pool.makeName(b"fs1/fs") names = [ZFSTest.pool.makeName(b"fs1/fs/test1"), ZFSTest.pool.makeName(b"fs1/fs/test2"), ZFSTest.pool.makeName(b"fs1/fs/test3"), ] # and one snap to see that it is not listed snap = ZFSTest.pool.makeName(b"fs1/fs@test") for fs in names: lzc.lzc_create(fs) lzc.lzc_snapshot([snap]) children = list(lzc.lzc_list_children(name)) self.assertItemsEqual(children, names) @needs_support(lzc.lzc_list_children) def test_list_children_nonexistent(self): fs = ZFSTest.pool.makeName(b"nonexistent") with self.assertRaises(lzc_exc.DatasetNotFound): list(lzc.lzc_list_children(fs)) @needs_support(lzc.lzc_list_children) def test_list_children_of_snap(self): snap = ZFSTest.pool.makeName(b"@newsnap") lzc.lzc_snapshot([snap]) children = list(lzc.lzc_list_children(snap)) self.assertEqual(children, []) @needs_support(lzc.lzc_list_snaps) def test_list_snaps(self): name = ZFSTest.pool.makeName(b"fs1/fs") names = [ZFSTest.pool.makeName(b"fs1/fs@test1"), ZFSTest.pool.makeName(b"fs1/fs@test2"), ZFSTest.pool.makeName(b"fs1/fs@test3"), ] # and one filesystem to see that it is not listed fs = ZFSTest.pool.makeName(b"fs1/fs/test") for snap in names: lzc.lzc_snapshot([snap]) lzc.lzc_create(fs) snaps = list(lzc.lzc_list_snaps(name)) self.assertItemsEqual(snaps, names) @needs_support(lzc.lzc_list_snaps) def test_list_snaps_nonexistent(self): fs = ZFSTest.pool.makeName(b"nonexistent") with self.assertRaises(lzc_exc.DatasetNotFound): list(lzc.lzc_list_snaps(fs)) @needs_support(lzc.lzc_list_snaps) def test_list_snaps_of_snap(self): snap = ZFSTest.pool.makeName(b"@newsnap") lzc.lzc_snapshot([snap]) snaps = list(lzc.lzc_list_snaps(snap)) self.assertEqual(snaps, []) @needs_support(lzc.lzc_get_props) def test_get_fs_props(self): fs = ZFSTest.pool.makeName(b"new") props = {b"user:foo": b"bar"} lzc.lzc_create(fs, props=props) actual_props = lzc.lzc_get_props(fs) self.assertDictContainsSubset(props, actual_props) @needs_support(lzc.lzc_get_props) def test_get_fs_props_with_child(self): parent = ZFSTest.pool.makeName(b"parent") child = ZFSTest.pool.makeName(b"parent/child") parent_props = {b"user:foo": b"parent"} child_props = {b"user:foo": b"child"} lzc.lzc_create(parent, props=parent_props) lzc.lzc_create(child, props=child_props) actual_parent_props = lzc.lzc_get_props(parent) actual_child_props = lzc.lzc_get_props(child) self.assertDictContainsSubset(parent_props, actual_parent_props) self.assertDictContainsSubset(child_props, actual_child_props) @needs_support(lzc.lzc_get_props) def test_get_snap_props(self): snapname = ZFSTest.pool.makeName(b"@snap") snaps = [snapname] props = {b"user:foo": b"bar"} lzc.lzc_snapshot(snaps, props) actual_props = lzc.lzc_get_props(snapname) self.assertDictContainsSubset(props, actual_props) @needs_support(lzc.lzc_get_props) def test_get_props_nonexistent(self): fs = ZFSTest.pool.makeName(b"nonexistent") with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_get_props(fs) @needs_support(lzc.lzc_get_props) def test_get_mountpoint_none(self): ''' If the *mountpoint* property is set to none, then its value is returned as `bytes` "none". Also, a child filesystem inherits that value. ''' fs = ZFSTest.pool.makeName(b"new") child = ZFSTest.pool.makeName(b"new/child") props = {b"mountpoint": b"none"} lzc.lzc_create(fs, props=props) lzc.lzc_create(child) actual_props = lzc.lzc_get_props(fs) self.assertDictContainsSubset(props, actual_props) # check that mountpoint value is correctly inherited child_props = lzc.lzc_get_props(child) self.assertDictContainsSubset(props, child_props) @needs_support(lzc.lzc_get_props) def test_get_mountpoint_legacy(self): ''' If the *mountpoint* property is set to legacy, then its value is returned as `bytes` "legacy". Also, a child filesystem inherits that value. ''' fs = ZFSTest.pool.makeName(b"new") child = ZFSTest.pool.makeName(b"new/child") props = {b"mountpoint": b"legacy"} lzc.lzc_create(fs, props=props) lzc.lzc_create(child) actual_props = lzc.lzc_get_props(fs) self.assertDictContainsSubset(props, actual_props) # check that mountpoint value is correctly inherited child_props = lzc.lzc_get_props(child) self.assertDictContainsSubset(props, child_props) @needs_support(lzc.lzc_get_props) def test_get_mountpoint_path(self): ''' If the *mountpoint* property is set to a path and the property is not explicitly set on a child filesystem, then its value is that of the parent filesystem with the child's name appended using the '/' separator. ''' fs = ZFSTest.pool.makeName(b"new") child = ZFSTest.pool.makeName(b"new/child") props = {b"mountpoint": b"/mnt"} lzc.lzc_create(fs, props=props) lzc.lzc_create(child) actual_props = lzc.lzc_get_props(fs) self.assertDictContainsSubset(props, actual_props) # check that mountpoint value is correctly inherited child_props = lzc.lzc_get_props(child) self.assertDictContainsSubset( {b"mountpoint": b"/mnt/child"}, child_props) @needs_support(lzc.lzc_get_props) def test_get_snap_clones(self): fs = ZFSTest.pool.makeName(b"new") snap = ZFSTest.pool.makeName(b"@snap") clone1 = ZFSTest.pool.makeName(b"clone1") clone2 = ZFSTest.pool.makeName(b"clone2") lzc.lzc_create(fs) lzc.lzc_snapshot([snap]) lzc.lzc_clone(clone1, snap) lzc.lzc_clone(clone2, snap) clones_prop = lzc.lzc_get_props(snap)["clones"] self.assertItemsEqual(clones_prop, [clone1, clone2]) @needs_support(lzc.lzc_rename) def test_rename(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"target") lzc.lzc_create(src) lzc.lzc_rename(src, tgt) self.assertNotExists(src) self.assertExists(tgt) @needs_support(lzc.lzc_rename) def test_rename_nonexistent(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"target") with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_rename) def test_rename_existing_target(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"target") lzc.lzc_create(src) lzc.lzc_create(tgt) with self.assertRaises(lzc_exc.FilesystemExists): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_rename) def test_rename_nonexistent_target_parent(self): src = ZFSTest.pool.makeName(b"source") tgt = ZFSTest.pool.makeName(b"parent/target") lzc.lzc_create(src) with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_rename) def test_rename_parent_is_zvol(self): src = ZFSTest.pool.makeName(b"source") zvol = ZFSTest.pool.makeName(b"parent") tgt = zvol + b"/target" props = {b"volsize": 1024 * 1024} lzc.lzc_create(src) lzc.lzc_create(zvol, ds_type='zvol', props=props) with self.assertRaises(lzc_exc.WrongParent): lzc.lzc_rename(src, tgt) @needs_support(lzc.lzc_destroy) def test_destroy(self): fs = ZFSTest.pool.makeName(b"test-fs") lzc.lzc_create(fs) lzc.lzc_destroy(fs) self.assertNotExists(fs) @needs_support(lzc.lzc_destroy) def test_destroy_nonexistent(self): fs = ZFSTest.pool.makeName(b"test-fs") with self.assertRaises(lzc_exc.FilesystemNotFound): lzc.lzc_destroy(fs) @needs_support(lzc.lzc_inherit_prop) def test_inherit_prop(self): parent = ZFSTest.pool.makeName(b"parent") child = ZFSTest.pool.makeName(b"parent/child") the_prop = b"user:foo" parent_props = {the_prop: b"parent"} child_props = {the_prop: b"child"} lzc.lzc_create(parent, props=parent_props) lzc.lzc_create(child, props=child_props) lzc.lzc_inherit_prop(child, the_prop) actual_props = lzc.lzc_get_props(child) self.assertDictContainsSubset(parent_props, actual_props) @needs_support(lzc.lzc_inherit_prop) def test_inherit_missing_prop(self): parent = ZFSTest.pool.makeName(b"parent") child = ZFSTest.pool.makeName(b"parent/child") the_prop = "user:foo" child_props = {the_prop: "child"} lzc.lzc_create(parent) lzc.lzc_create(child, props=child_props) lzc.lzc_inherit_prop(child, the_prop) actual_props = lzc.lzc_get_props(child) self.assertNotIn(the_prop, actual_props) @needs_support(lzc.lzc_inherit_prop) def test_inherit_readonly_prop(self): parent = ZFSTest.pool.makeName(b"parent") child = ZFSTest.pool.makeName(b"parent/child") the_prop = b"createtxg" lzc.lzc_create(parent) lzc.lzc_create(child) with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_inherit_prop(child, the_prop) @needs_support(lzc.lzc_inherit_prop) def test_inherit_unknown_prop(self): parent = ZFSTest.pool.makeName(b"parent") child = ZFSTest.pool.makeName(b"parent/child") the_prop = b"nosuchprop" lzc.lzc_create(parent) lzc.lzc_create(child) with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_inherit_prop(child, the_prop) @needs_support(lzc.lzc_inherit_prop) def test_inherit_prop_on_snap(self): fs = ZFSTest.pool.makeName(b"new") snapname = ZFSTest.pool.makeName(b"new@snap") prop = b"user:foo" fs_val = b"fs" snap_val = b"snap" lzc.lzc_create(fs, props={prop: fs_val}) lzc.lzc_snapshot([snapname], props={prop: snap_val}) actual_props = lzc.lzc_get_props(snapname) self.assertDictContainsSubset({prop: snap_val}, actual_props) lzc.lzc_inherit_prop(snapname, prop) actual_props = lzc.lzc_get_props(snapname) self.assertDictContainsSubset({prop: fs_val}, actual_props) @needs_support(lzc.lzc_set_prop) def test_set_fs_prop(self): fs = ZFSTest.pool.makeName(b"new") prop = b"user:foo" val = b"bar" lzc.lzc_create(fs) lzc.lzc_set_prop(fs, prop, val) actual_props = lzc.lzc_get_props(fs) self.assertDictContainsSubset({prop: val}, actual_props) @needs_support(lzc.lzc_set_prop) def test_set_snap_prop(self): snapname = ZFSTest.pool.makeName(b"@snap") prop = b"user:foo" val = b"bar" lzc.lzc_snapshot([snapname]) lzc.lzc_set_prop(snapname, prop, val) actual_props = lzc.lzc_get_props(snapname) self.assertDictContainsSubset({prop: val}, actual_props) @needs_support(lzc.lzc_set_prop) def test_set_prop_nonexistent(self): fs = ZFSTest.pool.makeName(b"nonexistent") prop = b"user:foo" val = b"bar" with self.assertRaises(lzc_exc.DatasetNotFound): lzc.lzc_set_prop(fs, prop, val) @needs_support(lzc.lzc_set_prop) def test_set_sys_prop(self): fs = ZFSTest.pool.makeName(b"new") prop = b"recordsize" val = 4096 lzc.lzc_create(fs) lzc.lzc_set_prop(fs, prop, val) actual_props = lzc.lzc_get_props(fs) self.assertDictContainsSubset({prop: val}, actual_props) @needs_support(lzc.lzc_set_prop) def test_set_invalid_prop(self): fs = ZFSTest.pool.makeName(b"new") prop = b"nosuchprop" val = 0 lzc.lzc_create(fs) with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_set_prop(fs, prop, val) @needs_support(lzc.lzc_set_prop) def test_set_invalid_value_prop(self): fs = ZFSTest.pool.makeName(b"new") prop = b"atime" val = 100 lzc.lzc_create(fs) with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_set_prop(fs, prop, val) @needs_support(lzc.lzc_set_prop) def test_set_invalid_value_prop_2(self): fs = ZFSTest.pool.makeName(b"new") prop = b"readonly" val = 100 lzc.lzc_create(fs) with self.assertRaises(lzc_exc.PropertyInvalid): lzc.lzc_set_prop(fs, prop, val) @needs_support(lzc.lzc_set_prop) def test_set_prop_too_small_quota(self): fs = ZFSTest.pool.makeName(b"new") prop = b"refquota" val = 1 lzc.lzc_create(fs) with self.assertRaises(lzc_exc.NoSpace): lzc.lzc_set_prop(fs, prop, val) @needs_support(lzc.lzc_set_prop) def test_set_readonly_prop(self): fs = ZFSTest.pool.makeName(b"new") prop = b"creation" val = 0 lzc.lzc_create(fs) lzc.lzc_set_prop(fs, prop, val) actual_props = lzc.lzc_get_props(fs) # the change is silently ignored self.assertTrue(actual_props[prop] != val) class _TempPool(object): SNAPSHOTS = [b'snap', b'snap1', b'snap2'] BOOKMARKS = [b'bmark', b'bmark1', b'bmark2'] _cachefile_suffix = ".cachefile" # XXX Whether to do a sloppy but much faster cleanup # or a proper but slower one. _recreate_pools = True def __init__(self, size=128 * 1024 * 1024, readonly=False, filesystems=[]): self._filesystems = filesystems self._readonly = readonly if sys.version_info < (3, 0): self._pool_name = b'pool.' + bytes(uuid.uuid4()) else: self._pool_name = b'pool.' + bytes(str(uuid.uuid4()), encoding='utf-8') self._root = _Filesystem(self._pool_name) (fd, self._pool_file_path) = tempfile.mkstemp( suffix='.zpool', prefix='tmp-') if readonly: cachefile = self._pool_file_path + _TempPool._cachefile_suffix else: cachefile = 'none' self._zpool_create = [ 'zpool', 'create', '-o', 'cachefile=' + cachefile, '-O', 'mountpoint=legacy', self._pool_name, self._pool_file_path] try: os.ftruncate(fd, size) os.close(fd) subprocess.check_output( self._zpool_create, stderr=subprocess.STDOUT) for fs in filesystems: lzc.lzc_create(self.makeName(fs)) self._bmarks_supported = self.isPoolFeatureEnabled('bookmarks') if readonly: # To make a pool read-only it must exported and re-imported # with readonly option. # The most deterministic way to re-import the pool is by using # a cache file. # But the cache file has to be stashed away before the pool is # exported, because otherwise the pool is removed from the # cache. shutil.copyfile(cachefile, cachefile + '.tmp') subprocess.check_output( ['zpool', 'export', '-f', self._pool_name], stderr=subprocess.STDOUT) os.rename(cachefile + '.tmp', cachefile) subprocess.check_output( ['zpool', 'import', '-f', '-N', '-c', cachefile, '-o', 'readonly=on', self._pool_name], stderr=subprocess.STDOUT) os.remove(cachefile) except subprocess.CalledProcessError as e: self.cleanUp() if b'permission denied' in e.output: raise unittest.SkipTest( 'insufficient privileges to run libzfs_core tests') print('command failed: ', e.output) raise except Exception: self.cleanUp() raise def reset(self): if self._readonly: return if not self.__class__._recreate_pools: snaps = [] for fs in [''] + self._filesystems: for snap in self.__class__.SNAPSHOTS: snaps.append(self.makeName(fs + '@' + snap)) self.getRoot().visitSnaps(lambda snap: snaps.append(snap)) lzc.lzc_destroy_snaps(snaps, defer=False) if self._bmarks_supported: bmarks = [] for fs in [''] + self._filesystems: for bmark in self.__class__.BOOKMARKS: bmarks.append(self.makeName(fs + '#' + bmark)) self.getRoot().visitBookmarks( lambda bmark: bmarks.append(bmark)) lzc.lzc_destroy_bookmarks(bmarks) self.getRoot().reset() return # On the Buildbot builders this may fail with "pool is busy" # Retry 5 times before raising an error retry = 0 while True: try: subprocess.check_output( ['zpool', 'destroy', '-f', self._pool_name], stderr=subprocess.STDOUT) subprocess.check_output( self._zpool_create, stderr=subprocess.STDOUT) break except subprocess.CalledProcessError as e: if b'pool is busy' in e.output and retry < 5: retry += 1 time.sleep(1) continue else: print('command failed: ', e.output) raise for fs in self._filesystems: lzc.lzc_create(self.makeName(fs)) self.getRoot().reset() def cleanUp(self): try: subprocess.check_output( ['zpool', 'destroy', '-f', self._pool_name], stderr=subprocess.STDOUT) except Exception: pass try: os.remove(self._pool_file_path) except Exception: pass try: os.remove(self._pool_file_path + _TempPool._cachefile_suffix) except Exception: pass try: os.remove( self._pool_file_path + _TempPool._cachefile_suffix + '.tmp') except Exception: pass def makeName(self, relative=None): if not relative: return self._pool_name if relative.startswith((b'@', b'#')): return self._pool_name + relative return self._pool_name + b'/' + relative def makeTooLongName(self, prefix=None): if not prefix: prefix = b'x' prefix = self.makeName(prefix) pad_len = lzc.MAXNAMELEN + 1 - len(prefix) if pad_len > 0: return prefix + b'x' * pad_len else: return prefix def makeTooLongComponent(self, prefix=None): padding = b'x' * (lzc.MAXNAMELEN + 1) if not prefix: prefix = padding else: prefix = prefix + padding return self.makeName(prefix) def getRoot(self): return self._root def getFilesystem(self, fsname): return _Filesystem(self._pool_name + b'/' + fsname) def isPoolFeatureAvailable(self, feature): output = subprocess.check_output( ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) output = output.strip() return output != '' def isPoolFeatureEnabled(self, feature): output = subprocess.check_output( ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name]) output = output.split()[2] return output in [b'active', b'enabled'] class _Filesystem(object): def __init__(self, name): self._name = name self.reset() def getName(self): return self._name def reset(self): self._children = [] self._fs_id = 0 self._snap_id = 0 self._bmark_id = 0 def getFilesystem(self): self._fs_id += 1 fsname = self._name + b'/fs' + str(self._fs_id).encode() fs = _Filesystem(fsname) self._children.append(fs) return fs def getProperty(self, propname, received=False): if received: output = subprocess.check_output( ['zfs', 'get', '-pH', '-o', 'received', propname, self._name]) else: output = subprocess.check_output( ['zfs', 'get', '-pH', '-o', 'value', propname, self._name]) return output.strip() def _makeSnapName(self, i): return self._name + b'@snap' + str(i).encode() def getSnap(self): self._snap_id += 1 return self._makeSnapName(self._snap_id) def _makeBookmarkName(self, i): return self._name + b'#bmark' + bytes(i) def getBookmark(self): self._bmark_id += 1 return self._makeBookmarkName(self._bmark_id) def _makeTooLongName(self, too_long_component): if too_long_component: return b'x' * (lzc.MAXNAMELEN + 1) # Note that another character is used for one of '/', '@', '#'. comp_len = lzc.MAXNAMELEN - len(self._name) if comp_len > 0: return b'x' * comp_len else: return b'x' def getTooLongFilesystemName(self, too_long_component): return self._name + b'/' + self._makeTooLongName(too_long_component) def getTooLongSnap(self, too_long_component): return self._name + b'@' + self._makeTooLongName(too_long_component) def getTooLongBookmark(self, too_long_component): return self._name + b'#' + self._makeTooLongName(too_long_component) def _visitFilesystems(self, visitor): for child in self._children: child._visitFilesystems(visitor) visitor(self) def visitFilesystems(self, visitor): def _fsVisitor(fs): visitor(fs._name) self._visitFilesystems(_fsVisitor) def visitSnaps(self, visitor): def _snapVisitor(fs): for i in range(1, fs._snap_id + 1): visitor(fs._makeSnapName(i)) self._visitFilesystems(_snapVisitor) def visitBookmarks(self, visitor): def _bmarkVisitor(fs): for i in range(1, fs._bmark_id + 1): visitor(fs._makeBookmarkName(i)) self._visitFilesystems(_bmarkVisitor) # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 diff --git a/include/os/linux/kernel/linux/blkdev_compat.h b/include/os/linux/kernel/linux/blkdev_compat.h index b57d0a896df3..fe809176fbbb 100644 --- a/include/os/linux/kernel/linux/blkdev_compat.h +++ b/include/os/linux/kernel/linux/blkdev_compat.h @@ -1,575 +1,575 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (C) 2011 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Brian Behlendorf . * LLNL-CODE-403049. */ #ifndef _ZFS_BLKDEV_H #define _ZFS_BLKDEV_H #include #include #include #include #include /* for SECTOR_* */ #ifndef HAVE_BLK_QUEUE_FLAG_SET static inline void blk_queue_flag_set(unsigned int flag, struct request_queue *q) { queue_flag_set(flag, q); } #endif #ifndef HAVE_BLK_QUEUE_FLAG_CLEAR static inline void blk_queue_flag_clear(unsigned int flag, struct request_queue *q) { queue_flag_clear(flag, q); } #endif /* * 4.7 - 4.x API, * The blk_queue_write_cache() interface has replaced blk_queue_flush() * interface. However, the new interface is GPL-only thus we implement * our own trivial wrapper when the GPL-only version is detected. * * 2.6.36 - 4.6 API, * The blk_queue_flush() interface has replaced blk_queue_ordered() * interface. However, while the old interface was available to all the * new one is GPL-only. Thus if the GPL-only version is detected we * implement our own trivial helper. */ static inline void blk_queue_set_write_cache(struct request_queue *q, bool wc, bool fua) { #if defined(HAVE_BLK_QUEUE_WRITE_CACHE_GPL_ONLY) if (wc) blk_queue_flag_set(QUEUE_FLAG_WC, q); else blk_queue_flag_clear(QUEUE_FLAG_WC, q); if (fua) blk_queue_flag_set(QUEUE_FLAG_FUA, q); else blk_queue_flag_clear(QUEUE_FLAG_FUA, q); #elif defined(HAVE_BLK_QUEUE_WRITE_CACHE) blk_queue_write_cache(q, wc, fua); #elif defined(HAVE_BLK_QUEUE_FLUSH_GPL_ONLY) if (wc) q->flush_flags |= REQ_FLUSH; if (fua) q->flush_flags |= REQ_FUA; #elif defined(HAVE_BLK_QUEUE_FLUSH) blk_queue_flush(q, (wc ? REQ_FLUSH : 0) | (fua ? REQ_FUA : 0)); #else #error "Unsupported kernel" #endif } static inline void blk_queue_set_read_ahead(struct request_queue *q, unsigned long ra_pages) { #ifdef HAVE_BLK_QUEUE_BDI_DYNAMIC q->backing_dev_info->ra_pages = ra_pages; #else q->backing_dev_info.ra_pages = ra_pages; #endif } #ifdef HAVE_BIO_BVEC_ITER #define BIO_BI_SECTOR(bio) (bio)->bi_iter.bi_sector #define BIO_BI_SIZE(bio) (bio)->bi_iter.bi_size #define BIO_BI_IDX(bio) (bio)->bi_iter.bi_idx #define BIO_BI_SKIP(bio) (bio)->bi_iter.bi_bvec_done #define bio_for_each_segment4(bv, bvp, b, i) \ bio_for_each_segment((bv), (b), (i)) typedef struct bvec_iter bvec_iterator_t; #else #define BIO_BI_SECTOR(bio) (bio)->bi_sector #define BIO_BI_SIZE(bio) (bio)->bi_size #define BIO_BI_IDX(bio) (bio)->bi_idx #define BIO_BI_SKIP(bio) (0) #define bio_for_each_segment4(bv, bvp, b, i) \ bio_for_each_segment((bvp), (b), (i)) typedef int bvec_iterator_t; #endif static inline void bio_set_flags_failfast(struct block_device *bdev, int *flags) { #ifdef CONFIG_BUG /* * Disable FAILFAST for loopback devices because of the * following incorrect BUG_ON() in loop_make_request(). * This support is also disabled for md devices because the * test suite layers md devices on top of loopback devices. * This may be removed when the loopback driver is fixed. * * BUG_ON(!lo || (rw != READ && rw != WRITE)); */ if ((MAJOR(bdev->bd_dev) == LOOP_MAJOR) || (MAJOR(bdev->bd_dev) == MD_MAJOR)) return; #ifdef BLOCK_EXT_MAJOR if (MAJOR(bdev->bd_dev) == BLOCK_EXT_MAJOR) return; #endif /* BLOCK_EXT_MAJOR */ #endif /* CONFIG_BUG */ *flags |= REQ_FAILFAST_MASK; } /* * Maximum disk label length, it may be undefined for some kernels. */ #if !defined(DISK_NAME_LEN) #define DISK_NAME_LEN 32 #endif /* DISK_NAME_LEN */ #ifdef HAVE_BIO_BI_STATUS static inline int bi_status_to_errno(blk_status_t status) { switch (status) { case BLK_STS_OK: return (0); case BLK_STS_NOTSUPP: return (EOPNOTSUPP); case BLK_STS_TIMEOUT: return (ETIMEDOUT); case BLK_STS_NOSPC: return (ENOSPC); case BLK_STS_TRANSPORT: return (ENOLINK); case BLK_STS_TARGET: return (EREMOTEIO); case BLK_STS_NEXUS: return (EBADE); case BLK_STS_MEDIUM: return (ENODATA); case BLK_STS_PROTECTION: return (EILSEQ); case BLK_STS_RESOURCE: return (ENOMEM); case BLK_STS_AGAIN: return (EAGAIN); case BLK_STS_IOERR: return (EIO); default: return (EIO); } } static inline blk_status_t errno_to_bi_status(int error) { switch (error) { case 0: return (BLK_STS_OK); case EOPNOTSUPP: return (BLK_STS_NOTSUPP); case ETIMEDOUT: return (BLK_STS_TIMEOUT); case ENOSPC: return (BLK_STS_NOSPC); case ENOLINK: return (BLK_STS_TRANSPORT); case EREMOTEIO: return (BLK_STS_TARGET); case EBADE: return (BLK_STS_NEXUS); case ENODATA: return (BLK_STS_MEDIUM); case EILSEQ: return (BLK_STS_PROTECTION); case ENOMEM: return (BLK_STS_RESOURCE); case EAGAIN: return (BLK_STS_AGAIN); case EIO: return (BLK_STS_IOERR); default: return (BLK_STS_IOERR); } } #endif /* HAVE_BIO_BI_STATUS */ /* * 4.3 API change * The bio_endio() prototype changed slightly. These are helper * macro's to ensure the prototype and invocation are handled. */ #ifdef HAVE_1ARG_BIO_END_IO_T #ifdef HAVE_BIO_BI_STATUS #define BIO_END_IO_ERROR(bio) bi_status_to_errno(bio->bi_status) #define BIO_END_IO_PROTO(fn, x, z) static void fn(struct bio *x) #define BIO_END_IO(bio, error) bio_set_bi_status(bio, error) static inline void bio_set_bi_status(struct bio *bio, int error) { ASSERT3S(error, <=, 0); bio->bi_status = errno_to_bi_status(-error); bio_endio(bio); } #else #define BIO_END_IO_ERROR(bio) (-(bio->bi_error)) #define BIO_END_IO_PROTO(fn, x, z) static void fn(struct bio *x) #define BIO_END_IO(bio, error) bio_set_bi_error(bio, error) static inline void bio_set_bi_error(struct bio *bio, int error) { ASSERT3S(error, <=, 0); bio->bi_error = error; bio_endio(bio); } #endif /* HAVE_BIO_BI_STATUS */ #else #define BIO_END_IO_PROTO(fn, x, z) static void fn(struct bio *x, int z) #define BIO_END_IO(bio, error) bio_endio(bio, error); #endif /* HAVE_1ARG_BIO_END_IO_T */ /* * 4.1 - x.y.z API, * 3.10.0 CentOS 7.x API, * blkdev_reread_part() * * For older kernels trigger a re-reading of the partition table by calling * check_disk_change() which calls flush_disk() to invalidate the device. * * For newer kernels (as of 5.10), bdev_check_media_change is used, in favor of * check_disk_change(), with the modification that invalidation is no longer * forced. */ #ifdef HAVE_CHECK_DISK_CHANGE #define zfs_check_media_change(bdev) check_disk_change(bdev) #ifdef HAVE_BLKDEV_REREAD_PART #define vdev_bdev_reread_part(bdev) blkdev_reread_part(bdev) #else #define vdev_bdev_reread_part(bdev) check_disk_change(bdev) #endif /* HAVE_BLKDEV_REREAD_PART */ #else #ifdef HAVE_BDEV_CHECK_MEDIA_CHANGE static inline int zfs_check_media_change(struct block_device *bdev) { struct gendisk *gd = bdev->bd_disk; const struct block_device_operations *bdo = gd->fops; if (!bdev_check_media_change(bdev)) return (0); /* * Force revalidation, to mimic the old behavior of * check_disk_change() */ if (bdo->revalidate_disk) bdo->revalidate_disk(gd); return (0); } #define vdev_bdev_reread_part(bdev) zfs_check_media_change(bdev) #else /* * This is encountered if check_disk_change() and bdev_check_media_change() * are not available in the kernel - likely due to an API change that needs * to be chased down. */ #error "Unsupported kernel: no usable disk change check" #endif /* HAVE_BDEV_CHECK_MEDIA_CHANGE */ #endif /* HAVE_CHECK_DISK_CHANGE */ /* * 2.6.27 API change * The function was exported for use, prior to this it existed but the * symbol was not exported. * * 4.4.0-6.21 API change for Ubuntu * lookup_bdev() gained a second argument, FMODE_*, to check inode permissions. * * 5.11 API change * Changed to take a dev_t argument which is set on success and return a * non-zero error code on failure. */ static inline int vdev_lookup_bdev(const char *path, dev_t *dev) { #if defined(HAVE_DEVT_LOOKUP_BDEV) return (lookup_bdev(path, dev)); #elif defined(HAVE_1ARG_LOOKUP_BDEV) struct block_device *bdev = lookup_bdev(path); if (IS_ERR(bdev)) return (PTR_ERR(bdev)); *dev = bdev->bd_dev; bdput(bdev); return (0); #elif defined(HAVE_MODE_LOOKUP_BDEV) struct block_device *bdev = lookup_bdev(path, FMODE_READ); if (IS_ERR(bdev)) return (PTR_ERR(bdev)); *dev = bdev->bd_dev; bdput(bdev); return (0); #else #error "Unsupported kernel" #endif } /* * Kernels without bio_set_op_attrs use bi_rw for the bio flags. */ #if !defined(HAVE_BIO_SET_OP_ATTRS) static inline void bio_set_op_attrs(struct bio *bio, unsigned rw, unsigned flags) { bio->bi_rw |= rw | flags; } #endif /* * bio_set_flush - Set the appropriate flags in a bio to guarantee * data are on non-volatile media on completion. * * 2.6.37 - 4.8 API, * Introduce WRITE_FLUSH, WRITE_FUA, and WRITE_FLUSH_FUA flags as a * replacement for WRITE_BARRIER to allow expressing richer semantics * to the block layer. It's up to the block layer to implement the * semantics correctly. Use the WRITE_FLUSH_FUA flag combination. * * 4.8 - 4.9 API, * REQ_FLUSH was renamed to REQ_PREFLUSH. For consistency with previous - * ZoL releases, prefer the WRITE_FLUSH_FUA flag set if it's available. + * OpenZFS releases, prefer the WRITE_FLUSH_FUA flag set if it's available. * * 4.10 API, * The read/write flags and their modifiers, including WRITE_FLUSH, * WRITE_FUA and WRITE_FLUSH_FUA were removed from fs.h in * torvalds/linux@70fd7614 and replaced by direct flag modification * of the REQ_ flags in bio->bi_opf. Use REQ_PREFLUSH. */ static inline void bio_set_flush(struct bio *bio) { #if defined(HAVE_REQ_PREFLUSH) /* >= 4.10 */ bio_set_op_attrs(bio, 0, REQ_PREFLUSH); #elif defined(WRITE_FLUSH_FUA) /* >= 2.6.37 and <= 4.9 */ bio_set_op_attrs(bio, 0, WRITE_FLUSH_FUA); #else #error "Allowing the build will cause bio_set_flush requests to be ignored." #endif } /* * 4.8 - 4.x API, * REQ_OP_FLUSH * * 4.8-rc0 - 4.8-rc1, * REQ_PREFLUSH * * 2.6.36 - 4.7 API, * REQ_FLUSH * * in all cases but may have a performance impact for some kernels. It * has the advantage of minimizing kernel specific changes in the zvol code. * */ static inline boolean_t bio_is_flush(struct bio *bio) { #if defined(HAVE_REQ_OP_FLUSH) && defined(HAVE_BIO_BI_OPF) return ((bio_op(bio) == REQ_OP_FLUSH) || (bio->bi_opf & REQ_PREFLUSH)); #elif defined(HAVE_REQ_PREFLUSH) && defined(HAVE_BIO_BI_OPF) return (bio->bi_opf & REQ_PREFLUSH); #elif defined(HAVE_REQ_PREFLUSH) && !defined(HAVE_BIO_BI_OPF) return (bio->bi_rw & REQ_PREFLUSH); #elif defined(HAVE_REQ_FLUSH) return (bio->bi_rw & REQ_FLUSH); #else #error "Unsupported kernel" #endif } /* * 4.8 - 4.x API, * REQ_FUA flag moved to bio->bi_opf * * 2.6.x - 4.7 API, * REQ_FUA */ static inline boolean_t bio_is_fua(struct bio *bio) { #if defined(HAVE_BIO_BI_OPF) return (bio->bi_opf & REQ_FUA); #elif defined(REQ_FUA) return (bio->bi_rw & REQ_FUA); #else #error "Allowing the build will cause fua requests to be ignored." #endif } /* * 4.8 - 4.x API, * REQ_OP_DISCARD * * 2.6.36 - 4.7 API, * REQ_DISCARD * * In all cases the normal I/O path is used for discards. The only * difference is how the kernel tags individual I/Os as discards. */ static inline boolean_t bio_is_discard(struct bio *bio) { #if defined(HAVE_REQ_OP_DISCARD) return (bio_op(bio) == REQ_OP_DISCARD); #elif defined(HAVE_REQ_DISCARD) return (bio->bi_rw & REQ_DISCARD); #else #error "Unsupported kernel" #endif } /* * 4.8 - 4.x API, * REQ_OP_SECURE_ERASE * * 2.6.36 - 4.7 API, * REQ_SECURE */ static inline boolean_t bio_is_secure_erase(struct bio *bio) { #if defined(HAVE_REQ_OP_SECURE_ERASE) return (bio_op(bio) == REQ_OP_SECURE_ERASE); #elif defined(REQ_SECURE) return (bio->bi_rw & REQ_SECURE); #else return (0); #endif } /* * 2.6.33 API change * Discard granularity and alignment restrictions may now be set. For * older kernels which do not support this it is safe to skip it. */ static inline void blk_queue_discard_granularity(struct request_queue *q, unsigned int dg) { q->limits.discard_granularity = dg; } /* * 4.8 - 4.x API, * blk_queue_secure_erase() * * 2.6.36 - 4.7 API, * blk_queue_secdiscard() */ static inline int blk_queue_discard_secure(struct request_queue *q) { #if defined(HAVE_BLK_QUEUE_SECURE_ERASE) return (blk_queue_secure_erase(q)); #elif defined(HAVE_BLK_QUEUE_SECDISCARD) return (blk_queue_secdiscard(q)); #else return (0); #endif } /* * A common holder for vdev_bdev_open() is used to relax the exclusive open * semantics slightly. Internal vdev disk callers may pass VDEV_HOLDER to * allow them to open the device multiple times. Other kernel callers and * user space processes which don't pass this value will get EBUSY. This is * currently required for the correct operation of hot spares. */ #define VDEV_HOLDER ((void *)0x2401de7) static inline unsigned long blk_generic_start_io_acct(struct request_queue *q __attribute__((unused)), struct gendisk *disk __attribute__((unused)), int rw __attribute__((unused)), struct bio *bio) { #if defined(HAVE_DISK_IO_ACCT) return (disk_start_io_acct(disk, bio_sectors(bio), bio_op(bio))); #elif defined(HAVE_BIO_IO_ACCT) return (bio_start_io_acct(bio)); #elif defined(HAVE_GENERIC_IO_ACCT_3ARG) unsigned long start_time = jiffies; generic_start_io_acct(rw, bio_sectors(bio), &disk->part0); return (start_time); #elif defined(HAVE_GENERIC_IO_ACCT_4ARG) unsigned long start_time = jiffies; generic_start_io_acct(q, rw, bio_sectors(bio), &disk->part0); return (start_time); #else /* Unsupported */ return (0); #endif } static inline void blk_generic_end_io_acct(struct request_queue *q __attribute__((unused)), struct gendisk *disk __attribute__((unused)), int rw __attribute__((unused)), struct bio *bio, unsigned long start_time) { #if defined(HAVE_DISK_IO_ACCT) disk_end_io_acct(disk, bio_op(bio), start_time); #elif defined(HAVE_BIO_IO_ACCT) bio_end_io_acct(bio, start_time); #elif defined(HAVE_GENERIC_IO_ACCT_3ARG) generic_end_io_acct(rw, &disk->part0, start_time); #elif defined(HAVE_GENERIC_IO_ACCT_4ARG) generic_end_io_acct(q, rw, &disk->part0, start_time); #endif } #ifndef HAVE_SUBMIT_BIO_IN_BLOCK_DEVICE_OPERATIONS static inline struct request_queue * blk_generic_alloc_queue(make_request_fn make_request, int node_id) { #if defined(HAVE_BLK_ALLOC_QUEUE_REQUEST_FN) return (blk_alloc_queue(make_request, node_id)); #elif defined(HAVE_BLK_ALLOC_QUEUE_REQUEST_FN_RH) return (blk_alloc_queue_rh(make_request, node_id)); #else struct request_queue *q = blk_alloc_queue(GFP_KERNEL); if (q != NULL) blk_queue_make_request(q, make_request); return (q); #endif } #endif /* !HAVE_SUBMIT_BIO_IN_BLOCK_DEVICE_OPERATIONS */ #endif /* _ZFS_BLKDEV_H */ diff --git a/man/man8/zed.8.in b/man/man8/zed.8.in index 57e2dc4dac06..38f8b663d38e 100644 --- a/man/man8/zed.8.in +++ b/man/man8/zed.8.in @@ -1,249 +1,249 @@ .\" .\" This file is part of the ZFS Event Daemon (ZED). .\" Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). .\" Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. -.\" Refer to the ZoL git commit log for authoritative copyright attribution. +.\" Refer to the OpenZFS git commit log for authoritative copyright attribution. .\" .\" The contents of this file are subject to the terms of the .\" Common Development and Distribution License Version 1.0 (CDDL-1.0). .\" You can obtain a copy of the license from the top-level file .\" "OPENSOLARIS.LICENSE" or at . .\" You may not use this file except in compliance with the license. .\" .TH ZED 8 "Aug 24, 2020" OpenZFS .SH NAME ZED \- ZFS Event Daemon .SH SYNOPSIS .HP .B zed .\" [\fB\-c\fR \fIconfigfile\fR] [\fB\-d\fR \fIzedletdir\fR] [\fB\-f\fR] [\fB\-F\fR] [\fB\-h\fR] [\fB\-I\fR] [\fB\-L\fR] [\fB\-M\fR] [\fB\-p\fR \fIpidfile\fR] [\fB\-P\fR \fIpath\fR] [\fB\-s\fR \fIstatefile\fR] [\fB\-j\fR \fIjobs\fR] [\fB\-v\fR] [\fB\-V\fR] [\fB\-Z\fR] .SH DESCRIPTION .PP \fBZED\fR (ZFS Event Daemon) monitors events generated by the ZFS kernel module. When a zevent (ZFS Event) is posted, \fBZED\fR will run any ZEDLETs (ZFS Event Daemon Linkage for Executable Tasks) that have been enabled for the corresponding zevent class. .SH OPTIONS .TP .BI \-h Display a summary of the command-line options. .TP .BI \-L Display license information. .TP .BI \-V Display version information. .TP .BI \-v Be verbose. .TP .BI \-f Force the daemon to run if at all possible, disabling security checks and throwing caution to the wind. Not recommended for use in production. .TP .BI \-F Run the daemon in the foreground. .TP .BI \-M Lock all current and future pages in the virtual memory address space. This may help the daemon remain responsive when the system is under heavy memory pressure. .TP .BI \-I Request that the daemon idle rather than exit when the kernel modules are not loaded. Processing of events will start, or resume, when the kernel modules are (re)loaded. Under Linux the kernel modules cannot be unloaded while the daemon is running. .TP .BI \-Z Zero the daemon's state, thereby allowing zevents still within the kernel to be reprocessed. .TP .BI \-d\ zedletdir Read the enabled ZEDLETs from the specified directory. .TP .BI \-p\ pidfile Write the daemon's process ID to the specified file. .TP .BI \-P\ path Custom $PATH for zedlets to use. Normally zedlets run in a locked-down environment, with hardcoded paths to the ZFS commands ($ZFS, $ZPOOL, $ZED, ...), and a hardcoded $PATH. This is done for security reasons. However, the ZFS test suite uses a custom PATH for its ZFS commands, and passes it to zed with -P. In short, -P is only to be used by the ZFS test suite; never use it in production! .TP .BI \-s\ statefile Write the daemon's state to the specified file. .TP .BI \-j\ jobs Allow at most \fIjobs\fR ZEDLETs to run concurrently, delaying execution of new ones until they finish. Defaults to 16. .SH ZEVENTS .PP A zevent is comprised of a list of nvpairs (name/value pairs). Each zevent contains an EID (Event IDentifier) that uniquely identifies it throughout the lifetime of the loaded ZFS kernel module; this EID is a monotonically increasing integer that resets to 1 each time the kernel module is loaded. Each zevent also contains a class string that identifies the type of event. For brevity, a subclass string is defined that omits the leading components of the class string. Additional nvpairs exist to provide event details. .PP The kernel maintains a list of recent zevents that can be viewed (along with their associated lists of nvpairs) using the "\fBzpool events \-v\fR" command. .SH CONFIGURATION .PP ZEDLETs to be invoked in response to zevents are located in the \fIenabled-zedlets\fR directory. These can be symlinked or copied from the \fIinstalled-zedlets\fR directory; symlinks allow for automatic updates from the installed ZEDLETs, whereas copies preserve local modifications. As a security measure, since ownership change is a privileged operation, ZEDLETs must be owned by root. They must have execute permissions for the user, but they must not have write permissions for group or other. Dotfiles are ignored. .PP ZEDLETs are named after the zevent class for which they should be invoked. In particular, a ZEDLET will be invoked for a given zevent if either its class or subclass string is a prefix of its filename (and is followed by a non-alphabetic character). As a special case, the prefix "all" matches all zevents. Multiple ZEDLETs may be invoked for a given zevent. .SH ZEDLETS .PP ZEDLETs are executables invoked by the ZED in response to a given zevent. They should be written under the presumption they can be invoked concurrently, and they should use appropriate locking to access any shared resources. Common variables used by ZEDLETs can be stored in the default rc file which is sourced by scripts; these variables should be prefixed with "ZED_". .PP The zevent nvpairs are passed to ZEDLETs as environment variables. Each nvpair name is converted to an environment variable in the following manner: 1) it is prefixed with "ZEVENT_", 2) it is converted to uppercase, and 3) each non-alphanumeric character is converted to an underscore. Some additional environment variables have been defined to present certain nvpair values in a more convenient form. An incomplete list of zevent environment variables is as follows: .TP .B ZEVENT_EID The Event IDentifier. .TP .B ZEVENT_CLASS The zevent class string. .TP .B ZEVENT_SUBCLASS The zevent subclass string. .TP .B ZEVENT_TIME The time at which the zevent was posted as "\fIseconds\fR\ \fInanoseconds\fR" since the Epoch. .TP .B ZEVENT_TIME_SECS The \fIseconds\fR component of ZEVENT_TIME. .TP .B ZEVENT_TIME_NSECS The \fInanoseconds\fR component of ZEVENT_TIME. .TP .B ZEVENT_TIME_STRING An almost-RFC3339-compliant string for ZEVENT_TIME. .PP Additionally, the following ZED & ZFS variables are defined: .TP .B ZED_PID The daemon's process ID. .TP .B ZED_ZEDLET_DIR The daemon's current \fIenabled-zedlets\fR directory. .TP .B ZFS_ALIAS The ZFS alias (\fIname-version-release\fR) string used to build the daemon. .TP .B ZFS_VERSION The ZFS version used to build the daemon. .TP .B ZFS_RELEASE The ZFS release used to build the daemon. .PP ZEDLETs may need to call other ZFS commands. The installation paths of the following executables are defined: \fBZDB\fR, \fBZED\fR, \fBZFS\fR, \fBZINJECT\fR, and \fBZPOOL\fR. These variables can be overridden in the rc file if needed. .SH FILES .TP .I @sysconfdir@/zfs/zed.d The default directory for enabled ZEDLETs. .TP .I @sysconfdir@/zfs/zed.d/zed.rc The default rc file for common variables used by ZEDLETs. .TP .I @zfsexecdir@/zed.d The default directory for installed ZEDLETs. .TP .I @runstatedir@/zed.pid The default file containing the daemon's process ID. .TP .I @runstatedir@/zed.state The default file containing the daemon's state. .SH SIGNALS .TP .B HUP Reconfigure the daemon and rescan the directory for enabled ZEDLETs. .TP .B TERM Terminate the daemon. .SH NOTES .PP \fBZED\fR requires root privileges. .\" Do not taunt zed. .SH BUGS .PP ZEDLETs are unable to return state/status information to the kernel. .PP Internationalization support via gettext has not been added. .SH LICENSE .PP \fBZED\fR (ZFS Event Daemon) is distributed under the terms of the Common Development and Distribution License Version 1.0 (CDDL\-1.0). .PP Developed at Lawrence Livermore National Laboratory (LLNL\-CODE\-403049). .SH SEE ALSO .BR zfs (8), .BR zpool (8) .BR zpool-events (8) diff --git a/module/icp/algs/edonr/edonr.c b/module/icp/algs/edonr/edonr.c index 7c677095f1ef..ee96e692ef00 100644 --- a/module/icp/algs/edonr/edonr.c +++ b/module/icp/algs/edonr/edonr.c @@ -1,746 +1,746 @@ /* * IDI,NTNU * * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright (C) 2009, 2010, Jorn Amundsen * Tweaked Edon-R implementation for SUPERCOP, based on NIST API. * * $Id: edonr.c 517 2013-02-17 20:34:39Z joern $ */ /* * Portions copyright (c) 2013, Saso Kiselkov, All rights reserved */ #include #include #include /* big endian support, provides no-op's if run on little endian hosts */ #include "edonr_byteorder.h" #define hashState224(x) ((x)->pipe->p256) #define hashState256(x) ((x)->pipe->p256) #define hashState384(x) ((x)->pipe->p512) #define hashState512(x) ((x)->pipe->p512) /* shift and rotate shortcuts */ #define shl(x, n) ((x) << n) #define shr(x, n) ((x) >> n) #define rotl32(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) #define rotr32(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) #define rotl64(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) #define rotr64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) #if !defined(__C99_RESTRICT) #define restrict /* restrict */ #endif #define EDONR_VALID_HASHBITLEN(x) \ ((x) == 512 || (x) == 384 || (x) == 256 || (x) == 224) /* EdonR224 initial double chaining pipe */ static const uint32_t i224p2[16] = { 0x00010203ul, 0x04050607ul, 0x08090a0bul, 0x0c0d0e0ful, 0x10111213ul, 0x14151617ul, 0x18191a1bul, 0x1c1d1e1ful, 0x20212223ul, 0x24252627ul, 0x28292a2bul, 0x2c2d2e2ful, 0x30313233ul, 0x34353637ul, 0x38393a3bul, 0x3c3d3e3ful, }; /* EdonR256 initial double chaining pipe */ static const uint32_t i256p2[16] = { 0x40414243ul, 0x44454647ul, 0x48494a4bul, 0x4c4d4e4ful, 0x50515253ul, 0x54555657ul, 0x58595a5bul, 0x5c5d5e5ful, 0x60616263ul, 0x64656667ul, 0x68696a6bul, 0x6c6d6e6ful, 0x70717273ul, 0x74757677ul, 0x78797a7bul, 0x7c7d7e7ful, }; /* EdonR384 initial double chaining pipe */ static const uint64_t i384p2[16] = { 0x0001020304050607ull, 0x08090a0b0c0d0e0full, 0x1011121314151617ull, 0x18191a1b1c1d1e1full, 0x2021222324252627ull, 0x28292a2b2c2d2e2full, 0x3031323334353637ull, 0x38393a3b3c3d3e3full, 0x4041424344454647ull, 0x48494a4b4c4d4e4full, 0x5051525354555657ull, 0x58595a5b5c5d5e5full, 0x6061626364656667ull, 0x68696a6b6c6d6e6full, 0x7071727374757677ull, 0x78797a7b7c7d7e7full }; /* EdonR512 initial double chaining pipe */ static const uint64_t i512p2[16] = { 0x8081828384858687ull, 0x88898a8b8c8d8e8full, 0x9091929394959697ull, 0x98999a9b9c9d9e9full, 0xa0a1a2a3a4a5a6a7ull, 0xa8a9aaabacadaeafull, 0xb0b1b2b3b4b5b6b7ull, 0xb8b9babbbcbdbebfull, 0xc0c1c2c3c4c5c6c7ull, 0xc8c9cacbcccdcecfull, 0xd0d1d2d3d4d5d6d7ull, 0xd8d9dadbdcdddedfull, 0xe0e1e2e3e4e5e6e7ull, 0xe8e9eaebecedeeefull, 0xf0f1f2f3f4f5f6f7ull, 0xf8f9fafbfcfdfeffull }; /* * First Latin Square * 0 7 1 3 2 4 6 5 * 4 1 7 6 3 0 5 2 * 7 0 4 2 5 3 1 6 * 1 4 0 5 6 2 7 3 * 2 3 6 7 1 5 0 4 * 5 2 3 1 7 6 4 0 * 3 6 5 0 4 7 2 1 * 6 5 2 4 0 1 3 7 */ #define LS1_256(c, x0, x1, x2, x3, x4, x5, x6, x7) \ { \ uint32_t x04, x17, x23, x56, x07, x26; \ x04 = x0+x4, x17 = x1+x7, x07 = x04+x17; \ s0 = c + x07 + x2; \ s1 = rotl32(x07 + x3, 4); \ s2 = rotl32(x07 + x6, 8); \ x23 = x2 + x3; \ s5 = rotl32(x04 + x23 + x5, 22); \ x56 = x5 + x6; \ s6 = rotl32(x17 + x56 + x0, 24); \ x26 = x23+x56; \ s3 = rotl32(x26 + x7, 13); \ s4 = rotl32(x26 + x1, 17); \ s7 = rotl32(x26 + x4, 29); \ } #define LS1_512(c, x0, x1, x2, x3, x4, x5, x6, x7) \ { \ uint64_t x04, x17, x23, x56, x07, x26; \ x04 = x0+x4, x17 = x1+x7, x07 = x04+x17; \ s0 = c + x07 + x2; \ s1 = rotl64(x07 + x3, 5); \ s2 = rotl64(x07 + x6, 15); \ x23 = x2 + x3; \ s5 = rotl64(x04 + x23 + x5, 40); \ x56 = x5 + x6; \ s6 = rotl64(x17 + x56 + x0, 50); \ x26 = x23+x56; \ s3 = rotl64(x26 + x7, 22); \ s4 = rotl64(x26 + x1, 31); \ s7 = rotl64(x26 + x4, 59); \ } /* * Second Orthogonal Latin Square * 0 4 2 3 1 6 5 7 * 7 6 3 2 5 4 1 0 * 5 3 1 6 0 2 7 4 * 1 0 5 4 3 7 2 6 * 2 1 0 7 4 5 6 3 * 3 5 7 0 6 1 4 2 * 4 7 6 1 2 0 3 5 * 6 2 4 5 7 3 0 1 */ #define LS2_256(c, y0, y1, y2, y3, y4, y5, y6, y7) \ { \ uint32_t y01, y25, y34, y67, y04, y05, y27, y37; \ y01 = y0+y1, y25 = y2+y5, y05 = y01+y25; \ t0 = ~c + y05 + y7; \ t2 = rotl32(y05 + y3, 9); \ y34 = y3+y4, y04 = y01+y34; \ t1 = rotl32(y04 + y6, 5); \ t4 = rotl32(y04 + y5, 15); \ y67 = y6+y7, y37 = y34+y67; \ t3 = rotl32(y37 + y2, 11); \ t7 = rotl32(y37 + y0, 27); \ y27 = y25+y67; \ t5 = rotl32(y27 + y4, 20); \ t6 = rotl32(y27 + y1, 25); \ } #define LS2_512(c, y0, y1, y2, y3, y4, y5, y6, y7) \ { \ uint64_t y01, y25, y34, y67, y04, y05, y27, y37; \ y01 = y0+y1, y25 = y2+y5, y05 = y01+y25; \ t0 = ~c + y05 + y7; \ t2 = rotl64(y05 + y3, 19); \ y34 = y3+y4, y04 = y01+y34; \ t1 = rotl64(y04 + y6, 10); \ t4 = rotl64(y04 + y5, 36); \ y67 = y6+y7, y37 = y34+y67; \ t3 = rotl64(y37 + y2, 29); \ t7 = rotl64(y37 + y0, 55); \ y27 = y25+y67; \ t5 = rotl64(y27 + y4, 44); \ t6 = rotl64(y27 + y1, 48); \ } #define quasi_exform256(r0, r1, r2, r3, r4, r5, r6, r7) \ { \ uint32_t s04, s17, s23, s56, t01, t25, t34, t67; \ s04 = s0 ^ s4, t01 = t0 ^ t1; \ r0 = (s04 ^ s1) + (t01 ^ t5); \ t67 = t6 ^ t7; \ r1 = (s04 ^ s7) + (t2 ^ t67); \ s23 = s2 ^ s3; \ r7 = (s23 ^ s5) + (t4 ^ t67); \ t34 = t3 ^ t4; \ r3 = (s23 ^ s4) + (t0 ^ t34); \ s56 = s5 ^ s6; \ r5 = (s3 ^ s56) + (t34 ^ t6); \ t25 = t2 ^ t5; \ r6 = (s2 ^ s56) + (t25 ^ t7); \ s17 = s1 ^ s7; \ r4 = (s0 ^ s17) + (t1 ^ t25); \ r2 = (s17 ^ s6) + (t01 ^ t3); \ } #define quasi_exform512(r0, r1, r2, r3, r4, r5, r6, r7) \ { \ uint64_t s04, s17, s23, s56, t01, t25, t34, t67; \ s04 = s0 ^ s4, t01 = t0 ^ t1; \ r0 = (s04 ^ s1) + (t01 ^ t5); \ t67 = t6 ^ t7; \ r1 = (s04 ^ s7) + (t2 ^ t67); \ s23 = s2 ^ s3; \ r7 = (s23 ^ s5) + (t4 ^ t67); \ t34 = t3 ^ t4; \ r3 = (s23 ^ s4) + (t0 ^ t34); \ s56 = s5 ^ s6; \ r5 = (s3 ^ s56) + (t34 ^ t6); \ t25 = t2 ^ t5; \ r6 = (s2 ^ s56) + (t25 ^ t7); \ s17 = s1 ^ s7; \ r4 = (s0 ^ s17) + (t1 ^ t25); \ r2 = (s17 ^ s6) + (t01 ^ t3); \ } static size_t Q256(size_t bitlen, const uint32_t *data, uint32_t *restrict p) { size_t bl; for (bl = bitlen; bl >= EdonR256_BLOCK_BITSIZE; bl -= EdonR256_BLOCK_BITSIZE, data += 16) { uint32_t s0, s1, s2, s3, s4, s5, s6, s7, t0, t1, t2, t3, t4, t5, t6, t7; uint32_t p0, p1, p2, p3, p4, p5, p6, p7, q0, q1, q2, q3, q4, q5, q6, q7; const uint32_t defix = 0xaaaaaaaa; #if defined(MACHINE_IS_BIG_ENDIAN) uint32_t swp0, swp1, swp2, swp3, swp4, swp5, swp6, swp7, swp8, swp9, swp10, swp11, swp12, swp13, swp14, swp15; #define d(j) swp ## j #define s32(j) ld_swap32((uint32_t *)data + j, swp ## j) #else #define d(j) data[j] #endif /* First row of quasigroup e-transformations */ #if defined(MACHINE_IS_BIG_ENDIAN) s32(8); s32(9); s32(10); s32(11); s32(12); s32(13); s32(14); s32(15); #endif LS1_256(defix, d(15), d(14), d(13), d(12), d(11), d(10), d(9), d(8)); #if defined(MACHINE_IS_BIG_ENDIAN) s32(0); s32(1); s32(2); s32(3); s32(4); s32(5); s32(6); s32(7); #undef s32 #endif LS2_256(defix, d(0), d(1), d(2), d(3), d(4), d(5), d(6), d(7)); quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7); LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_256(defix, d(8), d(9), d(10), d(11), d(12), d(13), d(14), d(15)); quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7); /* Second row of quasigroup e-transformations */ LS1_256(defix, p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); LS2_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7); LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_256(defix, q0, q1, q2, q3, q4, q5, q6, q7); quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7); /* Third row of quasigroup e-transformations */ LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_256(defix, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7); LS1_256(defix, q0, q1, q2, q3, q4, q5, q6, q7); LS2_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7); /* Fourth row of quasigroup e-transformations */ LS1_256(defix, d(7), d(6), d(5), d(4), d(3), d(2), d(1), d(0)); LS2_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7); LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_256(defix, q0, q1, q2, q3, q4, q5, q6, q7); quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7); /* Edon-R tweak on the original SHA-3 Edon-R submission. */ p[0] ^= d(8) ^ p0; p[1] ^= d(9) ^ p1; p[2] ^= d(10) ^ p2; p[3] ^= d(11) ^ p3; p[4] ^= d(12) ^ p4; p[5] ^= d(13) ^ p5; p[6] ^= d(14) ^ p6; p[7] ^= d(15) ^ p7; p[8] ^= d(0) ^ q0; p[9] ^= d(1) ^ q1; p[10] ^= d(2) ^ q2; p[11] ^= d(3) ^ q3; p[12] ^= d(4) ^ q4; p[13] ^= d(5) ^ q5; p[14] ^= d(6) ^ q6; p[15] ^= d(7) ^ q7; } #undef d return (bitlen - bl); } /* * Why is this #pragma here? * * Checksum functions like this one can go over the stack frame size check * Linux imposes on 32-bit platforms (-Wframe-larger-than=1024). We can - * safely ignore the compiler error since we know that in ZoL, that + * safely ignore the compiler error since we know that in OpenZFS, that * the function will be called from a worker thread that won't be using * much stack. The only function that goes over the 1k limit is Q512(), * which only goes over it by a hair (1248 bytes on ARM32). */ #include /* for _ILP32 */ #ifdef _ILP32 /* We're 32-bit, assume small stack frames */ #pragma GCC diagnostic ignored "-Wframe-larger-than=" #endif #if defined(__IBMC__) && defined(_AIX) && defined(__64BIT__) static inline size_t #else static size_t #endif Q512(size_t bitlen, const uint64_t *data, uint64_t *restrict p) { size_t bl; for (bl = bitlen; bl >= EdonR512_BLOCK_BITSIZE; bl -= EdonR512_BLOCK_BITSIZE, data += 16) { uint64_t s0, s1, s2, s3, s4, s5, s6, s7, t0, t1, t2, t3, t4, t5, t6, t7; uint64_t p0, p1, p2, p3, p4, p5, p6, p7, q0, q1, q2, q3, q4, q5, q6, q7; const uint64_t defix = 0xaaaaaaaaaaaaaaaaull; #if defined(MACHINE_IS_BIG_ENDIAN) uint64_t swp0, swp1, swp2, swp3, swp4, swp5, swp6, swp7, swp8, swp9, swp10, swp11, swp12, swp13, swp14, swp15; #define d(j) swp##j #define s64(j) ld_swap64((uint64_t *)data+j, swp##j) #else #define d(j) data[j] #endif /* First row of quasigroup e-transformations */ #if defined(MACHINE_IS_BIG_ENDIAN) s64(8); s64(9); s64(10); s64(11); s64(12); s64(13); s64(14); s64(15); #endif LS1_512(defix, d(15), d(14), d(13), d(12), d(11), d(10), d(9), d(8)); #if defined(MACHINE_IS_BIG_ENDIAN) s64(0); s64(1); s64(2); s64(3); s64(4); s64(5); s64(6); s64(7); #undef s64 #endif LS2_512(defix, d(0), d(1), d(2), d(3), d(4), d(5), d(6), d(7)); quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7); LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_512(defix, d(8), d(9), d(10), d(11), d(12), d(13), d(14), d(15)); quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7); /* Second row of quasigroup e-transformations */ LS1_512(defix, p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); LS2_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7); LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_512(defix, q0, q1, q2, q3, q4, q5, q6, q7); quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7); /* Third row of quasigroup e-transformations */ LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_512(defix, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7); LS1_512(defix, q0, q1, q2, q3, q4, q5, q6, q7); LS2_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7); /* Fourth row of quasigroup e-transformations */ LS1_512(defix, d(7), d(6), d(5), d(4), d(3), d(2), d(1), d(0)); LS2_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7); LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7); LS2_512(defix, q0, q1, q2, q3, q4, q5, q6, q7); quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7); /* Edon-R tweak on the original SHA-3 Edon-R submission. */ p[0] ^= d(8) ^ p0; p[1] ^= d(9) ^ p1; p[2] ^= d(10) ^ p2; p[3] ^= d(11) ^ p3; p[4] ^= d(12) ^ p4; p[5] ^= d(13) ^ p5; p[6] ^= d(14) ^ p6; p[7] ^= d(15) ^ p7; p[8] ^= d(0) ^ q0; p[9] ^= d(1) ^ q1; p[10] ^= d(2) ^ q2; p[11] ^= d(3) ^ q3; p[12] ^= d(4) ^ q4; p[13] ^= d(5) ^ q5; p[14] ^= d(6) ^ q6; p[15] ^= d(7) ^ q7; } #undef d return (bitlen - bl); } void EdonRInit(EdonRState *state, size_t hashbitlen) { ASSERT(EDONR_VALID_HASHBITLEN(hashbitlen)); switch (hashbitlen) { case 224: state->hashbitlen = 224; state->bits_processed = 0; state->unprocessed_bits = 0; bcopy(i224p2, hashState224(state)->DoublePipe, 16 * sizeof (uint32_t)); break; case 256: state->hashbitlen = 256; state->bits_processed = 0; state->unprocessed_bits = 0; bcopy(i256p2, hashState256(state)->DoublePipe, 16 * sizeof (uint32_t)); break; case 384: state->hashbitlen = 384; state->bits_processed = 0; state->unprocessed_bits = 0; bcopy(i384p2, hashState384(state)->DoublePipe, 16 * sizeof (uint64_t)); break; case 512: state->hashbitlen = 512; state->bits_processed = 0; state->unprocessed_bits = 0; bcopy(i512p2, hashState224(state)->DoublePipe, 16 * sizeof (uint64_t)); break; } } void EdonRUpdate(EdonRState *state, const uint8_t *data, size_t databitlen) { uint32_t *data32; uint64_t *data64; size_t bits_processed; ASSERT(EDONR_VALID_HASHBITLEN(state->hashbitlen)); switch (state->hashbitlen) { case 224: case 256: if (state->unprocessed_bits > 0) { /* LastBytes = databitlen / 8 */ int LastBytes = (int)databitlen >> 3; ASSERT(state->unprocessed_bits + databitlen <= EdonR256_BLOCK_SIZE * 8); bcopy(data, hashState256(state)->LastPart + (state->unprocessed_bits >> 3), LastBytes); state->unprocessed_bits += (int)databitlen; databitlen = state->unprocessed_bits; /* LINTED E_BAD_PTR_CAST_ALIGN */ data32 = (uint32_t *)hashState256(state)->LastPart; } else /* LINTED E_BAD_PTR_CAST_ALIGN */ data32 = (uint32_t *)data; bits_processed = Q256(databitlen, data32, hashState256(state)->DoublePipe); state->bits_processed += bits_processed; databitlen -= bits_processed; state->unprocessed_bits = (int)databitlen; if (databitlen > 0) { /* LastBytes = Ceil(databitlen / 8) */ int LastBytes = ((~(((-(int)databitlen) >> 3) & 0x01ff)) + 1) & 0x01ff; data32 += bits_processed >> 5; /* byte size update */ bcopy(data32, hashState256(state)->LastPart, LastBytes); } break; case 384: case 512: if (state->unprocessed_bits > 0) { /* LastBytes = databitlen / 8 */ int LastBytes = (int)databitlen >> 3; ASSERT(state->unprocessed_bits + databitlen <= EdonR512_BLOCK_SIZE * 8); bcopy(data, hashState512(state)->LastPart + (state->unprocessed_bits >> 3), LastBytes); state->unprocessed_bits += (int)databitlen; databitlen = state->unprocessed_bits; /* LINTED E_BAD_PTR_CAST_ALIGN */ data64 = (uint64_t *)hashState512(state)->LastPart; } else /* LINTED E_BAD_PTR_CAST_ALIGN */ data64 = (uint64_t *)data; bits_processed = Q512(databitlen, data64, hashState512(state)->DoublePipe); state->bits_processed += bits_processed; databitlen -= bits_processed; state->unprocessed_bits = (int)databitlen; if (databitlen > 0) { /* LastBytes = Ceil(databitlen / 8) */ int LastBytes = ((~(((-(int)databitlen) >> 3) & 0x03ff)) + 1) & 0x03ff; data64 += bits_processed >> 6; /* byte size update */ bcopy(data64, hashState512(state)->LastPart, LastBytes); } break; } } void EdonRFinal(EdonRState *state, uint8_t *hashval) { uint32_t *data32; uint64_t *data64, num_bits; size_t databitlen; int LastByte, PadOnePosition; num_bits = state->bits_processed + state->unprocessed_bits; ASSERT(EDONR_VALID_HASHBITLEN(state->hashbitlen)); switch (state->hashbitlen) { case 224: case 256: LastByte = (int)state->unprocessed_bits >> 3; PadOnePosition = 7 - (state->unprocessed_bits & 0x07); hashState256(state)->LastPart[LastByte] = (hashState256(state)->LastPart[LastByte] & (0xff << (PadOnePosition + 1))) ^ (0x01 << PadOnePosition); /* LINTED E_BAD_PTR_CAST_ALIGN */ data64 = (uint64_t *)hashState256(state)->LastPart; if (state->unprocessed_bits < 448) { (void) memset((hashState256(state)->LastPart) + LastByte + 1, 0x00, EdonR256_BLOCK_SIZE - LastByte - 9); databitlen = EdonR256_BLOCK_SIZE * 8; #if defined(MACHINE_IS_BIG_ENDIAN) st_swap64(num_bits, data64 + 7); #else data64[7] = num_bits; #endif } else { (void) memset((hashState256(state)->LastPart) + LastByte + 1, 0x00, EdonR256_BLOCK_SIZE * 2 - LastByte - 9); databitlen = EdonR256_BLOCK_SIZE * 16; #if defined(MACHINE_IS_BIG_ENDIAN) st_swap64(num_bits, data64 + 15); #else data64[15] = num_bits; #endif } /* LINTED E_BAD_PTR_CAST_ALIGN */ data32 = (uint32_t *)hashState256(state)->LastPart; state->bits_processed += Q256(databitlen, data32, hashState256(state)->DoublePipe); break; case 384: case 512: LastByte = (int)state->unprocessed_bits >> 3; PadOnePosition = 7 - (state->unprocessed_bits & 0x07); hashState512(state)->LastPart[LastByte] = (hashState512(state)->LastPart[LastByte] & (0xff << (PadOnePosition + 1))) ^ (0x01 << PadOnePosition); /* LINTED E_BAD_PTR_CAST_ALIGN */ data64 = (uint64_t *)hashState512(state)->LastPart; if (state->unprocessed_bits < 960) { (void) memset((hashState512(state)->LastPart) + LastByte + 1, 0x00, EdonR512_BLOCK_SIZE - LastByte - 9); databitlen = EdonR512_BLOCK_SIZE * 8; #if defined(MACHINE_IS_BIG_ENDIAN) st_swap64(num_bits, data64 + 15); #else data64[15] = num_bits; #endif } else { (void) memset((hashState512(state)->LastPart) + LastByte + 1, 0x00, EdonR512_BLOCK_SIZE * 2 - LastByte - 9); databitlen = EdonR512_BLOCK_SIZE * 16; #if defined(MACHINE_IS_BIG_ENDIAN) st_swap64(num_bits, data64 + 31); #else data64[31] = num_bits; #endif } state->bits_processed += Q512(databitlen, data64, hashState512(state)->DoublePipe); break; } switch (state->hashbitlen) { case 224: { #if defined(MACHINE_IS_BIG_ENDIAN) uint32_t *d32 = (uint32_t *)hashval; uint32_t *s32 = hashState224(state)->DoublePipe + 9; int j; for (j = 0; j < EdonR224_DIGEST_SIZE >> 2; j++) st_swap32(s32[j], d32 + j); #else bcopy(hashState256(state)->DoublePipe + 9, hashval, EdonR224_DIGEST_SIZE); #endif break; } case 256: { #if defined(MACHINE_IS_BIG_ENDIAN) uint32_t *d32 = (uint32_t *)hashval; uint32_t *s32 = hashState224(state)->DoublePipe + 8; int j; for (j = 0; j < EdonR256_DIGEST_SIZE >> 2; j++) st_swap32(s32[j], d32 + j); #else bcopy(hashState256(state)->DoublePipe + 8, hashval, EdonR256_DIGEST_SIZE); #endif break; } case 384: { #if defined(MACHINE_IS_BIG_ENDIAN) uint64_t *d64 = (uint64_t *)hashval; uint64_t *s64 = hashState384(state)->DoublePipe + 10; int j; for (j = 0; j < EdonR384_DIGEST_SIZE >> 3; j++) st_swap64(s64[j], d64 + j); #else bcopy(hashState384(state)->DoublePipe + 10, hashval, EdonR384_DIGEST_SIZE); #endif break; } case 512: { #if defined(MACHINE_IS_BIG_ENDIAN) uint64_t *d64 = (uint64_t *)hashval; uint64_t *s64 = hashState512(state)->DoublePipe + 8; int j; for (j = 0; j < EdonR512_DIGEST_SIZE >> 3; j++) st_swap64(s64[j], d64 + j); #else bcopy(hashState512(state)->DoublePipe + 8, hashval, EdonR512_DIGEST_SIZE); #endif break; } } } void EdonRHash(size_t hashbitlen, const uint8_t *data, size_t databitlen, uint8_t *hashval) { EdonRState state; EdonRInit(&state, hashbitlen); EdonRUpdate(&state, data, databitlen); EdonRFinal(&state, hashval); } #ifdef _KERNEL EXPORT_SYMBOL(EdonRInit); EXPORT_SYMBOL(EdonRUpdate); EXPORT_SYMBOL(EdonRHash); EXPORT_SYMBOL(EdonRFinal); #endif diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index 6dcce63291a8..b1750942f53f 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -1,555 +1,555 @@ %global _sbindir /sbin %global _libdir /%{_lib} # Set the default udev directory based on distribution. %if %{undefined _udevdir} %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 || 0%{?centos} >= 7 %global _udevdir %{_prefix}/lib/udev %else %global _udevdir /lib/udev %endif %endif # Set the default udevrule directory based on distribution. %if %{undefined _udevruledir} %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 || 0%{?centos} >= 7 %global _udevruledir %{_prefix}/lib/udev/rules.d %else %global _udevruledir /lib/udev/rules.d %endif %endif # Set the default dracut directory based on distribution. %if %{undefined _dracutdir} %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 || 0%{?centos} >= 7 %global _dracutdir %{_prefix}/lib/dracut %else %global _dracutdir %{_prefix}/share/dracut %endif %endif %if %{undefined _initconfdir} %global _initconfdir /etc/sysconfig %endif %if %{undefined _unitdir} %global _unitdir %{_prefix}/lib/systemd/system %endif %if %{undefined _presetdir} %global _presetdir %{_prefix}/lib/systemd/system-preset %endif %if %{undefined _modulesloaddir} %global _modulesloaddir %{_prefix}/lib/modules-load.d %endif %if %{undefined _systemdgeneratordir} %global _systemdgeneratordir %{_prefix}/lib/systemd/system-generators %endif %if %{undefined _pkgconfigdir} %global _pkgconfigdir %{_prefix}/%{_lib}/pkgconfig %endif %bcond_with debug %bcond_with debuginfo %bcond_with asan %bcond_with systemd %bcond_with pam # Generic enable switch for systemd %if %{with systemd} %define _systemd 1 %endif # RHEL >= 7 comes with systemd %if 0%{?rhel} >= 7 %define _systemd 1 %endif # Fedora >= 15 comes with systemd, but only >= 18 has # the proper macros %if 0%{?fedora} >= 18 %define _systemd 1 %endif # opensuse >= 12.1 comes with systemd, but only >= 13.1 # has the proper macros %if 0%{?suse_version} >= 1310 %define _systemd 1 %endif # When not specified default to distribution provided version. This # is normally Python 3, but for RHEL <= 7 only Python 2 is provided. %if %{undefined __use_python} %if 0%{?rhel} && 0%{?rhel} <= 7 %define __python /usr/bin/python2 %define __python_pkg_version 2 %define __python_cffi_pkg python-cffi %define __python_setuptools_pkg python-setuptools %else %define __python /usr/bin/python3 %define __python_pkg_version 3 %define __python_cffi_pkg python3-cffi %define __python_setuptools_pkg python3-setuptools %endif %else %define __python %{__use_python} %define __python_pkg_version %{__use_python_pkg_version} %define __python_cffi_pkg python%{__python_pkg_version}-cffi %define __python_setuptools_pkg python%{__python_pkg_version}-setuptools %endif %define __python_sitelib %(%{__python} -Esc "from distutils.sysconfig import get_python_lib; print(get_python_lib())") # By default python-pyzfs is enabled, with the exception of # RHEL 6 which by default uses Python 2.6 which is too old. %if 0%{?rhel} == 6 %bcond_with pyzfs %else %bcond_without pyzfs %endif Name: @PACKAGE@ Version: @VERSION@ Release: @RELEASE@%{?dist} Summary: Commands to control the kernel modules and libraries Group: System Environment/Kernel License: @ZFS_META_LICENSE@ URL: https://github.com/openzfs/zfs Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: libzpool5 = %{version} Requires: libnvpair3 = %{version} Requires: libuutil3 = %{version} Requires: libzfs5 = %{version} Requires: %{name}-kmod = %{version} Provides: %{name}-kmod-common = %{version} Obsoletes: spl -# zfs-fuse provides the same commands and man pages that ZoL does. Renaming -# those on either side would conflict with all available documentation. +# zfs-fuse provides the same commands and man pages that OpenZFS does. +# Renaming those on either side would conflict with all available documentation. Conflicts: zfs-fuse %if 0%{?rhel}%{?fedora}%{?suse_version} BuildRequires: gcc, make BuildRequires: zlib-devel BuildRequires: libuuid-devel BuildRequires: libblkid-devel BuildRequires: libudev-devel BuildRequires: libattr-devel BuildRequires: openssl-devel %if 0%{?fedora} >= 28 || 0%{?rhel} >= 8 || 0%{?centos} >= 8 BuildRequires: libtirpc-devel %endif Requires: openssl %if 0%{?_systemd} BuildRequires: systemd %endif %endif %if 0%{?_systemd} Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %endif # The zpool iostat/status -c scripts call some utilities like lsblk and iostat Requires: util-linux Requires: sysstat %description This package contains the core ZFS command line utilities. %package -n libzpool5 Summary: Native ZFS pool library for Linux Group: System Environment/Kernel Obsoletes: libzpool2 Obsoletes: libzpool4 %description -n libzpool5 This package contains the zpool library, which provides support for managing zpools %if %{defined ldconfig_scriptlets} %ldconfig_scriptlets -n libzpool5 %else %post -n libzpool5 -p /sbin/ldconfig %postun -n libzpool5 -p /sbin/ldconfig %endif %package -n libnvpair3 Summary: Solaris name-value library for Linux Group: System Environment/Kernel Obsoletes: libnvpair1 %description -n libnvpair3 This package contains routines for packing and unpacking name-value pairs. This functionality is used to portably transport data across process boundaries, between kernel and user space, and can be used to write self describing data structures on disk. %if %{defined ldconfig_scriptlets} %ldconfig_scriptlets -n libnvpair3 %else %post -n libnvpair3 -p /sbin/ldconfig %postun -n libnvpair3 -p /sbin/ldconfig %endif %package -n libuutil3 Summary: Solaris userland utility library for Linux Group: System Environment/Kernel Obsoletes: libuutil1 %description -n libuutil3 This library provides a variety of compatibility functions for OpenZFS: * libspl: The Solaris Porting Layer userland library, which provides APIs that make it possible to run Solaris user code in a Linux environment with relatively minimal modification. * libavl: The Adelson-Velskii Landis balanced binary tree manipulation library. * libefi: The Extensible Firmware Interface library for GUID disk partitioning. * libshare: NFS, SMB, and iSCSI service integration for ZFS. %if %{defined ldconfig_scriptlets} %ldconfig_scriptlets -n libuutil3 %else %post -n libuutil3 -p /sbin/ldconfig %postun -n libuutil3 -p /sbin/ldconfig %endif # The library version is encoded in the package name. When updating the # version information it is important to add an obsoletes line below for # the previous version of the package. %package -n libzfs5 Summary: Native ZFS filesystem library for Linux Group: System Environment/Kernel Obsoletes: libzfs2 Obsoletes: libzfs4 %description -n libzfs5 This package provides support for managing ZFS filesystems %if %{defined ldconfig_scriptlets} %ldconfig_scriptlets -n libzfs5 %else %post -n libzfs5 -p /sbin/ldconfig %postun -n libzfs5 -p /sbin/ldconfig %endif %package -n libzfs5-devel Summary: Development headers Group: System Environment/Kernel Requires: libzfs5 = %{version} Requires: libzpool5 = %{version} Requires: libnvpair3 = %{version} Requires: libuutil3 = %{version} Provides: libzpool5-devel Provides: libnvpair3-devel Provides: libuutil3-devel Obsoletes: zfs-devel Obsoletes: libzfs2-devel Obsoletes: libzfs4-devel %description -n libzfs5-devel This package contains the header files needed for building additional applications against the ZFS libraries. %package test Summary: Test infrastructure Group: System Environment/Kernel Requires: %{name}%{?_isa} = %{version}-%{release} Requires: parted Requires: lsscsi Requires: mdadm Requires: bc Requires: ksh Requires: fio Requires: acl Requires: sudo Requires: sysstat Requires: libaio Requires: python%{__python_pkg_version} %if 0%{?rhel}%{?fedora}%{?suse_version} BuildRequires: libaio-devel %endif AutoReqProv: no %description test This package contains test infrastructure and support scripts for validating the file system. %package dracut Summary: Dracut module Group: System Environment/Kernel BuildArch: noarch Requires: %{name} >= %{version} Requires: dracut Requires: /usr/bin/awk Requires: grep %description dracut This package contains a dracut module used to construct an initramfs image which is ZFS aware. %if %{with pyzfs} %package -n python%{__python_pkg_version}-pyzfs Summary: Python %{python_version} wrapper for libzfs_core Group: Development/Languages/Python License: Apache-2.0 BuildArch: noarch Requires: libzfs5 = %{version} Requires: libnvpair3 = %{version} Requires: libffi Requires: python%{__python_pkg_version} Requires: %{__python_cffi_pkg} %if 0%{?rhel}%{?fedora}%{?suse_version} BuildRequires: python%{__python_pkg_version}-devel BuildRequires: %{__python_cffi_pkg} BuildRequires: %{__python_setuptools_pkg} BuildRequires: libffi-devel %endif %description -n python%{__python_pkg_version}-pyzfs This package provides a python wrapper for the libzfs_core C library. %endif %if 0%{?_initramfs} %package initramfs Summary: Initramfs module Group: System Environment/Kernel Requires: %{name}%{?_isa} = %{version}-%{release} Requires: %{name} = %{version}-%{release} Requires: initramfs-tools %description initramfs This package contains a initramfs module used to construct an initramfs image which is ZFS aware. %endif %prep %if %{with debug} %define debug --enable-debug %else %define debug --disable-debug %endif %if %{with debuginfo} %define debuginfo --enable-debuginfo %else %define debuginfo --disable-debuginfo %endif %if %{with asan} %define asan --enable-asan %else %define asan --disable-asan %endif %if 0%{?_systemd} %define systemd --enable-systemd --with-systemdunitdir=%{_unitdir} --with-systemdpresetdir=%{_presetdir} --with-systemdmodulesloaddir=%{_modulesloaddir} --with-systemdgeneratordir=%{_systemdgeneratordir} --disable-sysvinit %define systemd_svcs zfs-import-cache.service zfs-import-scan.service zfs-mount.service zfs-share.service zfs-zed.service zfs.target zfs-import.target zfs-volume-wait.service zfs-volumes.target %else %define systemd --enable-sysvinit --disable-systemd %endif %if %{with pyzfs} %define pyzfs --enable-pyzfs %else %define pyzfs --disable-pyzfs %endif %if %{with pam} %define pam --enable-pam %else %define pam --disable-pam %endif %setup -q %build %configure \ --with-config=user \ --with-udevdir=%{_udevdir} \ --with-udevruledir=%{_udevruledir} \ --with-dracutdir=%{_dracutdir} \ --with-pamconfigsdir=%{_datadir}/pam-configs \ --with-pammoduledir=%{_libdir}/security \ --with-python=%{__python} \ --with-pkgconfigdir=%{_pkgconfigdir} \ --disable-static \ %{debug} \ %{debuginfo} \ %{asan} \ %{systemd} \ %{pam} \ %{pyzfs} make %{?_smp_mflags} %install %{__rm} -rf $RPM_BUILD_ROOT make install DESTDIR=%{?buildroot} find %{?buildroot}%{_libdir} -name '*.la' -exec rm -f {} \; %if 0%{!?__brp_mangle_shebangs:1} find %{?buildroot}%{_bindir} \ \( -name arc_summary -or -name arcstat -or -name dbufstat \) \ -exec %{__sed} -i 's|^#!.*|#!%{__python}|' {} \; find %{?buildroot}%{_datadir} \ \( -name test-runner.py -or -name zts-report.py \) \ -exec %{__sed} -i 's|^#!.*|#!%{__python}|' {} \; %endif %post %if 0%{?_systemd} %if 0%{?systemd_post:1} %systemd_post %{systemd_svcs} %else if [ "$1" = "1" -o "$1" = "install" ] ; then # Initial installation systemctl preset %{systemd_svcs} >/dev/null || true fi %endif %else if [ -x /sbin/chkconfig ]; then /sbin/chkconfig --add zfs-import /sbin/chkconfig --add zfs-mount /sbin/chkconfig --add zfs-share /sbin/chkconfig --add zfs-zed fi %endif exit 0 # On RHEL/CentOS 7 the static nodes aren't refreshed by default after # installing a package. This is the default behavior for Fedora. %posttrans %if 0%{?rhel} == 7 || 0%{?centos} == 7 systemctl restart kmod-static-nodes systemctl restart systemd-tmpfiles-setup-dev udevadm trigger %endif %preun %if 0%{?_systemd} %if 0%{?systemd_preun:1} %systemd_preun %{systemd_svcs} %else if [ "$1" = "0" -o "$1" = "remove" ] ; then # Package removal, not upgrade systemctl --no-reload disable %{systemd_svcs} >/dev/null || true systemctl stop %{systemd_svcs} >/dev/null || true fi %endif %else if [ "$1" = "0" -o "$1" = "remove" ] && [ -x /sbin/chkconfig ]; then /sbin/chkconfig --del zfs-import /sbin/chkconfig --del zfs-mount /sbin/chkconfig --del zfs-share /sbin/chkconfig --del zfs-zed fi %endif exit 0 %postun %if 0%{?_systemd} %if 0%{?systemd_postun:1} %systemd_postun %{systemd_svcs} %else systemctl --system daemon-reload >/dev/null || true %endif %endif %files # Core utilities %{_sbindir}/* %{_bindir}/raidz_test %{_sbindir}/zgenhostid %{_bindir}/zvol_wait # Optional Python 2/3 scripts %{_bindir}/arc_summary %{_bindir}/arcstat %{_bindir}/dbufstat # Man pages %{_mandir}/man1/* %{_mandir}/man5/* %{_mandir}/man8/* # Configuration files and scripts %{_libexecdir}/%{name} %{_udevdir}/vdev_id %{_udevdir}/zvol_id %{_udevdir}/rules.d/* %{_datadir}/%{name}/compatibility.d %if ! 0%{?_systemd} || 0%{?_initramfs} # Files needed for sysvinit and initramfs-tools %{_sysconfdir}/%{name}/zfs-functions %config(noreplace) %{_initconfdir}/zfs %else %exclude %{_sysconfdir}/%{name}/zfs-functions %exclude %{_initconfdir}/zfs %endif %if 0%{?_systemd} %{_unitdir}/* %{_presetdir}/* %{_modulesloaddir}/* %{_systemdgeneratordir}/* %else %config(noreplace) %{_sysconfdir}/init.d/* %endif %config(noreplace) %{_sysconfdir}/%{name}/zed.d/* %config(noreplace) %{_sysconfdir}/%{name}/zpool.d/* %config(noreplace) %{_sysconfdir}/%{name}/vdev_id.conf.*.example %attr(440, root, root) %config(noreplace) %{_sysconfdir}/sudoers.d/* %if %{with pam} %{_libdir}/security/* %{_datadir}/pam-configs/* %endif %files -n libzpool5 %{_libdir}/libzpool.so.* %files -n libnvpair3 %{_libdir}/libnvpair.so.* %files -n libuutil3 %{_libdir}/libuutil.so.* %files -n libzfs5 %{_libdir}/libzfs*.so.* %files -n libzfs5-devel %{_pkgconfigdir}/libzfs.pc %{_pkgconfigdir}/libzfsbootenv.pc %{_pkgconfigdir}/libzfs_core.pc %{_libdir}/*.so %{_includedir}/* %doc AUTHORS COPYRIGHT LICENSE NOTICE README.md %files test %{_datadir}/%{name}/zfs-tests %{_datadir}/%{name}/test-runner %{_datadir}/%{name}/runfiles %{_datadir}/%{name}/*.sh %files dracut %doc contrib/dracut/README.dracut.markdown %{_dracutdir}/modules.d/* %if %{with pyzfs} %files -n python%{__python_pkg_version}-pyzfs %doc contrib/pyzfs/README %doc contrib/pyzfs/LICENSE %defattr(-,root,root,-) %{__python_sitelib}/libzfs_core/* %{__python_sitelib}/pyzfs* %endif %if 0%{?_initramfs} %files initramfs %doc contrib/initramfs/README.initramfs.markdown /usr/share/initramfs-tools/* %else # Since we're not building the initramfs package, # ignore those files. %exclude /usr/share/initramfs-tools %endif diff --git a/scripts/zimport.sh b/scripts/zimport.sh index 56dfbadae47b..6c3b415ffcd1 100755 --- a/scripts/zimport.sh +++ b/scripts/zimport.sh @@ -1,517 +1,517 @@ #!/usr/bin/env bash # # Verify that an assortment of known good reference pools can be imported -# using different versions of the ZoL code. +# using different versions of OpenZFS code. # # By default references pools for the major ZFS implementation will be -# checked against the most recent ZoL tags and the master development branch. +# checked against the most recent OpenZFS tags and the master development branch. # Alternate tags or branches may be verified with the '-s option. # Passing the keyword "installed" will instruct the script to test whatever # version is installed. # # Preferentially a reference pool is used for all tests. However, if one # does not exist and the pool-tag matches one of the src-tags then a new # reference pool will be created using binaries from that source build. # This is particularly useful when you need to test your changes before # opening a pull request. The keyword 'all' can be used as short hand # refer to all available reference pools. # # New reference pools may be added by placing a bzip2 compressed tarball # of the pool in the scripts/zfs-images directory and then passing # the -p option. To increase the test coverage reference pools # should be collected for all the major ZFS implementations. Having these # pools easily available is also helpful to the developers. # # Care should be taken to run these tests with a kernel supported by all # the listed tags. Otherwise build failure will cause false positives. # # # EXAMPLES: # # The following example will verify the zfs-0.6.2 tag, the master branch, # and the installed zfs version can correctly import the listed pools. # Note there is no reference pool available for master and installed but # because binaries are available one is automatically constructed. The # working directory is also preserved between runs (-k) preventing the # need to rebuild from source for multiple runs. # # zimport.sh -k -f /var/tmp/zimport \ # -s "zfs-0.6.2 master installed" \ # -p "zevo-1.1.1 zol-0.6.2 zol-0.6.2-173 master installed" # # ------------------------ OpenZFS Source Versions ---------------- # zfs-0.6.2 master 0.6.2-175_g36eb554 # ----------------------------------------------------------------- # Clone ZFS Local Local Skip # Build ZFS Pass Pass Skip # ----------------------------------------------------------------- # zevo-1.1.1 Pass Pass Pass # zol-0.6.2 Pass Pass Pass # zol-0.6.2-173 Fail Pass Pass # master Pass Pass Pass # installed Pass Pass Pass # BASE_DIR=$(dirname "$0") SCRIPT_COMMON=common.sh if [ -f "${BASE_DIR}/${SCRIPT_COMMON}" ]; then . "${BASE_DIR}/${SCRIPT_COMMON}" else echo "Missing helper script ${SCRIPT_COMMON}" && exit 1 fi PROG=zimport.sh SRC_TAGS="zfs-0.6.5.11 master" POOL_TAGS="all master" POOL_CREATE_OPTIONS= TEST_DIR=$(mktemp -u -d -p /var/tmp zimport.XXXXXXXX) KEEP="no" VERBOSE="no" COLOR="yes" REPO="https://github.com/openzfs" IMAGES_DIR="$SCRIPTDIR/zfs-images/" IMAGES_TAR="https://github.com/openzfs/zfs-images/tarball/master" ERROR=0 CONFIG_LOG="configure.log" CONFIG_OPTIONS=${CONFIG_OPTIONS:-""} MAKE_LOG="make.log" MAKE_OPTIONS=${MAKE_OPTIONS:-"-s -j$(nproc)"} COLOR_GREEN="\033[0;32m" COLOR_RED="\033[0;31m" COLOR_BROWN="\033[0;33m" COLOR_RESET="\033[0m" usage() { cat << EOF USAGE: zimport.sh [hvl] [-r repo] [-s src-tag] [-i pool-dir] [-p pool-tag] [-f path] [-o options] DESCRIPTION: ZPOOL import verification tests OPTIONS: -h Show this message -v Verbose -c No color -k Keep temporary directory -r Source repository ($REPO) - -s ... Verify ZoL versions with the listed tags + -s ... Verify OpenZFS versions with the listed tags -i Pool image directory -p ... Verify pools created with the listed tags -f Temporary directory to use -o Additional options to pass to 'zpool create' EOF } while getopts 'hvckr:s:i:p:f:o:?' OPTION; do case $OPTION in h) usage exit 1 ;; v) VERBOSE="yes" ;; c) COLOR="no" ;; k) KEEP="yes" ;; r) REPO="$OPTARG" ;; s) SRC_TAGS="$OPTARG" ;; i) IMAGES_DIR="$OPTARG" ;; p) POOL_TAGS="$OPTARG" ;; f) TEST_DIR="$OPTARG" ;; o) POOL_CREATE_OPTIONS="$OPTARG" ;; ?) usage exit 1 ;; esac done # # Verify the module start is not loaded # if lsmod | grep zfs >/dev/null; then echo "ZFS modules must be unloaded" exit 1 fi # # Create a random directory tree of files and sub-directories to # to act as a copy source for the various regression tests. # populate() { local ROOT=$1 local MAX_DIR_SIZE=$2 local MAX_FILE_SIZE=$3 # shellcheck disable=SC2086 mkdir -p $ROOT/{a,b,c,d,e,f,g}/{h,i} DIRS=$(find "$ROOT") for DIR in $DIRS; do COUNT=$((RANDOM % MAX_DIR_SIZE)) # shellcheck disable=SC2034 for i in $(seq $COUNT); do FILE=$(mktemp -p "$DIR") SIZE=$((RANDOM % MAX_FILE_SIZE)) dd if=/dev/urandom of="$FILE" bs=1k \ count="$SIZE" &>/dev/null done done return 0 } SRC_DIR=$(mktemp -d -p /var/tmp/ zfs.src.XXXXXXXX) trap 'rm -Rf "$SRC_DIR"' INT TERM EXIT populate "$SRC_DIR" 10 100 SRC_DIR="$TEST_DIR/src" SRC_DIR_ZFS="$SRC_DIR/zfs" if [ "$COLOR" = "no" ]; then COLOR_GREEN="" COLOR_BROWN="" COLOR_RED="" COLOR_RESET="" fi pass_nonewline() { echo -n -e "${COLOR_GREEN}Pass${COLOR_RESET}\t\t" } skip_nonewline() { echo -n -e "${COLOR_BROWN}Skip${COLOR_RESET}\t\t" } fail_nonewline() { echo -n -e "${COLOR_RED}Fail${COLOR_RESET}\t\t" } # # Log a failure message, cleanup, and return an error. # fail() { echo -e "$PROG: $1" >&2 $ZFS_SH -u >/dev/null 2>&1 exit 1 } # # Set several helper variables which are derived from a source tag. # # ZFS_TAG - The passed zfs-x.y.z tag # ZFS_DIR - The zfs directory name # ZFS_URL - The zfs github URL to fetch the tarball # src_set_vars() { local TAG=$1 ZFS_TAG="$TAG" ZFS_DIR="$SRC_DIR_ZFS/$ZFS_TAG" ZFS_URL="$REPO/zfs/tarball/$ZFS_TAG" if [ "$TAG" = "installed" ]; then ZPOOL_CMD=$(command -v zpool) ZFS_CMD=$(command -v zfs) ZFS_SH="/usr/share/zfs/zfs.sh" else ZPOOL_CMD="./cmd/zpool/zpool" ZFS_CMD="./cmd/zfs/zfs" ZFS_SH="./scripts/zfs.sh" fi } # # Set several helper variables which are derived from a pool name such # as zol-0.6.x, zevo-1.1.1, etc. These refer to example pools from various # ZFS implementations which are used to verify compatibility. # # POOL_TAG - The example pools name in scripts/zfs-images/. # POOL_BZIP - The full path to the example bzip2 compressed pool. # POOL_DIR - The top level test path for this pool. # POOL_DIR_PRISTINE - The directory containing a pristine version of the pool. # POOL_DIR_COPY - The directory containing a working copy of the pool. # POOL_DIR_SRC - Location of a source build if it exists for this pool. # pool_set_vars() { local TAG=$1 POOL_TAG=$TAG POOL_BZIP=$IMAGES_DIR/$POOL_TAG.tar.bz2 POOL_DIR=$TEST_DIR/pools/$POOL_TAG POOL_DIR_PRISTINE=$POOL_DIR/pristine POOL_DIR_COPY=$POOL_DIR/copy POOL_DIR_SRC="$SRC_DIR_ZFS/${POOL_TAG//zol/zfs}" } # # Construct a non-trivial pool given a specific version of the source. More # interesting pools provide better test coverage so this function should # extended as needed to create more realistic pools. # pool_create() { pool_set_vars "$1" src_set_vars "$1" if [ "$POOL_TAG" != "installed" ]; then cd "$POOL_DIR_SRC" || fail "Failed 'cd $POOL_DIR_SRC'" fi $ZFS_SH zfs="spa_config_path=$POOL_DIR_PRISTINE" || \ fail "Failed to load kmods" # Create a file vdev RAIDZ pool. truncate -s 1G \ "$POOL_DIR_PRISTINE/vdev1" "$POOL_DIR_PRISTINE/vdev2" \ "$POOL_DIR_PRISTINE/vdev3" "$POOL_DIR_PRISTINE/vdev4" || \ fail "Failed 'truncate -s 1G ...'" # shellcheck disable=SC2086 $ZPOOL_CMD create $POOL_CREATE_OPTIONS "$POOL_TAG" raidz \ "$POOL_DIR_PRISTINE/vdev1" "$POOL_DIR_PRISTINE/vdev2" \ "$POOL_DIR_PRISTINE/vdev3" "$POOL_DIR_PRISTINE/vdev4" || \ fail "Failed '$ZPOOL_CMD create $POOL_CREATE_OPTIONS $POOL_TAG ...'" # Create a pool/fs filesystem with some random contents. $ZFS_CMD create "$POOL_TAG/fs" || \ fail "Failed '$ZFS_CMD create $POOL_TAG/fs'" populate "/$POOL_TAG/fs/" 10 100 # Snapshot that filesystem, clone it, remove the files/dirs, # replace them with new files/dirs. $ZFS_CMD snap "$POOL_TAG/fs@snap" || \ fail "Failed '$ZFS_CMD snap $POOL_TAG/fs@snap'" $ZFS_CMD clone "$POOL_TAG/fs@snap" "$POOL_TAG/clone" || \ fail "Failed '$ZFS_CMD clone $POOL_TAG/fs@snap $POOL_TAG/clone'" # shellcheck disable=SC2086 rm -Rf /$POOL_TAG/clone/* populate "/$POOL_TAG/clone/" 10 100 # Scrub the pool, delay slightly, then export it. It is now # somewhat interesting for testing purposes. $ZPOOL_CMD scrub "$POOL_TAG" || \ fail "Failed '$ZPOOL_CMD scrub $POOL_TAG'" sleep 10 $ZPOOL_CMD export "$POOL_TAG" || \ fail "Failed '$ZPOOL_CMD export $POOL_TAG'" $ZFS_SH -u || fail "Failed to unload kmods" } # If the zfs-images directory doesn't exist fetch a copy from Github then # cache it in the $TEST_DIR and update $IMAGES_DIR. if [ ! -d "$IMAGES_DIR" ]; then IMAGES_DIR="$TEST_DIR/zfs-images" mkdir -p "$IMAGES_DIR" curl -sL "$IMAGES_TAR" | \ tar -xz -C "$IMAGES_DIR" --strip-components=1 || \ fail "Failed to download pool images" fi # Given the available images in the zfs-images directory substitute the # list of available images for the reserved keyword 'all'. for TAG in $POOL_TAGS; do if [ "$TAG" = "all" ]; then # shellcheck disable=SC2010 ALL_TAGS=$(ls "$IMAGES_DIR" | grep "tar.bz2" | \ sed 's/.tar.bz2//' | tr '\n' ' ') NEW_TAGS="$NEW_TAGS $ALL_TAGS" else NEW_TAGS="$NEW_TAGS $TAG" fi done POOL_TAGS="$NEW_TAGS" if [ "$VERBOSE" = "yes" ]; then echo "---------------------------- Options ----------------------------" echo "VERBOSE=$VERBOSE" echo "KEEP=$KEEP" echo "REPO=$REPO" echo "SRC_TAGS=$SRC_TAGS" echo "POOL_TAGS=$POOL_TAGS" echo "PATH=$TEST_DIR" echo "POOL_CREATE_OPTIONS=$POOL_CREATE_OPTIONS" echo fi if [ ! -d "$TEST_DIR" ]; then mkdir -p "$TEST_DIR" fi if [ ! -d "$SRC_DIR" ]; then mkdir -p "$SRC_DIR" fi # Print a header for all tags which are being tested. echo "------------------------ OpenZFS Source Versions ----------------" printf "%-16s" " " for TAG in $SRC_TAGS; do src_set_vars "$TAG" if [ "$TAG" = "installed" ]; then ZFS_VERSION=$(modinfo zfs | awk '/version:/ { print $2; exit }') if [ -n "$ZFS_VERSION" ]; then printf "%-16s" "$ZFS_VERSION" else fail "ZFS is not installed" fi else printf "%-16s" "$TAG" fi done echo -e "\n-----------------------------------------------------------------" # # Attempt to generate the tarball from your local git repository, if that # fails then attempt to download the tarball from Github. # printf "%-16s" "Clone ZFS" for TAG in $SRC_TAGS; do src_set_vars "$TAG" if [ -d "$ZFS_DIR" ]; then skip_nonewline elif [ "$ZFS_TAG" = "installed" ]; then skip_nonewline else cd "$SRC_DIR" || fail "Failed 'cd $SRC_DIR'" if [ ! -d "$SRC_DIR_ZFS" ]; then mkdir -p "$SRC_DIR_ZFS" fi git archive --format=tar --prefix="$ZFS_TAG/ $ZFS_TAG" \ -o "$SRC_DIR_ZFS/$ZFS_TAG.tar" &>/dev/null || \ rm "$SRC_DIR_ZFS/$ZFS_TAG.tar" if [ -s "$SRC_DIR_ZFS/$ZFS_TAG.tar" ]; then tar -xf "$SRC_DIR_ZFS/$ZFS_TAG.tar" -C "$SRC_DIR_ZFS" rm "$SRC_DIR_ZFS/$ZFS_TAG.tar" echo -n -e "${COLOR_GREEN}Local${COLOR_RESET}\t\t" else mkdir -p "$ZFS_DIR" || fail "Failed to create $ZFS_DIR" curl -sL "$ZFS_URL" | tar -xz -C "$ZFS_DIR" \ --strip-components=1 || \ fail "Failed to download $ZFS_URL" echo -n -e "${COLOR_GREEN}Remote${COLOR_RESET}\t\t" fi fi done printf "\n" # Build the listed tags printf "%-16s" "Build ZFS" for TAG in $SRC_TAGS; do src_set_vars "$TAG" if [ -f "$ZFS_DIR/module/zfs/zfs.ko" ]; then skip_nonewline elif [ "$ZFS_TAG" = "installed" ]; then skip_nonewline else cd "$ZFS_DIR" || fail "Failed 'cd $ZFS_DIR'" make distclean &>/dev/null ./autogen.sh >>"$CONFIG_LOG" 2>&1 || \ fail "Failed ZFS 'autogen.sh'" # shellcheck disable=SC2086 ./configure $CONFIG_OPTIONS >>"$CONFIG_LOG" 2>&1 || \ fail "Failed ZFS 'configure $CONFIG_OPTIONS'" # shellcheck disable=SC2086 make $MAKE_OPTIONS >>"$MAKE_LOG" 2>&1 || \ fail "Failed ZFS 'make $MAKE_OPTIONS'" pass_nonewline fi done printf "\n" echo "-----------------------------------------------------------------" # Either create a new pool using 'zpool create', or alternately restore an # existing pool from another ZFS implementation for compatibility testing. for TAG in $POOL_TAGS; do pool_set_vars "$TAG" SKIP=0 printf "%-16s" "$POOL_TAG" rm -Rf "$POOL_DIR" mkdir -p "$POOL_DIR_PRISTINE" # Use the existing compressed image if available. if [ -f "$POOL_BZIP" ]; then tar -xjf "$POOL_BZIP" -C "$POOL_DIR_PRISTINE" \ --strip-components=1 || \ fail "Failed 'tar -xjf $POOL_BZIP" # Use the installed version to create the pool. elif [ "$TAG" = "installed" ]; then pool_create "$TAG" # A source build is available to create the pool. elif [ -d "$POOL_DIR_SRC" ]; then pool_create "$TAG" else SKIP=1 fi # Verify 'zpool import' works for all listed source versions. for SRC_TAG in $SRC_TAGS; do if [ $SKIP -eq 1 ]; then skip_nonewline continue fi src_set_vars "$SRC_TAG" if [ "$SRC_TAG" != "installed" ]; then cd "$ZFS_DIR" || fail "Failed 'cd $ZFS_DIR'" fi $ZFS_SH zfs="spa_config_path=$POOL_DIR_COPY" cp -a --sparse=always "$POOL_DIR_PRISTINE" \ "$POOL_DIR_COPY" || \ fail "Failed to copy $POOL_DIR_PRISTINE to $POOL_DIR_COPY" POOL_NAME=$($ZPOOL_CMD import -d "$POOL_DIR_COPY" | \ awk '/pool:/ { print $2; exit 0 }') $ZPOOL_CMD import -N -d "$POOL_DIR_COPY" \ "$POOL_NAME" &>/dev/null # shellcheck disable=SC2181 if [ $? -ne 0 ]; then fail_nonewline ERROR=1 else $ZPOOL_CMD export "$POOL_NAME" || \ fail "Failed to export pool" pass_nonewline fi rm -Rf "$POOL_DIR_COPY" $ZFS_SH -u || fail "Failed to unload kmods" done printf "\n" done if [ "$KEEP" = "no" ]; then rm -Rf "$TEST_DIR" fi exit $ERROR diff --git a/tests/zfs-tests/tests/functional/atime/root_atime_off.ksh b/tests/zfs-tests/tests/functional/atime/root_atime_off.ksh index 2fbf06b13773..7eb2ed937287 100755 --- a/tests/zfs-tests/tests/functional/atime/root_atime_off.ksh +++ b/tests/zfs-tests/tests/functional/atime/root_atime_off.ksh @@ -1,74 +1,74 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2016 by Delphix. All rights reserved. # Copyright (c) 2019 by Tomohiro Kusumi. All rights reserved. # . $STF_SUITE/tests/functional/atime/atime_common.kshlib # # DESCRIPTION: # When atime=off, verify the access time for files is not updated when read. # It is available to pool, fs snapshot and clone. # # STRATEGY: # 1. Create pool, fs. # 2. Create '$TESTFILE' for fs. # 3. Create snapshot and clone. # 4. Setting atime=off on dataset and read '$TESTFILE'. # 5. Verify the access time is not updated. # verify_runnable "both" log_assert "Setting atime=off, the access time for files will not be updated \ when read." log_onexit cleanup # # Create $TESTFILE, snapshot and clone. -# Same as 002 except that atime applies to root dataset (ZoL#8675). +# Same as 002 except that atime applies to root dataset (OpenZFS#8675). # setup_snap_clone reset_atime for dst in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCLONE $TESTPOOL/$TESTFS@$TESTSNAP do typeset mtpt=$(get_prop mountpoint $dst) if [[ $dst == $TESTPOOL/$TESTFS@$TESTSNAP ]]; then mtpt=$(snapshot_mountpoint $dst) else log_must zfs set atime=off $(dirname $dst) fi log_mustnot check_atime_updated $mtpt/$TESTFILE done log_pass "Verify the property atime=off passed." diff --git a/tests/zfs-tests/tests/functional/atime/root_atime_on.ksh b/tests/zfs-tests/tests/functional/atime/root_atime_on.ksh index 3976523b0b13..44d471a2128f 100755 --- a/tests/zfs-tests/tests/functional/atime/root_atime_on.ksh +++ b/tests/zfs-tests/tests/functional/atime/root_atime_on.ksh @@ -1,78 +1,78 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2016 by Delphix. All rights reserved. # Copyright (c) 2019 by Tomohiro Kusumi. All rights reserved. # . $STF_SUITE/tests/functional/atime/atime_common.kshlib # # DESCRIPTION: # When atime=on, verify the access time for files is updated when read. It # is available to fs and clone. To snapshot, it is unavailable. # # STRATEGY: # 1. Create pool and fs. # 2. Create '$TESTFILE' for fs. # 3. Create snapshot and clone. # 4. Setting atime=on on datasets except snapshot, and read '$TESTFILE'. # 5. Expect the access time is updated on datasets except snapshot. # verify_runnable "both" log_assert "Setting atime=on, the access time for files is updated when read." log_onexit cleanup # # Create $TESTFILE, snapshot and clone. -# Same as 001 except that atime/relatime applies to root dataset (ZoL#8675). +# Same as 001 except that atime/relatime applies to root dataset (OpenZFS#8675). # setup_snap_clone reset_atime for dst in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCLONE $TESTPOOL/$TESTFS@$TESTSNAP do typeset mtpt=$(get_prop mountpoint $dst) if [[ $dst == $TESTPOOL/$TESTFS@$TESTSNAP ]]; then mtpt=$(snapshot_mountpoint $dst) log_mustnot check_atime_updated $mtpt/$TESTFILE else if is_linux; then log_must zfs set relatime=off $(dirname $dst) fi log_must zfs set atime=on $(dirname $dst) log_must check_atime_updated $mtpt/$TESTFILE log_must check_atime_updated $mtpt/$TESTFILE fi done log_pass "Verify the property atime=on passed." diff --git a/tests/zfs-tests/tests/functional/atime/root_relatime_on.ksh b/tests/zfs-tests/tests/functional/atime/root_relatime_on.ksh index c919e9f29883..120129425afa 100755 --- a/tests/zfs-tests/tests/functional/atime/root_relatime_on.ksh +++ b/tests/zfs-tests/tests/functional/atime/root_relatime_on.ksh @@ -1,76 +1,76 @@ #!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright (c) 2019 by Tomohiro Kusumi. All rights reserved. # . $STF_SUITE/tests/functional/atime/atime_common.kshlib # # DESCRIPTION: # When relatime=on, verify the access time for files is updated when first # read but not on second. # It is available to fs and clone. To snapshot, it is unavailable. # # STRATEGY: # 1. Create pool and fs. # 2. Create '$TESTFILE' for fs. # 3. Create snapshot and clone. # 4. Setting atime=on and relatime=on on datasets. # 5. Expect the access time is updated for first read but not on second. # verify_runnable "both" log_assert "Setting relatime=on, the access time for files is updated when \ when read the first time, but not second time." log_onexit cleanup # # Create $TESTFILE, snapshot and clone. -# Same as 003 except that atime/relatime applies to root dataset (ZoL#8675). +# Same as 003 except that atime/relatime applies to root dataset (OpenZFS#8675). # setup_snap_clone reset_atime for dst in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCLONE $TESTPOOL/$TESTFS@$TESTSNAP do typeset mtpt=$(get_prop mountpoint $dst) if [[ $dst == $TESTPOOL/$TESTFS@$TESTSNAP ]]; then mtpt=$(snapshot_mountpoint $dst) log_mustnot check_atime_updated $mtpt/$TESTFILE else log_must zfs set atime=on $(dirname $dst) log_must zfs set relatime=on $(dirname $dst) log_must check_atime_updated $mtpt/$TESTFILE log_mustnot check_atime_updated $mtpt/$TESTFILE fi done log_pass "Verify the property relatime=on passed." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_test_race.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_test_race.ksh index 135b31354f07..3a5793d0707d 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_test_race.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_test_race.ksh @@ -1,116 +1,117 @@ #!/bin/ksh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright (c) 2019 by Tomohiro Kusumi. All rights reserved. # . $STF_SUITE/include/libtest.shlib . $STF_SUITE/tests/functional/cli_root/zfs_mount/zfs_mount.cfg # # DESCRIPTION: # Verify parallel mount ordering is consistent. # # There was a bug in initial thread dispatching algorithm which put threads # under race condition which resulted in undefined mount order. The purpose # of this test is to verify `zfs unmount -a` succeeds (not `zfs mount -a` # succeeds, it always does) after `zfs mount -a`, which could fail if threads # race. See github.com/openzfs/zfs/issues/{8450,8833,8878} for details. # # STRATEGY: # 1. Create pools and filesystems. # 2. Set same mount point for >1 datasets. # 3. Unmount all datasets. # 4. Mount all datasets. # 5. Unmount all datasets (verify this succeeds). # verify_runnable "both" TMPDIR=${TMPDIR:-$TEST_BASE_DIR} MNTPT=$TMPDIR/zfs_mount_test_race_mntpt DISK1="$TMPDIR/zfs_mount_test_race_disk1" DISK2="$TMPDIR/zfs_mount_test_race_disk2" TESTPOOL1=zfs_mount_test_race_tp1 TESTPOOL2=zfs_mount_test_race_tp2 export __ZFS_POOL_RESTRICT="$TESTPOOL1 $TESTPOOL2" log_must zfs $unmountall unset __ZFS_POOL_RESTRICT function cleanup { zpool destroy $TESTPOOL1 zpool destroy $TESTPOOL2 rm -rf $MNTPT rm -rf /$TESTPOOL1 rm -rf /$TESTPOOL2 rm -f $DISK1 rm -f $DISK2 export __ZFS_POOL_RESTRICT="$TESTPOOL1 $TESTPOOL2" log_must zfs $mountall unset __ZFS_POOL_RESTRICT } log_onexit cleanup log_note "Verify parallel mount ordering is consistent" log_must truncate -s $MINVDEVSIZE $DISK1 log_must truncate -s $MINVDEVSIZE $DISK2 log_must zpool create -f $TESTPOOL1 $DISK1 log_must zpool create -f $TESTPOOL2 $DISK2 log_must zfs create $TESTPOOL1/$TESTFS1 log_must zfs create $TESTPOOL2/$TESTFS2 log_must zfs set mountpoint=none $TESTPOOL1 log_must zfs set mountpoint=$MNTPT $TESTPOOL1/$TESTFS1 # Note that unmount can fail (due to race condition on `zfs mount -a`) with or # without `canmount=off`. The race has nothing to do with canmount property, # but turn it off for convenience of mount layout used in this test case. log_must zfs set canmount=off $TESTPOOL2 log_must zfs set mountpoint=$MNTPT $TESTPOOL2 # At this point, layout of datasets in two pools will look like below. # Previously, on next `zfs mount -a`, pthreads assigned to TESTFS1 and TESTFS2 -# could race, and TESTFS2 usually (actually always) won in ZoL. Note that the -# problem is how two or more threads could initially be assigned to the same -# top level directory, not this specific layout. This layout is just an example -# that can reproduce race, and is also the layout reported in #8833. +# could race, and TESTFS2 usually (actually always) won in OpenZFS. +# Note that the problem is how two or more threads could initially be assigned +# to the same top level directory, not this specific layout. +# This layout is just an example that can reproduce race, +# and is also the layout reported in #8833. # # NAME MOUNTED MOUNTPOINT # ---------------------------------------------- # /$TESTPOOL1 no none # /$TESTPOOL1/$TESTFS1 yes $MNTPT # /$TESTPOOL2 no $MNTPT # /$TESTPOOL2/$TESTFS2 yes $MNTPT/$TESTFS2 # Apparently two datasets must be mounted. log_must ismounted $TESTPOOL1/$TESTFS1 log_must ismounted $TESTPOOL2/$TESTFS2 # This unmount always succeeds, because potential race hasn't happened yet. log_must zfs unmount -a # This mount always succeeds, whether threads are under race condition or not. log_must zfs mount -a # Verify datasets are mounted (TESTFS2 fails if the race broke mount order). log_must ismounted $TESTPOOL1/$TESTFS1 log_must ismounted $TESTPOOL2/$TESTFS2 # Verify unmount succeeds (fails if the race broke mount order). log_must zfs unmount -a log_pass "Verify parallel mount ordering is consistent passed" diff --git a/tests/zfs-tests/tests/functional/rsend/send_encrypted_files.ksh b/tests/zfs-tests/tests/functional/rsend/send_encrypted_files.ksh index f89cb3b31bee..1fd21cbf7eff 100755 --- a/tests/zfs-tests/tests/functional/rsend/send_encrypted_files.ksh +++ b/tests/zfs-tests/tests/functional/rsend/send_encrypted_files.ksh @@ -1,120 +1,120 @@ #!/bin/ksh -p # # CDDL HEADER START # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # CDDL HEADER END # # # Copyright (c) 2018 by Datto Inc. All rights reserved. # . $STF_SUITE/tests/functional/rsend/rsend.kshlib # # DESCRIPTION: # Verify that a raw zfs send and receive can deal with several different # types of file layouts. # # STRATEGY: # 1. Create a new encrypted filesystem # 2. Add an empty file to the filesystem # 3. Add a small 512 byte file to the filesystem # 4. Add a larger 32M file to the filesystem # 5. Add a large sparse file to the filesystem # 6. Add 1000 empty files to the filesystem # 7. Add a file with a large xattr value # 8. Use xattrtest to create files with random xattrs (with and without xattrs=on) # 9. Take a snapshot of the filesystem # 10. Remove the 1000 empty files to the filesystem # 11. Take another snapshot of the filesystem # 12. Send and receive both snapshots # 13. Mount the filesystem and check the contents # verify_runnable "both" function cleanup { datasetexists $TESTPOOL/$TESTFS2 && \ log_must zfs destroy -r $TESTPOOL/$TESTFS2 datasetexists $TESTPOOL/recv && \ log_must zfs destroy -r $TESTPOOL/recv [[ -f $keyfile ]] && log_must rm $keyfile [[ -f $sendfile ]] && log_must rm $sendfile } log_onexit cleanup log_assert "Verify 'zfs send -w' works with many different file layouts" typeset keyfile=/$TESTPOOL/pkey typeset sendfile=/$TESTPOOL/sendfile typeset sendfile2=/$TESTPOOL/sendfile2 # Create an encrypted dataset log_must eval "echo 'password' > $keyfile" log_must zfs create -o encryption=on -o keyformat=passphrase \ -o keylocation=file://$keyfile $TESTPOOL/$TESTFS2 # Create files with varied layouts on disk log_must touch /$TESTPOOL/$TESTFS2/empty log_must mkfile 512 /$TESTPOOL/$TESTFS2/small log_must mkfile 32M /$TESTPOOL/$TESTFS2/full log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS2/sparse \ bs=512 count=1 seek=1048576 >/dev/null 2>&1 log_must mkdir -p /$TESTPOOL/$TESTFS2/dir for i in {1..1000}; do log_must mkfile 512 /$TESTPOOL/$TESTFS2/dir/file-$i done log_must mkdir -p /$TESTPOOL/$TESTFS2/xattrondir log_must zfs set xattr=on $TESTPOOL/$TESTFS2 log_must xattrtest -f 10 -x 3 -s 32768 -r -k -p /$TESTPOOL/$TESTFS2/xattrondir log_must mkdir -p /$TESTPOOL/$TESTFS2/xattrsadir log_must zfs set xattr=sa $TESTPOOL/$TESTFS2 log_must xattrtest -f 10 -x 3 -s 32768 -r -k -p /$TESTPOOL/$TESTFS2/xattrsadir -# ZoL issue #7432 +# OpenZFS issue #7432 log_must zfs set compression=on xattr=sa $TESTPOOL/$TESTFS2 log_must touch /$TESTPOOL/$TESTFS2/attrs log_must eval "python -c 'print \"a\" * 4096' | \ set_xattr_stdin bigval /$TESTPOOL/$TESTFS2/attrs" log_must zfs set compression=off xattr=on $TESTPOOL/$TESTFS2 log_must zfs snapshot $TESTPOOL/$TESTFS2@snap1 # Remove the empty files created in the first snapshot for i in {1..1000}; do log_must rm /$TESTPOOL/$TESTFS2/dir/file-$i done sync log_must zfs snapshot $TESTPOOL/$TESTFS2@snap2 expected_cksum=$(recursive_cksum /$TESTPOOL/$TESTFS2) log_must eval "zfs send -wp $TESTPOOL/$TESTFS2@snap1 > $sendfile" log_must eval "zfs send -wp -i @snap1 $TESTPOOL/$TESTFS2@snap2 > $sendfile2" log_must eval "zfs recv -F $TESTPOOL/recv < $sendfile" log_must eval "zfs recv -F $TESTPOOL/recv < $sendfile2" log_must zfs load-key $TESTPOOL/recv log_must zfs mount -a actual_cksum=$(recursive_cksum /$TESTPOOL/recv) [[ "$expected_cksum" != "$actual_cksum" ]] && \ log_fail "Recursive checksums differ ($expected_cksum != $actual_cksum)" log_must xattrtest -f 10 -o3 -y -p /$TESTPOOL/recv/xattrondir log_must xattrtest -f 10 -o3 -y -p /$TESTPOOL/recv/xattrsadir log_pass "Verified 'zfs send -w' works with many different file layouts"