syntax-check: expand TABs in generator.ml
[libguestfs.git] / src / generator.ml
old mode 100755 (executable)
new mode 100644 (file)
index 189b1fd..c261ea2
@@ -142,6 +142,7 @@ and argt =
   | DeviceList of string(* list of Device names (each cannot be NULL) *)
   | Bool of string     (* boolean *)
   | Int of string      (* int (smallish ints, signed, <= 31 bits) *)
   | DeviceList of string(* list of Device names (each cannot be NULL) *)
   | Bool of string     (* boolean *)
   | Int of string      (* int (smallish ints, signed, <= 31 bits) *)
+  | Int64 of string    (* any 64 bit int *)
     (* These are treated as filenames (simple string parameters) in
      * the C API and bindings.  But in the RPC protocol, we transfer
      * the actual file content up to or down from the daemon.
     (* These are treated as filenames (simple string parameters) in
      * the C API and bindings.  But in the RPC protocol, we transfer
      * the actual file content up to or down from the daemon.
@@ -178,13 +179,13 @@ type flags =
  *
  * Note that the test environment has 3 block devices, of size 500MB,
  * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc), and
  *
  * Note that the test environment has 3 block devices, of size 500MB,
  * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc), and
- * a fourth squashfs block device with some known files on it (/dev/sdd).
+ * a fourth ISO block device with some known files on it (/dev/sdd).
  *
  * Note for partitioning purposes, the 500MB device has 1015 cylinders.
  * Number of cylinders was 63 for IDE emulated disks with precisely
  * the same size.  How exactly this is calculated is a mystery.
  *
  *
  * Note for partitioning purposes, the 500MB device has 1015 cylinders.
  * Number of cylinders was 63 for IDE emulated disks with precisely
  * the same size.  How exactly this is calculated is a mystery.
  *
- * The squashfs block device (/dev/sdd) comes from images/test.sqsh.
+ * The ISO block device (/dev/sdd) comes from images/test.iso.
  *
  * To be able to run the tests in a reasonable amount of time,
  * the virtual machine and block devices are reused between tests.
  *
  * To be able to run the tests in a reasonable amount of time,
  * the virtual machine and block devices are reused between tests.
@@ -326,10 +327,10 @@ and test_init =
      *)
   | InitBasicFSonLVM
 
      *)
   | InitBasicFSonLVM
 
-    (* /dev/sdd (the squashfs, see images/ directory in source)
+    (* /dev/sdd (the ISO, see images/ directory in source)
      * is mounted on /
      *)
      * is mounted on /
      *)
-  | InitSquashFS
+  | InitISOFS
 
 (* Sequence of commands for testing. *)
 and seq = cmd list
 
 (* Sequence of commands for testing. *)
 and seq = cmd list
@@ -364,6 +365,7 @@ let test_all_args = [
   StringList "strlist";
   Bool "b";
   Int "integer";
   StringList "strlist";
   Bool "b";
   Int "integer";
+  Int64 "integer64";
   FileIn "filein";
   FileOut "fileout";
 ]
   FileIn "filein";
   FileOut "fileout";
 ]
