Author: trasz
Date: Fri Sep 18 17:32:22 2015
New Revision: 287964
URL: https://svnweb.freebsd.org/changeset/base/287964
Log:
Kernel part of reroot support - a way to change rootfs without reboot.
Note that the mountlist manipulations are somewhat fragile, and not very
pretty. The reason for this is to avoid changing vfs_mountroot(), which
is (obviously) rather mission-critical, but not very well documented,
and thus hard to test properly. It might be possible to rework it to use
its own simple root mount mechanism instead of vfs_mountroot().
Reviewed by: kib@
MFC after:1 month
Sponsored by: The FreeBSD Foundation
Differential Revision:https://reviews.freebsd.org/D2698
Modified:
head/lib/libc/sys/reboot.2
head/sys/kern/kern_shutdown.c
head/sys/kern/vfs_mountroot.c
head/sys/sys/reboot.h
Modified: head/lib/libc/sys/reboot.2
==
--- head/lib/libc/sys/reboot.2 Fri Sep 18 17:29:24 2015(r287963)
+++ head/lib/libc/sys/reboot.2 Fri Sep 18 17:32:22 2015(r287964)
@@ -113,6 +113,13 @@ Normally, the disks are sync'd (see
before the processor is halted or rebooted.
This option may be useful if file system changes have been made manually
or if the processor is on fire.
+.It Dv RB_REROOT
+Instead of rebooting, unmount all filesystems except the one containing
+currently-running executable, and mount root filesystem using the same
+mechanism which is used during normal boot, based on
+vfs.root.mountfrom
+.Xr kenv 8
+variable.
.It Dv RB_RDONLY
Initially mount the root file system read-only.
This is currently the default, and this option has been deprecated.
Modified: head/sys/kern/kern_shutdown.c
==
--- head/sys/kern/kern_shutdown.c Fri Sep 18 17:29:24 2015
(r287963)
+++ head/sys/kern/kern_shutdown.c Fri Sep 18 17:32:22 2015
(r287964)
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include
#include
#include
+#include
#include
#include
#include
@@ -150,10 +151,16 @@ static struct dumperinfo dumper; /* our
static struct pcb dumppcb; /* Registers. */
lwpid_t dumptid; /* Thread ID. */
+static struct cdevsw reroot_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name= "reroot",
+};
+
static void poweroff_wait(void *, int);
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
@@ -173,6 +180,26 @@ shutdown_conf(void *unused)
SYSINIT(shutdown_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, shutdown_conf, NULL);
/*
+ * The only reason this exists is to create the /dev/reroot/ directory,
+ * used by reroot code in init(8) as a mountpoint for tmpfs.
+ */
+static void
+reroot_conf(void *unused)
+{
+ int error;
+ struct cdev *cdev;
+
+ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, ,
+ _cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "reroot/reroot");
+ if (error != 0) {
+ printf("%s: failed to create device node, error %d",
+ __func__, error);
+ }
+}
+
+SYSINIT(reroot_conf, SI_SUB_DEVFS, SI_ORDER_ANY, reroot_conf, NULL);
+
+/*
* The system call that results in a reboot.
*/
/* ARGSUSED */
@@ -188,9 +215,13 @@ sys_reboot(struct thread *td, struct reb
if (error == 0)
error = priv_check(td, PRIV_REBOOT);
if (error == 0) {
- mtx_lock();
- kern_reboot(uap->opt);
- mtx_unlock();
+ if (uap->opt & RB_REROOT) {
+ error = kern_reroot();
+ } else {
+ mtx_lock();
+ kern_reboot(uap->opt);
+ mtx_unlock();
+ }
}
return (error);
}
@@ -336,6 +367,102 @@ kern_reboot(int howto)
}
/*
+ * The system call that results in changing the rootfs.
+ */
+static int
+kern_reroot(void)
+{
+ struct vnode *oldrootvnode, *vp;
+ struct mount *mp, *devmp;
+ int error;
+
+ if (curproc != initproc)
+ return (EPERM);
+
+ /*
+* Mark the filesystem containing currently-running executable
+* (the temporary copy of init(8)) busy.
+*/
+ vp = curproc->p_textvp;
+ error = vn_lock(vp, LK_SHARED);
+ if (error != 0)
+ return (error);
+ mp = vp->v_mount;
+ error = vfs_busy(mp, MBF_NOWAIT);
+ if (error != 0) {
+ vfs_ref(mp);
+ VOP_UNLOCK(vp, 0);
+ error = vfs_busy(mp, 0);
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ vfs_rel(mp);
+ if (error != 0) {
+ VOP_UNLOCK(vp, 0);
+