Added test suite.
authorRichard Jones <rjones@redhat.com>
Fri, 10 Apr 2009 17:25:07 +0000 (18:25 +0100)
committerRichard Jones <rjones@redhat.com>
Sat, 11 Apr 2009 16:04:35 +0000 (17:04 +0100)
33 files changed:
Makefile.am
daemon/actions.h
daemon/daemon.h
daemon/devsparts.c
daemon/dir.c
daemon/file.c
daemon/guestfsd.c
daemon/lvm.c
daemon/mount.c
daemon/proto.c
daemon/stubs.c
daemon/sync.c
fish/cmds.c
fish/fish.c
fish/fish.h
guestfish-actions.pod
guestfs-actions.pod
libguestfs.spec.in
make-initramfs.sh.in
ocaml/guestfs.ml
ocaml/guestfs.mli
ocaml/guestfs_c.c
ocaml/guestfs_c.h
ocaml/guestfs_c_actions.c
perl/Guestfs.xs
perl/lib/Sys/Guestfs.pm
src/generator.ml
src/guestfs-actions.c
src/guestfs-actions.h
src/guestfs_protocol.c
src/guestfs_protocol.h
src/guestfs_protocol.x
tests.c

index 47954a5..2345018 100644 (file)
@@ -72,6 +72,8 @@ tests_LDADD = $(top_builddir)/src/libguestfs.la
 
 TESTS = $(check_PROGRAMS)
 
+$(TESTS): $(INITRAMFS) $(VMLINUZ)
+
 # Manual pages.
 # guestfs-actions.pod and guestfs-structs are autogenerated.  There is
 # no include mechanism for POD, so we have to do it by hand.
index 1b35cad..2a9d3d1 100644 (file)
@@ -56,3 +56,16 @@ extern int do_mkdir (const char *path);
 extern int do_mkdir_p (const char *path);
 extern int do_chmod (int mode, const char *path);
 extern int do_chown (int owner, int group, const char *path);
+extern int do_exists (const char *path);
+extern int do_is_file (const char *path);
+extern int do_is_dir (const char *path);
+extern int do_pvcreate (const char *device);
+extern int do_vgcreate (const char *volgroup, char * const* const physvols);
+extern int do_lvcreate (const char *logvol, const char *volgroup, int mbytes);
+extern int do_mkfs (const char *fstype, const char *device);
+extern int do_sfdisk (const char *device, int cyls, int heads, int sectors, char * const* const lines);
+extern int do_write_file (const char *path, const char *content, int size);
+extern int do_umount (const char *pathordevice);
+extern char **do_mounts (void);
+extern int do_umount_all (void);
+extern int do_lvm_remove_all (void);
index b2396a1..27d86c9 100644 (file)
@@ -21,6 +21,9 @@
 
 #include <stdarg.h>
 #include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <rpc/types.h>
 #include <rpc/xdr.h>
 #include "../src/guestfs_protocol.h"
 
 /* in guestfsd.c */
-extern void xwrite (int sock, const void *buf, size_t len);
-extern void xread (int sock, void *buf, size_t len);
+extern int xwrite (int sock, const void *buf, size_t len);
+extern int xread (int sock, void *buf, size_t len);
 
 extern int add_string (char ***argv, int *size, int *alloc, const char *str);
-extern int count_strings (char **argv);
+extern int count_strings (char * const* const argv);
 extern void sort_strings (char **argv, int len);
 extern void free_strings (char **argv);
 extern void free_stringslen (char **argv, int len);
 
 extern int command (char **stdoutput, char **stderror, const char *name, ...);
+extern int commandv (char **stdoutput, char **stderror,
+                    char * const* const argv);
 
 /* in proto.c */
 extern int proc_nr;
@@ -75,6 +80,19 @@ extern void reply (xdrproc_t xdrp, char *ret);
     }                                                                  \
   } while (0)
 
+#define IS_DEVICE(path,errcode)                                                \
+  do {                                                                 \
+    struct stat statbuf;                                               \
+    if (strncmp ((path), "/dev/", 5) != 0) {                           \
+      reply_with_error ("%s: %s: expecting a device name", __func__, (path)); \
+      return (errcode);                                                        \
+    }                                                                  \
+    if (stat ((path), &statbuf) == -1) {                               \
+      reply_with_perror ("%s: %s", __func__, (path));                  \
+      return (errcode);                                                        \
+    }                                                                  \
+  } while (0)
+
 /* NB:
  * (1) You must match CHROOT_IN and CHROOT_OUT even along error paths.
  * (2) You must not change directory!  cwd must always be "/", otherwise
@@ -86,4 +104,11 @@ extern void reply (xdrproc_t xdrp, char *ret);
 #define CHROOT_OUT \
   do { int old_errno = errno; chroot ("."); errno = old_errno; } while (0)
 
+#define XXX_NOT_IMPL(errcode)                                          \
+  do {                                                                 \
+    reply_with_error ("%s: function not implemented", __func__);       \
+    return (errcode);                                                  \
+  }                                                                    \
+  while (0)
+
 #endif /* GUESTFSD_DAEMON_H */
index b0d7956..324b887 100644 (file)
@@ -131,3 +131,65 @@ do_list_partitions (void)
   sort_strings (r, size-1);
   return r;
 }
+
+int
+do_mkfs (const char *fstype, const char *device)
+{
+  char *err;
+  int r;
+
+  IS_DEVICE (device, -1);
+
+  r = command (NULL, &err, "/sbin/mkfs", "-t", fstype, device, NULL);
+  if (r == -1) {
+    reply_with_error ("mkfs: %s", err);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+  return 0;
+}
+
+int
+do_sfdisk (const char *device, int cyls, int heads, int sectors,
+          char * const* const lines)
+{
+  FILE *fp;
+  char buf[256];
+  int i;
+
+  IS_DEVICE (device, -1);
+
+  /* Safe because of IS_DEVICE above. */
+  strcpy (buf, "/sbin/sfdisk");
+  if (cyls)
+    sprintf (buf + strlen (buf), " -C %d", cyls);
+  if (heads)
+    sprintf (buf + strlen (buf), " -H %d", heads);
+  if (sectors)
+    sprintf (buf + strlen (buf), " -S %d", sectors);
+  sprintf (buf + strlen (buf), " %s", device);
+
+  fp = popen (buf, "w");
+  if (fp == NULL) {
+    reply_with_perror (buf);
+    return -1;
+  }
+
+  for (i = 0; lines[i] != NULL; ++i) {
+    if (fprintf (fp, "%s\n", lines[i]) < 0) {
+      reply_with_perror (buf);
+      fclose (fp);
+      return -1;
+    }
+  }
+
+  if (fclose (fp) == EOF) {
+    reply_with_perror (buf);
+    fclose (fp);
+    return -1;
+  }
+
+  return 0;
+}
index 7892682..f706466 100644 (file)
@@ -76,7 +76,7 @@ do_rm_rf (const char *path)
 
   snprintf (buf, len, "/sysroot%s", path);
 
-  r = command (NULL, &err, "rm", "-rf", buf);
+  r = command (NULL, &err, "rm", "-rf", buf, NULL);
   free (buf);
 
   /* rm -rf is never supposed to fail.  I/O errors perhaps? */
@@ -165,3 +165,28 @@ do_mkdir_p (const char *path)
 
   return 0;
 }
+
+int
+do_is_dir (const char *path)
+{
+  int r;
+  struct stat buf;
+
+  NEED_ROOT (-1);
+  ABS_PATH (path, -1);
+
+  CHROOT_IN;
+  r = lstat (path, &buf);
+  CHROOT_OUT;
+
+  if (r == -1) {
+    if (errno != ENOENT && errno != ENOTDIR) {
+      reply_with_perror ("stat: %s", path);
+      return -1;
+    }
+    else
+      return 0;                        /* Not a directory. */
+  }
+
+  return S_ISDIR (buf.st_mode);
+}
index 43c875c..0b8b463 100644 (file)
@@ -240,3 +240,77 @@ do_chown (int owner, int group, const char *path)
 
   return 0;
 }
+
+int
+do_exists (const char *path)
+{
+  int r;
+
+  NEED_ROOT (-1);
+  ABS_PATH (path, -1);
+
+  CHROOT_IN;
+  r = access (path, F_OK);
+  CHROOT_OUT;
+
+  return r == 0;
+}
+
+int
+do_is_file (const char *path)
+{
+  int r;
+  struct stat buf;
+
+  NEED_ROOT (-1);
+  ABS_PATH (path, -1);
+
+  CHROOT_IN;
+  r = lstat (path, &buf);
+  CHROOT_OUT;
+
+  if (r == -1) {
+    if (errno != ENOENT && errno != ENOTDIR) {
+      reply_with_perror ("stat: %s", path);
+      return -1;
+    }
+    else
+      return 0;                        /* Not a file. */
+  }
+
+  return S_ISREG (buf.st_mode);
+}
+
+int
+do_write_file (const char *path, const char *content, int size)
+{
+  int fd;
+
+  NEED_ROOT (-1);
+  ABS_PATH (path, -1);
+
+  if (size == 0)
+    size = strlen (content);
+
+  CHROOT_IN;
+  fd = open (path, O_WRONLY | O_CREAT | O_NOCTTY | O_NONBLOCK, 0666);
+  CHROOT_OUT;
+
+  if (fd == -1) {
+    reply_with_perror ("open: %s", path);
+    return -1;
+  }
+
+  if (xwrite (fd, content, size) == -1) {
+    reply_with_perror ("write");
+    close (fd);
+    return -1;
+  }
+
+  if (close (fd) == -1) {
+    reply_with_perror ("close: %s", path);
+    return -1;
+  }
+
+  return 0;
+}
index a243c0b..c701f19 100644 (file)
@@ -170,7 +170,7 @@ main (int argc, char *argv[])
     exit (1);
   }
 
-  xwrite (sock, buf, xdr_getpos (&xdr));
+  (void) xwrite (sock, buf, xdr_getpos (&xdr));
 
   xdr_destroy (&xdr);
 
@@ -188,7 +188,7 @@ main (int argc, char *argv[])
   exit (0);
 }
 
-void
+int
 xwrite (int sock, const void *buf, size_t len)
 {
   int r;
@@ -197,14 +197,16 @@ xwrite (int sock, const void *buf, size_t len)
     r = write (sock, buf, len);
     if (r == -1) {
       perror ("write");
-      exit (1);
+      return -1;
     }
     buf += r;
     len -= r;
   }
+
+  return 0;
 }
 
-void
+int
 xread (int sock, void *buf, size_t len)
 {
   int r;
@@ -213,15 +215,17 @@ xread (int sock, void *buf, size_t len)
     r = read (sock, buf, len);
     if (r == -1) {
       perror ("read");
-      exit (1);
+      return -1;
     }
     if (r == 0) {
-      fprintf (stderr, "read: unexpected end of file on comms socket\n");
-      exit (1);
+      fprintf (stderr, "read: unexpected end of file on fd %d\n", sock);
+      return -1;
     }
     buf += r;
     len -= r;
   }
+
+  return 0;
 }
 
 static void
@@ -263,7 +267,7 @@ add_string (char ***argv, int *size, int *alloc, const char *str)
 }
 
 int
-count_strings (char **argv)
+count_strings (char * const* const argv)
 {
   int argc;
 
@@ -314,15 +318,52 @@ free_stringslen (char **argv, int len)
 int
 command (char **stdoutput, char **stderror, const char *name, ...)
 {
+  va_list args;
+  char **argv;
+  char *s;
+  int i, r;
+
+  /* Collect the command line arguments into an array. */
+  va_start (args, name);
+
+  i = 2;
+  argv = malloc (sizeof (char *) * i);
+  argv[0] = (char *) name;
+  argv[1] = NULL;
+
+  while ((s = va_arg (args, char *)) != NULL) {
+    argv = realloc (argv, sizeof (char *) * (++i));
+    argv[i-2] = s;
+    argv[i-1] = NULL;
+  }
+
+  va_end (args);
+
+  r = commandv (stdoutput, stderror, argv);
+
+  /* NB: Mustn't free the strings which are on the stack. */
+  free (argv);
+
+  return r;
+}
+
+int
+commandv (char **stdoutput, char **stderror, char * const* const argv)
+{
   int so_size = 0, se_size = 0;
   int so_fd[2], se_fd[2];
-  int pid, r, quit;
+  int pid, r, quit, i;
   fd_set rset, rset2;
   char buf[256];
 
   if (stdoutput) *stdoutput = NULL;
   if (stderror) *stderror = NULL;
 
+  printf ("%s", argv[0]);
+  for (i = 1; argv[i] != NULL; ++i)
+    printf (" %s", argv[i]);
+  printf ("\n");
+
   if (pipe (so_fd) == -1 || pipe (se_fd) == -1) {
     perror ("pipe");
     return -1;
@@ -335,25 +376,6 @@ command (char **stdoutput, char **stderror, const char *name, ...)
   }
 
   if (pid == 0) {              /* Child process. */
-    va_list args;
-    char **argv;
-    char *s;
-    int i;
-
-    /* Collect the command line arguments into an array. */
-    va_start (args, name);
-
-    i = 2;
-    argv = malloc (sizeof (char *) * i);
-    argv[0] = (char *) name;
-    argv[1] = NULL;
-
-    while ((s = va_arg (args, char *)) != NULL) {
-      argv = realloc (argv, sizeof (char *) * (++i));
-      argv[i-2] = s;
-      argv[i-1] = NULL;
-    }
-
     close (0);
     close (so_fd[0]);
     close (se_fd[0]);
@@ -362,8 +384,8 @@ command (char **stdoutput, char **stderror, const char *name, ...)
     close (so_fd[1]);
     close (se_fd[1]);
 
-    execvp (name, argv);
-    perror (name);
+    execvp (argv[0], argv);
+    perror (argv[0]);
     _exit (1);
   }
 
@@ -376,7 +398,7 @@ command (char **stdoutput, char **stderror, const char *name, ...)
   FD_SET (se_fd[0], &rset);
 
   quit = 0;
-  while (!quit) {
+  while (quit < 2) {
     rset2 = rset;
     r = select (MAX (so_fd[0], se_fd[0]) + 1, &rset2, NULL, NULL, NULL);
     if (r == -1) {
@@ -392,7 +414,7 @@ command (char **stdoutput, char **stderror, const char *name, ...)
        waitpid (pid, NULL, 0);
        return -1;
       }
-      if (r == 0) quit = 1;
+      if (r == 0) { FD_CLR (so_fd[0], &rset); quit++; }
 
       if (r > 0 && stdoutput) {
        so_size += r;
@@ -413,7 +435,7 @@ command (char **stdoutput, char **stderror, const char *name, ...)
        waitpid (pid, NULL, 0);
        return -1;
       }
-      if (r == 0) quit = 1;
+      if (r == 0) { FD_CLR (se_fd[0], &rset); quit++; }
 
       if (r > 0 && stderror) {
        se_size += r;
index 7f31c84..55b41f0 100644 (file)
@@ -169,3 +169,134 @@ do_lvs_full (void)
 {
   return parse_command_line_lvs ();
 }
+
+int
+do_pvcreate (const char *device)
+{
+  char *err;
+  int r;
+
+  r = command (NULL, &err,
+              "/sbin/lvm", "pvcreate", device, NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+  return 0;
+}
+
+int
+do_vgcreate (const char *volgroup, char * const* const physvols)
+{
+  char *err;
+  int r, argc, i;
+  const char **argv;
+
+  argc = count_strings (physvols) + 3;
+  argv = malloc (sizeof (char *) * (argc + 1));
+  argv[0] = "/sbin/lvm";
+  argv[1] = "vgcreate";
+  argv[2] = volgroup;
+  for (i = 3; i <= argc; ++i)
+    argv[i] = physvols[i-3];
+
+  r = commandv (NULL, &err, argv);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+  return 0;
+}
+
+int
+do_lvcreate (const char *logvol, const char *volgroup, int mbytes)
+{
+  char *err;
+  int r;
+  char size[64];
+
+  snprintf (size, sizeof size, "%d", mbytes);
+
+  r = command (NULL, &err,
+              "/sbin/lvm", "lvcreate",
+              "-L", size, "-n", logvol, volgroup, NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+  return 0;
+}
+
+/* Super-dangerous command used for testing.  It removes all
+ * LVs, VGs and PVs permanently.
+ */
+int
+do_lvm_remove_all (void)
+{
+  char **xs;
+  int i, r;
+  char *err;
+
+  /* Remove LVs. */
+  xs = do_lvs ();
+  if (xs == NULL)
+    return -1;
+
+  for (i = 0; xs[i] != NULL; ++i) {
+    r = command (NULL, &err, "/sbin/lvm", "lvremove", "-f", xs[i], NULL);
+    if (r == -1) {
+      reply_with_error ("lvremove: %s: %s", xs[i], err);
+      free (err);
+      free_strings (xs);
+      return -1;
+    }
+    free (err);
+  }
+  free_strings (xs);
+
+  /* Remove VGs. */
+  xs = do_vgs ();
+  if (xs == NULL)
+    return -1;
+
+  for (i = 0; xs[i] != NULL; ++i) {
+    r = command (NULL, &err, "/sbin/lvm", "vgremove", "-f", xs[i], NULL);
+    if (r == -1) {
+      reply_with_error ("vgremove: %s: %s", xs[i], err);
+      free (err);
+      free_strings (xs);
+      return -1;
+    }
+    free (err);
+  }
+  free_strings (xs);
+
+  /* Remove PVs. */
+  xs = do_pvs ();
+  if (xs == NULL)
+    return -1;
+
+  for (i = 0; xs[i] != NULL; ++i) {
+    r = command (NULL, &err, "/sbin/lvm", "pvremove", "-f", xs[i], NULL);
+    if (r == -1) {
+      reply_with_error ("pvremove: %s: %s", xs[i], err);
+      free (err);
+      free_strings (xs);
+      return -1;
+    }
+    free (err);
+  }
+  free_strings (xs);
+
+  /* There, that was easy, sorry about your data. */
+  return 0;
+}
index 440ec0d..fd9c8e4 100644 (file)
@@ -74,3 +74,118 @@ do_mount (const char *device, const char *mountpoint)
 
   return 0;
 }
+
+/* Again, use the external /bin/umount program, so that /etc/mtab
+ * is kept updated.
+ */
+int
+do_umount (const char *pathordevice)
+{
+  int len, freeit = 0, r;
+  char *buf;
+  char *err;
+
+  if (strncmp (pathordevice, "/dev/", 5) == 0)
+    buf = (char *) pathordevice;
+  else {
+    len = strlen (pathordevice) + 9;
+    freeit = 1;
+    buf = malloc (len);
+    if (buf == NULL) {
+      reply_with_perror ("malloc");
+      return -1;
+    }
+    snprintf (buf, len, "/sysroot%s", pathordevice);
+  }
+
+  r = command (NULL, &err, "umount", buf, NULL);
+  if (freeit) free (buf);
+  if (r == -1) {
+    reply_with_error ("umount: %s: %s", pathordevice, err);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+
+  /* update root_mounted? */
+
+  return 0;
+}
+
+char **
+do_mounts (void)
+{
+  char *out, *err;
+  int r;
+  char **ret = NULL;
+  int size = 0, alloc = 0;
+  char *p, *pend, *p2;
+
+  r = command (&out, &err, "mount", NULL);
+  if (r == -1) {
+    reply_with_error ("mount: %s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+
+  free (err);
+
+  p = out;
+  while (p) {
+    pend = strchr (p, '\n');
+    if (pend) {
+      *pend = '\0';
+      pend++;
+    }
+
+    /* Lines have the format:
+     *   /dev/foo on /mountpoint type ...
+     */
+    p2 = strstr (p, " on /sysroot");
+    if (p2 != NULL) {
+      *p2 = '\0';
+      if (add_string (&ret, &size, &alloc, p) == -1) {
+       free (out);
+       return NULL;
+      }
+    }
+
+    p = pend;
+  }
+
+  free (out);
+
+  if (add_string (&ret, &size, &alloc, NULL) == -1)
+    return NULL;
+
+  return ret;
+}
+
+/* Only unmount stuff under /sysroot */
+int
+do_umount_all (void)
+{
+  char **mounts;
+  int i, r;
+  char *err;
+
+  mounts = do_mounts ();
+  if (mounts == NULL)          /* do_mounts has already replied */
+    return -1;
+
+  for (i = 0; mounts[i] != NULL; ++i) {
+    r = command (NULL, &err, "umount", mounts[i], NULL);
+    if (r == -1) {
+      reply_with_error ("umount: %s: %s", mounts[i], err);
+      free (err);
+      free_strings (mounts);
+      return -1;
+    }
+    free (err);
+  }
+
+  free_strings (mounts);
+  return 0;
+}
index 93d33c2..42bc6ea 100644 (file)
@@ -206,8 +206,8 @@ send_error (const char *msg)
   xdr_uint32_t (&xdr, &len);
   xdr_destroy (&xdr);
 
-  xwrite (sock, lenbuf, 4);
-  xwrite (sock, buf, len);
+  (void) xwrite (sock, lenbuf, 4);
+  (void) xwrite (sock, buf, len);
 }
 
 void
@@ -247,6 +247,6 @@ reply (xdrproc_t xdrp, char *ret)
   xdr_uint32_t (&xdr, &len);
   xdr_destroy (&xdr);
 
-  xwrite (sock, lenbuf, 4);
-  xwrite (sock, buf, len);
+  (void) xwrite (sock, lenbuf, 4);
+  (void) xwrite (sock, buf, len);
 }
index 1227fa8..4540ca5 100644 (file)
@@ -51,10 +51,12 @@ static void mount_stub (XDR *xdr_in)
 
   r = do_mount (device, mountpoint);
   if (r == -1)
-    /* do_mount has already called reply_with_error, so just return */
-    return;
+    /* do_mount has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_mount_args, (char *) &args);
 }
 
 static void sync_stub (XDR *xdr_in)
@@ -63,10 +65,11 @@ static void sync_stub (XDR *xdr_in)
 
   r = do_sync ();
   if (r == -1)
-    /* do_sync has already called reply_with_error, so just return */
-    return;
+    /* do_sync has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done: ;
 }
 
 static void touch_stub (XDR *xdr_in)
@@ -85,10 +88,12 @@ static void touch_stub (XDR *xdr_in)
 
   r = do_touch (path);
   if (r == -1)
-    /* do_touch has already called reply_with_error, so just return */
-    return;
+    /* do_touch has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_touch_args, (char *) &args);
 }
 
 static void cat_stub (XDR *xdr_in)
@@ -107,13 +112,15 @@ static void cat_stub (XDR *xdr_in)
 
   r = do_cat (path);
   if (r == NULL)
-    /* do_cat has already called reply_with_error, so just return */
-    return;
+    /* do_cat has already called reply_with_error */
+    goto done;
 
   struct guestfs_cat_ret ret;
   ret.content = r;
   reply ((xdrproc_t) &xdr_guestfs_cat_ret, (char *) &ret);
   free (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_cat_args, (char *) &args);
 }
 
 static void ll_stub (XDR *xdr_in)
@@ -132,13 +139,15 @@ static void ll_stub (XDR *xdr_in)
 
   r = do_ll (directory);
   if (r == NULL)
-    /* do_ll has already called reply_with_error, so just return */
-    return;
+    /* do_ll has already called reply_with_error */
+    goto done;
 
   struct guestfs_ll_ret ret;
   ret.listing = r;
   reply ((xdrproc_t) &xdr_guestfs_ll_ret, (char *) &ret);
   free (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_ll_args, (char *) &args);
 }
 
 static void ls_stub (XDR *xdr_in)
@@ -157,14 +166,16 @@ static void ls_stub (XDR *xdr_in)
 
   r = do_ls (directory);
   if (r == NULL)
-    /* do_ls has already called reply_with_error, so just return */
-    return;
+    /* do_ls has already called reply_with_error */
+    goto done;
 
   struct guestfs_ls_ret ret;
   ret.listing.listing_len = count_strings (r);
   ret.listing.listing_val = r;
   reply ((xdrproc_t) &xdr_guestfs_ls_ret, (char *) &ret);
   free_strings (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_ls_args, (char *) &args);
 }
 
 static void list_devices_stub (XDR *xdr_in)
@@ -173,14 +184,15 @@ static void list_devices_stub (XDR *xdr_in)
 
   r = do_list_devices ();
   if (r == NULL)
-    /* do_list_devices has already called reply_with_error, so just return */
-    return;
+    /* do_list_devices has already called reply_with_error */
+    goto done;
 
   struct guestfs_list_devices_ret ret;
   ret.devices.devices_len = count_strings (r);
   ret.devices.devices_val = r;
   reply ((xdrproc_t) &xdr_guestfs_list_devices_ret, (char *) &ret);
   free_strings (r);
+done: ;
 }
 
 static void list_partitions_stub (XDR *xdr_in)
@@ -189,14 +201,15 @@ static void list_partitions_stub (XDR *xdr_in)
 
   r = do_list_partitions ();
   if (r == NULL)
-    /* do_list_partitions has already called reply_with_error, so just return */
-    return;
+    /* do_list_partitions has already called reply_with_error */
+    goto done;
 
   struct guestfs_list_partitions_ret ret;
   ret.partitions.partitions_len = count_strings (r);
   ret.partitions.partitions_val = r;
   reply ((xdrproc_t) &xdr_guestfs_list_partitions_ret, (char *) &ret);
   free_strings (r);
+done: ;
 }
 
 static void pvs_stub (XDR *xdr_in)
@@ -205,14 +218,15 @@ static void pvs_stub (XDR *xdr_in)
 
   r = do_pvs ();
   if (r == NULL)
-    /* do_pvs has already called reply_with_error, so just return */
-    return;
+    /* do_pvs has already called reply_with_error */
+    goto done;
 
   struct guestfs_pvs_ret ret;
   ret.physvols.physvols_len = count_strings (r);
   ret.physvols.physvols_val = r;
   reply ((xdrproc_t) &xdr_guestfs_pvs_ret, (char *) &ret);
   free_strings (r);
+done: ;
 }
 
 static void vgs_stub (XDR *xdr_in)
@@ -221,14 +235,15 @@ static void vgs_stub (XDR *xdr_in)
 
   r = do_vgs ();
   if (r == NULL)
-    /* do_vgs has already called reply_with_error, so just return */
-    return;
+    /* do_vgs has already called reply_with_error */
+    goto done;
 
   struct guestfs_vgs_ret ret;
   ret.volgroups.volgroups_len = count_strings (r);
   ret.volgroups.volgroups_val = r;
   reply ((xdrproc_t) &xdr_guestfs_vgs_ret, (char *) &ret);
   free_strings (r);
+done: ;
 }
 
 static void lvs_stub (XDR *xdr_in)