@@ -440,13 +442,18 @@ You should call this after configuring the handle
 
   ("wait_ready", (RErr, []), -1, [NotInFish],
    [],
 
   ("wait_ready", (RErr, []), -1, [NotInFish],
    [],
-   "wait until the qemu subprocess launches",
+   "wait until the qemu subprocess launches (no op)",
    "\
    "\
-Internally libguestfs is implemented by running a virtual machine
-using L<qemu(1)>.
+This function is a no op.
+
+In versions of the API E<lt> 1.0.71 you had to call this function
+just after calling C<guestfs_launch> to wait for the launch
+to complete.  However this is no longer necessary because
+C<guestfs_launch> now does the waiting.
 
 
-You should call this after C<guestfs_launch> to wait for the launch
-to complete.");
+If you see any calls to this function in code then you can just
+remove them, unless you want to retain compatibility with older
+versions of the API.");
 
   ("kill_subprocess", (RErr, []), -1, [],
    [],
 
   ("kill_subprocess", (RErr, []), -1, [],
    [],
@@ -680,34 +687,6 @@ only useful for printing debug and internal error messages.
 
 For more information on states, see L<guestfs(3)>.");
 
 
 For more information on states, see L<guestfs(3)>.");
 
-  ("set_busy", (RErr, []), -1, [NotInFish],
-   [],
-   "set state to busy",
-   "\
-This sets the state to C<BUSY>.  This is only used when implementing
-actions using the low-level API.
-
-For more information on states, see L<guestfs(3)>.");
-
-  ("set_ready", (RErr, []), -1, [NotInFish],
-   [],
-   "set state to ready",
-   "\
-This sets the state to C<READY>.  This is only used when implementing
-actions using the low-level API.
-
-For more information on states, see L<guestfs(3)>.");
-
-  ("end_busy", (RErr, []), -1, [NotInFish],
-   [],
-   "leave the busy state",
-   "\
-This sets the state to C<READY>, or if in C<CONFIG> then it leaves the
-state as is.  This is only used when implementing
-actions using the low-level API.
-
-For more information on states, see L<guestfs(3)>.");
-
   ("set_memsize", (RErr, [Int "memsize"]), -1, [FishAlias "memsize"],
    [InitNone, Always, TestOutputInt (
       [["set_memsize"; "500"];
   ("set_memsize", (RErr, [Int "memsize"]), -1, [FishAlias "memsize"],
    [InitNone, Always, TestOutputInt (
       [["set_memsize"; "500"];
@@ -805,6 +784,83 @@ is passed to the appliance at boot time.  See C<guestfs_set_selinux>.
 For more information on the architecture of libguestfs,
 see L<guestfs(3)>.");
 
 For more information on the architecture of libguestfs,
 see L<guestfs(3)>.");
 
+  ("set_trace", (RErr, [Bool "trace"]), -1, [FishAlias "trace"],
+   [InitNone, Always, TestOutputFalse (
+      [["set_trace"; "false"];
+       ["get_trace"]])],
+   "enable or disable command traces",
+   "\
+If the command trace flag is set to 1, then commands are
+printed on stdout before they are executed in a format
+which is very similar to the one used by guestfish.  In
+other words, you can run a program with this enabled, and
+you will get out a script which you can feed to guestfish
+to perform the same set of actions.
+
+If you want to trace C API calls into libguestfs (and
+other libraries) then possibly a better way is to use
+the external ltrace(1) command.
+
+Command traces are disabled unless the environment variable
+C<LIBGUESTFS_TRACE> is defined and set to C<1>.");
+
+  ("get_trace", (RBool "trace", []), -1, [],
+   [],
+   "get command trace enabled flag",
+   "\
+Return the command trace flag.");
+
+  ("set_direct", (RErr, [Bool "direct"]), -1, [FishAlias "direct"],
+   [InitNone, Always, TestOutputFalse (
+      [["set_direct"; "false"];
+       ["get_direct"]])],
+   "enable or disable direct appliance mode",
+   "\
+If the direct appliance mode flag is enabled, then stdin and
+stdout are passed directly through to the appliance once it
+is launched.
+
+One consequence of this is that log messages aren't caught
+by the library and handled by C<guestfs_set_log_message_callback>,
+but go straight to stdout.
+
+You probably don't want to use this unless you know what you
+are doing.
+
+The default is disabled.");
+
+  ("get_direct", (RBool "direct", []), -1, [],
+   [],
+   "get direct appliance mode flag",
+   "\
+Return the direct appliance mode flag.");
+
+  ("set_recovery_proc", (RErr, [Bool "recoveryproc"]), -1, [FishAlias "recovery-proc"],
+   [InitNone, Always, TestOutputTrue (
+      [["set_recovery_proc"; "true"];
+       ["get_recovery_proc"]])],
+   "enable or disable the recovery process",
+   "\
+If this is called with the parameter C<false> then
+C<guestfs_launch> does not create a recovery process.  The
+purpose of the recovery process is to stop runaway qemu
+processes in the case where the main program aborts abruptly.
+
+This only has any effect if called before C<guestfs_launch>,
+and the default is true.
+
+About the only time when you would want to disable this is
+if the main process will fork itself into the background
+(\"daemonize\" itself).  In this case the recovery process
+thinks that the main program has disappeared and so kills
+qemu, which is not very helpful.");
+
+  ("get_recovery_proc", (RBool "recoveryproc", []), -1, [],
+   [],
+   "get recovery process enabled flag",
+   "\
+Return the recovery process enabled flag.");
+
 ]
 
 (* daemon_functions are any functions which cause some action
 ]
 
 (* daemon_functions are any functions which cause some action
@@ -814,7 +870,7 @@ see L<guestfs(3)>.");
 let daemon_functions = [
   ("mount", (RErr, [Device "device"; String "mountpoint"]), 1, [],
    [InitEmpty, Always, TestOutput (
 let daemon_functions = [
   ("mount", (RErr, [Device "device"; String "mountpoint"]), 1, [],
    [InitEmpty, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -859,7 +915,7 @@ update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.");
 
   ("cat", (RString "content", [Pathname "path"]), 4, [ProtocolLimitWarning],
 to create a new zero-length file.");
 
   ("cat", (RString "content", [Pathname "path"]), 4, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutput (
+   [InitISOFS, Always, TestOutput (
       [["cat"; "/known-2"]], "abcdef\n")],
    "list the contents of a file",
    "\
       [["cat"; "/known-2"]], "abcdef\n")],
    "list the contents of a file",
    "\
@@ -1007,9 +1063,9 @@ 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", [Pathname "path"]), 15, [],
 of the L<lvs(8)> command.  The \"full\" version includes all fields.");
 
   ("read_lines", (RStringList "lines", [Pathname "path"]), 15, [],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]);
       [["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["read_lines"; "/empty"]], [])],
    "read file as lines",
    "\
       [["read_lines"; "/empty"]], [])],
    "read file as lines",
    "\
@@ -1271,9 +1327,9 @@ names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).");
 
   ("exists", (RBool "existsflag", [Pathname "path"]), 36, [],
 yourself (Augeas support makes this relatively easy).");
 
   ("exists", (RBool "existsflag", [Pathname "path"]), 36, [],
-   [InitSquashFS, Always, TestOutputTrue (
+   [InitISOFS, Always, TestOutputTrue (
       [["exists"; "/empty"]]);
       [["exists"; "/empty"]]);
-    InitSquashFS, Always, TestOutputTrue (
+    InitISOFS, Always, TestOutputTrue (
       [["exists"; "/directory"]])],
    "test if file or directory exists",
    "\
       [["exists"; "/directory"]])],
    "test if file or directory exists",
    "\
@@ -1283,9 +1339,9 @@ This returns C<true> if and only if there is a file, directory
 See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>.");
 
   ("is_file", (RBool "fileflag", [Pathname "path"]), 37, [],
 See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>.");
 
   ("is_file", (RBool "fileflag", [Pathname "path"]), 37, [],
-   [InitSquashFS, Always, TestOutputTrue (
+   [InitISOFS, Always, TestOutputTrue (
       [["is_file"; "/known-1"]]);
       [["is_file"; "/known-1"]]);
-    InitSquashFS, Always, TestOutputFalse (
+    InitISOFS, Always, TestOutputFalse (
       [["is_file"; "/directory"]])],
    "test if file exists",
    "\
       [["is_file"; "/directory"]])],
    "test if file exists",
    "\
@@ -1296,9 +1352,9 @@ other objects like directories.
 See also C<guestfs_stat>.");
 
   ("is_dir", (RBool "dirflag", [Pathname "path"]), 38, [],
 See also C<guestfs_stat>.");
 
   ("is_dir", (RBool "dirflag", [Pathname "path"]), 38, [],
-   [InitSquashFS, Always, TestOutputFalse (
+   [InitISOFS, Always, TestOutputFalse (
       [["is_dir"; "/known-3"]]);
       [["is_dir"; "/known-3"]]);
-    InitSquashFS, Always, TestOutputTrue (
+    InitISOFS, Always, TestOutputTrue (
       [["is_dir"; "/directory"]])],
    "test if file exists",
    "\
       [["is_dir"; "/directory"]])],
    "test if file exists",
    "\
@@ -1358,7 +1414,7 @@ on the volume group C<volgroup>, with C<size> megabytes.");
 
   ("mkfs", (RErr, [String "fstype"; Device "device"]), 42, [],
    [InitEmpty, Always, TestOutput (
 
   ("mkfs", (RErr, [String "fstype"; Device "device"]), 42, [],
    [InitEmpty, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -1395,7 +1451,8 @@ 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).
 
 pass C<lines> as a single element list, when the single element being
 the string C<,> (comma).
 
-See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>");
+See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>,
+C<guestfs_part_init>");
 
   ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
    [InitBasicFS, Always, TestOutput (
 
   ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
    [InitBasicFS, Always, TestOutput (
@@ -1433,12 +1490,12 @@ use C<guestfs_upload>.");
 
   ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
    [InitEmpty, Always, TestOutputListOfDevices (
 
   ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
    [InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["mounts"]], ["/dev/sda1"]);
     InitEmpty, Always, TestOutputList (
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["mounts"]], ["/dev/sda1"]);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["umount"; "/"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["umount"; "/"];
@@ -1493,11 +1550,11 @@ This command removes all LVM logical volumes, volume groups
 and physical volumes.");
 
   ("file", (RString "description", [Dev_or_Path "path"]), 49, [],
 and physical volumes.");
 
   ("file", (RString "description", [Dev_or_Path "path"]), 49, [],
-   [InitSquashFS, Always, TestOutput (
+   [InitISOFS, Always, TestOutput (
       [["file"; "/empty"]], "empty");
       [["file"; "/empty"]], "empty");
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["file"; "/known-1"]], "ASCII text");
       [["file"; "/known-1"]], "ASCII text");
-    InitSquashFS, Always, TestLastFail (
+    InitISOFS, Always, TestLastFail (
       [["file"; "/notexists"]])],
    "determine file type",
    "\
       [["file"; "/notexists"]])],
    "determine file type",
    "\
@@ -1646,7 +1703,7 @@ result into a list of lines.
 See also: C<guestfs_sh_lines>");
 
   ("stat", (RStruct ("statbuf", "stat"), [Pathname "path"]), 52, [],
 See also: C<guestfs_sh_lines>");
 
   ("stat", (RStruct ("statbuf", "stat"), [Pathname "path"]), 52, [],
-   [InitSquashFS, Always, TestOutputStruct (
+   [InitISOFS, Always, TestOutputStruct (
       [["stat"; "/empty"]], [CompareWithInt ("size", 0)])],
    "get file information",
    "\
       [["stat"; "/empty"]], [CompareWithInt ("size", 0)])],
    "get file information",
    "\
@@ -1655,7 +1712,7 @@ Returns file information for the given C<path>.
 This is the same as the C<stat(2)> system call.");
 
   ("lstat", (RStruct ("statbuf", "stat"), [Pathname "path"]), 53, [],
 This is the same as the C<stat(2)> system call.");
 
   ("lstat", (RStruct ("statbuf", "stat"), [Pathname "path"]), 53, [],
-   [InitSquashFS, Always, TestOutputStruct (
+   [InitISOFS, Always, TestOutputStruct (
       [["lstat"; "/empty"]], [CompareWithInt ("size", 0)])],
    "get file information for a symbolic link",
    "\
       [["lstat"; "/empty"]], [CompareWithInt ("size", 0)])],
    "get file information for a symbolic link",
    "\
@@ -1668,8 +1725,8 @@ refers to.
 This is the same as the C<lstat(2)> system call.");
 
   ("statvfs", (RStruct ("statbuf", "statvfs"), [Pathname "path"]), 54, [],
 This is the same as the C<lstat(2)> system call.");
 
   ("statvfs", (RStruct ("statbuf", "statvfs"), [Pathname "path"]), 54, [],
-   [InitSquashFS, Always, TestOutputStruct (
-      [["statvfs"; "/"]], [CompareWithInt ("namemax", 256)])],
+   [InitISOFS, Always, TestOutputStruct (
+      [["statvfs"; "/"]], [CompareWithInt ("namemax", 255)])],
    "get file system statistics",
    "\
 Returns file system statistics for any mounted file system.
    "get file system statistics",
    "\
 Returns file system statistics for any mounted file system.
@@ -1834,21 +1891,21 @@ C<filename> can also be a named pipe.
 See also C<guestfs_upload>, C<guestfs_cat>.");
 
   ("checksum", (RString "checksum", [String "csumtype"; Pathname "path"]), 68, [],
 See also C<guestfs_upload>, C<guestfs_cat>.");
 
   ("checksum", (RString "checksum", [String "csumtype"; Pathname "path"]), 68, [],
-   [InitSquashFS, Always, TestOutput (
+   [InitISOFS, Always, TestOutput (
       [["checksum"; "crc"; "/known-3"]], "2891671662");
       [["checksum"; "crc"; "/known-3"]], "2891671662");
-    InitSquashFS, Always, TestLastFail (
+    InitISOFS, Always, TestLastFail (
       [["checksum"; "crc"; "/notexists"]]);
       [["checksum"; "crc"; "/notexists"]]);
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["checksum"; "md5"; "/known-3"]], "46d6ca27ee07cdc6fa99c2e138cc522c");
       [["checksum"; "md5"; "/known-3"]], "46d6ca27ee07cdc6fa99c2e138cc522c");
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["checksum"; "sha1"; "/known-3"]], "b7ebccc3ee418311091c3eda0a45b83c0a770f15");
       [["checksum"; "sha1"; "/known-3"]], "b7ebccc3ee418311091c3eda0a45b83c0a770f15");
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["checksum"; "sha224"; "/known-3"]], "d2cd1774b28f3659c14116be0a6dc2bb5c4b350ce9cd5defac707741");
       [["checksum"; "sha224"; "/known-3"]], "d2cd1774b28f3659c14116be0a6dc2bb5c4b350ce9cd5defac707741");
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["checksum"; "sha256"; "/known-3"]], "75bb71b90cd20cb13f86d2bea8dad63ac7194e7517c3b52b8d06ff52d3487d30");
       [["checksum"; "sha256"; "/known-3"]], "75bb71b90cd20cb13f86d2bea8dad63ac7194e7517c3b52b8d06ff52d3487d30");
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["checksum"; "sha384"; "/known-3"]], "5fa7883430f357b5d7b7271d3a1d2872b51d73cba72731de6863d3dea55f30646af2799bef44d5ea776a5ec7941ac640");
       [["checksum"; "sha384"; "/known-3"]], "5fa7883430f357b5d7b7271d3a1d2872b51d73cba72731de6863d3dea55f30646af2799bef44d5ea776a5ec7941ac640");
-    InitSquashFS, Always, TestOutput (
+    InitISOFS, Always, TestOutput (
       [["checksum"; "sha512"; "/known-3"]], "2794062c328c6b216dca90443b7f7134c5f40e56bd0ed7853123275a09982a6f992e6ca682f9d2fba34a4c5e870d8fe077694ff831e3032a004ee077e00603f6")],
    "compute MD5, SHAx or CRC checksum of file",
    "\
       [["checksum"; "sha512"; "/known-3"]], "2794062c328c6b216dca90443b7f7134c5f40e56bd0ed7853123275a09982a6f992e6ca682f9d2fba34a4c5e870d8fe077694ff831e3032a004ee077e00603f6")],
    "compute MD5, SHAx or CRC checksum of file",
    "\
@@ -1978,7 +2035,7 @@ to find out what you can do.");
 
   ("lvremove", (RErr, [Device "device"]), 77, [],
    [InitEmpty, Always, TestOutputList (
 
   ("lvremove", (RErr, [Device "device"]), 77, [],
    [InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -1986,7 +2043,7 @@ to find out what you can do.");
        ["lvremove"; "/dev/VG/LV1"];
        ["lvs"]], ["/dev/VG/LV2"]);
     InitEmpty, Always, TestOutputList (
        ["lvremove"; "/dev/VG/LV1"];
        ["lvs"]], ["/dev/VG/LV2"]);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -1994,7 +2051,7 @@ to find out what you can do.");
        ["lvremove"; "/dev/VG"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputList (
        ["lvremove"; "/dev/VG"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2011,7 +2068,7 @@ the VG name, C</dev/VG>.");
 
   ("vgremove", (RErr, [String "vgname"]), 78, [],
    [InitEmpty, Always, TestOutputList (
 
   ("vgremove", (RErr, [String "vgname"]), 78, [],
    [InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2019,7 +2076,7 @@ the VG name, C</dev/VG>.");
        ["vgremove"; "VG"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputList (
        ["vgremove"; "VG"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2035,7 +2092,7 @@ group (if any).");
 
   ("pvremove", (RErr, [Device "device"]), 79, [],
    [InitEmpty, Always, TestOutputListOfDevices (
 
   ("pvremove", (RErr, [Device "device"]), 79, [],
    [InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2044,7 +2101,7 @@ group (if any).");
        ["pvremove"; "/dev/sda1"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputListOfDevices (
        ["pvremove"; "/dev/sda1"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2053,7 +2110,7 @@ group (if any).");
        ["pvremove"; "/dev/sda1"];
        ["vgs"]], []);
     InitEmpty, Always, TestOutputListOfDevices (
        ["pvremove"; "/dev/sda1"];
        ["vgs"]], []);
     InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2288,9 +2345,9 @@ true if their content is exactly equal, or false otherwise.
 The external L<cmp(1)> program is used for the comparison.");
 
   ("strings", (RStringList "stringsout", [Pathname "path"]), 94, [ProtocolLimitWarning],
 The external L<cmp(1)> program is used for the comparison.");
 
   ("strings", (RStringList "stringsout", [Pathname "path"]), 94, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["strings"; "/known-5"]], ["abcdefghi"; "jklmnopqr"]);
       [["strings"; "/known-5"]], ["abcdefghi"; "jklmnopqr"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["strings"; "/empty"]], [])],
    "print the printable strings in a file",
    "\
       [["strings"; "/empty"]], [])],
    "print the printable strings in a file",
    "\
@@ -2298,7 +2355,7 @@ This runs the L<strings(1)> command on a file and returns
 the list of printable strings found.");
 
   ("strings_e", (RStringList "stringsout", [String "encoding"; Pathname "path"]), 95, [ProtocolLimitWarning],
 the list of printable strings found.");
 
   ("strings_e", (RStringList "stringsout", [String "encoding"; Pathname "path"]), 95, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["strings_e"; "b"; "/known-5"]], []);
     InitBasicFS, Disabled, TestOutputList (
       [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"];
       [["strings_e"; "b"; "/known-5"]], []);
     InitBasicFS, Disabled, TestOutputList (
       [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"];
@@ -2316,12 +2373,12 @@ show strings inside Windows/x86 files.
 The returned strings are transcoded to UTF-8.");
 
   ("hexdump", (RString "dump", [Pathname "path"]), 96, [ProtocolLimitWarning],
 The returned strings are transcoded to UTF-8.");
 
   ("hexdump", (RString "dump", [Pathname "path"]), 96, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutput (
+   [InitISOFS, Always, TestOutput (
       [["hexdump"; "/known-4"]], "00000000  61 62 63 0a 64 65 66 0a  67 68 69                 |abc.def.ghi|\n0000000b\n");
     (* Test for RHBZ#501888c2 regression which caused large hexdump
      * commands to segfault.
      *)
       [["hexdump"; "/known-4"]], "00000000  61 62 63 0a 64 65 66 0a  67 68 69                 |abc.def.ghi|\n0000000b\n");
     (* Test for RHBZ#501888c2 regression which caused large hexdump
      * commands to segfault.
      *)
-    InitSquashFS, Always, TestRun (
+    InitISOFS, Always, TestRun (
       [["hexdump"; "/100krandom"]])],
    "dump a file in hexadecimal",
    "\
       [["hexdump"; "/100krandom"]])],
    "dump a file in hexadecimal",
    "\
@@ -2330,7 +2387,7 @@ the human-readable, canonical hex dump of the file.");
 
   ("zerofree", (RErr, [Device "device"]), 97, [],
    [InitNone, Always, TestOutput (
 
   ("zerofree", (RErr, [Device "device"]), 97, [],
    [InitNone, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext3"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "test file"; "0"];
        ["mkfs"; "ext3"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "test file"; "0"];
@@ -2368,7 +2425,9 @@ This runs L<sfdisk(8)> option to modify just the single
 partition C<n> (note: C<n> counts from 1).
 
 For other parameters, see C<guestfs_sfdisk>.  You should usually
 partition C<n> (note: C<n> counts from 1).
 
 For other parameters, see C<guestfs_sfdisk>.  You should usually
-pass C<0> for the cyls/heads/sectors parameters.");
+pass C<0> for the cyls/heads/sectors parameters.
+
+See also: C<guestfs_part_add>");
 
   ("sfdisk_l", (RString "partitions", [Device "device"]), 100, [],
    [],
 
   ("sfdisk_l", (RString "partitions", [Device "device"]), 100, [],
    [],
@@ -2376,7 +2435,9 @@ pass C<0> for the cyls/heads/sectors parameters.");
    "\
 This displays the partition table on C<device>, in the
 human-readable output of the L<sfdisk(8)> command.  It is
    "\
 This displays the partition table on C<device>, in the
 human-readable output of the L<sfdisk(8)> command.  It is
-not intended to be parsed.");
+not intended to be parsed.
+
+See also: C<guestfs_part_list>");
 
   ("sfdisk_kernel_geometry", (RString "partitions", [Device "device"]), 101, [],
    [],
 
   ("sfdisk_kernel_geometry", (RString "partitions", [Device "device"]), 101, [],
    [],
@@ -2428,7 +2489,7 @@ are activated or deactivated.");
 
   ("lvresize", (RErr, [Device "device"; Int "mbytes"]), 105, [],
    [InitNone, Always, TestOutput (
 
   ("lvresize", (RErr, [Device "device"; Int "mbytes"]), 105, [],
    [InitNone, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV"; "VG"; "10"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV"; "VG"; "10"];
@@ -2460,7 +2521,7 @@ C<resize2fs> sometimes gives an error about this and sometimes not.
 In any case, it is always safe to call C<guestfs_e2fsck_f> before
 calling this function.");
 
 In any case, it is always safe to call C<guestfs_e2fsck_f> before
 calling this function.");
 
-  ("find", (RStringList "names", [Pathname "directory"]), 107, [],
+  ("find", (RStringList "names", [Pathname "directory"]), 107, [ProtocolLimitWarning],
    [InitBasicFS, Always, TestOutputList (
       [["find"; "/"]], ["lost+found"]);
     InitBasicFS, Always, TestOutputList (
    [InitBasicFS, Always, TestOutputList (
       [["find"; "/"]], ["lost+found"]);
     InitBasicFS, Always, TestOutputList (
@@ -2497,7 +2558,9 @@ then the returned list from C<guestfs_find> C</tmp> would be
 If C<directory> is not a directory, then this command returns
 an error.
 
 If C<directory> is not a directory, then this command returns
 an error.
 
-The returned list is sorted.");
+The returned list is sorted.
+
+See also C<guestfs_find0>.");
 
   ("e2fsck_f", (RErr, [Device "device"]), 108, [],
    [], (* lvresize tests this *)
 
   ("e2fsck_f", (RErr, [Device "device"]), 108, [],
    [], (* lvresize tests this *)
@@ -2519,11 +2582,11 @@ Sleep for C<secs> seconds.");
 
   ("ntfs_3g_probe", (RInt "status", [Bool "rw"; Device "device"]), 110, [],
    [InitNone, Always, TestOutputInt (
 
   ("ntfs_3g_probe", (RInt "status", [Bool "rw"; Device "device"]), 110, [],
    [InitNone, Always, TestOutputInt (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ntfs"; "/dev/sda1"];
        ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 0);
     InitNone, Always, TestOutputInt (
        ["mkfs"; "ntfs"; "/dev/sda1"];
        ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 0);
     InitNone, Always, TestOutputInt (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 12)],
    "probe NTFS volume",
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 12)],
    "probe NTFS volume",
@@ -2663,7 +2726,7 @@ directory and its contents after use.
 See also: L<mkdtemp(3)>");
 
   ("wc_l", (RInt "lines", [Pathname "path"]), 118, [],
 See also: L<mkdtemp(3)>");
 
   ("wc_l", (RInt "lines", [Pathname "path"]), 118, [],
-   [InitSquashFS, Always, TestOutputInt (
+   [InitISOFS, Always, TestOutputInt (
       [["wc_l"; "/10klines"]], 10000)],
    "count lines in a file",
    "\
       [["wc_l"; "/10klines"]], 10000)],
    "count lines in a file",
    "\
@@ -2671,7 +2734,7 @@ This command counts the lines in a file, using the
 C<wc -l> external command.");
 
   ("wc_w", (RInt "words", [Pathname "path"]), 119, [],
 C<wc -l> external command.");
 
   ("wc_w", (RInt "words", [Pathname "path"]), 119, [],
-   [InitSquashFS, Always, TestOutputInt (
+   [InitISOFS, Always, TestOutputInt (
       [["wc_w"; "/10klines"]], 10000)],
    "count words in a file",
    "\
       [["wc_w"; "/10klines"]], 10000)],
    "count words in a file",
    "\
@@ -2679,7 +2742,7 @@ This command counts the words in a file, using the
 C<wc -w> external command.");
 
   ("wc_c", (RInt "chars", [Pathname "path"]), 120, [],
 C<wc -w> external command.");
 
   ("wc_c", (RInt "chars", [Pathname "path"]), 120, [],
-   [InitSquashFS, Always, TestOutputInt (
+   [InitISOFS, Always, TestOutputInt (
       [["wc_c"; "/100kallspaces"]], 102400)],
    "count characters in a file",
    "\
       [["wc_c"; "/100kallspaces"]], 102400)],
    "count characters in a file",
    "\
@@ -2687,7 +2750,7 @@ This command counts the characters in a file, using the
 C<wc -c> external command.");
 
   ("head", (RStringList "lines", [Pathname "path"]), 121, [ProtocolLimitWarning],
 C<wc -c> external command.");
 
   ("head", (RStringList "lines", [Pathname "path"]), 121, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["head"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz";"3abcdefghijklmnopqrstuvwxyz";"4abcdefghijklmnopqrstuvwxyz";"5abcdefghijklmnopqrstuvwxyz";"6abcdefghijklmnopqrstuvwxyz";"7abcdefghijklmnopqrstuvwxyz";"8abcdefghijklmnopqrstuvwxyz";"9abcdefghijklmnopqrstuvwxyz"])],
    "return first 10 lines of a file",
    "\
       [["head"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz";"3abcdefghijklmnopqrstuvwxyz";"4abcdefghijklmnopqrstuvwxyz";"5abcdefghijklmnopqrstuvwxyz";"6abcdefghijklmnopqrstuvwxyz";"7abcdefghijklmnopqrstuvwxyz";"8abcdefghijklmnopqrstuvwxyz";"9abcdefghijklmnopqrstuvwxyz"])],
    "return first 10 lines of a file",
    "\
@@ -2695,11 +2758,11 @@ This command returns up to the first 10 lines of a file as
 a list of strings.");
 
   ("head_n", (RStringList "lines", [Int "nrlines"; Pathname "path"]), 122, [ProtocolLimitWarning],
 a list of strings.");
 
   ("head_n", (RStringList "lines", [Int "nrlines"; Pathname "path"]), 122, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["head_n"; "3"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]);
       [["head_n"; "3"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["head_n"; "-9997"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]);
       [["head_n"; "-9997"; "/10klines"]], ["0abcdefghijklmnopqrstuvwxyz";"1abcdefghijklmnopqrstuvwxyz";"2abcdefghijklmnopqrstuvwxyz"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["head_n"; "0"; "/10klines"]], [])],
    "return first N lines of a file",
    "\
       [["head_n"; "0"; "/10klines"]], [])],
    "return first N lines of a file",
    "\
@@ -2712,7 +2775,7 @@ from the file C<path>, excluding the last C<nrlines> lines.
 If the parameter C<nrlines> is zero, this returns an empty list.");
 
   ("tail", (RStringList "lines", [Pathname "path"]), 123, [ProtocolLimitWarning],
 If the parameter C<nrlines> is zero, this returns an empty list.");
 
   ("tail", (RStringList "lines", [Pathname "path"]), 123, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["tail"; "/10klines"]], ["9990abcdefghijklmnopqrstuvwxyz";"9991abcdefghijklmnopqrstuvwxyz";"9992abcdefghijklmnopqrstuvwxyz";"9993abcdefghijklmnopqrstuvwxyz";"9994abcdefghijklmnopqrstuvwxyz";"9995abcdefghijklmnopqrstuvwxyz";"9996abcdefghijklmnopqrstuvwxyz";"9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"])],
    "return last 10 lines of a file",
    "\
       [["tail"; "/10klines"]], ["9990abcdefghijklmnopqrstuvwxyz";"9991abcdefghijklmnopqrstuvwxyz";"9992abcdefghijklmnopqrstuvwxyz";"9993abcdefghijklmnopqrstuvwxyz";"9994abcdefghijklmnopqrstuvwxyz";"9995abcdefghijklmnopqrstuvwxyz";"9996abcdefghijklmnopqrstuvwxyz";"9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"])],
    "return last 10 lines of a file",
    "\
@@ -2720,11 +2783,11 @@ This command returns up to the last 10 lines of a file as
 a list of strings.");
 
   ("tail_n", (RStringList "lines", [Int "nrlines"; Pathname "path"]), 124, [ProtocolLimitWarning],
 a list of strings.");
 
   ("tail_n", (RStringList "lines", [Int "nrlines"; Pathname "path"]), 124, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["tail_n"; "3"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]);
       [["tail_n"; "3"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["tail_n"; "-9998"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]);
       [["tail_n"; "-9998"; "/10klines"]], ["9997abcdefghijklmnopqrstuvwxyz";"9998abcdefghijklmnopqrstuvwxyz";"9999abcdefghijklmnopqrstuvwxyz"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["tail_n"; "0"; "/10klines"]], [])],
    "return last N lines of a file",
    "\
       [["tail_n"; "0"; "/10klines"]], [])],
    "return last N lines of a file",
    "\
@@ -2762,8 +2825,8 @@ is I<not> intended that you try to parse the output string.
 Use C<statvfs> from programs.");
 
   ("du", (RInt64 "sizekb", [Pathname "path"]), 127, [],
 Use C<statvfs> from programs.");
 
   ("du", (RInt64 "sizekb", [Pathname "path"]), 127, [],
-   [InitSquashFS, Always, TestOutputInt (
-      [["du"; "/directory"]], 0 (* squashfs doesn't have blocks *))],
+   [InitISOFS, Always, TestOutputInt (
+      [["du"; "/directory"]], 2 (* ISO fs blocksize is 2K *))],
    "estimate file space usage",
    "\
 This command runs the C<du -s> command to estimate file space
    "estimate file space usage",
    "\
 This command runs the C<du -s> command to estimate file space
@@ -2777,7 +2840,7 @@ The result is the estimated size in I<kilobytes>
 (ie. units of 1024 bytes).");
 
   ("initrd_list", (RStringList "filenames", [Pathname "path"]), 128, [],
 (ie. units of 1024 bytes).");
 
   ("initrd_list", (RStringList "filenames", [Pathname "path"]), 128, [],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["initrd_list"; "/initrd"]], ["empty";"known-1";"known-2";"known-3";"known-4"; "known-5"])],
    "list files in an initrd",
    "\
       [["initrd_list"; "/initrd"]], ["empty";"known-1";"known-2";"known-3";"known-4"; "known-5"])],
    "list files in an initrd",
    "\
@@ -2801,7 +2864,7 @@ the command C<mount -o loop file mountpoint>.");
 
   ("mkswap", (RErr, [Device "device"]), 130, [],
    [InitEmpty, Always, TestRun (
 
   ("mkswap", (RErr, [Device "device"]), 130, [],
    [InitEmpty, Always, TestRun (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkswap"; "/dev/sda1"]])],
    "create a swap partition",
    "\
        ["mkswap"; "/dev/sda1"]])],
    "create a swap partition",
    "\
@@ -2809,7 +2872,7 @@ Create a swap partition on C<device>.");
 
   ("mkswap_L", (RErr, [String "label"; Device "device"]), 131, [],
    [InitEmpty, Always, TestRun (
 
   ("mkswap_L", (RErr, [String "label"; Device "device"]), 131, [],
    [InitEmpty, Always, TestRun (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkswap_L"; "hello"; "/dev/sda1"]])],
    "create a swap partition with a label",
    "\
        ["mkswap_L"; "hello"; "/dev/sda1"]])],
    "create a swap partition with a label",
    "\
@@ -2822,7 +2885,7 @@ a limitation of the kernel or swap tools.");
   ("mkswap_U", (RErr, [String "uuid"; Device "device"]), 132, [],
    (let uuid = uuidgen () in
     [InitEmpty, Always, TestRun (
   ("mkswap_U", (RErr, [String "uuid"; Device "device"]), 132, [],
    (let uuid = uuidgen () in
     [InitEmpty, Always, TestRun (
-       [["sfdiskM"; "/dev/sda"; ","];
+       [["part_disk"; "/dev/sda"; "mbr"];
         ["mkswap_U"; uuid; "/dev/sda1"]])]),
    "create a swap partition with an explicit UUID",
    "\
         ["mkswap_U"; uuid; "/dev/sda1"]])]),
    "create a swap partition with an explicit UUID",
    "\
@@ -2966,9 +3029,10 @@ only (rounded to the nearest cylinder) and you don't need
 to specify the cyls, heads and sectors parameters which
 were rarely if ever used anyway.
 
 to specify the cyls, heads and sectors parameters which
 were rarely if ever used anyway.
 
-See also C<guestfs_sfdisk> and the L<sfdisk(8)> manpage.");
+See also: C<guestfs_sfdisk>, the L<sfdisk(8)> manpage
+and C<guestfs_part_disk>");
 
 
-  ("zfile", (RString "description", [String "method"; Pathname "path"]), 140, [DeprecatedBy "file"],
+  ("zfile", (RString "description", [String "meth"; Pathname "path"]), 140, [DeprecatedBy "file"],
    [],
    "determine file type inside a compressed file",
    "\
    [],
    "determine file type inside a compressed file",
    "\
@@ -3089,7 +3153,7 @@ with C<guestfs_mkmountpoint>.  See C<guestfs_mkmountpoint>
 for full details.");
 
   ("read_file", (RBufferOut "content", [Pathname "path"]), 150, [ProtocolLimitWarning],
 for full details.");
 
   ("read_file", (RBufferOut "content", [Pathname "path"]), 150, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputBuffer (
+   [InitISOFS, Always, TestOutputBuffer (
       [["read_file"; "/known-4"]], "abc\ndef\nghi")],
    "read a file",
    "\
       [["read_file"; "/known-4"]], "abc\ndef\nghi")],
    "read a file",
    "\
@@ -3102,9 +3166,9 @@ However unlike C<guestfs_download>, this function is limited
 in the total size of file that can be handled.");
 
   ("grep", (RStringList "lines", [String "regex"; Pathname "path"]), 151, [ProtocolLimitWarning],
 in the total size of file that can be handled.");
 
   ("grep", (RStringList "lines", [String "regex"; Pathname "path"]), 151, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["grep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"]);
       [["grep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"]);
-    InitSquashFS, Always, TestOutputList (
+    InitISOFS, Always, TestOutputList (
       [["grep"; "nomatch"; "/test-grep.txt"]], [])],
    "return lines matching a pattern",
    "\
       [["grep"; "nomatch"; "/test-grep.txt"]], [])],
    "return lines matching a pattern",
    "\
@@ -3112,7 +3176,7 @@ This calls the external C<grep> program and returns the
 matching lines.");
 
   ("egrep", (RStringList "lines", [String "regex"; Pathname "path"]), 152, [ProtocolLimitWarning],
 matching lines.");
 
   ("egrep", (RStringList "lines", [String "regex"; Pathname "path"]), 152, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["egrep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
       [["egrep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
@@ -3120,7 +3184,7 @@ This calls the external C<egrep> program and returns the
 matching lines.");
 
   ("fgrep", (RStringList "lines", [String "pattern"; Pathname "path"]), 153, [ProtocolLimitWarning],
 matching lines.");
 
   ("fgrep", (RStringList "lines", [String "pattern"; Pathname "path"]), 153, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["fgrep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
       [["fgrep"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
@@ -3128,7 +3192,7 @@ This calls the external C<fgrep> program and returns the
 matching lines.");
 
   ("grepi", (RStringList "lines", [String "regex"; Pathname "path"]), 154, [ProtocolLimitWarning],
 matching lines.");
 
   ("grepi", (RStringList "lines", [String "regex"; Pathname "path"]), 154, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["grepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
       [["grepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
@@ -3136,7 +3200,7 @@ This calls the external C<grep -i> program and returns the
 matching lines.");
 
   ("egrepi", (RStringList "lines", [String "regex"; Pathname "path"]), 155, [ProtocolLimitWarning],
 matching lines.");
 
   ("egrepi", (RStringList "lines", [String "regex"; Pathname "path"]), 155, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["egrepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
       [["egrepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
@@ -3144,7 +3208,7 @@ This calls the external C<egrep -i> program and returns the
 matching lines.");
 
   ("fgrepi", (RStringList "lines", [String "pattern"; Pathname "path"]), 156, [ProtocolLimitWarning],
 matching lines.");
 
   ("fgrepi", (RStringList "lines", [String "pattern"; Pathname "path"]), 156, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["fgrepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
       [["fgrepi"; "abc"; "/test-grep.txt"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
@@ -3152,7 +3216,7 @@ This calls the external C<fgrep -i> program and returns the
 matching lines.");
 
   ("zgrep", (RStringList "lines", [String "regex"; Pathname "path"]), 157, [ProtocolLimitWarning],
 matching lines.");
 
   ("zgrep", (RStringList "lines", [String "regex"; Pathname "path"]), 157, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["zgrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
       [["zgrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
@@ -3160,7 +3224,7 @@ This calls the external C<zgrep> program and returns the
 matching lines.");
 
   ("zegrep", (RStringList "lines", [String "regex"; Pathname "path"]), 158, [ProtocolLimitWarning],
 matching lines.");
 
   ("zegrep", (RStringList "lines", [String "regex"; Pathname "path"]), 158, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["zegrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
       [["zegrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
@@ -3168,7 +3232,7 @@ This calls the external C<zegrep> program and returns the
 matching lines.");
 
   ("zfgrep", (RStringList "lines", [String "pattern"; Pathname "path"]), 159, [ProtocolLimitWarning],
 matching lines.");
 
   ("zfgrep", (RStringList "lines", [String "pattern"; Pathname "path"]), 159, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["zfgrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
       [["zfgrep"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"])],
    "return lines matching a pattern",
    "\
@@ -3176,7 +3240,7 @@ This calls the external C<zfgrep> program and returns the
 matching lines.");
 
   ("zgrepi", (RStringList "lines", [String "regex"; Pathname "path"]), 160, [ProtocolLimitWarning],
 matching lines.");
 
   ("zgrepi", (RStringList "lines", [String "regex"; Pathname "path"]), 160, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["zgrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
       [["zgrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
@@ -3184,7 +3248,7 @@ This calls the external C<zgrep -i> program and returns the
 matching lines.");
 
   ("zegrepi", (RStringList "lines", [String "regex"; Pathname "path"]), 161, [ProtocolLimitWarning],
 matching lines.");
 
   ("zegrepi", (RStringList "lines", [String "regex"; Pathname "path"]), 161, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["zegrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
       [["zegrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
@@ -3192,7 +3256,7 @@ This calls the external C<zegrep -i> program and returns the
 matching lines.");
 
   ("zfgrepi", (RStringList "lines", [String "pattern"; Pathname "path"]), 162, [ProtocolLimitWarning],
 matching lines.");
 
   ("zfgrepi", (RStringList "lines", [String "pattern"; Pathname "path"]), 162, [ProtocolLimitWarning],
-   [InitSquashFS, Always, TestOutputList (
+   [InitISOFS, Always, TestOutputList (
       [["zfgrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
       [["zfgrepi"; "abc"; "/test-grep.txt.gz"]], ["abc"; "abc123"; "ABC"])],
    "return lines matching a pattern",
    "\
@@ -3200,7 +3264,7 @@ This calls the external C<zfgrep -i> program and returns the
 matching lines.");
 
   ("realpath", (RString "rpath", [Pathname "path"]), 163, [],
 matching lines.");
 
   ("realpath", (RString "rpath", [Pathname "path"]), 163, [],
-   [InitSquashFS, Always, TestOutput (
+   [InitISOFS, Always, TestOutput (
       [["realpath"; "/../directory"]], "/directory")],
    "canonicalized absolute pathname",
    "\
       [["realpath"; "/../directory"]], "/directory")],
    "canonicalized absolute pathname",
    "\
@@ -3313,7 +3377,7 @@ This command disables the libguestfs appliance swap on file.");
 
   ("swapon_label", (RErr, [String "label"]), 174, [],
    [InitEmpty, Always, TestRun (
 
   ("swapon_label", (RErr, [String "label"]), 174, [],
    [InitEmpty, Always, TestRun (
-      [["sfdiskM"; "/dev/sdb"; ","];
+      [["part_disk"; "/dev/sdb"; "mbr"];
        ["mkswap_L"; "swapit"; "/dev/sdb1"];
        ["swapon_label"; "swapit"];
        ["swapoff_label"; "swapit"];
        ["mkswap_L"; "swapit"; "/dev/sdb1"];
        ["swapon_label"; "swapit"];
        ["swapoff_label"; "swapit"];
@@ -3361,7 +3425,7 @@ This command just writes a swap file signature to an existing
 file.  To create the file itself, use something like C<guestfs_fallocate>.");
 
   ("inotify_init", (RErr, [Int "maxevents"]), 179, [],
 file.  To create the file itself, use something like C<guestfs_fallocate>.");
 
   ("inotify_init", (RErr, [Int "maxevents"]), 179, [],
-   [InitSquashFS, Always, TestRun (
+   [InitISOFS, Always, TestRun (
       [["inotify_init"; "0"]])],
    "create an inotify handle",
    "\
       [["inotify_init"; "0"]])],
    "create an inotify handle",
    "\
@@ -3478,7 +3542,7 @@ and C<guestfs_setcon>");
 
   ("mkfs_b", (RErr, [String "fstype"; Int "blocksize"; Device "device"]), 187, [],
    [InitEmpty, Always, TestOutput (
 
   ("mkfs_b", (RErr, [String "fstype"; Int "blocksize"; Device "device"]), 187, [],
    [InitEmpty, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs_b"; "ext2"; "4096"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
        ["mkfs_b"; "ext2"; "4096"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -3560,6 +3624,483 @@ an external journal on the journal with UUID C<uuid>.
 
 See also C<guestfs_mke2journal_U>.");
 
 
 See also C<guestfs_mke2journal_U>.");
 
+  ("modprobe", (RErr, [String "modulename"]), 194, [],
+   [InitNone, Always, TestRun [["modprobe"; "fat"]]],
+   "load a kernel module",
+   "\
+This loads a kernel module in the appliance.
+
+The kernel module must have been whitelisted when libguestfs
+was built (see C<appliance/kmod.whitelist.in> in the source).");
+
+  ("echo_daemon", (RString "output", [StringList "words"]), 195, [],
+   [InitNone, Always, TestOutput (
+     [["echo_daemon"; "This is a test"]], "This is a test"
+   )],
+   "echo arguments back to the client",
+   "\
+This command concatenate the list of C<words> passed with single spaces between
+them and returns the resulting string.
+
+You can use this command to test the connection through to the daemon.
+
+See also C<guestfs_ping_daemon>.");
+
+  ("find0", (RErr, [Pathname "directory"; FileOut "files"]), 196, [],
+   [], (* There is a regression test for this. *)
+   "find all files and directories, returning NUL-separated list",
+   "\
+This command lists out all files and directories, recursively,
+starting at C<directory>, placing the resulting list in the
+external file called C<files>.
+
+This command works the same way as C<guestfs_find> with the
+following exceptions:
+
+=over 4
+
+=item *
+
+The resulting list is written to an external file.
+
+=item *
+
+Items (filenames) in the result are separated
+by C<\\0> characters.  See L<find(1)> option I<-print0>.
+
+=item *
+
+This command is not limited in the number of names that it
+can return.
+
+=item *
+
+The result list is not sorted.
+
+=back");
+
+  ("case_sensitive_path", (RString "rpath", [Pathname "path"]), 197, [],
+   [InitISOFS, Always, TestOutput (
+      [["case_sensitive_path"; "/DIRECTORY"]], "/directory");
+    InitISOFS, Always, TestOutput (
+      [["case_sensitive_path"; "/DIRECTORY/"]], "/directory");
+    InitISOFS, Always, TestOutput (
+      [["case_sensitive_path"; "/Known-1"]], "/known-1");
+    InitISOFS, Always, TestLastFail (
+      [["case_sensitive_path"; "/Known-1/"]]);
+    InitBasicFS, Always, TestOutput (
+      [["mkdir"; "/a"];
+       ["mkdir"; "/a/bbb"];
+       ["touch"; "/a/bbb/c"];
+       ["case_sensitive_path"; "/A/bbB/C"]], "/a/bbb/c");
+    InitBasicFS, Always, TestOutput (
+      [["mkdir"; "/a"];
+       ["mkdir"; "/a/bbb"];
+       ["touch"; "/a/bbb/c"];
+       ["case_sensitive_path"; "/A////bbB/C"]], "/a/bbb/c");
+    InitBasicFS, Always, TestLastFail (
+      [["mkdir"; "/a"];
+       ["mkdir"; "/a/bbb"];
+       ["touch"; "/a/bbb/c"];
+       ["case_sensitive_path"; "/A/bbb/../bbb/C"]])],
+   "return true path on case-insensitive filesystem",
+   "\
+This can be used to resolve case insensitive paths on
+a filesystem which is case sensitive.  The use case is
+to resolve paths which you have read from Windows configuration
+files or the Windows Registry, to the true path.
+
+The command handles a peculiarity of the Linux ntfs-3g
+filesystem driver (and probably others), which is that although
+the underlying filesystem is case-insensitive, the driver
+exports the filesystem to Linux as case-sensitive.
+
+One consequence of this is that special directories such
+as C<c:\\windows> may appear as C</WINDOWS> or C</windows>
+(or other things) depending on the precise details of how
+they were created.  In Windows itself this would not be
+a problem.
+
+Bug or feature?  You decide:
+L<http://www.tuxera.com/community/ntfs-3g-faq/#posixfilenames1>
+
+This function resolves the true case of each element in the
+path and returns the case-sensitive path.
+
+Thus C<guestfs_case_sensitive_path> (\"/Windows/System32\")
+might return C<\"/WINDOWS/system32\"> (the exact return value
+would depend on details of how the directories were originally
+created under Windows).
+
+I<Note>:
+This function does not handle drive names, backslashes etc.
+
+See also C<guestfs_realpath>.");
+
+  ("vfs_type", (RString "fstype", [Device "device"]), 198, [],
+   [InitBasicFS, Always, TestOutput (
+      [["vfs_type"; "/dev/sda1"]], "ext2")],
+   "get the Linux VFS type corresponding to a mounted device",
+   "\
+This command gets the block device type corresponding to
+a mounted device called C<device>.
+
+Usually the result is the name of the Linux VFS module that
+is used to mount this device (probably determined automatically
+if you used the C<guestfs_mount> call).");
+
+  ("truncate", (RErr, [Pathname "path"]), 199, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["write_file"; "/test"; "some stuff so size is not zero"; "0"];
+       ["truncate"; "/test"];
+       ["stat"; "/test"]], [CompareWithInt ("size", 0)])],
+   "truncate a file to zero size",
+   "\
+This command truncates C<path> to a zero-length file.  The
+file must exist already.");
+
+  ("truncate_size", (RErr, [Pathname "path"; Int64 "size"]), 200, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["touch"; "/test"];
+       ["truncate_size"; "/test"; "1000"];
+       ["stat"; "/test"]], [CompareWithInt ("size", 1000)])],
+   "truncate a file to a particular size",
+   "\
+This command truncates C<path> to size C<size> bytes.  The file
+must exist already.  If the file is smaller than C<size> then
+the file is extended to the required size with null bytes.");
+
+  ("utimens", (RErr, [Pathname "path"; Int64 "atsecs"; Int64 "atnsecs"; Int64 "mtsecs"; Int64 "mtnsecs"]), 201, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["touch"; "/test"];
+       ["utimens"; "/test"; "12345"; "67890"; "9876"; "5432"];
+       ["stat"; "/test"]], [CompareWithInt ("mtime", 9876)])],
+   "set timestamp of a file with nanosecond precision",
+   "\
+This command sets the timestamps of a file with nanosecond
+precision.
+
+C<atsecs, atnsecs> are the last access time (atime) in secs and
+nanoseconds from the epoch.
+
+C<mtsecs, mtnsecs> are the last modification time (mtime) in
+secs and nanoseconds from the epoch.
+
+If the C<*nsecs> field contains the special value C<-1> then
+the corresponding timestamp is set to the current time.  (The
+C<*secs> field is ignored in this case).
+
+If the C<*nsecs> field contains the special value C<-2> then
+the corresponding timestamp is left unchanged.  (The
+C<*secs> field is ignored in this case).");
+
+  ("mkdir_mode", (RErr, [Pathname "path"; Int "mode"]), 202, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["mkdir_mode"; "/test"; "0o111"];
+       ["stat"; "/test"]], [CompareWithInt ("mode", 0o40111)])],
+   "create a directory with a particular mode",
+   "\
+This command creates a directory, setting the initial permissions
+of the directory to C<mode>.  See also C<guestfs_mkdir>.");
+
+  ("lchown", (RErr, [Int "owner"; Int "group"; Pathname "path"]), 203, [],
+   [], (* XXX *)
+   "change file owner and group",
+   "\
+Change the file owner to C<owner> and group to C<group>.
+This is like C<guestfs_chown> but if C<path> is a symlink then
+the link itself is changed, not the target.
+
+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).");
+
+  ("lstatlist", (RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"]), 204, [],
+   [], (* XXX *)
+   "lstat on multiple files",
+   "\
+This call allows you to perform the C<guestfs_lstat> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of stat structs, with a one-to-one
+correspondence to the C<names> list.  If any name did not exist
+or could not be lstat'd, then the C<ino> field of that structure
+is set to C<-1>.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lxattrlist> for a similarly efficient call
+for getting extended attributes.  Very long directory listings
+might cause the protocol message size to be exceeded, causing
+this call to fail.  The caller must split up such requests
+into smaller groups of names.");
+
+  ("lxattrlist", (RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"]), 205, [],
+   [], (* XXX *)
+   "lgetxattr on multiple files",
+   "\
+This call allows you to get the extended attributes
+of multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a flat list of xattr structs which must be
+interpreted sequentially.  The first xattr struct always has a zero-length
+C<attrname>.  C<attrval> in this struct is zero-length
+to indicate there was an error doing C<lgetxattr> for this
+file, I<or> is a C string which is a decimal number
+(the number of following attributes for this file, which could
+be C<\"0\">).  Then after the first xattr struct are the
+zero or more attributes for the first named file.
+This repeats for the second and subsequent files.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lstatlist> for a similarly efficient call
+for getting standard stats.  Very long directory listings
+might cause the protocol message size to be exceeded, causing
+this call to fail.  The caller must split up such requests
+into smaller groups of names.");
+
+  ("readlinklist", (RStringList "links", [Pathname "path"; StringList "names"]), 206, [],
+   [], (* XXX *)
+   "readlink on multiple files",
+   "\
+This call allows you to do a C<readlink> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of strings, with a one-to-one
+correspondence to the C<names> list.  Each string is the
+value of the symbol link.
+
+If the C<readlink(2)> operation fails on any name, then
+the corresponding result string is the empty string C<\"\">.
+However the whole operation is completed even if there
+were C<readlink(2)> errors, and so you can call this
+function with names where you don't know if they are
+symbolic links already (albeit slightly less efficient).
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+Very long directory listings might cause the protocol
+message size to be exceeded, causing
+this call to fail.  The caller must split up such requests
+into smaller groups of names.");
+
+  ("pread", (RBufferOut "content", [Pathname "path"; Int "count"; Int64 "offset"]), 207, [ProtocolLimitWarning],
+   [InitISOFS, Always, TestOutputBuffer (
+      [["pread"; "/known-4"; "1"; "3"]], "\n");
+    InitISOFS, Always, TestOutputBuffer (
+      [["pread"; "/empty"; "0"; "100"]], "")],
+   "read part of a file",
+   "\
+This command lets you read part of a file.  It reads C<count>
+bytes of the file, starting at C<offset>, from file C<path>.
+
+This may read fewer bytes than requested.  For further details
+see the L<pread(2)> system call.");
+
+  ("part_init", (RErr, [Device "device"; String "parttype"]), 208, [],
+   [InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "gpt"]])],
+   "create an empty partition table",
+   "\
+This creates an empty partition table on C<device> of one of the
+partition types listed below.  Usually C<parttype> should be
+either C<msdos> or C<gpt> (for large disks).
+
+Initially there are no partitions.  Following this, you should
+call C<guestfs_part_add> for each partition required.
+
+Possible values for C<parttype> are:
+
+=over 4
+
+=item B<efi> | B<gpt>
+
+Intel EFI / GPT partition table.
+
+This is recommended for >= 2 TB partitions that will be accessed
+from Linux and Intel-based Mac OS X.  It also has limited backwards
+compatibility with the C<mbr> format.
+
+=item B<mbr> | B<msdos>
+
+The standard PC \"Master Boot Record\" (MBR) format used
+by MS-DOS and Windows.  This partition type will B<only> work
+for device sizes up to 2 TB.  For large disks we recommend
+using C<gpt>.
+
+=back
+
+Other partition table types that may work but are not
+supported include:
+
+=over 4
+
+=item B<aix>
+
+AIX disk labels.
+
+=item B<amiga> | B<rdb>
+
+Amiga \"Rigid Disk Block\" format.
+
+=item B<bsd>
+
+BSD disk labels.
+
+=item B<dasd>
+
+DASD, used on IBM mainframes.
+
+=item B<dvh>
+
+MIPS/SGI volumes.
+
+=item B<mac>
+
+Old Mac partition format.  Modern Macs use C<gpt>.
+
+=item B<pc98>
+
+NEC PC-98 format, common in Japan apparently.
+
+=item B<sun>
+
+Sun disk labels.
+
+=back");
+
+  ("part_add", (RErr, [Device "device"; String "prlogex"; Int64 "startsect"; Int64 "endsect"]), 209, [],
+   [InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "mbr"];
+       ["part_add"; "/dev/sda"; "primary"; "1"; "-1"]]);
+    InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "gpt"];
+       ["part_add"; "/dev/sda"; "primary"; "34"; "127"];
+       ["part_add"; "/dev/sda"; "primary"; "128"; "-34"]]);
+    InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "mbr"];
+       ["part_add"; "/dev/sda"; "primary"; "32"; "127"];
+       ["part_add"; "/dev/sda"; "primary"; "128"; "255"];
+       ["part_add"; "/dev/sda"; "primary"; "256"; "511"];
+       ["part_add"; "/dev/sda"; "primary"; "512"; "-1"]])],
+   "add a partition to the device",
+   "\
+This command adds a partition to C<device>.  If there is no partition
+table on the device, call C<guestfs_part_init> first.
+
+The C<prlogex> parameter is the type of partition.  Normally you
+should pass C<p> or C<primary> here, but MBR partition tables also
+support C<l> (or C<logical>) and C<e> (or C<extended>) partition
+types.
+
+C<startsect> and C<endsect> are the start and end of the partition
+in I<sectors>.  C<endsect> may be negative, which means it counts
+backwards from the end of the disk (C<-1> is the last sector).
+
+Creating a partition which covers the whole disk is not so easy.
+Use C<guestfs_part_disk> to do that.");
+
+  ("part_disk", (RErr, [Device "device"; String "parttype"]), 210, [DangerWillRobinson],
+   [InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "mbr"]]);
+    InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "gpt"]])],
+   "partition whole disk with a single primary partition",
+   "\
+This command is simply a combination of C<guestfs_part_init>
+followed by C<guestfs_part_add> to create a single primary partition
+covering the whole disk.
+
+C<parttype> is the partition table type, usually C<mbr> or C<gpt>,
+but other possible values are described in C<guestfs_part_init>.");
+
+  ("part_set_bootable", (RErr, [Device "device"; Int "partnum"; Bool "bootable"]), 211, [],
+   [InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "mbr"];
+       ["part_set_bootable"; "/dev/sda"; "1"; "true"]])],
+   "make a partition bootable",
+   "\
+This sets the bootable flag on partition numbered C<partnum> on
+device C<device>.  Note that partitions are numbered from 1.
+
+The bootable flag is used by some PC BIOSes to determine which
+partition to boot from.  It is by no means universally recognized,
+and in any case if your operating system installed a boot
+sector on the device itself, then that takes precedence.");
+
+  ("part_set_name", (RErr, [Device "device"; Int "partnum"; String "name"]), 212, [],
+   [InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "gpt"];
+       ["part_set_name"; "/dev/sda"; "1"; "thepartname"]])],
+   "set partition name",
+   "\
+This sets the partition name on partition numbered C<partnum> on
+device C<device>.  Note that partitions are numbered from 1.
+
+The partition name can only be set on certain types of partition
+table.  This works on C<gpt> but not on C<mbr> partitions.");
+
+  ("part_list", (RStructList ("partitions", "partition"), [Device "device"]), 213, [],
+   [], (* XXX Add a regression test for this. *)
+   "list partitions on a device",
+   "\
+This command parses the partition table on C<device> and
+returns the list of partitions found.
+
+The fields in the returned structure are:
+
+=over 4
+
+=item B<part_num>
+
+Partition number, counting from 1.
+
+=item B<part_start>
+
+Start of the partition I<in bytes>.  To get sectors you have to
+divide by the device's sector size, see C<guestfs_blockdev_getss>.
+
+=item B<part_end>
+
+End of the partition in bytes.
+
+=item B<part_size>
+
+Size of the partition in bytes.
+
+=back");
+
+  ("part_get_parttype", (RString "parttype", [Device "device"]), 214, [],
+   [InitEmpty, Always, TestOutput (
+      [["part_disk"; "/dev/sda"; "gpt"];
+       ["part_get_parttype"; "/dev/sda"]], "gpt")],
+   "get the partition table type",
+   "\
+This command examines the partition table on C<device> and
+returns the partition table type (format) being used.
+
+Common return values include: C<msdos> (a DOS/Windows style MBR
+partition table), C<gpt> (a GPT/EFI-style partition table).  Other
+values are possible, although unusual.  See C<guestfs_part_init>
+for a full list.");
+
+  ("fill", (RErr, [Int "c"; Int "len"; Pathname "path"]), 215, [],
+   [InitBasicFS, Always, TestOutputBuffer (
+      [["fill"; "0x63"; "10"; "/test"];
+       ["read_file"; "/test"]], "cccccccccc")],
+   "fill a file with octets",
+   "\
+This command creates a new file called C<path>.  The initial
+content of the file is C<len> octets of C<c>, where C<c>
+must be a number in the range C<[0..255]>.
+
+To fill a file with zero bytes (sparsely), it is
+much more efficient to use C<guestfs_truncate_size>.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -3728,6 +4269,14 @@ let structs = [
     "in_cookie", FUInt32;
     "in_name", FString;
   ];
     "in_cookie", FUInt32;
     "in_name", FString;
   ];
+
+  (* Partition table entry. *)
+  "partition", [
+    "part_num", FInt32;
+    "part_start", FBytes;
+    "part_end", FBytes;
+    "part_size", FBytes;
+  ];
 ] (* end of structs *)
 
 (* Ugh, Java has to be different ..
 ] (* end of structs *)
 
 (* Ugh, Java has to be different ..
@@ -3744,6 +4293,7 @@ let java_structs = [
   "version", "Version";
   "xattr", "XAttr";
   "inotify_event", "INotifyEvent";
   "version", "Version";
   "xattr", "XAttr";
   "inotify_event", "INotifyEvent";
+  "partition", "Partition";
 ]
 
 (* What structs are actually returned. *)
 ]
 
 (* What structs are actually returned. *)
@@ -3760,7 +4310,7 @@ type rstructs_used_t = RStructOnly | RStructListOnly | RStructAndList
  *    == there are functions returning both RStruct (_, structname)
  *                                      and RStructList (_, structname)
  *)
  *    == there are functions returning both RStruct (_, structname)
  *                                      and RStructList (_, structname)
  *)
-let rstructs_used =
+let rstructs_used_by functions =
   (* ||| is a "logical OR" for rstructs_used_t *)
   let (|||) a b =
     match a, b with
   (* ||| is a "logical OR" for rstructs_used_t *)
   let (|||) a b =
     match a, b with
@@ -3789,27 +4339,18 @@ let rstructs_used =
       | RStruct (_, structname) -> update structname RStructOnly
       | RStructList (_, structname) -> update structname RStructListOnly
       | _ -> ()
       | RStruct (_, structname) -> update structname RStructOnly
       | RStructList (_, structname) -> update structname RStructListOnly
       | _ -> ()
-  ) all_functions;
+  ) functions;
 
   (* return key->values as a list of (key,value) *)
   Hashtbl.fold (fun key value xs -> (key, value) :: xs) h []
 
 
   (* return key->values as a list of (key,value) *)
   Hashtbl.fold (fun key value xs -> (key, value) :: xs) h []
 
-(* debug:
-let () =
-  List.iter (
-    function
-    | sn, RStructOnly -> printf "%s RStructOnly\n" sn
-    | sn, RStructListOnly -> printf "%s RStructListOnly\n" sn
-    | sn, RStructAndList -> printf "%s RStructAndList\n" sn
-  ) rstructs_used
-*)
-
 (* Used for testing language bindings. *)
 type callt =
   | CallString of string
   | CallOptString of string option
   | CallStringList of string list
   | CallInt of int
 (* Used for testing language bindings. *)
 type callt =
   | CallString of string
   | CallOptString of string option
   | CallStringList of string list
   | CallInt of int
+  | CallInt64 of int64
   | CallBool of bool
 
 (* Used to memoize the result of pod2text. *)
   | CallBool of bool
 
 (* Used to memoize the result of pod2text. *)
@@ -3822,6 +4363,10 @@ let pod2text_memo : ((int * string * string), string list) Hashtbl.t =
     v
   with
     _ -> Hashtbl.create 13
     v
   with
     _ -> Hashtbl.create 13
+let pod2text_memo_updated () =
+  let chan = open_out pod2text_memo_filename in
+  output_value chan pod2text_memo;
+  close_out chan
 
 (* Useful functions.
  * Note we don't want to use any external OCaml libraries which
 
 (* Useful functions.
  * Note we don't want to use any external OCaml libraries which
@@ -3943,7 +4488,7 @@ let mapi f xs =
 
 let name_of_argt = function
   | Pathname n | Device n | Dev_or_Path n | String n | OptString n
 
 let name_of_argt = function
   | Pathname n | Device n | Dev_or_Path n | String n | OptString n
-  | StringList n | DeviceList n | Bool n | Int n
+  | StringList n | DeviceList n | Bool n | Int n | Int64 n
   | FileIn n | FileOut n -> n
 
 let java_name_of_struct typ =
   | FileIn n | FileOut n -> n
 
 let java_name_of_struct typ =
@@ -4037,7 +4582,36 @@ let check_functions () =
         if n = "i" || n = "n" then
           failwithf "%s has a param/ret called 'i' or 'n', which will cause some conflicts in the generated code" name;
         if n = "argv" || n = "args" then
         if n = "i" || n = "n" then
           failwithf "%s has a param/ret called 'i' or 'n', which will cause some conflicts in the generated code" name;
         if n = "argv" || n = "args" then
-          failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" name
+          failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" name;
+
+        (* List Haskell, OCaml and C keywords here.
+         * http://www.haskell.org/haskellwiki/Keywords
+         * http://caml.inria.fr/pub/docs/manual-ocaml/lex.html#operator-char
+         * http://en.wikipedia.org/wiki/C_syntax#Reserved_keywords
+         * Formatted via: cat c haskell ocaml|sort -u|grep -vE '_|^val$' \
+         *   |perl -pe 's/(.+)/"$1";/'|fmt -70
+         * Omitting _-containing words, since they're handled above.
+         * Omitting the OCaml reserved word, "val", is ok,
+         * and saves us from renaming several parameters.
+         *)
+        let reserved = [
+          "and"; "as"; "asr"; "assert"; "auto"; "begin"; "break"; "case";
+          "char"; "class"; "const"; "constraint"; "continue"; "data";
+          "default"; "deriving"; "do"; "done"; "double"; "downto"; "else";
+          "end"; "enum"; "exception"; "extern"; "external"; "false"; "float";
+          "for"; "forall"; "foreign"; "fun"; "function"; "functor"; "goto";
+          "hiding"; "if"; "import"; "in"; "include"; "infix"; "infixl";
+          "infixr"; "inherit"; "initializer"; "inline"; "instance"; "int";
+          "land"; "lazy"; "let"; "long"; "lor"; "lsl"; "lsr"; "lxor";
+          "match"; "mdo"; "method"; "mod"; "module"; "mutable"; "new";
+          "newtype"; "object"; "of"; "open"; "or"; "private"; "qualified";
+          "rec"; "register"; "restrict"; "return"; "short"; "sig"; "signed";
+          "sizeof"; "static"; "struct"; "switch"; "then"; "to"; "true"; "try";
+          "type"; "typedef"; "union"; "unsigned"; "virtual"; "void";
+          "volatile"; "when"; "where"; "while";
+          ] in
+        if List.mem n reserved then
+          failwithf "%s has param/ret using reserved word %s" name n;
       in
 
       (match fst style with
       in
 
       (match fst style with
@@ -4330,11 +4904,13 @@ and generate_xdr () =
            pr "struct %s_args {\n" name;
            List.iter (
              function
            pr "struct %s_args {\n" name;
            List.iter (
              function
-             | Pathname n | Device n | Dev_or_Path n | String n -> pr "  string %s<>;\n" n
+             | Pathname n | Device n | Dev_or_Path n | String n ->
+                 pr "  string %s<>;\n" n
              | OptString n -> pr "  str *%s;\n" n
              | StringList n | DeviceList n -> pr "  str %s<>;\n" n
              | Bool n -> pr "  bool %s;\n" n
              | Int n -> pr "  int %s;\n" n
              | OptString n -> pr "  str *%s;\n" n
              | StringList n | DeviceList n -> pr "  str %s<>;\n" n
              | Bool n -> pr "  bool %s;\n" n
              | Int n -> pr "  int %s;\n" n
+             | Int64 n -> pr "  hyper %s;\n" n
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "};\n\n"
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "};\n\n"
@@ -4506,6 +5082,16 @@ and generate_actions_h () =
         name style
   ) all_functions
 
         name style
   ) all_functions
 
+(* Generate the guestfs-internal-actions.h file. *)
+and generate_internal_actions_h () =
+  generate_header CStyle LGPLv2;
+  List.iter (
+    fun (shortname, style, _, _, _, _, _) ->
+      let name = "guestfs__" ^ shortname in
+      generate_prototype ~single_line:true ~newline:true ~handle:"handle"
+        name style
+  ) non_daemon_functions
+
 (* Generate the client-side dispatch stubs. *)
 and generate_client_actions () =
   generate_header CStyle LGPLv2;
 (* Generate the client-side dispatch stubs. *)
 and generate_client_actions () =
   generate_header CStyle LGPLv2;
@@ -4513,22 +5099,26 @@ and generate_client_actions () =
   pr "\
 #include <stdio.h>
 #include <stdlib.h>
   pr "\
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
 
 #include \"guestfs.h\"
 
 #include \"guestfs.h\"
+#include \"guestfs-internal.h\"
+#include \"guestfs-internal-actions.h\"
 #include \"guestfs_protocol.h\"
 
 #define error guestfs_error
 #include \"guestfs_protocol.h\"
 
 #define error guestfs_error
-#define perrorf guestfs_perrorf
+//#define perrorf guestfs_perrorf
 #define safe_malloc guestfs_safe_malloc
 #define safe_realloc guestfs_safe_realloc
 #define safe_malloc guestfs_safe_malloc
 #define safe_realloc guestfs_safe_realloc
-#define safe_strdup guestfs_safe_strdup
+//#define safe_strdup guestfs_safe_strdup
 #define safe_memdup guestfs_safe_memdup
 
 /* Check the return message from a call for validity. */
 static int
 check_reply_header (guestfs_h *g,
                     const struct guestfs_message_header *hdr,
 #define safe_memdup guestfs_safe_memdup
 
 /* Check the return message from a call for validity. */
 static int
 check_reply_header (guestfs_h *g,
                     const struct guestfs_message_header *hdr,
-                    int proc_nr, int serial)
+                    unsigned int proc_nr, unsigned int serial)
 {
   if (hdr->prog != GUESTFS_PROGRAM) {
     error (g, \"wrong program (%%d/%%d)\", hdr->prog, GUESTFS_PROGRAM);
 {
   if (hdr->prog != GUESTFS_PROGRAM) {
     error (g, \"wrong program (%%d/%%d)\", hdr->prog, GUESTFS_PROGRAM);
@@ -4560,16 +5150,13 @@ check_reply_header (guestfs_h *g,
 static int
 check_state (guestfs_h *g, const char *caller)
 {
 static int
 check_state (guestfs_h *g, const char *caller)
 {
-  if (!guestfs_is_ready (g)) {
-    if (guestfs_is_config (g))
+  if (!guestfs__is_ready (g)) {
+    if (guestfs__is_config (g) || guestfs__is_launching (g))
       error (g, \"%%s: call launch before using this function\\n(in guestfish, don't forget to use the 'run' command)\",
         caller);
       error (g, \"%%s: call launch before using this function\\n(in guestfish, don't forget to use the 'run' command)\",
         caller);
-    else if (guestfs_is_launching (g))
-      error (g, \"%%s: call wait_ready() before using this function\",
-        caller);
     else
       error (g, \"%%s called from the wrong state, %%d != READY\",
     else
       error (g, \"%%s called from the wrong state, %%d != READY\",
-        caller, guestfs_get_state (g));
+        caller, guestfs__get_state (g));
     return -1;
   }
   return 0;
     return -1;
   }
   return 0;
@@ -4577,80 +5164,74 @@ check_state (guestfs_h *g, const char *caller)
 
 ";
 
 
 ";
 
-  (* Client-side stubs for each function. *)
+  (* Generate code to generate guestfish call traces. *)
+  let trace_call shortname style =
+    pr "  if (guestfs__get_trace (g)) {\n";
+
+    let needs_i =
+      List.exists (function
+                   | StringList _ | DeviceList _ -> true
+                   | _ -> false) (snd style) in
+    if needs_i then (
+      pr "    int i;\n";
+      pr "\n"
+    );
+
+    pr "    printf (\"%s\");\n" shortname;
+    List.iter (
+      function
+      | String n                       (* strings *)
+      | Device n
+      | Pathname n
+      | Dev_or_Path n
+      | FileIn n
+      | FileOut n ->
+          (* guestfish doesn't support string escaping, so neither do we *)
+          pr "    printf (\" \\\"%%s\\\"\", %s);\n" n
+      | OptString n ->                 (* string option *)
+          pr "    if (%s) printf (\" \\\"%%s\\\"\", %s);\n" n n;
+          pr "    else printf (\" null\");\n"
+      | StringList n
+      | DeviceList n ->                        (* string list *)
+          pr "    putchar (' ');\n";
+          pr "    putchar ('\"');\n";
+          pr "    for (i = 0; %s[i]; ++i) {\n" n;
+          pr "      if (i > 0) putchar (' ');\n";
+          pr "      fputs (%s[i], stdout);\n" n;
+          pr "    }\n";
+          pr "    putchar ('\"');\n";
+      | Bool n ->                      (* boolean *)
+          pr "    fputs (%s ? \" true\" : \" false\", stdout);\n" n
+      | Int n ->                       (* int *)
+          pr "    printf (\" %%d\", %s);\n" n
+      | Int64 n ->
+          pr "    printf (\" %%\" PRIi64, %s);\n" n
+    ) (snd style);
+    pr "    putchar ('\\n');\n";
+    pr "  }\n";
+    pr "\n";
+  in
+
+  (* For non-daemon functions, generate a wrapper around each function. *)
   List.iter (
     fun (shortname, style, _, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
 
   List.iter (
     fun (shortname, style, _, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
 
-      (* Generate the context struct which stores the high-level
-       * state between callback functions.
-       *)
-      pr "struct %s_ctx {\n" shortname;
-      pr "  /* This flag is set by the callbacks, so we know we've done\n";
-      pr "   * the callbacks as expected, and in the right sequence.\n";
-      pr "   * 0 = not called, 1 = reply_cb called.\n";
-      pr "   */\n";
-      pr "  int cb_sequence;\n";
-      pr "  struct guestfs_message_header hdr;\n";
-      pr "  struct guestfs_message_error err;\n";
-      (match fst style with
-       | RErr -> ()
-       | RConstString _ | RConstOptString _ ->
-           failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-       | RInt _ | RInt64 _
-       | RBool _ | RString _ | RStringList _
-       | RStruct _ | RStructList _
-       | RHashtable _ | RBufferOut _ ->
-           pr "  struct %s_ret ret;\n" name
-      );
-      pr "};\n";
-      pr "\n";
-
-      (* Generate the reply callback function. *)
-      pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
+      generate_prototype ~extern:false ~semicolon:false ~newline:true
+        ~handle:"g" name style;
       pr "{\n";
       pr "{\n";
-      pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
-      pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
-      pr "\n";
-      pr "  /* This should definitely not happen. */\n";
-      pr "  if (ctx->cb_sequence != 0) {\n";
-      pr "    ctx->cb_sequence = 9999;\n";
-      pr "    error (g, \"%%s: internal error: reply callback called twice\", \"%s\");\n" name;
-      pr "    return;\n";
-      pr "  }\n";
-      pr "\n";
-      pr "  ml->main_loop_quit (ml, g);\n";
-      pr "\n";
-      pr "  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
-      pr "    error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
-      pr "    return;\n";
-      pr "  }\n";
-      pr "  if (ctx->hdr.status == GUESTFS_STATUS_ERROR) {\n";
-      pr "    if (!xdr_guestfs_message_error (xdr, &ctx->err)) {\n";
-      pr "      error (g, \"%%s: failed to parse reply error\", \"%s\");\n"
-        name;
-      pr "      return;\n";
-      pr "    }\n";
-      pr "    goto done;\n";
-      pr "  }\n";
-
-      (match fst style with
-       | RErr -> ()
-       | RConstString _ | RConstOptString _ ->
-           failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-       | RInt _ | RInt64 _
-       | RBool _ | RString _ | RStringList _
-       | RStruct _ | RStructList _
-       | RHashtable _ | RBufferOut _ ->
-           pr "  if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name;
-           pr "    error (g, \"%%s: failed to parse reply\", \"%s\");\n" name;
-           pr "    return;\n";
-           pr "  }\n";
-      );
+      trace_call shortname style;
+      pr "  return guestfs__%s " shortname;
+      generate_c_call_args ~handle:"g" style;
+      pr ";\n";
+      pr "}\n";
+      pr "\n"
+  ) non_daemon_functions;
 
 
-      pr " done:\n";
-      pr "  ctx->cb_sequence = 1;\n";
-      pr "}\n\n";
+  (* Client-side stubs for each function. *)
+  List.iter (
+    fun (shortname, style, _, _, _, _, _) ->
+      let name = "guestfs_" ^ shortname in
 
       (* Generate the action stub. *)
       generate_prototype ~extern:false ~semicolon:false ~newline:true
 
       (* Generate the action stub. *)
       generate_prototype ~extern:false ~semicolon:false ~newline:true
@@ -4673,20 +5254,32 @@ check_state (guestfs_h *g, const char *caller)
        | _ -> pr "  struct %s_args args;\n" name
       );
 
        | _ -> pr "  struct %s_args args;\n" name
       );
 
-      pr "  struct %s_ctx ctx;\n" shortname;
-      pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
+      pr "  guestfs_message_header hdr;\n";
+      pr "  guestfs_message_error err;\n";
+      let has_ret =
+        match fst style with
+        | RErr -> false
+        | RConstString _ | RConstOptString _ ->
+            failwithf "RConstString|RConstOptString cannot be used by daemon functions"
+        | RInt _ | RInt64 _
+        | RBool _ | RString _ | RStringList _
+        | RStruct _ | RStructList _
+        | RHashtable _ | RBufferOut _ ->
+            pr "  struct %s_ret ret;\n" name;
+            true in
+
       pr "  int serial;\n";
       pr "  int serial;\n";
+      pr "  int r;\n";
       pr "\n";
       pr "\n";
+      trace_call shortname style;
       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
-      pr "  guestfs_set_busy (g);\n";
-      pr "\n";
-      pr "  memset (&ctx, 0, sizeof ctx);\n";
+      pr "  guestfs___set_busy (g);\n";
       pr "\n";
 
       (* Send the main header and arguments. *)
       (match snd style with
        | [] ->
       pr "\n";
 
       (* Send the main header and arguments. *)
       (match snd style with
        | [] ->
-           pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
+           pr "  serial = guestfs___send (g, GUESTFS_PROC_%s, NULL, NULL);\n"
              (String.uppercase shortname)
        | args ->
            List.iter (
              (String.uppercase shortname)
        | args ->
            List.iter (
@@ -4702,15 +5295,17 @@ check_state (guestfs_h *g, const char *caller)
                  pr "  args.%s = %s;\n" n n
              | Int n ->
                  pr "  args.%s = %s;\n" n n
                  pr "  args.%s = %s;\n" n n
              | Int n ->
                  pr "  args.%s = %s;\n" n n
+             | Int64 n ->
+                 pr "  args.%s = %s;\n" n n
              | FileIn _ | FileOut _ -> ()
            ) args;
              | FileIn _ | FileOut _ -> ()
            ) args;
-           pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
+           pr "  serial = guestfs___send (g, GUESTFS_PROC_%s,\n"
              (String.uppercase shortname);
            pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
              name;
       );
       pr "  if (serial == -1) {\n";
              (String.uppercase shortname);
            pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
              name;
       );
       pr "  if (serial == -1) {\n";
-      pr "    guestfs_end_busy (g);\n";
+      pr "    guestfs___end_busy (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
@@ -4720,47 +5315,48 @@ check_state (guestfs_h *g, const char *caller)
       List.iter (
         function
         | FileIn n ->
       List.iter (
         function
         | FileIn n ->
-            pr "  {\n";
-            pr "    int r;\n";
-            pr "\n";
-            pr "    r = guestfs__send_file_sync (g, %s);\n" n;
-            pr "    if (r == -1) {\n";
-            pr "      guestfs_end_busy (g);\n";
-            pr "      return %s;\n" error_code;
-            pr "    }\n";
-            pr "    if (r == -2) /* daemon cancelled */\n";
-            pr "      goto read_reply;\n";
-            need_read_reply_label := true;
+            pr "  r = guestfs___send_file (g, %s);\n" n;
+            pr "  if (r == -1) {\n";
+            pr "    guestfs___end_busy (g);\n";
+            pr "    return %s;\n" error_code;
             pr "  }\n";
             pr "  }\n";
+            pr "  if (r == -2) /* daemon cancelled */\n";
+            pr "    goto read_reply;\n";
+            need_read_reply_label := true;
             pr "\n";
         | _ -> ()
       ) (snd style);
 
       (* Wait for the reply from the remote end. *)
       if !need_read_reply_label then pr " read_reply:\n";
             pr "\n";
         | _ -> ()
       ) (snd style);
 
       (* Wait for the reply from the remote end. *)
       if !need_read_reply_label then pr " read_reply:\n";
-      pr "  guestfs__switch_to_receiving (g);\n";
-      pr "  ctx.cb_sequence = 0;\n";
-      pr "  guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname;
-      pr "  (void) ml->main_loop_run (ml, g);\n";
-      pr "  guestfs_set_reply_callback (g, NULL, NULL);\n";
-      pr "  if (ctx.cb_sequence != 1) {\n";
-      pr "    error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
-      pr "    guestfs_end_busy (g);\n";
+      pr "  memset (&hdr, 0, sizeof hdr);\n";
+      pr "  memset (&err, 0, sizeof err);\n";
+      if has_ret then pr "  memset (&ret, 0, sizeof ret);\n";
+      pr "\n";
+      pr "  r = guestfs___recv (g, \"%s\", &hdr, &err,\n        " shortname;
+      if not has_ret then
+        pr "NULL, NULL"
+      else
+        pr "(xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret" shortname;
+      pr ");\n";
+
+      pr "  if (r == -1) {\n";
+      pr "    guestfs___end_busy (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
 
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
 
-      pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
+      pr "  if (check_reply_header (g, &hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
         (String.uppercase shortname);
         (String.uppercase shortname);
-      pr "    guestfs_end_busy (g);\n";
+      pr "    guestfs___end_busy (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
 
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
 
-      pr "  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n";
-      pr "    error (g, \"%%s\", ctx.err.error_message);\n";
-      pr "    free (ctx.err.error_message);\n";
-      pr "    guestfs_end_busy (g);\n";
+      pr "  if (hdr.status == GUESTFS_STATUS_ERROR) {\n";
+      pr "    error (g, \"%%s: %%s\", \"%s\", err.error_message);\n" shortname;
+      pr "    free (err.error_message);\n";
+      pr "    guestfs___end_busy (g);\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
       pr "    return %s;\n" error_code;
       pr "  }\n";
       pr "\n";
@@ -4769,41 +5365,53 @@ check_state (guestfs_h *g, const char *caller)
       List.iter (
         function
         | FileOut n ->
       List.iter (
         function
         | FileOut n ->
-            pr "  if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
-            pr "    guestfs_end_busy (g);\n";
+            pr "  if (guestfs___recv_file (g, %s) == -1) {\n" n;
+            pr "    guestfs___end_busy (g);\n";
             pr "    return %s;\n" error_code;
             pr "  }\n";
             pr "\n";
         | _ -> ()
       ) (snd style);
 
             pr "    return %s;\n" error_code;
             pr "  }\n";
             pr "\n";
         | _ -> ()
       ) (snd style);
 
-      pr "  guestfs_end_busy (g);\n";
+      pr "  guestfs___end_busy (g);\n";
 
       (match fst style with
        | RErr -> pr "  return 0;\n"
        | RInt n | RInt64 n | RBool n ->
 
       (match fst style with
        | RErr -> pr "  return 0;\n"
        | RInt n | RInt64 n | RBool n ->
-           pr "  return ctx.ret.%s;\n" n
+           pr "  return ret.%s;\n" n
        | RConstString _ | RConstOptString _ ->
            failwithf "RConstString|RConstOptString cannot be used by daemon functions"
        | RString n ->
        | RConstString _ | RConstOptString _ ->
            failwithf "RConstString|RConstOptString cannot be used by daemon functions"
        | RString n ->
-           pr "  return ctx.ret.%s; /* caller will free */\n" n
+           pr "  return ret.%s; /* caller will free */\n" n
        | RStringList n | RHashtable n ->
            pr "  /* caller will free this, but we need to add a NULL entry */\n";
        | RStringList n | RHashtable n ->
            pr "  /* caller will free this, but we need to add a NULL entry */\n";
-           pr "  ctx.ret.%s.%s_val =\n" n n;
-           pr "    safe_realloc (g, ctx.ret.%s.%s_val,\n" n n;
-           pr "                  sizeof (char *) * (ctx.ret.%s.%s_len + 1));\n"
+           pr "  ret.%s.%s_val =\n" n n;
+           pr "    safe_realloc (g, ret.%s.%s_val,\n" n n;
+           pr "                  sizeof (char *) * (ret.%s.%s_len + 1));\n"
              n n;
              n n;
-           pr "  ctx.ret.%s.%s_val[ctx.ret.%s.%s_len] = NULL;\n" n n n n;
-           pr "  return ctx.ret.%s.%s_val;\n" n n
+           pr "  ret.%s.%s_val[ret.%s.%s_len] = NULL;\n" n n n n;
+           pr "  return ret.%s.%s_val;\n" n n
        | RStruct (n, _) ->
            pr "  /* caller will free this */\n";
        | RStruct (n, _) ->
            pr "  /* caller will free this */\n";
-           pr "  return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n
+           pr "  return safe_memdup (g, &ret.%s, sizeof (ret.%s));\n" n n
        | RStructList (n, _) ->
            pr "  /* caller will free this */\n";
        | RStructList (n, _) ->
            pr "  /* caller will free this */\n";
-           pr "  return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n
+           pr "  return safe_memdup (g, &ret.%s, sizeof (ret.%s));\n" n n
        | RBufferOut n ->
        | RBufferOut n ->
-           pr "  *size_r = ctx.ret.%s.%s_len;\n" n n;
-           pr "  return ctx.ret.%s.%s_val; /* caller will free */\n" n n
+           pr "  /* RBufferOut is tricky: If the buffer is zero-length, then\n";
+           pr "   * _val might be NULL here.  To make the API saner for\n";
+           pr "   * callers, we turn this case into a unique pointer (using\n";
+           pr "   * malloc(1)).\n";
+           pr "   */\n";
+           pr "  if (ret.%s.%s_len > 0) {\n" n n;
+           pr "    *size_r = ret.%s.%s_len;\n" n n;
+           pr "    return ret.%s.%s_val; /* caller will free */\n" n n;
+           pr "  } else {\n";
+           pr "    free (ret.%s.%s_val);\n" n n;
+           pr "    char *p = safe_malloc (g, 1);\n";
+           pr "    *size_r = ret.%s.%s_len;\n" n n;
+           pr "    return p;\n";
+           pr "  }\n";
       );
 
       pr "}\n\n"
       );
 
       pr "}\n\n"
@@ -4860,11 +5468,11 @@ and generate_daemon_actions () =
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
   pr "#include <inttypes.h>\n";
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
   pr "#include <inttypes.h>\n";
-  pr "#include <ctype.h>\n";
   pr "#include <rpc/types.h>\n";
   pr "#include <rpc/xdr.h>\n";
   pr "\n";
   pr "#include \"daemon.h\"\n";
   pr "#include <rpc/types.h>\n";
   pr "#include <rpc/xdr.h>\n";
   pr "\n";
   pr "#include \"daemon.h\"\n";
+  pr "#include \"c-ctype.h\"\n";
   pr "#include \"../src/guestfs_protocol.h\"\n";
   pr "#include \"actions.h\"\n";
   pr "\n";
   pr "#include \"../src/guestfs_protocol.h\"\n";
   pr "#include \"actions.h\"\n";
   pr "\n";
@@ -4886,7 +5494,7 @@ and generate_daemon_actions () =
         | RStruct (_, typ) -> pr "  guestfs_int_%s *r;\n" typ; "NULL"
         | RStructList (_, typ) -> pr "  guestfs_int_%s_list *r;\n" typ; "NULL"
         | RBufferOut _ ->
         | RStruct (_, typ) -> pr "  guestfs_int_%s *r;\n" typ; "NULL"
         | RStructList (_, typ) -> pr "  guestfs_int_%s_list *r;\n" typ; "NULL"
         | RBufferOut _ ->
-            pr "  size_t size;\n";
+            pr "  size_t size = 1;\n";
             pr "  char *r;\n";
             "NULL" in
 
             pr "  char *r;\n";
             "NULL" in
 
@@ -4903,6 +5511,7 @@ and generate_daemon_actions () =
              | StringList n | DeviceList n -> pr "  char **%s;\n" n
              | Bool n -> pr "  int %s;\n" n
              | Int n -> pr "  int %s;\n" n
              | StringList n | DeviceList n -> pr "  char **%s;\n" n
              | Bool n -> pr "  int %s;\n" n
              | Int n -> pr "  int %s;\n" n
+             | Int64 n -> pr "  int64_t %s;\n" n
              | FileIn _ | FileOut _ -> ()
            ) args
       );
              | FileIn _ | FileOut _ -> ()
            ) args
       );
@@ -4954,6 +5563,7 @@ and generate_daemon_actions () =
                  pr "  }\n";
              | Bool n -> pr "  %s = args.%s;\n" n n
              | Int n -> pr "  %s = args.%s;\n" n n
                  pr "  }\n";
              | Bool n -> pr "  %s = args.%s;\n" n n
              | Int n -> pr "  %s = args.%s;\n" n n
+             | Int64 n -> pr "  %s = args.%s;\n" n n
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "\n"
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "\n"
@@ -4977,10 +5587,24 @@ and generate_daemon_actions () =
       generate_c_call_args (fst style, args');
       pr ";\n";
 
       generate_c_call_args (fst style, args');
       pr ";\n";
 
-      pr "  if (r == %s)\n" error_code;
-      pr "    /* do_%s has already called reply_with_error */\n" name;
-      pr "    goto done;\n";
-      pr "\n";
+      (match fst style with
+       | RErr | RInt _ | RInt64 _ | RBool _
+       | RConstString _ | RConstOptString _
+       | RString _ | RStringList _ | RHashtable _
+       | RStruct (_, _) | RStructList (_, _) ->
+           pr "  if (r == %s)\n" error_code;
+           pr "    /* do_%s has already called reply_with_error */\n" name;
+           pr "    goto done;\n";
+           pr "\n"
+       | RBufferOut _ ->
+           pr "  /* size == 0 && r == NULL could be a non-error case (just\n";
+           pr "   * an ordinary zero-length buffer), so be careful ...\n";
+           pr "   */\n";
+           pr "  if (size == 1 && r == %s)\n" error_code;
+           pr "    /* do_%s has already called reply_with_error */\n" name;
+           pr "    goto done;\n";
+           pr "\n"
+      );
 
       (* If there are any FileOut parameters, then the impl must
        * send its own reply.
 
       (* If there are any FileOut parameters, then the impl must
        * send its own reply.
@@ -5090,7 +5714,7 @@ and generate_daemon_actions () =
         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
         pr "    return -1;\n";
         pr "  }\n";
         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
         pr "    return -1;\n";
         pr "  }\n";
-        pr "  if (!*str || isspace (*str)) {\n";
+        pr "  if (!*str || c_isspace (*str)) {\n";
         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
         pr "    return -1;\n";
         pr "  }\n";
         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
         pr "    return -1;\n";
         pr "  }\n";
@@ -5192,7 +5816,7 @@ and generate_daemon_actions () =
         pr "      pend++;\n";
         pr "    }\n";
         pr "\n";
         pr "      pend++;\n";
         pr "    }\n";
         pr "\n";
-        pr "    while (*p && isspace (*p))     /* Skip any leading whitespace. */\n";
+        pr "    while (*p && c_isspace (*p))   /* Skip any leading whitespace. */\n";
         pr "      p++;\n";
         pr "\n";
         pr "    if (!*p) {                     /* Empty line?  Skip it. */\n";
         pr "      p++;\n";
         pr "\n";
         pr "    if (!*p) {                     /* Empty line?  Skip it. */\n";
@@ -5263,6 +5887,7 @@ and generate_tests () =
 #include <fcntl.h>
 
 #include \"guestfs.h\"
 #include <fcntl.h>
 
 #include \"guestfs.h\"
+#include \"guestfs-internal.h\"
 
 static guestfs_h *g;
 static int suppress_error = 0;
 
 static guestfs_h *g;
 static int suppress_error = 0;
@@ -5337,7 +5962,7 @@ static void print_table (char const *const *argv)
 int main (int argc, char *argv[])
 {
   char c = 0;
 int main (int argc, char *argv[])
 {
   char c = 0;
-  int failed = 0;
+  unsigned long int n_failed = 0;
   const char *filename;
   int fd;
   int nr_tests, test_num = 0;
   const char *filename;
   int fd;
   int nr_tests, test_num = 0;
@@ -5440,8 +6065,8 @@ int main (int argc, char *argv[])
     exit (1);
   }
 
     exit (1);
   }
 
-  if (guestfs_add_drive_ro (g, \"../images/test.sqsh\") == -1) {
-    printf (\"guestfs_add_drive_ro ../images/test.sqsh FAILED\\n\");
+  if (guestfs_add_drive_ro (g, \"../images/test.iso\") == -1) {
+    printf (\"guestfs_add_drive_ro ../images/test.iso FAILED\\n\");
     exit (1);
   }
 
     exit (1);
   }
 
@@ -5453,11 +6078,6 @@ int main (int argc, char *argv[])
   /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
   alarm (600);
 
   /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
   alarm (600);
 
-  if (guestfs_wait_ready (g) == -1) {
-    printf (\"guestfs_wait_ready FAILED\\n\");
-    exit (1);
-  }
-
   /* Cancel previous alarm. */
   alarm (0);
 
   /* Cancel previous alarm. */
   alarm (0);
 
@@ -5471,7 +6091,7 @@ int main (int argc, char *argv[])
       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
       pr "  if (%s () == -1) {\n" test_name;
       pr "    printf (\"%s FAILED\\n\");\n" test_name;
       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
       pr "  if (%s () == -1) {\n" test_name;
       pr "    printf (\"%s FAILED\\n\");\n" test_name;
-      pr "    failed++;\n";
+      pr "    n_failed++;\n";
       pr "  }\n";
   ) test_names;
   pr "\n";
       pr "  }\n";
   ) test_names;
   pr "\n";
@@ -5482,8 +6102,8 @@ int main (int argc, char *argv[])
   pr "  unlink (\"test3.img\");\n";
   pr "\n";
 
   pr "  unlink (\"test3.img\");\n";
   pr "\n";
 
-  pr "  if (failed > 0) {\n";
-  pr "    printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n";
+  pr "  if (n_failed > 0) {\n";
+  pr "    printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n";
   pr "    exit (1);\n";
   pr "  }\n";
   pr "\n";
   pr "    exit (1);\n";
   pr "  }\n";
   pr "\n";
@@ -5503,9 +6123,9 @@ static int %s_skip (void)
   if (str)
     return strstr (str, \"%s\") == NULL;
   str = getenv (\"SKIP_%s\");
   if (str)
     return strstr (str, \"%s\") == NULL;
   str = getenv (\"SKIP_%s\");
-  if (str && strcmp (str, \"1\") == 0) return 1;
+  if (str && STREQ (str, \"1\")) return 1;
   str = getenv (\"SKIP_TEST_%s\");
   str = getenv (\"SKIP_TEST_%s\");
-  if (str && strcmp (str, \"1\") == 0) return 1;
+  if (str && STREQ (str, \"1\")) return 1;
   return 0;
 }
 
   return 0;
 }
 
@@ -5576,14 +6196,14 @@ and generate_one_test_body name i test_name init test =
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["sfdiskM"; "/dev/sda"; ","]]
+          ["part_disk"; "/dev/sda"; "mbr"]]
    | InitBasicFS ->
        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
        List.iter (generate_test_command_call test_name)
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
    | InitBasicFS ->
        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
        List.iter (generate_test_command_call test_name)
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["sfdiskM"; "/dev/sda"; ","];
+          ["part_disk"; "/dev/sda"; "mbr"];
           ["mkfs"; "ext2"; "/dev/sda1"];
           ["mount"; "/dev/sda1"; "/"]]
    | InitBasicFSonLVM ->
           ["mkfs"; "ext2"; "/dev/sda1"];
           ["mount"; "/dev/sda1"; "/"]]
    | InitBasicFSonLVM ->
@@ -5593,19 +6213,19 @@ and generate_one_test_body name i test_name init test =
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["sfdiskM"; "/dev/sda"; ","];
+          ["part_disk"; "/dev/sda"; "mbr"];
           ["pvcreate"; "/dev/sda1"];
           ["vgcreate"; "VG"; "/dev/sda1"];
           ["lvcreate"; "LV"; "VG"; "8"];
           ["mkfs"; "ext2"; "/dev/VG/LV"];
           ["mount"; "/dev/VG/LV"; "/"]]
           ["pvcreate"; "/dev/sda1"];
           ["vgcreate"; "VG"; "/dev/sda1"];
           ["lvcreate"; "LV"; "VG"; "8"];
           ["mkfs"; "ext2"; "/dev/VG/LV"];
           ["mount"; "/dev/VG/LV"; "/"]]
-   | InitSquashFS ->
-       pr "  /* InitSquashFS for %s */\n" test_name;
+   | InitISOFS ->
+       pr "  /* InitISOFS for %s */\n" test_name;
        List.iter (generate_test_command_call test_name)
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
        List.iter (generate_test_command_call test_name)
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"]]
+          ["mount_ro"; "/dev/sdd"; "/"]]
   );
 
   let get_seq_last = function
   );
 
   let get_seq_last = function
@@ -5626,7 +6246,7 @@ and generate_one_test_body name i test_name init test =
       pr "  const char *expected = \"%s\";\n" (c_quote expected);
       let seq, last = get_seq_last seq in
       let test () =
       pr "  const char *expected = \"%s\";\n" (c_quote expected);
       let seq, last = get_seq_last seq in
       let test () =
-        pr "    if (strcmp (r, expected) != 0) {\n";
+        pr "    if (STRNEQ (r, expected)) {\n";
         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
         pr "      return -1;\n";
         pr "    }\n"
         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
         pr "      return -1;\n";
         pr "    }\n"
@@ -5646,7 +6266,7 @@ and generate_one_test_body name i test_name init test =
             pr "    }\n";
             pr "    {\n";
             pr "      const char *expected = \"%s\";\n" (c_quote str);
             pr "    }\n";
             pr "    {\n";
             pr "      const char *expected = \"%s\";\n" (c_quote str);
-            pr "      if (strcmp (r[%d], expected) != 0) {\n" i;
+            pr "      if (STRNEQ (r[%d], expected)) {\n" i;
             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
             pr "        return -1;\n";
             pr "      }\n";
             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
             pr "        return -1;\n";
             pr "      }\n";
@@ -5675,7 +6295,7 @@ and generate_one_test_body name i test_name init test =
             pr "    {\n";
             pr "      const char *expected = \"%s\";\n" (c_quote str);
             pr "      r[%d][5] = 's';\n" i;
             pr "    {\n";
             pr "      const char *expected = \"%s\";\n" (c_quote str);
             pr "      r[%d][5] = 's';\n" i;
-            pr "      if (strcmp (r[%d], expected) != 0) {\n" i;
+            pr "      if (STRNEQ (r[%d], expected)) {\n" i;
             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
             pr "        return -1;\n";
             pr "      }\n";
             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
             pr "        return -1;\n";
             pr "      }\n";
@@ -5771,7 +6391,7 @@ and generate_one_test_body name i test_name init test =
         pr "      fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len;
         pr "      return -1;\n";
         pr "    }\n";
         pr "      fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len;
         pr "      return -1;\n";
         pr "    }\n";
-        pr "    if (strncmp (r, expected, size) != 0) {\n";
+        pr "    if (STRNEQLEN (r, expected, size)) {\n";
         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
         pr "      return -1;\n";
         pr "    }\n"
         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
         pr "      return -1;\n";
         pr "    }\n"
@@ -5799,7 +6419,7 @@ and generate_one_test_body name i test_name init test =
               pr "      return -1;\n";
               pr "    }\n"
           | CompareWithString (field, expected) ->
               pr "      return -1;\n";
               pr "    }\n"
           | CompareWithString (field, expected) ->
-              pr "    if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
+              pr "    if (STRNEQ (r->%s, \"%s\")) {\n" field expected;
               pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
                 test_name field expected;
               pr "               r->%s);\n" field;
               pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
                 test_name field expected;
               pr "               r->%s);\n" field;
@@ -5813,7 +6433,7 @@ and generate_one_test_body name i test_name init test =
               pr "      return -1;\n";
               pr "    }\n"
           | CompareFieldsStrEq (field1, field2) ->
               pr "      return -1;\n";
               pr "    }\n"
           | CompareFieldsStrEq (field1, field2) ->
-              pr "    if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2;
+              pr "    if (STRNEQ (r->%s, r->%s)) {\n" field1 field2;
               pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
                 test_name field1 field2;
               pr "               r->%s, r->%s);\n" field1 field2;
               pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
                 test_name field1 field2;
               pr "               r->%s, r->%s);\n" field1 field2;
@@ -5861,6 +6481,7 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
         | OptString n, arg ->
             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
         | Int _, _
         | OptString n, arg ->
             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
         | Int _, _
+        | Int64 _, _
         | Bool _, _
         | FileIn _, _ | FileOut _, _ -> ()
         | StringList n, arg | DeviceList n, arg ->
         | Bool _, _
         | FileIn _, _ | FileOut _, _ -> ()
         | StringList n, arg | DeviceList n, arg ->
@@ -5919,6 +6540,12 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
               with Failure "int_of_string" ->
                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
             pr ", %d" i
               with Failure "int_of_string" ->
                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
             pr ", %d" i
+        | Int64 _, arg ->
+            let i =
+              try Int64.of_string arg
+              with Failure "int_of_string" ->
+                failwithf "%s: expecting an int64, but got '%s'" test_name arg in
+            pr ", %Ld" i
         | Bool _, arg ->
             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
       ) (List.combine (snd style) args);
         | Bool _, arg ->
             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
       ) (List.combine (snd style) args);
@@ -5982,9 +6609,9 @@ and generate_fish_cmds () =
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
   pr "#include <inttypes.h>\n";
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
   pr "#include <inttypes.h>\n";
-  pr "#include <ctype.h>\n";
   pr "\n";
   pr "#include <guestfs.h>\n";
   pr "\n";
   pr "#include <guestfs.h>\n";
+  pr "#include \"c-ctype.h\"\n";
   pr "#include \"fish.h\"\n";
   pr "\n";
 
   pr "#include \"fish.h\"\n";
   pr "\n";
 
@@ -6018,8 +6645,8 @@ and generate_fish_cmds () =
         match snd style with
         | [] -> name2
         | args ->
         match snd style with
         | [] -> name2
         | args ->
-            sprintf "%s <%s>"
-              name2 (String.concat "> <" (List.map name_of_argt args)) in
+            sprintf "%s %s"
+              name2 (String.concat " " (List.map name_of_argt args)) in
 
       let warnings =
         if List.mem ProtocolLimitWarning flags then
 
       let warnings =
         if List.mem ProtocolLimitWarning flags then
@@ -6048,21 +6675,38 @@ and generate_fish_cmds () =
         else "" in
 
       pr "  if (";
         else "" in
 
       pr "  if (";
-      pr "strcasecmp (cmd, \"%s\") == 0" name;
+      pr "STRCASEEQ (cmd, \"%s\")" name;
       if name <> name2 then
       if name <> name2 then
-        pr " || strcasecmp (cmd, \"%s\") == 0" name2;
+        pr " || STRCASEEQ (cmd, \"%s\")" name2;
       if name <> alias then
       if name <> alias then
-        pr " || strcasecmp (cmd, \"%s\") == 0" alias;
+        pr " || STRCASEEQ (cmd, \"%s\")" alias;
       pr ")\n";
       pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
         name2 shortdesc
       pr ")\n";
       pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
         name2 shortdesc
-        (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
+        ("=head1 SYNOPSIS\n\n " ^ synopsis ^ "\n\n" ^
+         "=head1 DESCRIPTION\n\n" ^
+         longdesc ^ warnings ^ describe_alias);
       pr "  else\n"
   ) all_functions;
   pr "    display_builtin_command (cmd);\n";
   pr "}\n";
   pr "\n";
 
       pr "  else\n"
   ) all_functions;
   pr "    display_builtin_command (cmd);\n";
   pr "}\n";
   pr "\n";
 
+  let emit_print_list_function typ =
+    pr "static void print_%s_list (struct guestfs_%s_list *%ss)\n"
+      typ typ typ;
+    pr "{\n";
+    pr "  unsigned int i;\n";
+    pr "\n";
+    pr "  for (i = 0; i < %ss->len; ++i) {\n" typ;
+    pr "    printf (\"[%%d] = {\\n\", i);\n";
+    pr "    print_%s_indent (&%ss->val[i], \"  \");\n" typ typ;
+    pr "    printf (\"}\\n\");\n";
+    pr "  }\n";
+    pr "}\n";
+    pr "\n";
+  in
+
   (* print_* functions *)
   List.iter (
     fun (typ, cols) ->
   (* print_* functions *)
   List.iter (
     fun (typ, cols) ->
@@ -6072,7 +6716,7 @@ and generate_fish_cmds () =
       pr "static void print_%s_indent (struct guestfs_%s *%s, const char *indent)\n" typ typ typ;
       pr "{\n";
       if needs_i then (
       pr "static void print_%s_indent (struct guestfs_%s *%s, const char *indent)\n" typ typ typ;
       pr "{\n";
       if needs_i then (
-        pr "  int i;\n";
+        pr "  unsigned int i;\n";
         pr "\n"
       );
       List.iter (
         pr "\n"
       );
       List.iter (
@@ -6080,17 +6724,17 @@ and generate_fish_cmds () =
         | name, FString ->
             pr "  printf (\"%%s%s: %%s\\n\", indent, %s->%s);\n" name typ name
         | name, FUUID ->
         | name, FString ->
             pr "  printf (\"%%s%s: %%s\\n\", indent, %s->%s);\n" name typ name
         | name, FUUID ->
-            pr "  printf (\"%s: \");\n" name;
+            pr "  printf (\"%%s%s: \", indent);\n" name;
             pr "  for (i = 0; i < 32; ++i)\n";
             pr "  for (i = 0; i < 32; ++i)\n";
-            pr "    printf (\"%%s%%c\", indent, %s->%s[i]);\n" typ name;
+            pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
             pr "  printf (\"\\n\");\n"
         | name, FBuffer ->
             pr "  printf (\"%%s%s: \", indent);\n" name;
             pr "  for (i = 0; i < %s->%s_len; ++i)\n" typ name;
             pr "  printf (\"\\n\");\n"
         | name, FBuffer ->
             pr "  printf (\"%%s%s: \", indent);\n" name;
             pr "  for (i = 0; i < %s->%s_len; ++i)\n" typ name;
-            pr "    if (isprint (%s->%s[i]))\n" typ name;
-            pr "      printf (\"%%s%%c\", indent, %s->%s[i]);\n" typ name;
+            pr "    if (c_isprint (%s->%s[i]))\n" typ name;
+            pr "      printf (\"%%c\", %s->%s[i]);\n" typ name;
             pr "    else\n";
             pr "    else\n";
-            pr "      printf (\"%%s\\\\x%%02x\", indent, %s->%s[i]);\n" typ name;
+            pr "      printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name;
             pr "  printf (\"\\n\");\n"
         | name, (FUInt64|FBytes) ->
             pr "  printf (\"%%s%s: %%\" PRIu64 \"\\n\", indent, %s->%s);\n"
             pr "  printf (\"\\n\");\n"
         | name, (FUInt64|FBytes) ->
             pr "  printf (\"%%s%s: %%\" PRIu64 \"\\n\", indent, %s->%s);\n"
@@ -6114,25 +6758,29 @@ and generate_fish_cmds () =
       ) cols;
       pr "}\n";
       pr "\n";
       ) cols;
       pr "}\n";
       pr "\n";
-      pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
-      pr "{\n";
-      pr "  print_%s_indent (%s, \"\");\n" typ typ;
-      pr "}\n";
-      pr "\n";
-      pr "static void print_%s_list (struct guestfs_%s_list *%ss)\n"
-        typ typ typ;
-      pr "{\n";
-      pr "  int i;\n";
-      pr "\n";
-      pr "  for (i = 0; i < %ss->len; ++i) {\n" typ;
-      pr "    printf (\"[%%d] = {\\n\", i);\n";
-      pr "    print_%s_indent (&%ss->val[i], \"  \");\n" typ typ;
-      pr "    printf (\"}\\n\");\n";
-      pr "  }\n";
-      pr "}\n";
-      pr "\n";
   ) structs;
 
   ) structs;
 
+  (* Emit a print_TYPE_list function definition only if that function is used. *)
+  List.iter (
+    function
+    | typ, (RStructListOnly | RStructAndList) ->
+        (* generate the function for typ *)
+        emit_print_list_function typ
+    | typ, _ -> () (* empty *)
+  ) (rstructs_used_by all_functions);
+
+  (* Emit a print_TYPE function definition only if that function is used. *)
+  List.iter (
+    function
+    | typ, (RStructOnly | RStructAndList) ->
+        pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
+        pr "{\n";
+        pr "  print_%s_indent (%s, \"\");\n" typ typ;
+        pr "}\n";
+        pr "\n";
+    | typ, _ -> () (* empty *)
+  ) (rstructs_used_by all_functions);
+
   (* run_<action> actions *)
   List.iter (
     fun (name, style, _, flags, _, _, _) ->
   (* run_<action> actions *)
   List.iter (
     fun (name, style, _, flags, _, _, _) ->
@@ -6154,15 +6802,17 @@ and generate_fish_cmds () =
       );
       List.iter (
         function
       );
       List.iter (
         function
-        | Pathname n
-        | Device n | Dev_or_Path n
+        | Device n
         | String n
         | OptString n
         | FileIn n
         | FileOut n -> pr "  const char *%s;\n" n
         | String n
         | OptString n
         | FileIn n
         | FileOut n -> pr "  const char *%s;\n" n
-        | StringList n | DeviceList n -> pr "  char *const *%s;\n" n
+        | Pathname n
+        | Dev_or_Path n -> pr "  char *%s;\n" n
+        | StringList n | DeviceList n -> pr "  char **%s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
+        | Int64 n -> pr "  int64_t %s;\n" n
       ) (snd style);
 
       (* Check and convert parameters. *)
       ) (snd style);
 
       (* Check and convert parameters. *)
@@ -6176,23 +6826,31 @@ and generate_fish_cmds () =
       iteri (
         fun i ->
           function
       iteri (
         fun i ->
           function
+          | Device name
+          | String name ->
+              pr "  %s = argv[%d];\n" name i
           | Pathname name
           | Pathname name
-          | Device name | Dev_or_Path name | String name -> pr "  %s = argv[%d];\n" name i
+          | Dev_or_Path name ->
+              pr "  %s = resolve_win_path (argv[%d]);\n" name i;
+              pr "  if (%s == NULL) return -1;\n" name
           | OptString name ->
           | OptString name ->
-              pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
+              pr "  %s = STRNEQ (argv[%d], \"\") ? argv[%d] : NULL;\n"
                 name i i
           | FileIn name ->
                 name i i
           | FileIn name ->
-              pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdin\";\n"
+              pr "  %s = STRNEQ (argv[%d], \"-\") ? argv[%d] : \"/dev/stdin\";\n"
                 name i i
           | FileOut name ->
                 name i i
           | FileOut name ->
-              pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdout\";\n"
+              pr "  %s = STRNEQ (argv[%d], \"-\") ? argv[%d] : \"/dev/stdout\";\n"
                 name i i
           | StringList name | DeviceList name ->
                 name i i
           | StringList name | DeviceList name ->
-              pr "  %s = parse_string_list (argv[%d]);\n" name i
+              pr "  %s = parse_string_list (argv[%d]);\n" name i;
+              pr "  if (%s == NULL) return -1;\n" name;
           | Bool name ->
               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
           | Int name ->
               pr "  %s = atoi (argv[%d]);\n" name i
           | Bool name ->
               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
           | Int name ->
               pr "  %s = atoi (argv[%d]);\n" name i
+          | Int64 name ->
+              pr "  %s = atoll (argv[%d]);\n" name i
       ) (snd style);
 
       (* Call C API function. *)
       ) (snd style);
 
       (* Call C API function. *)
@@ -6203,6 +6861,17 @@ and generate_fish_cmds () =
       generate_c_call_args ~handle:"g" style;
       pr ";\n";
 
       generate_c_call_args ~handle:"g" style;
       pr ";\n";
 
+      List.iter (
+        function
+        | Device name | String name
+        | OptString name | FileIn name | FileOut name | Bool name
+        | Int name | Int64 name -> ()
+        | Pathname name | Dev_or_Path name ->
+            pr "  free (%s);\n" name
+        | StringList name | DeviceList name ->
+            pr "  free_strings (%s);\n" name
+      ) (snd style);
+
       (* Check return value for errors and display command results. *)
       (match fst style with
        | RErr -> pr "  return r;\n"
       (* Check return value for errors and display command results. *)
       (match fst style with
        | RErr -> pr "  return r;\n"
@@ -6270,11 +6939,11 @@ and generate_fish_cmds () =
         try find_map (function FishAlias n -> Some n | _ -> None) flags
         with Not_found -> name in
       pr "  if (";
         try find_map (function FishAlias n -> Some n | _ -> None) flags
         with Not_found -> name in
       pr "  if (";
-      pr "strcasecmp (cmd, \"%s\") == 0" name;
+      pr "STRCASEEQ (cmd, \"%s\")" name;
       if name <> name2 then
       if name <> name2 then
-        pr " || strcasecmp (cmd, \"%s\") == 0" name2;
+        pr " || STRCASEEQ (cmd, \"%s\")" name2;
       if name <> alias then
       if name <> alias then
-        pr " || strcasecmp (cmd, \"%s\") == 0" alias;
+        pr " || STRCASEEQ (cmd, \"%s\")" alias;
       pr ")\n";
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
       pr ")\n";
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
@@ -6350,7 +7019,7 @@ generator (const char *text, int state)
 
   while ((name = commands[index]) != NULL) {
     index++;
 
   while ((name = commands[index]) != NULL) {
     index++;
-    if (strncasecmp (name, text, len) == 0)
+    if (STRCASEEQLEN (name, text, len))
       return strdup (name);
   }
 
       return strdup (name);
   }
 
@@ -6415,6 +7084,7 @@ and generate_fish_actions_pod () =
         | StringList n | DeviceList n -> pr " '%s ...'" n
         | Bool _ -> pr " true|false"
         | Int n -> pr " %s" n
         | StringList n | DeviceList n -> pr " '%s ...'" n
         | Bool _ -> pr " true|false"
         | Int n -> pr " %s" n
+        | Int64 n -> pr " %s" n
         | FileIn n | FileOut n -> pr " (%s|-)" n
       ) (snd style);
       pr "\n";
         | FileIn n | FileOut n -> pr " (%s|-)" n
       ) (snd style);
       pr "\n";
@@ -6487,6 +7157,7 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
           pr "char *const *%s" n
       | Bool n -> next (); pr "int %s" n
       | Int n -> next (); pr "int %s" n
           pr "char *const *%s" n
       | Bool n -> next (); pr "int %s" n
       | Int n -> next (); pr "int %s" n
+      | Int64 n -> next (); pr "int64_t %s" n
       | FileIn n
       | FileOut n ->
           if not in_daemon then (next (); pr "const char *%s" n)
       | FileIn n
       | FileOut n ->
           if not in_daemon then (next (); pr "const char *%s" n)
@@ -6539,12 +7210,21 @@ type t
 exception Error of string
 (** This exception is raised when there is an error. *)
 
 exception Error of string
 (** This exception is raised when there is an error. *)
 
+exception Handle_closed of string
+(** This exception is raised if you use a {!Guestfs.t} handle
+    after calling {!close} on it.  The string is the name of
+    the function. *)
+
 val create : unit -> t
 val create : unit -> t
+(** Create a {!Guestfs.t} handle. *)
 
 val close : t -> unit
 
 val close : t -> unit
-(** Handles are closed by the garbage collector when they become
-    unreferenced, but callers can also call this in order to
-    provide predictable cleanup. *)
+(** Close the {!Guestfs.t} handle and free up all resources used
+    by it immediately.
+
+    Handles are closed by the garbage collector when they become
+    unreferenced, but callers can call this in order to provide
+    predictable cleanup. *)
 
 ";
   generate_ocaml_structure_decls ();
 
 ";
   generate_ocaml_structure_decls ();
@@ -6555,7 +7235,7 @@ val close : t -> unit
       generate_ocaml_prototype name style;
       pr "(** %s *)\n" shortdesc;
       pr "\n"
       generate_ocaml_prototype name style;
       pr "(** %s *)\n" shortdesc;
       pr "\n"
-  ) all_functions
+  ) all_functions_sorted
 
 (* Generate the OCaml bindings implementation. *)
 and generate_ocaml_ml () =
 
 (* Generate the OCaml bindings implementation. *)
 and generate_ocaml_ml () =
@@ -6563,12 +7243,17 @@ and generate_ocaml_ml () =
 
   pr "\
 type t
 
   pr "\
 type t
+
 exception Error of string
 exception Error of string
+exception Handle_closed of string
+
 external create : unit -> t = \"ocaml_guestfs_create\"
 external close : t -> unit = \"ocaml_guestfs_close\"
 
 external create : unit -> t = \"ocaml_guestfs_create\"
 external close : t -> unit = \"ocaml_guestfs_close\"
 
+(* Give the exceptions names, so they can be raised from the C code. *)
 let () =
 let () =
-  Callback.register_exception \"ocaml_guestfs_error\" (Error \"\")
+  Callback.register_exception \"ocaml_guestfs_error\" (Error \"\");
+  Callback.register_exception \"ocaml_guestfs_closed\" (Handle_closed \"\")
 
 ";
 
 
 ";
 
@@ -6578,7 +7263,7 @@ let () =
   List.iter (
     fun (name, style, _, _, _, shortdesc, _) ->
       generate_ocaml_prototype ~is_external:true name style;
   List.iter (
     fun (name, style, _, _, _, shortdesc, _) ->
       generate_ocaml_prototype ~is_external:true name style;
-  ) all_functions
+  ) all_functions_sorted
 
 (* Generate the OCaml bindings C implementation. *)
 and generate_ocaml_c () =
 
 (* Generate the OCaml bindings C implementation. *)
 and generate_ocaml_c () =
@@ -6631,6 +7316,29 @@ copy_table (char * const * argv)
 ";
 
   (* Struct copy functions. *)
 ";
 
   (* Struct copy functions. *)
+
+  let emit_ocaml_copy_list_function typ =
+    pr "static CAMLprim value\n";
+    pr "copy_%s_list (const struct guestfs_%s_list *%ss)\n" typ typ typ;
+    pr "{\n";
+    pr "  CAMLparam0 ();\n";
+    pr "  CAMLlocal2 (rv, v);\n";
+    pr "  unsigned int i;\n";
+    pr "\n";
+    pr "  if (%ss->len == 0)\n" typ;
+    pr "    CAMLreturn (Atom (0));\n";
+    pr "  else {\n";
+    pr "    rv = caml_alloc (%ss->len, 0);\n" typ;
+    pr "    for (i = 0; i < %ss->len; ++i) {\n" typ;
+    pr "      v = copy_%s (&%ss->val[i]);\n" typ typ;
+    pr "      caml_modify (&Field (rv, i), v);\n";
+    pr "    }\n";
+    pr "    CAMLreturn (rv);\n";
+    pr "  }\n";
+    pr "}\n";
+    pr "\n";
+  in
+
   List.iter (
     fun (typ, cols) ->
       let has_optpercent_col =
   List.iter (
     fun (typ, cols) ->
       let has_optpercent_col =
@@ -6677,38 +7385,37 @@ copy_table (char * const * argv)
       pr "  CAMLreturn (rv);\n";
       pr "}\n";
       pr "\n";
       pr "  CAMLreturn (rv);\n";
       pr "}\n";
       pr "\n";
-
-      pr "static CAMLprim value\n";
-      pr "copy_%s_list (const struct guestfs_%s_list *%ss)\n"
-        typ typ typ;
-      pr "{\n";
-      pr "  CAMLparam0 ();\n";
-      pr "  CAMLlocal2 (rv, v);\n";
-      pr "  int i;\n";
-      pr "\n";
-      pr "  if (%ss->len == 0)\n" typ;
-      pr "    CAMLreturn (Atom (0));\n";
-      pr "  else {\n";
-      pr "    rv = caml_alloc (%ss->len, 0);\n" typ;
-      pr "    for (i = 0; i < %ss->len; ++i) {\n" typ;
-      pr "      v = copy_%s (&%ss->val[i]);\n" typ typ;
-      pr "      caml_modify (&Field (rv, i), v);\n";
-      pr "    }\n";
-      pr "    CAMLreturn (rv);\n";
-      pr "  }\n";
-      pr "}\n";
-      pr "\n";
   ) structs;
 
   ) structs;
 
+  (* Emit a copy_TYPE_list function definition only if that function is used. *)
+  List.iter (
+    function
+    | typ, (RStructListOnly | RStructAndList) ->
+        (* generate the function for typ *)
+        emit_ocaml_copy_list_function typ
+    | typ, _ -> () (* empty *)
+  ) (rstructs_used_by all_functions);
+
   (* The wrappers. *)
   List.iter (
     fun (name, style, _, _, _, _, _) ->
   (* The wrappers. *)
   List.iter (
     fun (name, style, _, _, _, _, _) ->
+      pr "/* Automatically generated wrapper for function\n";
+      pr " * ";
+      generate_ocaml_prototype name style;
+      pr " */\n";
+      pr "\n";
+
       let params =
         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
 
       let needs_extra_vs =
         match fst style with RConstOptString _ -> true | _ -> false in
 
       let params =
         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
 
       let needs_extra_vs =
         match fst style with RConstOptString _ -> true | _ -> false in
 
+      pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
+      pr "CAMLprim value ocaml_guestfs_%s (value %s" name (List.hd params);
+      List.iter (pr ", value %s") (List.tl params); pr ");\n";
+      pr "\n";
+
       pr "CAMLprim value\n";
       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
       List.iter (pr ", value %s") (List.tl params);
       pr "CAMLprim value\n";
       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
       List.iter (pr ", value %s") (List.tl params);
@@ -6733,7 +7440,7 @@ copy_table (char * const * argv)
 
       pr "  guestfs_h *g = Guestfs_val (gv);\n";
       pr "  if (g == NULL)\n";
 
       pr "  guestfs_h *g = Guestfs_val (gv);\n";
       pr "  if (g == NULL)\n";
-      pr "    caml_failwith (\"%s: used handle after closing it\");\n" name;
+      pr "    ocaml_guestfs_raise_closed (\"%s\");\n" name;
       pr "\n";
 
       List.iter (
       pr "\n";
 
       List.iter (
@@ -6754,6 +7461,8 @@ copy_table (char * const * argv)
             pr "  int %s = Bool_val (%sv);\n" n n
         | Int n ->
             pr "  int %s = Int_val (%sv);\n" n n
             pr "  int %s = Bool_val (%sv);\n" n n
         | Int n ->
             pr "  int %s = Int_val (%sv);\n" n n
+        | Int64 n ->
+            pr "  int64_t %s = Int64_val (%sv);\n" n n
       ) (snd style);
       let error_code =
         match fst style with
       ) (snd style);
       let error_code =
         match fst style with
@@ -6792,7 +7501,8 @@ copy_table (char * const * argv)
         function
         | StringList n | DeviceList n ->
             pr "  ocaml_guestfs_free_strings (%s);\n" n;
         function
         | StringList n | DeviceList n ->
             pr "  ocaml_guestfs_free_strings (%s);\n" n;
-        | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _
+        | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
+        | Bool _ | Int _ | Int64 _
         | FileIn _ | FileOut _ -> ()
       ) (snd style);
 
         | FileIn _ | FileOut _ -> ()
       ) (snd style);
 
@@ -6842,6 +7552,9 @@ copy_table (char * const * argv)
       pr "\n";
 
       if List.length params > 5 then (
       pr "\n";
 
       if List.length params > 5 then (
+        pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
+        pr "CAMLprim value ";
+        pr "ocaml_guestfs_%s_byte (value *argv, int argn);\n" name;
         pr "CAMLprim value\n";
         pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
         pr "{\n";
         pr "CAMLprim value\n";
         pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
         pr "{\n";
@@ -6851,7 +7564,7 @@ copy_table (char * const * argv)
         pr "}\n";
         pr "\n"
       )
         pr "}\n";
         pr "\n"
       )
-  ) all_functions
+  ) all_functions_sorted
 
 and generate_ocaml_structure_decls () =
   List.iter (
 
 and generate_ocaml_structure_decls () =
   List.iter (
@@ -6881,6 +7594,7 @@ and generate_ocaml_prototype ?(is_external = false) name style =
     | StringList _ | DeviceList _ -> pr "string array -> "
     | Bool _ -> pr "bool -> "
     | Int _ -> pr "int -> "
     | StringList _ | DeviceList _ -> pr "string array -> "
     | Bool _ -> pr "bool -> "
     | Int _ -> pr "int -> "
+    | Int64 _ -> pr "int64 -> "
   ) (snd style);
   (match fst style with
    | RErr -> pr "unit" (* all errors are turned into exceptions *)
   ) (snd style);
   (match fst style with
    | RErr -> pr "unit" (* all errors are turned into exceptions *)
@@ -7032,12 +7746,14 @@ DESTROY (g)
           | StringList n | DeviceList n -> pr "      char **%s;\n" n
           | Bool n -> pr "      int %s;\n" n
           | Int n -> pr "      int %s;\n" n
           | StringList n | DeviceList n -> pr "      char **%s;\n" n
           | Bool n -> pr "      int %s;\n" n
           | Int n -> pr "      int %s;\n" n
+          | Int64 n -> pr "      int64_t %s;\n" n
       ) (snd style);
 
       let do_cleanups () =
         List.iter (
           function
       ) (snd style);
 
       let do_cleanups () =
         List.iter (
           function
-          | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _
+          | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
+          | Bool _ | Int _ | Int64 _
           | FileIn _ | FileOut _ -> ()
           | StringList n | DeviceList n -> pr "      free (%s);\n" n
         ) (snd style)
           | FileIn _ | FileOut _ -> ()
           | StringList n | DeviceList n -> pr "      free (%s);\n" n
         ) (snd style)
@@ -7274,7 +7990,6 @@ Sys::Guestfs - Perl bindings for libguestfs
  my $h = Sys::Guestfs->new ();
  $h->add_drive ('guest.img');
  $h->launch ();
  my $h = Sys::Guestfs->new ();
  $h->add_drive ('guest.img');
  $h->launch ();
- $h->wait_ready ();
  $h->mount ('/dev/sda1', '/');
  $h->touch ('/hello');
  $h->sync ();
  $h->mount ('/dev/sda1', '/');
  $h->touch ('/hello');
  $h->sync ();
@@ -7410,7 +8125,7 @@ and generate_perl_prototype name style =
       comma := true;
       match arg with
       | Pathname n | Device n | Dev_or_Path n | String n
       comma := true;
       match arg with
       | Pathname n | Device n | Dev_or_Path n | String n
-      | OptString n | Bool n | Int n | FileIn n | FileOut n ->
+      | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n ->
           pr "$%s" n
       | StringList n | DeviceList n ->
           pr "\\@%s" n
           pr "$%s" n
       | StringList n | DeviceList n ->
           pr "\\@%s" n
@@ -7638,7 +8353,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
         (* generate the function for typ *)
         emit_put_list_function typ
     | typ, _ -> () (* empty *)
         (* generate the function for typ *)
         emit_put_list_function typ
     | typ, _ -> () (* empty *)
-  ) rstructs_used;
+  ) (rstructs_used_by all_functions);
 
   (* Python wrapper functions. *)
   List.iter (
 
   (* Python wrapper functions. *)
   List.iter (
@@ -7677,6 +8392,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
             pr "  char **%s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
             pr "  char **%s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
+        | Int64 n -> pr "  long long %s;\n" n
       ) (snd style);
 
       pr "\n";
       ) (snd style);
 
       pr "\n";
@@ -7690,6 +8406,9 @@ py_guestfs_close (PyObject *self, PyObject *args)
         | StringList _ | DeviceList _ -> pr "O"
         | Bool _ -> pr "i" (* XXX Python has booleans? *)
         | Int _ -> pr "i"
         | StringList _ | DeviceList _ -> pr "O"
         | Bool _ -> pr "i" (* XXX Python has booleans? *)
         | Int _ -> pr "i"
+        | Int64 _ -> pr "L" (* XXX Whoever thought it was a good idea to
+                             * emulate C's int/long/long long in Python?
+                             *)
       ) (snd style);
       pr ":guestfs_%s\",\n" name;
       pr "                         &py_g";
       ) (snd style);
       pr ":guestfs_%s\",\n" name;
       pr "                         &py_g";
@@ -7700,6 +8419,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
         | StringList n | DeviceList n -> pr ", &py_%s" n
         | Bool n -> pr ", &%s" n
         | Int n -> pr ", &%s" n
         | StringList n | DeviceList n -> pr ", &py_%s" n
         | Bool n -> pr ", &%s" n
         | Int n -> pr ", &%s" n
+        | Int64 n -> pr ", &%s" n
       ) (snd style);
 
       pr "))\n";
       ) (snd style);
 
       pr "))\n";
@@ -7709,7 +8429,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> ()
         | StringList n | DeviceList n ->
             pr "  %s = get_string_list (py_%s);\n" n n;
             pr "  if (!%s) return NULL;\n" n
         | StringList n | DeviceList n ->
             pr "  %s = get_string_list (py_%s);\n" n n;
             pr "  if (!%s) return NULL;\n" n
@@ -7724,7 +8444,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> ()
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
@@ -7812,7 +8532,6 @@ import guestfs
 g = guestfs.GuestFS ()
 g.add_drive (\"guest.img\")
 g.launch ()
 g = guestfs.GuestFS ()
 g.add_drive (\"guest.img\")
 g.launch ()
-g.wait_ready ()
 parts = g.list_partitions ()
 
 The guestfs module provides a Python binding to the libguestfs API
 parts = g.list_partitions ()
 
 The guestfs module provides a Python binding to the libguestfs API
@@ -7847,7 +8566,6 @@ g.add_drive (\"guest.img\")
 
 # Launch the qemu subprocess and wait for it to become ready:
 g.launch ()
 
 # Launch the qemu subprocess and wait for it to become ready:
 g.launch ()
-g.wait_ready ()
 
 # Now you can issue commands, for example:
 logvols = g.lvs ()
 
 # Now you can issue commands, for example:
 logvols = g.lvs ()
@@ -7953,9 +8671,7 @@ and pod2text ~width name longdesc =
          failwithf "pod2text: process signalled or stopped by signal %d" i
     );
     Hashtbl.add pod2text_memo key lines;
          failwithf "pod2text: process signalled or stopped by signal %d" i
     );
     Hashtbl.add pod2text_memo key lines;
-    let chan = open_out pod2text_memo_filename in
-    output_value chan pod2text_memo;
-    close_out chan;
+    pod2text_memo_updated ();
     lines
 
 (* Generate ruby bindings. *)
     lines
 
 (* Generate ruby bindings. *)
@@ -8058,6 +8774,8 @@ static VALUE ruby_guestfs_close (VALUE gv)
             pr "  int %s = RTEST (%sv);\n" n n
         | Int n ->
             pr "  int %s = NUM2INT (%sv);\n" n n
             pr "  int %s = RTEST (%sv);\n" n n
         | Int n ->
             pr "  int %s = NUM2INT (%sv);\n" n n
+        | Int64 n ->
+            pr "  long long %s = NUM2LL (%sv);\n" n n
       ) (snd style);
       pr "\n";
 
       ) (snd style);
       pr "\n";
 
@@ -8085,7 +8803,7 @@ static VALUE ruby_guestfs_close (VALUE gv)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> ()
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
@@ -8408,6 +9126,8 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
           pr "boolean %s" n
       | Int n ->
           pr "int %s" n
           pr "boolean %s" n
       | Int n ->
           pr "int %s" n
+      | Int64 n ->
+          pr "long %s" n
   ) (snd style);
 
   pr ")\n";
   ) (snd style);
 
   pr ")\n";
@@ -8527,6 +9247,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
             pr ", jboolean j%s" n
         | Int n ->
             pr ", jint j%s" n
             pr ", jboolean j%s" n
         | Int n ->
             pr ", jint j%s" n
+        | Int64 n ->
+            pr ", jlong j%s" n
       ) (snd style);
       pr ")\n";
       pr "{\n";
       ) (snd style);
       pr ")\n";
       pr "{\n";
@@ -8580,6 +9302,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
         | Bool n
         | Int n ->
             pr "  int %s;\n" n
         | Bool n
         | Int n ->
             pr "  int %s;\n" n
+        | Int64 n ->
+            pr "  int64_t %s;\n" n
       ) (snd style);
 
       let needs_i =
       ) (snd style);
 
       let needs_i =
@@ -8621,7 +9345,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
             pr "  }\n";
             pr "  %s[%s_len] = NULL;\n" n n;
         | Bool n
             pr "  }\n";
             pr "  %s[%s_len] = NULL;\n" n n;
         | Bool n
-        | Int n ->
+        | Int n
+        | Int64 n ->
             pr "  %s = j%s;\n" n n
       ) (snd style);
 
             pr "  %s = j%s;\n" n n
       ) (snd style);
 
@@ -8650,7 +9375,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
             pr "  }\n";
             pr "  free (%s);\n" n
         | Bool n
             pr "  }\n";
             pr "  free (%s);\n" n
         | Bool n
-        | Int n -> ()
+        | Int n
+        | Int64 n -> ()
       ) (snd style);
 
       (* Check for errors. *)
       ) (snd style);
 
       (* Check for errors. *)
@@ -8838,6 +9564,12 @@ module Guestfs (
 
   pr "
   ) where
 
   pr "
   ) where
+
+-- Unfortunately some symbols duplicate ones already present
+-- in Prelude.  We don't know which, so we hard-code a list
+-- here.
+import Prelude hiding (truncate)
+
 import Foreign
 import Foreign.C
 import Foreign.C.Types
 import Foreign
 import Foreign.C
 import Foreign.C.Types
@@ -8912,7 +9644,7 @@ last_error h = do
           | Pathname n | Device n | Dev_or_Path n | String n -> pr "withCString %s $ \\%s -> " n n
           | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
           | StringList n | DeviceList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
           | Pathname n | Device n | Dev_or_Path n | String n -> pr "withCString %s $ \\%s -> " n n
           | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
           | StringList n | DeviceList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
-          | Bool _ | Int _ -> ()
+          | Bool _ | Int _ | Int64 _ -> ()
         ) (snd style);
         (* Convert integer arguments. *)
         let args =
         ) (snd style);
         (* Convert integer arguments. *)
         let args =
@@ -8920,6 +9652,7 @@ last_error h = do
             function
             | Bool n -> sprintf "(fromBool %s)" n
             | Int n -> sprintf "(fromIntegral %s)" n
             function
             | Bool n -> sprintf "(fromBool %s)" n
             | Int n -> sprintf "(fromIntegral %s)" n
+            | Int64 n -> sprintf "(fromIntegral %s)" n
             | FileIn n | FileOut n
             | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n -> n
           ) (snd style) in
             | FileIn n | FileOut n
             | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n -> n
           ) (snd style) in
@@ -8976,6 +9709,7 @@ and generate_haskell_prototype ~handle ?(hs = false) style =
        | StringList _ | DeviceList _ -> if hs then pr "[String]" else pr "Ptr CString"
        | Bool _ -> pr "%s" bool
        | Int _ -> pr "%s" int
        | StringList _ | DeviceList _ -> if hs then pr "[String]" else pr "Ptr CString"
        | Bool _ -> pr "%s" bool
        | Int _ -> pr "%s" int
+       | Int64 _ -> pr "%s" int
        | FileIn _ -> pr "%s" string
        | FileOut _ -> pr "%s" string
       );
        | FileIn _ -> pr "%s" string
        | FileOut _ -> pr "%s" string
       );
@@ -9012,6 +9746,8 @@ and generate_bindtests () =
 #include <string.h>
 
 #include \"guestfs.h\"
 #include <string.h>
 
 #include \"guestfs.h\"
+#include \"guestfs-internal.h\"
+#include \"guestfs-internal-actions.h\"
 #include \"guestfs_protocol.h\"
 
 #define error guestfs_error
 #include \"guestfs_protocol.h\"
 
 #define error guestfs_error
@@ -9042,7 +9778,7 @@ print_strings (char *const *argv)
   let () =
     let (name, style, _, _, _, _, _) = test0 in
     generate_prototype ~extern:false ~semicolon:false ~newline:true
   let () =
     let (name, style, _, _, _, _, _) = test0 in
     generate_prototype ~extern:false ~semicolon:false ~newline:true
-      ~handle:"g" ~prefix:"guestfs_" name style;
+      ~handle:"g" ~prefix:"guestfs__" name style;
     pr "{\n";
     List.iter (
       function
     pr "{\n";
     List.iter (
       function
@@ -9055,6 +9791,7 @@ print_strings (char *const *argv)
       | StringList n | DeviceList n -> pr "  print_strings (%s);\n" n
       | Bool n -> pr "  printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n
       | Int n -> pr "  printf (\"%%d\\n\", %s);\n" n
       | StringList n | DeviceList n -> pr "  print_strings (%s);\n" n
       | Bool n -> pr "  printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n
       | Int n -> pr "  printf (\"%%d\\n\", %s);\n" n
+      | Int64 n -> pr "  printf (\"%%\" PRIi64 \"\\n\", %s);\n" n
     ) (snd style);
     pr "  /* Java changes stdout line buffering so we need this: */\n";
     pr "  fflush (stdout);\n";
     ) (snd style);
     pr "  /* Java changes stdout line buffering so we need this: */\n";
     pr "  fflush (stdout);\n";
@@ -9067,7 +9804,7 @@ print_strings (char *const *argv)
       if String.sub name (String.length name - 3) 3 <> "err" then (
         pr "/* Test normal return. */\n";
         generate_prototype ~extern:false ~semicolon:false ~newline:true
       if String.sub name (String.length name - 3) 3 <> "err" then (
         pr "/* Test normal return. */\n";
         generate_prototype ~extern:false ~semicolon:false ~newline:true
-          ~handle:"g" ~prefix:"guestfs_" name style;
+          ~handle:"g" ~prefix:"guestfs__" name style;
         pr "{\n";
         (match fst style with
          | RErr ->
         pr "{\n";
         (match fst style with
          | RErr ->
@@ -9081,7 +9818,7 @@ print_strings (char *const *argv)
              pr "  sscanf (val, \"%%\" SCNi64, &r);\n";
              pr "  return r;\n"
          | RBool _ ->
              pr "  sscanf (val, \"%%\" SCNi64, &r);\n";
              pr "  return r;\n"
          | RBool _ ->
-             pr "  return strcmp (val, \"true\") == 0;\n"
+             pr "  return STREQ (val, \"true\");\n"
          | RConstString _
          | RConstOptString _ ->
              (* Can't return the input string here.  Return a static
          | RConstString _
          | RConstOptString _ ->
              (* Can't return the input string here.  Return a static
@@ -9133,7 +9870,7 @@ print_strings (char *const *argv)
       ) else (
         pr "/* Test error return. */\n";
         generate_prototype ~extern:false ~semicolon:false ~newline:true
       ) else (
         pr "/* Test error return. */\n";
         generate_prototype ~extern:false ~semicolon:false ~newline:true
-          ~handle:"g" ~prefix:"guestfs_" name style;
+          ~handle:"g" ~prefix:"guestfs__" name style;
         pr "{\n";
         pr "  error (g, \"error\");\n";
         (match fst style with
         pr "{\n";
         pr "  error (g, \"error\");\n";
         (match fst style with
@@ -9170,6 +9907,8 @@ let () =
             "[|" ^ String.concat ";" (List.map (sprintf "\"%s\"") xs) ^ "|]"
         | CallInt i when i >= 0 -> string_of_int i
         | CallInt i (* when i < 0 *) -> "(" ^ string_of_int i ^ ")"
             "[|" ^ String.concat ";" (List.map (sprintf "\"%s\"") xs) ^ "|]"
         | CallInt i when i >= 0 -> string_of_int i
         | CallInt i (* when i < 0 *) -> "(" ^ string_of_int i ^ ")"
+        | CallInt64 i when i >= 0L -> Int64.to_string i ^ "L"
+        | CallInt64 i (* when i < 0L *) -> "(" ^ Int64.to_string i ^ "L)"
         | CallBool b -> string_of_bool b
       ) args
     )
         | CallBool b -> string_of_bool b
       ) args
     )
@@ -9203,6 +9942,7 @@ my $g = Sys::Guestfs->new ();
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> if b then "1" else "0"
       ) args
     )
         | CallBool b -> if b then "1" else "0"
       ) args
     )
@@ -9233,6 +9973,7 @@ g = guestfs.GuestFS ()
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> if b then "1" else "0"
       ) args
     )
         | CallBool b -> if b then "1" else "0"
       ) args
     )
@@ -9263,6 +10004,7 @@ g = Guestfs::create()
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> string_of_bool b
       ) args
     )
         | CallBool b -> string_of_bool b
       ) args
     )
@@ -9298,6 +10040,7 @@ public class Bindtests {
             "new String[]{" ^
               String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "}"
         | CallInt i -> string_of_int i
             "new String[]{" ^
               String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "}"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> string_of_bool b
       ) args
     )
         | CallBool b -> string_of_bool b
       ) args
     )
@@ -9340,6 +10083,8 @@ main = do
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i when i < 0 -> "(" ^ string_of_int i ^ ")"
         | CallInt i -> string_of_int i
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i when i < 0 -> "(" ^ string_of_int i ^ ")"
         | CallInt i -> string_of_int i
+        | CallInt64 i when i < 0L -> "(" ^ Int64.to_string i ^ ")"
+        | CallInt64 i -> Int64.to_string i
         | CallBool true -> "True"
         | CallBool false -> "False"
       ) args
         | CallBool true -> "True"
         | CallBool false -> "False"
       ) args
@@ -9358,43 +10103,43 @@ main = do
 and generate_lang_bindtests call =
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList []; CallBool false;
 and generate_lang_bindtests call =
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString None;
                 CallStringList []; CallBool false;
   call "test0" [CallString "abc"; CallOptString None;
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString ""; CallOptString (Some "def");
                 CallStringList []; CallBool false;
   call "test0" [CallString ""; CallOptString (Some "def");
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString ""; CallOptString (Some "");
                 CallStringList []; CallBool false;
   call "test0" [CallString ""; CallOptString (Some "");
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"; "2"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"; "2"]; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool true;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool true;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt (-1); CallString "123"; CallString "456"];
+                CallInt (-1); CallInt64 (-1L); CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt (-2); CallString "123"; CallString "456"];
+                CallInt (-2); CallInt64 (-2L); CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 1; CallString "123"; CallString "456"];
+                CallInt 1; CallInt64 1L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 2; CallString "123"; CallString "456"];
+                CallInt 2; CallInt64 2L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 4095; CallString "123"; CallString "456"];
+                CallInt 4095; CallInt64 4095L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 0; CallString ""; CallString ""]
+                CallInt 0; CallInt64 0L; CallString ""; CallString ""]
 
 (* XXX Add here tests of the return and error functions. *)
 
 
 (* XXX Add here tests of the return and error functions. *)
 
@@ -9456,6 +10201,10 @@ Run it from the top source directory using the command
   generate_actions_h ();
   close ();
 
   generate_actions_h ();
   close ();
 
+  let close = output_to "src/guestfs-internal-actions.h" in
+  generate_internal_actions_h ();
+  close ();
+
   let close = output_to "src/guestfs-actions.c" in
   generate_client_actions ();
   close ();
   let close = output_to "src/guestfs-actions.c" in
   generate_client_actions ();
   close ();