X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=helper%2Fappliance.c;h=c4d0b32117eedefb0604b2e3cdc723d67c61de61;hb=2e1e2d686ca8f819c202d2ed4e8a5c5758451f26;hp=c7200bd1585f5c7c0076f3ff3f896c028ff6cfe8;hpb=e23a9c8f05e3646feb826d5db36d8656a80a27ab;p=febootstrap.git diff --git a/helper/appliance.c b/helper/appliance.c index c7200bd..c4d0b32 100644 --- a/helper/appliance.c +++ b/helper/appliance.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -32,32 +31,15 @@ #include "error.h" #include "fts_.h" -#include "full-write.h" #include "xalloc.h" #include "xvasprintf.h" #include "helper.h" -/* Buffer size used in copy operations throughout. Large for - * greatest efficiency. - */ -#define BUFFER_SIZE 65536 - -static void iterate_inputs (char **inputs, int nr_inputs); -static void iterate_input_directory (const char *dirname, int dirfd); -static void write_kernel_modules (const char *whitelist, const char *modpath); -static void write_hostfiles (const char *hostfiles_file); -static void write_to_fd (const void *buffer, size_t len); -static void write_file_to_fd (const char *filename); -static void write_file_len_to_fd (const char *filename, size_t len); -static void write_padding (size_t len); -static void cpio_append_fts_entry (FTSENT *entry); -static void cpio_append_stat (const char *filename, struct stat *); -static void cpio_append (const char *filename); -static void cpio_append_trailer (void); - -static int out_fd = -1; -static off_t out_offset = 0; +static void iterate_inputs (char **inputs, int nr_inputs, struct writer *); +static void iterate_input_directory (const char *dirname, int dirfd, struct writer *); +static void add_kernel_modules (const char *whitelist, const char *modpath, struct writer *); +static void add_hostfiles (const char *hostfiles_file, struct writer *); /* Create the appliance. * @@ -83,33 +65,30 @@ static off_t out_offset = 0; * hostfiles) or use a directory to store these files. */ void -create_appliance (char **inputs, int nr_inputs, +create_appliance (const char *hostcpu, + char **inputs, int nr_inputs, const char *whitelist, const char *modpath, - const char *initrd) + const char *initrd, + const char *appliance, + struct writer *writer) { - out_fd = open (initrd, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644); - if (out_fd == -1) - error (EXIT_FAILURE, errno, "open: %s", initrd); - out_offset = 0; + writer->wr_start (hostcpu, appliance, modpath, initrd); - iterate_inputs (inputs, nr_inputs); + iterate_inputs (inputs, nr_inputs, writer); + writer->wr_file ("/lib/modules"); /* Kernel modules (3). */ - write_kernel_modules (whitelist, modpath); - - cpio_append_trailer (); + add_kernel_modules (whitelist, modpath, writer); - /* Finish off and close output file. */ - if (close (out_fd) == -1) - error (EXIT_FAILURE, errno, "close: %s", initrd); + writer->wr_end (); } /* Iterate over the inputs to find out what they are, visiting * directories if specified. */ static void -iterate_inputs (char **inputs, int nr_inputs) +iterate_inputs (char **inputs, int nr_inputs, struct writer *writer) { int i; for (i = 0; i < nr_inputs; ++i) { @@ -126,16 +105,16 @@ iterate_inputs (char **inputs, int nr_inputs) /* Directory? */ if (S_ISDIR (statbuf.st_mode)) - iterate_input_directory (inputs[i], fd); + iterate_input_directory (inputs[i], fd, writer); else if (S_ISREG (statbuf.st_mode)) { /* Is it a cpio file? */ char buf[6]; if (read (fd, buf, 6) == 6 && memcmp (buf, "070701", 6) == 0) /* Yes, a cpio file. This is a skeleton appliance, case (1). */ - write_file_to_fd (inputs[i]); + writer->wr_cpio_file (inputs[i]); else /* No, must be hostfiles, case (2). */ - write_hostfiles (inputs[i]); + add_hostfiles (inputs[i], writer); } else error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory", @@ -145,27 +124,33 @@ iterate_inputs (char **inputs, int nr_inputs) } } -static void -iterate_input_directory (const char *dirname, int dirfd) +static int +string_compare (const void *p1, const void *p2) { - char path[PATH_MAX]; - strcpy (path, dirname); - size_t len = strlen (dirname); - path[len++] = '/'; - - char *inputs[] = { path }; + return strcmp (* (char * const *) p1, * (char * const *) p2); +} +static void +iterate_input_directory (const char *dirname, int dirfd, struct writer *writer) +{ DIR *dir = fdopendir (dirfd); if (dir == NULL) error (EXIT_FAILURE, errno, "fdopendir: %s", dirname); + char **entries = NULL; + size_t nr_entries = 0, nr_alloc = 0; + struct dirent *d; while ((errno = 0, d = readdir (dir)) != NULL) { if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */ continue; - strcpy (&path[len], d->d_name); - iterate_inputs (inputs, 1); + /* Ignore *~ files created by editors. */ + size_t len = strlen (d->d_name); + if (len > 0 && d->d_name[len-1] == '~') + continue; + + add_string (&entries, &nr_entries, &nr_alloc, d->d_name); } if (errno != 0) @@ -173,6 +158,27 @@ iterate_input_directory (const char *dirname, int dirfd) if (closedir (dir) == -1) error (EXIT_FAILURE, errno, "closedir: %s", dirname); + + add_string (&entries, &nr_entries, &nr_alloc, NULL); + + /* Visit directory entries in order. In febootstrap <= 2.8 we + * didn't impose any order, but that led to some difficult + * heisenbugs. + */ + sort (entries, string_compare); + + char path[PATH_MAX]; + strcpy (path, dirname); + size_t len = strlen (dirname); + path[len++] = '/'; + + char *inputs[] = { path }; + + size_t i; + for (i = 0; entries[i] != NULL; ++i) { + strcpy (&path[len], entries[i]); + iterate_inputs (inputs, 1, writer); + } } /* Copy kernel modules. @@ -188,8 +194,12 @@ iterate_input_directory (const char *dirname, int dirfd) * whitelist_file may be NULL, to include ALL kernel modules. */ static void -write_kernel_modules (const char *whitelist_file, const char *modpath) +add_kernel_modules (const char *whitelist_file, const char *modpath, + struct writer *writer) { + if (verbose) + print_timestamped_message ("adding kernel modules"); + char **whitelist = NULL; if (whitelist_file != NULL) whitelist = load_file (whitelist_file); @@ -197,13 +207,13 @@ write_kernel_modules (const char *whitelist_file, const char *modpath) char *paths[2] = { (char *) modpath, NULL }; FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL); if (fts == NULL) - error (EXIT_FAILURE, errno, "write_kernel_modules: fts_open: %s", modpath); + error (EXIT_FAILURE, errno, "add_kernel_modules: fts_open: %s", modpath); for (;;) { errno = 0; FTSENT *entry = fts_read (fts); if (entry == NULL && errno != 0) - error (EXIT_FAILURE, errno, "write_kernel_modules: fts_read: %s", modpath); + error (EXIT_FAILURE, errno, "add_kernel_modules: fts_read: %s", modpath); if (entry == NULL) break; @@ -227,7 +237,7 @@ write_kernel_modules (const char *whitelist_file, const char *modpath) if (verbose >= 2) fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n", entry->fts_name, whitelist[j]); - cpio_append_fts_entry (entry); + writer->wr_fts_entry (entry); break; } else if (r != FNM_NOMATCH) error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n", @@ -236,15 +246,15 @@ write_kernel_modules (const char *whitelist_file, const char *modpath) } else { /* whitelist == NULL, always include */ if (verbose >= 2) fprintf (stderr, "including kernel module %s\n", entry->fts_name); - cpio_append_fts_entry (entry); + writer->wr_fts_entry (entry); } } else /* It's some other sort of file, or a directory, always include. */ - cpio_append_fts_entry (entry); + writer->wr_fts_entry (entry); } if (fts_close (fts) == -1) - error (EXIT_FAILURE, errno, "write_kernel_modules: fts_close: %s", modpath); + error (EXIT_FAILURE, errno, "add_kernel_modules: fts_close: %s", modpath); } /* Copy the host files. @@ -254,7 +264,7 @@ write_kernel_modules (const char *whitelist_file, const char *modpath) * that exist. Ignore any files that don't exist or are not readable. */ static void -write_hostfiles (const char *hostfiles_file) +add_hostfiles (const char *hostfiles_file, struct writer *writer) { char **hostfiles = load_file (hostfiles_file); @@ -274,7 +284,9 @@ write_hostfiles (const char *hostfiles_file) if (strchr (hostfile, '*') || strchr (hostfile, '?')) { char *dirname = xstrdup (hostfile); char *patt = strrchr (dirname, '/'); - assert (patt); + if (!patt) + error (EXIT_FAILURE, 0, "%s: line %zu: invalid pattern\n(is this file a supermin appliance hostfiles file?)", + hostfiles_file, i+1); *patt++ = '\0'; char **files = read_dir (dirname); @@ -287,7 +299,7 @@ write_hostfiles (const char *hostfiles_file) if (verbose >= 2) fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt); - cpio_append (tmp); + writer->wr_file (tmp); free (tmp); } @@ -298,204 +310,7 @@ write_hostfiles (const char *hostfiles_file) fprintf (stderr, "including host file %s (directly referenced)\n", hostfile); - cpio_append_stat (hostfile, &statbuf); + writer->wr_file_stat (hostfile, &statbuf); } /* Ignore files that don't exist. */ } } - -/* Copy contents of buffer to out_fd and keep out_offset correct. */ -static void -write_to_fd (const void *buffer, size_t len) -{ - if (full_write (out_fd, buffer, len) != len) - error (EXIT_FAILURE, errno, "write"); - out_offset += len; -} - -/* Copy contents of file to out_fd. */ -static void -write_file_to_fd (const char *filename) -{ - char buffer[BUFFER_SIZE]; - int fd2; - ssize_t r; - - if (verbose >= 2) - fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd); - - fd2 = open (filename, O_RDONLY); - if (fd2 == -1) - error (EXIT_FAILURE, errno, "open: %s", filename); - for (;;) { - r = read (fd2, buffer, sizeof buffer); - if (r == 0) - break; - if (r == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) - continue; - error (EXIT_FAILURE, errno, "read: %s", filename); - } - write_to_fd (buffer, r); - } - - if (close (fd2) == -1) - error (EXIT_FAILURE, errno, "close: %s", filename); -} - -/* Copy file of given length to output, and fail if the file has - * changed size. - */ -static void -write_file_len_to_fd (const char *filename, size_t len) -{ - char buffer[BUFFER_SIZE]; - size_t count = 0; - - if (verbose >= 2) - fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd); - - int fd2 = open (filename, O_RDONLY); - if (fd2 == -1) - error (EXIT_FAILURE, errno, "open: %s", filename); - for (;;) { - ssize_t r = read (fd2, buffer, sizeof buffer); - if (r == 0) - break; - if (r == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) - continue; - error (EXIT_FAILURE, errno, "read: %s", filename); - } - write_to_fd (buffer, r); - count += r; - if (count > len) - error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename); - } - - if (close (fd2) == -1) - error (EXIT_FAILURE, errno, "close: %s", filename); - - if (count != len) - error (EXIT_FAILURE, 0, "febootstrap-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename); -} - -/* Append the file pointed to by FTSENT to the cpio output. */ -static void -cpio_append_fts_entry (FTSENT *entry) -{ - if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK) - cpio_append (entry->fts_path); - else - cpio_append_stat (entry->fts_path, entry->fts_statp); -} - -/* Append the file named 'filename' to the cpio output. */ -static void -cpio_append (const char *filename) -{ - struct stat statbuf; - - if (lstat (filename, &statbuf) == -1) - error (EXIT_FAILURE, errno, "lstat: %s", filename); - cpio_append_stat (filename, &statbuf); -} - -/* Append the file to the cpio output. */ -#define PADDING(len) ((((len) + 3) & ~3) - (len)) - -#define CPIO_HEADER_LEN (6 + 13*8) - -static void -cpio_append_stat (const char *filename, struct stat *statbuf) -{ - const char *orig_filename = filename; - - if (*filename == '/') - filename++; - if (*filename == '\0') - filename = "."; - - if (verbose >= 2) - fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n", - orig_filename, statbuf->st_mode, out_fd); - - /* Regular files and symlinks are the only ones that have a "body" - * in this cpio entry. - */ - int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode); - - size_t len = strlen (filename) + 1; - - char header[CPIO_HEADER_LEN + 1]; - snprintf (header, sizeof header, - "070701" /* magic */ - "%08X" /* inode */ - "%08X" /* mode */ - "%08X" "%08X" /* uid, gid */ - "%08X" /* nlink */ - "%08X" /* mtime */ - "%08X" /* file length */ - "%08X" "%08X" /* device holding file major, minor */ - "%08X" "%08X" /* for specials, device major, minor */ - "%08X" /* name length (including \0 byte) */ - "%08X", /* checksum (not used by the kernel) */ - (unsigned) statbuf->st_ino, statbuf->st_mode, - statbuf->st_uid, statbuf->st_gid, - (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime, - has_body ? (unsigned) statbuf->st_size : 0, - major (statbuf->st_dev), minor (statbuf->st_dev), - major (statbuf->st_rdev), minor (statbuf->st_rdev), - (unsigned) len, 0); - - /* Write the header. */ - write_to_fd (header, CPIO_HEADER_LEN); - - /* Follow with the filename, and pad it. */ - write_to_fd (filename, len); - size_t padding_len = PADDING (CPIO_HEADER_LEN + len); - write_padding (padding_len); - - /* Follow with the file or symlink content, and pad it. */ - if (has_body) { - if (S_ISREG (statbuf->st_mode)) - write_file_len_to_fd (orig_filename, statbuf->st_size); - else if (S_ISLNK (statbuf->st_mode)) { - char tmp[PATH_MAX]; - if (readlink (orig_filename, tmp, sizeof tmp) == -1) - error (EXIT_FAILURE, errno, "readlink: %s", orig_filename); - write_to_fd (tmp, statbuf->st_size); - } - - padding_len = PADDING (statbuf->st_size); - write_padding (padding_len); - } -} - -/* CPIO voodoo. */ -static void -cpio_append_trailer (void) -{ - struct stat statbuf; - memset (&statbuf, 0, sizeof statbuf); - statbuf.st_nlink = 1; - cpio_append_stat ("TRAILER!!!", &statbuf); - - /* CPIO seems to pad up to the next block boundary, ie. up to - * the next 512 bytes. - */ - write_padding (((out_offset + 511) & ~511) - out_offset); - assert ((out_offset & 511) == 0); -} - -/* Write 'len' bytes of zeroes out. */ -static void -write_padding (size_t len) -{ - static const char buffer[512] = { 0 }; - - while (len > 0) { - size_t n = len < sizeof buffer ? len : sizeof buffer; - write_to_fd (buffer, n); - len -= n; - } -}