@@ -237,14 +252,15 @@ static void lvs_stub (XDR *xdr_in)
 
   r = do_lvs ();
   if (r == NULL)
-    /* do_lvs has already called reply_with_error, so just return */
-    return;
+    /* do_lvs has already called reply_with_error */
+    goto done;
 
   struct guestfs_lvs_ret ret;
   ret.logvols.logvols_len = count_strings (r);
   ret.logvols.logvols_val = r;
   reply ((xdrproc_t) &xdr_guestfs_lvs_ret, (char *) &ret);
   free_strings (r);
+done: ;
 }
 
 static void pvs_full_stub (XDR *xdr_in)
@@ -253,13 +269,14 @@ static void pvs_full_stub (XDR *xdr_in)
 
   r = do_pvs_full ();
   if (r == NULL)
-    /* do_pvs_full has already called reply_with_error, so just return */
-    return;
+    /* do_pvs_full has already called reply_with_error */
+    goto done;
 
   struct guestfs_pvs_full_ret ret;
   ret.physvols = *r;
   reply ((xdrproc_t) xdr_guestfs_pvs_full_ret, (char *) &ret);
   xdr_free ((xdrproc_t) xdr_guestfs_pvs_full_ret, (char *) &ret);
+done: ;
 }
 
 static void vgs_full_stub (XDR *xdr_in)
@@ -268,13 +285,14 @@ static void vgs_full_stub (XDR *xdr_in)
 
   r = do_vgs_full ();
   if (r == NULL)
-    /* do_vgs_full has already called reply_with_error, so just return */
-    return;
+    /* do_vgs_full has already called reply_with_error */
+    goto done;
 
   struct guestfs_vgs_full_ret ret;
   ret.volgroups = *r;
   reply ((xdrproc_t) xdr_guestfs_vgs_full_ret, (char *) &ret);
   xdr_free ((xdrproc_t) xdr_guestfs_vgs_full_ret, (char *) &ret);
+done: ;
 }
 
 static void lvs_full_stub (XDR *xdr_in)
@@ -283,13 +301,14 @@ static void lvs_full_stub (XDR *xdr_in)
 
   r = do_lvs_full ();
   if (r == NULL)
-    /* do_lvs_full has already called reply_with_error, so just return */
-    return;
+    /* do_lvs_full has already called reply_with_error */
+    goto done;
 
   struct guestfs_lvs_full_ret ret;
   ret.logvols = *r;
   reply ((xdrproc_t) xdr_guestfs_lvs_full_ret, (char *) &ret);
   xdr_free ((xdrproc_t) xdr_guestfs_lvs_full_ret, (char *) &ret);
+done: ;
 }
 
 static void read_lines_stub (XDR *xdr_in)
@@ -308,14 +327,16 @@ static void read_lines_stub (XDR *xdr_in)
 
   r = do_read_lines (path);
   if (r == NULL)
-    /* do_read_lines has already called reply_with_error, so just return */
-    return;
+    /* do_read_lines has already called reply_with_error */
+    goto done;
 
   struct guestfs_read_lines_ret ret;
   ret.lines.lines_len = count_strings (r);
   ret.lines.lines_val = r;
   reply ((xdrproc_t) &xdr_guestfs_read_lines_ret, (char *) &ret);
   free_strings (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_read_lines_args, (char *) &args);
 }
 
 static void aug_init_stub (XDR *xdr_in)
@@ -336,10 +357,12 @@ static void aug_init_stub (XDR *xdr_in)
 
   r = do_aug_init (root, flags);
   if (r == -1)
-    /* do_aug_init has already called reply_with_error, so just return */
-    return;
+    /* do_aug_init has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_init_args, (char *) &args);
 }
 
 static void aug_close_stub (XDR *xdr_in)
@@ -348,10 +371,11 @@ static void aug_close_stub (XDR *xdr_in)
 
   r = do_aug_close ();
   if (r == -1)
-    /* do_aug_close has already called reply_with_error, so just return */
-    return;
+    /* do_aug_close has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done: ;
 }
 
 static void aug_defvar_stub (XDR *xdr_in)
@@ -372,12 +396,14 @@ static void aug_defvar_stub (XDR *xdr_in)
 
   r = do_aug_defvar (name, expr);
   if (r == -1)
-    /* do_aug_defvar has already called reply_with_error, so just return */
-    return;
+    /* do_aug_defvar has already called reply_with_error */
+    goto done;
 
   struct guestfs_aug_defvar_ret ret;
   ret.nrnodes = r;
   reply ((xdrproc_t) &xdr_guestfs_aug_defvar_ret, (char *) &ret);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_defvar_args, (char *) &args);
 }
 
 static void aug_defnode_stub (XDR *xdr_in)
@@ -400,11 +426,13 @@ static void aug_defnode_stub (XDR *xdr_in)
 
   r = do_aug_defnode (name, expr, val);
   if (r == NULL)
-    /* do_aug_defnode has already called reply_with_error, so just return */
-    return;
+    /* do_aug_defnode has already called reply_with_error */
+    goto done;
 
   reply ((xdrproc_t) xdr_guestfs_aug_defnode_ret, (char *) r);
   xdr_free ((xdrproc_t) xdr_guestfs_aug_defnode_ret, (char *) r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_defnode_args, (char *) &args);
 }
 
 static void aug_get_stub (XDR *xdr_in)
@@ -423,13 +451,15 @@ static void aug_get_stub (XDR *xdr_in)
 
   r = do_aug_get (path);
   if (r == NULL)
-    /* do_aug_get has already called reply_with_error, so just return */
-    return;
+    /* do_aug_get has already called reply_with_error */
+    goto done;
 
   struct guestfs_aug_get_ret ret;
   ret.val = r;
   reply ((xdrproc_t) &xdr_guestfs_aug_get_ret, (char *) &ret);
   free (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_get_args, (char *) &args);
 }
 
 static void aug_set_stub (XDR *xdr_in)
@@ -450,10 +480,12 @@ static void aug_set_stub (XDR *xdr_in)
 
   r = do_aug_set (path, val);
   if (r == -1)
-    /* do_aug_set has already called reply_with_error, so just return */
-    return;
+    /* do_aug_set has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_set_args, (char *) &args);
 }
 
 static void aug_insert_stub (XDR *xdr_in)
@@ -476,10 +508,12 @@ static void aug_insert_stub (XDR *xdr_in)
 
   r = do_aug_insert (path, label, before);
   if (r == -1)
-    /* do_aug_insert has already called reply_with_error, so just return */
-    return;
+    /* do_aug_insert has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_insert_args, (char *) &args);
 }
 
 static void aug_rm_stub (XDR *xdr_in)
@@ -498,12 +532,14 @@ static void aug_rm_stub (XDR *xdr_in)
 
   r = do_aug_rm (path);
   if (r == -1)
-    /* do_aug_rm has already called reply_with_error, so just return */
-    return;
+    /* do_aug_rm has already called reply_with_error */
+    goto done;
 
   struct guestfs_aug_rm_ret ret;
   ret.nrnodes = r;
   reply ((xdrproc_t) &xdr_guestfs_aug_rm_ret, (char *) &ret);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_rm_args, (char *) &args);
 }
 
 static void aug_mv_stub (XDR *xdr_in)
@@ -524,10 +560,12 @@ static void aug_mv_stub (XDR *xdr_in)
 
   r = do_aug_mv (src, dest);
   if (r == -1)
-    /* do_aug_mv has already called reply_with_error, so just return */
-    return;
+    /* do_aug_mv has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_mv_args, (char *) &args);
 }
 
 static void aug_match_stub (XDR *xdr_in)
@@ -546,14 +584,16 @@ static void aug_match_stub (XDR *xdr_in)
 
   r = do_aug_match (path);
   if (r == NULL)
-    /* do_aug_match has already called reply_with_error, so just return */
-    return;
+    /* do_aug_match has already called reply_with_error */
+    goto done;
 
   struct guestfs_aug_match_ret ret;
   ret.matches.matches_len = count_strings (r);
   ret.matches.matches_val = r;
   reply ((xdrproc_t) &xdr_guestfs_aug_match_ret, (char *) &ret);
   free_strings (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_match_args, (char *) &args);
 }
 
 static void aug_save_stub (XDR *xdr_in)
@@ -562,10 +602,11 @@ static void aug_save_stub (XDR *xdr_in)
 
   r = do_aug_save ();
   if (r == -1)
-    /* do_aug_save has already called reply_with_error, so just return */
-    return;
+    /* do_aug_save has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done: ;
 }
 
 static void aug_load_stub (XDR *xdr_in)
@@ -574,10 +615,11 @@ static void aug_load_stub (XDR *xdr_in)
 
   r = do_aug_load ();
   if (r == -1)
-    /* do_aug_load has already called reply_with_error, so just return */
-    return;
+    /* do_aug_load has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done: ;
 }
 
 static void aug_ls_stub (XDR *xdr_in)
@@ -596,14 +638,16 @@ static void aug_ls_stub (XDR *xdr_in)
 
   r = do_aug_ls (path);
   if (r == NULL)
-    /* do_aug_ls has already called reply_with_error, so just return */
-    return;
+    /* do_aug_ls has already called reply_with_error */
+    goto done;
 
   struct guestfs_aug_ls_ret ret;
   ret.matches.matches_len = count_strings (r);
   ret.matches.matches_val = r;
   reply ((xdrproc_t) &xdr_guestfs_aug_ls_ret, (char *) &ret);
   free_strings (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_aug_ls_args, (char *) &args);
 }
 
 static void rm_stub (XDR *xdr_in)
@@ -622,10 +666,12 @@ static void rm_stub (XDR *xdr_in)
 
   r = do_rm (path);
   if (r == -1)
-    /* do_rm has already called reply_with_error, so just return */
-    return;
+    /* do_rm has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_rm_args, (char *) &args);
 }
 
 static void rmdir_stub (XDR *xdr_in)
@@ -644,10 +690,12 @@ static void rmdir_stub (XDR *xdr_in)
 
   r = do_rmdir (path);
   if (r == -1)
-    /* do_rmdir has already called reply_with_error, so just return */
-    return;
+    /* do_rmdir has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_rmdir_args, (char *) &args);
 }
 
 static void rm_rf_stub (XDR *xdr_in)
@@ -666,10 +714,12 @@ static void rm_rf_stub (XDR *xdr_in)
 
   r = do_rm_rf (path);
   if (r == -1)
-    /* do_rm_rf has already called reply_with_error, so just return */
-    return;
+    /* do_rm_rf has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_rm_rf_args, (char *) &args);
 }
 
 static void mkdir_stub (XDR *xdr_in)
@@ -688,10 +738,12 @@ static void mkdir_stub (XDR *xdr_in)
 
   r = do_mkdir (path);
   if (r == -1)
-    /* do_mkdir has already called reply_with_error, so just return */
-    return;
+    /* do_mkdir has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_mkdir_args, (char *) &args);
 }
 
 static void mkdir_p_stub (XDR *xdr_in)
@@ -710,10 +762,12 @@ static void mkdir_p_stub (XDR *xdr_in)
 
   r = do_mkdir_p (path);
   if (r == -1)
-    /* do_mkdir_p has already called reply_with_error, so just return */
-    return;
+    /* do_mkdir_p has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_mkdir_p_args, (char *) &args);
 }
 
 static void chmod_stub (XDR *xdr_in)
@@ -734,10 +788,12 @@ static void chmod_stub (XDR *xdr_in)
 
   r = do_chmod (mode, path);
   if (r == -1)
-    /* do_chmod has already called reply_with_error, so just return */
-    return;
+    /* do_chmod has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_chmod_args, (char *) &args);
 }
 
 static void chown_stub (XDR *xdr_in)
@@ -760,10 +816,325 @@ static void chown_stub (XDR *xdr_in)
 
   r = do_chown (owner, group, path);
   if (r == -1)
-    /* do_chown has already called reply_with_error, so just return */
+    /* do_chown has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_chown_args, (char *) &args);
+}
+
+static void exists_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_exists_args args;
+  const char *path;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_exists_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "exists");
     return;
+  }
+  path = args.path;
+
+  r = do_exists (path);
+  if (r == -1)
+    /* do_exists has already called reply_with_error */
+    goto done;
+
+  struct guestfs_exists_ret ret;
+  ret.existsflag = r;
+  reply ((xdrproc_t) &xdr_guestfs_exists_ret, (char *) &ret);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_exists_args, (char *) &args);
+}
+
+static void is_file_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_is_file_args args;
+  const char *path;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_is_file_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "is_file");
+    return;
+  }
+  path = args.path;
+
+  r = do_is_file (path);
+  if (r == -1)
+    /* do_is_file has already called reply_with_error */
+    goto done;
+
+  struct guestfs_is_file_ret ret;
+  ret.fileflag = r;
+  reply ((xdrproc_t) &xdr_guestfs_is_file_ret, (char *) &ret);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_is_file_args, (char *) &args);
+}
+
+static void is_dir_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_is_dir_args args;
+  const char *path;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_is_dir_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "is_dir");
+    return;
+  }
+  path = args.path;
+
+  r = do_is_dir (path);
+  if (r == -1)
+    /* do_is_dir has already called reply_with_error */
+    goto done;
+
+  struct guestfs_is_dir_ret ret;
+  ret.dirflag = r;
+  reply ((xdrproc_t) &xdr_guestfs_is_dir_ret, (char *) &ret);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_is_dir_args, (char *) &args);
+}
+
+static void pvcreate_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_pvcreate_args args;
+  const char *device;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_pvcreate_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "pvcreate");
+    return;
+  }
+  device = args.device;
+
+  r = do_pvcreate (device);
+  if (r == -1)
+    /* do_pvcreate has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_pvcreate_args, (char *) &args);
+}
+
+static void vgcreate_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_vgcreate_args args;
+  const char *volgroup;
+  char **physvols;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_vgcreate_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "vgcreate");
+    return;
+  }
+  volgroup = args.volgroup;
+  args.physvols.physvols_val = realloc (args.physvols.physvols_val, sizeof (char *) * (args.physvols.physvols_len+1));
+  args.physvols.physvols_val[args.physvols.physvols_len] = NULL;
+  physvols = args.physvols.physvols_val;
+
+  r = do_vgcreate (volgroup, physvols);
+  if (r == -1)
+    /* do_vgcreate has already called reply_with_error */
+    goto done;
 
   reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_vgcreate_args, (char *) &args);
