diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 64cf696..43b811c 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -149,6 +149,16 @@ struct event {
   void *opaque2;
 };
 
+struct drive {
+  char *path;
+
+  int readonly;
+  char *format;
+  char *iface;
+
+  struct drive *next;
+};
+
 struct guestfs_h
 {
   struct guestfs_h *next;	/* Linked list of open handles. */
@@ -181,6 +191,8 @@ struct guestfs_h
   char *qemu;			/* Qemu binary. */
   char *append;			/* Append to kernel command line. */
 
+  struct drive *drives;
+
   enum attach_method attach_method;
   char *attach_method_arg;
 
@@ -353,6 +365,7 @@ extern void guestfs___trace (guestfs_h *g, const char *fs, ...)
 extern const char *guestfs___persistent_tmpdir (void);
 extern void guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...);
 extern void guestfs___free_inspect_info (guestfs_h *g);
+extern void guestfs___free_drives (struct drive **drives);
 extern int guestfs___set_busy (guestfs_h *g);
 extern int guestfs___end_busy (guestfs_h *g);
 extern int guestfs___send (guestfs_h *g, int proc_nr, uint64_t progress_hint, uint64_t optargs_bitmask, xdrproc_t xdrp, char *args);
@@ -374,8 +387,8 @@ extern int guestfs___match2 (guestfs_h *g, const char *str, const pcre *re, char
 extern int guestfs___match3 (guestfs_h *g, const char *str, const pcre *re, char **ret1, char **ret2, char **ret3);
 extern int guestfs___feature_available (guestfs_h *g, const char *feature);
 extern void guestfs___free_string_list (char **);
-extern size_t guestfs___checkpoint_cmdline (guestfs_h *g);
-extern void guestfs___rollback_cmdline (guestfs_h *g, size_t pos);
+extern struct drive ** guestfs___checkpoint_drives (guestfs_h *g);
+extern void guestfs___rollback_drives (guestfs_h *g, struct drive **i);
 extern void guestfs___call_callbacks_void (guestfs_h *g, uint64_t event);
 extern void guestfs___call_callbacks_message (guestfs_h *g, uint64_t event, const char *buf, size_t buf_len);
 extern void guestfs___call_callbacks_array (guestfs_h *g, uint64_t event, const uint64_t *array, size_t array_len);
diff --git a/src/guestfs.c b/src/guestfs.c
index f7ad967..f4b79e7 100644
--- a/src/guestfs.c
+++ b/src/guestfs.c
@@ -206,6 +206,7 @@ guestfs_close (guestfs_h *g)
   g->events = NULL;
 
   guestfs___free_inspect_info (g);
+  guestfs___free_drives (&g->drives);
 
   /* Close sockets. */
   if (g->fd[0] >= 0)
@@ -994,3 +995,22 @@ guestfs___free_string_list (char **argv)
     free (argv[i]);
   free (argv);
 }
+
+void
+guestfs___free_drives (struct drive **drives)
+{
+  struct drive *i = *drives;
+  *drives = NULL;
+
+  while (i != NULL) {
+    struct drive *next = i->next;
+
+    free (i->path);
+    free (i->format);
+    free (i->iface);
+    free (i->name);
+    free (i);
+
+    i = next;
+  }
+}
diff --git a/src/launch.c b/src/launch.c
index 8e171f7..6462adc 100644
--- a/src/launch.c
+++ b/src/launch.c
@@ -134,23 +134,18 @@ add_cmdline (guestfs_h *g, const char *str)
   return 0;
 }
 
-size_t
-guestfs___checkpoint_cmdline (guestfs_h *g)
+struct drive **
+guestfs___checkpoint_drives (guestfs_h *g)
 {
-  return g->cmdline_size;
+  struct drive **i = &g->drives;
+  while (*i != NULL) i = &((*i)->next);
+  return i;
 }
 
 void
-guestfs___rollback_cmdline (guestfs_h *g, size_t pos)
+guestfs___rollback_drives (guestfs_h *g, struct drive **i)
 {
-  size_t i;
-
-  assert (g->cmdline_size >= pos);
-
-  for (i = pos; i < g->cmdline_size; ++i)
-    free (g->cmdline[i]);
-
-  g->cmdline_size = pos;
+  guestfs___free_drives(i);
 }
 
 /* Internal command to return the command line. */
