Add -f checksum mode to allow caching of appliances.
authorRichard Jones <rjones@redhat.com>
Fri, 20 Aug 2010 20:31:49 +0000 (21:31 +0100)
committerRichard Jones <rjones@redhat.com>
Tue, 24 Aug 2010 12:19:39 +0000 (13:19 +0100)
helper/Makefile.am
helper/appliance.c
helper/checksum.c [new file with mode: 0644]
helper/cpio.c
helper/ext2.c
helper/febootstrap-supermin-helper.pod
helper/helper.h
helper/kernel.c
helper/main.c

index 96b5581..940a2ee 100644 (file)
@@ -23,6 +23,7 @@ bin_PROGRAMS = \
 febootstrap_supermin_helper_SOURCES = \
        helper.h \
        appliance.c \
+       checksum.c \
        cpio.c \
        ext2.c \
        ext2cpio.c \
index 2474b14..4cbebf4 100644 (file)
@@ -65,14 +65,15 @@ static void add_hostfiles (const char *hostfiles_file, struct writer *);
  * 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 *appliance,
                   struct writer *writer)
 {
-  writer->wr_start (appliance, modpath, initrd);
+  writer->wr_start (hostcpu, appliance, modpath, initrd);
 
   iterate_inputs (inputs, nr_inputs, writer);
 
diff --git a/helper/checksum.c b/helper/checksum.c
new file mode 100644 (file)
index 0000000..337134c
--- /dev/null
@@ -0,0 +1,117 @@
+/* febootstrap-supermin-helper reimplementation in C.
+ * Copyright (C) 2009-2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "error.h"
+
+#include "helper.h"
+
+static FILE *pp = NULL;
+
+/* This is the command we run to calculate the SHA.  Note that we sort
+ * the rows first so that the checksum is roughly stable, since the
+ * order that we output files might not be (eg. because we rely on the
+ * ordering of readdir).  Uncomment the second line to see the output
+ * before hashing.
+ */
+static const char *shacmd = "sort | sha256sum | awk '{print $1}'";
+//static const char *shacmd = "sort | cat";
+
+static void
+checksum_start (const char *hostcpu, const char *appliance,
+                const char *modpath, const char *initrd)
+{
+  pp = popen (shacmd, "w");
+  if (pp == NULL)
+    error (EXIT_FAILURE, errno, "popen: command failed: %s", shacmd);
+
+  fprintf (pp, "%s %s %s %d\n",
+           PACKAGE_STRING, hostcpu, modpath, geteuid ());
+}
+
+static void
+checksum_end (void)
+{
+  if (pclose (pp) == -1)
+    error (EXIT_FAILURE, errno, "pclose: command failed: %s", shacmd);
+  pp = NULL;
+}
+
+static void
+checksum_file_stat (const char *filename, const struct stat *statbuf)
+{
+  /* Publically writable directories (ie. /tmp) don't have stable
+   * times.  Since we only care about some attributes of directories
+   * in any case, we vary the output accordingly.
+   */
+  if (!S_ISDIR (statbuf->st_mode))
+    fprintf (pp, "%s %ld %ld %d %d %ld %o\n",
+             filename,
+             (long) statbuf->st_ctime, (long) statbuf->st_mtime,
+             statbuf->st_uid, statbuf->st_gid, statbuf->st_size,
+             statbuf->st_mode);
+  else
+    fprintf (pp, "%s %d %d %o\n",
+             filename,
+             statbuf->st_uid, statbuf->st_gid,
+             statbuf->st_mode);
+}
+
+static void
+checksum_file (const char *filename)
+{
+  struct stat statbuf;
+
+  if (lstat (filename, &statbuf) == -1)
+    error (EXIT_FAILURE, errno, "lstat: %s", filename);
+  checksum_file_stat (filename, &statbuf);
+}
+
+static void
+checksum_fts_entry (FTSENT *entry)
+{
+  if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
+    checksum_file (entry->fts_path);
+  else
+    checksum_file_stat (entry->fts_path, entry->fts_statp);
+}
+
+static void
+checksum_cpio_file (const char *cpio_file)
+{
+  checksum_file (cpio_file);
+}
+
+struct writer checksum_writer = {
+  .wr_start = checksum_start,
+  .wr_end = checksum_end,
+  .wr_file = checksum_file,
+  .wr_file_stat = checksum_file_stat,
+  .wr_fts_entry = checksum_fts_entry,
+  .wr_cpio_file = checksum_cpio_file,
+};
index 1dd22b0..0ca59a6 100644 (file)
@@ -250,7 +250,7 @@ write_padding (size_t len)
 }
 
 static void
