X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;ds=sidebyside;f=helper%2Fext2initrd.c;fp=helper%2Fext2initrd.c;h=c9812322b22a0d733299f6acfbc3dad36a4eb11b;hb=89e336ee166be538e376d288fb2b3fbbffd66d4c;hp=0000000000000000000000000000000000000000;hpb=53bf430e26f4a53837bd38b58a427079caab3d4b;p=febootstrap.git 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 + +#include +#include +#include +#include +#include +#include + +#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; +}