13e429cc912e64185eac3c00d761bde3c2f4eace
[febootstrap.git] / helper / ext2initrd.c
1 /* febootstrap-supermin-helper reimplementation in C.
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 /* ext2 requires a small initrd in order to boot.  This builds it. */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <fnmatch.h>
31
32 #include "error.h"
33 #include "full-write.h"
34 #include "xalloc.h"
35 #include "xvasprintf.h"
36
37 #include "helper.h"
38 #include "ext2internal.h"
39
40 static void read_module_deps (const char *modpath);
41 static void free_module_deps (void);
42 static void add_module_dep (const char *name, const char *dep);
43 static struct module * add_module (const char *name);
44 static struct module * find_module (const char *name);
45 static void print_module_load_order (FILE *f, FILE *pp, struct module *m);
46
47 /* The init binary. */
48 extern char _binary_init_start, _binary_init_end, _binary_init_size;
49
50 /* The list of modules (wildcards) we consider for inclusion in the
51  * mini initrd.  Only what is needed in order to find a device with an
52  * ext2 filesystem on it.
53  */
54 static const char *kmods[] = {
55   "ext2.ko",
56   "virtio*.ko",
57   "ide*.ko",
58   "libata*.ko",
59   "piix*.ko",
60   "scsi_transport_spi.ko",
61   "scsi_mod.ko",
62   "sd_mod.ko",
63   "sym53c8xx.ko",
64   "ata_piix.ko",
65   "sr_mod.ko",
66   "mbcache.ko",
67   "crc*.ko",
68   "libcrc*.ko",
69   NULL
70 };
71
72 /* Module dependencies. */
73 struct module {
74   struct module *next;
75   struct moddep *deps;
76   char *name;
77   int visited;
78 };
79 struct module *modules = NULL;
80
81 struct moddep {
82   struct moddep *next;
83   struct module *dep;
84 };
85
86 void
87 ext2_make_initrd (const char *modpath, const char *initrd)
88 {
89   char dir[] = "/tmp/ext2initrdXXXXXX";
90   if (mkdtemp (dir) == NULL)
91     error (EXIT_FAILURE, errno, "mkdtemp");
92
93   read_module_deps (modpath);
94   add_module ("");
95   for (int i = 0; kmods[i] != NULL; ++i) {
96     for (struct module *m = modules; m; m = m->next) {
97       char *n = strrchr (m->name, '/');
98       if (n)
99         n += 1;
100       else
101         n = m->name;
102       if (fnmatch (kmods[i], n, FNM_PATHNAME) == 0) {
103         if (verbose >= 2)
104           fprintf (stderr, "Adding top-level dependency %s (%s)\n", m->name, kmods[i]);
105         add_module_dep ("", m->name);
106       }
107     }
108   }
109
110   char *cmd = xasprintf ("cd %s; xargs cp -t %s", modpath, dir);
111   char *outfile = xasprintf ("%s/modules", dir);
112   if (verbose >= 2) fprintf (stderr, "writing to %s\n", cmd);
113
114   FILE *f = fopen (outfile, "w");
115   if (f == NULL)
116     error (EXIT_FAILURE, errno, "failed to create modules list (%s)", outfile);
117   FILE *pp = popen (cmd, "w");
118   if (pp == NULL)
119     error (EXIT_FAILURE, errno, "failed to create pipe (%s)", cmd);
120
121   /* The "pseudo" module depends on all modules matched by the contents of kmods */
122   struct module *pseudo = find_module ("");
123   print_module_load_order (pp, f, pseudo);
124   fclose (pp);
125   pclose (f);
126
127   free (cmd);
128   free_module_deps ();
129
130   /* Copy in the init program, linked into this program as a data blob. */
131   char *init = xasprintf ("%s/init", dir);
132   int fd = open (init, O_WRONLY|O_TRUNC|O_CREAT|O_NOCTTY, 0755);
133   if (fd == -1)
134     error (EXIT_FAILURE, errno, "open: %s", init);
135
136   size_t n = (size_t) &_binary_init_size;
137   if (full_write (fd, &_binary_init_start, n) != n)
138     error (EXIT_FAILURE, errno, "write: %s", init);
139
140   if (close (fd) == -1)
141     error (EXIT_FAILURE, errno, "close: %s", init);
142
143   free (init);
144
145   /* Build the cpio file. */
146   cmd = xasprintf ("(cd %s && (echo . ; ls -1)"
147                    " | cpio --quiet -o -H newc) > '%s'",
148                    dir, initrd);
149   if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
150   int r = system (cmd);
151   if (r == -1 || WEXITSTATUS (r) != 0)
152     error (EXIT_FAILURE, 0, "ext2_make_initrd: cpio failed");
153   free (cmd);
154
155   /* Construction of 'dir' above ensures this is safe. */
156   cmd = xasprintf ("rm -rf %s", dir);
157   if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
158   system (cmd);
159   free (cmd);
160 }
161
162 static void
163 free_module_deps (void)
164 {
165   /* Short-lived program, don't bother to free it. */
166   modules = NULL;
167 }
168
169 /* Read modules.dep into internal structure. */
170 static void
171 read_module_deps (const char *modpath)
172 {
173   free_module_deps ();
174
175   char *filename = xasprintf ("%s/modules.dep", modpath);
176   FILE *fp = fopen (filename, "r");
177   if (fp == NULL)
178     error (EXIT_FAILURE, errno, "open: %s", modpath);
179
180   char *line = NULL;
181   size_t llen = 0;
182   ssize_t len;
183   while ((len = getline (&line, &llen, fp)) != -1) {
184     if (len > 0 && line[len-1] == '\n')
185       line[--len] = '\0';
186
187     char *name = strtok (line, ": ");
188     if (!name) continue;
189
190     add_module (name);
191     char *dep;
192     while ((dep = strtok (NULL, " ")) != NULL) {
193       add_module_dep (name, dep);
194     }
195   }
196
197   free (line);
198   fclose (fp);
199 }
200
201 static struct module *
202 add_module (const char *name)
203 {
204   struct module *m = find_module (name);
205   if (m)
206     return m;
207   m = xmalloc (sizeof *m);
208   m->name = xstrdup (name);
209   m->deps = NULL;
210   m->next = modules;
211   m->visited = 0;
212   modules = m;
213   return m;
214 }
215
216 static struct module *
217 find_module (const char *name)
218 {
219   struct module *m;
220   for (m = modules; m; m = m->next) {
221     if (strcmp (name, m->name) == 0)
222       break;
223   }
224   return m;
225 }
226
227 /* Module 'name' requires 'dep' to be loaded first. */
228 static void
229 add_module_dep (const char *name, const char *dep)
230 {
231   if (verbose >= 2) fprintf (stderr, "add_module_dep %s: %s\n", name, dep);
232   struct module *m1 = add_module (name);
233   struct module *m2 = add_module (dep);
234   struct moddep *d;
235   for (d = m1->deps; d; d = d->next) {
236     if (d->dep == m2)
237       return;
238   }
239   d = xmalloc (sizeof *d);
240   d->next = m1->deps;
241   d->dep = m2;
242   m1->deps = d;
243   return;
244 }
245
246 /* DFS on the dependency graph */
247 static void
248 print_module_load_order (FILE *pipe, FILE *list, struct module *m)
249 {
250   if (m->visited)
251     return;
252
253   for (struct moddep *d = m->deps; d; d = d->next)
254     print_module_load_order (pipe, list, d->dep);
255
256   if (m->name[0] == 0)
257     return;
258
259   char *basename = strrchr (m->name, '/');
260   if (basename)
261     ++basename;
262   else
263     basename = m->name;
264
265   fputs (m->name, pipe);
266   fputc ('\n', pipe);
267   fputs (basename, list);
268   fputc ('\n', list);
269   m->visited = 1;
270 }