svn commit: r252310 - in head: etc/defaults etc/rc.d include sbin/swapon share/man/man5

Hiroki Sato hrs at FreeBSD.org
Thu Jun 27 18:28:48 UTC 2013


Author: hrs
Date: Thu Jun 27 18:28:45 2013
New Revision: 252310
URL: http://svnweb.freebsd.org/changeset/base/252310

Log:
  - 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)

Added:
  head/etc/rc.d/swap
     - copied, changed from r251659, head/etc/rc.d/swap1
  head/etc/rc.d/swaplate   (contents, props changed)
Deleted:
  head/etc/rc.d/encswap
  head/etc/rc.d/swap1
Modified:
  head/etc/defaults/rc.conf
  head/etc/rc.d/Makefile
  head/etc/rc.d/addswap
  head/etc/rc.d/fsck
  head/etc/rc.d/mdconfig
  head/include/paths.h
  head/sbin/swapon/swapon.8
  head/sbin/swapon/swapon.c
  head/share/man/man5/fstab.5

Modified: head/etc/defaults/rc.conf
==============================================================================
--- head/etc/defaults/rc.conf	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/etc/defaults/rc.conf	Thu Jun 27 18:28:45 2013	(r252310)
@@ -32,8 +32,6 @@ early_late_divider="FILESYSTEMS"	# Scrip
 always_force_depends="NO"	# Set to check that indicated dependencies are
 				# running during boot (can increase boot time).
 
-swapfile="NO"		# Set to name of swapfile if aux swapfile desired.
-swapfile_mdunit="99"	# Swapfile md(4) unit number created by mdconfig(8).
 apm_enable="NO"		# Set to YES to enable APM BIOS functions (or NO).
 apmd_enable="NO"	# Run apmd to handle APM event from userland.
 apmd_flags=""		# Flags to apmd (if enabled).
@@ -85,9 +83,6 @@ geli_autodetach="YES"	# Automatically de
 #geli_da1_autodetach="NO"
 #geli_mirror_home_flags="-k /etc/geli/home.keys"
 
-geli_swap_flags="-e aes -l 256 -s 4096 -d"	# Options for GELI-encrypted
-						# swap partitions.
-
 root_rw_mount="YES"	# Set to NO to inhibit remounting root read-write.
 fsck_y_enable="NO"	# Set to YES to do fsck -y if the initial preen fails.
 fsck_y_flags=""		# Additional flags for fsck -y

Modified: head/etc/rc.d/Makefile
==============================================================================
--- head/etc/rc.d/Makefile	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/etc/rc.d/Makefile	Thu Jun 27 18:28:45 2013	(r252310)
@@ -37,7 +37,6 @@ FILES=	DAEMON \
 	dhclient \
 	dmesg \
 	dumpon \
-	encswap \
 	faith \
 	fsck \
 	ftp-proxy \
@@ -139,7 +138,8 @@ FILES=	DAEMON \
 	static_arp \
 	static_ndp \
 	stf \
-	swap1 \
+	swap \
+	swaplate \
 	syscons \
 	sysctl \
 	syslogd \

Modified: head/etc/rc.d/addswap
==============================================================================
--- head/etc/rc.d/addswap	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/etc/rc.d/addswap	Thu Jun 27 18:28:45 2013	(r252310)
@@ -13,57 +13,12 @@
 . /etc/rc.subr
 
 name="addswap"
-start_cmd="addswap_start"
-stop_cmd="addswap_stop"
+start_cmd=":"
+stop_cmd=":"
+rcvar=
 
