From 8a3748f531c8740f607c4699474b610e9fd67f6c Mon Sep 17 00:00:00 2001 From: hrs Date: Thu, 27 Jun 2013 18:28:45 +0000 Subject: - Add vnode-backed swap space specification support. This is enabled when device names "md" or "md[0-9]*" and a "file" option are specified in /etc/fstab like this: md none swap sw,file=/swap.bin 0 0 - Add GBDE/GELI encrypted swap space specification support, which rc.d/encswap supported. The /etc/fstab lines are like the following: /dev/ada1p1.bde none swap sw 0 0 /dev/ada1p2.eli none swap sw 0 0 .eli devices accepts aalgo, ealgo, keylen, and sectorsize as options. swapctl(8) can understand an encrypted device in the command line like this: # swapctl -a /dev/ada2p1.bde - "-L" flag is added to support "late" option to defer swapon until rc.d/mountlate runs. - rc.d script change: rc.d/encswap -> removed rc.d/addswap -> just display a warning message if $swapfile is defined rc.d/swap1 -> renamed to rc.d/swap rc.d/swaplate -> newly added to support "late" option These changes alleviate a race condition between device creation/removal and swapon/swapoff. MFC after: 1 week Reviewed by: wblock (manual page) --- sbin/swapon/swapon.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 543 insertions(+), 24 deletions(-) (limited to 'sbin/swapon/swapon.c') diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c index 5b9a0ed..55beb5b 100644 --- a/sbin/swapon/swapon.c +++ b/sbin/swapon/swapon.c @@ -41,35 +41,51 @@ static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93"; #include __FBSDID("$FreeBSD$"); -#include #include +#include +#include +#include #include +#include #include #include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include static void usage(void); -static int swap_on_off(char *name, int ignoreebusy); +static const char *swap_on_off(char *, int, char *); +static const char *swap_on_off_gbde(char *, int); +static const char *swap_on_off_geli(char *, char *, int); +static const char *swap_on_off_md(char *, char *, int); +static const char *swap_on_off_sfile(char *, int); static void swaplist(int, int, int); +static int run_cmd(int *, const char *, ...) __printflike(2, 3); static enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL; +static int qflag; + int main(int argc, char **argv) { struct fstab *fsp; + const char *swfile; char *ptr; int ret; int ch, doall; - int sflag = 0, lflag = 0, hflag = 0, qflag = 0; + int sflag = 0, lflag = 0, late = 0, hflag = 0; const char *etc_fstab; if ((ptr = strrchr(argv[0], '/')) == NULL) @@ -82,7 +98,7 @@ main(int argc, char **argv) doall = 0; etc_fstab = NULL; - while ((ch = getopt(argc, argv, "AadghklmqsUF:")) != -1) { + while ((ch = getopt(argc, argv, "AadghklLmqsUF:")) != -1) { switch(ch) { case 'A': if (which_prog == SWAPCTL) { @@ -116,6 +132,9 @@ main(int argc, char **argv) case 'l': lflag = 1; break; + case 'L': + late = 1; + break; case 'm': hflag = 'M'; break; @@ -145,6 +164,7 @@ main(int argc, char **argv) argv += optind; ret = 0; + swfile = NULL; if (etc_fstab != NULL) setfstab(etc_fstab); if (which_prog == SWAPON || which_prog == SWAPOFF) { @@ -154,27 +174,37 @@ main(int argc, char **argv) continue; if (strstr(fsp->fs_mntops, "noauto")) continue; - if (swap_on_off(fsp->fs_spec, 1)) { + if (which_prog != SWAPOFF && + strstr(fsp->fs_mntops, "late") && + !late) + continue; + swfile = swap_on_off(fsp->fs_spec, 1, + fsp->fs_mntops); + if (swfile == NULL) { ret = 1; - } else { - if (!qflag) { - printf("%s: %sing %s as swap device\n", - getprogname(), - which_prog == SWAPOFF ? "remov" : "add", - fsp->fs_spec); - } + continue; + } + if (!qflag) { + printf("%s: %sing %s as swap device\n", + getprogname(), + (which_prog == SWAPOFF) ? + "remov" : "add", swfile); } } } else if (!*argv) usage(); for (; *argv; ++argv) { - if (swap_on_off(*argv, 0)) { + swfile = swap_on_off(*argv, 0, NULL); + if (swfile == NULL) { ret = 1; - } else if (orig_prog == SWAPCTL) { + continue; + } + if (orig_prog == SWAPCTL) { printf("%s: %sing %s as swap device\n", - getprogname(), which_prog == SWAPOFF ? "remov" : "add", - *argv); + getprogname(), + (which_prog == SWAPOFF) ? "remov" : "add", + swfile); } } } else { @@ -186,14 +216,503 @@ main(int argc, char **argv) exit(ret); } +static const char * +swap_on_off(char *name, int doingall, char *mntops) +{ + char base[PATH_MAX]; + + /* Swap on vnode-backed md(4) device. */ + if (mntops != NULL && + (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) != FNM_NOMATCH || + fnmatch(MD_NAME "[0-9]*", name, 0) != FNM_NOMATCH || + strncmp(_PATH_DEV MD_NAME, name, + sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 || + strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0)) + return (swap_on_off_md(name, mntops, doingall)); + + /* Swap on encrypted device by GEOM_BDE. */ + basename_r(name, base); + if (fnmatch("*.bde", base, 0) != FNM_NOMATCH) + return (swap_on_off_gbde(name, doingall)); + + /* Swap on encrypted device by GEOM_ELI. */ + if (fnmatch("*.eli", base, 0) != FNM_NOMATCH) + return (swap_on_off_geli(name, mntops, doingall)); + + /* Swap on special file. */ + return (swap_on_off_sfile(name, doingall)); +} + +static const char * +swap_on_off_gbde(char *name, int doingall) +{ + const char *ret; + char pass[64 * 2 + 1], bpass[64]; + char *devname, *p; + int i, fd, error; + + devname = strdup(name); + p = strrchr(devname, '.'); + if (p == NULL) { + warnx("%s: Malformed device name", name); + return (NULL); + } + *p = '\0'; + + fd = -1; + switch (which_prog) { + case SWAPON: + arc4random_buf(bpass, sizeof(bpass)); + for (i = 0; i < (int)sizeof(bpass); i++) + sprintf(&pass[2 * i], "%02x", bpass[i]); + pass[sizeof(pass) - 1] = '\0'; + + error = run_cmd(&fd, "%s init %s -P %s", _PATH_GBDE, + devname, pass); + if (error) { + /* bde device found. Ignore it. */ + close(fd); + if (!qflag) + warnx("%s: Device already in use", name); + return (NULL); + } + close(fd); + error = run_cmd(&fd, "%s attach %s -p %s", _PATH_GBDE, + devname, pass); + if (error) { + close(fd); + warnx("gbde (attach) error: %s", name); + return (NULL); + } + break; + case SWAPOFF: + break; + default: + return (NULL); + break; + } + if (fd != -1) + close(fd); + ret = swap_on_off_sfile(name, doingall); + + fd = -1; + switch (which_prog) { + case SWAPOFF: + error = run_cmd(&fd, "%s detach %s", _PATH_GBDE, devname); + if (error) { + /* bde device not found. Ignore it. */ + if (!qflag) + warnx("%s: Device not found", devname); + return (NULL); + } + break; + default: + return (NULL); + break; + } + + if (fd != -1) + close(fd); + return (ret); +} + +static const char * +swap_on_off_geli(char *name, char *mntops, int doingall) +{ + const char *ops, *aalgo, *ealgo, *keylen_str, *sectorsize_str; + char *devname, *p; + char args[4096]; + struct stat sb; + int fd, error, keylen, sectorsize; + u_long ul; + + devname = strdup(name); + p = strrchr(devname, '.'); + if (p == NULL) { + warnx("%s: Malformed device name", name); + return (NULL); + } + *p = '\0'; + + ops = strdup(mntops); + + /* Default parameters for geli(8). */ + aalgo = "hmac/sha256"; + ealgo = "aes"; + keylen = 256; + sectorsize = 4096; + + if ((p = strstr(ops, "aalgo=")) != NULL) { + aalgo = p + sizeof("aalgo=") - 1; + p = strchr(aalgo, ','); + if (p != NULL) + *p = '\0'; + } + if ((p = strstr(ops, "ealgo=")) != NULL) { + ealgo = p + sizeof("ealgo=") - 1; + p = strchr(ealgo, ','); + if (p != NULL) + *p = '\0'; + } + if ((p = strstr(ops, "keylen=")) != NULL) { + keylen_str = p + sizeof("keylen=") - 1; + p = strchr(keylen_str, ','); + if (p != NULL) + *p = '\0'; + errno = 0; + ul = strtoul(keylen_str, &p, 10); + if (errno == 0) { + if (*p != '\0' || ul > INT_MAX) + errno = EINVAL; + } + if (errno) { + warn("Invalid keylen: %s", keylen_str); + return (NULL); + } + keylen = (int)ul; + } + if ((p = strstr(ops, "sectorsize=")) != NULL) { + sectorsize_str = p + sizeof("sectorsize=") - 1; + p = strchr(sectorsize_str, ','); + if (p != NULL) + *p = '\0'; + errno = 0; + ul = strtoul(sectorsize_str, &p, 10); + if (errno == 0) { + if (*p != '\0' || ul > INT_MAX) + errno = EINVAL; + } + if (errno) { + warn("Invalid sectorsize: %s", sectorsize_str); + return (NULL); + } + sectorsize = (int)ul; + } + snprintf(args, sizeof(args), "-a %s -e %s -l %d -s %d -d", + aalgo, ealgo, keylen, sectorsize); + args[sizeof(args) - 1] = '\0'; + free((void *)ops); + + fd = -1; + switch (which_prog) { + case SWAPON: + error = run_cmd(&fd, "%s onetime %s %s", _PATH_GELI, args, + devname); + if (error) { + /* eli device found. Ignore it. */ + close(fd); + if (!qflag) + warnx("%s: Device already in use " + "or invalid parameters", name); + return (NULL); + } + break; + case SWAPOFF: + if (stat(name, &sb) == -1 && errno == ENOENT) { + if (!qflag) + warnx("%s: Device not found", name); + return (NULL); + } + break; + default: + return (NULL); + break; + } + if (fd != -1) + close(fd); + + return (swap_on_off_sfile(name, doingall)); +} + +static const char * +swap_on_off_md(char *name, char *mntops, int doingall) +{ + FILE *sfd; + int fd, mdunit, error; + const char *ret; + char mdpath[PATH_MAX], linebuf[PATH_MAX]; + char *p, *vnodefile; + size_t linelen; + u_long ul; + + fd = -1; + sfd = NULL; + if (strlen(name) == (sizeof(MD_NAME) - 1)) + mdunit = -1; + else { + errno = 0; + ul = strtoul(name + 2, &p, 10); + if (errno == 0) { + if (*p != '\0' || ul > INT_MAX) + errno = EINVAL; + } + if (errno) { + warn("Bad device unit: %s", name); + return (NULL); + } + mdunit = (int)ul; + } + + vnodefile = NULL; + if ((p = strstr(mntops, "file=")) != NULL) { + vnodefile = strdup(p + sizeof("file=") - 1); + p = strchr(vnodefile, ','); + if (p != NULL) + *p = '\0'; + } + if (vnodefile == NULL) { + warnx("file option not found for %s", name); + return (NULL); + } + + switch (which_prog) { + case SWAPON: + if (mdunit == -1) { + error = run_cmd(&fd, "%s -l -n -f %s", + _PATH_MDCONFIG, vnodefile); + if (error == 0) { + /* md device found. Ignore it. */ + close(fd); + if (!qflag) + warnx("%s: Device already in use", + vnodefile); + return (NULL); + } + error = run_cmd(&fd, "%s -a -t vnode -n -f %s", + _PATH_MDCONFIG, vnodefile); + if (error) { + warnx("mdconfig (attach) error: file=%s", + vnodefile); + return (NULL); + } + sfd = fdopen(fd, "r"); + if (sfd == NULL) { + warn("mdconfig (attach) fdopen error"); + ret = NULL; + goto err; + } + p = fgetln(sfd, &linelen); + if (p == NULL && + (linelen < 2 || linelen > sizeof(linebuf))) { + warn("mdconfig (attach) unexpected output"); + ret = NULL; + goto err; + } + strncpy(linebuf, p, linelen); + linebuf[linelen - 1] = '\0'; + errno = 0; + ul = strtoul(linebuf, &p, 10); + if (errno == 0) { + if (*p != '\0' || ul > INT_MAX) + errno = EINVAL; + } + if (errno) { + warn("mdconfig (attach) unexpected output: %s", + linebuf); + ret = NULL; + goto err; + } + mdunit = (int)ul; + } else { + error = run_cmd(&fd, "%s -l -n -f %s -u %d", + _PATH_MDCONFIG, vnodefile, mdunit); + if (error == 0) { + /* md device found. Ignore it. */ + close(fd); + if (!qflag) + warnx("md%d on %s: Device already " + "in use", mdunit, vnodefile); + return (NULL); + } + error = run_cmd(NULL, "%s -a -t vnode -u %d -f %s", + _PATH_MDCONFIG, mdunit, vnodefile); + if (error) { + warnx("mdconfig (attach) error: " + "md%d on file=%s", mdunit, vnodefile); + return (NULL); + } + } + break; + case SWAPOFF: + if (mdunit == -1) { + error = run_cmd(&fd, "%s -l -n -f %s", + _PATH_MDCONFIG, vnodefile); + if (error) { + /* md device not found. Ignore it. */ + close(fd); + if (!qflag) + warnx("md on %s: Device not found", + vnodefile); + return (NULL); + } + sfd = fdopen(fd, "r"); + if (sfd == NULL) { + warn("mdconfig (list) fdopen error"); + ret = NULL; + goto err; + } + p = fgetln(sfd, &linelen); + if (p == NULL && + (linelen < 2 || linelen > sizeof(linebuf) - 1)) { + warn("mdconfig (list) unexpected output"); + ret = NULL; + goto err; + } + strncpy(linebuf, p, linelen); + linebuf[linelen - 1] = '\0'; + p = strchr(linebuf, ' '); + if (p != NULL) + *p = '\0'; + errno = 0; + ul = strtoul(linebuf, &p, 10); + if (errno == 0) { + if (*p != '\0' || ul > INT_MAX) + errno = EINVAL; + } + if (errno) { + warn("mdconfig (list) unexpected output: %s", + linebuf); + ret = NULL; + goto err; + } + mdunit = (int)ul; + } else { + error = run_cmd(&fd, "%s -l -n -f %s -u %d", + _PATH_MDCONFIG, vnodefile, mdunit); + if (error) { + /* md device not found. Ignore it. */ + close(fd); + if (!qflag) + warnx("md%d on %s: Device not found", + mdunit, vnodefile); + return (NULL); + } + } + break; + default: + return (NULL); + } + snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV, + MD_NAME, mdunit); + mdpath[sizeof(mdpath) - 1] = '\0'; + ret = swap_on_off_sfile(mdpath, doingall); + + switch (which_prog) { + case SWAPOFF: + if (ret != NULL) { + error = run_cmd(NULL, "%s -d -u %d", + _PATH_MDCONFIG, mdunit); + if (error) + warn("mdconfig (detach) detach failed: %s%s%d", + _PATH_DEV, MD_NAME, mdunit); + } + break; + default: + break; + } +err: + if (sfd != NULL) + fclose(sfd); + if (fd != -1) + close(fd); + return (ret); +} + static int -swap_on_off(char *name, int doingall) +run_cmd(int *ofd, const char *cmdline, ...) { - if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) { + va_list ap; + char **argv, **argvp, *cmd, *p; + int argc, pid, status, rv; + int pfd[2], nfd, dup2dn; + + va_start(ap, cmdline); + rv = vasprintf(&cmd, cmdline, ap); + if (rv == -1) { + warn("%s", __func__); + return (rv); + } + va_end(ap); + + for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) + argc++; + argv = (char **)malloc(sizeof(*argv) * (argc + 1)); + for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) + if (**argvp != '\0' && (++argvp > &argv[argc])) { + *argvp = NULL; + break; + } + /* The argv array ends up NULL-terminated here. */ +#if 0 + { + int i; + + fprintf(stderr, "DEBUG: running:"); + /* Should be equivalent to 'cmd' (before strsep, of course). */ + for (i = 0; argv[i] != NULL; i++) + fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } +#endif + dup2dn = 1; + if (ofd != NULL) { + if (pipe(&pfd[0]) == -1) { + warn("%s: pipe", __func__); + return (-1); + } + *ofd = pfd[0]; + dup2dn = 0; + } + pid = fork(); + switch (pid) { + case 0: + /* Child process. */ + if (ofd != NULL) + if (dup2(pfd[1], STDOUT_FILENO) < 0) + err(1, "dup2 in %s", __func__); + nfd = open(_PATH_DEVNULL, O_RDWR); + if (nfd == -1) + err(1, "%s: open %s", __func__, _PATH_DEVNULL); + if (dup2(nfd, STDIN_FILENO) < 0) + err(1, "%s: dup2", __func__); + if (dup2dn && dup2(nfd, STDOUT_FILENO) < 0) + err(1, "%s: dup2", __func__); + if (dup2(nfd, STDERR_FILENO) < 0) + err(1, "%s: dup2", __func__); + execv(argv[0], argv); + warn("exec: %s", argv[0]); + _exit(-1); + case -1: + err(1, "%s: fork", __func__); + } + free(cmd); + free(argv); + while (waitpid(pid, &status, 0) != pid) + ; + return (WEXITSTATUS(status)); +} + +static const char * +swap_on_off_sfile(char *name, int doingall) +{ + int error; + + switch (which_prog) { + case SWAPON: + error = swapon(name); + break; + case SWAPOFF: + error = swapoff(name); + break; + default: + error = 0; + break; + } + if (error == -1) { switch (errno) { case EBUSY: if (!doingall) - warnx("%s: device already in use", name); + warnx("%s: Device already in use", name); break; case EINVAL: if (which_prog == SWAPON) @@ -205,9 +724,9 @@ swap_on_off(char *name, int doingall) warn("%s", name); break; } - return(1); + return (NULL); } - return(0); + return (name); } static void @@ -217,7 +736,7 @@ usage(void) switch(orig_prog) { case SWAPON: case SWAPOFF: - fprintf(stderr, "[-F fstab] -aq | file ...\n"); + fprintf(stderr, "[-F fstab] -aLq | file ...\n"); break; case SWAPCTL: fprintf(stderr, "[-AghklmsU] [-a file ... | -d file ...]\n"); -- cgit v1.1