+}
+
+static void lvcreate_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_lvcreate_args args;
+  const char *logvol;
+  const char *volgroup;
+  int mbytes;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_lvcreate_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "lvcreate");
+    return;
+  }
+  logvol = args.logvol;
+  volgroup = args.volgroup;
+  mbytes = args.mbytes;
+
+  r = do_lvcreate (logvol, volgroup, mbytes);
+  if (r == -1)
+    /* do_lvcreate has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_lvcreate_args, (char *) &args);
+}
+
+static void mkfs_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_mkfs_args args;
+  const char *fstype;
+  const char *device;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_mkfs_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "mkfs");
+    return;
+  }
+  fstype = args.fstype;
+  device = args.device;
+
+  r = do_mkfs (fstype, device);
+  if (r == -1)
+    /* do_mkfs has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_mkfs_args, (char *) &args);
+}
+
+static void sfdisk_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_sfdisk_args args;
+  const char *device;
+  int cyls;
+  int heads;
+  int sectors;
+  char **lines;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_sfdisk_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "sfdisk");
+    return;
+  }
+  device = args.device;
+  cyls = args.cyls;
+  heads = args.heads;
+  sectors = args.sectors;
+  args.lines.lines_val = realloc (args.lines.lines_val, sizeof (char *) * (args.lines.lines_len+1));
+  args.lines.lines_val[args.lines.lines_len] = NULL;
+  lines = args.lines.lines_val;
+
+  r = do_sfdisk (device, cyls, heads, sectors, lines);
+  if (r == -1)
+    /* do_sfdisk has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_sfdisk_args, (char *) &args);
+}
+
+static void write_file_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_write_file_args args;
+  const char *path;
+  const char *content;
+  int size;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_write_file_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "write_file");
+    return;
+  }
+  path = args.path;
+  content = args.content;
+  size = args.size;
+
+  r = do_write_file (path, content, size);
+  if (r == -1)
+    /* do_write_file has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_write_file_args, (char *) &args);
+}
+
+static void umount_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_umount_args args;
+  const char *pathordevice;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_umount_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "umount");
+    return;
+  }
+  pathordevice = args.pathordevice;
+
+  r = do_umount (pathordevice);
+  if (r == -1)
+    /* do_umount has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_umount_args, (char *) &args);
+}
+
+static void mounts_stub (XDR *xdr_in)
+{
+  char **r;
+
+  r = do_mounts ();
+  if (r == NULL)
+    /* do_mounts has already called reply_with_error */
+    goto done;
+
+  struct guestfs_mounts_ret ret;
+  ret.devices.devices_len = count_strings (r);
+  ret.devices.devices_val = r;
+  reply ((xdrproc_t) &xdr_guestfs_mounts_ret, (char *) &ret);
+  free_strings (r);
+done: ;
+}
+
+static void umount_all_stub (XDR *xdr_in)
+{
+  int r;
+
+  r = do_umount_all ();
+  if (r == -1)
+    /* do_umount_all has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done: ;
+}
+
+static void lvm_remove_all_stub (XDR *xdr_in)
+{
+  int r;
+
+  r = do_lvm_remove_all ();
+  if (r == -1)
+    /* do_lvm_remove_all has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done: ;
 }
 
 void dispatch_incoming_message (XDR *xdr_in)
@@ -874,6 +1245,45 @@ void dispatch_incoming_message (XDR *xdr_in)
     case GUESTFS_PROC_CHOWN:
       chown_stub (xdr_in);
       break;
+    case GUESTFS_PROC_EXISTS:
+      exists_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_IS_FILE:
+      is_file_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_IS_DIR:
+      is_dir_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_PVCREATE:
+      pvcreate_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_VGCREATE:
+      vgcreate_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_LVCREATE:
+      lvcreate_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_MKFS:
+      mkfs_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_SFDISK:
+      sfdisk_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_WRITE_FILE:
+      write_file_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_UMOUNT:
+      umount_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_MOUNTS:
+      mounts_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_UMOUNT_ALL:
+      umount_all_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_LVM_REMOVE_ALL:
+      lvm_remove_all_stub (xdr_in);
+      break;
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
   }
index 9ade840..70962df 100644 (file)
@@ -27,6 +27,5 @@ int
 do_sync ()
 {
   sync ();
-  fprintf (stderr, "guestfsd: disk synched\n");
   return 0;
 }
index cef38cc..1ab9ea0 100644 (file)
@@ -50,20 +50,28 @@ void list_commands (void)
   printf ("%-20s %s\n", "chmod", "change file mode");
   printf ("%-20s %s\n", "chown", "change file owner and group");
   printf ("%-20s %s\n", "config", "add qemu parameters");
+  printf ("%-20s %s\n", "exists", "test if file or directory exists");
   printf ("%-20s %s\n", "get-autosync", "get autosync mode");
   printf ("%-20s %s\n", "get-path", "get the search path");
   printf ("%-20s %s\n", "get-verbose", "get verbose mode");
+  printf ("%-20s %s\n", "is-dir", "test if file exists");
+  printf ("%-20s %s\n", "is-file", "test if file exists");
   printf ("%-20s %s\n", "kill-subprocess", "kill the qemu subprocess");
   printf ("%-20s %s\n", "launch", "launch the qemu subprocess");
   printf ("%-20s %s\n", "list-devices", "list the block devices");
   printf ("%-20s %s\n", "list-partitions", "list the partitions");
   printf ("%-20s %s\n", "ll", "list the files in a directory (long format)");
   printf ("%-20s %s\n", "ls", "list the files in a directory");
+  printf ("%-20s %s\n", "lvcreate", "create an LVM volume group");
+  printf ("%-20s %s\n", "lvm-remove-all", "remove all LVM LVs, VGs and PVs");
   printf ("%-20s %s\n", "lvs", "list the LVM logical volumes (LVs)");
   printf ("%-20s %s\n", "lvs-full", "list the LVM logical volumes (LVs)");
   printf ("%-20s %s\n", "mkdir", "create a directory");
   printf ("%-20s %s\n", "mkdir-p", "create a directory and parents");
+  printf ("%-20s %s\n", "mkfs", "make a filesystem");
   printf ("%-20s %s\n", "mount", "mount a guest disk at a position in the filesystem");
+  printf ("%-20s %s\n", "mounts", "show mounted filesystems");
+  printf ("%-20s %s\n", "pvcreate", "create an LVM physical volume");
   printf ("%-20s %s\n", "pvs", "list the LVM physical volumes (PVs)");
   printf ("%-20s %s\n", "pvs-full", "list the LVM physical volumes (PVs)");
   printf ("%-20s %s\n", "read-lines", "read file as lines");
@@ -73,10 +81,15 @@ void list_commands (void)
   printf ("%-20s %s\n", "set-autosync", "set autosync mode");
   printf ("%-20s %s\n", "set-path", "set the search path");
   printf ("%-20s %s\n", "set-verbose", "set verbose mode");
+  printf ("%-20s %s\n", "sfdisk", "create partitions on a block device");
   printf ("%-20s %s\n", "sync", "sync disks, writes are flushed through to the disk image");
   printf ("%-20s %s\n", "touch", "update file timestamps or create a new file");
+  printf ("%-20s %s\n", "umount", "unmount a filesystem");
+  printf ("%-20s %s\n", "umount-all", "unmount all filesystems");
+  printf ("%-20s %s\n", "vgcreate", "create an LVM volume group");
   printf ("%-20s %s\n", "vgs", "list the LVM volume groups (VGs)");
   printf ("%-20s %s\n", "vgs-full", "list the LVM volume groups (VGs)");
+  printf ("%-20s %s\n", "write-file", "Create a file");
   printf ("    Use -h <cmd> / help <cmd> to show detailed help for a command.\n");
 }
 
@@ -220,6 +233,45 @@ void display_command (const char *cmd)
   if (strcasecmp (cmd, "chown") == 0)
     pod2text ("chown - change file owner and group", " chown <owner> <group> <path>\n\nChange the file owner to C<owner> and group to C<group>.\n\nOnly numeric uid and gid are supported.  If you want to use\nnames, you will need to locate and parse the password file\nyourself (Augeas support makes this relatively easy).");
   else
+  if (strcasecmp (cmd, "exists") == 0)
+    pod2text ("exists - test if file or directory exists", " exists <path>\n\nThis returns C<true> if and only if there is a file, directory\n(or anything) with the given C<path> name.\n\nSee also C<is_file>, C<is_dir>, C<stat>.");
+  else
+  if (strcasecmp (cmd, "is_file") == 0 || strcasecmp (cmd, "is-file") == 0)
+    pod2text ("is-file - test if file exists", " is-file <path>\n\nThis returns C<true> if and only if there is a file\nwith the given C<path> name.  Note that it returns false for\nother objects like directories.\n\nSee also C<stat>.");
+  else
+  if (strcasecmp (cmd, "is_dir") == 0 || strcasecmp (cmd, "is-dir") == 0)
+    pod2text ("is-dir - test if file exists", " is-dir <path>\n\nThis returns C<true> if and only if there is a directory\nwith the given C<path> name.  Note that it returns false for\nother objects like files.\n\nSee also C<stat>.");
+  else
+  if (strcasecmp (cmd, "pvcreate") == 0)
+    pod2text ("pvcreate - create an LVM physical volume", " pvcreate <device>\n\nThis creates an LVM physical volume on the named C<device>,\nwhere C<device> should usually be a partition name such\nas C</dev/sda1>.");
+  else
+  if (strcasecmp (cmd, "vgcreate") == 0)
+    pod2text ("vgcreate - create an LVM volume group", " vgcreate <volgroup> <physvols>\n\nThis creates an LVM volume group called C<volgroup>\nfrom the non-empty list of physical volumes C<physvols>.");
+  else
+  if (strcasecmp (cmd, "lvcreate") == 0)
+    pod2text ("lvcreate - create an LVM volume group", " lvcreate <logvol> <volgroup> <mbytes>\n\nThis creates an LVM volume group called C<logvol>\non the volume group C<volgroup>, with C<size> megabytes.");
+  else
+  if (strcasecmp (cmd, "mkfs") == 0)
+    pod2text ("mkfs - make a filesystem", " mkfs <fstype> <device>\n\nThis creates a filesystem on C<device> (usually a partition\nof LVM logical volume).  The filesystem type is C<fstype>, for\nexample C<ext3>.");
+  else
+  if (strcasecmp (cmd, "sfdisk") == 0)
+    pod2text ("sfdisk - create partitions on a block device", " sfdisk <device> <cyls> <heads> <sectors> <lines>\n\nThis is a direct interface to the L<sfdisk(8)> program for creating\npartitions on block devices.\n\nC<device> should be a block device, for example C</dev/sda>.\n\nC<cyls>, C<heads> and C<sectors> are the number of cylinders, heads\nand sectors on the device, which are passed directly to sfdisk as\nthe I<-C>, I<-H> and I<-S> parameters.  If you pass C<0> for any\nof these, then the corresponding parameter is omitted.  Usually for\n'large' disks, you can just pass C<0> for these, but for small\n(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work\nout the right geometry and you will need to tell it.\n\nC<lines> is a list of lines that we feed to C<sfdisk>.  For more\ninformation refer to the L<sfdisk(8)> manpage.\n\nTo create a single partition occupying the whole disk, you would\npass C<lines> as a single element list, when the single element being\nthe string C<,> (comma).\n\nB<This command is dangerous.  Without careful use you\ncan easily destroy all your data>.");
+  else
+  if (strcasecmp (cmd, "write_file") == 0 || strcasecmp (cmd, "write-file") == 0)
+    pod2text ("write-file - Create a file", " write-file <path> <content> <size>\n\nThis call creates a file called C<path>.  The contents of the\nfile is the string C<content> (which can contain any 8 bit data),\nwith length C<size>.\n\nAs a special case, if C<size> is C<0>\nthen the length is calculated using C<strlen> (so in this case\nthe content cannot contain embedded ASCII NULs).\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB.  To transfer large files you should use\nFTP.");
+  else
+  if (strcasecmp (cmd, "umount") == 0 || strcasecmp (cmd, "unmount") == 0)
+    pod2text ("umount - unmount a filesystem", " umount <pathordevice>\n\nThis unmounts the given filesystem.  The filesystem may be\nspecified either by its mountpoint (path) or the device which\ncontains the filesystem.\n\nYou can use 'unmount' as an alias for this command.");
+  else
+  if (strcasecmp (cmd, "mounts") == 0)
+    pod2text ("mounts - show mounted filesystems", " mounts\n\nThis returns the list of currently mounted filesystems.  It returns\nthe list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).\n\nSome internal mounts are not shown.");
+  else
+  if (strcasecmp (cmd, "umount_all") == 0 || strcasecmp (cmd, "umount-all") == 0 || strcasecmp (cmd, "unmount-all") == 0)
+    pod2text ("umount-all - unmount all filesystems", " umount-all\n\nThis unmounts all mounted filesystems.\n\nSome internal mounts are not unmounted by this call.\n\nYou can use 'unmount-all' as an alias for this command.");
+  else
+  if (strcasecmp (cmd, "lvm_remove_all") == 0 || strcasecmp (cmd, "lvm-remove-all") == 0)
+    pod2text ("lvm-remove-all - remove all LVM LVs, VGs and PVs", " lvm-remove-all\n\nThis command removes all LVM logical volumes, volume groups\nand physical volumes.\n\nB<This command is dangerous.  Without careful use you\ncan easily destroy all your data>.");
+  else
     display_builtin_command (cmd);
 }
 
@@ -1020,6 +1072,211 @@ static int run_chown (const char *cmd, int argc, char *argv[])
   return r;
 }
 
+static int run_exists (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *path;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  path = argv[0];
+  r = guestfs_exists (g, path);
+  if (r == -1) return -1;
+  if (r) printf ("true\n"); else printf ("false\n");
+  return 0;
+}
+
+static int run_is_file (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *path;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  path = argv[0];
+  r = guestfs_is_file (g, path);
+  if (r == -1) return -1;
+  if (r) printf ("true\n"); else printf ("false\n");
+  return 0;
+}
+
+static int run_is_dir (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *path;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  path = argv[0];
+  r = guestfs_is_dir (g, path);
+  if (r == -1) return -1;
+  if (r) printf ("true\n"); else printf ("false\n");
+  return 0;
+}
+
+static int run_pvcreate (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *device;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  device = argv[0];
+  r = guestfs_pvcreate (g, device);
+  return r;
+}
+
+static int run_vgcreate (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *volgroup;
+  char **physvols;
+  if (argc != 2) {
+    fprintf (stderr, "%s should have 2 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  volgroup = argv[0];
+  physvols = parse_string_list (argv[1]);
+  r = guestfs_vgcreate (g, volgroup, physvols);
+  return r;
+}
+
+static int run_lvcreate (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *logvol;
+  const char *volgroup;
+  int mbytes;
+  if (argc != 3) {
+    fprintf (stderr, "%s should have 3 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  logvol = argv[0];
+  volgroup = argv[1];
+  mbytes = atoi (argv[2]);
+  r = guestfs_lvcreate (g, logvol, volgroup, mbytes);
+  return r;
+}
+
+static int run_mkfs (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *fstype;
+  const char *device;
+  if (argc != 2) {
+    fprintf (stderr, "%s should have 2 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  fstype = argv[0];
+  device = argv[1];
+  r = guestfs_mkfs (g, fstype, device);
+  return r;
+}
+
+static int run_sfdisk (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *device;
+  int cyls;
+  int heads;
+  int sectors;
+  char **lines;
+  if (argc != 5) {
+    fprintf (stderr, "%s should have 5 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  device = argv[0];
+  cyls = atoi (argv[1]);
+  heads = atoi (argv[2]);
+  sectors = atoi (argv[3]);
+  lines = parse_string_list (argv[4]);
+  r = guestfs_sfdisk (g, device, cyls, heads, sectors, lines);
+  return r;
+}
+
+static int run_write_file (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *path;
+  const char *content;
+  int size;
+  if (argc != 3) {
+    fprintf (stderr, "%s should have 3 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  path = argv[0];
+  content = argv[1];
+  size = atoi (argv[2]);
+  r = guestfs_write_file (g, path, content, size);
+  return r;
+}
+
+static int run_umount (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *pathordevice;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  pathordevice = argv[0];
+  r = guestfs_umount (g, pathordevice);
+  return r;
+}
+
+static int run_mounts (const char *cmd, int argc, char *argv[])
+{
+  char **r;
+  if (argc != 0) {
+    fprintf (stderr, "%s should have 0 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  r = guestfs_mounts (g);
+  if (r == NULL) return -1;
+  print_strings (r);
+  free_strings (r);
+  return 0;
+}
+
+static int run_umount_all (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  if (argc != 0) {
+    fprintf (stderr, "%s should have 0 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  r = guestfs_umount_all (g);
+  return r;
+}
+
+static int run_lvm_remove_all (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  if (argc != 0) {
+    fprintf (stderr, "%s should have 0 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  r = guestfs_lvm_remove_all (g);
+  return r;
+}
+
 int run_action (const char *cmd, int argc, char *argv[])
 {
   if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0)
@@ -1160,6 +1417,45 @@ int run_action (const char *cmd, int argc, char *argv[])
   if (strcasecmp (cmd, "chown") == 0)
     return run_chown (cmd, argc, argv);
   else
+  if (strcasecmp (cmd, "exists") == 0)
+    return run_exists (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "is_file") == 0 || strcasecmp (cmd, "is-file") == 0)
+    return run_is_file (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "is_dir") == 0 || strcasecmp (cmd, "is-dir") == 0)
+    return run_is_dir (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "pvcreate") == 0)
+    return run_pvcreate (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "vgcreate") == 0)
+    return run_vgcreate (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "lvcreate") == 0)
+    return run_lvcreate (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "mkfs") == 0)
+    return run_mkfs (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "sfdisk") == 0)
+    return run_sfdisk (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "write_file") == 0 || strcasecmp (cmd, "write-file") == 0)
+    return run_write_file (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "umount") == 0 || strcasecmp (cmd, "unmount") == 0)
+    return run_umount (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "mounts") == 0)
+    return run_mounts (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "umount_all") == 0 || strcasecmp (cmd, "umount-all") == 0 || strcasecmp (cmd, "unmount-all") == 0)
+    return run_umount_all (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "lvm_remove_all") == 0 || strcasecmp (cmd, "lvm-remove-all") == 0)
+    return run_lvm_remove_all (cmd, argc, argv);
+  else
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
       return -1;
index e845e4d..18b8d45 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <config.h>
 
+#define _GNU_SOURCE // for strchrnul
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -495,7 +497,7 @@ free_strings (char **argv)
 }
 
 void
-print_strings (char **argv)
+print_strings (char * const * const argv)
 {
   int argc;
 
@@ -513,3 +515,34 @@ is_true (const char *str)
     strcasecmp (str, "n") != 0 &&
     strcasecmp (str, "no") != 0;
 }
+
+/* This is quite inadequate for real use.  For example, there is no way
+ * to specify an empty list.  We need to use a real parser to allow
+ * quoting, empty lists, etc.
+ */
+char **
+parse_string_list (const char *str)
+{
+  char **argv;
+  const char *p, *pend;
+  int argc, i;
+
+  argc = 1;
+  for (i = 0; str[i]; ++i)
+    if (str[i] == ':') argc++;
+
+  argv = malloc (sizeof (char *) * (argc+1));
+  if (argv == NULL) { perror ("malloc"); exit (1); }
+
+  p = str;
+  i = 0;
+  while (*p) {
+    pend = strchrnul (p, ':');
+    argv[i] = strndup (p, pend-p);
+    i++;
+    p = *pend == ':' ? pend+1 : p;
+  }
+  argv[i] = NULL;
+
+  return argv;
+}
index 6d0892f..3997d6d 100644 (file)
@@ -30,9 +30,10 @@ extern void pod2text (const char *heading, const char *body);
 extern void list_builtin_commands (void);
 extern void display_builtin_command (const char *cmd);
 extern void free_strings (char **argv);
-extern void print_strings (char **argv);
+extern void print_strings (char * const * const argv);
 extern int launch (guestfs_h *);
 extern int is_true (const char *str);
+extern char **parse_string_list (const char *str);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
index d47e9bf..50ecfec 100644 (file)
@@ -193,6 +193,10 @@ Note that this function cannot correctly handle binary files
 as end of string).  For those you need to use the C<read_file>
 function which has a more complex interface.
 
+Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP.
+
 =head2 chmod
 
  chmod mode path
@@ -223,6 +227,15 @@ The first character of C<param> string must be a C<-> (dash).
 
 C<value> can be NULL.
 
+=head2 exists
+
+ exists path
+
+This returns C<true> if and only if there is a file, directory
+(or anything) with the given C<path> name.
+
+See also C<is_file>, C<is_dir>, C<stat>.
+
 =head2 get-autosync
 
  get-autosync
@@ -244,6 +257,26 @@ return the default path.
 
 This returns the verbose messages flag.
 
+=head2 is-dir
+
+ is-dir path
+
+This returns C<true> if and only if there is a directory
+with the given C<path> name.  Note that it returns false for
+other objects like files.
+
+See also C<stat>.
+
+=head2 is-file
+
+ is-file path
+
+This returns C<true> if and only if there is a file
+with the given C<path> name.  Note that it returns false for
+other objects like directories.
+
+See also C<stat>.
+
 =head2 kill-subprocess
 
  kill-subprocess
@@ -300,6 +333,23 @@ hidden files are shown.
 This command is mostly useful for interactive sessions.  Programs
 should probably use C<readdir> instead.
 
+=head2 lvcreate
+
+ lvcreate logvol volgroup mbytes
+
+This creates an LVM volume group called C<logvol>
+on the volume group C<volgroup>, with C<size> megabytes.
+
+=head2 lvm-remove-all
+
+ lvm-remove-all
+
+This command removes all LVM logical volumes, volume groups
+and physical volumes.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
 =head2 lvs
 
  lvs
@@ -332,6 +382,14 @@ Create a directory named C<path>.
 Create a directory named C<path>, creating any parent directories
 as necessary.  This is like the C<mkdir -p> shell command.
 
+=head2 mkfs
+
+ mkfs fstype device
+
+This creates a filesystem on C<device> (usually a partition
+of LVM logical volume).  The filesystem type is C<fstype>, for
+example C<ext3>.
+
 =head2 mount
 
  mount device mountpoint
@@ -353,6 +411,23 @@ on the underlying device.
 The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.
 
+=head2 mounts
+
+ mounts
+
+This returns the list of currently mounted filesystems.  It returns
+the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
+
+Some internal mounts are not shown.
+
+=head2 pvcreate
+
+ pvcreate device
+
+This creates an LVM physical volume on the named C<device>,
+where C<device> should usually be a partition name such
+as C</dev/sda1>.
+
 =head2 pvs
 
  pvs
@@ -437,6 +512,33 @@ If C<verbose> is true, this turns on verbose messages (to C<stderr>).
 Verbose messages are disabled unless the environment variable
 C<LIBGUESTFS_DEBUG> is defined and set to C<1>.
 