-addswap_start()
-{
-	case ${swapfile} in
-	[Nn][Oo] | '')
-		;;
-	*)
-		if [ -w "${swapfile}" ]; then
-			check_startmsgs && echo "Adding ${swapfile} as additional swap"
-
-			if [ -n "${swapfile_mdunit}" ]; then
-				mdev="/dev/md${swapfile_mdunit#md}"
-				mdconfig -a -t vnode -f "${swapfile}" -u ${swapfile_mdunit}
-			else
-				mdev="/dev/`mdconfig -a -t vnode -f "${swapfile}"`"
-			fi
-
-			if [ $? -eq 0 ]; then
-				swapon ${mdev}
-			else
-				echo "error creating swapfile device"
-			fi
-		fi
-		;;
-	esac
-}
-
-addswap_stop()
-{
-	case ${swapfile} in
-	[Nn][Oo] | '')
-		;;
-	*)
-		if [ -n "${swapfile_mdunit}" ]; then
-			mdev="/dev/md${swapfile_mdunit#md}"
-		else
-			mdev="/dev/`mdconfig -lv | grep "${swapfile}" | cut -f1`"
-			swapfile_mdunit=${mdev#md}
-		fi
-		if [ -n "${swapfile_mdunit}" ]; then
-			swapctl -l | grep -q ${mdev}
-			if [ $? -eq 0 ]; then
-				echo "Dismounting swapfile ${swapfile}"
-				swapoff ${mdev} && mdconfig -d -u ${swapfile_mdunit}
-			fi
-		fi
-		;;
-	esac
-}
+set_rcvar_obsolete swapfile
+set_rcvar_obsolete geli_swap_flags
 
 load_rc_config $name
 run_rc_command "$1"

Modified: head/etc/rc.d/fsck
==============================================================================
--- head/etc/rc.d/fsck	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/etc/rc.d/fsck	Thu Jun 27 18:28:45 2013	(r252310)
@@ -4,7 +4,7 @@
 #
 
 # PROVIDE: fsck
-# REQUIRE: localswap
+# REQUIRE: swap
 # KEYWORD: nojail
 
 . /etc/rc.subr

Modified: head/etc/rc.d/mdconfig
==============================================================================
--- head/etc/rc.d/mdconfig	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/etc/rc.d/mdconfig	Thu Jun 27 18:28:45 2013	(r252310)
@@ -28,7 +28,7 @@
 #
 
 # PROVIDE: mdconfig
-# REQUIRE: localswap root
+# REQUIRE: swap root
 
 . /etc/rc.subr
 

Copied and modified: head/etc/rc.d/swap (from r251659, head/etc/rc.d/swap1)
==============================================================================
--- head/etc/rc.d/swap1	Wed Jun 12 16:13:05 2013	(r251659, copy source)
+++ head/etc/rc.d/swap	Thu Jun 27 18:28:45 2013	(r252310)
@@ -3,15 +3,15 @@
 # $FreeBSD$
 #
 
-# PROVIDE: localswap
+# PROVIDE: swap
 # REQUIRE: disks
 # KEYWORD: nojail shutdown
 
 . /etc/rc.subr
 
-name="swap1"
-start_cmd='swapon -aq'
+name="swap"
+start_cmd='/sbin/swapon -aq'
 stop_cmd=':'
 
-load_rc_config swap
+load_rc_config $name
 run_rc_command "$1"

Added: head/etc/rc.d/swaplate
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/etc/rc.d/swaplate	Thu Jun 27 18:28:45 2013	(r252310)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: swaplate
+# REQUIRE: mountlate
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="swaplate"
+start_cmd='/sbin/swapon -aLq'
+stop_cmd='/sbin/swapoff -aq'
+
+load_rc_config swap
+run_rc_command "$1"

Modified: head/include/paths.h
==============================================================================
--- head/include/paths.h	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/include/paths.h	Thu Jun 27 18:28:45 2013	(r252310)
@@ -57,6 +57,8 @@
 #define	_PATH_ETC	"/etc"
 #define	_PATH_FTPUSERS	"/etc/ftpusers"
 #define	_PATH_FWMEM	"/dev/fwmem"
+#define	_PATH_GBDE	"/sbin/gbde"
+#define	_PATH_GELI	"/sbin/geli"
 #define	_PATH_HALT	"/sbin/halt"
 #ifdef COMPAT_32BIT
 #define	_PATH_I18NMODULE	"/usr/lib32/i18n"

Modified: head/sbin/swapon/swapon.8
==============================================================================
--- head/sbin/swapon/swapon.8	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/sbin/swapon/swapon.8	Thu Jun 27 18:28:45 2013	(r252310)
@@ -28,7 +28,7 @@
 .\"     @(#)swapon.8	8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd June 23, 2008