-cpio_start (const char *appliance,
+cpio_start (const char *hostcpu, const char *appliance,
             const char *modpath, const char *initrd)
 {
   out_fd = open (appliance, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
index 9d60da9..a27fb47 100644 (file)
@@ -52,7 +52,7 @@ ext2_filsys fs;
 #define APPLIANCE_SIZE (1024*1024*1024)
 
 static void
-ext2_start (const char *appliance,
+ext2_start (const char *hostcpu, const char *appliance,
             const char *modpath, const char *initrd)
 {
   initialize_ext2_error_table ();
index 7a3dbd1..c6c551c 100644 (file)
@@ -9,6 +9,8 @@ febootstrap-supermin-helper - Reconstruct initramfs from supermin appliance.
 
  febootstrap-supermin-helper -f ext2 input [...] host_cpu kernel initrd appliance
 
+ febootstrap-supermin-helper -f checksum input [...] host_cpu
+
 =head1 DESCRIPTION
 
 I<febootstrap-supermin-helper> reconstructs a bootable kernel and
@@ -31,7 +33,8 @@ C<kernel>, C<initrd> and C<appliance> are the temporary output files
 that this script produces.  These output files are meant to be used
 just for booting the appliance, and should be deleted straight
 afterwards.  The extra C<appliance> parameter is only required when
-the format is C<ext2>.
+the format is C<ext2>.  None of these parameters are needed for
+the checksum output C<-f checksum>.
 
 =head1 OPTIONS
 
@@ -54,9 +57,22 @@ and C<initrd>, where the C<initrd> is the appliance.
 
 An ext2 filesystem.
 
-In this case you have to supply names for the C<kernel>,
-a small C<initrd> which is used just to locate the appliance,
-and the C<appliance> (the ext2 filesystem).
+In this case you have to supply names for the C<kernel>, a small
+C<initrd> which is used just to locate the appliance, and the
+C<appliance> (the ext2 filesystem).
+
+=item checksum
+
+Output a checksum.
+
+This prints a checksum which only changes when one of the input files
+changes.
+
+You can use this in order to cache the output of a previous run of
+this program: computing the checksum is much quicker than building an
+appliance, and you only need to invalidate the cache (and consequently
+rebuild the appliance) when the checksum changes.  Note that the
+host_cpu and the UID of the current user are included in the checksum.
 
 =back
 
index cb28695..e0d1fbb 100644 (file)
@@ -28,7 +28,7 @@ struct writer {
    * 'initrd' is the mini-initrd to create (only used for ext2 output).
    * 'modpath' is the kernel module path.
    */
-  void (*wr_start) (const char *appliance,
+  void (*wr_start) (const char *hostcpu, const char *appliance,
                     const char *modpath, const char *initrd);
 
   /* Finish off the appliance. */
@@ -52,7 +52,10 @@ extern struct timeval start_t;
 extern int verbose;
 
 /* appliance.c */
-extern void create_appliance (char **inputs, int nr_inputs, const char *whitelist, const char *modpath, const char *initrd, const char *appliance, struct writer *writer);
+extern void create_appliance (const char *hostcpu, char **inputs, int nr_inputs, const char *whitelist, const char *modpath, const char *initrd, const char *appliance, struct writer *writer);
+
+/* checksum.c */
+extern struct writer checksum_writer;
 
 /* cpio.c */
 extern struct writer cpio_writer;
index 3301826..0903805 100644 (file)
@@ -122,16 +122,18 @@ create_kernel (const char *hostcpu, const char *kernel)
 
   sort (candidates, reverse_filevercmp);
 
-  /* Choose the first candidate. */
-  char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]);
+  if (kernel) {
+    /* Choose the first candidate. */
+    char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]);
 
-  if (verbose)
-    fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
+    if (verbose >= 2)
+      fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
 
-  if (symlink (tmp, kernel) == -1)
-    error (EXIT_FAILURE, errno, "symlink kernel");
+    if (symlink (tmp, kernel) == -1)
+      error (EXIT_FAILURE, errno, "symlink kernel");
 
-  free (tmp);
+    free (tmp);
+  }
 
   return get_modpath (candidates[0]);
 
index b4359b6..4afcb24 100644 (file)
@@ -58,6 +58,7 @@ usage (const char *progname)
           "Usage:\n"
           "  %s [-options] inputs [...] host_cpu kernel initrd\n"
           "  %s -f ext2 inputs [...] host_cpu kernel initrd appliance\n"
+          "  %s -f checksum inputs [...] host_cpu\n"
           "  %s --help\n"
           "  %s --version\n"
           "\n"
@@ -72,7 +73,7 @@ usage (const char *progname)
           "Options:\n"
           "  --help\n"
           "       Display this help text and exit.\n"
-          "  -f cpio|ext2 | --format cpio|ext2\n"
+          "  -f cpio|ext2|checksum | --format cpio|ext2|checksum\n"
           "       Specify output format (default: cpio).\n"
           "  -k file | --kmods file\n"
           "       Specify kernel module whitelist.\n"
@@ -80,7 +81,7 @@ usage (const char *progname)
           "       Enable verbose messages (give multiple times for more verbosity).\n"
           "  --version | -V\n"
           "       Display version number and exit.\n",
-          progname, progname, progname, progname, progname);
+          progname, progname, progname, progname, progname, progname);
 }
 
 int