+=head2 sfdisk
+
+ sfdisk device cyls heads sectors lines,...
+
+This is a direct interface to the L<sfdisk(8)> program for creating
+partitions on block devices.
+
+C<device> should be a block device, for example C</dev/sda>.
+
+C<cyls>, C<heads> and C<sectors> are the number of cylinders, heads
+and sectors on the device, which are passed directly to sfdisk as
+the I<-C>, I<-H> and I<-S> parameters.  If you pass C<0> for any
+of these, then the corresponding parameter is omitted.  Usually for
+'large' disks, you can just pass C<0> for these, but for small
+(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work
+out the right geometry and you will need to tell it.
+
+C<lines> is a list of lines that we feed to C<sfdisk>.  For more
+information refer to the L<sfdisk(8)> manpage.
+
+To create a single partition occupying the whole disk, you would
+pass C<lines> as a single element list, when the single element being
+the string C<,> (comma).
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
 =head2 sync
 
  sync
@@ -455,6 +557,29 @@ Touch acts like the L<touch(1)> command.  It can be used to
 update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.
 
+=head2 umount | unmount
+
+ umount pathordevice
+
+This unmounts the given filesystem.  The filesystem may be
+specified either by its mountpoint (path) or the device which
+contains the filesystem.
+
+=head2 umount-all | unmount-all
+
+ umount-all
+
+This unmounts all mounted filesystems.
+
+Some internal mounts are not unmounted by this call.
+
+=head2 vgcreate
+
+ vgcreate volgroup physvols,...
+
+This creates an LVM volume group called C<volgroup>
+from the non-empty list of physical volumes C<physvols>.
+
 =head2 vgs
 
  vgs
@@ -474,3 +599,19 @@ See also C<vgs_full>.
 List all the volumes groups detected.  This is the equivalent
 of the L<vgs(8)> command.  The "full" version includes all fields.
 
+=head2 write-file
+
+ write-file path content size
+
+This call creates a file called C<path>.  The contents of the
+file is the string C<content> (which can contain any 8 bit data),
+with length C<size>.
+
+As a special case, if C<size> is C<0>
+then the length is calculated using C<strlen> (so in this case
+the content cannot contain embedded ASCII NULs).
+
+Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP.
+
index 069ea8f..310db2e 100644 (file)
@@ -300,6 +300,18 @@ C<value> can be NULL.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_exists
+
+ int guestfs_exists (guestfs_h *handle,
+               const char *path);
+
+This returns C<true> if and only if there is a file, directory
+(or anything) with the given C<path> name.
+
+See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>.
+
+This function returns a C truth value on success or -1 on error.
+
 =head2 guestfs_get_autosync
 
  int guestfs_get_autosync (guestfs_h *handle);
@@ -328,6 +340,32 @@ This returns the verbose messages flag.
 
 This function returns a C truth value on success or -1 on error.
 
+=head2 guestfs_is_dir
+
+ int guestfs_is_dir (guestfs_h *handle,
+               const char *path);
+
+This returns C<true> if and only if there is a directory
+with the given C<path> name.  Note that it returns false for
+other objects like files.
+
+See also C<guestfs_stat>.
+
+This function returns a C truth value on success or -1 on error.
+
+=head2 guestfs_is_file
+
+ int guestfs_is_file (guestfs_h *handle,
+               const char *path);
+
+This returns C<true> if and only if there is a file
+with the given C<path> name.  Note that it returns false for
+other objects like directories.
+
+See also C<guestfs_stat>.
+
+This function returns a C truth value on success or -1 on error.
+
 =head2 guestfs_kill_subprocess
 
  int guestfs_kill_subprocess (guestfs_h *handle);
@@ -405,6 +443,30 @@ This function returns a NULL-terminated array of strings
 (like L<environ(3)>), or NULL if there was an error.
 I<The caller must free the strings and the array after use>.
 
+=head2 guestfs_lvcreate
+
+ int guestfs_lvcreate (guestfs_h *handle,
+               const char *logvol,
+               const char *volgroup,
+               int mbytes);
+
+This creates an LVM volume group called C<logvol>
+on the volume group C<volgroup>, with C<size> megabytes.
+
+This function returns 0 on success or -1 on error.
+
+=head2 guestfs_lvm_remove_all
+
+ int guestfs_lvm_remove_all (guestfs_h *handle);
+
+This command removes all LVM logical volumes, volume groups
+and physical volumes.
+
+This function returns 0 on success or -1 on error.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
 =head2 guestfs_lvs
 
  char **guestfs_lvs (guestfs_h *handle);
@@ -450,6 +512,18 @@ as necessary.  This is like the C<mkdir -p> shell command.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_mkfs
+
+ int guestfs_mkfs (guestfs_h *handle,
+               const char *fstype,
+               const char *device);
+
+This creates a filesystem on C<device> (usually a partition
+of LVM logical volume).  The filesystem type is C<fstype>, for
+example C<ext3>.
+
+This function returns 0 on success or -1 on error.
+
 =head2 guestfs_mount
 
  int guestfs_mount (guestfs_h *handle,
@@ -475,6 +549,30 @@ call, in order to improve reliability.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_mounts
+
+ char **guestfs_mounts (guestfs_h *handle);
+
+This returns the list of currently mounted filesystems.  It returns
+the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
+
+Some internal mounts are not shown.
+
+This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+I<The caller must free the strings and the array after use>.
+
+=head2 guestfs_pvcreate
+
+ int guestfs_pvcreate (guestfs_h *handle,
+               const char *device);
+
+This creates an LVM physical volume on the named C<device>,
+where C<device> should usually be a partition name such
+as C</dev/sda1>.
+
+This function returns 0 on success or -1 on error.
+
 =head2 guestfs_pvs
 
  char **guestfs_pvs (guestfs_h *handle);
@@ -589,6 +687,40 @@ C<LIBGUESTFS_DEBUG> is defined and set to C<1>.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_sfdisk
+
+ int guestfs_sfdisk (guestfs_h *handle,
+               const char *device,
+               int cyls,
+               int heads,
+               int sectors,
+               char * const* const lines);
+
+This is a direct interface to the L<sfdisk(8)> program for creating
+partitions on block devices.
+
+C<device> should be a block device, for example C</dev/sda>.
+
+C<cyls>, C<heads> and C<sectors> are the number of cylinders, heads
+and sectors on the device, which are passed directly to sfdisk as
+the I<-C>, I<-H> and I<-S> parameters.  If you pass C<0> for any
+of these, then the corresponding parameter is omitted.  Usually for
+'large' disks, you can just pass C<0> for these, but for small
+(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work
+out the right geometry and you will need to tell it.
+
+C<lines> is a list of lines that we feed to C<sfdisk>.  For more
+information refer to the L<sfdisk(8)> manpage.
+
+To create a single partition occupying the whole disk, you would
+pass C<lines> as a single element list, when the single element being
+the string C<,> (comma).
+
+This function returns 0 on success or -1 on error.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
 =head2 guestfs_sync
 
  int guestfs_sync (guestfs_h *handle);
@@ -612,6 +744,38 @@ to create a new zero-length file.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_umount
+
+ int guestfs_umount (guestfs_h *handle,
+               const char *pathordevice);
+
+This unmounts the given filesystem.  The filesystem may be
+specified either by its mountpoint (path) or the device which
+contains the filesystem.
+
+This function returns 0 on success or -1 on error.
+
+=head2 guestfs_umount_all
+
+ int guestfs_umount_all (guestfs_h *handle);
+
+This unmounts all mounted filesystems.
+
+Some internal mounts are not unmounted by this call.
+
+This function returns 0 on success or -1 on error.
+
+=head2 guestfs_vgcreate
+
+ int guestfs_vgcreate (guestfs_h *handle,
+               const char *volgroup,
+               char * const* const physvols);
+
+This creates an LVM volume group called C<volgroup>
+from the non-empty list of physical volumes C<physvols>.
+
+This function returns 0 on success or -1 on error.
+
 =head2 guestfs_vgs
 
  char **guestfs_vgs (guestfs_h *handle);
@@ -650,3 +814,24 @@ to complete.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_write_file
+
+ int guestfs_write_file (guestfs_h *handle,
+               const char *path,
+               const char *content,
+               int size);
+
+This call creates a file called C<path>.  The contents of the
+file is the string C<content> (which can contain any 8 bit data),
+with length C<size>.
+
+As a special case, if C<size> is C<0>
+then the length is calculated using C<strlen> (so in this case
+the content cannot contain embedded ASCII NULs).
+
+This function returns 0 on success or -1 on error.
+
+Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP.
+
index 29c95ff..e4dcaef 100644 (file)
@@ -138,6 +138,10 @@ Requires:    perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)
 make INSTALLDIRS=vendor
 
 
+%check
+make check
+
+
 %install
 rm -rf $RPM_BUILD_ROOT
 
index 4579e90..3dbac63 100755 (executable)
@@ -65,11 +65,13 @@ find initramfs -name '*plymouth*' -print0 | xargs -0 rm -rf
 find initramfs -name '*python*' -print0 | xargs -0 rm -rf
 
 # Modules take up nearly half of the image.  It's a rough guess that
-# we don't need any drivers (which take up most of the space).
+# we don't need many drivers (which take up most of the space).
 find initramfs/lib/modules/*/kernel \
   -name '*.ko' \
   -a ! -name 'mii.ko' \
   -a ! -name '8139cp.ko' \
+  -a ! -name 'ext2.ko' \
+  -a ! -name 'ext4.ko' \
   -a -delete
 
 # Pull the kernel out into the current directory.  We don't want it in
index 32a6b82..58f99dc 100644 (file)
@@ -132,3 +132,16 @@ external mkdir : t -> string -> unit = "ocaml_guestfs_mkdir"
 external mkdir_p : t -> string -> unit = "ocaml_guestfs_mkdir_p"
 external chmod : t -> int -> string -> unit = "ocaml_guestfs_chmod"
 external chown : t -> int -> int -> string -> unit = "ocaml_guestfs_chown"
+external exists : t -> string -> bool = "ocaml_guestfs_exists"
+external is_file : t -> string -> bool = "ocaml_guestfs_is_file"
+external is_dir : t -> string -> bool = "ocaml_guestfs_is_dir"
+external pvcreate : t -> string -> unit = "ocaml_guestfs_pvcreate"
+external vgcreate : t -> string -> string array -> unit = "ocaml_guestfs_vgcreate"
+external lvcreate : t -> string -> string -> int -> unit = "ocaml_guestfs_lvcreate"
+external mkfs : t -> string -> string -> unit = "ocaml_guestfs_mkfs"
+external sfdisk : t -> string -> int -> int -> int -> string array -> unit = "ocaml_guestfs_sfdisk_byte" "ocaml_guestfs_sfdisk"
+external write_file : t -> string -> string -> int -> unit = "ocaml_guestfs_write_file"
+external umount : t -> string -> unit = "ocaml_guestfs_umount"
+external mounts : t -> string array = "ocaml_guestfs_mounts"
+external umount_all : t -> unit = "ocaml_guestfs_umount_all"
+external lvm_remove_all : t -> unit = "ocaml_guestfs_lvm_remove_all"
index ecf86f1..1ce1cab 100644 (file)
@@ -235,3 +235,42 @@ val chmod : t -> int -> string -> unit
 val chown : t -> int -> int -> string -> unit
 (** change file owner and group *)
 
+val exists : t -> string -> bool
+(** test if file or directory exists *)
+
+val is_file : t -> string -> bool
+(** test if file exists *)
+
+val is_dir : t -> string -> bool
+(** test if file exists *)
+
+val pvcreate : t -> string -> unit
+(** create an LVM physical volume *)
+
+val vgcreate : t -> string -> string array -> unit
+(** create an LVM volume group *)
+
+val lvcreate : t -> string -> string -> int -> unit
+(** create an LVM volume group *)
+
+val mkfs : t -> string -> string -> unit
+(** make a filesystem *)
+
+val sfdisk : t -> string -> int -> int -> int -> string array -> unit
+(** create partitions on a block device *)
+
+val write_file : t -> string -> string -> int -> unit
+(** Create a file *)
+
+val umount : t -> string -> unit
+(** unmount a filesystem *)
+
+val mounts : t -> string array
+(** show mounted filesystems *)
+
+val umount_all : t -> unit
+(** unmount all filesystems *)
+
+val lvm_remove_all : t -> unit
+(** remove all LVM LVs, VGs and PVs *)
+
index 600440c..291c430 100644 (file)
@@ -119,3 +119,29 @@ ocaml_guestfs_close (value gv)
 
   CAMLreturn (Val_unit);
 }
+
+/* Copy string array value. */
+char **
+ocaml_guestfs_strings_val (value sv)
+{
+  CAMLparam1 (sv);
+  char **r;
+  int i;
+
+  r = malloc (sizeof (char *) * (Wosize_val (sv) + 1));
+  for (i = 0; i < Wosize_val (sv); ++i)
+    r[i] = String_val (Field (sv, i));
+  r[i] = NULL;
+
+  CAMLreturnT (char **, r);
+}
+
+/* Free array of strings. */
+void
+ocaml_guestfs_free_strings (char **argv)
+{
+  /* Don't free the actual strings - they are String_vals on
+   * the OCaml heap.
+   */
+  free (argv);
+}
index 3da41d0..4fb8188 100644 (file)
@@ -22,5 +22,7 @@
 #define Guestfs_val(v) (*((guestfs_h **)Data_custom_val(v)))
 extern void ocaml_guestfs_raise_error (guestfs_h *g, const char *func)
   Noreturn;
+extern char **ocaml_guestfs_strings_val (value sv);
+extern void ocaml_guestfs_free_strings (char **r);
 
 #endif /* GUESTFS_OCAML_C_H */
index 6675cf8..6b1a9f6 100644 (file)
@@ -1347,3 +1347,321 @@ ocaml_guestfs_chown (value gv, value ownerv, value groupv, value pathv)
   CAMLreturn (rv);
 }
 
+CAMLprim value
+ocaml_guestfs_exists (value gv, value pathv)
+{
+  CAMLparam2 (gv, pathv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("exists: used handle after closing it");
+
+  const char *path = String_val (pathv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_exists (g, path);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "exists");
+
+  rv = Val_bool (r);
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_is_file (value gv, value pathv)
+{
+  CAMLparam2 (gv, pathv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("is_file: used handle after closing it");
+
+  const char *path = String_val (pathv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_is_file (g, path);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "is_file");
+
+  rv = Val_bool (r);
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_is_dir (value gv, value pathv)
+{
+  CAMLparam2 (gv, pathv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("is_dir: used handle after closing it");
+
+  const char *path = String_val (pathv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_is_dir (g, path);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "is_dir");
+
+  rv = Val_bool (r);
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_pvcreate (value gv, value devicev)
+{
+  CAMLparam2 (gv, devicev);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("pvcreate: used handle after closing it");
+
+  const char *device = String_val (devicev);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_pvcreate (g, device);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "pvcreate");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_vgcreate (value gv, value volgroupv, value physvolsv)
+{
+  CAMLparam3 (gv, volgroupv, physvolsv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("vgcreate: used handle after closing it");
+
+  const char *volgroup = String_val (volgroupv);
+  char **physvols = ocaml_guestfs_strings_val (physvolsv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_vgcreate (g, volgroup, physvols);
+  caml_leave_blocking_section ();
+  ocaml_guestfs_free_strings (physvols);
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "vgcreate");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_lvcreate (value gv, value logvolv, value volgroupv, value mbytesv)
+{
+  CAMLparam4 (gv, logvolv, volgroupv, mbytesv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("lvcreate: used handle after closing it");
+
+  const char *logvol = String_val (logvolv);
+  const char *volgroup = String_val (volgroupv);
+  int mbytes = Int_val (mbytesv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_lvcreate (g, logvol, volgroup, mbytes);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "lvcreate");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_mkfs (value gv, value fstypev, value devicev)
+{
+  CAMLparam3 (gv, fstypev, devicev);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("mkfs: used handle after closing it");
+
+  const char *fstype = String_val (fstypev);
+  const char *device = String_val (devicev);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_mkfs (g, fstype, device);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "mkfs");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_sfdisk (value gv, value devicev, value cylsv, value headsv, value sectorsv, value linesv)
+{
+  CAMLparam5 (gv, devicev, cylsv, headsv, sectorsv);
+  CAMLxparam1 (linesv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("sfdisk: used handle after closing it");
+
+  const char *device = String_val (devicev);
+  int cyls = Int_val (cylsv);
+  int heads = Int_val (headsv);
+  int sectors = Int_val (sectorsv);
+  char **lines = ocaml_guestfs_strings_val (linesv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_sfdisk (g, device, cyls, heads, sectors, lines);
+  caml_leave_blocking_section ();
+  ocaml_guestfs_free_strings (lines);
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "sfdisk");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_sfdisk_byte (value *argv, int argn)
+{
+  return ocaml_guestfs_sfdisk (argv[0], argv[0], argv[1], argv[2], argv[3], argv[4]);
+}
+
+CAMLprim value
+ocaml_guestfs_write_file (value gv, value pathv, value contentv, value sizev)
+{
+  CAMLparam4 (gv, pathv, contentv, sizev);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("write_file: used handle after closing it");
+
+  const char *path = String_val (pathv);
+  const char *content = String_val (contentv);
+  int size = Int_val (sizev);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_write_file (g, path, content, size);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "write_file");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_umount (value gv, value pathordevicev)
+{
+  CAMLparam2 (gv, pathordevicev);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("umount: used handle after closing it");
+
+  const char *pathordevice = String_val (pathordevicev);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_umount (g, pathordevice);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "umount");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_mounts (value gv)
+{
+  CAMLparam1 (gv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("mounts: used handle after closing it");
+
+  int i;
+  char **r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_mounts (g);
+  caml_leave_blocking_section ();
+  if (r == NULL)
+    ocaml_guestfs_raise_error (g, "mounts");
+
+  rv = caml_copy_string_array ((const char **) r);
+  for (i = 0; r[i] != NULL; ++i) free (r[i]);
+  free (r);
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_umount_all (value gv)
+{
+  CAMLparam1 (gv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("umount_all: used handle after closing it");
+
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_umount_all (g);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "umount_all");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_lvm_remove_all (value gv)
+{
+  CAMLparam1 (gv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("lvm_remove_all: used handle after closing it");
+
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_lvm_remove_all (g);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "lvm_remove_all");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
index d5f1da6..0f72c28 100644 (file)
@@ -73,6 +73,35 @@ error_handler (guestfs_h *g,
   last_error = strdup (msg);
 }
 
+/* http://www.perlmonks.org/?node_id=680842 */
+static char **
+XS_unpack_charPtrPtr (SV *arg) {
+  char **ret;
+  AV *av;
+  I32 i;
+
+  if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV) {
+    croak ("array reference expected");
+  }
+
+  av = (AV *)SvRV (arg);
+  ret = (char **)malloc (av_len (av) + 1 + 1);
+
+  for (i = 0; i <= av_len (av); i++) {
+    SV **elem = av_fetch (av, i, 0);
+
+      if (!elem || !*elem) {
+        croak ("missing element in list");
+      }
+
+      ret[i] = SvPV_nolen (*elem);
+  }
+
+  ret[i + 1] = NULL;
+
+  return ret;
+}
+
 MODULE = Sys::Guestfs  PACKAGE = Sys::Guestfs
 
 guestfs_h *
@@ -95,38 +124,43 @@ void
 launch (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_launch (g) == -1)
+      if (guestfs_launch (g) == -1) {
         croak ("launch: %s", last_error);
+      }
 
 void
 wait_ready (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_wait_ready (g) == -1)
+      if (guestfs_wait_ready (g) == -1) {
         croak ("wait_ready: %s", last_error);
+      }
 
 void
 kill_subprocess (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_kill_subprocess (g) == -1)
+      if (guestfs_kill_subprocess (g) == -1) {
         croak ("kill_subprocess: %s", last_error);
+      }
 
 void
 add_drive (g, filename)
       guestfs_h *g;
       char *filename;
  PPCODE:
-      if (guestfs_add_drive (g, filename) == -1)
+      if (guestfs_add_drive (g, filename) == -1) {
         croak ("add_drive: %s", last_error);
+      }
 
 void
 add_cdrom (g, filename)
       guestfs_h *g;
       char *filename;
  PPCODE:
-      if (guestfs_add_cdrom (g, filename) == -1)
+      if (guestfs_add_cdrom (g, filename) == -1) {
         croak ("add_cdrom: %s", last_error);
+      }
 
 void
 config (g, qemuparam, qemuvalue)
@@ -134,16 +168,18 @@ config (g, qemuparam, qemuvalue)
       char *qemuparam;
       char *qemuvalue;
  PPCODE:
-      if (guestfs_config (g, qemuparam, qemuvalue) == -1)
+      if (guestfs_config (g, qemuparam, qemuvalue) == -1) {
         croak ("config: %s", last_error);
+      }
 
 void
 set_path (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_set_path (g, path) == -1)
+      if (guestfs_set_path (g, path) == -1) {
         croak ("set_path: %s", last_error);
+      }
 
 SV *
 get_path (g)
@@ -152,8 +188,9 @@ PREINIT:
       const char *path;
    CODE:
       path = guestfs_get_path (g);
-      if (path == NULL)
+      if (path == NULL) {
         croak ("get_path: %s", last_error);
+      }
       RETVAL = newSVpv (path, 0);
  OUTPUT:
       RETVAL
@@ -163,8 +200,9 @@ set_autosync (g, autosync)
       guestfs_h *g;
       int autosync;
  PPCODE:
-      if (guestfs_set_autosync (g, autosync) == -1)
+      if (guestfs_set_autosync (g, autosync) == -1) {
         croak ("set_autosync: %s", last_error);
+      }
 
 SV *
 get_autosync (g)
@@ -173,8 +211,9 @@ PREINIT:
       int autosync;
    CODE:
       autosync = guestfs_get_autosync (g);
-      if (autosync == -1)
+      if (autosync == -1) {
         croak ("get_autosync: %s", last_error);
+      }
       RETVAL = newSViv (autosync);
  OUTPUT:
       RETVAL
@@ -184,8 +223,9 @@ set_verbose (g, verbose)
       guestfs_h *g;
       int verbose;
  PPCODE:
-      if (guestfs_set_verbose (g, verbose) == -1)
+      if (guestfs_set_verbose (g, verbose) == -1) {
         croak ("set_verbose: %s", last_error);
+      }
 
 SV *
 get_verbose (g)
@@ -194,8 +234,9 @@ PREINIT:
       int verbose;
    CODE:
       verbose = guestfs_get_verbose (g);
-      if (verbose == -1)
+      if (verbose == -1) {
         croak ("get_verbose: %s", last_error);
+      }
       RETVAL = newSViv (verbose);
  OUTPUT:
       RETVAL
@@ -206,23 +247,26 @@ mount (g, device, mountpoint)
       char *device;
       char *mountpoint;
  PPCODE:
-      if (guestfs_mount (g, device, mountpoint) == -1)
+      if (guestfs_mount (g, device, mountpoint) == -1) {
         croak ("mount: %s", last_error);
+      }
 
 void
 sync (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_sync (g) == -1)
+      if (guestfs_sync (g) == -1) {
         croak ("sync: %s", last_error);
+      }
 
 void
 touch (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_touch (g, path) == -1)
+      if (guestfs_touch (g, path) == -1) {
         croak ("touch: %s", last_error);
+      }
 
 SV *
 cat (g, path)
@@ -232,8 +276,9 @@ PREINIT:
       char *content;
    CODE:
       content = guestfs_cat (g, path);
-      if (content == NULL)
+      if (content == NULL) {
         croak ("cat: %s", last_error);
+      }
       RETVAL = newSVpv (content, 0);
       free (content);
  OUTPUT:
@@ -247,8 +292,9 @@ PREINIT:
       char *listing;
    CODE:
       listing = guestfs_ll (g, directory);
-      if (listing == NULL)
+      if (listing == NULL) {
         croak ("ll: %s", last_error);
+      }
       RETVAL = newSVpv (listing, 0);
       free (listing);
  OUTPUT:
@@ -263,8 +309,9 @@ PREINIT:
       int i, n;
  PPCODE:
       listing = guestfs_ls (g, directory);
-      if (listing == NULL)
+      if (listing == NULL) {
         croak ("ls: %s", last_error);
+      }
       for (n = 0; listing[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -281,8 +328,9 @@ PREINIT:
       int i, n;
  PPCODE:
       devices = guestfs_list_devices (g);
-      if (devices == NULL)
+      if (devices == NULL) {
         croak ("list_devices: %s", last_error);
+      }
       for (n = 0; devices[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -299,8 +347,9 @@ PREINIT:
       int i, n;
  PPCODE:
       partitions = guestfs_list_partitions (g);
-      if (partitions == NULL)
+      if (partitions == NULL) {
         croak ("list_partitions: %s", last_error);
+      }
       for (n = 0; partitions[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -317,8 +366,9 @@ PREINIT:
       int i, n;
  PPCODE:
       physvols = guestfs_pvs (g);
-      if (physvols == NULL)
+      if (physvols == NULL) {
         croak ("pvs: %s", last_error);
+      }
       for (n = 0; physvols[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -335,8 +385,9 @@ PREINIT:
       int i, n;
  PPCODE:
       volgroups = guestfs_vgs (g);
-      if (volgroups == NULL)
+      if (volgroups == NULL) {
         croak ("vgs: %s", last_error);
+      }
       for (n = 0; volgroups[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -353,8 +404,9 @@ PREINIT:
       int i, n;
  PPCODE:
       logvols = guestfs_lvs (g);
-      if (logvols == NULL)
+      if (logvols == NULL) {
         croak ("lvs: %s", last_error);
+      }
       for (n = 0; logvols[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -475,8 +527,9 @@ PREINIT:
       int i, n;
  PPCODE:
       lines = guestfs_read_lines (g, path);
-      if (lines == NULL)
+      if (lines == NULL) {
         croak ("read_lines: %s", last_error);
+      }
       for (n = 0; lines[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -491,15 +544,17 @@ aug_init (g, root, flags)
       char *root;
       int flags;
  PPCODE:
-      if (guestfs_aug_init (g, root, flags) == -1)
+      if (guestfs_aug_init (g, root, flags) == -1) {
         croak ("aug_init: %s", last_error);
+      }
 
 void
 aug_close (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_aug_close (g) == -1)
+      if (guestfs_aug_close (g) == -1) {
         croak ("aug_close: %s", last_error);
+      }
 
 SV *
 aug_defvar (g, name, expr)
@@ -510,8 +565,9 @@ PREINIT:
       int nrnodes;
    CODE:
       nrnodes = guestfs_aug_defvar (g, name, expr);
-      if (nrnodes == -1)
+      if (nrnodes == -1) {
         croak ("aug_defvar: %s", last_error);
+      }
       RETVAL = newSViv (nrnodes);
  OUTPUT:
       RETVAL
@@ -526,8 +582,9 @@ PREINIT:
       struct guestfs_int_bool *r;
  PPCODE:
       r = guestfs_aug_defnode (g, name, expr, val);
-      if (r == NULL)
+      if (r == NULL) {
         croak ("aug_defnode: %s", last_error);
+      }
       EXTEND (SP, 2);
       PUSHs (sv_2mortal (newSViv (r->i)));
       PUSHs (sv_2mortal (newSViv (r->b)));
@@ -541,8 +598,9 @@ PREINIT:
       char *val;
    CODE:
       val = guestfs_aug_get (g, path);
-      if (val == NULL)
+      if (val == NULL) {
         croak ("aug_get: %s", last_error);
+      }
       RETVAL = newSVpv (val, 0);
       free (val);
  OUTPUT:
@@ -554,8 +612,9 @@ aug_set (g, path, val)
       char *path;
       char *val;
  PPCODE:
-      if (guestfs_aug_set (g, path, val) == -1)
+      if (guestfs_aug_set (g, path, val) == -1) {
         croak ("aug_set: %s", last_error);
+      }
 
 void
 aug_insert (g, path, label, before)
@@ -564,8 +623,9 @@ aug_insert (g, path, label, before)
       char *label;
       int before;
  PPCODE:
-      if (guestfs_aug_insert (g, path, label, before) == -1)
+      if (guestfs_aug_insert (g, path, label, before) == -1) {
         croak ("aug_insert: %s", last_error);
+      }
 
 SV *
 aug_rm (g, path)
@@ -575,8 +635,9 @@ PREINIT:
       int nrnodes;
    CODE:
       nrnodes = guestfs_aug_rm (g, path);
-      if (nrnodes == -1)
+      if (nrnodes == -1) {
         croak ("aug_rm: %s", last_error);
+      }
       RETVAL = newSViv (nrnodes);
  OUTPUT:
       RETVAL
@@ -587,8 +648,9 @@ aug_mv (g, src, dest)
       char *src;
       char *dest;
  PPCODE:
-      if (guestfs_aug_mv (g, src, dest) == -1)
+      if (guestfs_aug_mv (g, src, dest) == -1) {
         croak ("aug_mv: %s", last_error);
+      }
 
 void
 aug_match (g, path)
@@ -599,8 +661,9 @@ PREINIT:
       int i, n;
  PPCODE:
       matches = guestfs_aug_match (g, path);
-      if (matches == NULL)
+      if (matches == NULL) {
         croak ("aug_match: %s", last_error);
+      }
       for (n = 0; matches[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -613,15 +676,17 @@ void
 aug_save (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_aug_save (g) == -1)
+      if (guestfs_aug_save (g) == -1) {
         croak ("aug_save: %s", last_error);
+      }
 
 void
 aug_load (g)
       guestfs_h *g;
  PPCODE:
-      if (guestfs_aug_load (g) == -1)
+      if (guestfs_aug_load (g) == -1) {
         croak ("aug_load: %s", last_error);
+      }
 
 void
 aug_ls (g, path)
@@ -632,8 +697,9 @@ PREINIT:
       int i, n;
  PPCODE:
       matches = guestfs_aug_ls (g, path);
-      if (matches == NULL)
+      if (matches == NULL) {
         croak ("aug_ls: %s", last_error);
+      }
       for (n = 0; matches[n] != NULL; ++n) /**/;
       EXTEND (SP, n);
       for (i = 0; i < n; ++i) {
@@ -647,40 +713,45 @@ rm (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_rm (g, path) == -1)
+      if (guestfs_rm (g, path) == -1) {
         croak ("rm: %s", last_error);
+      }
 
 void
 rmdir (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_rmdir (g, path) == -1)
+      if (guestfs_rmdir (g, path) == -1) {
         croak ("rmdir: %s", last_error);
+      }
 
 void
 rm_rf (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_rm_rf (g, path) == -1)
+      if (guestfs_rm_rf (g, path) == -1) {
         croak ("rm_rf: %s", last_error);
+      }
 
 void
 mkdir (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_mkdir (g, path) == -1)
+      if (guestfs_mkdir (g, path) == -1) {
         croak ("mkdir: %s", last_error);
+      }
 
 void
 mkdir_p (g, path)
       guestfs_h *g;
       char *path;
  PPCODE:
-      if (guestfs_mkdir_p (g, path) == -1)
+      if (guestfs_mkdir_p (g, path) == -1) {
         croak ("mkdir_p: %s", last_error);
+      }
 
 void
 chmod (g, mode, path)
@@ -688,8 +759,9 @@ chmod (g, mode, path)
       int mode;
       char *path;
  PPCODE:
-      if (guestfs_chmod (g, mode, path) == -1)
+      if (guestfs_chmod (g, mode, path) == -1) {
         croak ("chmod: %s", last_error);
+      }
 
 void
 chown (g, owner, group, path)
@@ -698,6 +770,164 @@ chown (g, owner, group, path)
       int group;
       char *path;
  PPCODE:
-      if (guestfs_chown (g, owner, group, path) == -1)
+      if (guestfs_chown (g, owner, group, path) == -1) {
         croak ("chown: %s", last_error);
+      }
+
+SV *
+exists (g, path)
+      guestfs_h *g;
+      char *path;
+PREINIT:
+      int existsflag;
+   CODE:
+      existsflag = guestfs_exists (g, path);
+      if (existsflag == -1) {
+        croak ("exists: %s", last_error);
+      }
+      RETVAL = newSViv (existsflag);
+ OUTPUT:
+      RETVAL
+
+SV *
+is_file (g, path)
+      guestfs_h *g;
+      char *path;
+PREINIT:
+      int fileflag;
+   CODE:
+      fileflag = guestfs_is_file (g, path);
+      if (fileflag == -1) {
+        croak ("is_file: %s", last_error);
+      }
+      RETVAL = newSViv (fileflag);
+ OUTPUT:
+      RETVAL
+
+SV *
+is_dir (g, path)
+      guestfs_h *g;
+      char *path;
+PREINIT:
+      int dirflag;
+   CODE:
+      dirflag = guestfs_is_dir (g, path);
+      if (dirflag == -1) {
+        croak ("is_dir: %s", last_error);
+      }
+      RETVAL = newSViv (dirflag);
+ OUTPUT:
+      RETVAL
+
+void
+pvcreate (g, device)
+      guestfs_h *g;
+      char *device;
+ PPCODE:
+      if (guestfs_pvcreate (g, device) == -1) {
+        croak ("pvcreate: %s", last_error);
+      }
+
+void
+vgcreate (g, volgroup, physvols)
+      guestfs_h *g;
+      char *volgroup;
+      char **physvols;
+ PPCODE:
+      if (guestfs_vgcreate (g, volgroup, physvols) == -1) {
+        free (physvols);
+        croak ("vgcreate: %s", last_error);
+      }
+        free (physvols);
+
+void
+lvcreate (g, logvol, volgroup, mbytes)
+      guestfs_h *g;
+      char *logvol;
+      char *volgroup;
+      int mbytes;
+ PPCODE:
+      if (guestfs_lvcreate (g, logvol, volgroup, mbytes) == -1) {
+        croak ("lvcreate: %s", last_error);
+      }
+
+void
+mkfs (g, fstype, device)
+      guestfs_h *g;
+      char *fstype;
+      char *device;
+ PPCODE:
+      if (guestfs_mkfs (g, fstype, device) == -1) {
+        croak ("mkfs: %s", last_error);
+      }
+
+void
+sfdisk (g, device, cyls, heads, sectors, lines)
+      guestfs_h *g;
+      char *device;
+      int cyls;
+      int heads;
+      int sectors;
+      char **lines;
+ PPCODE:
+      if (guestfs_sfdisk (g, device, cyls, heads, sectors, lines) == -1) {
+        free (lines);
+        croak ("sfdisk: %s", last_error);
+      }
+        free (lines);
+
+void
+write_file (g, path, content, size)
+      guestfs_h *g;
+      char *path;
+      char *content;
+      int size;
+ PPCODE:
+      if (guestfs_write_file (g, path, content, size) == -1) {
+        croak ("write_file: %s", last_error);
+      }
+
+void
+umount (g, pathordevice)
+      guestfs_h *g;
+      char *pathordevice;
+ PPCODE:
+      if (guestfs_umount (g, pathordevice) == -1) {
+        croak ("umount: %s", last_error);
+      }
+
+void
+mounts (g)
+      guestfs_h *g;
+PREINIT:
+      char **devices;
+      int i, n;
+ PPCODE:
+      devices = guestfs_mounts (g);
+      if (devices == NULL) {
+        croak ("mounts: %s", last_error);
+      }
+      for (n = 0; devices[n] != NULL; ++n) /**/;
+      EXTEND (SP, n);
+      for (i = 0; i < n; ++i) {
+        PUSHs (sv_2mortal (newSVpv (devices[i], 0)));
+        free (devices[i]);
+      }
+      free (devices);
+
+void
+umount_all (g)
+      guestfs_h *g;
+ PPCODE:
+      if (guestfs_umount_all (g) == -1) {
+        croak ("umount_all: %s", last_error);
+      }
+
+void
+lvm_remove_all (g)
+      guestfs_h *g;
+ PPCODE:
+      if (guestfs_lvm_remove_all (g) == -1) {
+        croak ("lvm_remove_all: %s", last_error);
+      }
 
index 50f5d75..c708a29 100644 (file)
@@ -91,13 +91,13 @@ sub new {
   return $self;
 }
 
-=item $h->add_cdrom (filename);
+=item $h->add_cdrom ($filename);
 
 This function adds a virtual CD-ROM disk image to the guest.
 
 This is equivalent to the qemu parameter C<-cdrom filename>.
 
-=item $h->add_drive (filename);
+=item $h->add_drive ($filename);
 
 This function adds a virtual machine disk image C<filename> to the
 guest.  The first time you call this function, the disk appears as IDE
@@ -119,7 +119,7 @@ used by it.  After calling this, you have to call
 C<$h-E<gt>aug_init> again before you can use any other
 Augeas functions.
 
-=item ($nrnodes, $created) = $h->aug_defnode (name, expr, val);
+=item ($nrnodes, $created) = $h->aug_defnode ($name, $expr, $val);
 
 Defines a variable C<name> whose value is the result of
 evaluating C<expr>.
@@ -132,7 +132,7 @@ On success this returns a pair containing the
 number of nodes in the nodeset, and a boolean flag
 if a node was created.
 
-=item $nrnodes = $h->aug_defvar (name, expr);
+=item $nrnodes = $h->aug_defvar ($name, $expr);
 
 Defines an Augeas variable C<name> whose value is the result
 of evaluating C<expr>.  If C<expr> is NULL, then C<name> is
@@ -141,12 +141,12 @@ undefined.
 On success this returns the number of nodes in C<expr>, or
 C<0> if C<expr> evaluates to something which is not a nodeset.
 
-=item $val = $h->aug_get (path);
+=item $val = $h->aug_get ($path);
 
 Look up the value associated with C<path>.  If C<path>
 matches exactly one node, the C<value> is returned.
 
-=item $h->aug_init (root, flags);
+=item $h->aug_init ($root, $flags);
 
 Create a new Augeas handle for editing configuration files.
 If there was any previous Augeas handle associated with this
@@ -195,7 +195,7 @@ To close the handle, you can call C<$h-E<gt>aug_close>.
 
 To find out more about Augeas, see L<http://augeas.net/>.
 
-=item $h->aug_insert (path, label, before);
+=item $h->aug_insert ($path, $label, $before);
 
 Create a new sibling C<label> for C<path>, inserting it into
 the tree before or after C<path> (depending on the boolean
@@ -212,23 +212,23 @@ Load files into the tree.
 See C<aug_load> in the Augeas documentation for the full gory
 details.
 
-=item @matches = $h->aug_ls (path);
+=item @matches = $h->aug_ls ($path);
 
 This is just a shortcut for listing C<$h-E<gt>aug_match>
 C<path/*> and sorting the resulting nodes into alphabetical order.
 
-=item @matches = $h->aug_match (path);
+=item @matches = $h->aug_match ($path);
 
 Returns a list of paths which match the path expression C<path>.
 The returned paths are sufficiently qualified so that they match
 exactly one node in the current tree.
 
-=item $h->aug_mv (src, dest);
+=item $h->aug_mv ($src, $dest);
 
 Move the node C<src> to C<dest>.  C<src> must match exactly
 one node.  C<dest> is overwritten if it exists.
 
-=item $nrnodes = $h->aug_rm (path);
+=item $nrnodes = $h->aug_rm ($path);
 
 Remove C<path> and all of its children.
 
@@ -241,11 +241,11 @@ This writes all pending changes to disk.
 The flags which were passed to C<$h-E<gt>aug_init> affect exactly
 how files are saved.
 
-=item $h->aug_set (path, val);
+=item $h->aug_set ($path, $val);
 
 Set the value associated with C<path> to C<value>.
 
-=item $content = $h->cat (path);
+=item $content = $h->cat ($path);
 
 Return the contents of the file named C<path>.
 
@@ -258,12 +258,12 @@ Because of the message protocol, there is a transfer limit
 of somewhere between 2MB and 4MB.  To transfer large files you should use
 FTP.
 
-=item $h->chmod (mode, path);
+=item $h->chmod ($mode, $path);
 
 Change the mode (permissions) of C<path> to C<mode>.  Only
 numeric modes are supported.
 
-=item $h->chown (owner, group, path);
+=item $h->chown ($owner, $group, $path);
 
 Change the file owner to C<owner> and group to C<group>.
 
@@ -271,7 +271,7 @@ Only numeric uid and gid are supported.  If you want to use
 names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).
 
-=item $h->config (qemuparam, qemuvalue);
+=item $h->config ($qemuparam, $qemuvalue);
 
 This can be used to add arbitrary qemu command line parameters
 of the form C<-param value>.  Actually it's not quite arbitrary - we
@@ -282,6 +282,13 @@ The first character of C<param> string must be a C<-> (dash).
 
 C<value> can be NULL.
 
+=item $existsflag = $h->exists ($path);
+
+This returns C<true> if and only if there is a file, directory
+(or anything) with the given C<path> name.
+
+See also C<$h-E<gt>is_file>, C<$h-E<gt>is_dir>, C<$h-E<gt>stat>.
+
 =item $autosync = $h->get_autosync ();
 
 Get the autosync flag.
@@ -297,6 +304,22 @@ return the default path.
 
 This returns the verbose messages flag.
 
+=item $dirflag = $h->is_dir ($path);
+
+This returns C<true> if and only if there is a directory
+with the given C<path> name.  Note that it returns false for
+other objects like files.
+
+See also C<$h-E<gt>stat>.
+
+=item $fileflag = $h->is_file ($path);
+
+This returns C<true> if and only if there is a file
+with the given C<path> name.  Note that it returns false for
+other objects like directories.
+
+See also C<$h-E<gt>stat>.
+
 =item $h->kill_subprocess ();
 
 This kills the qemu subprocess.  You should never need to call this.
@@ -324,7 +347,7 @@ The full partition device names are returned, eg. C</dev/sda1>
 This does not return logical volumes.  For that you will need to
 call C<$h-E<gt>lvs>.
 
-=item $listing = $h->ll (directory);
+=item $listing = $h->ll ($directory);
 
 List the files in C<directory> (relative to the root directory,
 there is no cwd) in the format of 'ls -la'.
@@ -332,7 +355,7 @@ there is no cwd) in the format of 'ls -la'.
 This command is mostly useful for interactive sessions.  It
 is I<not> intended that you try to parse the output string.
 
-=item @listing = $h->ls (directory);
+=item @listing = $h->ls ($directory);
 
 List the files in C<directory> (relative to the root directory,
 there is no cwd).  The '.' and '..' entries are not returned, but
@@ -341,6 +364,19 @@ hidden files are shown.
 This command is mostly useful for interactive sessions.  Programs
 should probably use C<$h-E<gt>readdir> instead.
 
+=item $h->lvcreate ($logvol, $volgroup, $mbytes);
+
+This creates an LVM volume group called C<logvol>
+on the volume group C<volgroup>, with C<size> megabytes.
+
+=item $h->lvm_remove_all ();
+
+This command removes all LVM logical volumes, volume groups
+and physical volumes.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
 =item @logvols = $h->lvs ();
 
 List all the logical volumes detected.  This is the equivalent
@@ -356,16 +392,22 @@ See also C<$h-E<gt>lvs_full>.
 List all the logical volumes detected.  This is the equivalent
 of the L<lvs(8)> command.  The "full" version includes all fields.
 
-=item $h->mkdir (path);
+=item $h->mkdir ($path);
 
 Create a directory named C<path>.
 
-=item $h->mkdir_p (path);
+=item $h->mkdir_p ($path);
 
 Create a directory named C<path>, creating any parent directories
 as necessary.  This is like the C<mkdir -p> shell command.
 
-=item $h->mount (device, mountpoint);
+=item $h->mkfs ($fstype, $device);
+
+This creates a filesystem on C<device> (usually a partition
+of LVM logical volume).  The filesystem type is C<fstype>, for
+example C<ext3>.
+
+=item $h->mount ($device, $mountpoint);
 
 Mount a guest disk at a position in the filesystem.  Block devices
 are named C</dev/sda>, C</dev/sdb> and so on, as they were added to
@@ -384,6 +426,19 @@ on the underlying device.
 The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.
 
+=item @devices = $h->mounts ();
+
+This returns the list of currently mounted filesystems.  It returns
+the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
+
+Some internal mounts are not shown.
+
+=item $h->pvcreate ($device);
+
+This creates an LVM physical volume on the named C<device>,
+where C<device> should usually be a partition name such
+as C</dev/sda1>.
+
 =item @physvols = $h->pvs ();
 
 List all the physical volumes detected.  This is the equivalent
@@ -399,7 +454,7 @@ See also C<$h-E<gt>pvs_full>.
 List all the physical volumes detected.  This is the equivalent
 of the L<pvs(8)> command.  The "full" version includes all fields.
 
-=item @lines = $h->read_lines (path);
+=item @lines = $h->read_lines ($path);
 
 Return the contents of the file named C<path>.
 
@@ -411,27 +466,27 @@ Note that this function cannot correctly handle binary files
 as end of line).  For those you need to use the C<$h-E<gt>read_file>
 function which has a more complex interface.
 
-=item $h->rm (path);
+=item $h->rm ($path);
 
 Remove the single file C<path>.
 
-=item $h->rm_rf (path);
+=item $h->rm_rf ($path);
 
 Remove the file or directory C<path>, recursively removing the
 contents if its a directory.  This is like the C<rm -rf> shell
 command.
 
-=item $h->rmdir (path);
+=item $h->rmdir ($path);
 
 Remove the single directory C<path>.
 
-=item $h->set_autosync (autosync);
+=item $h->set_autosync ($autosync);
 
 If C<autosync> is true, this enables autosync.  Libguestfs will make a
 best effort attempt to run C<$h-E<gt>sync> when the handle is closed
 (also if the program exits without closing handles).
 
-=item $h->set_path (path);
+=item $h->set_path ($path);
 
 Set the path that libguestfs searches for kernel and initrd.img.
 
@@ -443,13 +498,38 @@ must make sure it remains valid for the lifetime of the handle.
 
 Setting C<path> to C<NULL> restores the default path.
 
-=item $h->set_verbose (verbose);
+=item $h->set_verbose ($verbose);
 
 If C<verbose> is true, this turns on verbose messages (to C<stderr>).
 
 Verbose messages are disabled unless the environment variable
 C<LIBGUESTFS_DEBUG> is defined and set to C<1>.
 
+=item $h->sfdisk ($device, $cyls, $heads, $sectors, \@lines);
+
+This is a direct interface to the L<sfdisk(8)> program for creating
+partitions on block devices.
+
+C<device> should be a block device, for example C</dev/sda>.
+
+C<cyls>, C<heads> and C<sectors> are the number of cylinders, heads
+and sectors on the device, which are passed directly to sfdisk as
+the I<-C>, I<-H> and I<-S> parameters.  If you pass C<0> for any
+of these, then the corresponding parameter is omitted.  Usually for
+'large' disks, you can just pass C<0> for these, but for small
+(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work
+out the right geometry and you will need to tell it.
+
+C<lines> is a list of lines that we feed to C<sfdisk>.  For more
+information refer to the L<sfdisk(8)> manpage.
+
+To create a single partition occupying the whole disk, you would
+pass C<lines> as a single element list, when the single element being
+the string C<,> (comma).
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
 =item $h->sync ();
 
 This syncs the disk, so that any writes are flushed through to the
@@ -458,12 +538,29 @@ underlying disk image.
 You should always call this if you have modified a disk image, before
 closing the handle.
 
-=item $h->touch (path);
+=item $h->touch ($path);
 
 Touch acts like the L<touch(1)> command.  It can be used to
 update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.
 
+=item $h->umount ($pathordevice);
+
+This unmounts the given filesystem.  The filesystem may be
+specified either by its mountpoint (path) or the device which
+contains the filesystem.
+
+=item $h->umount_all ();
+
+This unmounts all mounted filesystems.
+
+Some internal mounts are not unmounted by this call.
+
+=item $h->vgcreate ($volgroup, \@physvols);
+
+This creates an LVM volume group called C<volgroup>
+from the non-empty list of physical volumes C<physvols>.
+
 =item @volgroups = $h->vgs ();
 
 List all the volumes groups detected.  This is the equivalent
@@ -487,6 +584,20 @@ using L<qemu(1)>.
 You should call this after C<$h-E<gt>launch> to wait for the launch
 to complete.
 
+=item $h->write_file ($path, $content, $size);
+
+This call creates a file called C<path>.  The contents of the
+file is the string C<content> (which can contain any 8 bit data),
+with length C<size>.
+
+As a special case, if C<size> is C<0>
+then the length is calculated using C<strlen> (so in this case
+the content cannot contain embedded ASCII NULs).
+
+Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP.
+
 =cut
 
 1;
index 06a638e..6792e07 100755 (executable)
@@ -77,59 +77,74 @@ and args = argt list        (* Function parameters, guestfs handle is implicit. *)
 and argt =
   | String of string   (* const char *name, cannot be NULL *)
   | OptString of string        (* const char *name, may be NULL *)
+  | StringList of string(* list of strings (each string cannot be NULL) *)
   | Bool of string     (* boolean *)
   | Int of string      (* int (smallish ints, signed, <= 31 bits) *)
 
 type flags =
   | ProtocolLimitWarning  (* display warning about protocol size limits *)
+  | DangerWillRobinson   (* flags particularly dangerous commands *)
   | FishAlias of string          (* provide an alias for this cmd in guestfish *)
   | FishAction of string  (* call this function in guestfish *)
   | NotInFish            (* do not export via guestfish *)
 
+let protocol_limit_warning =
+  "Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP."
+
+let danger_will_robinson =
+  "B<This command is dangerous.  Without careful use you
+can easily destroy all your data>."
+
 (* You can supply zero or as many tests as you want per API call.
  *
- * Note that the test environment has 3 block devices, of size 10M, 20M
- * and 30M (respectively /dev/sda, /dev/sdb, /dev/sdc).  To run the
- * tests in a reasonable amount of time, the virtual machine and
- * block devices are reused between tests. So don't try testing
- * kill_subprocess :-x
+ * Note that the test environment has 3 block devices, of size 500MB,
+ * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc).
+ * Note for partitioning purposes, the 500MB device has 63 cylinders.
+ *
+ * To be able to run the tests in a reasonable amount of time,
+ * the virtual machine and block devices are reused between tests.
+ * So don't try testing kill_subprocess :-x
+ *
+ * Between each test we umount-all and lvm-remove-all.
  *
  * Don't assume anything about the previous contents of the block
  * devices.  Use 'Init*' to create some initial scenarios.
  *)
-type tests = test list
+type tests = (test_init * test) list
 and test =
     (* Run the command sequence and just expect nothing to fail. *)
-  | TestRun of test_init * seq
+  | TestRun of seq
     (* Run the command sequence and expect the output of the final
      * command to be the string.
      *)
-  | TestOutput of test_init * seq * string
+  | TestOutput of seq * string
     (* Run the command sequence and expect the output of the final
      * command to be the list of strings.
      *)
-  | TestOutputList of test_init * seq * string list
+  | TestOutputList of seq * string list
     (* Run the command sequence and expect the output of the final
      * command to be the integer.
      *)
-  | TestOutputInt of test_init * seq * int
+  | TestOutputInt of seq * int
     (* Run the command sequence and expect the output of the final
      * command to be a true value (!= 0 or != NULL).
      *)
-  | TestOutputTrue of test_init * seq
+  | TestOutputTrue of seq
     (* Run the command sequence and expect the output of the final
      * command to be a false value (== 0 or == NULL, but not an error).
      *)
-  | TestOutputFalse of test_init * seq
+  | TestOutputFalse of seq
     (* Run the command sequence and expect the output of the final
      * command to be a list of the given length (but don't care about
      * content).
      *)
-  | TestOutputLength of test_init * seq * int
+  | TestOutputLength of seq * int
     (* Run the command sequence and expect the final command (only)
      * to fail.
      *)
-  | TestLastFail of test_init * seq
+  | TestLastFail of seq
 
 (* Some initial scenarios for testing. *)
 and test_init =
@@ -143,7 +158,7 @@ and test_init =
   | InitEmpty
     (* /dev/sda:
      *   /dev/sda1 (is a PV):
-     *     /dev/VG/LV:
+     *     /dev/VG/LV (size 8MB):
      *       formatted as ext2, empty [except for lost+found], mounted on /
      * /dev/sdb and /dev/sdc may have random content.
      *)
@@ -281,9 +296,8 @@ This returns the verbose messages flag.")
 
 let daemon_functions = [
   ("mount", (RErr, [String "device"; String "mountpoint"]), 1, [],
-   [TestOutput (
-      InitNone,
-      [["sfdisk"];
+   [InitNone, TestOutput (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -308,7 +322,7 @@ The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.");
 
   ("sync", (RErr, []), 2, [],
-   [ TestRun (InitNone, [["sync"]])],
+   [ InitNone, TestRun [["sync"]]],
    "sync disks, writes are flushed through to the disk image",
    "\
 This syncs the disk, so that any writes are flushed through to the
@@ -318,8 +332,7 @@ You should always call this if you have modified a disk image, before
 closing the handle.");
 
   ("touch", (RErr, [String "path"]), 3, [],
-   [TestOutputTrue (
-      InitEmpty,
+   [InitEmpty, TestOutputTrue (
       [["touch"; "/new"];
        ["exists"; "/new"]])],
    "update file timestamps or create a new file",
@@ -329,8 +342,7 @@ update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.");
 
   ("cat", (RString "content", [String "path"]), 4, [ProtocolLimitWarning],
-   [TestOutput (
-      InitEmpty,
+   [InitEmpty, TestOutput (
       [["write_file"; "/new"; "new file contents"; "0"];
        ["cat"; "/new"]], "new file contents")],
    "list the contents of a file",
@@ -355,8 +367,7 @@ This command is mostly useful for interactive sessions.  It
 is I<not> intended that you try to parse the output string.");
 
   ("ls", (RStringList "listing", [String "directory"]), 6, [],
-   [TestOutputList (
-      InitEmpty,
+   [InitEmpty, TestOutputList (
       [["touch"; "/new"];
        ["touch"; "/newer"];
        ["touch"; "/newest"];
@@ -371,8 +382,7 @@ This command is mostly useful for interactive sessions.  Programs
 should probably use C<guestfs_readdir> instead.");
 
   ("list_devices", (RStringList "devices", []), 7, [],
-   [TestOutputList (
-      InitNone,
+   [InitNone, TestOutputList (
       [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])],
    "list the block devices",
    "\
@@ -381,12 +391,10 @@ List all the block devices.
 The full block device names are returned, eg. C</dev/sda>");
 
   ("list_partitions", (RStringList "partitions", []), 8, [],
-   [TestOutputList (
-      InitEmpty,
+   [InitEmpty, TestOutputList (
       [["list_partitions"]], ["/dev/sda1"]);
-    TestOutputList (
-      InitEmpty,
-      [["sfdisk"];
+    InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
        ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
    "list the partitions",
    "\
@@ -398,12 +406,10 @@ This does not return logical volumes.  For that you will need to
 call C<guestfs_lvs>.");
 
   ("pvs", (RStringList "physvols", []), 9, [],
-   [TestOutputList (
-      InitEmptyLVM,
+   [InitEmptyLVM, TestOutputList (
       [["pvs"]], ["/dev/sda1"]);
-    TestOutputList (
-      InitNone,
-      [["sfdisk"];
+    InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
        ["pvcreate"; "/dev/sda1"];
        ["pvcreate"; "/dev/sda2"];
        ["pvcreate"; "/dev/sda3"];
@@ -419,12 +425,10 @@ PVs (eg. C</dev/sda2>).
 See also C<guestfs_pvs_full>.");
 
   ("vgs", (RStringList "volgroups", []), 10, [],
-   [TestOutputList (
-      InitEmptyLVM,
+   [InitEmptyLVM, TestOutputList (
       [["vgs"]], ["VG"]);
-    TestOutputList (
-      InitNone,
-      [["sfdisk"];
+    InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
        ["pvcreate"; "/dev/sda1"];
        ["pvcreate"; "/dev/sda2"];
        ["pvcreate"; "/dev/sda3"];
@@ -442,21 +446,19 @@ detected (eg. C<VolGroup00>).
 See also C<guestfs_vgs_full>.");
 
   ("lvs", (RStringList "logvols", []), 11, [],
-   [TestOutputList (
-      InitEmptyLVM,
+   [InitEmptyLVM, TestOutputList (
       [["lvs"]], ["/dev/VG/LV"]);
-    TestOutputList (
-      InitNone,
-      [["sfdisk"];
+    InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
        ["pvcreate"; "/dev/sda1"];
        ["pvcreate"; "/dev/sda2"];
        ["pvcreate"; "/dev/sda3"];
        ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
        ["vgcreate"; "VG2"; "/dev/sda3"];
-       ["lvcreate"; "LV1"; "VG1"; "5000"];
-       ["lvcreate"; "LV2"; "VG1"; "5000"];
-       ["lvcreate"; "LV3"; "VG2"; "5000"];
-       ["lvs"]], ["LV1"; "LV2"; "LV3"])],
+       ["lvcreate"; "LV1"; "VG1"; "50"];
+       ["lvcreate"; "LV2"; "VG1"; "50"];
+       ["lvcreate"; "LV3"; "VG2"; "50"];
+       ["lvs"]], ["/dev/VG1/LV1"; "/dev/VG1/LV2"; "/dev/VG2/LV3"])],
    "list the LVM logical volumes (LVs)",
    "\
 List all the logical volumes detected.  This is the equivalent
@@ -468,8 +470,7 @@ This returns a list of the logical volume device names
 See also C<guestfs_lvs_full>.");
 
   ("pvs_full", (RPVList "physvols", []), 12, [],
-   [TestOutputLength (
-      InitEmptyLVM,
+   [InitEmptyLVM, TestOutputLength (
       [["pvs"]], 1)],
    "list the LVM physical volumes (PVs)",
    "\
@@ -477,8 +478,7 @@ List all the physical volumes detected.  This is the equivalent
 of the L<pvs(8)> command.  The \"full\" version includes all fields.");
 
   ("vgs_full", (RVGList "volgroups", []), 13, [],
-   [TestOutputLength (
-      InitEmptyLVM,
+   [InitEmptyLVM, TestOutputLength (
       [["pvs"]], 1)],
    "list the LVM volume groups (VGs)",
    "\
@@ -486,8 +486,7 @@ List all the volumes groups detected.  This is the equivalent
 of the L<vgs(8)> command.  The \"full\" version includes all fields.");
 
   ("lvs_full", (RLVList "logvols", []), 14, [],
-   [TestOutputLength (
-      InitEmptyLVM,
+   [InitEmptyLVM, TestOutputLength (
       [["pvs"]], 1)],
    "list the LVM logical volumes (LVs)",
    "\
@@ -495,12 +494,10 @@ List all the logical volumes detected.  This is the equivalent
 of the L<lvs(8)> command.  The \"full\" version includes all fields.");
 
   ("read_lines", (RStringList "lines", [String "path"]), 15, [],
-   [TestOutputList (
-      InitEmpty,
+   [InitEmpty, TestOutputList (
       [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"];
        ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]);
-    TestOutputList (
-      InitEmpty,
+    InitEmpty, TestOutputList (
       [["write_file"; "/new"; ""; "0"];
        ["read_lines"; "/new"]], [])],
    "read file as lines",
@@ -675,45 +672,38 @@ This is just a shortcut for listing C<guestfs_aug_match>
 C<path/*> and sorting the resulting nodes into alphabetical order.");
 
   ("rm", (RErr, [String "path"]), 29, [],
-   [TestRun (
-      InitEmpty,
+   [InitEmpty, TestRun
       [["touch"; "/new"];
-       ["rm"; "/new"]]);
-    TestLastFail (
-      InitEmpty,
-      [["rm"; "/new"]]);
-    TestLastFail (
-      InitEmpty,
+       ["rm"; "/new"]];
+    InitEmpty, TestLastFail
+      [["rm"; "/new"]];
+    InitEmpty, TestLastFail
       [["mkdir"; "/new"];
-       ["rm"; "/new"]])],
+       ["rm"; "/new"]]],
    "remove a file",
    "\
 Remove the single file C<path>.");
 
   ("rmdir", (RErr, [String "path"]), 30, [],
-   [TestRun (
-      InitEmpty,
+   [InitEmpty, TestRun
       [["mkdir"; "/new"];
-       ["rmdir"; "/new"]]);
-    TestLastFail (
-      InitEmpty,
-      [["rmdir"; "/new"]]);
-    TestLastFail (
-      InitEmpty,
+       ["rmdir"; "/new"]];
+    InitEmpty, TestLastFail
+      [["rmdir"; "/new"]];
+    InitEmpty, TestLastFail
       [["touch"; "/new"];
-       ["rmdir"; "/new"]])],
+       ["rmdir"; "/new"]]],
    "remove a directory",
    "\
 Remove the single directory C<path>.");
 
   ("rm_rf", (RErr, [String "path"]), 31, [],
-   [TestOutputFalse (
-      InitEmpty,
+   [InitEmpty, TestOutputFalse
       [["mkdir"; "/new"];
        ["mkdir"; "/new/foo"];
        ["touch"; "/new/foo/bar"];
        ["rm_rf"; "/new"];
-       ["exists"; "/new"]])],
+       ["exists"; "/new"]]],
    "remove a file or directory recursively",
    "\
 Remove the file or directory C<path>, recursively removing the
@@ -721,27 +711,25 @@ contents if its a directory.  This is like the C<rm -rf> shell
 command.");
 
   ("mkdir", (RErr, [String "path"]), 32, [],
-   [TestOutputTrue (
-      InitEmpty,
+   [InitEmpty, TestOutputTrue
       [["mkdir"; "/new"];
-       ["is_dir"; "/new"]])],
+       ["is_dir"; "/new"]];
+    InitEmpty, TestLastFail
+      [["mkdir"; "/new/foo/bar"]]],
    "create a directory",
    "\
 Create a directory named C<path>.");
 
   ("mkdir_p", (RErr, [String "path"]), 33, [],
-   [TestOutputTrue (
-      InitEmpty,
+   [InitEmpty, TestOutputTrue
       [["mkdir_p"; "/new/foo/bar"];
-       ["is_dir"; "/new/foo/bar"]]);
-    TestOutputTrue (
-      InitEmpty,
+       ["is_dir"; "/new/foo/bar"]];
+    InitEmpty, TestOutputTrue
       [["mkdir_p"; "/new/foo/bar"];
-       ["is_dir"; "/new/foo"]]);
-    TestOutputTrue (
-      InitEmpty,
+       ["is_dir"; "/new/foo"]];
+    InitEmpty, TestOutputTrue
       [["mkdir_p"; "/new/foo/bar"];
-       ["is_dir"; "/new"]])],
+       ["is_dir"; "/new"]]],
    "create a directory and parents",
    "\
 Create a directory named C<path>, creating any parent directories
@@ -763,6 +751,200 @@ Change the file owner to C<owner> and group to C<group>.
 Only numeric uid and gid are supported.  If you want to use
 names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).");
+
+  ("exists", (RBool "existsflag", [String "path"]), 36, [],
+   [InitEmpty, TestOutputTrue (
+      [["touch"; "/new"];
+       ["exists"; "/new"]]);
+    InitEmpty, TestOutputTrue (
+      [["mkdir"; "/new"];
+       ["exists"; "/new"]])],
+   "test if file or directory exists",
+   "\
+This returns C<true> if and only if there is a file, directory
+(or anything) with the given C<path> name.
+
+See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>.");
+
+  ("is_file", (RBool "fileflag", [String "path"]), 37, [],
+   [InitEmpty, TestOutputTrue (
+      [["touch"; "/new"];
+       ["is_file"; "/new"]]);
+    InitEmpty, TestOutputFalse (
+      [["mkdir"; "/new"];
+       ["is_file"; "/new"]])],
+   "test if file exists",
+   "\
+This returns C<true> if and only if there is a file
+with the given C<path> name.  Note that it returns false for
+other objects like directories.
+
+See also C<guestfs_stat>.");
+
+  ("is_dir", (RBool "dirflag", [String "path"]), 38, [],
+   [InitEmpty, TestOutputFalse (
+      [["touch"; "/new"];
+       ["is_dir"; "/new"]]);
+    InitEmpty, TestOutputTrue (
+      [["mkdir"; "/new"];
+       ["is_dir"; "/new"]])],
+   "test if file exists",
+   "\
+This returns C<true> if and only if there is a directory
+with the given C<path> name.  Note that it returns false for
+other objects like files.
+
+See also C<guestfs_stat>.");
+
+  ("pvcreate", (RErr, [String "device"]), 39, [],
+   [InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
+       ["pvcreate"; "/dev/sda1"];
+       ["pvcreate"; "/dev/sda2"];
+       ["pvcreate"; "/dev/sda3"];
+       ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
+   "create an LVM physical volume",
+   "\
+This creates an LVM physical volume on the named C<device>,
+where C<device> should usually be a partition name such
+as C</dev/sda1>.");
+
+  ("vgcreate", (RErr, [String "volgroup"; StringList "physvols"]), 40, [],
+   [InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
+       ["pvcreate"; "/dev/sda1"];
+       ["pvcreate"; "/dev/sda2"];
+       ["pvcreate"; "/dev/sda3"];
+       ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
+       ["vgcreate"; "VG2"; "/dev/sda3"];
+       ["vgs"]], ["VG1"; "VG2"])],
+   "create an LVM volume group",
+   "\
+This creates an LVM volume group called C<volgroup>
+from the non-empty list of physical volumes C<physvols>.");
+
+  ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [],
+   [InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
+       ["pvcreate"; "/dev/sda1"];
+       ["pvcreate"; "/dev/sda2"];
+       ["pvcreate"; "/dev/sda3"];
+       ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
+       ["vgcreate"; "VG2"; "/dev/sda3"];
+       ["lvcreate"; "LV1"; "VG1"; "50"];
+       ["lvcreate"; "LV2"; "VG1"; "50"];
+       ["lvcreate"; "LV3"; "VG2"; "50"];
+       ["lvcreate"; "LV4"; "VG2"; "50"];
+       ["lvcreate"; "LV5"; "VG2"; "50"];
+       ["lvs"]],
+      ["/dev/VG1/LV1"; "/dev/VG1/LV2";
+       "/dev/VG2/LV3"; "/dev/VG2/LV4"; "/dev/VG2/LV5"])],
+   "create an LVM volume group",
+   "\
+This creates an LVM volume group called C<logvol>
+on the volume group C<volgroup>, with C<size> megabytes.");
+
+  ("mkfs", (RErr, [String "fstype"; String "device"]), 42, [],
+   [InitNone, TestOutput (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+       ["mkfs"; "ext2"; "/dev/sda1"];
+       ["mount"; "/dev/sda1"; "/"];
+       ["write_file"; "/new"; "new file contents"; "0"];
+       ["cat"; "/new"]], "new file contents")],
+   "make a filesystem",
+   "\
+This creates a filesystem on C<device> (usually a partition
+of LVM logical volume).  The filesystem type is C<fstype>, for
+example C<ext3>.");
+
+  ("sfdisk", (RErr, [String "device";
+                    Int "cyls"; Int "heads"; Int "sectors";
+                    StringList "lines"]), 43, [DangerWillRobinson],
+   [],
+   "create partitions on a block device",
+   "\
+This is a direct interface to the L<sfdisk(8)> program for creating
+partitions on block devices.
+
+C<device> should be a block device, for example C</dev/sda>.
+
+C<cyls>, C<heads> and C<sectors> are the number of cylinders, heads
+and sectors on the device, which are passed directly to sfdisk as
+the I<-C>, I<-H> and I<-S> parameters.  If you pass C<0> for any
+of these, then the corresponding parameter is omitted.  Usually for
+'large' disks, you can just pass C<0> for these, but for small
+(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work
+out the right geometry and you will need to tell it.
+
+C<lines> is a list of lines that we feed to C<sfdisk>.  For more
+information refer to the L<sfdisk(8)> manpage.
+
+To create a single partition occupying the whole disk, you would
+pass C<lines> as a single element list, when the single element being
+the string C<,> (comma).");
+
+  ("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
+   [InitNone, TestOutput (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+       ["mkfs"; "ext2"; "/dev/sda1"];
+       ["mount"; "/dev/sda1"; "/"];
+       ["write_file"; "/new"; "new file contents"; "0"];
+       ["cat"; "/new"]], "new file contents")],
+   "Create a file",
+   "\
+This call creates a file called C<path>.  The contents of the
+file is the string C<content> (which can contain any 8 bit data),
+with length C<size>.
+
+As a special case, if C<size> is C<0>
+then the length is calculated using C<strlen> (so in this case
+the content cannot contain embedded ASCII NULs).");
+
+  ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
+   [InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+       ["mkfs"; "ext2"; "/dev/sda1"];
+       ["mount"; "/dev/sda1"; "/"];
+       ["mounts"]], ["/dev/sda1"]);
+    InitNone, TestOutputList (
+      [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+       ["mkfs"; "ext2"; "/dev/sda1"];
+       ["mount"; "/dev/sda1"; "/"];
+       ["umount"; "/"];
+       ["mounts"]], [])],
+   "unmount a filesystem",
+   "\
+This unmounts the given filesystem.  The filesystem may be
+specified either by its mountpoint (path) or the device which
+contains the filesystem.");
+
+  ("mounts", (RStringList "devices", []), 46, [],
+   [InitEmpty, TestOutputList (
+      [["mounts"]], ["/dev/sda1"])],
+   "show mounted filesystems",
+   "\
+This returns the list of currently mounted filesystems.  It returns
+the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
+
+Some internal mounts are not shown.");
+
+  ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"],
+   [InitEmpty, TestOutputList (
+      [["umount_all"];
+       ["mounts"]], [])],
+   "unmount all filesystems",
+   "\
+This unmounts all mounted filesystems.
+
+Some internal mounts are not unmounted by this call.");
+
+  ("lvm_remove_all", (RErr, []), 48, [DangerWillRobinson],
+   [],
+   "remove all LVM LVs, VGs and PVs",
+   "\
+This command removes all LVM logical volumes, volume groups
+and physical volumes.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -884,6 +1066,17 @@ let rec replace_str s s1 s2 =
     s' ^ s2 ^ replace_str s'' s1 s2
   )
 
+let rec string_split sep str =
+  let len = String.length str in
+  let seplen = String.length sep in
+  let i = find str sep in
+  if i = -1 then [str]
+  else (
+    let s' = String.sub str 0 i in
+    let s'' = String.sub str (i+seplen) (len-i-seplen) in
+    s' :: string_split sep s''
+  )
+
 let rec find_map f = function
   | [] -> raise Not_found
   | x :: xs ->
@@ -898,7 +1091,15 @@ let iteri f xs =
   in
   loop 0 xs
 
-let name_of_argt = function String n | OptString n | Bool n | Int n -> n
+let mapi f xs =
+  let rec loop i = function
+    | [] -> []
+    | x :: xs -> let r = f i x in r :: loop (i+1) xs
+  in
+  loop 0 xs
+
+let name_of_argt = function
+  | String n | OptString n | StringList n | Bool n | Int n -> n
 
 (* Check function names etc. for consistency. *)
 let check_functions () =
@@ -1089,9 +1290,9 @@ I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
 I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
       );
       if List.mem ProtocolLimitWarning flags then
-       pr "Because of the message protocol, there is a transfer limit 
-of somewhere between 2MB and 4MB.  To transfer large files you should use
-FTP.\n\n";
+       pr "%s\n\n" protocol_limit_warning;
+      if List.mem DangerWillRobinson flags then
+       pr "%s\n\n" danger_will_robinson;
   ) all_functions_sorted
 
 and generate_structs_pod () =
@@ -1169,6 +1370,7 @@ and generate_xdr () =
             function
             | String n -> pr "  string %s<>;\n" n
             | OptString n -> pr "  str *%s;\n" n
+            | StringList n -> pr "  str %s<>;\n" n
             | Bool n -> pr "  bool %s;\n" n
             | Int n -> pr "  int %s;\n" n
           ) args;
@@ -1427,6 +1629,9 @@ and generate_client_actions () =
                 pr "  args.%s = (char *) %s;\n" n n
             | OptString n ->
                 pr "  args.%s = %s ? (char **) &%s : NULL;\n" n n n
+            | StringList n ->
+                pr "  args.%s.%s_val = (char **) %s;\n" n n n;
+                pr "  for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
             | Bool n ->
                 pr "  args.%s = %s;\n" n n
             | Int n ->
@@ -1556,6 +1761,7 @@ and generate_daemon_actions () =
             function
             | String n
             | OptString n -> pr "  const char *%s;\n" n
+            | StringList n -> pr "  char **%s;\n" n
             | Bool n -> pr "  int %s;\n" n
             | Int n -> pr "  int %s;\n" n
           ) args
@@ -1575,6 +1781,10 @@ and generate_daemon_actions () =
             function
             | String n -> pr "  %s = args.%s;\n" n n
             | OptString n -> pr "  %s = args.%s ? *args.%s : NULL;\n" n n n
+            | StringList n ->
+                pr "  args.%s.%s_val = realloc (args.%s.%s_val, sizeof (char *) * (args.%s.%s_len+1));\n" n n n n n n;
+                pr "  args.%s.%s_val[args.%s.%s_len] = NULL;\n" n n n n;
+                pr "  %s = args.%s.%s_val;\n" n n n
             | Bool n -> pr "  %s = args.%s;\n" n n
             | Int n -> pr "  %s = args.%s;\n" n n
           ) args;
@@ -1586,8 +1796,8 @@ and generate_daemon_actions () =
       pr ";\n";
 
       pr "  if (r == %s)\n" error_code;
-      pr "    /* do_%s has already called reply_with_error, so just return */\n" name;
-      pr "    return;\n";
+      pr "    /* do_%s has already called reply_with_error */\n" name;
+      pr "    goto done;\n";
       pr "\n";
 
       (match fst style with
@@ -1633,6 +1843,16 @@ and generate_daemon_actions () =
           pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
       );
 
+      (* Free the args. *)
+      (match snd style with
+       | [] ->
+          pr "done: ;\n";
+       | _ ->
+          pr "done:\n";
+          pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
+            name
+      );
+
       pr "}\n\n";
   ) daemon_functions;
 
@@ -1823,20 +2043,459 @@ and generate_daemon_actions () =
 and generate_tests () =
   generate_header CStyle GPLv2;
 
-  pr "#include <stdio.h>\n";
-  pr "#include <stdlib.h>\n";
-  pr "#include <string.h>\n";
-  pr "\n";
-  pr "#include \"guestfs.h\"\n";
+  pr "\
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include \"guestfs.h\"
+
+static guestfs_h *g;
+static int suppress_error = 0;
+
+static void print_error (guestfs_h *g, void *data, const char *msg)
+{
+  if (!suppress_error)
+    fprintf (stderr, \"%%s\\n\", msg);
+}
+
+static void print_strings (char * const * const argv)
+{
+  int argc;
+
+  for (argc = 0; argv[argc] != NULL; ++argc)
+    printf (\"\\t%%s\\n\", argv[argc]);
+}
+
+";
+
+  let test_names =
+    List.map (
+      fun (name, _, _, _, tests, _, _) ->
+       mapi (generate_one_test name) tests
+    ) all_functions in
+  let test_names = List.concat test_names in
+  let nr_tests = List.length test_names in
+
+  pr "\
+int main (int argc, char *argv[])
+{
+  char c = 0;
+  int failed = 0;
+  const char *srcdir;
+  int fd;
+  char buf[256];
+
+  g = guestfs_create ();
+  if (g == NULL) {
+    printf (\"guestfs_create FAILED\\n\");
+    exit (1);
+  }
+
+  guestfs_set_error_handler (g, print_error, NULL);
+
+  srcdir = getenv (\"srcdir\");
+  if (!srcdir) srcdir = \".\";
+  guestfs_set_path (g, srcdir);
+
+  snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);
+  fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+  if (fd == -1) {
+    perror (buf);
+    exit (1);
+  }
+  if (lseek (fd, %d, SEEK_SET) == -1) {
+    perror (\"lseek\");
+    close (fd);
+    unlink (buf);
+    exit (1);
+  }
+  if (write (fd, &c, 1) == -1) {
+    perror (\"write\");
+    close (fd);
+    unlink (buf);
+    exit (1);
+  }
+  if (close (fd) == -1) {
+    perror (buf);
+    unlink (buf);
+    exit (1);
+  }
+  if (guestfs_add_drive (g, buf) == -1) {
+    printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
+    exit (1);
+  }
+
+  snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);
+  fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+  if (fd == -1) {
+    perror (buf);
+    exit (1);
+  }
+  if (lseek (fd, %d, SEEK_SET) == -1) {
+    perror (\"lseek\");
+    close (fd);
+    unlink (buf);
+    exit (1);
+  }
+  if (write (fd, &c, 1) == -1) {
+    perror (\"write\");
+    close (fd);
+    unlink (buf);
+    exit (1);
+  }
+  if (close (fd) == -1) {
+    perror (buf);
+    unlink (buf);
+    exit (1);
+  }
+  if (guestfs_add_drive (g, buf) == -1) {
+    printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
+    exit (1);
+  }
+
+  snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);
+  fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+  if (fd == -1) {
+    perror (buf);
+    exit (1);
+  }
+  if (lseek (fd, %d, SEEK_SET) == -1) {
+    perror (\"lseek\");
+    close (fd);
+    unlink (buf);
+    exit (1);
+  }
+  if (write (fd, &c, 1) == -1) {
+    perror (\"write\");
+    close (fd);
+    unlink (buf);
+    exit (1);
+  }
+  if (close (fd) == -1) {
+    perror (buf);
+    unlink (buf);
+    exit (1);
+  }
+  if (guestfs_add_drive (g, buf) == -1) {
+    printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
+    exit (1);
+  }
+
+  if (guestfs_launch (g) == -1) {
+    printf (\"guestfs_launch FAILED\\n\");
+    exit (1);
+  }
+  if (guestfs_wait_ready (g) == -1) {
+    printf (\"guestfs_wait_ready FAILED\\n\");
+    exit (1);
+  }
+
+" (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024);
+
+  iteri (
+    fun i test_name ->
+      pr "  printf (\"%3d/%3d %s\\n\");\n" (i+1) nr_tests test_name;
+      pr "  if (%s () == -1) {\n" test_name;
+      pr "    printf (\"%s FAILED\\n\");\n" test_name;
+      pr "    failed++;\n";
+      pr "  }\n";
+  ) test_names;
   pr "\n";
 
+  pr "  guestfs_close (g);\n";
+  pr "  snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);\n";
+  pr "  unlink (buf);\n";
+  pr "  snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);\n";
+  pr "  unlink (buf);\n";
+  pr "  snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);\n";
+  pr "  unlink (buf);\n";
+  pr "\n";
 
+  pr "  if (failed > 0) {\n";
+  pr "    printf (\"***** %%d / %d tests FAILED *****\\n\", failed);\n"
+    nr_tests;
+  pr "    exit (1);\n";
+  pr "  }\n";
+  pr "\n";
 
-  pr "int main (int argc, char *argv[])\n";
-  pr "{\n";
   pr "  exit (0);\n";
   pr "}\n"
 
+and generate_one_test name i (init, test) =
+  let test_name = sprintf "test_%s_%d" name i in
+
+  pr "static int %s (void)\n" test_name;
+  pr "{\n";
+
+  (match init with
+   | InitNone ->
+       pr "  /* InitNone for %s (%d) */\n" name i;
+       List.iter (generate_test_command_call test_name)
+        [["umount_all"];
+         ["lvm_remove_all"]]
+   | InitEmpty ->
+       pr "  /* InitEmpty for %s (%d): create ext2 on /dev/sda1 */\n" name i;
+       List.iter (generate_test_command_call test_name)
+        [["umount_all"];
+         ["lvm_remove_all"];
+         ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+         ["mkfs"; "ext2"; "/dev/sda1"];
+         ["mount"; "/dev/sda1"; "/"]]
+   | InitEmptyLVM ->
+       pr "  /* InitEmptyLVM for %s (%d): create ext2 on /dev/VG/LV */\n"
+        name i;
+       List.iter (generate_test_command_call test_name)
+        [["umount_all"];
+         ["lvm_remove_all"];
+         ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+         ["pvcreate"; "/dev/sda1"];
+         ["vgcreate"; "VG"; "/dev/sda1"];
+         ["lvcreate"; "LV"; "VG"; "8"];
+         ["mkfs"; "ext2"; "/dev/VG/LV"];
+         ["mount"; "/dev/VG/LV"; "/"]]
+  );
+
+  let get_seq_last = function
+    | [] ->
+       failwithf "%s: you cannot use [] (empty list) when expecting a command"
+         test_name
+    | seq ->
+       let seq = List.rev seq in
+       List.rev (List.tl seq), List.hd seq
+  in
+
+  (match test with
+   | TestRun seq ->
+       pr "  /* TestRun for %s (%d) */\n" name i;
+       List.iter (generate_test_command_call test_name) seq
+   | TestOutput (seq, expected) ->
+       pr "  /* TestOutput for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       let test () =
+        pr "    if (strcmp (r, \"%s\") != 0) {\n" (c_quote expected);
+        pr "      fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r);\n" test_name (c_quote expected);
+        pr "      return -1;\n";
+        pr "    }\n"
+       in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call ~test test_name last
+   | TestOutputList (seq, expected) ->
+       pr "  /* TestOutputList for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       let test () =
+        iteri (
+          fun i str ->
+            pr "    if (!r[%d]) {\n" i;
+            pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
+            pr "      print_strings (r);\n";
+            pr "      return -1;\n";
+            pr "    }\n";
+            pr "    if (strcmp (r[%d], \"%s\") != 0) {\n" i (c_quote str);
+            pr "      fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r[%d]);\n" test_name (c_quote str) i;
+            pr "      return -1;\n";
+            pr "    }\n"
+        ) expected;
+        pr "    if (r[%d] != NULL) {\n" (List.length expected);
+        pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
+          test_name;
+        pr "      print_strings (r);\n";
+        pr "      return -1;\n";
+        pr "    }\n"
+       in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call ~test test_name last
+   | TestOutputInt (seq, expected) ->
+       pr "  /* TestOutputInt for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       let test () =
+        pr "    if (r != %d) {\n" expected;
+        pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\", r);\n"
+          test_name expected;
+        pr "      return -1;\n";
+        pr "    }\n"
+       in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call ~test test_name last
+   | TestOutputTrue seq ->
+       pr "  /* TestOutputTrue for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       let test () =
+        pr "    if (!r) {\n";
+        pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
+          test_name;
+        pr "      return -1;\n";
+        pr "    }\n"
+       in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call ~test test_name last
+   | TestOutputFalse seq ->
+       pr "  /* TestOutputFalse for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       let test () =
+        pr "    if (r) {\n";
+        pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
+          test_name;
+        pr "      return -1;\n";
+        pr "    }\n"
+       in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call ~test test_name last
+   | TestOutputLength (seq, expected) ->
+       pr "  /* TestOutputLength for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       let test () =
+        pr "    int j;\n";
+        pr "    for (j = 0; j < %d; ++j)\n" expected;
+        pr "      if (r[j] == NULL) {\n";
+        pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
+          test_name;
+        pr "        print_strings (r);\n";
+        pr "        return -1;\n";
+        pr "      }\n";
+        pr "    if (r[j] != NULL) {\n";
+        pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
+          test_name;
+        pr "      print_strings (r);\n";
+        pr "      return -1;\n";
+        pr "    }\n"
+       in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call ~test test_name last
+   | TestLastFail seq ->
+       pr "  /* TestLastFail for %s (%d) */\n" name i;
+       let seq, last = get_seq_last seq in
+       List.iter (generate_test_command_call test_name) seq;
+       generate_test_command_call test_name ~expect_error:true last
+  );
+
+  pr "  return 0;\n";
+  pr "}\n";
+  pr "\n";
+  test_name
+
+(* Generate the code to run a command, leaving the result in 'r'.
+ * If you expect to get an error then you should set expect_error:true.
+ *)
+and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
+  match cmd with
+  | [] -> assert false
+  | name :: args ->
+      (* Look up the command to find out what args/ret it has. *)
+      let style =
+       try
+         let _, style, _, _, _, _, _ =
+           List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
+         style
+       with Not_found ->
+         failwithf "%s: in test, command %s was not found" test_name name in
+
+      if List.length (snd style) <> List.length args then
+       failwithf "%s: in test, wrong number of args given to %s"
+         test_name name;
+
+      pr "  {\n";
+
+      List.iter (
+       function
+       | String _, _
+       | OptString _, _
+       | Int _, _
+       | Bool _, _ -> ()
+       | StringList n, arg ->
+           pr "    char *%s[] = {\n" n;
+           let strs = string_split " " arg in
+           List.iter (
+             fun str -> pr "      \"%s\",\n" (c_quote str)
+           ) strs;
+           pr "      NULL\n";
+           pr "    };\n";
+      ) (List.combine (snd style) args);
+
+      let error_code =
+       match fst style with
+       | RErr | RInt _ | RBool _ -> pr "    int r;\n"; "-1"
+       | RConstString _ -> pr "    const char *r;\n"; "NULL"
+       | RString _ -> pr "    char *r;\n"; "NULL"
+       | RStringList _ ->
+           pr "    char **r;\n";
+           pr "    int i;\n";
+           "NULL"
+       | RIntBool _ ->
+           pr "    struct guestfs_int_bool *r;\n";
+           "NULL"
+       | RPVList _ ->
+           pr "    struct guestfs_lvm_pv_list *r;\n";
+           "NULL"
+       | RVGList _ ->
+           pr "    struct guestfs_lvm_vg_list *r;\n";
+           "NULL"
+       | RLVList _ ->
+           pr "    struct guestfs_lvm_lv_list *r;\n";
+           "NULL" in
+
+      pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
+      pr "    r = guestfs_%s (g" name;
+
+      (* Generate the parameters. *)
+      List.iter (
+       function
+       | String _, arg -> pr ", \"%s\"" (c_quote arg)
+       | OptString _, arg ->
+           if arg = "NULL" then pr ", NULL" else pr ", \"%s\"" (c_quote arg)
+       | StringList n, _ ->
+           pr ", %s" n
+       | Int _, arg ->
+           let i =
+             try int_of_string arg
+             with Failure "int_of_string" ->
+               failwithf "%s: expecting an int, but got '%s'" test_name arg in
+           pr ", %d" i
+       | Bool _, arg ->
+           let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
+      ) (List.combine (snd style) args);
+
+      pr ");\n";
+      if not expect_error then
+       pr "    if (r == %s)\n" error_code
+      else
+       pr "    if (r != %s)\n" error_code;
+      pr "      return -1;\n";
+
+      (* Insert the test code. *)
+      (match test with
+       | None -> ()
+       | Some f -> f ()
+      );
+
+      (match fst style with
+       | RErr | RInt _ | RBool _ | RConstString _ -> ()
+       | RString _ -> pr "    free (r);\n"
+       | RStringList _ ->
+          pr "    for (i = 0; r[i] != NULL; ++i)\n";
+          pr "      free (r[i]);\n";
+          pr "    free (r);\n"
+       | RIntBool _ ->
+          pr "    guestfs_free_int_bool (r);\n"
+       | RPVList _ ->
+          pr "    guestfs_free_lvm_pv_list (r);\n"
+       | RVGList _ ->
+          pr "    guestfs_free_lvm_vg_list (r);\n"
+       | RLVList _ ->
+          pr "    guestfs_free_lvm_lv_list (r);\n"
+      );
+
+      pr "  }\n"
+
+and c_quote str =
+  let str = replace_str str "\r" "\\r" in
+  let str = replace_str str "\n" "\\n" in
+  let str = replace_str str "\t" "\\t" in
+  str
+
 (* Generate a lot of different functions for guestfish. *)
 and generate_fish_cmds () =
   generate_header CStyle GPLv2;
@@ -1893,11 +2552,19 @@ and generate_fish_cmds () =
 
       let warnings =
        if List.mem ProtocolLimitWarning flags then
-         "\n\nBecause of the message protocol, there is a transfer limit 
-of somewhere between 2MB and 4MB.  To transfer large files you should use
-FTP."
+         ("\n\n" ^ protocol_limit_warning)
        else "" in
 
+      (* For DangerWillRobinson commands, we should probably have
+       * guestfish prompt before allowing you to use them (especially
+       * in interactive mode). XXX
+       *)
+      let warnings =
+       warnings ^
+         if List.mem DangerWillRobinson flags then
+           ("\n\n" ^ danger_will_robinson)
+         else "" in
+
       let describe_alias =
        if name <> alias then
          sprintf "\n\nYou can use '%s' as an alias for this command." alias
@@ -1977,8 +2644,9 @@ FTP."
       );
       List.iter (
        function
-       | String n -> pr "  const char *%s;\n" n
+       | String n
        | OptString n -> pr "  const char *%s;\n" n
+       | StringList n -> pr "  char **%s;\n" n
        | Bool n -> pr "  int %s;\n" n
        | Int n -> pr "  int %s;\n" n
       ) (snd style);
@@ -1998,6 +2666,8 @@ FTP."
          | OptString name ->
              pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
                name i i
+         | StringList name ->
+             pr "  %s = parse_string_list (argv[%d]);\n" name i
          | Bool name ->
              pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
          | Int name ->
@@ -2115,12 +2785,19 @@ and generate_fish_actions_pod () =
        function
        | String n -> pr " %s" n
        | OptString n -> pr " %s" n
+       | StringList n -> pr " %s,..." n
        | Bool _ -> pr " true|false"
        | Int n -> pr " %s" n
       ) (snd style);
       pr "\n";
       pr "\n";
-      pr "%s\n\n" longdesc
+      pr "%s\n\n" longdesc;
+
+      if List.mem ProtocolLimitWarning flags then
+       pr "%s\n\n" protocol_limit_warning;
+
+      if List.mem DangerWillRobinson flags then
+       pr "%s\n\n" danger_will_robinson
   ) all_functions_sorted
 
 (* Generate a C function prototype. *)
@@ -2169,6 +2846,7 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
       function
       | String n -> next (); pr "const char *%s" n
       | OptString n -> next (); pr "const char *%s" n
+      | StringList n -> next (); pr "char * const* const %s" n
       | Bool n -> next (); pr "int %s" n
       | Int n -> next (); pr "int %s" n
     ) (snd style);
@@ -2190,9 +2868,10 @@ and generate_call_args ?handle style =
       if !comma then pr ", ";
       comma := true;
       match arg with
-      | String n -> pr "%s" n
-      | OptString n -> pr "%s" n
-      | Bool n -> pr "%s" n
+      | String n
+      | OptString n
+      | StringList n
+      | Bool n
       | Int n -> pr "%s" n
   ) (snd style);
   pr ")"
@@ -2339,18 +3018,23 @@ and generate_ocaml_c () =
 
   List.iter (
     fun (name, style, _, _, _, _, _) ->
+      let params =
+       "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
+
       pr "CAMLprim value\n";
-      pr "ocaml_guestfs_%s (value gv" name;
-      List.iter (
-       fun arg -> pr ", value %sv" (name_of_argt arg)
-      ) (snd style);
+      pr "ocaml_guestfs_%s (value %s" name (List.hd params);
+      List.iter (pr ", value %s") (List.tl params);
       pr ")\n";
       pr "{\n";
-      pr "  CAMLparam%d (gv" (1 + (List.length (snd style)));
-      List.iter (
-       fun arg -> pr ", %sv" (name_of_argt arg)
-      ) (snd style);
-      pr ");\n";
+
+      (match params with
+       | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
+          pr "  CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
+          pr "  CAMLxparam%d (%s);\n"
+            (List.length rest) (String.concat ", " rest)
+       | ps ->
+          pr "  CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
+      );
       pr "  CAMLlocal1 (rv);\n";
       pr "\n";
 
@@ -2367,6 +3051,8 @@ and generate_ocaml_c () =
            pr "  const char *%s =\n" n;
            pr "    %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n"
              n n
+       | StringList n ->
+           pr "  char **%s = ocaml_guestfs_strings_val (%sv);\n" n n
        | Bool n ->
            pr "  int %s = Bool_val (%sv);\n" n n
        | Int n ->
@@ -2402,6 +3088,14 @@ and generate_ocaml_c () =
       generate_call_args ~handle:"g" style;
       pr ";\n";
       pr "  caml_leave_blocking_section ();\n";
+
+      List.iter (
+       function
+       | StringList n ->
+           pr "  ocaml_guestfs_free_strings (%s);\n" n;
+       | String _ | OptString _ | Bool _ | Int _ -> ()
+      ) (snd style);
+
       pr "  if (r == %s)\n" error_code;
       pr "    ocaml_guestfs_raise_error (g, \"%s\");\n" name;
       pr "\n";
@@ -2436,7 +3130,18 @@ and generate_ocaml_c () =
 
       pr "  CAMLreturn (rv);\n";
       pr "}\n";
-      pr "\n"
+      pr "\n";
+
+      if List.length params > 5 then (
+       pr "CAMLprim value\n";
+       pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
+       pr "{\n";
+       pr "  return ocaml_guestfs_%s (argv[0]" name;
+       iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
+       pr ");\n";
+       pr "}\n";
+       pr "\n"
+      )
   ) all_functions
 
 and generate_ocaml_lvm_structure_decls () =
@@ -2462,6 +3167,7 @@ and generate_ocaml_prototype ?(is_external = false) name style =
     function
     | String _ -> pr "string -> "
     | OptString _ -> pr "string option -> "
+    | StringList _ -> pr "string array -> "
     | Bool _ -> pr "bool -> "
     | Int _ -> pr "int -> "
   ) (snd style);
@@ -2477,7 +3183,12 @@ and generate_ocaml_prototype ?(is_external = false) name style =
    | RVGList _ -> pr "lvm_vg array"
    | RLVList _ -> pr "lvm_lv array"
   );
-  if is_external then pr " = \"ocaml_guestfs_%s\"" name;
+  if is_external then (
+    pr " = ";
+    if List.length (snd style) + 1 > 5 then
+      pr "\"ocaml_guestfs_%s_byte\" " name;
+    pr "\"ocaml_guestfs_%s\"" name
+  );
   pr "\n"
 
 (* Generate Perl xs code, a sort of crazy variation of C with macros. *)
@@ -2539,6 +3250,35 @@ error_handler (guestfs_h *g,
   last_error = strdup (msg);
 }
 
+/* http://www.perlmonks.org/?node_id=680842 */
+static char **
+XS_unpack_charPtrPtr (SV *arg) {
+  char **ret;
+  AV *av;
+  I32 i;
+
+  if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV) {
+    croak (\"array reference expected\");
+  }
+
+  av = (AV *)SvRV (arg);
+  ret = (char **)malloc (av_len (av) + 1 + 1);
+
+  for (i = 0; i <= av_len (av); i++) {
+    SV **elem = av_fetch (av, i, 0);
+
+      if (!elem || !*elem) {
+        croak (\"missing element in list\");
+      }
+
+      ret[i] = SvPV_nolen (*elem);
+  }
+
+  ret[i + 1] = NULL;
+
+  return ret;
+}
+
 MODULE = Sys::Guestfs  PACKAGE = Sys::Guestfs
 
 guestfs_h *
@@ -2581,17 +3321,32 @@ DESTROY (g)
        function
        | String n -> pr "      char *%s;\n" n
        | OptString n -> pr "      char *%s;\n" n
+       | StringList n -> pr "      char **%s;\n" n
        | Bool n -> pr "      int %s;\n" n
        | Int n -> pr "      int %s;\n" n
       ) (snd style);
+
+      let do_cleanups () =
+       List.iter (
+         function
+         | String _
+         | OptString _
+         | Bool _
+         | Int _ -> ()
+         | StringList n -> pr "        free (%s);\n" n
+       ) (snd style)
+      in
+
       (* Code. *)
       (match fst style with
        | RErr ->
           pr " PPCODE:\n";
           pr "      if (guestfs_%s " name;
           generate_call_args ~handle:"g" style;
-          pr " == -1)\n";
-          pr "        croak (\"%s: %%s\", last_error);\n" name
+          pr " == -1) {\n";
+          do_cleanups ();
+          pr "        croak (\"%s: %%s\", last_error);\n" name;
+          pr "      }\n"
        | RInt n
        | RBool n ->
           pr "PREINIT:\n";
@@ -2600,8 +3355,10 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == -1)\n" n;
+          pr "      if (%s == -1) {\n" n;
+          do_cleanups ();
           pr "        croak (\"%s: %%s\", last_error);\n" name;
+          pr "      }\n";
           pr "      RETVAL = newSViv (%s);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
@@ -2612,8 +3369,10 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL)\n" n;
+          pr "      if (%s == NULL) {\n" n;
+          do_cleanups ();
           pr "        croak (\"%s: %%s\", last_error);\n" name;
+          pr "      }\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
@@ -2624,8 +3383,10 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL)\n" n;
+          pr "      if (%s == NULL) {\n" n;
+          do_cleanups ();
           pr "        croak (\"%s: %%s\", last_error);\n" name;
+          pr "      }\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr "      free (%s);\n" n;
           pr " OUTPUT:\n";
@@ -2638,8 +3399,10 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL)\n" n;
+          pr "      if (%s == NULL) {\n" n;
+          do_cleanups ();
           pr "        croak (\"%s: %%s\", last_error);\n" name;
+          pr "      }\n";
           pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
           pr "      EXTEND (SP, n);\n";
           pr "      for (i = 0; i < n; ++i) {\n";
@@ -2654,8 +3417,10 @@ DESTROY (g)
           pr "      r = guestfs_%s " name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (r == NULL)\n";
+          pr "      if (r == NULL) {\n";
+          do_cleanups ();
           pr "        croak (\"%s: %%s\", last_error);\n" name;
+          pr "      }\n";
           pr "      EXTEND (SP, 2);\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
@@ -2667,6 +3432,9 @@ DESTROY (g)
        | RLVList n ->
           generate_perl_lvm_code "lv" lv_cols name style n;
       );
+
+      do_cleanups ();
+
       pr "\n"
   ) all_functions
 
@@ -2797,9 +3565,9 @@ sub new {
       pr "\n\n";
       pr "%s\n\n" longdesc;
       if List.mem ProtocolLimitWarning flags then
-       pr "Because of the message protocol, there is a transfer limit 
-of somewhere between 2MB and 4MB.  To transfer large files you should use
-FTP.\n\n";
+       pr "%s\n\n" protocol_limit_warning;
+      if List.mem DangerWillRobinson flags then
+       pr "%s\n\n" danger_will_robinson
   ) all_functions_sorted;
 
   (* End of file. *)
@@ -2844,7 +3612,11 @@ and generate_perl_prototype name style =
     fun arg ->
       if !comma then pr ", ";
       comma := true;
-      pr "%s" (name_of_argt arg)
+      match arg with
+      | String n | OptString n | Bool n | Int n ->
+         pr "$%s" n
+      | StringList n ->
+         pr "\\@%s" n
   ) (snd style);
   pr ");"
 
index 25892cd..669ca76 100644 (file)
@@ -2540,3 +2540,934 @@ int guestfs_chown (guestfs_h *g,
   return 0;
 }
 
+struct exists_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_exists_ret ret;
+};
+
+static void exists_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct exists_rv *rv = (struct exists_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_exists: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_exists: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_exists_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_exists: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_exists (guestfs_h *g,
+               const char *path)
+{
+  struct guestfs_exists_args args;
+  struct exists_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_exists called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.path = (char *) path;
+  serial = dispatch (g, GUESTFS_PROC_EXISTS,
+                     (xdrproc_t) xdr_guestfs_exists_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = exists_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_exists failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_EXISTS, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return rv.ret.existsflag;
+}
+
+struct is_file_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_is_file_ret ret;
+};
+
+static void is_file_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct is_file_rv *rv = (struct is_file_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_is_file: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_is_file: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_is_file_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_is_file: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_is_file (guestfs_h *g,
+               const char *path)
+{
+  struct guestfs_is_file_args args;
+  struct is_file_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_is_file called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.path = (char *) path;
+  serial = dispatch (g, GUESTFS_PROC_IS_FILE,
+                     (xdrproc_t) xdr_guestfs_is_file_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = is_file_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_is_file failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_IS_FILE, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return rv.ret.fileflag;
+}
+
+struct is_dir_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_is_dir_ret ret;
+};
+
+static void is_dir_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct is_dir_rv *rv = (struct is_dir_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_is_dir: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_is_dir: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_is_dir_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_is_dir: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_is_dir (guestfs_h *g,
+               const char *path)
+{
+  struct guestfs_is_dir_args args;
+  struct is_dir_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_is_dir called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.path = (char *) path;
+  serial = dispatch (g, GUESTFS_PROC_IS_DIR,
+                     (xdrproc_t) xdr_guestfs_is_dir_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = is_dir_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_is_dir failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_IS_DIR, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return rv.ret.dirflag;
+}
+
+struct pvcreate_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void pvcreate_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct pvcreate_rv *rv = (struct pvcreate_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_pvcreate: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_pvcreate: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_pvcreate (guestfs_h *g,
+               const char *device)
+{
+  struct guestfs_pvcreate_args args;
+  struct pvcreate_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_pvcreate called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.device = (char *) device;
+  serial = dispatch (g, GUESTFS_PROC_PVCREATE,
+                     (xdrproc_t) xdr_guestfs_pvcreate_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = pvcreate_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_pvcreate failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_PVCREATE, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct vgcreate_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void vgcreate_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct vgcreate_rv *rv = (struct vgcreate_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_vgcreate: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_vgcreate: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_vgcreate (guestfs_h *g,
+               const char *volgroup,
+               char * const* const physvols)
+{
+  struct guestfs_vgcreate_args args;
+  struct vgcreate_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_vgcreate called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.volgroup = (char *) volgroup;
+  args.physvols.physvols_val = (char **) physvols;
+  for (args.physvols.physvols_len = 0; physvols[args.physvols.physvols_len]; args.physvols.physvols_len++) ;
+  serial = dispatch (g, GUESTFS_PROC_VGCREATE,
+                     (xdrproc_t) xdr_guestfs_vgcreate_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = vgcreate_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_vgcreate failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_VGCREATE, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct lvcreate_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void lvcreate_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct lvcreate_rv *rv = (struct lvcreate_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_lvcreate: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_lvcreate: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_lvcreate (guestfs_h *g,
+               const char *logvol,
+               const char *volgroup,
+               int mbytes)
+{
+  struct guestfs_lvcreate_args args;
+  struct lvcreate_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_lvcreate called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.logvol = (char *) logvol;
+  args.volgroup = (char *) volgroup;
+  args.mbytes = mbytes;
+  serial = dispatch (g, GUESTFS_PROC_LVCREATE,
+                     (xdrproc_t) xdr_guestfs_lvcreate_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = lvcreate_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_lvcreate failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LVCREATE, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct mkfs_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void mkfs_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct mkfs_rv *rv = (struct mkfs_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_mkfs: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_mkfs: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_mkfs (guestfs_h *g,
+               const char *fstype,
+               const char *device)
+{
+  struct guestfs_mkfs_args args;
+  struct mkfs_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_mkfs called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.fstype = (char *) fstype;
+  args.device = (char *) device;
+  serial = dispatch (g, GUESTFS_PROC_MKFS,
+                     (xdrproc_t) xdr_guestfs_mkfs_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = mkfs_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_mkfs failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_MKFS, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct sfdisk_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void sfdisk_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct sfdisk_rv *rv = (struct sfdisk_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_sfdisk: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_sfdisk: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_sfdisk (guestfs_h *g,
+               const char *device,
+               int cyls,
+               int heads,
+               int sectors,
+               char * const* const lines)
+{
+  struct guestfs_sfdisk_args args;
+  struct sfdisk_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_sfdisk called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.device = (char *) device;
+  args.cyls = cyls;
+  args.heads = heads;
+  args.sectors = sectors;
+  args.lines.lines_val = (char **) lines;
+  for (args.lines.lines_len = 0; lines[args.lines.lines_len]; args.lines.lines_len++) ;
+  serial = dispatch (g, GUESTFS_PROC_SFDISK,
+                     (xdrproc_t) xdr_guestfs_sfdisk_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = sfdisk_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_sfdisk failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_SFDISK, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct write_file_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void write_file_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct write_file_rv *rv = (struct write_file_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_write_file: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_write_file: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_write_file (guestfs_h *g,
+               const char *path,
+               const char *content,
+               int size)
+{
+  struct guestfs_write_file_args args;
+  struct write_file_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_write_file called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.path = (char *) path;
+  args.content = (char *) content;
+  args.size = size;
+  serial = dispatch (g, GUESTFS_PROC_WRITE_FILE,
+                     (xdrproc_t) xdr_guestfs_write_file_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = write_file_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_write_file failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_WRITE_FILE, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct umount_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void umount_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct umount_rv *rv = (struct umount_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_umount: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_umount: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_umount (guestfs_h *g,
+               const char *pathordevice)
+{
+  struct guestfs_umount_args args;
+  struct umount_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_umount called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.pathordevice = (char *) pathordevice;
+  serial = dispatch (g, GUESTFS_PROC_UMOUNT,
+                     (xdrproc_t) xdr_guestfs_umount_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = umount_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_umount failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_UMOUNT, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct mounts_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_mounts_ret ret;
+};
+
+static void mounts_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct mounts_rv *rv = (struct mounts_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_mounts: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_mounts: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_mounts_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_mounts: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+char **guestfs_mounts (guestfs_h *g)
+{
+  struct mounts_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_mounts called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_MOUNTS, NULL, NULL);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = mounts_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_mounts failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_MOUNTS, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  /* caller will free this, but we need to add a NULL entry */
+  rv.ret.devices.devices_val =    safe_realloc (g, rv.ret.devices.devices_val,
+                  sizeof (char *) * (rv.ret.devices.devices_len + 1));
+  rv.ret.devices.devices_val[rv.ret.devices.devices_len] = NULL;
+  return rv.ret.devices.devices_val;
+}
+
+struct umount_all_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void umount_all_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct umount_all_rv *rv = (struct umount_all_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_umount_all: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_umount_all: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_umount_all (guestfs_h *g)
+{
+  struct umount_all_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_umount_all called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_UMOUNT_ALL, NULL, NULL);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = umount_all_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_umount_all failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_UMOUNT_ALL, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct lvm_remove_all_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void lvm_remove_all_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct lvm_remove_all_rv *rv = (struct lvm_remove_all_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_lvm_remove_all: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_lvm_remove_all: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_lvm_remove_all (guestfs_h *g)
+{
+  struct lvm_remove_all_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_lvm_remove_all called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_LVM_REMOVE_ALL, NULL, NULL);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = lvm_remove_all_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_lvm_remove_all failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LVM_REMOVE_ALL, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
index 06f1485..d40ef15 100644 (file)
@@ -66,3 +66,16 @@ extern int guestfs_mkdir (guestfs_h *handle, const char *path);
 extern int guestfs_mkdir_p (guestfs_h *handle, const char *path);
 extern int guestfs_chmod (guestfs_h *handle, int mode, const char *path);
 extern int guestfs_chown (guestfs_h *handle, int owner, int group, const char *path);
+extern int guestfs_exists (guestfs_h *handle, const char *path);
+extern int guestfs_is_file (guestfs_h *handle, const char *path);
+extern int guestfs_is_dir (guestfs_h *handle, const char *path);
+extern int guestfs_pvcreate (guestfs_h *handle, const char *device);
+extern int guestfs_vgcreate (guestfs_h *handle, const char *volgroup, char * const* const physvols);
+extern int guestfs_lvcreate (guestfs_h *handle, const char *logvol, const char *volgroup, int mbytes);
+extern int guestfs_mkfs (guestfs_h *handle, const char *fstype, const char *device);
+extern int guestfs_sfdisk (guestfs_h *handle, const char *device, int cyls, int heads, int sectors, char * const* const lines);
+extern int guestfs_write_file (guestfs_h *handle, const char *path, const char *content, int size);
+extern int guestfs_umount (guestfs_h *handle, const char *pathordevice);
+extern char **guestfs_mounts (guestfs_h *handle);
+extern int guestfs_umount_all (guestfs_h *handle);
+extern int guestfs_lvm_remove_all (guestfs_h *handle);
index 781c2da..8e86812 100644 (file)
@@ -619,6 +619,214 @@ xdr_guestfs_chown_args (XDR *xdrs, guestfs_chown_args *objp)
 }
 
 bool_t
+xdr_guestfs_exists_args (XDR *xdrs, guestfs_exists_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_exists_ret (XDR *xdrs, guestfs_exists_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->existsflag))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_is_file_args (XDR *xdrs, guestfs_is_file_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_is_file_ret (XDR *xdrs, guestfs_is_file_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->fileflag))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_is_dir_args (XDR *xdrs, guestfs_is_dir_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_is_dir_ret (XDR *xdrs, guestfs_is_dir_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->dirflag))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_pvcreate_args (XDR *xdrs, guestfs_pvcreate_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->device, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_vgcreate_args (XDR *xdrs, guestfs_vgcreate_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->volgroup, ~0))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->physvols.physvols_val, (u_int *) &objp->physvols.physvols_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvcreate_args (XDR *xdrs, guestfs_lvcreate_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->logvol, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->volgroup, ~0))
+                return FALSE;
+        if (!xdr_int (xdrs, &objp->mbytes))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_mkfs_args (XDR *xdrs, guestfs_mkfs_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->fstype, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->device, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_sfdisk_args (XDR *xdrs, guestfs_sfdisk_args *objp)
+{
+       register int32_t *buf;
+
+
+       if (xdrs->x_op == XDR_ENCODE) {
+                if (!xdr_string (xdrs, &objp->device, ~0))
+                        return FALSE;
+               buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_int (xdrs, &objp->cyls))
+                                return FALSE;
+                        if (!xdr_int (xdrs, &objp->heads))
+                                return FALSE;
+                        if (!xdr_int (xdrs, &objp->sectors))
+                                return FALSE;
+
+               } else {
+               IXDR_PUT_LONG(buf, objp->cyls);
+               IXDR_PUT_LONG(buf, objp->heads);
+               IXDR_PUT_LONG(buf, objp->sectors);
+               }
+                if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0,
+                       sizeof (str), (xdrproc_t) xdr_str))
+                        return FALSE;
+               return TRUE;
+       } else if (xdrs->x_op == XDR_DECODE) {
+                if (!xdr_string (xdrs, &objp->device, ~0))
+                        return FALSE;
+               buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
+               if (buf == NULL) {
+                        if (!xdr_int (xdrs, &objp->cyls))
+                                return FALSE;
+                        if (!xdr_int (xdrs, &objp->heads))
+                                return FALSE;
+                        if (!xdr_int (xdrs, &objp->sectors))
+                                return FALSE;
+
+               } else {
+               objp->cyls = IXDR_GET_LONG(buf);
+               objp->heads = IXDR_GET_LONG(buf);
+               objp->sectors = IXDR_GET_LONG(buf);
+               }
+                if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0,
+                       sizeof (str), (xdrproc_t) xdr_str))
+                        return FALSE;
+        return TRUE;
+       }
+
+        if (!xdr_string (xdrs, &objp->device, ~0))
+                return FALSE;
+        if (!xdr_int (xdrs, &objp->cyls))
+                return FALSE;
+        if (!xdr_int (xdrs, &objp->heads))
+                return FALSE;
+        if (!xdr_int (xdrs, &objp->sectors))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_write_file_args (XDR *xdrs, guestfs_write_file_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->content, ~0))
+                return FALSE;
+        if (!xdr_int (xdrs, &objp->size))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_umount_args (XDR *xdrs, guestfs_umount_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->pathordevice, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_mounts_ret (XDR *xdrs, guestfs_mounts_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->devices.devices_val, (u_int *) &objp->devices.devices_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
 {
        register int32_t *buf;
index 508bc4e..fedd007 100644 (file)
@@ -337,6 +337,95 @@ struct guestfs_chown_args {
 };
 typedef struct guestfs_chown_args guestfs_chown_args;
 
+struct guestfs_exists_args {
+       char *path;
+};
+typedef struct guestfs_exists_args guestfs_exists_args;
+
+struct guestfs_exists_ret {
+       bool_t existsflag;
+};
+typedef struct guestfs_exists_ret guestfs_exists_ret;
+
+struct guestfs_is_file_args {
+       char *path;
+};
+typedef struct guestfs_is_file_args guestfs_is_file_args;
+
+struct guestfs_is_file_ret {
+       bool_t fileflag;
+};
+typedef struct guestfs_is_file_ret guestfs_is_file_ret;
+
+struct guestfs_is_dir_args {
+       char *path;
+};
+typedef struct guestfs_is_dir_args guestfs_is_dir_args;
+
+struct guestfs_is_dir_ret {
+       bool_t dirflag;
+};
+typedef struct guestfs_is_dir_ret guestfs_is_dir_ret;
+
+struct guestfs_pvcreate_args {
+       char *device;
+};
+typedef struct guestfs_pvcreate_args guestfs_pvcreate_args;
+
+struct guestfs_vgcreate_args {
+       char *volgroup;
+       struct {
+               u_int physvols_len;
+               str *physvols_val;
+       } physvols;
+};
+typedef struct guestfs_vgcreate_args guestfs_vgcreate_args;
+
+struct guestfs_lvcreate_args {
+       char *logvol;
+       char *volgroup;
+       int mbytes;
+};
+typedef struct guestfs_lvcreate_args guestfs_lvcreate_args;
+
+struct guestfs_mkfs_args {
+       char *fstype;
+       char *device;
+};
+typedef struct guestfs_mkfs_args guestfs_mkfs_args;
+
+struct guestfs_sfdisk_args {
+       char *device;
+       int cyls;
+       int heads;
+       int sectors;
+       struct {
+               u_int lines_len;
+               str *lines_val;
+       } lines;
+};
+typedef struct guestfs_sfdisk_args guestfs_sfdisk_args;
+
+struct guestfs_write_file_args {
+       char *path;
+       char *content;
+       int size;
+};
+typedef struct guestfs_write_file_args guestfs_write_file_args;
+
+struct guestfs_umount_args {
+       char *pathordevice;
+};
+typedef struct guestfs_umount_args guestfs_umount_args;
+
+struct guestfs_mounts_ret {
+       struct {
+               u_int devices_len;
+               str *devices_val;
+       } devices;
+};
+typedef struct guestfs_mounts_ret guestfs_mounts_ret;
+
 enum guestfs_procedure {
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
@@ -373,7 +462,20 @@ enum guestfs_procedure {
        GUESTFS_PROC_MKDIR_P = 33,
        GUESTFS_PROC_CHMOD = 34,
        GUESTFS_PROC_CHOWN = 35,
-       GUESTFS_PROC_dummy = 35 + 1,
+       GUESTFS_PROC_EXISTS = 36,
+       GUESTFS_PROC_IS_FILE = 37,
+       GUESTFS_PROC_IS_DIR = 38,
+       GUESTFS_PROC_PVCREATE = 39,
+       GUESTFS_PROC_VGCREATE = 40,
+       GUESTFS_PROC_LVCREATE = 41,
+       GUESTFS_PROC_MKFS = 42,
+       GUESTFS_PROC_SFDISK = 43,
+       GUESTFS_PROC_WRITE_FILE = 44,
+       GUESTFS_PROC_UMOUNT = 45,
+       GUESTFS_PROC_MOUNTS = 46,
+       GUESTFS_PROC_UMOUNT_ALL = 47,
+       GUESTFS_PROC_LVM_REMOVE_ALL = 48,
+       GUESTFS_PROC_dummy = 48 + 1,
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
@@ -459,6 +561,20 @@ extern  bool_t xdr_guestfs_mkdir_args (XDR *, guestfs_mkdir_args*);
 extern  bool_t xdr_guestfs_mkdir_p_args (XDR *, guestfs_mkdir_p_args*);
 extern  bool_t xdr_guestfs_chmod_args (XDR *, guestfs_chmod_args*);
 extern  bool_t xdr_guestfs_chown_args (XDR *, guestfs_chown_args*);
+extern  bool_t xdr_guestfs_exists_args (XDR *, guestfs_exists_args*);
+extern  bool_t xdr_guestfs_exists_ret (XDR *, guestfs_exists_ret*);
+extern  bool_t xdr_guestfs_is_file_args (XDR *, guestfs_is_file_args*);
+extern  bool_t xdr_guestfs_is_file_ret (XDR *, guestfs_is_file_ret*);
+extern  bool_t xdr_guestfs_is_dir_args (XDR *, guestfs_is_dir_args*);
+extern  bool_t xdr_guestfs_is_dir_ret (XDR *, guestfs_is_dir_ret*);
+extern  bool_t xdr_guestfs_pvcreate_args (XDR *, guestfs_pvcreate_args*);
+extern  bool_t xdr_guestfs_vgcreate_args (XDR *, guestfs_vgcreate_args*);
+extern  bool_t xdr_guestfs_lvcreate_args (XDR *, guestfs_lvcreate_args*);
+extern  bool_t xdr_guestfs_mkfs_args (XDR *, guestfs_mkfs_args*);
+extern  bool_t xdr_guestfs_sfdisk_args (XDR *, guestfs_sfdisk_args*);
+extern  bool_t xdr_guestfs_write_file_args (XDR *, guestfs_write_file_args*);
+extern  bool_t xdr_guestfs_umount_args (XDR *, guestfs_umount_args*);
+extern  bool_t xdr_guestfs_mounts_ret (XDR *, guestfs_mounts_ret*);
 extern  bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*);
 extern  bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*);
 extern  bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*);
@@ -514,6 +630,20 @@ extern bool_t xdr_guestfs_mkdir_args ();
 extern bool_t xdr_guestfs_mkdir_p_args ();
 extern bool_t xdr_guestfs_chmod_args ();
 extern bool_t xdr_guestfs_chown_args ();
+extern bool_t xdr_guestfs_exists_args ();
+extern bool_t xdr_guestfs_exists_ret ();
+extern bool_t xdr_guestfs_is_file_args ();
+extern bool_t xdr_guestfs_is_file_ret ();
+extern bool_t xdr_guestfs_is_dir_args ();
+extern bool_t xdr_guestfs_is_dir_ret ();
+extern bool_t xdr_guestfs_pvcreate_args ();
+extern bool_t xdr_guestfs_vgcreate_args ();
+extern bool_t xdr_guestfs_lvcreate_args ();
+extern bool_t xdr_guestfs_mkfs_args ();
+extern bool_t xdr_guestfs_sfdisk_args ();
+extern bool_t xdr_guestfs_write_file_args ();
+extern bool_t xdr_guestfs_umount_args ();
+extern bool_t xdr_guestfs_mounts_ret ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
index 41116b5..c547d0b 100644 (file)
@@ -262,6 +262,72 @@ struct guestfs_chown_args {
   string path<>;
 };
 
+struct guestfs_exists_args {
+  string path<>;
+};
+
+struct guestfs_exists_ret {
+  bool existsflag;
+};
+
+struct guestfs_is_file_args {
+  string path<>;
+};
+
+struct guestfs_is_file_ret {
+  bool fileflag;
+};
+
+struct guestfs_is_dir_args {
+  string path<>;
+};
+
+struct guestfs_is_dir_ret {
+  bool dirflag;
+};
+
+struct guestfs_pvcreate_args {
+  string device<>;
+};
+
+struct guestfs_vgcreate_args {
+  string volgroup<>;
+  str physvols<>;
+};
+
+struct guestfs_lvcreate_args {
+  string logvol<>;
+  string volgroup<>;
+  int mbytes;
+};
+
+struct guestfs_mkfs_args {
+  string fstype<>;
+  string device<>;
+};
+
+struct guestfs_sfdisk_args {
+  string device<>;
+  int cyls;
+  int heads;
+  int sectors;
+  str lines<>;
+};
+
+struct guestfs_write_file_args {
+  string path<>;
+  string content<>;
+  int size;
+};
+
+struct guestfs_umount_args {
+  string pathordevice<>;
+};
+
+struct guestfs_mounts_ret {
+  str devices<>;
+};
+
 enum guestfs_procedure {
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
@@ -298,6 +364,19 @@ enum guestfs_procedure {
   GUESTFS_PROC_MKDIR_P = 33,
   GUESTFS_PROC_CHMOD = 34,
   GUESTFS_PROC_CHOWN = 35,
+  GUESTFS_PROC_EXISTS = 36,
+  GUESTFS_PROC_IS_FILE = 37,
+  GUESTFS_PROC_IS_DIR = 38,
+  GUESTFS_PROC_PVCREATE = 39,
+  GUESTFS_PROC_VGCREATE = 40,
+  GUESTFS_PROC_LVCREATE = 41,
+  GUESTFS_PROC_MKFS = 42,
+  GUESTFS_PROC_SFDISK = 43,
+  GUESTFS_PROC_WRITE_FILE = 44,
+  GUESTFS_PROC_UMOUNT = 45,
+  GUESTFS_PROC_MOUNTS = 46,
+  GUESTFS_PROC_UMOUNT_ALL = 47,
+  GUESTFS_PROC_LVM_REMOVE_ALL = 48,
   GUESTFS_PROC_dummy
 };
 
diff --git a/tests.c b/tests.c
index 104e6fa..3e0fbd3 100644 (file)
--- a/tests.c
+++ b/tests.c
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
 
 #include "guestfs.h"
 
+static guestfs_h *g;
+static int suppress_error = 0;
+
+static void print_error (guestfs_h *g, void *data, const char *msg)
+{
+  if (!suppress_error)
+    fprintf (stderr, "%s\n", msg);
+}
+
+static void print_strings (char * const * const argv)
+{
+  int argc;
+
+  for (argc = 0; argv[argc] != NULL; ++argc)
+    printf ("\t%s\n", argv[argc]);
+}
+
+static int test_mount_0 (void)
+{
+  /* InitNone for mount (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutput for mount (0) */
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/new", "new file contents", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *r;
+    suppress_error = 0;
+    r = guestfs_cat (g, "/new");
+    if (r == NULL)
+      return -1;
+    if (strcmp (r, "new file contents") != 0) {
+      fprintf (stderr, "test_mount_0: expected \"new file contents\" but got \"%s\"\n", r);
+      return -1;
+    }
+    free (r);
+  }
+  return 0;
+}
+
+static int test_sync_0 (void)
+{
+  /* InitNone for sync (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestRun for sync (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_sync (g);
+    if (r == -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_touch_0 (void)
+{
+  /* InitEmpty for touch (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for touch (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_exists (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_touch_0: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_cat_0 (void)
+{
+  /* InitEmpty for cat (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutput for cat (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/new", "new file contents", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *r;
+    suppress_error = 0;
+    r = guestfs_cat (g, "/new");
+    if (r == NULL)
+      return -1;
+    if (strcmp (r, "new file contents") != 0) {
+      fprintf (stderr, "test_cat_0: expected \"new file contents\" but got \"%s\"\n", r);
+      return -1;
+    }
+    free (r);
+  }
+  return 0;
+}
+
+static int test_ls_0 (void)
+{
+  /* InitEmpty for ls (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for ls (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/newer");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/newest");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_ls (g, "/");
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_ls_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "lost+found") != 0) {
+      fprintf (stderr, "test_ls_0: expected \"lost+found\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_ls_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "new") != 0) {
+      fprintf (stderr, "test_ls_0: expected \"new\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_ls_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "newer") != 0) {
+      fprintf (stderr, "test_ls_0: expected \"newer\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (!r[3]) {
+      fprintf (stderr, "test_ls_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[3], "newest") != 0) {
+      fprintf (stderr, "test_ls_0: expected \"newest\" but got \"%s\"\n", r[3]);
+      return -1;
+    }
+    if (r[4] != NULL) {
+      fprintf (stderr, "test_ls_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_list_devices_0 (void)
+{
+  /* InitNone for list_devices (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for list_devices (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_list_devices (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_list_devices_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda") != 0) {
+      fprintf (stderr, "test_list_devices_0: expected \"/dev/sda\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_list_devices_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "/dev/sdb") != 0) {
+      fprintf (stderr, "test_list_devices_0: expected \"/dev/sdb\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_list_devices_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "/dev/sdc") != 0) {
+      fprintf (stderr, "test_list_devices_0: expected \"/dev/sdc\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (r[3] != NULL) {
+      fprintf (stderr, "test_list_devices_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_list_partitions_0 (void)
+{
+  /* InitEmpty for list_partitions (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for list_partitions (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_list_partitions (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_list_partitions_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_list_partitions_0: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (r[1] != NULL) {
+      fprintf (stderr, "test_list_partitions_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_list_partitions_1 (void)
+{
+  /* InitNone for list_partitions (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for list_partitions (1) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_list_partitions (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_list_partitions_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_list_partitions_1: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_list_partitions_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "/dev/sda2") != 0) {
+      fprintf (stderr, "test_list_partitions_1: expected \"/dev/sda2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_list_partitions_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "/dev/sda3") != 0) {
+      fprintf (stderr, "test_list_partitions_1: expected \"/dev/sda3\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (r[3] != NULL) {
+      fprintf (stderr, "test_list_partitions_1: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_pvs_0 (void)
+{
+  /* InitEmptyLVM for pvs (0): create ext2 on /dev/VG/LV */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV", "VG", 8);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/VG/LV");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/VG/LV", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for pvs (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_pvs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_pvs_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_pvs_0: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (r[1] != NULL) {
+      fprintf (stderr, "test_pvs_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_pvs_1 (void)
+{
+  /* InitNone for pvs (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for pvs (1) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda3");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_pvs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_pvs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_pvs_1: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_pvs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "/dev/sda2") != 0) {
+      fprintf (stderr, "test_pvs_1: expected \"/dev/sda2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_pvs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "/dev/sda3") != 0) {
+      fprintf (stderr, "test_pvs_1: expected \"/dev/sda3\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (r[3] != NULL) {
+      fprintf (stderr, "test_pvs_1: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_vgs_0 (void)
+{
+  /* InitEmptyLVM for vgs (0): create ext2 on /dev/VG/LV */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV", "VG", 8);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/VG/LV");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/VG/LV", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for vgs (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_vgs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_vgs_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "VG") != 0) {
+      fprintf (stderr, "test_vgs_0: expected \"VG\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (r[1] != NULL) {
+      fprintf (stderr, "test_vgs_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_vgs_1 (void)
+{
+  /* InitNone for vgs (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for vgs (1) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda3");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      "/dev/sda2",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG1", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda3",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG2", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_vgs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_vgs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "VG1") != 0) {
+      fprintf (stderr, "test_vgs_1: expected \"VG1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_vgs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "VG2") != 0) {
+      fprintf (stderr, "test_vgs_1: expected \"VG2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (r[2] != NULL) {
+      fprintf (stderr, "test_vgs_1: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_lvs_0 (void)
+{
+  /* InitEmptyLVM for lvs (0): create ext2 on /dev/VG/LV */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV", "VG", 8);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/VG/LV");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/VG/LV", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for lvs (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_lvs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_lvs_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/VG/LV") != 0) {
+      fprintf (stderr, "test_lvs_0: expected \"/dev/VG/LV\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (r[1] != NULL) {
+      fprintf (stderr, "test_lvs_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_lvs_1 (void)
+{
+  /* InitNone for lvs (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for lvs (1) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda3");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      "/dev/sda2",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG1", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda3",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG2", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV1", "VG1", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV2", "VG1", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV3", "VG2", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_lvs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_lvs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/VG1/LV1") != 0) {
+      fprintf (stderr, "test_lvs_1: expected \"/dev/VG1/LV1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_lvs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "/dev/VG1/LV2") != 0) {
+      fprintf (stderr, "test_lvs_1: expected \"/dev/VG1/LV2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_lvs_1: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "/dev/VG2/LV3") != 0) {
+      fprintf (stderr, "test_lvs_1: expected \"/dev/VG2/LV3\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (r[3] != NULL) {
+      fprintf (stderr, "test_lvs_1: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_pvs_full_0 (void)
+{
+  /* InitEmptyLVM for pvs_full (0): create ext2 on /dev/VG/LV */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV", "VG", 8);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/VG/LV");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/VG/LV", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputLength for pvs_full (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_pvs (g);
+    if (r == NULL)
+      return -1;
+    int j;
+    for (j = 0; j < 1; ++j)
+      if (r[j] == NULL) {
+        fprintf (stderr, "test_pvs_full_0: short list returned\n");
+        print_strings (r);
+        return -1;
+      }
+    if (r[j] != NULL) {
+      fprintf (stderr, "test_pvs_full_0: long list returned\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_vgs_full_0 (void)
+{
+  /* InitEmptyLVM for vgs_full (0): create ext2 on /dev/VG/LV */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV", "VG", 8);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/VG/LV");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/VG/LV", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputLength for vgs_full (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_pvs (g);
+    if (r == NULL)
+      return -1;
+    int j;
+    for (j = 0; j < 1; ++j)
+      if (r[j] == NULL) {
+        fprintf (stderr, "test_vgs_full_0: short list returned\n");
+        print_strings (r);
+        return -1;
+      }
+    if (r[j] != NULL) {
+      fprintf (stderr, "test_vgs_full_0: long list returned\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_lvs_full_0 (void)
+{
+  /* InitEmptyLVM for lvs_full (0): create ext2 on /dev/VG/LV */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV", "VG", 8);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/VG/LV");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/VG/LV", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputLength for lvs_full (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_pvs (g);
+    if (r == NULL)
+      return -1;
+    int j;
+    for (j = 0; j < 1; ++j)
+      if (r[j] == NULL) {
+        fprintf (stderr, "test_lvs_full_0: short list returned\n");
+        print_strings (r);
+        return -1;
+      }
+    if (r[j] != NULL) {
+      fprintf (stderr, "test_lvs_full_0: long list returned\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_read_lines_0 (void)
+{
+  /* InitEmpty for read_lines (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for read_lines (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/new", "line1\r\nline2\nline3", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_read_lines (g, "/new");
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_read_lines_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "line1") != 0) {
+      fprintf (stderr, "test_read_lines_0: expected \"line1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_read_lines_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "line2") != 0) {
+      fprintf (stderr, "test_read_lines_0: expected \"line2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_read_lines_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "line3") != 0) {
+      fprintf (stderr, "test_read_lines_0: expected \"line3\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (r[3] != NULL) {
+      fprintf (stderr, "test_read_lines_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_read_lines_1 (void)
+{
+  /* InitEmpty for read_lines (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for read_lines (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/new", "", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_read_lines (g, "/new");
+    if (r == NULL)
+      return -1;
+    if (r[0] != NULL) {
+      fprintf (stderr, "test_read_lines_1: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_rm_0 (void)
+{
+  /* InitEmpty for rm (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestRun for rm (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_rm (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_rm_1 (void)
+{
+  /* InitEmpty for rm (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestLastFail for rm (1) */
+  {
+    int r;
+    suppress_error = 1;
+    r = guestfs_rm (g, "/new");
+    if (r != -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_rm_2 (void)
+{
+  /* InitEmpty for rm (2): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestLastFail for rm (2) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 1;
+    r = guestfs_rm (g, "/new");
+    if (r != -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_rmdir_0 (void)
+{
+  /* InitEmpty for rmdir (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestRun for rmdir (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_rmdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_rmdir_1 (void)
+{
+  /* InitEmpty for rmdir (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestLastFail for rmdir (1) */
+  {
+    int r;
+    suppress_error = 1;
+    r = guestfs_rmdir (g, "/new");
+    if (r != -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_rmdir_2 (void)
+{
+  /* InitEmpty for rmdir (2): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestLastFail for rmdir (2) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 1;
+    r = guestfs_rmdir (g, "/new");
+    if (r != -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_rm_rf_0 (void)
+{
+  /* InitEmpty for rm_rf (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputFalse for rm_rf (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new/foo");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new/foo/bar");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_rm_rf (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_exists (g, "/new");
+    if (r == -1)
+      return -1;
+    if (r) {
+      fprintf (stderr, "test_rm_rf_0: expected false, got true\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_mkdir_0 (void)
+{
+  /* InitEmpty for mkdir (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for mkdir (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_dir (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_mkdir_0: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_mkdir_1 (void)
+{
+  /* InitEmpty for mkdir (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestLastFail for mkdir (1) */
+  {
+    int r;
+    suppress_error = 1;
+    r = guestfs_mkdir (g, "/new/foo/bar");
+    if (r != -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_mkdir_p_0 (void)
+{
+  /* InitEmpty for mkdir_p (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for mkdir_p (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir_p (g, "/new/foo/bar");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_dir (g, "/new/foo/bar");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_mkdir_p_0: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_mkdir_p_1 (void)
+{
+  /* InitEmpty for mkdir_p (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for mkdir_p (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir_p (g, "/new/foo/bar");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_dir (g, "/new/foo");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_mkdir_p_1: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_mkdir_p_2 (void)
+{
+  /* InitEmpty for mkdir_p (2): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for mkdir_p (2) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir_p (g, "/new/foo/bar");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_dir (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_mkdir_p_2: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_exists_0 (void)
+{
+  /* InitEmpty for exists (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for exists (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_exists (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_exists_0: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_exists_1 (void)
+{
+  /* InitEmpty for exists (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for exists (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_exists (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_exists_1: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_is_file_0 (void)
+{
+  /* InitEmpty for is_file (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for is_file (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_file (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_is_file_0: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_is_file_1 (void)
+{
+  /* InitEmpty for is_file (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputFalse for is_file (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_file (g, "/new");
+    if (r == -1)
+      return -1;
+    if (r) {
+      fprintf (stderr, "test_is_file_1: expected false, got true\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_is_dir_0 (void)
+{
+  /* InitEmpty for is_dir (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputFalse for is_dir (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_touch (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_dir (g, "/new");
+    if (r == -1)
+      return -1;
+    if (r) {
+      fprintf (stderr, "test_is_dir_0: expected false, got true\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_is_dir_1 (void)
+{
+  /* InitEmpty for is_dir (1): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputTrue for is_dir (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkdir (g, "/new");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_is_dir (g, "/new");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_is_dir_1: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_pvcreate_0 (void)
+{
+  /* InitNone for pvcreate (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for pvcreate (0) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda3");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_pvs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_pvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_pvcreate_0: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_pvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "/dev/sda2") != 0) {
+      fprintf (stderr, "test_pvcreate_0: expected \"/dev/sda2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_pvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "/dev/sda3") != 0) {
+      fprintf (stderr, "test_pvcreate_0: expected \"/dev/sda3\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (r[3] != NULL) {
+      fprintf (stderr, "test_pvcreate_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_vgcreate_0 (void)
+{
+  /* InitNone for vgcreate (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for vgcreate (0) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda3");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      "/dev/sda2",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG1", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda3",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG2", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_vgs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_vgcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "VG1") != 0) {
+      fprintf (stderr, "test_vgcreate_0: expected \"VG1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_vgcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "VG2") != 0) {
+      fprintf (stderr, "test_vgcreate_0: expected \"VG2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (r[2] != NULL) {
+      fprintf (stderr, "test_vgcreate_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_lvcreate_0 (void)
+{
+  /* InitNone for lvcreate (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for lvcreate (0) */
+  {
+    char *lines[] = {
+      ",10",
+      ",20",
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_pvcreate (g, "/dev/sda3");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda1",
+      "/dev/sda2",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG1", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *physvols[] = {
+      "/dev/sda3",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_vgcreate (g, "VG2", physvols);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV1", "VG1", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV2", "VG1", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV3", "VG2", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV4", "VG2", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvcreate (g, "LV5", "VG2", 50);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_lvs (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_lvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/VG1/LV1") != 0) {
+      fprintf (stderr, "test_lvcreate_0: expected \"/dev/VG1/LV1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (!r[1]) {
+      fprintf (stderr, "test_lvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[1], "/dev/VG1/LV2") != 0) {
+      fprintf (stderr, "test_lvcreate_0: expected \"/dev/VG1/LV2\" but got \"%s\"\n", r[1]);
+      return -1;
+    }
+    if (!r[2]) {
+      fprintf (stderr, "test_lvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[2], "/dev/VG2/LV3") != 0) {
+      fprintf (stderr, "test_lvcreate_0: expected \"/dev/VG2/LV3\" but got \"%s\"\n", r[2]);
+      return -1;
+    }
+    if (!r[3]) {
+      fprintf (stderr, "test_lvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[3], "/dev/VG2/LV4") != 0) {
+      fprintf (stderr, "test_lvcreate_0: expected \"/dev/VG2/LV4\" but got \"%s\"\n", r[3]);
+      return -1;
+    }
+    if (!r[4]) {
+      fprintf (stderr, "test_lvcreate_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[4], "/dev/VG2/LV5") != 0) {
+      fprintf (stderr, "test_lvcreate_0: expected \"/dev/VG2/LV5\" but got \"%s\"\n", r[4]);
+      return -1;
+    }
+    if (r[5] != NULL) {
+      fprintf (stderr, "test_lvcreate_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_mkfs_0 (void)
+{
+  /* InitNone for mkfs (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutput for mkfs (0) */
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/new", "new file contents", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *r;
+    suppress_error = 0;
+    r = guestfs_cat (g, "/new");
+    if (r == NULL)
+      return -1;
+    if (strcmp (r, "new file contents") != 0) {
+      fprintf (stderr, "test_mkfs_0: expected \"new file contents\" but got \"%s\"\n", r);
+      return -1;
+    }
+    free (r);
+  }
+  return 0;
+}
+
+static int test_write_file_0 (void)
+{
+  /* InitNone for write_file (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutput for write_file (0) */
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/new", "new file contents", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *r;
+    suppress_error = 0;
+    r = guestfs_cat (g, "/new");
+    if (r == NULL)
+      return -1;
+    if (strcmp (r, "new file contents") != 0) {
+      fprintf (stderr, "test_write_file_0: expected \"new file contents\" but got \"%s\"\n", r);
+      return -1;
+    }
+    free (r);
+  }
+  return 0;
+}
+
+static int test_umount_0 (void)
+{
+  /* InitNone for umount (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for umount (0) */
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_mounts (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_umount_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_umount_0: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (r[1] != NULL) {
+      fprintf (stderr, "test_umount_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_umount_1 (void)
+{
+  /* InitNone for umount (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for umount (1) */
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount (g, "/");
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_mounts (g);
+    if (r == NULL)
+      return -1;
+    if (r[0] != NULL) {
+      fprintf (stderr, "test_umount_1: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_mounts_0 (void)
+{
+  /* InitEmpty for mounts (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for mounts (0) */
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_mounts (g);
+    if (r == NULL)
+      return -1;
+    if (!r[0]) {
+      fprintf (stderr, "test_mounts_0: short list returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    if (strcmp (r[0], "/dev/sda1") != 0) {
+      fprintf (stderr, "test_mounts_0: expected \"/dev/sda1\" but got \"%s\"\n", r[0]);
+      return -1;
+    }
+    if (r[1] != NULL) {
+      fprintf (stderr, "test_mounts_0: extra elements returned from command\n");
+      print_strings (r);
+      return -1;
+    }
+    for (i = 0; r[i] != NULL; ++i)
+      free (r[i]);
+    free (r);
+  }
+  return 0;
+}
+
+static int test_umount_all_0 (void)
+{
+  /* InitEmpty for umount_all (0): create ext2 on /dev/sda1 */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_lvm_remove_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char *lines[] = {
+      ",",
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, "/dev/sda1", "/");
+    if (r == -1)
+      return -1;
+  }
+  /* TestOutputList for umount_all (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_umount_all (g);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char **r;
+    int i;
+    suppress_error = 0;
+    r = guestfs_mounts (g);
+    if (r ==&nbs