1 /* febootstrap-supermin-helper reimplementation in C.
2 * Copyright (C) 2009-2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* ext2 requires a small initrd in order to boot. This builds it. */
32 #include "full-write.h"
34 #include "xvasprintf.h"
37 #include "ext2internal.h"
39 static void read_module_deps (const char *modpath);
40 static void free_module_deps (void);
41 static const char *get_module_dep (const char *);
43 /* The init binary. */
44 extern char _binary_init_start, _binary_init_end, _binary_init_size;
46 /* The list of modules (wildcards) we consider for inclusion in the
47 * mini initrd. Only what is needed in order to find a device with an
48 * ext2 filesystem on it.
50 static const char *kmods[] = {
56 "scsi_transport_spi.ko",
69 ext2_make_initrd (const char *modpath, const char *initrd)
71 char dir[] = "/tmp/ext2initrdXXXXXX";
72 if (mkdtemp (dir) == NULL)
73 error (EXIT_FAILURE, errno, "mkdtemp");
78 /* Copy kernel modules into tmpdir. */
79 size_t n = strlen (modpath) + strlen (dir) + 64;
81 for (i = 0; kmods[i] != NULL; ++i)
82 n += strlen (kmods[i]) + 16;
84 /* "cd /" here is for virt-v2v. It's cwd might not be accessible by
85 * the current user (because it sometimes sets its own uid) and the
86 * "find" command works by changing directory then changing back to
87 * the cwd. This results in a warning:
89 * find: failed to restore initial working directory: Permission denied
91 * Note this only works because "modpath" and temporary "dir" are
92 * currently guaranteed to be absolute paths, hence assertion.
94 assert (modpath[0] == '/');
95 sprintf (cmd, "cd / ; find '%s' ", modpath);
96 for (i = 0; kmods[i] != NULL; ++i) {
97 if (i > 0) strcat (cmd, "-o ");
98 strcat (cmd, "-name '");
99 strcat (cmd, kmods[i]);
102 strcat (cmd, "| xargs cp -t ");
104 if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
106 if (r == -1 || WEXITSTATUS (r) != 0)
107 error (EXIT_FAILURE, 0, "ext2_make_initrd: copy kmods failed");
110 /* The above command effectively gives us the final list of modules.
111 * Calculate dependencies from modpath/modules.dep and write that
114 read_module_deps (modpath);
116 cmd = xasprintf ("tsort > %s/modules", dir);
117 if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
118 FILE *pp = popen (cmd, "w");
120 error (EXIT_FAILURE, errno, "tsort: failed to create modules list");
122 DIR *dr = opendir (dir);
124 error (EXIT_FAILURE, errno, "opendir: %s", dir);
127 while ((errno = 0, d = readdir (dr)) != NULL) {
128 size_t n = strlen (d->d_name);
130 d->d_name[n-3] == '.' &&
131 d->d_name[n-2] == 'k' &&
132 d->d_name[n-1] == 'o') {
133 const char *dep = get_module_dep (d->d_name);
135 /* Reversed so that tsort will print the final list in the
136 * order that it has to be loaded.
138 fprintf (pp, "%s %s\n", dep, d->d_name);
140 /* No dependencies, just make it depend on itself so that
143 fprintf (pp, "%s %s\n", d->d_name, d->d_name);
147 error (EXIT_FAILURE, errno, "readdir: %s", dir);
149 if (closedir (dr) == -1)
150 error (EXIT_FAILURE, errno, "closedir: %s", dir);
152 if (pclose (pp) == -1)
153 error (EXIT_FAILURE, errno, "pclose: %s", cmd);
158 /* Copy in insmod static binary. */
159 cmd = xasprintf ("cp %s %s", INSMODSTATIC, dir);
160 if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
162 if (r == -1 || WEXITSTATUS (r) != 0)
163 error (EXIT_FAILURE, 0,
164 "ext2_make_initrd: copy %s failed", INSMODSTATIC);
167 /* Copy in the init program, linked into this program as a data blob. */
168 char *init = xasprintf ("%s/init", dir);
169 int fd = open (init, O_WRONLY|O_TRUNC|O_CREAT|O_NOCTTY, 0755);
171 error (EXIT_FAILURE, errno, "open: %s", init);
173 n = (size_t) &_binary_init_size;
174 if (full_write (fd, &_binary_init_start, n) != n)
175 error (EXIT_FAILURE, errno, "write: %s", init);
177 if (close (fd) == -1)
178 error (EXIT_FAILURE, errno, "close: %s", init);
182 /* Build the cpio file. */
183 cmd = xasprintf ("(cd %s && (echo . ; ls -1)"
184 " | cpio --quiet -o -H newc) > '%s'",
186 if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
188 if (r == -1 || WEXITSTATUS (r) != 0)
189 error (EXIT_FAILURE, 0, "ext2_make_initrd: cpio failed");
192 /* Construction of 'dir' above ensures this is safe. */
193 cmd = xasprintf ("rm -rf %s", dir);
194 if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
199 /* Module dependencies. */
205 struct moddep *moddeps = NULL;
207 static void add_module_dep (const char *name, const char *dep);
210 free_module_deps (void)
212 /* Short-lived program, don't bother to free it. */
216 /* Read modules.dep into internal structure. */
218 read_module_deps (const char *modpath)
222 char *filename = xasprintf ("%s/modules.dep", modpath);
223 FILE *fp = fopen (filename, "r");
225 error (EXIT_FAILURE, errno, "open: %s", modpath);
230 while ((len = getline (&line, &llen, fp)) != -1) {
231 if (len > 0 && line[len-1] == '\n')
234 char *name = strtok (line, ": ");
237 /* Only want the module basename, but keep the ".ko" extension. */
238 char *p = strrchr (name, '/');
242 while ((dep = strtok (NULL, " ")) != NULL) {
243 p = strrchr (dep, '/');
246 add_module_dep (name, dep);
254 /* Module 'name' requires 'dep' to be loaded first. */
256 add_module_dep (const char *name, const char *dep)
258 struct moddep *m = xmalloc (sizeof *m);
261 m->name = xstrdup (name);
262 m->dep = xstrdup (dep);
266 get_module_dep (const char *name)
270 for (m = moddeps; m; m = m->next)
271 if (strcmp (m->name, name) == 0)