diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 4e014025f623..f29fad1395f8 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1,1229 +1,1231 @@ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 tags=package=tests . bin cat .. chflags .. chmod .. cp .. date .. dd .. echo .. expr .. hostname .. ln .. ls .. mkdir .. mv .. pax .. pkill .. pwait .. rm .. rmdir .. sh builtins .. errors .. execution .. expansion .. invocation .. parameters .. parser .. set-e .. .. sleep .. test .. timeout .. .. cddl lib .. sbin .. usr.bin ctfconvert .. ztest .. .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. env .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. kinst .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. oformat .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. sugar .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. i386 arrays .. funcs .. pid .. ustack .. .. amd64 arrays .. .. .. zfsd .. .. .. etc rc.d .. .. examples .. games .. gnu lib .. usr.bin diff .. .. .. include .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. csu dynamic .. dynamiclib .. static .. .. googletest gmock .. gmock_main .. gtest .. gtest_main .. .. libarchive .. libbe .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. stdtime .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcam .. libcasper services cap_dns .. cap_grp .. cap_pwd .. cap_sysctl .. .. .. libcrypt .. libdevdctl .. libexecinfo .. libkvm .. libmp .. libnv .. libproc .. libregex data .. .. librt .. libsbuf .. libsysdecode .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-pytest-wrapper .. atf-sh .. .. nuageinit .. rc .. rtld-elf rtld_deepbind .. .. tftpd .. .. sbin bectl .. dhclient .. devd .. growfs .. ifconfig .. ipfw .. md5 .. mdconfig .. newfs_msdos .. nvmecontrol .. pfctl files .. .. ping .. route .. savecore .. + swapon + .. sysctl .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. googletest .. plain .. tap .. .. .. zoneinfo .. .. sys acl .. aio .. audit .. auditpipe .. cam ctl .. .. capsicum .. cddl zfs bin .. include .. tests acl cifs .. nontrivial .. trivial .. .. atime .. bootfs .. cache .. cachefile .. clean_mirror .. cli_root zfs_upgrade .. zfs_promote .. zfs_clone .. zfs_property .. zfs_destroy .. zpool_create .. zpool_history .. zpool_expand .. zpool_remove .. zfs_mount .. zfs_unshare .. zdb .. zpool_online .. zpool_get .. zpool_export .. zfs_copies .. zfs_get .. zfs .. zpool_clear .. zpool_import blockfiles .. .. zpool .. zpool_offline .. zpool_replace .. zfs_rollback .. zpool_set .. zfs_send .. zfs_set .. zpool_detach .. zfs_diff .. zpool_scrub .. zfs_inherit .. zfs_snapshot .. zfs_share .. zpool_destroy .. zpool_status .. zfs_unmount .. zfs_receive .. zfs_create .. zpool_upgrade blockfiles .. .. zpool_add .. zfs_rename .. zpool_attach .. zfs_reservation .. .. cli_user misc .. zfs_list .. zpool_iostat .. zpool_list .. .. compression .. ctime .. delegate .. devices .. exec .. grow_pool .. grow_replicas .. history .. hotplug .. hotspare .. inheritance .. interop .. inuse .. iscsi .. large_files .. largest_pool .. link_count .. migration .. mmap .. mount .. mv_files .. nestedfs .. no_space .. online_offline .. pool_names .. poolversion .. quota .. redundancy .. refquota .. refreserv .. rename_dirs .. replacement .. reservation .. rootpool .. rsend .. scrub_mirror .. slog .. snapshot .. snapused .. sparse .. threadsappend .. truncate .. txg_integrity .. userquota .. utils_test .. write_dirs .. xattr .. zfsd .. zil .. zinject .. zones .. zvol zvol_ENOSPC .. zvol_cli .. zvol_misc .. zvol_swap .. .. zvol_thrash .. .. .. .. compat32 .. devrandom .. dtrace .. fifo .. file .. fs fusefs .. tarfs .. tmpfs .. .. geom class concat .. eli .. gate .. gpt .. mirror .. multipath .. nop .. part .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue libkqueue .. .. mac bsdextended .. ipacl .. portacl .. .. mqueue .. net if_ovpn .. routing .. .. netgraph .. netinet .. netinet6 frag6 .. .. netipsec tunnel .. .. netlink .. netmap .. netpfil common .. ipfw .. pf ioctl .. .. .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. utimensat .. .. posixshm .. sound .. sys .. vfs .. vm stack .. .. vmm .. .. usr.bin apply .. asa .. awk bugs-fixed .. netbsd .. .. basename .. bintrans .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. column .. compress .. cpio .. col .. comm .. csplit .. cut .. dc .. diff .. diff3 .. dirname .. du .. env .. factor .. file2c .. file .. find .. fold .. getconf .. gh-bc .. grep .. gzip .. head .. hexdump .. ident .. indent .. join .. jot .. lastcomm .. limits .. locale .. lockf .. lorder .. m4 .. mail .. mkimg .. mktemp .. ncal .. opensm .. patch .. pr .. printf .. procstat .. renice .. rs .. sdiff .. sed regress.multitest.out .. .. seq .. soelim .. sort .. split .. stat .. tail .. tar .. tee .. tftp .. touch .. tr .. truncate .. tsort .. units .. unifdef .. uniq .. unzip .. vmstat .. wc .. xargs .. xinstall .. xo .. yacc yacc .. .. .. usr.sbin chown .. ctladm .. daemon .. etcupdate .. extattr .. fstyp .. jail .. makefs .. mixer .. newsyslog .. nmtree .. praudit .. pw .. rpcbind .. sa .. syslogd .. sysrc .. traceroute .. .. .. # vim: set expandtab ts=4 sw=4: diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile index 4cca18367c1e..7696fc51b463 100644 --- a/sbin/swapon/Makefile +++ b/sbin/swapon/Makefile @@ -1,13 +1,18 @@ # @(#)Makefile 8.1 (Berkeley) 6/5/93 PACKAGE=runtime PROG= swapon MAN= swapon.8 LINKS= ${BINDIR}/swapon ${BINDIR}/swapoff LINKS+= ${BINDIR}/swapon ${BINDIR}/swapctl MLINKS= swapon.8 swapoff.8 MLINKS+=swapon.8 swapctl.8 LIBADD= util +.include + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + .include diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c index b9b865c0fc09..f7ec26decb1d 100644 --- a/sbin/swapon/swapon.c +++ b/sbin/swapon/swapon.c @@ -1,935 +1,965 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define DOT_ELI ".eli" + static void usage(void) __dead2; static const char *swap_on_off(const char *, int, char *); static const char *swap_on_off_gbde(const char *, int); static const char *swap_on_off_geli(const char *, char *, int); static const char *swap_on_off_md(const char *, char *, int); static const char *swap_on_off_sfile(const 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 Eflag, fflag, qflag; int main(int argc, char **argv) { struct fstab *fsp; const char *swfile; char *ptr; int ret, ch, doall; int sflag, lflag, late, hflag; const char *etc_fstab; sflag = lflag = late = hflag = 0; if ((ptr = strrchr(argv[0], '/')) == NULL) ptr = argv[0]; if (strstr(ptr, "swapon") != NULL) which_prog = SWAPON; else if (strstr(ptr, "swapoff") != NULL) which_prog = SWAPOFF; orig_prog = which_prog; doall = 0; etc_fstab = NULL; while ((ch = getopt(argc, argv, "AadEfghklLmqsUF:")) != -1) { switch(ch) { case 'A': if (which_prog == SWAPCTL) { doall = 1; which_prog = SWAPON; } else usage(); break; case 'a': if (which_prog == SWAPON || which_prog == SWAPOFF) doall = 1; else which_prog = SWAPON; break; case 'd': if (which_prog == SWAPCTL) which_prog = SWAPOFF; else usage(); break; case 'E': if (which_prog == SWAPON) Eflag = 2; else usage(); break; case 'f': if (which_prog == SWAPOFF) fflag = 1; else usage(); break; case 'g': hflag = 'G'; break; case 'h': hflag = 'H'; break; case 'k': hflag = 'K'; break; case 'l': lflag = 1; break; case 'L': late = 1; break; case 'm': hflag = 'M'; break; case 'q': if (which_prog == SWAPON || which_prog == SWAPOFF) qflag = 1; break; case 's': sflag = 1; break; case 'U': if (which_prog == SWAPCTL) { doall = 1; which_prog = SWAPOFF; } else usage(); break; case 'F': etc_fstab = optarg; break; case '?': default: usage(); } } argv += optind; ret = 0; swfile = NULL; if (etc_fstab != NULL) setfstab(etc_fstab); if (which_prog == SWAPON || which_prog == SWAPOFF) { if (doall) { while ((fsp = getfsent()) != NULL) { if (strcmp(fsp->fs_type, FSTAB_SW) != 0) continue; if (strstr(fsp->fs_mntops, "noauto") != NULL) continue; if (which_prog != SWAPOFF && strstr(fsp->fs_mntops, "late") && late == 0) continue; if (which_prog == SWAPOFF && strstr(fsp->fs_mntops, "late") == NULL && late != 0) continue; Eflag |= (strstr(fsp->fs_mntops, "trimonce") != NULL); swfile = swap_on_off(fsp->fs_spec, 1, fsp->fs_mntops); Eflag &= ~1; if (swfile == NULL) { ret = 1; continue; } if (qflag == 0) { printf("%s: %sing %s as swap device\n", getprogname(), (which_prog == SWAPOFF) ? "remov" : "add", swfile); } } } else if (*argv == NULL) usage(); for (; *argv; ++argv) { swfile = swap_on_off(*argv, 0, NULL); if (swfile == NULL) { ret = 1; continue; } if (orig_prog == SWAPCTL) { printf("%s: %sing %s as swap device\n", getprogname(), (which_prog == SWAPOFF) ? "remov" : "add", swfile); } } } else { if (lflag || sflag) swaplist(lflag, sflag, hflag); else usage(); } exit(ret); } static const char * swap_on_off(const char *name, int doingall, char *mntops) { char *base, *basebuf; /* Swap on vnode-backed md(4) device. */ if (mntops != NULL && (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) == 0 || fnmatch(MD_NAME "[0-9]*", name, 0) == 0 || strncmp(_PATH_DEV MD_NAME, name, - sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 || - strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0)) + sizeof(_PATH_DEV MD_NAME)) == 0 || + strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0 || + strncmp(_PATH_DEV MD_NAME DOT_ELI, name, + sizeof(_PATH_DEV MD_NAME DOT_ELI)) == 0 || + strncmp(MD_NAME DOT_ELI, name, sizeof(MD_NAME DOT_ELI)) == 0)) return (swap_on_off_md(name, mntops, doingall)); basebuf = strdup(name); base = basename(basebuf); /* Swap on encrypted device by GEOM_BDE. */ if (fnmatch("*.bde", base, 0) == 0) { free(basebuf); return (swap_on_off_gbde(name, doingall)); } /* Swap on encrypted device by GEOM_ELI. */ - if (fnmatch("*.eli", base, 0) == 0) { + if (fnmatch("*" DOT_ELI, base, 0) == 0) { free(basebuf); return (swap_on_off_geli(name, mntops, doingall)); } /* Swap on special file. */ free(basebuf); return (swap_on_off_sfile(name, doingall)); } /* Strip off .bde or .eli suffix from swap device name */ static char * swap_basename(const char *name) { char *dname, *p; dname = strdup(name); p = strrchr(dname, '.'); /* assert(p != NULL); */ *p = '\0'; return (dname); } static const char * swap_on_off_gbde(const char *name, int doingall) { const char *ret; char pass[64 * 2 + 1]; unsigned char bpass[64]; char *dname; int i, error; dname = swap_basename(name); if (dname == NULL) return (NULL); if (which_prog == 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(NULL, "%s init %s -P %s", _PATH_GBDE, dname, pass); if (error) { /* bde device found. Ignore it. */ free(dname); if (qflag == 0) warnx("%s: Device already in use", name); return (NULL); } error = run_cmd(NULL, "%s attach %s -p %s", _PATH_GBDE, dname, pass); free(dname); if (error) { warnx("gbde (attach) error: %s", name); return (NULL); } } ret = swap_on_off_sfile(name, doingall); if (which_prog == SWAPOFF) { error = run_cmd(NULL, "%s detach %s", _PATH_GBDE, dname); free(dname); if (error) { /* bde device not found. Ignore it. */ if (qflag == 0) warnx("%s: Device not found", name); return (NULL); } } return (ret); } /* Build geli(8) arguments from mntops */ static char * swap_on_geli_args(const char *mntops) { const char *aalgo, *ealgo, *keylen_str, *sectorsize_str; const char *aflag, *eflag, *lflag, *Tflag, *sflag; char *p, *args, *token, *string, *ops; int pagesize; size_t pagesize_len; u_long ul; /* Use built-in defaults for geli(8). */ aalgo = ealgo = keylen_str = ""; aflag = eflag = lflag = Tflag = ""; /* We will always specify sectorsize. */ sflag = " -s "; sectorsize_str = NULL; if (mntops != NULL) { string = ops = strdup(mntops); while ((token = strsep(&string, ",")) != NULL) { if ((p = strstr(token, "aalgo=")) == token) { aalgo = p + sizeof("aalgo=") - 1; aflag = " -a "; } else if ((p = strstr(token, "ealgo=")) == token) { ealgo = p + sizeof("ealgo=") - 1; eflag = " -e "; } else if ((p = strstr(token, "keylen=")) == token) { keylen_str = p + sizeof("keylen=") - 1; 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); free(ops); return (NULL); } lflag = " -l "; } else if ((p = strstr(token, "sectorsize=")) == token) { sectorsize_str = p + sizeof("sectorsize=") - 1; 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); free(ops); return (NULL); } } else if (strcmp(token, "notrim") == 0) { if (Eflag) { warn("Options \"notrim\" and " "\"trimonce\" conflict"); free(ops); return (NULL); } Tflag = " -T "; + } else if ((p = strstr(token, "file=")) == token) { + /* ignore known option */ } else if (strcmp(token, "late") == 0) { /* ignore known option */ } else if (strcmp(token, "noauto") == 0) { /* ignore known option */ } else if (strcmp(token, "sw") == 0) { /* ignore known option */ } else if (strcmp(token, "trimonce") == 0) { /* ignore known option */ } else { warnx("Invalid option: %s", token); free(ops); return (NULL); } } } else ops = NULL; /* * If we do not have a sector size at this point, fill in * pagesize as sector size. */ if (sectorsize_str == NULL) { /* Use pagesize as default sectorsize. */ pagesize = getpagesize(); pagesize_len = snprintf(NULL, 0, "%d", pagesize) + 1; p = alloca(pagesize_len); snprintf(p, pagesize_len, "%d", pagesize); sectorsize_str = p; } (void)asprintf(&args, "%s%s%s%s%s%s%s%s%s -d", aflag, aalgo, eflag, ealgo, lflag, keylen_str, Tflag, sflag, sectorsize_str); free(ops); return (args); } static const char * swap_on_off_geli(const char *name, char *mntops, int doingall) { struct stat sb; char *dname, *args; int error; error = stat(name, &sb); if (which_prog == SWAPON) do { /* Skip if the .eli device already exists. */ if (error == 0) break; args = swap_on_geli_args(mntops); if (args == NULL) return (NULL); dname = swap_basename(name); if (dname == NULL) { free(args); return (NULL); } error = run_cmd(NULL, "%s onetime%s %s", _PATH_GELI, args, dname); free(dname); free(args); if (error) { /* error occurred during creation. */ if (qflag == 0) warnx("%s: Invalid parameters", name); return (NULL); } } while (0); return (swap_on_off_sfile(name, doingall)); } static const char * swap_on_off_md(const char *name, char *mntops, int doingall) { FILE *sfd; int fd, mdunit, error; const char *ret; static char mdpath[PATH_MAX], linebuf[PATH_MAX]; char *p, *vnodefile; size_t linelen; u_long ul; + const char *suffix; + char *devbuf, *dname; + int name_len; fd = -1; sfd = NULL; - if (strlen(name) == (sizeof(MD_NAME) - 1)) + devbuf = strdup(name); + name_len = strlen(name) - strlen(DOT_ELI); + if (name_len > 0 && strcmp(suffix = &name[name_len], DOT_ELI) == 0) { + suffix++; + devbuf[name_len] = '\0'; + } else + suffix = NULL; + /* dname will be name without /dev/ prefix and .eli suffix */ + dname = basename(devbuf); + if (strlen(dname) == (sizeof(MD_NAME) - 1)) mdunit = -1; else { errno = 0; - ul = strtoul(name + 2, &p, 10); + ul = strtoul(dname + 2, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { - warn("Bad device unit: %s", name); + warn("Bad device unit: %s", dname); + free(devbuf); return (NULL); } mdunit = (int)ul; } + free(devbuf); 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); } if (which_prog == 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); free(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); free(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; } strlcpy(linebuf, p, linelen); 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 == 0) warnx("md%d on %s: Device already " "in use", mdunit, vnodefile); free(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); free(vnodefile); return (NULL); } } } else /* 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); free(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))) { warn("mdconfig (list) unexpected output"); ret = NULL; goto err; } strlcpy(linebuf, p, linelen); 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); free(vnodefile); 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); + + if (suffix != NULL && strcmp("eli", suffix) == 0) { + /* Swap on encrypted device by GEOM_ELI. */ + snprintf(mdpath, sizeof(mdpath), "%s%s%d" DOT_ELI, _PATH_DEV, + MD_NAME, mdunit); + mdpath[sizeof(mdpath) - 1] = '\0'; + ret = swap_on_off_geli(mdpath, mntops, doingall); + } else { + snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV, + MD_NAME, mdunit); + mdpath[sizeof(mdpath) - 1] = '\0'; + ret = swap_on_off_sfile(mdpath, doingall); + } if (which_prog == 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); } } err: if (sfd != NULL) fclose(sfd); if (fd != -1) close(fd); free(vnodefile); return (ret); } static int run_cmd(int *ofd, const char *cmdline, ...) { 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__); va_end(ap); 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 int swapon_trim(const char *name) { struct stat sb; off_t ioarg[2], sz; int error, fd; /* Open a descriptor to create a consumer of the device. */ fd = open(name, O_WRONLY); if (fd < 0) errx(1, "Cannot open %s", name); /* Find the device size. */ if (fstat(fd, &sb) < 0) errx(1, "Cannot stat %s", name); if (S_ISREG(sb.st_mode)) sz = sb.st_size; else if (S_ISCHR(sb.st_mode)) { if (ioctl(fd, DIOCGMEDIASIZE, &sz) != 0) err(1, "ioctl(DIOCGMEDIASIZE)"); } else errx(1, "%s has an invalid file type", name); /* Trim the device. */ ioarg[0] = BBSIZE; ioarg[1] = sz - BBSIZE; if (ioctl(fd, DIOCGDELETE, ioarg) != 0) warn("ioctl(DIOCGDELETE)"); /* Start using the device for swapping, creating a second consumer. */ error = swapon(name); /* * Do not close the device until the swap pager has attempted to create * another consumer. For GELI devices created with the 'detach -l' * option, removing the last consumer causes the device to be detached * - that is, to disappear. This ordering ensures that the device will * not be detached until swapoff is called. */ close(fd); return (error); } static const char * swap_on_off_sfile(const char *name, int doingall) { int error; if (which_prog == SWAPON) error = Eflag ? swapon_trim(name) : swapon(name); else /* SWAPOFF */ error = swapoff(name, fflag ? SWAPOFF_FORCE : 0); if (error == -1) { switch (errno) { case EBUSY: if (doingall == 0) warnx("%s: Device already in use", name); break; case EINVAL: if (which_prog == SWAPON) warnx("%s: NSWAPDEV limit reached", name); else if (doingall == 0) warn("%s", name); break; default: warn("%s", name); break; } return (NULL); } return (name); } static void usage(void) { fprintf(stderr, "usage: %s ", getprogname()); switch(orig_prog) { case SWAPON: fprintf(stderr, "[-F fstab] -aLq | [-E] file ...\n"); break; case SWAPOFF: fprintf(stderr, "[-F fstab] -afLq | file ...\n"); break; case SWAPCTL: fprintf(stderr, "[-AghklmsU] [-a file ... | -d file ...]\n"); break; } exit(1); } static void sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen, long blocksize) { char tmp[16]; if (hflag == 'H') { humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); snprintf(buf, bufsize, "%*s", hlen, tmp); } else snprintf(buf, bufsize, "%*lld", hlen, val / blocksize); } static void swaplist(int lflag, int sflag, int hflag) { size_t mibsize, size; struct xswdev xsw; int hlen, mib[16], n, pagesize; long blocksize; long long total = 0; long long used = 0; long long tmp_total; long long tmp_used; char buf[32]; pagesize = getpagesize(); switch(hflag) { case 'G': blocksize = 1024 * 1024 * 1024; strlcpy(buf, "1GB-blocks", sizeof(buf)); hlen = 10; break; case 'H': blocksize = -1; strlcpy(buf, "Bytes", sizeof(buf)); hlen = 10; break; case 'K': blocksize = 1024; strlcpy(buf, "1kB-blocks", sizeof(buf)); hlen = 10; break; case 'M': blocksize = 1024 * 1024; strlcpy(buf, "1MB-blocks", sizeof(buf)); hlen = 10; break; default: getbsize(&hlen, &blocksize); snprintf(buf, sizeof(buf), "%ld-blocks", blocksize); break; } mibsize = nitems(mib); if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) err(1, "sysctlnametomib()"); if (lflag) { printf("%-13s %*s %*s\n", "Device:", hlen, buf, hlen, "Used:"); } for (n = 0; ; ++n) { mib[mibsize] = n; size = sizeof xsw; if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1) break; if (xsw.xsw_version != XSWDEV_VERSION) errx(1, "xswdev version mismatch"); tmp_total = (long long)xsw.xsw_nblks * pagesize; tmp_used = (long long)xsw.xsw_used * pagesize; total += tmp_total; used += tmp_used; if (lflag) { sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen, blocksize); printf("/dev/%-8s %s ", devname(xsw.xsw_dev, S_IFCHR), buf); sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen, blocksize); printf("%s\n", buf); } } if (errno != ENOENT) err(1, "sysctl()"); if (sflag) { sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize); printf("Total: %s ", buf); sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize); printf("%s\n", buf); } } diff --git a/sbin/swapon/tests/Makefile b/sbin/swapon/tests/Makefile new file mode 100644 index 000000000000..aa0c9cf202d6 --- /dev/null +++ b/sbin/swapon/tests/Makefile @@ -0,0 +1,5 @@ +ATF_TESTS_SH= swapon_test + +TEST_METADATA.swapon_test+= required_user="root" + +.include diff --git a/sbin/swapon/tests/swapon_test.sh b/sbin/swapon/tests/swapon_test.sh new file mode 100755 index 000000000000..3c4286cd6815 --- /dev/null +++ b/sbin/swapon/tests/swapon_test.sh @@ -0,0 +1,181 @@ +# Copyright (c) 2025 Ronald Klop +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# + +atf_test_case attach_mdX cleanup +attach_mdX_head() +{ + atf_set "descr" "mdX device should attach" +} +attach_mdX_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo 'md3 none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md3 as swap device" -x "swapon -F fstab.out -a" +} +attach_mdX_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_mdX cleanup +attach_dev_mdX_head() +{ + atf_set "descr" "/dev/mdX device should attach" +} +attach_dev_mdX_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md3 none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md3 as swap device" -x "swapon -F fstab.out -a" +} +attach_dev_mdX_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_md cleanup +attach_md_head() +{ + atf_set "descr" "mdX device should attach" +} +attach_md_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo 'md none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md0 as swap device" -x "swapon -F fstab.out -a" +} +attach_md_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_md cleanup +attach_dev_md_head() +{ + atf_set "descr" "/dev/md device should attach" +} +attach_dev_md_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md0 as swap device" -x "swapon -F fstab.out -a" +} +attach_dev_md_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_mdX_eli cleanup +attach_mdX_eli_head() +{ + atf_set "descr" "mdX.eli device should attach" +} +attach_mdX_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo 'md3.eli none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md3.eli as swap device" -x "swapon -F fstab.out -a" +} +attach_mdX_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_mdX_eli cleanup +attach_dev_mdX_eli_head() +{ + atf_set "descr" "/dev/mdX.eli device should attach" +} +attach_dev_mdX_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md3.eli none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md3.eli as swap device" -x "swapon -F fstab.out -a" +} +attach_dev_mdX_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_md_eli cleanup +attach_md_eli_head() +{ + atf_set "descr" "md.eli device should attach" +} +attach_md_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo 'md.eli none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md0.eli as swap device" -x "swapon -F fstab.out -a" +} +attach_md_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_md_eli cleanup +attach_dev_md_eli_head() +{ + atf_set "descr" "/dev/md.eli device should attach" +} +attach_dev_md_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + atf_check -s exit:0 -x "truncate -s 10k swapfile" + atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md.eli none swap sw,file=swapfile 0 0'" + atf_check -s exit:0 -o match:"swapon: adding /dev/md0.eli as swap device" -x "swapon -F fstab.out -a" +} +attach_dev_md_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_init_test_cases() +{ + atf_add_test_case attach_mdX + atf_add_test_case attach_dev_mdX + atf_add_test_case attach_md + atf_add_test_case attach_dev_md + + atf_add_test_case attach_mdX_eli + atf_add_test_case attach_dev_mdX_eli + atf_add_test_case attach_md_eli + atf_add_test_case attach_dev_md_eli +}