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 /* This script builds the supermin appliance on the fly each
20 * time the appliance runs.
22 * *NOTE*: This program is designed to be very short-lived, and so we
23 * don't normally bother to free up any memory that we allocate.
24 * That's not completely true - we free up stuff if it's obvious and
25 * easy to free up, and ignore the rest.
43 #include <sys/types.h>
49 #include "filevercmp.h"
51 #include "full-write.h"
55 #include "xvasprintf.h"
57 /* Directory containing candidate kernels. We could make this
58 * configurable at some point.
60 #define KERNELDIR "/boot"
61 #define MODULESDIR "/lib/modules"
63 /* Buffer size used in copy operations throughout. Large for
64 * greatest efficiency.
66 #define BUFFER_SIZE 65536
68 static struct timeval start_t;
69 static int verbose = 0;
71 static void print_timestamped_message (const char *fs, ...);
72 static const char *create_kernel (const char *hostcpu, const char *kernel);
73 static void create_appliance (char **inputs, int nr_inputs, const char *whitelist, const char *modpath, const char *initrd);
75 enum { HELP_OPTION = CHAR_MAX + 1 };
77 static const char *options = "k:vV";
78 static const struct option long_options[] = {
79 { "help", 0, 0, HELP_OPTION },
80 { "kmods", required_argument, 0, 'k' },
81 { "verbose", 0, 0, 'v' },
82 { "version", 0, 0, 'V' },
87 usage (const char *progname)
89 printf ("%s: build the supermin appliance on the fly\n"
92 " %s [-options] inputs [...] whitelist host_cpu kernel initrd\n"
96 "This script is used by febootstrap to build the supermin appliance\n"
97 "(kernel and initrd output files). You should NOT need to run this\n"
98 "program directly except if you are debugging tricky supermin\n"
99 "appliance problems.\n"
101 "NB: The kernel and initrd parameters are OUTPUT parameters. If\n"
102 "those files exist, they are overwritten by the output.\n"
106 " Display this help text and exit.\n"
107 " -k file | --kmods file\n"
108 " Specify kernel module whitelist.\n"
110 " Enable verbose messages (give multiple times for more verbosity).\n"
112 " Display version number and exit.\n",
113 progname, progname, progname, progname);
117 main (int argc, char *argv[])
119 /* First thing: start the clock. */
120 gettimeofday (&start_t, NULL);
122 const char *whitelist = NULL;
124 /* Command line arguments. */
126 int c = getopt_long (argc, argv, options, long_options, NULL);
143 printf (PACKAGE_NAME " " PACKAGE_VERSION "\n");
152 char **inputs = &argv[optind];
153 int nr_inputs = argc - optind - 3;
160 /* See: https://bugzilla.redhat.com/show_bug.cgi?id=558593 */
161 const char *hostcpu = argv[argc-3];
164 const char *kernel = argv[argc-2];
165 const char *initrd = argv[argc-1];
168 print_timestamped_message ("whitelist = %s, "
172 whitelist ? : "(not specified)",
173 hostcpu, kernel, initrd);
175 for (i = 0; i < nr_inputs; ++i)
176 print_timestamped_message ("inputs[%d] = %s", i, inputs[i]);
179 /* Remove the output files if they exist. */
183 /* Create kernel output file. */
185 modpath = create_kernel (hostcpu, kernel);
188 print_timestamped_message ("finished creating kernel");
190 /* Create the appliance. */
191 create_appliance (inputs, nr_inputs, whitelist, modpath, initrd);
194 print_timestamped_message ("finished creating appliance");
199 /* Compute Y - X and return the result in milliseconds.
200 * Approximately the same as this code:
201 * http://www.mpp.mpg.de/~huber/util/timevaldiff.c
204 timeval_diff (const struct timeval *x, const struct timeval *y)
208 msec = (y->tv_sec - x->tv_sec) * 1000;
209 msec += (y->tv_usec - x->tv_usec) / 1000;
214 print_timestamped_message (const char *fs, ...)
217 gettimeofday (&tv, NULL);
224 err = vasprintf (&msg, fs, args);
229 fprintf (stderr, "supermin helper [%05" PRIi64 "ms] %s\n",
230 timeval_diff (&start_t, &tv), msg);
235 static char **read_dir (const char *dir);
236 static char **filter (char **strings, int (*)(const char *));
237 static char **filter_fnmatch (char **strings, const char *patt, int flags);
238 static char **filter_notmatching_substring (char **strings, const char *sub);
239 static void sort (char **strings, int (*compare) (const void *, const void *));
240 static int isdir (const char *path);
243 reverse_filevercmp (const void *p1, const void *p2)
245 const char *s1 = * (char * const *) p1;
246 const char *s2 = * (char * const *) p2;
248 /* Note, arguments are reversed to achieve a reverse sort. */
249 return filevercmp (s2, s1);
253 get_modpath (const char *kernel_name)
255 /* Ignore "vmlinuz-" at the beginning of the kernel name. */
256 const char *version = &kernel_name[8];
258 /* /lib/modules/<version> */
259 char *modpath = xasprintf (MODULESDIR "/%s", version);
261 perror ("xasprintf");
268 /* kernel_name is "vmlinuz-*". Check if there is a corresponding
269 * module path in /lib/modules.
272 has_modpath (const char *kernel_name)
274 char *modpath = get_modpath (kernel_name);
277 fprintf (stderr, "checking modpath %s is a directory\n", modpath);
279 int r = isdir (modpath);
283 fprintf (stderr, "picked %s because modpath %s exists\n",
284 kernel_name, modpath);
294 /* Create the kernel. This chooses an appropriate kernel and makes a
297 * Look for the most recent kernel named vmlinuz-*.<arch>* which has a
298 * corresponding directory in /lib/modules/. If the architecture is
299 * x86, look for any x86 kernel.
301 * RHEL 5 didn't append the arch to the kernel name, so look for
302 * kernels without arch second.
304 * If no suitable kernel can be found, exit with an error.
306 * This function returns the module path (ie. /lib/modules/<version>).
309 create_kernel (const char *hostcpu, const char *kernel)
311 char **all_files = read_dir (KERNELDIR);
313 /* In original: ls -1dvr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen */
315 if (hostcpu[0] == 'i' && hostcpu[2] == '8' && hostcpu[3] == '6' &&
317 patt = "vmlinuz-*.i?86*";
319 patt = xasprintf ("vmlinuz-*.%s*", hostcpu);
322 candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
323 candidates = filter_notmatching_substring (candidates, "xen");
324 candidates = filter (candidates, has_modpath);
326 if (candidates[0] == NULL) {
327 /* In original: ls -1dvr /boot/vmlinuz-* 2>/dev/null | grep -v xen */
329 candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
330 candidates = filter_notmatching_substring (candidates, "xen");
331 candidates = filter (candidates, has_modpath);
333 if (candidates[0] == NULL)
337 sort (candidates, reverse_filevercmp);
339 /* Choose the first candidate. */
340 char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]);
343 fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
345 if (symlink (tmp, kernel) == -1)
346 error (EXIT_FAILURE, errno, "symlink kernel");
350 return get_modpath (candidates[0]);
352 /* Print more diagnostics here than the old script did. */
355 "febootstrap-supermin-helper: failed to find a suitable kernel.\n"
356 "I looked for kernels in " KERNELDIR " and modules in " MODULESDIR
358 "If this is a Xen guest, and you only have Xen domU kernels\n"
359 "installed, try installing a fullvirt kernel (only for\n"
360 "febootstrap use, you shouldn't boot the Xen guest with it).\n");
364 static void iterate_inputs (char **inputs, int nr_inputs);
365 static void iterate_input_directory (const char *dirname, int dirfd);
366 static void write_kernel_modules (const char *whitelist, const char *modpath);
367 static void write_hostfiles (const char *hostfiles_file);
368 static void write_to_fd (const void *buffer, size_t len);
369 static void write_file_to_fd (const char *filename);
370 static void write_file_len_to_fd (const char *filename, size_t len);
371 static void write_padding (size_t len);
372 static char **load_file (const char *filename);
373 static void cpio_append_fts_entry (FTSENT *entry);
374 static void cpio_append_stat (const char *filename, struct stat *);
375 static void cpio_append (const char *filename);
376 static void cpio_append_trailer (void);
378 static int out_fd = -1;
379 static off_t out_offset = 0;
381 /* Create the appliance.
383 * The initrd consists of these components concatenated together:
385 * (1) The base skeleton appliance that we constructed at build time.
386 * format = plain cpio
387 * (2) The host files which match wildcards in *.supermin.hostfiles.
388 * input format = plain text, output format = plain cpio
389 * (3) The modules from modpath which are on the module whitelist.
390 * output format = plain cpio
392 * The original shell script used the external cpio program to create
393 * parts (2) and (3), but we have decided it's going to be faster if
394 * we just write out the data outselves. The reasons are that
395 * external cpio is slow (particularly when used with SELinux because
396 * it does 512 byte reads), and the format that we're writing is
397 * narrow and well understood, because we only care that the Linux
398 * kernel can read it.
400 * This version contains some improvements over the C version written
401 * for libguestfs, in that we can have multiple base images (or
402 * hostfiles) or use a directory to store these files.
405 create_appliance (char **inputs, int nr_inputs,
406 const char *whitelist,
410 out_fd = open (initrd, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
412 error (EXIT_FAILURE, errno, "open: %s", initrd);
415 iterate_inputs (inputs, nr_inputs);
417 /* Kernel modules (3). */
418 write_kernel_modules (whitelist, modpath);
420 cpio_append_trailer ();
422 /* Finish off and close output file. */
423 if (close (out_fd) == -1)
424 error (EXIT_FAILURE, errno, "close: %s", initrd);
427 /* Iterate over the inputs to find out what they are, visiting
428 * directories if specified.
431 iterate_inputs (char **inputs, int nr_inputs)
434 for (i = 0; i < nr_inputs; ++i) {
436 print_timestamped_message ("visiting %s", inputs[i]);
438 int fd = open (inputs[i], O_RDONLY);
440 error (EXIT_FAILURE, errno, "open: %s", inputs[i]);
443 if (fstat (fd, &statbuf) == -1)
444 error (EXIT_FAILURE, errno, "fstat: %s", inputs[i]);
447 if (S_ISDIR (statbuf.st_mode))
448 iterate_input_directory (inputs[i], fd);
449 else if (S_ISREG (statbuf.st_mode)) {
450 /* Is it a cpio file? */
452 if (read (fd, buf, 6) == 6 && memcmp (buf, "070701", 6) == 0)
453 /* Yes, a cpio file. This is a skeleton appliance, case (1). */
454 write_file_to_fd (inputs[i]);
456 /* No, must be hostfiles, case (2). */
457 write_hostfiles (inputs[i]);
460 error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory",
468 iterate_input_directory (const char *dirname, int dirfd)
471 strcpy (path, dirname);
472 size_t len = strlen (dirname);
475 char *inputs[] = { path };
477 DIR *dir = fdopendir (dirfd);
479 error (EXIT_FAILURE, errno, "fdopendir: %s", dirname);
482 while ((errno = 0, d = readdir (dir)) != NULL) {
483 if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */
486 strcpy (&path[len], d->d_name);
487 iterate_inputs (inputs, 1);
491 error (EXIT_FAILURE, errno, "readdir: %s", dirname);
493 if (closedir (dir) == -1)
494 error (EXIT_FAILURE, errno, "closedir: %s", dirname);
497 /* Copy kernel modules.
499 * Find every file under modpath.
501 * Exclude all *.ko files, *except* ones which match names in
502 * the whitelist (which may contain wildcards). Include all
505 * Add chosen files to the output.
507 * whitelist_file may be NULL, to include ALL kernel modules.
510 write_kernel_modules (const char *whitelist_file, const char *modpath)
512 char **whitelist = NULL;
513 if (whitelist_file != NULL)
514 whitelist = load_file (whitelist_file);
516 char *paths[2] = { (char *) modpath, NULL };
517 FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL);
519 error (EXIT_FAILURE, errno, "write_kernel_modules: fts_open: %s", modpath);
523 FTSENT *entry = fts_read (fts);
524 if (entry == NULL && errno != 0)
525 error (EXIT_FAILURE, errno, "write_kernel_modules: fts_read: %s", modpath);
529 /* Ignore directories being visited in post-order. */
530 if (entry->fts_info & FTS_DP)
533 /* Is it a *.ko file? */
534 if (entry->fts_namelen >= 3 &&
535 entry->fts_name[entry->fts_namelen-3] == '.' &&
536 entry->fts_name[entry->fts_namelen-2] == 'k' &&
537 entry->fts_name[entry->fts_namelen-1] == 'o') {
539 /* Is it a *.ko file which is on the whitelist? */
541 for (j = 0; whitelist[j] != NULL; ++j) {
543 r = fnmatch (whitelist[j], entry->fts_name, 0);
545 /* It's on the whitelist, so include it. */
547 fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n",
548 entry->fts_name, whitelist[j]);
549 cpio_append_fts_entry (entry);
551 } else if (r != FNM_NOMATCH)
552 error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
553 whitelist[j], entry->fts_name, 0, r);
555 } else { /* whitelist == NULL, always include */
557 fprintf (stderr, "including kernel module %s\n", entry->fts_name);
558 cpio_append_fts_entry (entry);
561 /* It's some other sort of file, or a directory, always include. */
562 cpio_append_fts_entry (entry);
565 if (fts_close (fts) == -1)
566 error (EXIT_FAILURE, errno, "write_kernel_modules: fts_close: %s", modpath);
569 /* Copy the host files.
571 * Read the list of entries in hostfiles (which may contain
572 * wildcards). Look them up in the filesystem, and add those files
573 * that exist. Ignore any files that don't exist or are not readable.
576 write_hostfiles (const char *hostfiles_file)
578 char **hostfiles = load_file (hostfiles_file);
580 /* Hostfiles list can contain "." before each path - ignore it.
581 * It also contains each directory name before we enter it. But
582 * we don't read that until we see a wildcard for that directory.
585 for (i = 0; hostfiles[i] != NULL; ++i) {
586 char *hostfile = hostfiles[i];
587 if (hostfile[0] == '.')
592 /* Is it a wildcard? */
593 if (strchr (hostfile, '*') || strchr (hostfile, '?')) {
594 char *dirname = xstrdup (hostfile);
595 char *patt = strrchr (dirname, '/');
599 char **files = read_dir (dirname);
600 files = filter_fnmatch (files, patt, FNM_NOESCAPE);
602 /* Add matching files. */
603 for (j = 0; files[j] != NULL; ++j) {
604 char *tmp = xasprintf ("%s/%s", dirname, files[j]);
607 fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt);
614 /* Else does this file/directory/whatever exist? */
615 else if (lstat (hostfile, &statbuf) == 0) {
617 fprintf (stderr, "including host file %s (directly referenced)\n",
620 cpio_append_stat (hostfile, &statbuf);
621 } /* Ignore files that don't exist. */
626 /* Helper functions. */
629 add_string (char ***argv, size_t *n_used, size_t *n_alloc, const char *str)
633 if (*n_used >= *n_alloc)
634 *argv = x2nrealloc (*argv, n_alloc, sizeof (char *));
637 new_str = xstrdup (str);
641 (*argv)[*n_used] = new_str;
647 count_strings (char *const *argv)
651 for (argc = 0; argv[argc] != NULL; ++argc)
662 dir_cache_hash (void const *x, size_t table_size)
664 struct dir_cache const *p = x;
665 return hash_pjw (p->path, table_size);
669 dir_cache_compare (void const *x, void const *y)
671 struct dir_cache const *p = x;
672 struct dir_cache const *q = y;
673 return strcmp (p->path, q->path) == 0;
676 /* Read a directory into a list of strings.
678 * Previously looked up directories are cached and returned quickly,
679 * saving some considerable amount of time compared to reading the
680 * directory over again. However this means you really must not
681 * alter the array of strings that are returned.
683 * Returns an empty list if the directory cannot be opened.
686 read_dir (const char *name)
688 static Hash_table *ht = NULL;
691 ht = hash_initialize (1024, NULL, dir_cache_hash, dir_cache_compare, NULL);
693 struct dir_cache key = { .path = (char *) name };
694 struct dir_cache *p = hash_lookup (ht, &key);
699 size_t n_used = 0, n_alloc = 0;
701 DIR *dir = opendir (name);
703 /* If it fails to open, that's OK, skip to the end. */
710 struct dirent *d = readdir (dir);
713 /* But if it fails here, after opening and potentially reading
714 * part of the directory, that's a proper failure - inform the
717 error (EXIT_FAILURE, errno, "%s", name);
721 add_string (&files, &n_used, &n_alloc, d->d_name);
724 if (closedir (dir) == -1)
725 error (EXIT_FAILURE, errno, "closedir: %s", name);
728 /* NULL-terminate the array. */
729 add_string (&files, &n_used, &n_alloc, NULL);
731 /* Add it to the hash for next time. */
732 p = xmalloc (sizeof *p);
733 p->path = (char *) name;
735 p = hash_insert (ht, p);
741 /* Filter a list of strings, returning only those where f(s) != 0. */
743 filter (char **strings, int (*f) (const char *))
746 size_t n_used = 0, n_alloc = 0;
749 for (i = 0; strings[i] != NULL; ++i) {
750 if (f (strings[i]) != 0)
751 add_string (&out, &n_used, &n_alloc, strings[i]);
754 add_string (&out, &n_used, &n_alloc, NULL);
758 /* Filter a list of strings and return only those matching the wildcard. */
760 filter_fnmatch (char **strings, const char *patt, int flags)
763 size_t n_used = 0, n_alloc = 0;
766 for (i = 0; strings[i] != NULL; ++i) {
767 r = fnmatch (patt, strings[i], flags);
769 add_string (&out, &n_used, &n_alloc, strings[i]);
770 else if (r != FNM_NOMATCH)
771 error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
772 patt, strings[i], flags, r);
775 add_string (&out, &n_used, &n_alloc, NULL);
779 /* Filter a list of strings and return only those which DON'T contain sub. */
781 filter_notmatching_substring (char **strings, const char *sub)
784 size_t n_used = 0, n_alloc = 0;
787 for (i = 0; strings[i] != NULL; ++i) {
788 if (strstr (strings[i], sub) == NULL)
789 add_string (&out, &n_used, &n_alloc, strings[i]);
792 add_string (&out, &n_used, &n_alloc, NULL);
796 /* Sort a list of strings, in place, with the comparison function supplied. */
798 sort (char **strings, int (*compare) (const void *, const void *))
800 qsort (strings, count_strings (strings), sizeof (char *), compare);
803 /* Return true iff path exists and is a directory. This version
807 isdir (const char *path)
811 if (stat (path, &statbuf) == -1)
814 return S_ISDIR (statbuf.st_mode);
817 /* Copy contents of buffer to out_fd and keep out_offset correct. */
819 write_to_fd (const void *buffer, size_t len)
821 if (full_write (out_fd, buffer, len) != len)
822 error (EXIT_FAILURE, errno, "write");
826 /* Copy contents of file to out_fd. */
828 write_file_to_fd (const char *filename)
830 char buffer[BUFFER_SIZE];
835 fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
837 fd2 = open (filename, O_RDONLY);
839 error (EXIT_FAILURE, errno, "open: %s", filename);
841 r = read (fd2, buffer, sizeof buffer);
845 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
847 error (EXIT_FAILURE, errno, "read: %s", filename);
849 write_to_fd (buffer, r);
852 if (close (fd2) == -1)
853 error (EXIT_FAILURE, errno, "close: %s", filename);
856 /* Copy file of given length to output, and fail if the file has
860 write_file_len_to_fd (const char *filename, size_t len)
862 char buffer[BUFFER_SIZE];
866 fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
868 int fd2 = open (filename, O_RDONLY);
870 error (EXIT_FAILURE, errno, "open: %s", filename);
872 ssize_t r = read (fd2, buffer, sizeof buffer);
876 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
878 error (EXIT_FAILURE, errno, "read: %s", filename);
880 write_to_fd (buffer, r);
883 error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename);
886 if (close (fd2) == -1)
887 error (EXIT_FAILURE, errno, "close: %s", filename);
890 error (EXIT_FAILURE, 0, "febootstrap-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename);
893 /* Load in a file, returning a list of lines. */
895 load_file (const char *filename)
898 size_t n_used = 0, n_alloc = 0;
901 fp = fopen (filename, "r");
903 error (EXIT_FAILURE, errno, "fopen: %s", filename);
906 while (fgets (line, sizeof line, fp)) {
907 size_t len = strlen (line);
908 if (len > 0 && line[len-1] == '\n')
910 add_string (&lines, &n_used, &n_alloc, line);
913 add_string (&lines, &n_used, &n_alloc, NULL);
917 /* Append the file pointed to by FTSENT to the cpio output. */
919 cpio_append_fts_entry (FTSENT *entry)
921 if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
922 cpio_append (entry->fts_path);
924 cpio_append_stat (entry->fts_path, entry->fts_statp);
927 /* Append the file named 'filename' to the cpio output. */
929 cpio_append (const char *filename)
933 if (lstat (filename, &statbuf) == -1)
934 error (EXIT_FAILURE, errno, "lstat: %s", filename);
935 cpio_append_stat (filename, &statbuf);
938 /* Append the file to the cpio output. */
939 #define PADDING(len) ((((len) + 3) & ~3) - (len))
941 #define CPIO_HEADER_LEN (6 + 13*8)
944 cpio_append_stat (const char *filename, struct stat *statbuf)
946 const char *orig_filename = filename;
948 if (*filename == '/')
950 if (*filename == '\0')
954 fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n",
955 orig_filename, statbuf->st_mode, out_fd);
957 /* Regular files and symlinks are the only ones that have a "body"
958 * in this cpio entry.
960 int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode);
962 size_t len = strlen (filename) + 1;
964 char header[CPIO_HEADER_LEN + 1];
965 snprintf (header, sizeof header,
969 "%08X" "%08X" /* uid, gid */
972 "%08X" /* file length */
973 "%08X" "%08X" /* device holding file major, minor */
974 "%08X" "%08X" /* for specials, device major, minor */
975 "%08X" /* name length (including \0 byte) */
976 "%08X", /* checksum (not used by the kernel) */
977 (unsigned) statbuf->st_ino, statbuf->st_mode,
978 statbuf->st_uid, statbuf->st_gid,
979 (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime,
980 has_body ? (unsigned) statbuf->st_size : 0,
981 major (statbuf->st_dev), minor (statbuf->st_dev),
982 major (statbuf->st_rdev), minor (statbuf->st_rdev),
985 /* Write the header. */
986 write_to_fd (header, CPIO_HEADER_LEN);
988 /* Follow with the filename, and pad it. */
989 write_to_fd (filename, len);
990 size_t padding_len = PADDING (CPIO_HEADER_LEN + len);
991 write_padding (padding_len);
993 /* Follow with the file or symlink content, and pad it. */
995 if (S_ISREG (statbuf->st_mode))
996 write_file_len_to_fd (orig_filename, statbuf->st_size);
997 else if (S_ISLNK (statbuf->st_mode)) {
999 if (readlink (orig_filename, tmp, sizeof tmp) == -1)
1000 error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
1001 write_to_fd (tmp, statbuf->st_size);
1004 padding_len = PADDING (statbuf->st_size);
1005 write_padding (padding_len);
1011 cpio_append_trailer (void)
1013 struct stat statbuf;
1014 memset (&statbuf, 0, sizeof statbuf);
1015 statbuf.st_nlink = 1;
1016 cpio_append_stat ("TRAILER!!!", &statbuf);
1018 /* CPIO seems to pad up to the next block boundary, ie. up to
1019 * the next 512 bytes.
1021 write_padding (((out_offset + 511) & ~511) - out_offset);
1022 assert ((out_offset & 511) == 0);
1025 /* Write 'len' bytes of zeroes out. */
1027 write_padding (size_t len)
1029 static const char buffer[512] = { 0 };
1032 size_t n = len < sizeof buffer ? len : sizeof buffer;
1033 write_to_fd (buffer, n);