[prev in list] [next in list] [prev in thread] [next in thread] 

List:       libguestfs
Subject:    [Libguestfs] [PATCH] fish: Add -N option for making prepared disk images.
From:       "Richard W.M. Jones" <rjones () redhat ! com>
Date:       2010-04-22 16:13:52
Message-ID: 20100422161352.GF25840 () amd ! home ! annexia ! org
[Download RAW message or body]

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw

["0001-fish-Add-N-option-for-making-prepared-disk-images.patch" (text/plain)]

> From cfc15d0de927969d12f82f449dec858f3d4b914f Mon Sep 17 00:00:00 2001
From: Richard Jones <rjones@redhat.com>
Date: Thu, 22 Apr 2010 10:42:58 +0100
Subject: [PATCH] fish: Add -N option for making prepared disk images.

Previously you might have typed:

$ guestfish
> <fs> alloc test1.img 100M
> <fs> run
> <fs> part-disk /dev/sda mbr
> <fs> mkfs ext4 /dev/sda1

now you can do the same with:

$ guestfish -N fs:ext4

Some tests have also been updated to use this new
functionality.
---
 TODO                                               |   10 -
 fish/Makefile.am                                   |    1 +
 fish/alloc.c                                       |  141 ++++-----
 fish/fish.c                                        |   72 ++++-
 fish/fish.h                                        |   10 +
 fish/guestfish.pod                                 |   76 ++++-
 fish/prep.c                                        |  335 ++++++++++++++++++++
 po/POTFILES.in                                     |    1 +
 regressions/rhbz503169c10.sh                       |    7 +-
 regressions/rhbz503169c13.sh                       |    7 +-
 regressions/rhbz557655.sh                          |    8 +-
 regressions/rhbz576879.sh                          |    4 +-
 regressions/test-bootbootboot.sh                   |    2 +-
 .../test-cancellation-upload-daemoncancels.sh      |   13 +-
 regressions/test-qemudie-killsub.sh                |    9 +-
 regressions/test-qemudie-midcommand.sh             |    9 +-
 regressions/test-qemudie-synch.sh                  |    9 +-
 17 files changed, 562 insertions(+), 152 deletions(-)
 create mode 100644 fish/prep.c

diff --git a/TODO b/TODO
index 56b429a..881067d 100644
--- a/TODO
+++ b/TODO
@@ -212,16 +212,6 @@ ntfsclone
 Useful imaging tool:
 http://man.linux-ntfs.org/ntfsclone.8.html
 
-Standard images
----------------
-
-Equip guestfish with some standard images that it can load
-quickly, eg:
-
-  load ext2
-
-Maybe it's better to create these on the fly?
-
 virt-rescue pty
 ---------------
 
diff --git a/fish/Makefile.am b/fish/Makefile.am
index 43dce00..c25a602 100644
--- a/fish/Makefile.am
+++ b/fish/Makefile.am
@@ -45,6 +45,7 @@ guestfish_SOURCES = \
 	glob.c \
 	lcd.c \
 	more.c \
+	prep.c \
 	rc.c \
 	reopen.c \
 	tilde.c \
diff --git a/fish/alloc.c b/fish/alloc.c
index 93cd8af..f91c5bb 100644
--- a/fish/alloc.c
+++ b/fish/alloc.c
@@ -28,88 +28,47 @@
 
 #include "fish.h"
 
