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 "full-write.h"
37 #include "xvasprintf.h"
41 /* Buffer size used in copy operations throughout. Large for
42 * greatest efficiency.
44 #define BUFFER_SIZE 65536
46 static void iterate_inputs (char **inputs, int nr_inputs);
47 static void iterate_input_directory (const char *dirname, int dirfd);
48 static void write_kernel_modules (const char *whitelist, const char *modpath);
49 static void write_hostfiles (const char *hostfiles_file);
50 static void write_to_fd (const void *buffer, size_t len);
51 static void write_file_to_fd (const char *filename);
52 static void write_file_len_to_fd (const char *filename, size_t len);
53 static void write_padding (size_t len);
54 static void cpio_append_fts_entry (FTSENT *entry);
55 static void cpio_append_stat (const char *filename, struct stat *);
56 static void cpio_append (const char *filename);
57 static void cpio_append_trailer (void);
59 static int out_fd = -1;
60 static off_t out_offset = 0;
62 /* Create the appliance.
64 * The initrd consists of these components concatenated together:
66 * (1) The base skeleton appliance that we constructed at build time.
68 * (2) The host files which match wildcards in *.supermin.hostfiles.
69 * input format = plain text, output format = plain cpio
70 * (3) The modules from modpath which are on the module whitelist.
71 * output format = plain cpio
73 * The original shell script used the external cpio program to create
74 * parts (2) and (3), but we have decided it's going to be faster if
75 * we just write out the data outselves. The reasons are that
76 * external cpio is slow (particularly when used with SELinux because
77 * it does 512 byte reads), and the format that we're writing is
78 * narrow and well understood, because we only care that the Linux
81 * This version contains some improvements over the C version written
82 * for libguestfs, in that we can have multiple base images (or
83 * hostfiles) or use a directory to store these files.
86 create_appliance (char **inputs, int nr_inputs,
87 const char *whitelist,
89 const char *appliance)
91 out_fd = open (appliance, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
93 error (EXIT_FAILURE, errno, "open: %s", appliance);
96 iterate_inputs (inputs, nr_inputs);
98 /* Kernel modules (3). */
99 write_kernel_modules (whitelist, modpath);
101 cpio_append_trailer ();
103 /* Finish off and close output file. */
104 if (close (out_fd) == -1)
105 error (EXIT_FAILURE, errno, "close: %s", appliance);
108 /* Iterate over the inputs to find out what they are, visiting
109 * directories if specified.
112 iterate_inputs (char **inputs, int nr_inputs)
115 for (i = 0; i < nr_inputs; ++i) {
117 print_timestamped_message ("visiting %s", inputs[i]);
119 int fd = open (inputs[i], O_RDONLY);
121 error (EXIT_FAILURE, errno, "open: %s", inputs[i]);
124 if (fstat (fd, &statbuf) == -1)
125 error (EXIT_FAILURE, errno, "fstat: %s", inputs[i]);
128 if (S_ISDIR (statbuf.st_mode))
129 iterate_input_directory (inputs[i], fd);
130 else if (S_ISREG (statbuf.st_mode)) {
131 /* Is it a cpio file? */
133 if (read (fd, buf, 6) == 6 && memcmp (buf, "070701", 6) == 0)
134 /* Yes, a cpio file. This is a skeleton appliance, case (1). */
135 write_file_to_fd (inputs[i]);
137 /* No, must be hostfiles, case (2). */
138 write_hostfiles (inputs[i]);
141 error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory",
149 iterate_input_directory (const char *dirname, int dirfd)
152 strcpy (path, dirname);
153 size_t len = strlen (dirname);
156 char *inputs[] = { path };
158 DIR *dir = fdopendir (dirfd);
160 error (EXIT_FAILURE, errno, "fdopendir: %s", dirname);
163 while ((errno = 0, d = readdir (dir)) != NULL) {
164 if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */
167 strcpy (&path[len], d->d_name);
168 iterate_inputs (inputs, 1);
172 error (EXIT_FAILURE, errno, "readdir: %s", dirname);
174 if (closedir (dir) == -1)
175 error (EXIT_FAILURE, errno, "closedir: %s", dirname);
178 /* Copy kernel modules.
180 * Find every file under modpath.
182 * Exclude all *.ko files, *except* ones which match names in
183 * the whitelist (which may contain wildcards). Include all
186 * Add chosen files to the output.
188 * whitelist_file may be NULL, to include ALL kernel modules.
191 write_kernel_modules (const char *whitelist_file, const char *modpath)
193 char **whitelist = NULL;
194 if (whitelist_file != NULL)
195 whitelist = load_file (whitelist_file);
197 char *paths[2] = { (char *) modpath, NULL };
198 FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL);
200 error (EXIT_FAILURE, errno, "write_kernel_modules: fts_open: %s", modpath);
204 FTSENT *entry = fts_read (fts);
205 if (entry == NULL && errno != 0)
206 error (EXIT_FAILURE, errno, "write_kernel_modules: fts_read: %s", modpath);
210 /* Ignore directories being visited in post-order. */
211 if (entry->fts_info & FTS_DP)
214 /* Is it a *.ko file? */
215 if (entry->fts_namelen >= 3 &&
216 entry->fts_name[entry->fts_namelen-3] == '.' &&
217 entry->fts_name[entry->fts_namelen-2] == 'k' &&
218 entry->fts_name[entry->fts_namelen-1] == 'o') {
220 /* Is it a *.ko file which is on the whitelist? */
222 for (j = 0; whitelist[j] != NULL; ++j) {
224 r = fnmatch (whitelist[j], entry->fts_name, 0);
226 /* It's on the whitelist, so include it. */
228 fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n",
229 entry->fts_name, whitelist[j]);
230 cpio_append_fts_entry (entry);
232 } else if (r != FNM_NOMATCH)
233 error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
234 whitelist[j], entry->fts_name, 0, r);
236 } else { /* whitelist == NULL, always include */
238 fprintf (stderr, "including kernel module %s\n", entry->fts_name);
239 cpio_append_fts_entry (entry);
242 /* It's some other sort of file, or a directory, always include. */
243 cpio_append_fts_entry (entry);
246 if (fts_close (fts) == -1)
247 error (EXIT_FAILURE, errno, "write_kernel_modules: fts_close: %s", modpath);
250 /* Copy the host files.
252 * Read the list of entries in hostfiles (which may contain
253 * wildcards). Look them up in the filesystem, and add those files
254 * that exist. Ignore any files that don't exist or are not readable.
257 write_hostfiles (const char *hostfiles_file)
259 char **hostfiles = load_file (hostfiles_file);
261 /* Hostfiles list can contain "." before each path - ignore it.
262 * It also contains each directory name before we enter it. But
263 * we don't read that until we see a wildcard for that directory.
266 for (i = 0; hostfiles[i] != NULL; ++i) {
267 char *hostfile = hostfiles[i];
268 if (hostfile[0] == '.')
273 /* Is it a wildcard? */
274 if (strchr (hostfile, '*') || strchr (hostfile, '?')) {
275 char *dirname = xstrdup (hostfile);
276 char *patt = strrchr (dirname, '/');
280 char **files = read_dir (dirname);
281 files = filter_fnmatch (files, patt, FNM_NOESCAPE);
283 /* Add matching files. */
284 for (j = 0; files[j] != NULL; ++j) {
285 char *tmp = xasprintf ("%s/%s", dirname, files[j]);
288 fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt);
295 /* Else does this file/directory/whatever exist? */
296 else if (lstat (hostfile, &statbuf) == 0) {
298 fprintf (stderr, "including host file %s (directly referenced)\n",
301 cpio_append_stat (hostfile, &statbuf);
302 } /* Ignore files that don't exist. */
306 /* Copy contents of buffer to out_fd and keep out_offset correct. */
308 write_to_fd (const void *buffer, size_t len)
310 if (full_write (out_fd, buffer, len) != len)
311 error (EXIT_FAILURE, errno, "write");
315 /* Copy contents of file to out_fd. */
317 write_file_to_fd (const char *filename)
319 char buffer[BUFFER_SIZE];
324 fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
326 fd2 = open (filename, O_RDONLY);
328 error (EXIT_FAILURE, errno, "open: %s", filename);
330 r = read (fd2, buffer, sizeof buffer);
334 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
336 error (EXIT_FAILURE, errno, "read: %s", filename);
338 write_to_fd (buffer, r);
341 if (close (fd2) == -1)
342 error (EXIT_FAILURE, errno, "close: %s", filename);
345 /* Copy file of given length to output, and fail if the file has
349 write_file_len_to_fd (const char *filename, size_t len)
351 char buffer[BUFFER_SIZE];
355 fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
357 int fd2 = open (filename, O_RDONLY);
359 error (EXIT_FAILURE, errno, "open: %s", filename);
361 ssize_t r = read (fd2, buffer, sizeof buffer);
365 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
367 error (EXIT_FAILURE, errno, "read: %s", filename);
369 write_to_fd (buffer, r);
372 error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename);
375 if (close (fd2) == -1)
376 error (EXIT_FAILURE, errno, "close: %s", filename);
379 error (EXIT_FAILURE, 0, "febootstrap-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename);
382 /* Append the file pointed to by FTSENT to the cpio output. */
384 cpio_append_fts_entry (FTSENT *entry)
386 if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
387 cpio_append (entry->fts_path);
389 cpio_append_stat (entry->fts_path, entry->fts_statp);
392 /* Append the file named 'filename' to the cpio output. */
394 cpio_append (const char *filename)
398 if (lstat (filename, &statbuf) == -1)
399 error (EXIT_FAILURE, errno, "lstat: %s", filename);
400 cpio_append_stat (filename, &statbuf);
403 /* Append the file to the cpio output. */
404 #define PADDING(len) ((((len) + 3) & ~3) - (len))
406 #define CPIO_HEADER_LEN (6 + 13*8)
409 cpio_append_stat (const char *filename, struct stat *statbuf)
411 const char *orig_filename = filename;
413 if (*filename == '/')
415 if (*filename == '\0')
419 fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n",
420 orig_filename, statbuf->st_mode, out_fd);
422 /* Regular files and symlinks are the only ones that have a "body"
423 * in this cpio entry.
425 int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode);
427 size_t len = strlen (filename) + 1;
429 char header[CPIO_HEADER_LEN + 1];
430 snprintf (header, sizeof header,
434 "%08X" "%08X" /* uid, gid */
437 "%08X" /* file length */
438 "%08X" "%08X" /* device holding file major, minor */
439 "%08X" "%08X" /* for specials, device major, minor */
440 "%08X" /* name length (including \0 byte) */
441 "%08X", /* checksum (not used by the kernel) */
442 (unsigned) statbuf->st_ino, statbuf->st_mode,
443 statbuf->st_uid, statbuf->st_gid,
444 (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime,
445 has_body ? (unsigned) statbuf->st_size : 0,
446 major (statbuf->st_dev), minor (statbuf->st_dev),
447 major (statbuf->st_rdev), minor (statbuf->st_rdev),
450 /* Write the header. */
451 write_to_fd (header, CPIO_HEADER_LEN);
453 /* Follow with the filename, and pad it. */
454 write_to_fd (filename, len);
455 size_t padding_len = PADDING (CPIO_HEADER_LEN + len);
456 write_padding (padding_len);
458 /* Follow with the file or symlink content, and pad it. */
460 if (S_ISREG (statbuf->st_mode))
461 write_file_len_to_fd (orig_filename, statbuf->st_size);
462 else if (S_ISLNK (statbuf->st_mode)) {
464 if (readlink (orig_filename, tmp, sizeof tmp) == -1)
465 error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
466 write_to_fd (tmp, statbuf->st_size);
469 padding_len = PADDING (statbuf->st_size);
470 write_padding (padding_len);
476 cpio_append_trailer (void)
479 memset (&statbuf, 0, sizeof statbuf);
480 statbuf.st_nlink = 1;
481 cpio_append_stat ("TRAILER!!!", &statbuf);
483 /* CPIO seems to pad up to the next block boundary, ie. up to
484 * the next 512 bytes.
486 write_padding (((out_offset + 511) & ~511) - out_offset);
487 assert ((out_offset & 511) == 0);
490 /* Write 'len' bytes of zeroes out. */
492 write_padding (size_t len)
494 static const char buffer[512] = { 0 };
497 size_t n = len < sizeof buffer ? len : sizeof buffer;
498 write_to_fd (buffer, n);