*
* This program runs from the initrd before the final /sbin/init.
*
- * Milestone B: Basic early init - checks for /newroot and executes /sbin/init
- * Milestone C: Will add pivot_root and mount moving
+ * Milestone C: Full pivot_root implementation
+ * - Move virtual filesystems (/dev, /proc, /tmp) to /newroot
+ * - Perform pivot_root to make /newroot the new root
+ * - Unmount old root
+ * - Execute /sbin/init from the new root
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <errno.h>
+/* MS_MOVE is not defined in ulibc, define it manually */
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
#define NEWROOT "/newroot"
+#define OLDROOT "/oldroot"
+
+static int mkdir_p(const char* path) {
+ char tmp[256];
+ char* p = NULL;
+ size_t len;
+
+ snprintf(tmp, sizeof(tmp), "%s", path);
+ len = strlen(tmp);
+ if (tmp[len - 1] == '/') {
+ tmp[len - 1] = 0;
+ }
+
+ for (p = tmp + 1; *p; p++) {
+ if (*p == '/') {
+ *p = 0;
+ if (mkdir(tmp, 0755) < 0 && errno != EEXIST) {
+ perror("mkdir_p");
+ return -1;
+ }
+ *p = '/';
+ }
+ }
+
+ if (mkdir(tmp, 0755) < 0 && errno != EEXIST) {
+ perror("mkdir_p");
+ return -1;
+ }
+
+ return 0;
+}
static int is_mounted(const char* path) {
FILE* fp = fopen("/proc/mounts", "r");
return found;
}
+static int mount_virtual_fs(const char* fstype, const char* target) {
+ if (is_mounted(target)) {
+ printf("[init] %s already mounted on %s\n", fstype, target);
+ return 0;
+ }
+
+ if (mkdir_p(target) < 0) {
+ return -1;
+ }
+
+ printf("[init] mounting %s on %s\n", fstype, target);
+ if (mount("none", target, fstype, 0, NULL) < 0) {
+ perror("mount");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int move_mount(const char* src, const char* dst) {
+ printf("[init] moving mount from %s to %s\n", src, dst);
+
+ if (mkdir_p(dst) < 0) {
+ return -1;
+ }
+
+ /* Linux uses MS_MOVE flag for mount --move */
+ if (mount(src, dst, NULL, MS_MOVE, NULL) < 0) {
+ perror("mount --move");
+ return -1;
+ }
+
+ return 0;
+}
+
int main(int argc __attribute__((unused)), char** argv) {
printf("[init] early userspace init starting\n");
return 1;
}
- printf("[init] /newroot is mounted by kernel\n");
- printf("[init] Milestone B: basic handoff - executing /sbin/init directly\n");
- printf("[init] (pivot_root and mount moving will be added in Milestone C)\n");
+ printf("[init] /newroot is mounted, preparing handoff\n");
+
+ /* Create /oldroot for pivot_root */
+ if (mkdir(OLDROOT, 0755) < 0 && errno != EEXIST) {
+ perror("mkdir /oldroot");
+ return 1;
+ }
+
+ /* Move virtual filesystems to /newroot if they exist on old root */
+ const char* virtual_mounts[] = { "/dev", "/proc", "/tmp", NULL };
+ for (int i = 0; virtual_mounts[i]; i++) {
+ char src[128];
+ char dst[128];
+
+ snprintf(src, sizeof(src), "%s", virtual_mounts[i]);
+ snprintf(dst, sizeof(dst), "%s%s", NEWROOT, virtual_mounts[i]);
+
+ if (is_mounted(src)) {
+ if (move_mount(src, dst) < 0) {
+ printf("[init] warning: failed to move %s, will remount\n", src);
+ /* Fallback: mount directly on new root */
+ const char* fstype = NULL;
+ if (strcmp(src, "/dev") == 0) fstype = "devfs";
+ else if (strcmp(src, "/proc") == 0) fstype = "procfs";
+ else if (strcmp(src, "/tmp") == 0) fstype = "tmpfs";
+
+ if (fstype) {
+ mount_virtual_fs(fstype, dst);
+ }
+ }
+ } else {
+ /* Not mounted on old root, mount directly on new root */
+ const char* fstype = NULL;
+ if (strcmp(src, "/dev") == 0) fstype = "devfs";
+ else if (strcmp(src, "/proc") == 0) fstype = "procfs";
+ else if (strcmp(src, "/tmp") == 0) fstype = "tmpfs";
+
+ if (fstype) {
+ mount_virtual_fs(fstype, dst);
+ }
+ }
+ }
+
+ /* Perform pivot_root */
+ printf("[init] performing pivot_root(%s, %s)\n", NEWROOT, OLDROOT);
+ if (pivot_root(NEWROOT, OLDROOT) < 0) {
+ perror("pivot_root");
+ printf("[init] pivot_root failed, falling back to /sbin/init\n");
+ execve("/sbin/init", argv, environ);
+ return 1;
+ }
+
+ /* Change to new root */
+ if (chdir("/") < 0) {
+ perror("chdir /");
+ return 1;
+ }
+
+ /* Unmount old root */
+ printf("[init] unmounting %s\n", OLDROOT);
+ if (umount2(OLDROOT, 0) < 0) {
+ perror("umount2 /oldroot");
+ printf("[init] warning: failed to unmount old root\n");
+ }
- /* For now, just execute /sbin/init from the initrd overlay */
- /* The kernel has already mounted the real root on /newroot */
- /* Future Milestone C will implement pivot_root to make /newroot the actual root */
+ /* Execute final init from new root */
+ printf("[init] executing /sbin/init from new root\n");
execve("/sbin/init", argv, environ);
perror("execve /sbin/init");