summaryrefslogtreecommitdiffstats
path: root/helper
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-08-20 19:23:03 +0100
committerRichard Jones <rjones@redhat.com>2010-08-24 13:17:41 +0100
commit89e336ee166be538e376d288fb2b3fbbffd66d4c (patch)
treecd6ddae9a5ef7a0e572db824e2755972991dc074 /helper
parent53bf430e26f4a53837bd38b58a427079caab3d4b (diff)
downloadfebootstrap-89e336ee166be538e376d288fb2b3fbbffd66d4c.tar.gz
febootstrap-89e336ee166be538e376d288fb2b3fbbffd66d4c.tar.xz
febootstrap-89e336ee166be538e376d288fb2b3fbbffd66d4c.zip
Implement ext2 output module.
Diffstat (limited to 'helper')
-rw-r--r--helper/Makefile.am23
-rwxr-xr-xhelper/elf-default-arch27
-rw-r--r--helper/ext2.c447
-rw-r--r--helper/ext2cpio.c358
-rw-r--r--helper/ext2initrd.c263
-rw-r--r--helper/ext2internal.h45
-rw-r--r--helper/init.c195
-rw-r--r--helper/main.c4
8 files changed, 1352 insertions, 10 deletions
diff --git a/helper/Makefile.am b/helper/Makefile.am
index 3af1d64..96b5581 100644
--- a/helper/Makefile.am
+++ b/helper/Makefile.am
@@ -25,11 +25,29 @@ febootstrap_supermin_helper_SOURCES = \
appliance.c \
cpio.c \
ext2.c \
+ ext2cpio.c \
+ ext2initrd.c \
+ ext2internal.h \
kernel.c \
main.c \
utils.c
febootstrap_supermin_helper_CFLAGS = -Wall -I../lib
-febootstrap_supermin_helper_LDADD = $(LTLIBINTL) -L../lib -lgnu
+febootstrap_supermin_helper_LDADD = \
+ ext2init.o -lext2fs -lcom_err $(LTLIBINTL) -L../lib -lgnu
+
+# init "script" used by ext2 initrd.
+noinst_PROGRAMS = init
+init_SOURCES = init.c
+init_CFLAGS = -static
+init_LDFLAGS = -all-static
+
+# http://www.doof.me.uk/2010/05/07/cute-objcopy-hack/
+ELF_DEFAULT_ARCH = $(shell $(srcdir)/elf-default-arch)
+ext2init.o: init
+ strip --strip-all $<
+ @file $< | grep -isq static || \
+ (echo "*** error: init is not staticly linked"; exit 1)
+ objcopy -I binary -B i386 -O $(ELF_DEFAULT_ARCH) $< $@
man_MANS = \
febootstrap-supermin-helper.8
@@ -50,4 +68,5 @@ endif
EXTRA_DIST = \
febootstrap-supermin-helper.8 febootstrap-supermin-helper.txt \
- febootstrap-supermin-helper.pod
+ febootstrap-supermin-helper.pod \
+ elf-default-arch
diff --git a/helper/elf-default-arch b/helper/elf-default-arch
new file mode 100755
index 0000000..0adc351
--- /dev/null
+++ b/helper/elf-default-arch
@@ -0,0 +1,27 @@
+#!/bin/sh -
+# Copyright (C) 2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Calculate the default ELF object architecture for this format.
+# There doesn't seem to be an easy way to derive this from binutils,
+# so instead we hard code it.
+
+case $(uname -m) in
+ i[3456]86) echo elf32-i386 ;;
+ x86_64) echo elf64-x86-64 ;;
+ *)
+ echo "This architecture is not recognized. Please update helper/elf-default-arch."
+esac
diff --git a/helper/ext2.c b/helper/ext2.c
index 2900221..9d60da9 100644
--- a/helper/ext2.c
+++ b/helper/ext2.c
@@ -21,26 +21,461 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
#include <sys/stat.h>
#include <assert.h>
#include "error.h"
#include "fts_.h"
+#include "xvasprintf.h"
#include "helper.h"
+#include "ext2internal.h"
+
+ext2_filsys fs;
+
+/* The ext2 image that we build always has a fixed size, and we 'hope'
+ * that the files fit in (otherwise we'll get an error). Note that
+ * the file is sparsely allocated.
+ *
+ * The downside of allocating a very large initial disk is that the
+ * fixed overhead of ext2 is larger (since ext2 calculates it based on
+ * the size of the disk). For a 1GB disk the overhead is
+ * approximately 16MB.
+ *
+ * In future, make this configurable, or determine it from the input
+ * files (XXX).
+ */
+#define APPLIANCE_SIZE (1024*1024*1024)
static void
ext2_start (const char *appliance,
const char *modpath, const char *initrd)
{
- abort ();
+ initialize_ext2_error_table ();
+
+ /* Make the initrd. */
+ ext2_make_initrd (modpath, initrd);
+
+ /* Make the appliance sparse image. */
+ int fd = open (appliance, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
+ if (fd == -1)
+ error (EXIT_FAILURE, errno, "open: %s", appliance);
+
+ if (lseek (fd, APPLIANCE_SIZE - 1, SEEK_SET) == -1)
+ error (EXIT_FAILURE, errno, "lseek");
+
+ char c = 0;
+ if (write (fd, &c, 1) != 1)
+ error (EXIT_FAILURE, errno, "write");
+
+ if (close (fd) == -1)
+ error (EXIT_FAILURE, errno, "close");
+
+ /* Run mke2fs on the file.
+ * XXX Quoting, but this string doesn't come from an untrusted source.
+ */
+ char *cmd = xasprintf ("%s -t ext2 -F%s '%s'",
+ MKE2FS,
+ verbose >= 2 ? "" : "q",
+ appliance);
+ int r = system (cmd);
+ if (r == -1 || WEXITSTATUS (r) != 0)
+ error (EXIT_FAILURE, 0, "%s: failed", cmd);
+ free (cmd);
+
+ if (verbose)
+ print_timestamped_message ("finished mke2fs");
+
+ /* Open the filesystem. */
+ errcode_t err =
+ ext2fs_open (appliance, EXT2_FLAG_RW, 0, 0, unix_io_manager, &fs);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_open: %s", error_message (err));
+
+ /* Bitmaps are not loaded by default, so load them. ext2fs_close will
+ * write out any changes.
+ */
+ err = ext2fs_read_bitmaps (fs);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_read_bitmaps: %s", error_message (err));
+}
+
+static void
+ext2_end (void)
+{
+ /* Write out changes and close. */
+ errcode_t err = ext2fs_close (fs);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_close: %s", error_message (err));
+}
+
+void
+ext2_mkdir (ext2_ino_t dir_ino, const char *dirname, const char *basename,
+ mode_t mode, uid_t uid, gid_t gid,
+ time_t ctime, time_t atime, time_t mtime)
+{
+ errcode_t err;
+
+ mode = LINUX_S_IFDIR | (mode & 0777);
+
+ /* Does the directory exist? This is legitimate: we just skip
+ * this case.
+ */
+ ext2_ino_t ino;
+ err = ext2fs_namei (fs, EXT2_ROOT_INO, dir_ino, basename, &ino);
+ if (err == 0)
+ return; /* skip */
+
+ /* Otherwise, create it. */
+ err = ext2fs_new_inode (fs, dir_ino, mode, 0, &ino);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_new_inode: %s", error_message (err));
+
+ err = ext2fs_mkdir (fs, dir_ino, ino, basename);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_mkdir: %s/%s: %s",
+ dirname, basename, error_message (err));
+
+ /* Copy the final permissions, UID etc. to the inode. */
+ struct ext2_inode inode;
+ err = ext2fs_read_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
+ inode.i_mode = mode;
+ inode.i_uid = uid;
+ inode.i_gid = gid;
+ inode.i_ctime = ctime;
+ inode.i_atime = atime;
+ inode.i_mtime = mtime;
+ err = ext2fs_write_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
+}
+
+void
+ext2_empty_inode (ext2_ino_t dir_ino, const char *dirname, const char *basename,
+ mode_t mode, uid_t uid, gid_t gid,
+ time_t ctime, time_t atime, time_t mtime,
+ int major, int minor, int dir_ft, ext2_ino_t *ino_ret)
+{
+ errcode_t err;
+ struct ext2_inode inode;
+ ext2_ino_t ino;
+
+ err = ext2fs_new_inode (fs, dir_ino, mode, 0, &ino);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_new_inode: %s", error_message (err));
+
+ memset (&inode, 0, sizeof inode);
+ inode.i_mode = mode;
+ inode.i_uid = uid;
+ inode.i_gid = gid;
+ inode.i_blocks = 0;
+ inode.i_links_count = 1;
+ inode.i_ctime = ctime;
+ inode.i_atime = atime;
+ inode.i_mtime = mtime;
+ inode.i_size = 0;
+ inode.i_block[0] = (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
+
+ err = ext2fs_write_new_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
+
+ ext2_link (dir_ino, basename, ino, dir_ft);
+
+ ext2fs_inode_alloc_stats2 (fs, ino, 1, 0);
+
+ if (ino_ret)
+ *ino_ret = ino;
+}
+
+/* You must create the file first with ext2_empty_inode. */
+void
+ext2_write_file (ext2_ino_t ino, const char *buf, size_t size)
+{
+ errcode_t err;
+ ext2_file_t file;
+ err = ext2fs_file_open2 (fs, ino, NULL, EXT2_FILE_WRITE, &file);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_file_open2: %s", error_message (err));
+
+ /* ext2fs_file_write cannot deal with partial writes. You have
+ * to write the entire file in a single call.
+ */
+ unsigned int written;
+ err = ext2fs_file_write (file, buf, size, &written);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_file_write: %s", error_message (err));
+ if ((size_t) written != size)
+ error (EXIT_FAILURE, 0,
+ "ext2fs_file_write: size = %zu != written = %u\n",
+ size, written);
+
+ err = ext2fs_file_flush (file);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_file_flush: %s", error_message (err));
+ err = ext2fs_file_close (file);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_file_close: %s", error_message (err));
+
+ /* Update the true size in the inode. */
+ struct ext2_inode inode;
+ err = ext2fs_read_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
+ inode.i_size = size;
+ err = ext2fs_write_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
+}
+
+/* This is just a wrapper around ext2fs_link which calls
+ * ext2fs_expand_dir as necessary if the directory fills up. See
+ * definition of expand_dir in the sources of debugfs.
+ */
+void
+ext2_link (ext2_ino_t dir_ino, const char *basename, ext2_ino_t ino, int dir_ft)
+{
+ errcode_t err;
+
+ again:
+ err = ext2fs_link (fs, dir_ino, basename, ino, dir_ft);
+
+ if (err == EXT2_ET_DIR_NO_SPACE) {
+ err = ext2fs_expand_dir (fs, dir_ino);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2_link: ext2fs_expand_dir: %s: %s",
+ basename, error_message (err));
+ goto again;
+ }
+
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_link: %s: %s",
+ basename, error_message (err));
+}
+
+static int
+release_block (ext2_filsys fs, blk_t *blocknr,
+ int blockcnt, void *private)
+{
+ blk_t block;
+
+ block = *blocknr;
+ ext2fs_block_alloc_stats (fs, block, -1);
+ return 0;
+}
+
+/* unlink or rmdir path, if it exists. */
+void
+ext2_clean_path (ext2_ino_t dir_ino,
+ const char *dirname, const char *basename,
+ int isdir)
+{
+ errcode_t err;
+
+ ext2_ino_t ino;
+ err = ext2fs_lookup (fs, dir_ino, basename, strlen (basename),
+ NULL, &ino);
+ if (err == EXT2_ET_FILE_NOT_FOUND)
+ return;
+
+ if (!isdir) {
+ struct ext2_inode inode;
+ err = ext2fs_read_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_read_inode: %s", error_message (err));
+ inode.i_links_count--;
+ err = ext2fs_write_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
+
+ err = ext2fs_unlink (fs, dir_ino, basename, 0, 0);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_unlink_inode: %s", error_message (err));
+
+ if (inode.i_links_count == 0) {
+ inode.i_dtime = time (NULL);
+ err = ext2fs_write_inode (fs, ino, &inode);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2fs_write_inode: %s", error_message (err));
+
+ if (ext2fs_inode_has_valid_blocks (&inode))
+ ext2fs_block_iterate (fs, ino, BLOCK_FLAG_READ_ONLY, NULL,
+ release_block, NULL);
+
+ ext2fs_inode_alloc_stats2 (fs, ino, -1, isdir);
+ }
+ }
+ /* else it's a directory, what to do? XXX */
+}
+
+/* Read in the whole file into memory. Check the size is still 'size'. */
+static char *
+read_whole_file (const char *filename, size_t size)
+{
+ char *buf = malloc (size);
+ if (buf == NULL)
+ error (EXIT_FAILURE, errno, "malloc");
+
+ int fd = open (filename, O_RDONLY);
+ if (fd == -1)
+ error (EXIT_FAILURE, errno, "open: %s", filename);
+
+ size_t n = 0;
+ char *p = buf;
+
+ while (n < size) {
+ ssize_t r = read (fd, p, size - n);
+ if (r == -1)
+ error (EXIT_FAILURE, errno, "read: %s", filename);
+ if (r == 0)
+ error (EXIT_FAILURE, 0,
+ "error: file has changed size unexpectedly: %s", filename);
+ n += r;
+ p += r;
+ }
+
+ if (close (fd) == -1)
+ error (EXIT_FAILURE, errno, "close: %s", filename);
+
+ return buf;
+}
+
+/* Add a file (or directory etc) from the host. */
+static void
+ext2_file_stat (const char *orig_filename, const struct stat *statbuf)
+{
+ errcode_t err;
+
+ if (verbose >= 2)
+ fprintf (stderr, "ext2_file_stat %s 0%o\n",
+ orig_filename, statbuf->st_mode);
+
+ /* Sanity check the path. These rules are always true for the paths
+ * passed to us here from the appliance layer. The assertions just
+ * verify that the rules haven't changed.
+ */
+ size_t n = strlen (orig_filename);
+ assert (n <= PATH_MAX);
+ assert (n > 0);
+ assert (orig_filename[0] == '/'); /* always absolute path */
+ assert (n == 1 || orig_filename[n-1] != '/'); /* no trailing slash */
+
+ /* Don't make the root directory, it always exists. This simplifies
+ * the code that follows.
+ */
+ if (n == 1) return;
+
+ const char *dirname, *basename;
+ const char *p = strrchr (orig_filename, '/');
+ ext2_ino_t dir_ino;
+ if (orig_filename == p) { /* "/foo" */
+ dirname = "/";
+ basename = orig_filename+1;
+ dir_ino = EXT2_ROOT_INO;
+ } else { /* "/foo/bar" */
+ dirname = strndup (orig_filename, p-orig_filename);
+ basename = p+1;
+
+ /* Look up the parent directory. */
+ err = ext2fs_namei (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, dirname, &dir_ino);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2: parent directory not found: %s: %s",
+ dirname, error_message (err));
+ }
+
+ ext2_clean_path (dir_ino, dirname, basename, S_ISDIR (statbuf->st_mode));
+
+ int dir_ft;
+
+ /* Create regular file. */
+ if (S_ISREG (statbuf->st_mode)) {
+ /* XXX Hard links get duplicated here. */
+ ext2_ino_t ino;
+ ext2_empty_inode (dir_ino, dirname, basename,
+ statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
+ statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
+ 0, 0, EXT2_FT_REG_FILE, &ino);
+
+ if (statbuf->st_size > 0) {
+ char *buf = read_whole_file (orig_filename, statbuf->st_size);
+ ext2_write_file (ino, buf, statbuf->st_size);
+ free (buf);
+ }
+ }
+ /* Create a symlink. */
+ else if (S_ISLNK (statbuf->st_mode)) {
+ ext2_ino_t ino;
+ ext2_empty_inode (dir_ino, dirname, basename,
+ statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
+ statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
+ 0, 0, EXT2_FT_SYMLINK, &ino);
+
+ char buf[PATH_MAX+1];
+ ssize_t r = readlink (orig_filename, buf, sizeof buf);
+ if (r == -1)
+ error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
+ ext2_write_file (ino, buf, r);
+ }
+ /* Create directory. */
+ else if (S_ISDIR (statbuf->st_mode))
+ ext2_mkdir (dir_ino, dirname, basename,
+ statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
+ statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime);
+ /* Create a special file. */
+ else if (S_ISBLK (statbuf->st_mode)) {
+ dir_ft = EXT2_FT_BLKDEV;
+ goto make_special;
+ }
+ else if (S_ISCHR (statbuf->st_mode)) {
+ dir_ft = EXT2_FT_CHRDEV;
+ goto make_special;
+ } else if (S_ISFIFO (statbuf->st_mode)) {
+ dir_ft = EXT2_FT_FIFO;
+ goto make_special;
+ } else if (S_ISSOCK (statbuf->st_mode)) {
+ dir_ft = EXT2_FT_SOCK;
+ make_special:
+ ext2_empty_inode (dir_ino, dirname, basename,
+ statbuf->st_mode, statbuf->st_uid, statbuf->st_gid,
+ statbuf->st_ctime, statbuf->st_atime, statbuf->st_mtime,
+ major (statbuf->st_rdev), minor (statbuf->st_rdev),
+ dir_ft, NULL);
+ }
+}
+
+static void
+ext2_file (const char *filename)
+{
+ struct stat statbuf;
+
+ if (lstat (filename, &statbuf) == -1)
+ error (EXIT_FAILURE, errno, "lstat: %s", filename);
+ ext2_file_stat (filename, &statbuf);
+}
+
+/* In theory this could be optimized to avoid a namei lookup, but
+ * it probably wouldn't make much difference.
+ */
+static void
+ext2_fts_entry (FTSENT *entry)
+{
+ if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
+ ext2_file (entry->fts_path);
+ else
+ ext2_file_stat (entry->fts_path, entry->fts_statp);
}
struct writer ext2_writer = {
.wr_start = ext2_start,
- /* .wr_end = ,
- .wr_file = ,
- .wr_file_stat = ,
- .wr_fts_entry = ,
- .wr_cpio_file = , */
+ .wr_end = ext2_end,
+ .wr_file = ext2_file,
+ .wr_file_stat = ext2_file_stat,
+ .wr_fts_entry = ext2_fts_entry,
+ .wr_cpio_file = ext2_cpio_file,
};
diff --git a/helper/ext2cpio.c b/helper/ext2cpio.c
new file mode 100644
index 0000000..7690553
--- /dev/null
+++ b/helper/ext2cpio.c
@@ -0,0 +1,358 @@
+/* febootstrap-supermin-helper reimplementation in C.
+ * Copyright (C) 2009-2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "error.h"
+
+#include "helper.h"
+#include "ext2internal.h"
+
+/* This function must unpack the cpio file and add the files it
+ * contains to the ext2 filesystem. Essentially this is doing the
+ * same thing as the kernel init/initramfs.c code. Note that we
+ * assume that the cpio is uncompressed newc format and can't/won't
+ * deal with anything else. All this cpio parsing code is copied to
+ * some extent from init/initramfs.c in the kernel.
+ */
+#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
+
+static unsigned long cpio_ino, nlink;
+static mode_t mode;
+static unsigned long body_len, name_len;
+static uid_t uid;
+static gid_t gid;
+static time_t mtime;
+static int dev_major, dev_minor, rdev_major, rdev_minor;
+static loff_t curr, next_header;
+static FILE *fp;
+
+static void parse_header (char *s);
+static int parse_next_entry (void);
+static void skip_to_next_header (void);
+static void read_file (void);
+static char *read_whole_body (void);
+static ext2_ino_t maybe_link (void);
+static void add_link (ext2_ino_t real_ino);
+static void clear_links (void);
+
+void
+ext2_cpio_file (const char *cpio_file)
+{
+ fp = fopen (cpio_file, "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, "open: %s", cpio_file);
+
+ curr = 0;
+ while (parse_next_entry ())
+ ;
+
+ fclose (fp);
+}
+
+static int
+parse_next_entry (void)
+{
+ clearerr (fp);
+
+ char header[110];
+
+ /* Skip padding and synchronize with the next header. */
+ again:
+ if (fread (&header[0], 4, 1, fp) != 1) {
+ if (feof (fp))
+ return 0;
+ error (EXIT_FAILURE, errno, "read failure reading cpio file");
+ }
+ curr += 4;
+ if (memcmp (header, "\0\0\0\0", 4) == 0)
+ goto again;
+
+ /* Read the rest of the header field. */
+ if (fread (&header[4], sizeof header - 4, 1, fp) != 1)
+ error (EXIT_FAILURE, errno, "read failure reading cpio file");
+ curr += sizeof header - 4;
+
+ if (verbose >= 2)
+ fprintf (stderr, "cpio header %s\n", header);
+
+ if (memcmp (header, "070707", 6) == 0)
+ error (EXIT_FAILURE, 0, "incorrect cpio method: use -H newc option");
+ if (memcmp (header, "070701", 6) != 0)
+ error (EXIT_FAILURE, 0, "input is not a cpio file");
+
+ parse_header (header);
+
+ next_header = curr + N_ALIGN(name_len) + body_len;
+ next_header = (next_header + 3) & ~3;
+ if (name_len <= 0 || name_len > PATH_MAX)
+ skip_to_next_header ();
+ else if (S_ISLNK (mode)) {
+ if (body_len <= 0 || body_len > PATH_MAX)
+ skip_to_next_header ();
+ else
+ read_file ();
+ }
+ else if (!S_ISREG (mode) && body_len > 0)
+ skip_to_next_header (); /* only regular files have bodies */
+ else
+ read_file (); /* could be file, directory, block special, ... */
+
+ return 1;
+}
+
+static void
+parse_header (char *s)
+{
+ unsigned long parsed[12];
+ char buf[9];
+ int i;
+
+ buf[8] = '\0';
+ for (i = 0, s += 6; i < 12; i++, s += 8) {
+ memcpy (buf, s, 8);
+ parsed[i] = strtoul (buf, NULL, 16);
+ }
+ cpio_ino = parsed[0]; /* fake inode number from cpio file */
+ mode = parsed[1];
+ uid = parsed[2];
+ gid = parsed[3];
+ nlink = parsed[4];
+ mtime = parsed[5];
+ body_len = parsed[6];
+ dev_major = parsed[7];
+ dev_minor = parsed[8];
+ rdev_major = parsed[9];
+ rdev_minor = parsed[10];
+ name_len = parsed[11];
+}
+
+static void
+skip_to_next_header (void)
+{
+ char buf[65536];
+
+ while (curr < next_header) {
+ size_t bytes = (size_t) (next_header - curr);
+ if (bytes > sizeof buf)
+ bytes = sizeof buf;
+ size_t r = fread (buf, 1, bytes, fp);
+ if (r == 0)
+ error (EXIT_FAILURE, errno, "error or unexpected end of cpio file");
+ curr += r;
+ }
+}
+
+/* Read any sort of file. The body will only be present for
+ * regular files and symlinks.
+ */
+static void
+read_file (void)
+{
+ errcode_t err;
+ int dir_ft;
+ char name[N_ALIGN(name_len)+1]; /* asserted above this is <= PATH_MAX */
+
+ if (fread (name, N_ALIGN(name_len), 1, fp) != 1)
+ error (EXIT_FAILURE, errno, "read failure reading name field in cpio file");
+ curr += N_ALIGN(name_len);
+
+ name[name_len] = '\0';
+
+ if (verbose >= 2)
+ fprintf (stderr, "ext2 read_file %s %o\n", name, mode);
+
+ if (strcmp (name, "TRAILER!!!") == 0) {
+ clear_links ();
+ goto skip;
+ }
+
+ /* The name will be something like "bin/ls" or "./bin/ls". It won't
+ * (ever?) be an absolute path. Skip leading parts, and if it refers
+ * to the root directory just skip it entirely.
+ */
+ char *dirname = name, *basename;
+ if (*dirname == '.')
+ dirname++;
+ if (*dirname == '/')
+ dirname++;
+ if (*dirname == '\0')
+ goto skip;
+
+ ext2_ino_t dir_ino;
+ basename = strrchr (dirname, '/');
+ if (basename == NULL) {
+ basename = dirname;
+ dir_ino = EXT2_ROOT_INO;
+ } else {
+ *basename++ = '\0';
+
+ /* Look up the parent directory. */
+ err = ext2fs_namei (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, dirname, &dir_ino);
+ if (err != 0)
+ error (EXIT_FAILURE, 0, "ext2: parent directory not found: %s: %s",
+ dirname, error_message (err));
+ }
+
+ if (verbose >= 2)
+ fprintf (stderr, "ext2 read_file dirname %s basename %s\n",
+ dirname, basename);
+
+ ext2_clean_path (dir_ino, dirname, basename, S_ISDIR (mode));
+
+ /* Create a regular file. */
+ if (S_ISREG (mode)) {
+ ext2_ino_t ml = maybe_link ();
+ ext2_ino_t ino;
+ if (ml <= 1) {
+ ext2_empty_inode (dir_ino, dirname, basename,
+ mode, uid, gid, mtime, mtime, mtime,
+ 0, 0, EXT2_FT_REG_FILE, &ino);
+ if (ml == 1)
+ add_link (ino);
+ }
+ else /* ml >= 2 */ {
+ /* It's a hard link back to a previous file. */
+ ino = ml;
+ ext2_link (dir_ino, basename, ino, EXT2_FT_REG_FILE);
+ }
+
+ if (body_len) {
+ char *buf = read_whole_body ();
+ ext2_write_file (ino, buf, body_len);
+ free (buf);
+ }
+ }
+ /* Create a symlink. */
+ else if (S_ISLNK (mode)) {
+ ext2_ino_t ino;
+ ext2_empty_inode (dir_ino, dirname, basename,
+ mode, uid, gid, mtime, mtime, mtime,
+ 0, 0, EXT2_FT_SYMLINK, &ino);
+
+ char *buf = read_whole_body ();
+ ext2_write_file (ino, buf, body_len);
+ free (buf);
+ }
+ /* Create a directory. */
+ else if (S_ISDIR (mode)) {
+ ext2_mkdir (dir_ino, dirname, basename,
+ mode, uid, gid, mtime, mtime, mtime);
+ }
+ /* Create a special file. */
+ else if (S_ISBLK (mode)) {
+ dir_ft = EXT2_FT_BLKDEV;
+ goto make_special;
+ }
+ else if (S_ISCHR (mode)) {
+ dir_ft = EXT2_FT_CHRDEV;
+ goto make_special;
+ } else if (S_ISFIFO (mode)) {
+ dir_ft = EXT2_FT_FIFO;
+ goto make_special;
+ } else if (S_ISSOCK (mode)) {
+ dir_ft = EXT2_FT_SOCK;
+ make_special:
+ /* Just like the kernel, we ignore special files with nlink > 1. */
+ if (maybe_link () == 0)
+ ext2_empty_inode (dir_ino, dirname, basename,
+ mode, uid, gid, mtime, mtime, mtime,
+ rdev_major, rdev_minor, dir_ft, NULL);
+ }
+
+ skip:
+ skip_to_next_header ();
+}
+
+static char *
+read_whole_body (void)
+{
+ char *buf = malloc (body_len);
+ if (buf == NULL)
+ error (EXIT_FAILURE, errno, "malloc");
+
+ size_t r = fread (buf, body_len, 1, fp);
+ if (r != 1)
+ error (EXIT_FAILURE, errno, "read failure reading body in cpio file");
+ curr += body_len;
+
+ return buf;
+}
+
+struct links {
+ struct links *next;
+ unsigned long cpio_ino; /* fake ino from cpio file */
+ int minor;
+ int major;
+ ext2_ino_t real_ino; /* real inode number on ext2 filesystem */
+};
+static struct links *links_head = NULL;
+
+/* If it's a hard link, return the linked inode number in the real
+ * ext2 filesystem.
+ *
+ * Returns: 0 = not a hard link
+ * 1 = possible unresolved hard link
+ * inode number = resolved hard link to this inode
+ */
+static ext2_ino_t
+maybe_link (void)
+{
+ if (nlink >= 2) {
+ struct links *p;
+ for (p = links_head; p; p = p->next) {
+ if (p->cpio_ino != cpio_ino)
+ continue;
+ if (p->minor != dev_minor)
+ continue;
+ if (p->major != dev_major)
+ continue;
+ return p->real_ino;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+add_link (ext2_ino_t real_ino)
+{
+ struct links *p = malloc (sizeof (*p));
+ p->cpio_ino = cpio_ino;
+ p->minor = dev_minor;
+ p->major = dev_major;
+ p->real_ino = real_ino;
+}
+
+static void
+clear_links (void)
+{
+ /* Don't bother to free the linked list in this short-lived program. */
+ links_head = NULL;
+}
diff --git a/helper/ext2initrd.c b/helper/ext2initrd.c
new file mode 100644
index 0000000..c981232
--- /dev/null
+++ b/helper/ext2initrd.c
@@ -0,0 +1,263 @@
+/* febootstrap-supermin-helper reimplementation in C.
+ * Copyright (C) 2009-2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* ext2 requires a small initrd in order to boot. This builds it. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "error.h"
+#include "full-write.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+
+#include "helper.h"
+#include "ext2internal.h"
+
+static void read_module_deps (const char *modpath);
+static void free_module_deps (void);
+static const char *get_module_dep (const char *);
+
+/* The init binary. */
+extern char _binary_init_start, _binary_init_end, _binary_init_size;
+
+/* The list of modules (wildcards) we consider for inclusion in the
+ * mini initrd. Only what is needed in order to find a device with an
+ * ext2 filesystem on it.
+ */
+static const char *kmods[] = {
+ "ext2.ko",
+ "virtio*.ko",
+ "ide*.ko",
+ "libata*.ko",
+ "piix*.ko",
+ "scsi_transport_spi.ko",
+ "scsi_mod.ko",
+ "sd_mod.ko",
+ "sym53c8xx.ko",
+ "ata_piix.ko",
+ "sr_mod.ko",
+ "mbcache.ko",
+ "crc*.ko",
+ "libcrc*.ko",
+ NULL
+};
+
+void
+ext2_make_initrd (const char *modpath, const char *initrd)
+{
+ char dir[] = "/tmp/ext2initrdXXXXXX";
+ if (mkdtemp (dir) == NULL)
+ error (EXIT_FAILURE, errno, "mkdtemp");
+
+ char *cmd;
+ int r;
+
+ /* Copy kernel modules into tmpdir. */
+ size_t n = strlen (modpath) + strlen (dir) + 64;
+ size_t i;
+ for (i = 0; kmods[i] != NULL; ++i)
+ n += strlen (kmods[i]) + 16;
+ cmd = malloc (n);
+ sprintf (cmd, "find '%s' ", modpath);
+ for (i = 0; kmods[i] != NULL; ++i) {
+ if (i > 0) strcat (cmd, "-o ");
+ strcat (cmd, "-name '");
+ strcat (cmd, kmods[i]);
+ strcat (cmd, "' ");
+ }
+ strcat (cmd, "| xargs cp -t ");
+ strcat (cmd, dir);
+ if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
+ r = system (cmd);
+ if (r == -1 || WEXITSTATUS (r) != 0)
+ error (EXIT_FAILURE, 0, "ext2_make_initrd: copy kmods failed");
+ free (cmd);
+
+ /* The above command effectively gives us the final list of modules.
+ * Calculate dependencies from modpath/modules.dep and write that
+ * into the output.
+ */
+ read_module_deps (modpath);
+
+ cmd = xasprintf ("tsort > %s/modules", dir);
+ if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
+ FILE *pp = popen (cmd, "w");
+ if (pp == NULL)
+ error (EXIT_FAILURE, errno, "tsort: failed to create modules list");
+
+ DIR *dr = opendir (dir);
+ if (dr == NULL)
+ error (EXIT_FAILURE, errno, "opendir: %s", dir);
+
+ struct dirent *d;
+ while ((errno = 0, d = readdir (dr)) != NULL) {
+ size_t n = strlen (d->d_name);
+ if (n >= 3 &&
+ d->d_name[n-3] == '.' &&
+ d->d_name[n-2] == 'k' &&
+ d->d_name[n-1] == 'o') {
+ const char *dep = get_module_dep (d->d_name);
+ if (dep)
+ /* Reversed so that tsort will print the final list in the
+ * order that it has to be loaded.
+ */
+ fprintf (pp, "%s %s\n", dep, d->d_name);
+ else
+ /* No dependencies, just make it depend on itself so that
+ * tsort prints it.
+ */
+ fprintf (pp, "%s %s\n", d->d_name, d->d_name);
+ }
+ }
+ if (errno)
+ error (EXIT_FAILURE, errno, "readdir: %s", dir);
+
+ if (closedir (dr) == -1)
+ error (EXIT_FAILURE, errno, "closedir: %s", dir);
+
+ if (pclose (pp) == -1)
+ error (EXIT_FAILURE, errno, "pclose: %s", cmd);
+
+ free (cmd);
+ free_module_deps ();
+
+ /* Copy in insmod static binary. */
+ cmd = xasprintf ("cp /sbin/insmod.static %s", dir);
+ if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
+ r = system (cmd);
+ if (r == -1 || WEXITSTATUS (r) != 0)
+ error (EXIT_FAILURE, 0,
+ "ext2_make_initrd: copy /sbin/insmod.static failed");
+ free (cmd);
+
+ /* Copy in the init program, linked into this program as a data blob. */
+ char *init = xasprintf ("%s/init", dir);
+ int fd = open (init, O_WRONLY|O_TRUNC|O_CREAT|O_NOCTTY, 0755);
+ if (fd == -1)
+ error (EXIT_FAILURE, errno, "open: %s", init);
+
+ n = (size_t) &_binary_init_size;
+ if (full_write (fd, &_binary_init_start, n) != n)
+ error (EXIT_FAILURE, errno, "write: %s", init);
+
+ if (close (fd) == -1)
+ error (EXIT_FAILURE, errno, "close: %s", init);
+
+ free (init);
+
+ /* Build the cpio file. */
+ cmd = xasprintf ("(cd %s && (echo . ; ls -1)"
+ " | cpio --quiet -o -H newc) > '%s'",
+ dir, initrd);
+ if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
+ r = system (cmd);
+ if (r == -1 || WEXITSTATUS (r) != 0)
+ error (EXIT_FAILURE, 0, "ext2_make_initrd: cpio failed");
+ free (cmd);
+
+ /* Construction of 'dir' above ensures this is safe. */
+ cmd = xasprintf ("rm -rf %s", dir);
+ if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
+ system (cmd);
+ free (cmd);
+}
+
+/* Module dependencies. */
+struct moddep {
+ struct moddep *next;
+ char *name;
+ char *dep;
+};
+struct moddep *moddeps = NULL;
+
+static void add_module_dep (const char *name, const char *dep);
+
+static void
+free_module_deps (void)
+{
+ /* Short-lived program, don't bother to free it. */
+ moddeps = NULL;
+}
+
+/* Read modules.dep into internal structure. */
+static void
+read_module_deps (const char *modpath)
+{
+ free_module_deps ();
+
+ char *filename = xasprintf ("%s/modules.dep", modpath);
+ FILE *fp = fopen (filename, "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, "open: %s", modpath);
+
+ char *line = NULL;
+ size_t llen = 0;
+ ssize_t len;
+ while ((len = getline (&line, &llen, fp)) != -1) {
+ if (len > 0 && line[len-1] == '\n')
+ line[--len] = '\0';
+
+ char *name = strtok (line, ": ");
+ if (!name) continue;
+
+ /* Only want the module basename, but keep the ".ko" extension. */
+ char *p = strrchr (name, '/');
+ if (p) name = p+1;
+
+ char *dep;
+ while ((dep = strtok (NULL, " ")) != NULL) {
+ p = strrchr (dep, '/');
+ if (p) dep = p+1;
+
+ add_module_dep (name, dep);
+ }
+ }
+
+ free (line);
+ fclose (fp);
+}
+
+/* Module 'name' requires 'dep' to be loaded first. */
+static void
+add_module_dep (const char *name, const char *dep)
+{
+ struct moddep *m = xmalloc (sizeof *m);
+ m->next = moddeps;
+ moddeps = m;
+ m->name = xstrdup (name);
+ m->dep = xstrdup (dep);
+}
+
+static const char *
+get_module_dep (const char *name)
+{
+ struct moddep *m;
+
+ for (m = moddeps; m; m = m->next)
+ if (strcmp (m->name, name) == 0)
+ return m->dep;
+
+ return NULL;
+}
diff --git a/helper/ext2internal.h b/helper/ext2internal.h
new file mode 100644
index 0000000..7efadb5
--- /dev/null
+++ b/helper/ext2internal.h
@@ -0,0 +1,45 @@
+/* febootstrap-supermin-helper reimplementation in C.
+ * Copyright (C) 2009-2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is a private interface used between the parts of the ext2 plugin. */
+
+#ifndef FEBOOTSTRAP_SUPERMIN_EXT2INTERNAL_H
+#define FEBOOTSTRAP_SUPERMIN_EXT2INTERNAL_H
+
+/* Inlining is broken in the ext2fs header file. Disable it by
+ * defining the following:
+ */
+#define NO_INLINE_FUNCS
+#include <ext2fs/ext2fs.h>
+
+/* ext2.c */
+extern ext2_filsys fs;
+
+extern void ext2_mkdir (ext2_ino_t dir_ino, const char *dirname, const char *basename, mode_t mode, uid_t uid, gid_t gid, time_t ctime, time_t atime, time_t mtime);
+extern void ext2_empty_inode (ext2_ino_t dir_ino, const char *dirname, const char *basename, mode_t mode, uid_t uid, gid_t gid, time_t ctime, time_t atime, time_t mtime, int major, int minor, int dir_ft, ext2_ino_t *ino_ret);
+extern void ext2_write_file (ext2_ino_t ino, const char *buf, size_t size);
+extern void ext2_link (ext2_ino_t dir_ino, const char *basename, ext2_ino_t ino, int dir_ft);
+extern void ext2_clean_path (ext2_ino_t dir_ino, const char *dirname, const char *basename, int isdir);
+
+/* ext2cpio.c */
+extern void ext2_cpio_file (const char *cpio_file);
+
+/* ext2initrd.c */
+extern void ext2_make_initrd (const char *modpath, const char *initrd);
+
+#endif /* FEBOOTSTRAP_SUPERMIN_EXT2INTERNAL_H */
diff --git a/helper/init.c b/helper/init.c
new file mode 100644
index 0000000..5dd05cf
--- /dev/null
+++ b/helper/init.c
@@ -0,0 +1,195 @@
+/* febootstrap-supermin-helper reimplementation in C.
+ * Copyright (C) 2009-2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This very minimal init "script" goes in the mini-initrd used to
+ * boot the ext2-based appliance. Note we have no shell, so we cannot
+ * use system(3) to run external commands. In fact, we don't have
+ * very much at all, except this program, insmod.static, and some
+ * kernel modules.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+/* Leave this enabled for now. When we get more confident in the boot
+ * process we can turn this off or make it configurable.
+ */
+#define verbose 1
+
+static void print_uptime (void);
+static void insmod (const char *filename);
+
+static char line[1024];
+
+void
+main ()
+{
+ print_uptime ();
+ fprintf (stderr, "febootstrap: ext2 mini initrd starting up\n");
+
+ /* Create some fixed directories. */
+ mkdir ("/dev", 0755);
+ mkdir ("/root", 0755);
+ mkdir ("/sys", 0755);
+
+ /* Mount /sys. */
+ if (verbose)
+ fprintf (stderr, "febootstrap: mounting /sys\n");
+ if (mount ("sysfs", "/sys", "sysfs", 0, "") == -1) {
+ perror ("mount: /sys");
+ exit (EXIT_FAILURE);
+ }
+
+ FILE *fp = fopen ("/modules", "r");
+ if (fp == NULL) {
+ perror ("fopen: /modules");
+ exit (EXIT_FAILURE);
+ }
+ while (fgets (line, sizeof line, fp)) {
+ size_t n = strlen (line);
+ if (n > 0 && line[n-1] == '\n')
+ line[--n] = '\0';
+ insmod (line);
+ }
+ fclose (fp);
+
+ /* Look for the ext2 filesystem device. It's always the last
+ * one that was added.
+ * XXX More than 25 devices?
+ */
+ char path[] = "/sys/block/xdx/dev";
+ char class[3] = { 'v', 's', 'h' };
+ size_t i, j;
+ fp = NULL;
+ for (i = 0; i < sizeof class; ++i) {
+ for (j = 'z'; j >= 'a'; --j) {
+ path[11] = class[i];
+ path[13] = j;
+ fp = fopen (path, "r");
+ if (fp != NULL)
+ goto found;
+ }
+ }
+ fprintf (stderr,
+ "febootstrap: no ext2 root device found\n"
+ "Please include FULL verbose output in your bug report.\n");
+ exit (EXIT_FAILURE);
+
+ found:
+ if (verbose)
+ fprintf (stderr, "febootstrap: picked %s as root device\n", path);
+
+ fgets (line, sizeof line, fp);
+ int major = atoi (line);
+ char *p = line + strcspn (line, ":") + 1;
+ int minor = atoi (p);
+
+ fclose (fp);
+ if (umount ("/sys") == -1) {
+ perror ("umount: /sys");
+ exit (EXIT_FAILURE);
+ }
+
+ if (verbose)
+ fprintf (stderr, "febootstrap: creating /dev/root as block special %d:%d\n",
+ major, minor);
+
+ if (mknod ("/dev/root", S_IFBLK|0700, makedev (major, minor)) == -1) {
+ perror ("mknod: /dev/root");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Mount new root and chroot to it. */
+ if (verbose)
+ fprintf (stderr, "febootstrap: mounting new root on /root\n");
+ if (mount ("/dev/root", "/root", "ext2", MS_NOATIME, "") == -1) {
+ perror ("mount: /root");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Note that pivot_root won't work. See the note in
+ * Documentation/filesystems/ramfs-rootfs-initramfs.txt
+ * We could remove the old initramfs files, but let's not bother.
+ */
+ if (verbose)
+ fprintf (stderr, "febootstrap: chroot\n");
+
+ if (chroot ("/root") == -1) {
+ perror ("chroot: /root");
+ exit (EXIT_FAILURE);
+ }
+
+ chdir ("/");
+
+ /* Run /init from ext2 filesystem. */
+ print_uptime ();
+ execl ("/init", "init", NULL);
+ perror ("execl: /init");
+ exit (EXIT_FAILURE);
+}
+
+static void
+insmod (const char *filename)
+{
+ if (verbose)
+ fprintf (stderr, "febootstrap: insmod %s\n", filename);
+
+ pid_t pid = fork ();
+ if (pid == -1) {
+ perror ("insmod: fork");
+ exit (EXIT_FAILURE);
+ }
+
+ if (pid == 0) { /* Child. */
+ execl ("/insmod.static", "insmod.static", filename, NULL);
+ perror ("insmod: execl");
+ _exit (EXIT_FAILURE);
+ }
+
+ /* Parent. */
+ int status;
+ if (wait (&status) == -1 ||
+ WEXITSTATUS (status) != 0)
+ perror ("insmod: wait");
+ /* but ignore the error, some will be because the device is not found */
+}
+
+/* Print contents of /proc/uptime. */
+static void
+print_uptime (void)
+{
+ FILE *fp = fopen ("/proc/uptime", "r");
+ if (fp == NULL) {
+ perror ("/proc/uptime");
+ return;
+ }
+
+ fgets (line, sizeof line, fp);
+ fclose (fp);
+
+ fprintf (stderr, "febootstrap: uptime: %s", line);
+}
diff --git a/helper/main.c b/helper/main.c
index d7dd25a..b4359b6 100644
--- a/helper/main.c
+++ b/helper/main.c
@@ -72,7 +72,7 @@ usage (const char *progname)
"Options:\n"
" --help\n"
" Display this help text and exit.\n"
- " -f cpio | --format cpio\n"
+ " -f cpio|ext2 | --format cpio|ext2\n"
" Specify output format (default: cpio).\n"
" -k file | --kmods file\n"
" Specify kernel module whitelist.\n"
@@ -136,7 +136,7 @@ main (int argc, char *argv[])
nr_outputs = 3; /* kernel, initrd, appliance */
}
else {
- fprintf (stderr, "%s: incorrect output format (-f): must be cpio\n",
+ fprintf (stderr, "%s: incorrect output format (-f): must be cpio|ext2\n",
argv[0]);
exit (EXIT_FAILURE);
}