-static int parse_size (const char *str, off_t *size_rtn);
-
 int
 do_alloc (const char *cmd, int argc, char *argv[])
 {
-  off_t size;
-  int fd;
-
   if (argc != 2) {
     fprintf (stderr, _("use 'alloc file size' to create an image\n"));
     return -1;
   }
 
-  if (parse_size (argv[1], &size) == -1)
-    return -1;
-
-  if (!guestfs_is_config (g)) {
-    fprintf (stderr, _("can't allocate or add disks after launching\n"));
-    return -1;
-  }
-
-  fd = open (argv[0], O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
-  if (fd == -1) {
-    perror (argv[0]);
+  if (alloc_disk (argv[0], argv[1], 1, 0) == -1)
     return -1;
-  }
 
-#ifdef HAVE_POSIX_FALLOCATE
-  int err = posix_fallocate (fd, 0, size);
-  if (err != 0) {
-    errno = err;
-    perror ("fallocate");
-    close (fd);
-    unlink (argv[0]);
-    return -1;
-  }
-#else
-  /* Slow emulation of posix_fallocate on platforms which don't have it. */
-  char buffer[BUFSIZ];
-  memset (buffer, 0, sizeof buffer);
-
-  size_t remaining = size;
-  while (remaining > 0) {
-    size_t n = remaining > sizeof buffer ? sizeof buffer : remaining;
-    ssize_t r = write (fd, buffer, n);
-    if (r == -1) {
-      perror ("write");
-      close (fd);
-      unlink (argv[0]);
-      return -1;
-    }
-    remaining -= r;
-  }
-#endif
+  return 0;
+}
 
-  if (close (fd) == -1) {
-    perror (argv[0]);
-    unlink (argv[0]);
+int
+do_sparse (const char *cmd, int argc, char *argv[])
+{
+  if (argc != 2) {
+    fprintf (stderr, _("use 'sparse file size' to create a sparse image\n"));
     return -1;
   }
 
-  if (guestfs_add_drive (g, argv[0]) == -1) {
-    unlink (argv[0]);
+  if (alloc_disk (argv[0], argv[1], 1, 1) == -1)
     return -1;
-  }
 
   return 0;
 }
 
+static int parse_size (const char *str, off_t *size_rtn);
+
+/* This is the underlying allocation function.  It's called from
+ * a few other places in guestfish.
+ */
 int
-do_sparse (const char *cmd, int argc, char *argv[])
+alloc_disk (const char *filename, const char *size_str, int add, int sparse)
 {
   off_t size;
   int fd;
   char c = 0;
 
-  if (argc != 2) {
-    fprintf (stderr, _("use 'sparse file size' to create a sparse image\n"));
-    return -1;
-  }
-
-  if (parse_size (argv[1], &size) == -1)
+  if (parse_size (size_str, &size) == -1)
     return -1;
 
   if (!guestfs_is_config (g)) {
@@ -117,35 +76,67 @@ do_sparse (const char *cmd, int argc, char *argv[])
     return -1;
   }
 
-  fd = open (argv[0], O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
+  fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
   if (fd == -1) {
-    perror (argv[0]);
+    perror (filename);
     return -1;
   }
 
-  if (lseek (fd, size-1, SEEK_SET) == (off_t) -1) {
-    perror ("lseek");
-    close (fd);
-    unlink (argv[0]);
-    return -1;
-  }
+  if (!sparse) {                /* Not sparse */
+#ifdef HAVE_POSIX_FALLOCATE
+    int err = posix_fallocate (fd, 0, size);
+    if (err != 0) {
+      errno = err;
+      perror ("fallocate");
+      close (fd);
+      unlink (filename);
+      return -1;
+    }
+#else
+    /* Slow emulation of posix_fallocate on platforms which don't have it. */
+    char buffer[BUFSIZ];
+    memset (buffer, 0, sizeof buffer);
+
+    size_t remaining = size;
+    while (remaining > 0) {
+      size_t n = remaining > sizeof buffer ? sizeof buffer : remaining;
+      ssize_t r = write (fd, buffer, n);
+      if (r == -1) {
+        perror ("write");
+        close (fd);
+        unlink (filename);
+        return -1;
+      }
+      remaining -= r;
+    }
+#endif
+  } else {                      /* Sparse */
+    if (lseek (fd, size-1, SEEK_SET) == (off_t) -1) {
+      perror ("lseek");
+      close (fd);
+      unlink (filename);
+      return -1;
+    }
 
-  if (write (fd, &c, 1) != 1) {
-    perror ("write");
-    close (fd);
-    unlink (argv[0]);
-    return -1;
+    if (write (fd, &c, 1) != 1) {
+      perror ("write");
+      close (fd);
+      unlink (filename);
+      return -1;
+    }
   }
 
   if (close (fd) == -1) {
-    perror (argv[0]);
-    unlink (argv[0]);
+    perror (filename);
+    unlink (filename);
     return -1;
   }
 
-  if (guestfs_add_drive (g, argv[0]) == -1) {
-    unlink (argv[0]);
-    return -1;
+  if (add) {
+    if (guestfs_add_drive (g, filename) == -1) {
+      unlink (filename);
+      return -1;
+    }
   }
 
   return 0;
diff --git a/fish/fish.c b/fish/fish.c
index db3149e..4a7e70b 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -42,18 +42,21 @@
 #include "closeout.h"
 #include "progname.h"
 
+struct drv {
+  struct drv *next;
+  char *filename;               /* disk filename (for -a or -N options) */
+  prep_data *data;              /* prepared type (for -N option only) */
+  char *device;                 /* device inside the appliance */
+};
+
 struct mp {
   struct mp *next;
   char *device;
   char *mountpoint;
 };
 
-struct drv {
-  struct drv *next;
-  char *filename;
-};
-
 static void add_drives (struct drv *drv);
+static void prepare_drives (struct drv *drv);
 static void mount_mps (struct mp *mp);
 static void interactive (void);
 static void shell_script (void);
@@ -119,6 +122,7 @@ usage (int status)
              "  --listen             Listen for remote commands\n"
              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
              "  -n|--no-sync         Don't autosync\n"
+             "  -N|--new type        Create prepared disk (test1.img, ...)\n"
              "  --remote[=pid]       Send commands to remote %s\n"
              "  -r|--ro              Mount read-only\n"
              "  --selinux            Enable SELinux support\n"
@@ -147,7 +151,7 @@ main (int argc, char *argv[])
 
   enum { HELP_OPTION = CHAR_MAX + 1 };
 
-  static const char *options = "a:Df:h::im:nrv?Vx";
+  static const char *options = "a:Df:h::im:nN:rv?Vx";
   static const struct option long_options[] = {
     { "add", 1, 0, 'a' },
     { "cmd-help", 2, 0, 'h' },
@@ -156,6 +160,7 @@ main (int argc, char *argv[])
     { "inspector", 0, 0, 'i' },
     { "listen", 0, 0, 0 },
     { "mount", 1, 0, 'm' },
+    { "new", 1, 0, 'N' },
     { "no-dest-paths", 0, 0, 'D' },
     { "no-sync", 0, 0, 'n' },
     { "remote", 2, 0, 0 },
@@ -174,6 +179,8 @@ main (int argc, char *argv[])
   int inspector = 0;
   int option_index;
   struct sigaction sa;
+  char next_drive = 'a';
+  int next_prepared_drive = 1;
 
   initialize_readline ();
 
@@ -259,6 +266,36 @@ main (int argc, char *argv[])
         exit (EXIT_FAILURE);
       }
       drv->filename = optarg;
+      drv->data = NULL;
+      /* We could fill the device field in, but in fact we
+       * only use it for the -N option at present.
+       */
+      drv->device = NULL;
+      drv->next = drvs;
+      drvs = drv;
+      next_drive++;
+      break;
+
+    case 'N':
+      if (STRCASEEQ (optarg, "list")) {
+        list_prepared_drives ();
+        exit (EXIT_SUCCESS);
+      }
+      drv = malloc (sizeof (struct drv));
+      if (!drv) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      if (asprintf (&drv->filename, "test%d.img",
+                    next_prepared_drive++) == -1) {
+        perror ("asprintf");
+        exit (EXIT_FAILURE);
+      }
+      drv->data = create_prepared_file (optarg, drv->filename);
+      if (asprintf (&drv->device, "/dev/sd%c", next_drive++) == -1) {
+        perror ("asprintf");
+        exit (EXIT_FAILURE);
+      }
       drv->next = drvs;
       drvs = drv;
       break;
@@ -342,8 +379,8 @@ main (int argc, char *argv[])
 
     if (drvs || mps || remote_control_listen || remote_control ||
         guestfs_get_selinux (g)) {
-      fprintf (stderr, _("%s: cannot use -i option with -a, -m,"
-                         " --listen, --remote or --selinux\n"),
+      fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
+                         "--listen, --remote or --selinux\n"),
                program_name);
       exit (EXIT_FAILURE);
     }
@@ -396,9 +433,12 @@ main (int argc, char *argv[])
   /* If we've got drives to add, add them now. */
   add_drives (drvs);
 
-  /* If we've got mountpoints, we must launch the guest and mount them. */
-  if (mps != NULL) {
+  /* If we've got mountpoints or prepared drives, we must launch the
+   * guest and mount them.
+   */
+  if (next_prepared_drive > 1 || mps != NULL) {
     if (launch (g) == -1) exit (EXIT_FAILURE);
+    prepare_drives (drvs);
     mount_mps (mps);
   }
 
@@ -495,7 +535,8 @@ add_drives (struct drv *drv)
 
   if (drv) {
     add_drives (drv->next);
-    if (!read_only)
+
+    if (drv->data /* -N option is not affected by --ro */ || !read_only)
       r = guestfs_add_drive (g, drv->filename);
     else
       r = guestfs_add_drive_ro (g, drv->filename);
@@ -505,6 +546,15 @@ add_drives (struct drv *drv)
 }
 
 static void
+prepare_drives (struct drv *drv)
+{
+  if (drv) {
+    prepare_drives (drv->next);
+    prepare_drive (drv->filename, drv->data, drv->device);
+  }
+}
+
+static void
 interactive (void)
 {
   script (1);
diff --git a/fish/fish.h b/fish/fish.h
index 05135fb..13efa9a 100644
--- a/fish/fish.h
+++ b/fish/fish.h
@@ -85,6 +85,8 @@ extern char *complete_dest_paths_generator (const char *text, int \
state);  /* in alloc.c */
 extern int do_alloc (const char *cmd, int argc, char *argv[]);
 extern int do_sparse (const char *cmd, int argc, char *argv[]);
+extern int alloc_disk (const char *filename, const char *size,
+                       int add, int sparse);
 
 /* in echo.c */
 extern int do_echo (const char *cmd, int argc, char *argv[]);
@@ -101,6 +103,14 @@ extern int do_glob (const char *cmd, int argc, char *argv[]);
 /* in more.c */
 extern int do_more (const char *cmd, int argc, char *argv[]);
 
+/* in prep.c */
+typedef struct prep_data prep_data;
+extern void list_prepared_drives (void);
+extern prep_data *create_prepared_file (const char *type_string,
+                                        const char *filename);
+extern void prepare_drive (const char *filename, prep_data *data,
+                           const char *device);
+
 /* in rc.c (remote control) */
 extern void rc_listen (void) __attribute__((noreturn));
 extern int rc_remote (int pid, const char *cmd, int argc, char *argv[],
diff --git a/fish/guestfish.pod b/fish/guestfish.pod
index a0c3975..165cdf2 100644
--- a/fish/guestfish.pod
+++ b/fish/guestfish.pod
@@ -75,14 +75,25 @@ in the virtual machine:
 
 =head2 As a script interpreter
 
-Create a 50MB disk containing an ext2-formatted partition:
+Create a 100MB disk containing an ext2-formatted partition:
 
  #!/usr/bin/guestfish -f
- alloc /tmp/output.img 50M
+ sparse test1.img 100M
  run
  part-disk /dev/sda mbr
  mkfs ext2 /dev/sda1
 
+=head2 Start with a prepared disk
+
+An alternate way to create a 100MB disk called C<test1.img> containing
+a single ext2-formatted partition:
+
+ guestfish -N fs
+
+To list what is available do:
+
+ guestfish -N list | less
+
 =head2 Remote control
 
  eval `guestfish --listen --ro`
@@ -159,9 +170,9 @@ Typical usage is either:
 
  guestfish -i /dev/Guests/MyGuest
 
-You cannot use I<-a>, I<-m>, I<--listen>, I<--remote> or I<--selinux>
-in conjunction with this option, and options other than I<--ro> might
-not behave correctly.
+You cannot use I<-a>, I<-m>, I<-N>, I<--listen>, I<--remote> or
+I<--selinux> in conjunction with this option, and options other than
+I<--ro> might not behave correctly.
 
 See also: L<virt-inspector(1)>.
 
@@ -191,6 +202,13 @@ or you can use the L<virt-list-filesystems(1)> program.
 Disable autosync.  This is enabled by default.  See the discussion
 of autosync in the L<guestfs(3)> manpage.
 
+=item B<-N type> | B<--new type> | B<-N list>
+
+Prepare a fresh disk image formatted as "type".  This is an
+alternative to the I<-a> option: whereas I<-a> adds an existing disk,
+I<-N> creates a preformatted disk with a filesystem and adds it.
+See L</PREPARED DISK IMAGES> below.
+
 =item B<--remote[=pid]>
 
 Send remote commands to C<$GUESTFISH_PID> or C<pid>.  See section
@@ -205,6 +223,9 @@ The option must always be used if the disk image or virtual \
machine  might be running, and is generally recommended in cases where you
 don't need write access to the disk.
 
+Note that prepared disk images created with I<-N> are not affected by
+the I<--ro> option.
+
 =item B<--selinux>
 
 Enable SELinux support for the guest.  See L<guestfs(3)/SELINUX>.
@@ -486,6 +507,51 @@ user ID of the process, and C<$PID> is the process ID of the \
server.  
 Guestfish client and server versions must match exactly.
 
+=head1 PREPARED DISK IMAGES
+
+Use the I<-N type> or I<--new type> parameter to select one of a set
+of preformatted disk images that guestfish can make for you to save
+typing.  This is particularly useful for testing purposes.  This
+option is used instead of the I<-a> option, and like I<-a> can appear
+multiple times (and can be mixed with I<-a>).
+
+The new disk is called C<test1.img> for the first I<-N>, C<test2.img>
+for the second and so on.  Existing files in the current directory are
+not overwritten, so you may need to do C<rm -f test1.img>.
+
+The type briefly describes how the disk should be sized, partitioned,
+how filesystem(s) should be created, and how content should be added.
+Optionally the type can be followed by extra parameters, separated by
+C<:> (colon) characters.  For example, I<-N fs> creates a default
+100MB, sparsely-allocated disk, containing a single partition, with
+the partition formatted as ext2.  I<-N fs:ext4:1G> is the same, but
+for an ext4 filesystem on a 1GB disk instead.
+
+To list the available types and any extra parameters they take, run:
+
+ guestfish -N list | less
+
+Note that the prepared filesystem is not mounted.  You would usually
+have to use the C<mount /dev/sda1 /> command or add the
+I<-m /dev/sda1> option.
+
+If any I<-N> or I<--new> options are given, the guest is automatically
+launched.
+
+=head2 EXAMPLES
+
+Create a 100MB disk with an ext4-formatted partition:
+
+ guestfish -N fs:ext4
+
+Create a 32MB disk with a VFAT-formatted partition, and mount it:
+
+ guestfish -N fs:vfat:32M -m /dev/sda1
+
+Create a blank 200MB disk:
+
+ guestfish -N disk:200M
+
 =head1 UPLOADING AND DOWNLOADING FILES
 
 For commands such as C<upload>, C<download>, C<tar-in>, C<tar-out> and
diff --git a/fish/prep.c b/fish/prep.c
new file mode 100644
index 0000000..b16d09e
--- /dev/null
+++ b/fish/prep.c
@@ -0,0 +1,335 @@
+/* guestfish - the filesystem interactive shell
+ * Copyright (C) 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 "fish.h"
+
+static prep_data *parse_type_string (const char *type_string);
+static void prep_error (prep_data *data, const char *filename) \
__attribute__((noreturn)); +static void prep_command_failed (prep_data *data, const \
char *filename, const char *cmd, char **argv) __attribute__((noreturn)); +
+struct prep {
+  const char *name;             /* eg. "fs" */
+
+  size_t nr_params;             /* optional parameters */
+  struct param *params;
+
+  const char *shortdesc;        /* short description */
+  const char *longdesc;         /* long description */
+
+                                /* functions to implement it */
+  void (*prelaunch) (const char *filename, prep_data *);
+  void (*postlaunch) (const char *filename, prep_data *, const char *device);
+};
+
+struct param {
+  const char *pname;            /* parameter name */
+  const char *pdefault;         /* parameter default */
+  const char *pdesc;            /* parameter description */
+};
+
+static void prelaunch_disk (const char *filename, prep_data *data);
+static struct param disk_params[] = {
+  { "size", "100M", "the size of the disk image" },
+};
+
+static void prelaunch_part (const char *filename, prep_data *data);
+static void postlaunch_part (const char *filename, prep_data *data, const char \
*device); +static struct param part_params[] = {
+  { "size", "100M", "the size of the disk image" },
+  { "partition", "mbr", "partition table type" },
+};
+
+static void prelaunch_fs (const char *filename, prep_data *data);
+static void postlaunch_fs (const char *filename, prep_data *data, const char \
*device); +static struct param fs_params[] = {
+  { "filesystem", "ext2", "the type of filesystem to use" },
+  { "size", "100M", "the size of the disk image" },
+  { "partition", "mbr", "partition table type" },
+};
+
+static const struct prep preps[] = {
+  { "disk",
+    1, disk_params,
+    "create a blank disk",
+    "\
+  Create a blank disk, size 100MB (by default).\n\
+\n\
+  The default size can be changed by supplying an optional parameter.",
+    prelaunch_disk, NULL
+  },
+  { "part",
+    2, part_params,
+    "create a partitioned disk",
+    "\
+  Create a disk with a single partition.  By default the size of the disk\n\
+  is 100MB (the available space in the partition will be a tiny bit smaller)\n\
+  and the partition table will be MBR (old DOS-style).\n\
+\n\
+  These defaults can be changed by supplying optional parameters.",
+    prelaunch_part, postlaunch_part
+  },
+  { "fs",
+    3, fs_params,
+    "create a filesystem",
+    "\
+  Create a disk with a single partition, with the partition containing\n\
+  an empty filesystem.  This defaults to creating a 100MB disk (the available\n\
+  space in the filesystem will be a tiny bit smaller) with an MBR (old\n\
+  DOS-style) partition table and an ext2 filesystem.\n\
+\n\
+  These defaults can be changed by supplying optional parameters.",
+    prelaunch_fs, postlaunch_fs
+  },
+};
+
+void
+list_prepared_drives (void)
+{
+  size_t i, j;
+
+  printf (_("List of available prepared disk images:\n\n"));
+
+  for (i = 0; i < sizeof preps / sizeof preps[0]; ++i) {
+    printf (_("\
+guestfish -N %-16s %s\n\
+\n\
+%s\n"),
+            preps[i].name, preps[i].shortdesc, preps[i].longdesc);
+
+    if (preps[i].nr_params > 0) {
+      printf ("\n");
+      printf (_("  Optional parameters:\n"));
+      printf ("    -N %s", preps[i].name);
+      for (j = 0; j < preps[i].nr_params; ++j)
+        printf (":<%s>", preps[i].params[j].pname);
+      printf ("\n");
+      for (j = 0; j < preps[i].nr_params; ++j) {
+        printf ("      ");
+        printf (_("<%s> %s (default: %s)\n"),
+                preps[i].params[j].pname,
+                preps[i].params[j].pdesc,
+                preps[i].params[j].pdefault);
+      }
+    }
+
+    printf ("\n");
+  }
+
+  printf (_("\
+Prepared disk images are written to file \"test1.img\" in the local\n\
+directory.  (\"test2.img\" etc if -N option is given multiple times).\n\
+For more information see the guestfish(1) manual.\n"));
+}
+
+struct prep_data {
+  const struct prep *prep;
+  const char *orig_type_string;
+  const char **params;
+};
+
+/* Parse the type string (from the command line) and create the output
+ * file 'filename'.  This is called before launch.  Return the opaque
+ * prep_data which will be passed back to us in prepare_drive below.
+ */
+prep_data *
+create_prepared_file (const char *type_string, const char *filename)
+{
+  if (access (filename, F_OK) == 0) {
+    fprintf (stderr, _("guestfish: file '%s' exists and the '-N' option will not \
overwrite it\n"), +             filename);
+    exit (EXIT_FAILURE);
+  }
+
+  prep_data *data = parse_type_string (type_string);
+  if (data->prep->prelaunch)
+    data->prep->prelaunch (filename, data);
+  return data;
+}
+
+static prep_data *
+parse_type_string (const char *type_string)
+{
+  size_t i;
+
+  /* Match on the type part (without parameters). */
+  size_t len = strcspn (type_string, ":");
+  for (i = 0; i < sizeof preps / sizeof preps[0]; ++i)
+    if (STRCASEEQLEN (type_string, preps[i].name, len))
+      break;
+
+  if (preps[i].name == NULL) {
+    fprintf (stderr, _("\
+guestfish: -N parameter '%s': no such prepared disk image known.\n\
+Use 'guestfish -N list' to list possible values for the -N parameter.\n"),
+             type_string);
+    exit (EXIT_FAILURE);
+  }
+
+  prep_data *data = malloc (sizeof *data);
+  if (data == NULL) {
+    perror ("malloc");
+    exit (EXIT_FAILURE);
+  }
+  data->prep = &preps[i];
+  data->orig_type_string = type_string;
+
+  /* Set up the optional parameters to all-defaults. */
+  data->params = malloc (data->prep->nr_params * sizeof (char *));
+  if (data->params == NULL) {
+    perror ("malloc");
+    exit (EXIT_FAILURE);
+  }
+
+  for (i = 0; i < data->prep->nr_params; ++i)
+    data->params[i] = data->prep->params[i].pdefault;
+
+  /* Parse the optional parameters. */
+  const char *p = type_string + len;
+  if (*p) p++; /* skip colon char */
+
+  i = 0;
+  while (*p) {
+    len = strcspn (p, ":");
+    data->params[i] = strndup (p, len);
+    if (data->params[i] == NULL) {
+      perror ("strndup");
+      exit (EXIT_FAILURE);
+    }
+
+    p += len;
+    if (*p) p++; /* skip colon char */
+    i++;
+  }
+
+  return data;
+}
+
+/* Prepare a drive.  The appliance has been launched, and 'device' is
+ * the libguestfs device.  'data' is the requested type.  'filename'
+ * is just used for error messages.
+ */
+void
+prepare_drive (const char *filename, prep_data *data,
+               const char *device)
+{
+  if (data->prep->postlaunch)
+    data->prep->postlaunch (filename, data, device);
+}
+
+static void
+prep_error (prep_data *data, const char *filename)
+{
+  fprintf (stderr,
+           _("guestfish: error creating prepared disk image '%s' on '%s'.\n"),
+           data->orig_type_string, filename);
+  exit (EXIT_FAILURE);
+}
+
+static void
+prep_command_failed (prep_data *data, const char *filename,
+                     const char *cmd, char **argv)
+{
+  int i;
+
+  fprintf (stderr, "guestfish: ");
+  fprintf (stderr, "%s", cmd);
+  for (i = 0; argv[i] != NULL; ++i)
+    fprintf (stderr, " %s", argv[i]);
+  fprintf (stderr, ": ");
+  fprintf (stderr, _("command failed, see earlier error messages\n"));
+  prep_error (data, filename);
+}
+
+static void
+prelaunch_disk (const char *filename, prep_data *data)
+{
+  if (alloc_disk (filename, data->params[0], 0, 1) == -1)
+    prep_error (data, filename);
+}
+
+static void
+prelaunch_part (const char *filename, prep_data *data)
+{
+  if (alloc_disk (filename, data->params[0], 0, 1) == -1)
+    prep_error (data, filename);
+}
+
+static void
+postlaunch_part (const char *filename, prep_data *data, const char *device)
+{
+  const char *cmd;
+  char *argv[10];
+
+  cmd = "part-disk";
+  argv[0] = (char *) device;
+  argv[1] = (char *) data->params[1];
+  argv[2] = NULL;
+  if (issue_command (cmd, argv, NULL) == -1)
+    goto error;
+
+  return;
+
+ error:
+  prep_command_failed (data, filename, cmd, argv);
+}
+
+static void
+prelaunch_fs (const char *filename, prep_data *data)
+{
+  if (alloc_disk (filename, data->params[1], 0, 1) == -1)
+    prep_error (data, filename);
+}
+
+static void
+postlaunch_fs (const char *filename, prep_data *data, const char *device)
+{
+  const char *cmd;
+  char *argv[10];
+
+  cmd = "part-disk";
+  argv[0] = (char *) device;
+  argv[1] = (char *) data->params[2];
+  argv[2] = NULL;
+  if (issue_command (cmd, argv, NULL) == -1)
+    goto error;
+
+  char *part;
+  if (asprintf (&part, "%s1", device) == -1) {
+    perror ("asprintf");
+    exit (EXIT_FAILURE);
+  }
+
+  cmd = "mkfs";
+  argv[0] = (char *) data->params[0];
+  argv[1] = (char *) part;
+  argv[2] = NULL;
+  if (issue_command (cmd, argv, NULL) == -1)
+    goto error;
+
+  return;
+
+ error:
+  prep_command_failed (data, filename, cmd, argv);
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index aebe450..8cf6e16 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -76,6 +76,7 @@ fish/fish.c
 fish/glob.c
 fish/lcd.c
 fish/more.c
+fish/prep.c
 fish/rc.c
 fish/reopen.c
 fish/tilde.c
diff --git a/regressions/rhbz503169c10.sh b/regressions/rhbz503169c10.sh
index a975f69..a777b22 100755
--- a/regressions/rhbz503169c10.sh
+++ b/regressions/rhbz503169c10.sh
@@ -23,12 +23,7 @@ set -e
 
 rm -f test1.img
 
-../fish/guestfish <<EOF
-sparse test1.img 10M
-launch
-part-disk /dev/sda mbr
-mkfs ext2 /dev/sda1
-mount-options "" /dev/sda1 /
+../fish/guestfish -N fs -m /dev/sda1 <<EOF
 ll /../dev/console
 ll /../dev/full
 ll /../dev/mapper/
diff --git a/regressions/rhbz503169c13.sh b/regressions/rhbz503169c13.sh
index 1b19cdf..a1a84c6 100755
--- a/regressions/rhbz503169c13.sh
+++ b/regressions/rhbz503169c13.sh
@@ -28,12 +28,7 @@ set -e
 
 rm -f test1.img
 
-../fish/guestfish <<EOF
-sparse test1.img 10M
-run
-part-disk /dev/sda mbr
-mkfs ext2 /dev/sda1
-mount-options "" /dev/sda1 /
+../fish/guestfish -N fs -m /dev/sda1 <<EOF
 mkdir /dev
 -command /ignore-this-error
 unmount-all
diff --git a/regressions/rhbz557655.sh b/regressions/rhbz557655.sh
index 85cfb1e..228b498 100755
--- a/regressions/rhbz557655.sh
+++ b/regressions/rhbz557655.sh
@@ -49,13 +49,7 @@ get-memsize
 -set-memsize 123L
 EOF
 
-../fish/guestfish >> test.out 2>> test.err <<EOF
-alloc test1.img 10M
-run
-part-disk /dev/sda mbr
-mkfs ext2 /dev/sda1
-mount /dev/sda1 /
-
+../fish/guestfish -N fs -m /dev/sda1 >> test.out 2>> test.err <<EOF
 touch /test
 
 # truncate-size takes an Int64 argument
diff --git a/regressions/rhbz576879.sh b/regressions/rhbz576879.sh
index 639dd7c..7453ac7 100755
--- a/regressions/rhbz576879.sh
+++ b/regressions/rhbz576879.sh
@@ -24,9 +24,7 @@ set -e
 
 rm -f test1.img
 
-../fish/guestfish <<EOF
-alloc test1.img 10M
-run
+../fish/guestfish -N disk <<EOF
 -upload $srcdir/rhbz576879.sh /test.sh
 # Shouldn't lose synchronization, so next command should work:
 ping-daemon
diff --git a/regressions/test-bootbootboot.sh b/regressions/test-bootbootboot.sh
index 842e41f..d3a30a3 100755
--- a/regressions/test-bootbootboot.sh
+++ b/regressions/test-bootbootboot.sh
@@ -32,7 +32,7 @@ export LIBGUESTFS_DEBUG=1
 
 for i in $(seq 1 $n); do
   echo Test boot $i of $n ...
-  ../fish/guestfish sparse test1.img 500M : run
+  ../fish/guestfish -N disk </dev/null
 done
 
 rm test1.img
diff --git a/regressions/test-cancellation-upload-daemoncancels.sh \
b/regressions/test-cancellation-upload-daemoncancels.sh index 4962c25..8204530 100755
--- a/regressions/test-cancellation-upload-daemoncancels.sh
+++ b/regressions/test-cancellation-upload-daemoncancels.sh
@@ -22,16 +22,9 @@
 
 set -e
 
-rm -f test.img
-
-../fish/guestfish <<'EOF'
-alloc test.img 10M
-run
-
-part-disk /dev/sda mbr
-mkfs ext2 /dev/sda1
-mount-options "" /dev/sda1 /
+rm -f test1.img
 
+../fish/guestfish -N fs -m /dev/sda1 <<'EOF'
 # Upload image, daemon should cancel because the image is too large
 # to upload into itself.
 echo "Expect: write: /test: No space left on device"
@@ -40,4 +33,4 @@ echo "Expect: write: /test: No space left on device"
 ping-daemon
 EOF
 
-rm -f test.img
+rm -f test1.img
diff --git a/regressions/test-qemudie-killsub.sh \
b/regressions/test-qemudie-killsub.sh index 14b4688..3280975 100755
--- a/regressions/test-qemudie-killsub.sh
+++ b/regressions/test-qemudie-killsub.sh
@@ -20,12 +20,9 @@
 
 set -e
 
-rm -f test.img
-
-../fish/guestfish <<'EOF'
-alloc test.img 10M
-run
+rm -f test1.img
 
+../fish/guestfish -N disk <<'EOF'
 # Kill the subprocess.
 kill-subprocess
 
@@ -38,4 +35,4 @@ run
 ping-daemon
 EOF
 
-rm -f test.img
+rm -f test1.img
diff --git a/regressions/test-qemudie-midcommand.sh \
b/regressions/test-qemudie-midcommand.sh index 92a4c68..84e19b0 100755
--- a/regressions/test-qemudie-midcommand.sh
+++ b/regressions/test-qemudie-midcommand.sh
@@ -20,12 +20,9 @@
 
 set -e
 
-rm -f test.pid test.img
-
-../fish/guestfish <<'EOF'
-alloc test.img 10M
-run
+rm -f test.pid test1.img
 
+../fish/guestfish -N disk <<'EOF'
 # Kill the subprocess after a short wait.
 pid | cat > test.pid
 ! sleep 2 ; kill $(cat test.pid) &
@@ -38,4 +35,4 @@ run
 ping-daemon
 EOF
 
-rm -f test.pid test.img
+rm -f test.pid test1.img
diff --git a/regressions/test-qemudie-synch.sh b/regressions/test-qemudie-synch.sh
index 5932fef..85aef69 100755
--- a/regressions/test-qemudie-synch.sh
+++ b/regressions/test-qemudie-synch.sh
@@ -20,12 +20,9 @@
 
 set -e
 
-rm -f test.pid test.img
-
-../fish/guestfish <<'EOF'
-alloc test.img 10M
-run
+rm -f test.pid test1.img
 
+../fish/guestfish -N disk <<'EOF'
 # Kill subprocess.
 pid | cat > test.pid
 ! kill $(cat test.pid) ; sleep 2
@@ -39,4 +36,4 @@ run
 ping-daemon
 EOF
 
-rm -f test.pid test.img
+rm -f test.pid test1.img
-- 
1.7.0.1



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic