Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168409080
D2698.id7066.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
D2698.id7066.diff
View Options
Index: sbin/init/Makefile
===================================================================
--- sbin/init/Makefile
+++ sbin/init/Makefile
@@ -2,12 +2,19 @@
# $FreeBSD$
PROG= init
+SRCS= init.c
+SRCS+= getmntopts.c
MAN= init.8
PRECIOUSPROG=
INSTALLFLAGS=-b -B.bak
CFLAGS+=-DDEBUGSHELL -DSECURE -DLOGIN_CAP -DCOMPAT_SYSV_INIT
LIBADD= util crypt
+# Needed for getmntopts.c
+MOUNT= ${.CURDIR}/../../sbin/mount
+CFLAGS+=-I${MOUNT}
+.PATH: ${MOUNT}
+
NO_SHARED?= YES
.include <bsd.prog.mk>
Index: sbin/init/init.8
===================================================================
--- sbin/init/init.8
+++ sbin/init/init.8
@@ -284,6 +284,7 @@
as follows:
.Bl -column Run-level SIGTERM
.It Sy "Run-level Signal Action"
+.It Cm 0 Ta Dv SIGUSR1 Ta "Halt"
.It Cm 0 Ta Dv SIGUSR2 Ta "Halt and turn the power off"
.It Cm 1 Ta Dv SIGTERM Ta "Go to single-user mode"
.It Cm 6 Ta Dv SIGINT Ta "Reboot the machine"
Index: sbin/init/init.c
===================================================================
--- sbin/init/init.c
+++ sbin/init/init.c
@@ -46,6 +46,7 @@
#include <sys/param.h>
#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
@@ -79,6 +80,7 @@
#include <login_cap.h>
#endif
+#include "mntopts.h"
#include "pathnames.h"
/*
@@ -122,6 +124,8 @@
static state_func_t catatonia(void);
static state_func_t death(void);
static state_func_t death_single(void);
+static state_func_t reroot(void);
+static state_func_t reroot_phase_two(void);
static state_func_t run_script(const char *);
@@ -226,6 +230,9 @@
case 'q': /* rescan /etc/ttys */
sig = SIGHUP;
break;
+ case 'r': /* remount root */
+ sig = SIGEMT;
+ break;
default:
goto invalid;
}
@@ -247,8 +254,13 @@
/*
* Create an initial session.
*/
- if (setsid() < 0)
- warning("initial setsid() failed: %m");
+ if (setsid() < 0) {
+ if (errno == EPERM && getsid(0) == getpid()) {
+ /* Okay, we already did setsid() before. */
+ } else {
+ warning("initial setsid() failed: %m");
+ }
+ }
/*
* Establish an initial user so that programs running
@@ -261,7 +273,7 @@
* This code assumes that we always get arguments through flags,
* never through bits set in some random machine register.
*/
- while ((c = getopt(argc, argv, "dsf")) != -1)
+ while ((c = getopt(argc, argv, "dsfR")) != -1)
switch (c) {
case 'd':
devfs = 1;
@@ -272,6 +284,9 @@
case 'f':
runcom_mode = FASTBOOT;
break;
+ case 'R':
+ initial_transition = reroot_phase_two;
+ break;
default:
warning("unrecognized flag '-%c'", c);
break;
@@ -287,13 +302,13 @@
handle(badsys, SIGSYS, 0);
handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU,
SIGXFSZ, 0);
- handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGUSR1,
- SIGUSR2, 0);
+ handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP,
+ SIGUSR1, SIGUSR2, 0);
handle(alrm_handler, SIGALRM, 0);
sigfillset(&mask);
delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
- SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM,
- SIGUSR1, SIGUSR2, 0);
+ SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP,
+ SIGALRM, SIGUSR1, SIGUSR2, 0);
sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
@@ -620,6 +635,191 @@
write(STDERR_FILENO, message, strlen(message));
}
+static int
+read_file(const char *path, void **bufp, size_t *bufsizep)
+{
+ struct stat sb;
+ int error, fd;
+ size_t bufsize;
+ void *buf;
+ ssize_t nbytes;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ emergency("%s: %s", path, strerror(errno));
+ return (-1);
+ }
+
+ error = fstat(fd, &sb);
+ if (error != 0) {
+ emergency("fstat: %s", strerror(errno));
+ return (error);
+ }
+
+ bufsize = sb.st_size;
+ buf = malloc(bufsize);
+ if (buf == NULL) {
+ emergency("malloc: %s", strerror(errno));
+ return (error);
+ }
+
+ nbytes = read(fd, buf, bufsize);
+ if (nbytes != (ssize_t)bufsize) {
+ emergency("read: %s", strerror(errno));
+ free(buf);
+ return (error);
+ }
+
+ error = close(fd);
+ if (error != 0) {
+ emergency("close: %s", strerror(errno));
+ free(buf);
+ return (error);
+ }
+
+ *bufp = buf;
+ *bufsizep = bufsize;
+
+ return (0);
+}
+
+static int
+create_file(const char *path, void *buf, size_t bufsize)
+{
+ int error, fd;
+ ssize_t nbytes;
+
+ fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0700);
+ if (fd < 0) {
+ emergency("%s: %s", path, strerror(errno));
+ return (-1);
+ }
+
+ nbytes = write(fd, buf, bufsize);
+ if (nbytes != (ssize_t)bufsize) {
+ emergency("write: %s", strerror(errno));
+ return (-1);
+ }
+
+ error = close(fd);
+ if (error != 0) {
+ emergency("close: %s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+mount_tmpfs(const char *fspath)
+{
+ struct iovec *iov = NULL;
+ char errmsg[255];
+ int error, iovlen = 0;
+
+ memset(errmsg, 0, sizeof(errmsg));
+
+ build_iovec(&iov, &iovlen, "fstype",
+ __DECONST(void *, "tmpfs"), (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath",
+ __DECONST(void *, fspath), (size_t)-1);
+ // XXX: Needed?
+ build_iovec(&iov, &iovlen, "from",
+ __DECONST(void *, "tmpfs"), (size_t)-1);
+ build_iovec(&iov, &iovlen, "errmsg",
+ errmsg, sizeof(errmsg));
+
+ error = nmount(iov, iovlen, 0);
+ if (error != 0) {
+ if (*errmsg != '\0') {
+ emergency("cannot mount tmpfs on %s: %s: %s",
+ fspath, errmsg, strerror(errno));
+ } else {
+ emergency("cannot mount tmpfs on %s: %s",
+ fspath, strerror(errno));
+ }
+ return (error);
+ }
+
+ return (0);
+}
+
+static state_func_t
+reroot(void)
+{
+ int error;
+ void *buf;
+ size_t bufsize;
+
+ warning("%s: go!", __func__);
+
+ /*
+ * Copy the init binary into tmpfs, so that we can unmount
+ * the old rootfs without committing suicide.
+ *
+ * XXX: Hardcoded path.
+ */
+ error = read_file("/sbin/init", &buf, &bufsize);
+ if (error != 0)
+ goto out;
+ error = mount_tmpfs("/tmp");
+ if (error != 0)
+ goto out;
+ error = create_file("/tmp/init", buf, bufsize);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Execute the temporary init.
+ */
+ execl("/tmp/init", "/tmp/init", "-R", NULL);
+ emergency("cannot exec %s: %s", "/tmp/init", strerror(errno));
+
+out:
+ emergency("reroot failed; going to single user mode");
+
+ /*
+ * Make sure we don't loop when calling single_user on error.
+ */
+ howto = RB_AUTOBOOT;
+ Reboot = FALSE;
+ return (state_func_t) single_user;
+}
+
+static state_func_t
+reroot_phase_two(void)
+{
+ int error;
+
+ /*
+ * Ask the kernel to mount the new rootfs.
+ */
+ error = reboot(RB_REROOT);
+ if (error != 0) {
+ emergency("RB_REBOOT failed: %s", strerror(errno));
+ goto out;
+ }
+
+ /*
+ * Execute init(8) from the new rootfs.
+ *
+ * Note that at this point, all this warning() stuff is useless
+ * anyway; we don't have stderr or stdout.
+ */
+ execl("/sbin/init", "/sbin/init", NULL);
+ emergency("cannot exec %s: %s", "/sbin/init", strerror(errno));
+
+out:
+ /*
+ * Make sure we don't loop when calling single_user on error.
+ */
+ howto = RB_AUTOBOOT;
+ Reboot = FALSE;
+ emergency("reroot failed; going to single user mode");
+ return (state_func_t) single_user;
+}
+
/*
* Bring the system up single user.
*/
@@ -641,6 +841,8 @@
#ifdef DEBUGSHELL
char altshell[128];
#endif
+ if (howto == RB_REROOT)
+ return (reroot());
if (Reboot) {
/* Instead of going single user, let's reboot the machine */
@@ -1308,6 +1510,8 @@
howto = RB_POWEROFF;
case SIGUSR1:
howto |= RB_HALT;
+ case SIGEMT:
+ howto = RB_REROOT;
case SIGINT:
Reboot = TRUE;
case SIGTERM:
Index: sbin/reboot/reboot.8
===================================================================
--- sbin/reboot/reboot.8
+++ sbin/reboot/reboot.8
@@ -28,7 +28,7 @@
.\" @(#)reboot.8 8.1 (Berkeley) 6/9/93
.\" $FreeBSD$
.\"
-.Dd October 11, 2010
+.Dd May 22, 2015
.Dt REBOOT 8
.Os
.Sh NAME
@@ -42,7 +42,7 @@
.Op Fl lnpq
.Op Fl k Ar kernel
.Nm
-.Op Fl dlnpq
+.Op Fl dlnpqr
.Op Fl k Ar kernel
.Nm fasthalt
.Op Fl lnpq
@@ -111,6 +111,13 @@
.Fl n
option is not specified).
This option should probably not be used.
+.It Fl r
+The system kills all processes, unmounts all filesystems, mounts the new
+root filesystem, and starts
+.Xr init 8 .
+It can be used after updating vfs.root.mountfrom using
+.Xr kenv 8 ,
+to change the root filesystem while preserving kernel state.
.El
.Pp
The
Index: sbin/reboot/reboot.c
===================================================================
--- sbin/reboot/reboot.c
+++ sbin/reboot/reboot.c
@@ -77,7 +77,7 @@
} else
howto = 0;
lflag = nflag = qflag = 0;
- while ((ch = getopt(argc, argv, "dk:lnpq")) != -1)
+ while ((ch = getopt(argc, argv, "dk:lnpqr")) != -1)
switch(ch) {
case 'd':
howto |= RB_DUMP;
@@ -98,6 +98,9 @@
case 'q':
qflag = 1;
break;
+ case 'r':
+ howto |= RB_REROOT;
+ break;
case '?':
default:
usage();
@@ -107,10 +110,14 @@
if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
errx(1, "cannot dump (-d) when halting; must reboot instead");
+ if ((howto & RB_REROOT) != 0 && (howto & RB_REROOT) != RB_REROOT)
+ errx(1, "-r flag is mutually exclusive with -d, -n, and -p");
+#if 0
if (geteuid()) {
errno = EPERM;
err(1, NULL);
}
+#endif
if (qflag) {
reboot(howto);
@@ -170,6 +177,16 @@
*/
(void)signal(SIGPIPE, SIG_IGN);
+ /*
+ * Nobody but init(8) can perform rerooting.
+ */
+ if (howto & RB_REROOT) {
+ if (kill(1, SIGEMT) == -1)
+ err(1, "SIGEMT init");
+
+ return (0);
+ }
+
/* Just stop init -- if we fail, we'll restart it. */
if (kill(1, SIGTSTP) == -1)
err(1, "SIGTSTP init");
Index: sys/fs/devfs/devfs_vfsops.c
===================================================================
--- sys/fs/devfs/devfs_vfsops.c
+++ sys/fs/devfs/devfs_vfsops.c
@@ -182,6 +182,8 @@
fmp = VFSTODEVFS(mp);
KASSERT(fmp->dm_mount != NULL,
("devfs_unmount unmounted devfs_mount"));
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
/* There is 1 extra root vnode reference from devfs_mount(). */
error = vflush(mp, 1, flags, curthread);
if (error)
Index: sys/geom/geom_dev.c
===================================================================
--- sys/geom/geom_dev.c
+++ sys/geom/geom_dev.c
@@ -358,6 +358,13 @@
#else
e = 0;
#endif
+
+ /*
+ * This happens on attempt to open a device node with O_EXEC.
+ */
+ if (r + w + e == 0)
+ return (EINVAL);
+
if (w) {
/*
* When running in very secure mode, do not allow
@@ -401,6 +408,10 @@
#else
e = 0;
#endif
+
+ if (r + w + e == 0)
+ return (EINVAL);
+
sc = cp->private;
mtx_lock(&sc->sc_mtx);
sc->sc_open += r + w + e;
Index: sys/kern/kern_shutdown.c
===================================================================
--- sys/kern/kern_shutdown.c
+++ sys/kern/kern_shutdown.c
@@ -50,6 +50,7 @@
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/eventhandler.h>
+#include <sys/filedesc.h>
#include <sys/jail.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
@@ -67,6 +68,7 @@
#include <sys/smp.h>
#include <sys/sysctl.h>
#include <sys/sysproto.h>
+#include <sys/sx.h>
#include <sys/vnode.h>
#include <sys/watchdog.h>
@@ -154,6 +156,7 @@
static void shutdown_halt(void *junk, int howto);
static void shutdown_panic(void *junk, int howto);
static void shutdown_reset(void *junk, int howto);
+static int kern_reroot(void);
/* register various local shutdown events */
static void
@@ -172,9 +175,6 @@
SYSINIT(shutdown_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, shutdown_conf, NULL);
-/*
- * The system call that results in a reboot.
- */
/* ARGSUSED */
int
sys_reboot(struct thread *td, struct reboot_args *uap)
@@ -188,9 +188,17 @@
if (error == 0)
error = priv_check(td, PRIV_REBOOT);
if (error == 0) {
- mtx_lock(&Giant);
- kern_reboot(uap->opt);
- mtx_unlock(&Giant);
+ if (uap->opt & RB_REROOT) {
+ mtx_lock(&Giant);
+ error = kern_reroot();
+ mtx_unlock(&Giant);
+ } else {
+ mtx_lock(&Giant);
+ kern_reboot(uap->opt);
+ swapoff_all();
+ DELAY(100000); /* wait for console output to finish */
+ mtx_unlock(&Giant);
+ }
}
return (error);
}
@@ -454,6 +462,40 @@
}
/*
+ * The system call that results in changing the rootfs.
+ */
+static int
+kern_reroot(void)
+{
+ struct vnode *oldrootvnode;
+
+ if (curproc->p_pid != 1)
+ return (EPERM);
+
+ oldrootvnode = rootvnode;
+
+ /*
+ * Unmount everything, including /dev and rootfs.
+ */
+ printf("%s: vfs_unmountall...\n", __func__);
+ vfs_unmountall();
+
+ /*
+ * Mount stuff.
+ */
+ printf("%s: vfs_mountroot...\n", __func__);
+ vfs_mountroot();
+
+ /*
+ * Update all references to the old rootvnode.
+ */
+ printf("%s: mountcheckdirs...\n", __func__);
+ mountcheckdirs(oldrootvnode, rootvnode);
+
+ return (0);
+}
+
+/*
* If the shutdown was a clean halt, behave accordingly.
*/
static void
Index: sys/kern/vfs_mount.c
===================================================================
--- sys/kern/vfs_mount.c
+++ sys/kern/vfs_mount.c
@@ -1195,6 +1195,7 @@
return ((uap->flags & MNT_BYFSID) ? ENOENT : EINVAL);
}
+#if 0
/*
* Don't allow unmounting the root filesystem.
*/
@@ -1202,6 +1203,7 @@
vfs_rel(mp);
return (EINVAL);
}
+#endif
error = dounmount(mp, uap->flags, td);
return (error);
}
Index: sys/kern/vfs_mountroot.c
===================================================================
--- sys/kern/vfs_mountroot.c
+++ sys/kern/vfs_mountroot.c
@@ -678,12 +678,17 @@
char *dev, *fs, *opts, *tok;
int delay, error, timeout;
+ printf("%s: go!\n", __func__);
+
error = parse_token(conf, &tok);
- if (error)
+ if (error) {
+ printf("%s: parse_token() failed with error %d!\n", __func__, error);
return (error);
+ }
fs = tok;
error = parse_skipto(&tok, ':');
if (error) {
+ printf("%s: parse_skipto() failed with error %d!\n", __func__, error);
free(fs, M_TEMP);
return (error);
}
@@ -761,15 +766,23 @@
char *conf;
int error;
+ printf("%s: go!\n", __func__);
+
root_mount_mddev = -1;
retry:
conf = sbuf_data(sb);
+ printf("%s: 1\n", __func__);
mp = TAILQ_NEXT(mpdevfs, mnt_list);
+ if (mp != NULL && strcmp(mp->mnt_stat.f_fstypename, "tmpfs") == 0)
+ mp = TAILQ_NEXT(mp, mnt_list);
error = (mp == NULL) ? 0 : EDOOFUS;
root_mount_onfail = A_CONTINUE;
- while (mp == NULL) {
+ //while (mp == NULL) {
+ printf("%s: 2\n", __func__);
+ while (1) {
error = parse_skipto(&conf, CC_NONWHITESPACE);
+ printf("%s: parse_skipto() returned %d\n", __func__, error);
if (error == PE_EOL) {
parse_advance(&conf);
continue;
@@ -779,12 +792,17 @@
switch (parse_peek(&conf)) {
case '#':
error = parse_skipto(&conf, '\n');
+ printf("%s: parse_skipto() returned %d\n", __func__, error);
break;
case '.':
error = parse_directive(&conf);
+ printf("%s: parse_directive() returned %d\n", __func__, error);
break;
default:
error = parse_mount(&conf);
+ printf("%s: parse_mount() returned %d\n", __func__, error);
+ if (error == 0)
+ return (0);
break;
}
if (error < 0)
@@ -795,6 +813,8 @@
(void)parse_skipto(&conf, '\n');
}
mp = TAILQ_NEXT(mpdevfs, mnt_list);
+ if (mp != NULL && strcmp(mp->mnt_stat.f_fstypename, "tmpfs") == 0)
+ mp = TAILQ_NEXT(mp, mnt_list);
}
if (mp != NULL)
return (0);
@@ -824,6 +844,8 @@
char *s, *tok, *mnt, *opt;
int error;
+ printf("%s: go!\n", __func__);
+
sbuf_printf(sb, ".onfail panic\n");
sbuf_printf(sb, ".timeout %d\n", root_mount_timeout);
if (boothowto & RB_ASKNAME)
@@ -940,15 +962,21 @@
td = curthread;
+ printf("%s: go!\n", __func__);
+
vfs_mountroot_wait();
sb = sbuf_new_auto();
vfs_mountroot_conf0(sb);
sbuf_finish(sb);
+ printf("%s: before vfs_mountroot_devfs()\n", __func__);
error = vfs_mountroot_devfs(td, &mp);
+ printf("%s: vfs_mountroot_devfs() done, error %d\n", __func__, error);
while (!error) {
+ printf("%s: before vfs_mountroot_parse()\n", __func__);
error = vfs_mountroot_parse(sb, mp);
+ printf("%s: vfs_mountroot_parse() done, error %d\n", __func__, error);
if (!error) {
error = vfs_mountroot_shuffle(td, mp);
if (!error) {
@@ -990,6 +1018,7 @@
mtx_unlock(&root_holds_mtx);
EVENTHANDLER_INVOKE(mountroot);
+ printf("%s: done\n", __func__);
}
static struct mntarg *
Index: sys/kern/vfs_subr.c
===================================================================
--- sys/kern/vfs_subr.c
+++ sys/kern/vfs_subr.c
@@ -2709,10 +2709,12 @@
* If FORCECLOSE is set, forcibly close the vnode.
*/
if (vp->v_usecount == 0 || (flags & FORCECLOSE)) {
+#if 0
VNASSERT(vp->v_usecount == 0 ||
vp->v_op != &devfs_specops ||
(vp->v_type != VCHR && vp->v_type != VBLK), vp,
("device VNODE %p is FORCECLOSED", vp));
+#endif
vgonel(vp);
} else {
busy++;
@@ -3554,7 +3556,7 @@
void
vfs_unmountall(void)
{
- struct mount *mp;
+ struct mount *mp, *tmp;
struct thread *td;
int error;
@@ -3564,10 +3566,19 @@
/*
* Since this only runs when rebooting, it is not interlocked.
*/
- while(!TAILQ_EMPTY(&mountlist)) {
- mp = TAILQ_LAST(&mountlist, mntlist);
+ TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mntlist, mnt_list, tmp) {
+#if 0
+ if (strcmp(mp->mnt_stat.f_mntonname, "/tmp") == 0) {
+ printf("%s: skipping %s\n", __func__, mp->mnt_stat.f_mntonname);
+ continue;
+ }
+#endif
vfs_ref(mp);
- error = dounmount(mp, MNT_FORCE, td);
+ printf("%s: unmounting %s\n", __func__, mp->mnt_stat.f_mntonname);
+ if (strcmp(mp->mnt_stat.f_mntonname, "/dev") == 0)
+ error = dounmount(mp, 0, td);
+ else
+ error = dounmount(mp, MNT_FORCE, td);
if (error != 0) {
TAILQ_REMOVE(&mountlist, mp, mnt_list);
/*
Index: sys/sys/reboot.h
===================================================================
--- sys/sys/reboot.h
+++ sys/sys/reboot.h
@@ -59,6 +59,7 @@
#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */
#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */
#define RB_PAUSE 0x100000 /* pause after each output line during probe */
+#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */
#define RB_MULTIPLE 0x20000000 /* use multiple consoles */
#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Aug 29, 2:59 AM (12 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37443178
Default Alt Text
D2698.id7066.diff (17 KB)
Attached To
Mode
D2698: Add "reboot -r" support - unmount old root and mount a new one.
Attached
Detach File
Event Timeline
Log In to Comment