+.Dd June 21, 2013
 .Dt SWAPON 8
 .Os
 .Sh NAME
@@ -38,11 +38,11 @@
 .Nm swapon
 .Oo Fl F Ar fstab
 .Oc
-.Fl aq | Ar
+.Fl aLq | Ar
 .Nm swapoff
 .Oo Fl F Ar fstab
 .Oc
-.Fl aq | Ar
+.Fl aLq | Ar
 .Nm swapctl
 .Op Fl AghklmsU
 .Oo
@@ -74,10 +74,19 @@ option is used, all swap devices in
 .Pa /etc/fstab
 will be added, unless their
 .Dq noauto
+or
+.Dq late
 option is also set.
 If the
+.Fl L
+option is specified,
+swap devices with the
+.Dq late
+option will be added as well as ones with no option.
+If the
 .Fl q
-option is used informational messages will not be
+option is used,
+informational messages will not be
 written to standard output when a swap device is added.
 .Pp
 The
@@ -89,10 +98,19 @@ option is used, all swap devices in
 .Pa /etc/fstab
 will be removed, unless their
 .Dq noauto
+or
+.Dq late
 option is also set.
 If the
+.Fl L
+option is specified,
+swap devices with the
+.Dq late
+option will be removed as well as ones with no option.
+If the
 .Fl q
-option is used informational messages will not be
+option is used,
+informational messages will not be
 written to standard output when a swap device is removed.
 Note that
 .Nm swapoff

Modified: head/sbin/swapon/swapon.c
==============================================================================
--- head/sbin/swapon/swapon.c	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/sbin/swapon/swapon.c	Thu Jun 27 18:28:45 2013	(r252310)
@@ -41,35 +41,51 @@ static char sccsid[] = "@(#)swapon.c	8.1
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
-#include <sys/stat.h>
 #include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mdioctl.h>
+#include <sys/stat.h>
 #include <sys/sysctl.h>
+#include <sys/wait.h>
 #include <vm/vm_param.h>
 
 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
 #include <fstab.h>
+#include <libgen.h>
+#include <libutil.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <libutil.h>
 
 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, ...)
+{
+	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)
 {
-	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
+	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");

Modified: head/share/man/man5/fstab.5
==============================================================================
--- head/share/man/man5/fstab.5	Thu Jun 27 18:24:24 2013	(r252309)
+++ head/share/man/man5/fstab.5	Thu Jun 27 18:28:45 2013	(r252310)
@@ -32,7 +32,7 @@
 .\"     @(#)fstab.5	8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd October 11, 2012
+.Dd June 21, 2013
 .Dt FSTAB 5
 .Os
 .Sh NAME
@@ -185,6 +185,15 @@ variable must be used to extend the
 .Xr rc 8
 startup script's list of network file system types.
 .Pp
+If the option
+.Dq late
+is specified, the file system will be automatically mounted
+at a stage of system startup after remote mount points are mounted.
+For more detail about this option,
+see the
+.Xr mount 8
+manual page.
+.Pp
 The type of the mount is extracted from the
 .Fa fs_mntops
 field and stored separately in the
@@ -202,6 +211,7 @@ then the file system whose name is given
 .Fa fs_file
 field is normally mounted read-write or read-only on the
 specified special file.
+.Pp
 If
 .Fa fs_type
 is
@@ -210,6 +220,25 @@ then the special file is made available 
 space by the
 .Xr swapon 8
 command at the end of the system reboot procedure.
+For vnode-backed swap spaces,
+.Dq file
+is supported in the
+.Fa fs_mntops
+field.
+When
+.Fa fs_spec
+is an
+.Xr md 4
+device file
+.Pq Do md Dc or Do md[0-9]* Dc
+and 
+.Dq file
+is specified in
+.Fa fs_mntopts ,
+an
+.Xr md 4
+device is created with the specified file used as backing store,
+and then the new device is used as swap space.
 The fields other than
 .Fa fs_spec
 and


More information about the svn-src-head mailing list