@@ -135,8 +136,13 @@ main (int argc, char *argv[])
     writer = &ext2_writer;
     nr_outputs = 3;             /* kernel, initrd, appliance */
   }
+  else if (strcmp (format, "checksum") == 0) {
+    writer = &checksum_writer;
+    nr_outputs = 0;             /* (none) */
+  }
   else {
-    fprintf (stderr, "%s: incorrect output format (-f): must be cpio|ext2\n",
+    fprintf (stderr,
+             "%s: incorrect output format (-f): must be cpio|ext2|checksum\n",
              argv[0]);
     exit (EXIT_FAILURE);
   }
@@ -160,10 +166,11 @@ main (int argc, char *argv[])
   const char *hostcpu = outputs[-1];
 
   /* Output files. */
-  const char *kernel = outputs[0];
-  const char *initrd;
-  const char *appliance;
-  initrd = appliance = outputs[1];
+  const char *kernel = NULL, *initrd = NULL, *appliance = NULL;
+  if (nr_outputs > 0)
+    kernel = outputs[0];
+  if (nr_outputs > 1)
+    initrd = appliance = outputs[1];
   if (nr_outputs > 2)
     appliance = outputs[2];
 
@@ -181,20 +188,21 @@ main (int argc, char *argv[])
   }
 
   /* Remove the output files if they exist. */
-  unlink (kernel);
-  unlink (initrd);
-  if (initrd != appliance)
+  if (kernel)
+    unlink (kernel);
+  if (initrd)
+    unlink (initrd);
+  if (appliance && initrd != appliance)
     unlink (appliance);
 
   /* Create kernel output file. */
-  const char *modpath;
-  modpath = create_kernel (hostcpu, kernel);
+  const char *modpath = create_kernel (hostcpu, kernel);
 
   if (verbose)
     print_timestamped_message ("finished creating kernel");
 
   /* Create the appliance. */
-  create_appliance (inputs, nr_inputs, whitelist, modpath,
+  create_appliance (hostcpu, inputs, nr_inputs, whitelist, modpath,
                     initrd, appliance, writer);
 
   if (verbose)