@@ -258,8 +253,8 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
                          const struct guestfs_add_drive_opts_argv *optargs)
 {
   int readonly;
-  const char *format;
-  const char *iface;
+  char *format;
+  char *iface;
 
   if (strchr (filename, ',') != NULL) {
     error (g, _("filename cannot contain ',' (comma) character"));
@@ -269,9 +264,9 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
   readonly = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK
              ? optargs->readonly : 0;
   format = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK
-           ? optargs->format : NULL;
+           ? safe_strdup (g, optargs->format) : NULL;
   iface = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK
-          ? optargs->iface : DRIVE_IF;
+          ? safe_strdup (g, optargs->iface) : safe_strdup (g, DRIVE_IF);
 
   if (format && !valid_format_iface (format)) {
     error (g, _("%s parameter is empty or contains disallowed characters"),
@@ -284,35 +279,17 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
     return -1;
   }
 
-  /* For writable files, see if we can use cache=off.  This also
-   * checks for the existence of the file.  For readonly we have
-   * to do the check explicitly.
-   */
-  int use_cache_off = readonly ? 0 : test_cache_off (g, filename);
-  if (use_cache_off == -1)
-    return -1;
+  struct drive **i = &(g->drives);
+  while (*i != NULL) i = &((*i)->next);
 
-  if (readonly) {
-    if (access (filename, F_OK) == -1) {
-      perrorf (g, "%s", filename);
-      return -1;
-    }
-  }
-
-  /* Construct the final -drive parameter. */
-  size_t len = 64 + strlen (filename) + strlen (iface);
-  if (format) len += strlen (format);
-  char buf[len];
+  *i = safe_malloc (g, sizeof (struct drive));
+  (*i)->path = safe_strdup (g, filename);
+  (*i)->readonly = readonly;
+  (*i)->format = format;
+  (*i)->iface = iface;
+  (*i)->next = NULL;
 
-  snprintf (buf, len, "file=%s%s%s%s%s,if=%s",
-            filename,
-            readonly ? ",snapshot=on" : "",
-            use_cache_off ? ",cache=off" : "",
-            format ? ",format=" : "",
-            format ? format : "",
-            iface);
-
-  return guestfs__config (g, "-drive", buf);
+  return 0;
 }
 
 int
@@ -432,7 +409,7 @@ launch_appliance (guestfs_h *g)
   /* At present you must add drives before starting the appliance.  In
    * future when we enable hotplugging you won't need to do this.
    */
-  if (!g->cmdline) {
+  if (!g->drives) {
     error (g, _("you must call guestfs_add_drive before guestfs_launch"));
     return -1;
   }
@@ -518,7 +495,43 @@ launch_appliance (guestfs_h *g)
     /* Set up the full command line.  Do this in the subprocess so we
      * don't need to worry about cleaning up.
      */
-    g->cmdline[0] = g->qemu;
+
+    /* Add drives */
+    struct drive *i = g->drives;
+    while (i != NULL) {
+      /* For writable files, see if we can use cache=off.  This also
+       * checks for the existence of the file.  For readonly we have
+       * to do the check explicitly.
+       */
+      int use_cache_off = i->readonly ? 0 : test_cache_off (g, i->path);
+      if (use_cache_off == -1)
+        return -1;
+
+      if (i->readonly) {
+        if (access (i->path, F_OK) == -1) {
+          perrorf (g, "%s", i->path);
+          return -1;
+        }
+      }
+
+      /* Construct the final -drive parameter. */
+      size_t len = 64 + strlen (i->path) + strlen (i->iface);
+      if (i->format) len += strlen (i->format);
+      char buf[len];
+
+      snprintf (buf, len, "file=%s%s%s%s%s,if=%s",
+                i->path,
+                i->readonly ? ",snapshot=on" : "",
+                use_cache_off ? ",cache=off" : "",
+                i->format ? ",format=" : "",
+                i->format ? i->format : "",
+                i->iface);
+
+      add_cmdline (g, "-drive");
+      add_cmdline (g, buf);
+
+      i = i->next;
+    }
 
     if (qemu_supports (g, "-nodefconfig"))
       add_cmdline (g, "-nodefconfig");
@@ -662,6 +675,7 @@ launch_appliance (guestfs_h *g)
     /* Finish off the command line. */
     incr_cmdline_size (g);
     g->cmdline[g->cmdline_size-1] = NULL;
+    g->cmdline[0] = g->qemu;
 
     if (!g->direct) {
       /* Set up stdin, stdout, stderr. */
diff --git a/src/virt.c b/src/virt.c
index 58cb999..cc11c68 100644
--- a/src/virt.c
+++ b/src/virt.c
@@ -392,10 +392,10 @@ guestfs___add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
   /* Checkpoint the command line around the operation so that either
    * all disks are added or none are added.
    */
-  cmdline_pos = guestfs___checkpoint_cmdline (g);
+  struct drive **cp = guestfs___checkpoint_drives (g);
   r = guestfs___for_each_disk (g, dom, add_disk, &optargs2);
   if (r == -1)
-    guestfs___rollback_cmdline (g, cmdline_pos);
+    guestfs___rollback_drives (g, cp);
 
   return r;
 }
