/* * Handle initrd, thus putting the backwards into backwards compatible */ #include #include #include #include #include #include #include #include #include #include "do_mounts.h" #include "kinit.h" #include "xpio.h" #define BUF_SIZE 65536 /* Should be a power of 2 */ /* * Copy the initrd to /dev/ram0, copy from the end to the beginning * to avoid taking 2x the memory. */ static int rd_copy_uncompressed(int ffd, int dfd) { char buffer[BUF_SIZE]; off_t bytes; struct stat st; dprintf("kinit: uncompressed initrd\n"); if (ffd < 0 || fstat(ffd, &st) || !S_ISREG(st.st_mode) || (bytes = st.st_size) == 0) return -1; while (bytes) { ssize_t blocksize = ((bytes - 1) & (BUF_SIZE - 1)) + 1; off_t offset = bytes - blocksize; dprintf("kinit: copying %zd bytes at offset %llu\n", blocksize, offset); if (xpread(ffd, buffer, blocksize, offset) != blocksize || xpwrite(dfd, buffer, blocksize, offset) != blocksize) return -1; ftruncate(ffd, offset); /* Free up memory */ bytes = offset; } return 0; } static int rd_copy_image(const char *path) { int ffd = open(path, O_RDONLY); int rv = -1; unsigned char gzip_magic[2]; if (ffd < 0) goto barf; if (xpread(ffd, gzip_magic, 2, 0) == 2 && gzip_magic[0] == 037 && gzip_magic[1] == 0213) { FILE *wfd = fopen("/dev/ram0", "w"); if (!wfd) goto barf; rv = load_ramdisk_compressed(path, wfd, 0); fclose(wfd); } else { int dfd = open("/dev/ram0", O_WRONLY); if (dfd < 0) goto barf; rv = rd_copy_uncompressed(ffd, dfd); close(dfd); } barf: if (ffd >= 0) close(ffd); return rv; } /* * Run /linuxrc, for emulation of old-style initrd */ static int run_linuxrc(int argc, char *argv[], dev_t root_dev) { int root_fd, old_fd; pid_t pid; long realroot = Root_RAM0; const char *ramdisk_name = "/dev/ram0"; FILE *fp; dprintf("kinit: mounting initrd\n"); mkdir("/root", 0700); if (!mount_block(ramdisk_name, "/root", NULL, MS_VERBOSE, NULL)) return -errno; /* Write the current "real root device" out to procfs */ dprintf("kinit: real_root_dev = %#x\n", root_dev); fp = fopen("/proc/sys/kernel/real-root-dev", "w"); fprintf(fp, "%u", root_dev); fclose(fp); mkdir("/old", 0700); root_fd = open_cloexec("/", O_RDONLY | O_DIRECTORY, 0); old_fd = open_cloexec("/old", O_RDONLY | O_DIRECTORY, 0); if (root_fd < 0 || old_fd < 0) return -errno; if (chdir("/root") || mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".")) return -errno; pid = vfork(); if (pid == 0) { setsid(); /* Looks like linuxrc doesn't get the init environment or parameters. Weird, but so is the whole linuxrc bit. */ execl("/linuxrc", "linuxrc", NULL); _exit(255); } else if (pid > 0) { dprintf("kinit: Waiting for linuxrc to complete...\n"); while (waitpid(pid, NULL, 0) != pid) ; dprintf("kinit: linuxrc done\n"); } else { return -errno; } if (fchdir(old_fd) || mount("/", ".", NULL, MS_MOVE, NULL) || fchdir(root_fd) || chroot(".")) return -errno; close(root_fd); close(old_fd); getintfile("/proc/sys/kernel/real-root-dev", &realroot); /* If realroot is Root_RAM0, then the initrd did any necessary work */ if (realroot == Root_RAM0) { if (mount("/old", "/root", NULL, MS_MOVE, NULL)) return -errno; } else { mount_root(argc, argv, (dev_t) realroot, NULL); /* If /root/initrd exists, move the initrd there, otherwise discard */ if (!mount("/old", "/root/initrd", NULL, MS_MOVE, NULL)) { /* We're good */ } else { int olddev = open(ramdisk_name, O_RDWR); umount2("/old", MNT_DETACH); if (olddev < 0 || ioctl(olddev, BLKFLSBUF, (long)0) || close(olddev)) { fprintf(stderr, "%s: Cannot flush initrd contents\n", progname); } } } rmdir("/old"); return 0; } int initrd_load(int argc, char *argv[], dev_t root_dev) { if (access("/initrd.image", R_OK)) return 0; /* No initrd */ dprintf("kinit: initrd found\n"); create_dev("/dev/ram0", Root_RAM0); if (rd_copy_image("/initrd.image") || unlink("/initrd.image")) { fprintf(stderr, "%s: initrd installation failed (too big?)\n", progname); return 0; /* Failed to copy initrd */ } dprintf("kinit: initrd copied\n"); if (root_dev == Root_MULTI) { dprintf("kinit: skipping linuxrc: incompatible with multiple roots\n"); /* Mounting initrd as ordinary root */ return 0; } if (root_dev != Root_RAM0) { int err; dprintf("kinit: running linuxrc\n"); err = run_linuxrc(argc, argv, root_dev); if (err) fprintf(stderr, "%s: running linuxrc: %s\n", progname, strerror(-err)); return 1; /* initrd is root, or run_linuxrc took care of it */ } else { dprintf("kinit: permament (or pivoting) initrd, not running linuxrc\n"); return 0; /* Mounting initrd as ordinary root */ } }