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.
35 #include "xvasprintf.h"
39 static void iterate_inputs (char **inputs, int nr_inputs, struct writer *);
40 static void iterate_input_directory (const char *dirname, int dirfd, struct writer *);
41 static void add_kernel_modules (const char *whitelist, const char *modpath, struct writer *);
42 static void add_hostfiles (const char *hostfiles_file, struct writer *);
44 /* Create the appliance.
46 * The initrd consists of these components concatenated together:
48 * (1) The base skeleton appliance that we constructed at build time.
50 * (2) The host files which match wildcards in *.supermin.hostfiles.
51 * input format = plain text, output format = plain cpio
52 * (3) The modules from modpath which are on the module whitelist.
53 * output format = plain cpio
55 * The original shell script used the external cpio program to create
56 * parts (2) and (3), but we have decided it's going to be faster if
57 * we just write out the data outselves. The reasons are that
58 * external cpio is slow (particularly when used with SELinux because
59 * it does 512 byte reads), and the format that we're writing is
60 * narrow and well understood, because we only care that the Linux
63 * This version contains some improvements over the C version written
64 * for libguestfs, in that we can have multiple base images (or
65 * hostfiles) or use a directory to store these files.
68 create_appliance (const char *hostcpu,
69 char **inputs, int nr_inputs,
70 const char *whitelist,
73 const char *appliance,
74 struct writer *writer)
76 writer->wr_start (hostcpu, appliance, modpath, initrd);
78 iterate_inputs (inputs, nr_inputs, writer);
80 writer->wr_file ("/lib/modules");
81 /* Kernel modules (3). */
82 add_kernel_modules (whitelist, modpath, writer);
87 /* Iterate over the inputs to find out what they are, visiting
88 * directories if specified.
91 iterate_inputs (char **inputs, int nr_inputs, struct writer *writer)
94 for (i = 0; i < nr_inputs; ++i) {
96 print_timestamped_message ("visiting %s", inputs[i]);
98 int fd = open (inputs[i], O_RDONLY);
100 error (EXIT_FAILURE, errno, "open: %s", inputs[i]);
103 if (fstat (fd, &statbuf) == -1)
104 error (EXIT_FAILURE, errno, "fstat: %s", inputs[i]);
107 if (S_ISDIR (statbuf.st_mode))
108 iterate_input_directory (inputs[i], fd, writer);
109 else if (S_ISREG (statbuf.st_mode)) {
110 /* Is it a cpio file? */
112 if (read (fd, buf, 6) == 6 && memcmp (buf, "070701", 6) == 0)
113 /* Yes, a cpio file. This is a skeleton appliance, case (1). */
114 writer->wr_cpio_file (inputs[i]);
116 /* No, must be hostfiles, case (2). */
117 add_hostfiles (inputs[i], writer);
120 error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory",
128 string_compare (const void *p1, const void *p2)
130 return strcmp (* (char * const *) p1, * (char * const *) p2);
134 iterate_input_directory (const char *dirname, int dirfd, struct writer *writer)
136 DIR *dir = fdopendir (dirfd);
138 error (EXIT_FAILURE, errno, "fdopendir: %s", dirname);
140 char **entries = NULL;
141 size_t nr_entries = 0, nr_alloc = 0;
144 while ((errno = 0, d = readdir (dir)) != NULL) {
145 if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */
148 /* Ignore *~ files created by editors. */
149 size_t len = strlen (d->d_name);
150 if (len > 0 && d->d_name[len-1] == '~')
153 add_string (&entries, &nr_entries, &nr_alloc, d->d_name);
157 error (EXIT_FAILURE, errno, "readdir: %s", dirname);
159 if (closedir (dir) == -1)
160 error (EXIT_FAILURE, errno, "closedir: %s", dirname);
162 add_string (&entries, &nr_entries, &nr_alloc, NULL);
164 /* Visit directory entries in order. In febootstrap <= 2.8 we
165 * didn't impose any order, but that led to some difficult
168 sort (entries, string_compare);
171 strcpy (path, dirname);
172 size_t len = strlen (dirname);
175 char *inputs[] = { path };
178 for (i = 0; entries[i] != NULL; ++i) {
179 strcpy (&path[len], entries[i]);
180 iterate_inputs (inputs, 1, writer);
184 /* Copy kernel modules.
186 * Find every file under modpath.
188 * Exclude all *.ko files, *except* ones which match names in
189 * the whitelist (which may contain wildcards). Include all
192 * Add chosen files to the output.
194 * whitelist_file may be NULL, to include ALL kernel modules.
197 add_kernel_modules (const char *whitelist_file, const char *modpath,
198 struct writer *writer)
200 char **whitelist = NULL;
201 if (whitelist_file != NULL)
202 whitelist = load_file (whitelist_file);
204 char *paths[2] = { (char *) modpath, NULL };
205 FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL);
207 error (EXIT_FAILURE, errno, "add_kernel_modules: fts_open: %s", modpath);
211 FTSENT *entry = fts_read (fts);
212 if (entry == NULL && errno != 0)
213 error (EXIT_FAILURE, errno, "add_kernel_modules: fts_read: %s", modpath);
217 /* Ignore directories being visited in post-order. */
218 if (entry->fts_info & FTS_DP)
221 /* Is it a *.ko file? */
222 if (entry->fts_namelen >= 3 &&
223 entry->fts_name[entry->fts_namelen-3] == '.' &&
224 entry->fts_name[entry->fts_namelen-2] == 'k' &&
225 entry->fts_name[entry->fts_namelen-1] == 'o') {
227 /* Is it a *.ko file which is on the whitelist? */
229 for (j = 0; whitelist[j] != NULL; ++j) {
231 r = fnmatch (whitelist[j], entry->fts_name, 0);
233 /* It's on the whitelist, so include it. */
235 fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n",
236 entry->fts_name, whitelist[j]);
237 writer->wr_fts_entry (entry);
239 } else if (r != FNM_NOMATCH)
240 error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
241 whitelist[j], entry->fts_name, 0, r);
243 } else { /* whitelist == NULL, always include */
245 fprintf (stderr, "including kernel module %s\n", entry->fts_name);
246 writer->wr_fts_entry (entry);
249 /* It's some other sort of file, or a directory, always include. */
250 writer->wr_fts_entry (entry);
253 if (fts_close (fts) == -1)
254 error (EXIT_FAILURE, errno, "add_kernel_modules: fts_close: %s", modpath);
257 /* Copy the host files.
259 * Read the list of entries in hostfiles (which may contain
260 * wildcards). Look them up in the filesystem, and add those files
261 * that exist. Ignore any files that don't exist or are not readable.
264 add_hostfiles (const char *hostfiles_file, struct writer *writer)
266 char **hostfiles = load_file (hostfiles_file);
268 /* Hostfiles list can contain "." before each path - ignore it.
269 * It also contains each directory name before we enter it. But
270 * we don't read that until we see a wildcard for that directory.
273 for (i = 0; hostfiles[i] != NULL; ++i) {
274 char *hostfile = hostfiles[i];
275 if (hostfile[0] == '.')
280 /* Is it a wildcard? */
281 if (strchr (hostfile, '*') || strchr (hostfile, '?')) {
282 char *dirname = xstrdup (hostfile);
283 char *patt = strrchr (dirname, '/');
287 char **files = read_dir (dirname);
288 files = filter_fnmatch (files, patt, FNM_NOESCAPE);
290 /* Add matching files. */
291 for (j = 0; files[j] != NULL; ++j) {
292 char *tmp = xasprintf ("%s/%s", dirname, files[j]);
295 fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt);
297 writer->wr_file (tmp);
302 /* Else does this file/directory/whatever exist? */
303 else if (lstat (hostfile, &statbuf) == 0) {
305 fprintf (stderr, "including host file %s (directly referenced)\n",
308 writer->wr_file_stat (hostfile, &statbuf);
309 } /* Ignore files that don't exist. */