[Libguestfs] [PATCH 3/3] generator: Annotate returned strings which are devices or mountables.

Richard W.M. Jones rjones at redhat.com
Thu May 4 08:13:55 UTC 2017


Previously the generator did not change any string returned from the
daemon.  Thus guestfs_list_devices (for example) might return internal
device names like /dev/vda (if virtio-blk was in use).

This changes calls to the daemon so that returned strings are
annotated as plain strings, devices or mountables:

    old               --->     new
  RString "uuid"             RString (RPlainString "uuid")
  RString "device"           RString (RDevice "device")
  RString "fs"               RString (RMountable "fs")

For hash tables, keys and values must be annotated separately.  For
example a hash table of mountables (keys) -> plain strings (values)
would be annotated like this:

    old               --->     new
  RHashtable "fses"          RHashtable (RMountable, RPlainString, "fses")

The daemon calls reverse_device_name_translation (currently a no-op)
for devices and mountables.

Note that this has no effect for calls which are handled on the
library side.
---
 daemon/daemon.h                            |   1 +
 daemon/device-name-translation.c           |  15 +++
 generator/XDR.ml                           |   6 +-
 generator/actions_augeas.ml                |   6 +-
 generator/actions_core.ml                  | 190 ++++++++++++++---------------
 generator/actions_core_deprecated.ml       |  34 +++---
 generator/actions_debug.ml                 |   4 +-
 generator/actions_hivex.ml                 |   6 +-
 generator/actions_inspection.ml            |  36 +++---
 generator/actions_internal_tests.ml        |   6 +-
 generator/actions_properties.ml            |  14 +--
 generator/actions_properties_deprecated.ml |   2 +-
 generator/c.ml                             |   4 +-
 generator/checks.ml                        |   6 +-
 generator/csharp.ml                        |   6 +-
 generator/daemon.ml                        |  60 ++++++++-
 generator/perl.ml                          |   6 +-
 generator/types.ml                         |  26 ++--
 18 files changed, 255 insertions(+), 173 deletions(-)

diff --git a/daemon/daemon.h b/daemon/daemon.h
index 3a7f6795e..400116514 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -167,6 +167,7 @@ extern int is_device_mounted (const char *device);
 
 /*-- in device-name-translation.c --*/
 extern char *device_name_translation (const char *device);
+extern char *reverse_device_name_translation (const char *device);
 
 /*-- in stubs.c (auto-generated) --*/
 extern void dispatch_incoming_message (XDR *);
diff --git a/daemon/device-name-translation.c b/daemon/device-name-translation.c
index 0c4387f54..b849e4b13 100644
--- a/daemon/device-name-translation.c
+++ b/daemon/device-name-translation.c
@@ -93,3 +93,18 @@ device_name_translation (const char *device)
 
   return NULL;
 }
+
+char *
+reverse_device_name_translation (const char *device)
+{
+  char *ret;
+
+  /* Currently a no-op. */
+  ret = strdup (device);
+  if (ret == NULL) {
+    reply_with_perror ("strdup");
+    return NULL;
+  }
+
+  return ret;
+}
diff --git a/generator/XDR.ml b/generator/XDR.ml
index b0ca26558..2d799929b 100644
--- a/generator/XDR.ml
+++ b/generator/XDR.ml
@@ -128,11 +128,11 @@ let generate_xdr () =
            pr "};\n\n"
        | RConstString _ | RConstOptString _ ->
            failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-       | RString n ->
+       | RString (_, n) ->
            pr "struct %s_ret {\n" name;
            pr "  string %s<>;\n" n;
            pr "};\n\n"
-       | RStringList n ->
+       | RStringList (_, n) ->
            pr "struct %s_ret {\n" name;
            pr "  guestfs_str %s<>;\n" n;
            pr "};\n\n"
@@ -144,7 +144,7 @@ let generate_xdr () =
            pr "struct %s_ret {\n" name;
            pr "  guestfs_int_%s_list %s;\n" typ n;
            pr "};\n\n"
-       | RHashtable n ->
+       | RHashtable (_, _, n) ->
            pr "struct %s_ret {\n" name;
            pr "  guestfs_str %s<>;\n" n;
            pr "};\n\n"
diff --git a/generator/actions_augeas.ml b/generator/actions_augeas.ml
index 83cdcc38c..5439db140 100644
--- a/generator/actions_augeas.ml
+++ b/generator/actions_augeas.ml
@@ -127,7 +127,7 @@ if a node was created." };
 
   { defaults with
     name = "aug_get"; added = (0, 0, 7);
-    style = RString "val", [String (PlainString, "augpath")], [];
+    style = RString (RPlainString, "val"), [String (PlainString, "augpath")], [];
     shortdesc = "look up the value of an Augeas path";
     longdesc = "\
 Look up the value associated with C<path>.  If C<path>
@@ -198,7 +198,7 @@ one node.  C<dest> is overwritten if it exists." };
 
   { defaults with
     name = "aug_match"; added = (0, 0, 7);
-    style = RStringList "matches", [String (PlainString, "augpath")], [];
+    style = RStringList (RPlainString, "matches"), [String (PlainString, "augpath")], [];
     shortdesc = "return Augeas nodes which match augpath";
     longdesc = "\
 Returns a list of paths which match the path expression C<path>.
@@ -227,7 +227,7 @@ details." };
 
   { defaults with
     name = "aug_ls"; added = (0, 0, 8);
-    style = RStringList "matches", [String (PlainString, "augpath")], [];
+    style = RStringList (RPlainString, "matches"), [String (PlainString, "augpath")], [];
     tests = [
       InitBasicFS, Always, TestResult (
         [["mkdir"; "/etc"];
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 1860e9766..0e667eff1 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -184,7 +184,7 @@ Use C<guestfs_available> or C<guestfs_feature_available> instead." };
 
   { defaults with
     name = "file_architecture"; added = (1, 5, 3);
-    style = RString "arch", [String (Pathname, "filename")], [];
+    style = RString (RPlainString, "arch"), [String (Pathname, "filename")], [];
     tests = [
       InitISOFS, Always, TestResultString (
         [["file_architecture"; "/bin-aarch64-dynamic"]], "aarch64"), [];
@@ -372,7 +372,7 @@ initrd or kernel module(s) instead.
 
   { defaults with
     name = "mountable_device"; added = (1, 33, 15);
-    style = RString "device", [String (Mountable, "mountable")], [];
+    style = RString (RDevice, "device"), [String (Mountable, "mountable")], [];
     shortdesc = "extract the device part of a mountable";
     longdesc = "\
 Returns the device name of a mountable. In quite a lot of
@@ -385,7 +385,7 @@ extract the subvolume path of the mountable if any)." };
 
   { defaults with
     name = "mountable_subvolume"; added = (1, 33, 15);
-    style = RString "subvolume", [String (Mountable, "mountable")], [];
+    style = RString (RPlainString, "subvolume"), [String (Mountable, "mountable")], [];
     shortdesc = "extract the subvolume part of a mountable";
     longdesc = "\
 Returns the subvolume path of a mountable. Btrfs subvolumes
@@ -398,7 +398,7 @@ this function fails and the C<errno> is set to C<EINVAL>." };
 
   { defaults with
     name = "list_filesystems"; added = (1, 5, 15);
-    style = RHashtable "fses", [], [];
+    style = RHashtable (RMountable, RPlainString, "fses"), [], [];
     shortdesc = "list filesystems";
     longdesc = "\
 This inspection command looks for filesystems on partitions,
@@ -905,7 +905,7 @@ information on this topic." };
 
   { defaults with
     name = "canonical_device_name"; added = (1, 19, 7);
-    style = RString "canonical", [String (PlainString, "device")], [];
+    style = RString (RPlainString, "canonical"), [String (PlainString, "device")], [];
     tests = [
       InitNone, Always, TestResultString (
         [["canonical_device_name"; "/dev/hda"]], "/dev/sda"), [];
@@ -969,7 +969,7 @@ but note that any errors are ignored in that case." };
 
   { defaults with
     name = "cat"; added = (0, 0, 4);
-    style = RString "content", [String (Pathname, "path")], [];
+    style = RString (RPlainString, "content"), [String (Pathname, "path")], [];
     tests = [
       InitISOFS, Always, TestResultString (
         [["cat"; "/known-2"]], "abcdef\n"), []
@@ -985,7 +985,7 @@ or C<guestfs_download> functions." };
 
   { defaults with
     name = "find"; added = (1, 0, 27);
-    style = RStringList "names", [String (Pathname, "directory")], [];
+    style = RStringList (RPlainString, "names"), [String (Pathname, "directory")], [];
     tests = [
       InitBasicFS, Always, TestResult (
         [["find"; "/"]],
@@ -1047,7 +1047,7 @@ handle files that contain embedded ASCII NUL characters." };
 
   { defaults with
     name = "read_lines"; added = (0, 0, 7);
-    style = RStringList "lines", [String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (Pathname, "path")], [];
     tests = [
       InitISOFS, Always, TestResult (
         [["read_lines"; "/known-4"]],
@@ -1194,7 +1194,7 @@ for getting standard stats." };
 
   { defaults with
     name = "readlinklist"; added = (1, 0, 77);
-    style = RStringList "links", [String (Pathname, "path"); StringList (Filename, "names")], [];
+    style = RStringList (RPlainString, "links"), [String (Pathname, "path"); StringList (Filename, "names")], [];
     shortdesc = "readlink on multiple files";
     longdesc = "\
 This call allows you to do a C<readlink> operation
@@ -1217,7 +1217,7 @@ list a directory contents without making many round-trips." };
 
   { defaults with
     name = "ls"; added = (0, 0, 4);
-    style = RStringList "listing", [String (Pathname, "directory")], [];
+    style = RStringList (RPlainString, "listing"), [String (Pathname, "directory")], [];
     tests = [
       InitScratchFS, Always, TestResult (
         [["mkdir"; "/ls"];
@@ -1235,7 +1235,7 @@ hidden files are shown." };
 
   { defaults with
     name = "disk_format"; added = (1, 19, 38);
-    style = RString "format", [String (PlainString, "filename")], [];
+    style = RString (RPlainString, "format"), [String (PlainString, "filename")], [];
     tests = [
       InitEmpty, Always, TestResultString (
         [["disk_format"; "../../test-data/blank-disks/blank-disk-1s.raw"]], "raw"), [];
@@ -1377,7 +1377,7 @@ See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
 
   { defaults with
     name = "get_libvirt_requested_credentials"; added = (1, 19, 52);
-    style = RStringList "creds", [], [];
+    style = RStringList (RPlainString, "creds"), [], [];
     blocking = false;
     shortdesc = "get list of credentials requested by libvirt";
     longdesc = "\
@@ -1392,7 +1392,7 @@ See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
 
   { defaults with
     name = "get_libvirt_requested_credential_prompt"; added = (1, 19, 52);
-    style = RString "prompt", [Int "index"], [];
+    style = RString (RPlainString, "prompt"), [Int "index"], [];
     blocking = false;
     shortdesc = "prompt of i'th requested credential";
     longdesc = "\
@@ -1404,7 +1404,7 @@ See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
 
   { defaults with
     name = "get_libvirt_requested_credential_challenge"; added = (1, 19, 52);
-    style = RString "challenge", [Int "index"], [];
+    style = RString (RPlainString, "challenge"), [Int "index"], [];
     blocking = false;
     shortdesc = "challenge of i'th requested credential";
     longdesc = "\
@@ -1416,7 +1416,7 @@ See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
 
   { defaults with
     name = "get_libvirt_requested_credential_defresult"; added = (1, 19, 52);
-    style = RString "defresult", [Int "index"], [];
+    style = RString (RPlainString, "defresult"), [Int "index"], [];
     blocking = false;
     shortdesc = "default result of i'th requested credential";
     longdesc = "\
@@ -1804,7 +1804,7 @@ file types such as directories, symbolic links, block special etc." };
 
   { defaults with
     name = "ll"; added = (0, 0, 4);
-    style = RString "listing", [String (Pathname, "directory")], [];
+    style = RString (RPlainString, "listing"), [String (Pathname, "directory")], [];
     test_excuse = "tricky to test because it depends on the exact format of the 'ls -l' command, which changed between Fedora 10 and Fedora 11";
     shortdesc = "list the files in a directory (long format)";
     longdesc = "\
@@ -1816,7 +1816,7 @@ is I<not> intended that you try to parse the output string." };
 
   { defaults with
     name = "list_devices"; added = (0, 0, 4);
-    style = RStringList "devices", [], [];
+    style = RStringList (RDevice, "devices"), [], [];
     tests = [
       InitEmpty, Always, TestResult (
         [["list_devices"]],
@@ -1832,7 +1832,7 @@ See also C<guestfs_list_filesystems>." };
 
   { defaults with
     name = "list_partitions"; added = (0, 0, 4);
-    style = RStringList "partitions", [], [];
+    style = RStringList (RDevice, "partitions"), [], [];
     tests = [
       InitBasicFS, Always, TestResult (
         [["list_partitions"]],
@@ -1858,7 +1858,7 @@ See also C<guestfs_list_filesystems>." };
 
   { defaults with
     name = "pvs"; added = (0, 0, 4);
-    style = RStringList "physvols", [], [];
+    style = RStringList (RDevice, "physvols"), [], [];
     optional = Some "lvm2";
     tests = [
       InitBasicFSonLVM, Always, TestResult (
@@ -1886,7 +1886,7 @@ See also C<guestfs_pvs_full>." };
 
   { defaults with
     name = "vgs"; added = (0, 0, 4);
-    style = RStringList "volgroups", [], [];
+    style = RStringList (RPlainString, "volgroups"), [], [];
     optional = Some "lvm2";
     tests = [
       InitBasicFSonLVM, Always, TestResult (
@@ -1916,7 +1916,7 @@ See also C<guestfs_vgs_full>." };
 
   { defaults with
     name = "lvs"; added = (0, 0, 4);
-    style = RStringList "logvols", [], [];
+    style = RStringList (RDevice, "logvols"), [], [];
     optional = Some "lvm2";
     tests = [
       InitBasicFSonLVM, Always, TestResult (
@@ -2263,7 +2263,7 @@ contains the filesystem." };
 
   { defaults with
     name = "mounts"; added = (0, 0, 8);
-    style = RStringList "devices", [], [];
+    style = RStringList (RMountable, "devices"), [], [];
     tests = [
       InitScratchFS, Always, TestResult (
         [["mounts"]], "is_device_list (ret, 1, \"/dev/sdb1\")"), []
@@ -2320,7 +2320,7 @@ and physical volumes." };
 
   { defaults with
     name = "file"; added = (1, 9, 1);
-    style = RString "description", [String (Dev_or_Path, "path")], [];
+    style = RString (RPlainString, "description"), [String (Dev_or_Path, "path")], [];
     tests = [
       InitISOFS, Always, TestResultString (
         [["file"; "/empty"]], "empty"), [];
@@ -2354,7 +2354,7 @@ C<guestfs_is_file>, C<guestfs_is_blockdev> (etc), C<guestfs_is_zero>." };
 
   { defaults with
     name = "command"; added = (1, 9, 1);
-    style = RString "output", [StringList (PlainString, "arguments")], [];
+    style = RString (RPlainString, "output"), [StringList (PlainString, "arguments")], [];
     protocol_limit_warning = true;
     tests = [
       InitScratchFS, IfNotCrossAppliance, TestResultString (
@@ -2457,7 +2457,7 @@ locations." };
 
   { defaults with
     name = "command_lines"; added = (1, 9, 1);
-    style = RStringList "lines", [StringList (PlainString, "arguments")], [];
+    style = RStringList (RPlainString, "lines"), [StringList (PlainString, "arguments")], [];
     protocol_limit_warning = true;
     tests = [
       InitScratchFS, IfNotCrossAppliance, TestResult (
@@ -2551,7 +2551,7 @@ This is the same as the L<statvfs(2)> system call." };
 
   { defaults with
     name = "tune2fs_l"; added = (1, 9, 2);
-    style = RHashtable "superblock", [String (Device, "device")], [];
+    style = RHashtable (RPlainString, RPlainString, "superblock"), [String (Device, "device")], [];
     tests = [
       InitScratchFS, Always, TestResult (
         [["tune2fs_l"; "/dev/sdb1"]],
@@ -2752,7 +2752,7 @@ See also C<guestfs_upload>, C<guestfs_cat>." };
 
   { defaults with
     name = "checksum"; added = (1, 0, 2);
-    style = RString "checksum", [String (PlainString, "csumtype"); String (Pathname, "path")], [];
+    style = RString (RPlainString, "checksum"), [String (PlainString, "csumtype"); String (Pathname, "path")], [];
     tests = [
       InitISOFS, Always, TestResultString (
         [["checksum"; "crc"; "/known-3"]], "2891671662"), [];
@@ -3277,7 +3277,7 @@ so that the maximum guest memory is freed." };
 
   { defaults with
     name = "dmesg"; added = (1, 0, 18);
-    style = RString "kmsgs", [], [];
+    style = RString (RPlainString, "kmsgs"), [], [];
     tests = [
       InitEmpty, Always, TestRun (
         [["dmesg"]]), []
@@ -3334,7 +3334,7 @@ The external L<cmp(1)> program is used for the comparison." };
 
   { defaults with
     name = "strings"; added = (1, 0, 22);
-    style = RStringList "stringsout", [String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "stringsout"), [String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -3358,7 +3358,7 @@ version of libguestfs, but see L<guestfs(3)/CVE-2014-8484>." };
 
   { defaults with
     name = "strings_e"; added = (1, 0, 22);
-    style = RStringList "stringsout", [String (PlainString, "encoding"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "stringsout"), [String (PlainString, "encoding"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -3416,7 +3416,7 @@ version of libguestfs, but see L<guestfs(3)/CVE-2014-8484>." };
 
   { defaults with
     name = "hexdump"; added = (1, 0, 22);
-    style = RString "dump", [String (Pathname, "path")], [];
+    style = RString (RPlainString, "dump"), [String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResultString (
@@ -3474,7 +3474,7 @@ volume to match the new size of the underlying device." };
 
   { defaults with
     name = "sfdisk_kernel_geometry"; added = (1, 0, 26);
-    style = RString "partitions", [String (Device, "device")], [];
+    style = RString (RPlainString, "partitions"), [String (Device, "device")], [];
     shortdesc = "display the kernel geometry";
     longdesc = "\
 This displays the kernel’s idea of the geometry of C<device>.
@@ -3484,7 +3484,7 @@ be parsed." };
 
   { defaults with
     name = "sfdisk_disk_geometry"; added = (1, 0, 26);
-    style = RString "partitions", [String (Device, "device")], [];
+    style = RString (RPlainString, "partitions"), [String (Device, "device")], [];
     shortdesc = "display the disk geometry from the partition table";
     longdesc = "\
 This displays the disk geometry of C<device> read from the
@@ -3606,7 +3606,7 @@ L<ntfs-3g.probe(8)> manual page." };
 
   { defaults with
     name = "sh"; added = (1, 0, 50);
-    style = RString "output", [String (PlainString, "command")], [];
+    style = RString (RPlainString, "output"), [String (PlainString, "command")], [];
     shortdesc = "run a command via the shell";
     longdesc = "\
 This call runs a command from the guest filesystem via the
@@ -3624,7 +3624,7 @@ All the provisos about C<guestfs_command> apply to this call." };
 
   { defaults with
     name = "sh_lines"; added = (1, 0, 50);
-    style = RStringList "lines", [String (PlainString, "command")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "command")], [];
     shortdesc = "run a command via the shell returning lines";
     longdesc = "\
 This is the same as C<guestfs_sh>, but splits the result
@@ -3639,7 +3639,7 @@ See also: C<guestfs_command_lines>" };
      * start with "/".  There is no concept of "cwd" in libguestfs,
      * hence no "."-relative names.
      *)
-    style = RStringList "paths", [String (Pathname, "pattern")], [OBool "directoryslash"];
+    style = RStringList (RPlainString, "paths"), [String (Pathname, "pattern")], [OBool "directoryslash"];
     once_had_no_optargs = true;
     tests = [
       InitScratchFS, Always, TestResult (
@@ -3752,7 +3752,7 @@ manual page for more details." };
 
   { defaults with
     name = "mkdtemp"; added = (1, 0, 54);
-    style = RString "dir", [String (Pathname, "tmpl")], [];
+    style = RString (RPlainString, "dir"), [String (Pathname, "tmpl")], [];
     tests = [
       InitScratchFS, Always, TestRun (
         [["mkdir"; "/mkdtemp"];
@@ -3820,7 +3820,7 @@ C<wc -c> external command." };
 
   { defaults with
     name = "head"; added = (1, 0, 54);
-    style = RStringList "lines", [String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -3838,7 +3838,7 @@ a list of strings." };
 
   { defaults with
     name = "head_n"; added = (1, 0, 54);
-    style = RStringList "lines", [Int "nrlines"; String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [Int "nrlines"; String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -3863,7 +3863,7 @@ If the parameter C<nrlines> is zero, this returns an empty list." };
 
   { defaults with
     name = "tail"; added = (1, 0, 54);
-    style = RStringList "lines", [String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -3877,7 +3877,7 @@ a list of strings." };
 
   { defaults with
     name = "tail_n"; added = (1, 0, 54);
-    style = RStringList "lines", [Int "nrlines"; String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [Int "nrlines"; String (Pathname, "path")], [];
     protocol_limit_warning = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -3902,7 +3902,7 @@ If the parameter C<nrlines> is zero, this returns an empty list." };
 
   { defaults with
     name = "df"; added = (1, 0, 54);
-    style = RString "output", [], [];
+    style = RString (RPlainString, "output"), [], [];
     test_excuse = "tricky to test because it depends on the exact format of the 'df' command and other imponderables";
     shortdesc = "report file system disk space usage";
     longdesc = "\
@@ -3914,7 +3914,7 @@ Use C<guestfs_statvfs> from programs." };
 
   { defaults with
     name = "df_h"; added = (1, 0, 54);
-    style = RString "output", [], [];
+    style = RString (RPlainString, "output"), [], [];
     test_excuse = "tricky to test because it depends on the exact format of the 'df' command and other imponderables";
     shortdesc = "report file system disk space usage (human readable)";
     longdesc = "\
@@ -3947,7 +3947,7 @@ The result is the estimated size in I<kilobytes>
 
   { defaults with
     name = "initrd_list"; added = (1, 0, 54);
-    style = RStringList "filenames", [String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "filenames"), [String (Pathname, "path")], [];
     tests = [
       InitISOFS, Always, TestResult (
         [["initrd_list"; "/initrd"]],
@@ -4265,7 +4265,7 @@ of the link itself." };
 
   { defaults with
     name = "mountpoints"; added = (1, 0, 62);
-    style = RHashtable "mps", [], [];
+    style = RHashtable (RMountable, RPlainString, "mps"), [], [];
     shortdesc = "show mountpoints";
     longdesc = "\
 This call is similar to C<guestfs_mounts>.  That call returns
@@ -4332,7 +4332,7 @@ for full details." };
 
   { defaults with
     name = "grep"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [OBool "extended"; OBool "fixed"; OBool "insensitive"; OBool "compressed"];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [OBool "extended"; OBool "fixed"; OBool "insensitive"; OBool "compressed"];
     protocol_limit_warning = true; once_had_no_optargs = true;
     tests = [
       InitISOFS, Always, TestResult (
@@ -4411,7 +4411,7 @@ compress- or gzip-compressed.
 
   { defaults with
     name = "realpath"; added = (1, 0, 66);
-    style = RString "rpath", [String (Pathname, "path")], [];
+    style = RString (RPlainString, "rpath"), [String (Pathname, "path")], [];
     tests = [
       InitISOFS, Always, TestResultString (
         [["realpath"; "/../directory"]], "/directory"), []
@@ -4483,7 +4483,7 @@ The I<-f> option removes the link (C<linkname>) if it exists already." };
 
   { defaults with
     name = "readlink"; added = (1, 0, 66);
-    style = RString "link", [String (Pathname, "path")], [];
+    style = RString (RPlainString, "link"), [String (Pathname, "path")], [];
     shortdesc = "read the target of a symbolic link";
     longdesc = "\
 This command reads the target of a symbolic link." };
@@ -4706,7 +4706,7 @@ size and leave remaining events in the queue." };
 
   { defaults with
     name = "inotify_files"; added = (1, 0, 66);
-    style = RStringList "paths", [], [];
+    style = RStringList (RPlainString, "paths"), [], [];
     optional = Some "inotify";
     shortdesc = "return list of watched files that had events";
     longdesc = "\
@@ -4737,7 +4737,7 @@ This loads a kernel module in the appliance." };
 
   { defaults with
     name = "echo_daemon"; added = (1, 0, 69);
-    style = RString "output", [StringList (PlainString, "words")], [];
+    style = RString (RPlainString, "output"), [StringList (PlainString, "words")], [];
     tests = [
       InitNone, Always, TestResultString (
         [["echo_daemon"; "This is a test"]], "This is a test"), [];
@@ -4786,7 +4786,7 @@ The result list is not sorted.
 
   { defaults with
     name = "case_sensitive_path"; added = (1, 0, 75);
-    style = RString "rpath", [String (Pathname, "path")], [];
+    style = RString (RPlainString, "rpath"), [String (Pathname, "path")], [];
     tests = [
       InitISOFS, Always, TestResultString (
         [["case_sensitive_path"; "/DIRECTORY"]], "/directory"), [];
@@ -4870,7 +4870,7 @@ See also C<guestfs_realpath>." };
 
   { defaults with
     name = "vfs_type"; added = (1, 0, 75);
-    style = RString "fstype", [String (Mountable, "mountable")], [];
+    style = RString (RPlainString, "fstype"), [String (Mountable, "mountable")], [];
     tests = [
       InitScratchFS, Always, TestResultString (
         [["vfs_type"; "/dev/sdb1"]], "ext2"), []
@@ -5032,7 +5032,7 @@ into smaller groups of names." };
 
   { defaults with
     name = "internal_readlinklist"; added = (1, 19, 32);
-    style = RStringList "links", [String (Pathname, "path"); StringList (Filename, "names")], [];
+    style = RStringList (RPlainString, "links"), [String (Pathname, "path"); StringList (Filename, "names")], [];
     visibility = VInternal;
     shortdesc = "readlink on multiple files";
     longdesc = "\
@@ -5282,7 +5282,7 @@ Size of the partition in bytes.
 
   { defaults with
     name = "part_get_parttype"; added = (1, 0, 78);
-    style = RString "parttype", [String (Device, "device")], [];
+    style = RString (RPlainString, "parttype"), [String (Device, "device")], [];
     tests = [
       InitEmpty, Always, TestResultString (
         [["part_disk"; "/dev/sda"; "gpt"];
@@ -5390,28 +5390,28 @@ See also C<guestfs_initrd_list>." };
 
   { defaults with
     name = "pvuuid"; added = (1, 0, 87);
-    style = RString "uuid", [String (Device, "device")], [];
+    style = RString (RPlainString, "uuid"), [String (Device, "device")], [];
     shortdesc = "get the UUID of a physical volume";
     longdesc = "\
 This command returns the UUID of the LVM PV C<device>." };
 
   { defaults with
     name = "vguuid"; added = (1, 0, 87);
-    style = RString "uuid", [String (PlainString, "vgname")], [];
+    style = RString (RPlainString, "uuid"), [String (PlainString, "vgname")], [];
     shortdesc = "get the UUID of a volume group";
     longdesc = "\
 This command returns the UUID of the LVM VG named C<vgname>." };
 
   { defaults with
     name = "lvuuid"; added = (1, 0, 87);
-    style = RString "uuid", [String (Device, "device")], [];
+    style = RString (RPlainString, "uuid"), [String (Device, "device")], [];
     shortdesc = "get the UUID of a logical volume";
     longdesc = "\
 This command returns the UUID of the LVM LV C<device>." };
 
   { defaults with
     name = "vgpvuuids"; added = (1, 0, 87);
-    style = RStringList "uuids", [String (PlainString, "vgname")], [];
+    style = RStringList (RPlainString, "uuids"), [String (PlainString, "vgname")], [];
     shortdesc = "get the PV UUIDs containing the volume group";
     longdesc = "\
 Given a VG called C<vgname>, this returns the UUIDs of all
@@ -5424,7 +5424,7 @@ See also C<guestfs_vglvuuids>." };
 
   { defaults with
     name = "vglvuuids"; added = (1, 0, 87);
-    style = RStringList "uuids", [String (PlainString, "vgname")], [];
+    style = RStringList (RPlainString, "uuids"), [String (PlainString, "vgname")], [];
     shortdesc = "get the LV UUIDs of all LVs in the volume group";
     longdesc = "\
 Given a VG called C<vgname>, this returns the UUIDs of all
@@ -5536,7 +5536,7 @@ types (see C<guestfs_part_get_parttype>)." };
 
   { defaults with
     name = "checksum_device"; added = (1, 3, 2);
-    style = RString "checksum", [String (PlainString, "csumtype"); String (Device, "device")], [];
+    style = RString (RPlainString, "checksum"), [String (PlainString, "csumtype"); String (Device, "device")], [];
     tests = [
       InitISOFS, Always, TestResult (
         [["checksum_device"; "md5"; "/dev/sdd"]],
@@ -5723,7 +5723,7 @@ allows you to specify the new size (in bytes) explicitly." };
 
   { defaults with
     name = "available_all_groups"; added = (1, 3, 15);
-    style = RStringList "groups", [], [];
+    style = RStringList (RPlainString, "groups"), [], [];
     tests = [
       InitNone, Always, TestRun [["available_all_groups"]], []
     ];
@@ -5766,7 +5766,7 @@ a file in the host and attach it as a device." };
 
   { defaults with
     name = "vfs_label"; added = (1, 3, 18);
-    style = RString "label", [String (Mountable, "mountable")], [];
+    style = RString (RPlainString, "label"), [String (Mountable, "mountable")], [];
     tests = [
       InitBasicFS, Always, TestResultString (
         [["set_label"; "/dev/sda1"; "LTEST"];
@@ -5791,7 +5791,7 @@ To find a filesystem from the label, use C<guestfs_findfs_label>." };
 
   { defaults with
     name = "vfs_uuid"; added = (1, 3, 18);
-    style = RString "uuid", [String (Mountable, "mountable")], [];
+    style = RString (RPlainString, "uuid"), [String (Mountable, "mountable")], [];
     fish_alias = ["get-uuid"];
     tests = [
         InitBasicFS, Always, TestResultString (
@@ -5955,7 +5955,7 @@ returns true iff this is the case." };
 
   { defaults with
     name = "findfs_uuid"; added = (1, 5, 3);
-    style = RString "device", [String (PlainString, "uuid")], [];
+    style = RString (RDevice, "device"), [String (PlainString, "uuid")], [];
     shortdesc = "find a filesystem by UUID";
     longdesc = "\
 This command searches the filesystems and returns the one
@@ -5966,7 +5966,7 @@ To find the UUID of a filesystem, use C<guestfs_vfs_uuid>." };
 
   { defaults with
     name = "findfs_label"; added = (1, 5, 3);
-    style = RString "device", [String (PlainString, "label")], [];
+    style = RString (RDevice, "device"), [String (PlainString, "label")], [];
     shortdesc = "find a filesystem by label";
     longdesc = "\
 This command searches the filesystems and returns the one
@@ -6083,7 +6083,7 @@ See also C<guestfs_stat>." };
 
   { defaults with
     name = "part_to_dev"; added = (1, 5, 15);
-    style = RString "device", [String (Device, "partition")], [];
+    style = RString (RDevice, "device"), [String (Device, "partition")], [];
     tests = [
       InitPartition, Always, TestResultDevice (
         [["part_to_dev"; "/dev/sda1"]], "/dev/sda"), [];
@@ -6206,7 +6206,7 @@ See also C<guestfs_pread>." };
 
   { defaults with
     name = "lvm_canonical_lv_name"; added = (1, 5, 24);
-    style = RString "lv", [String (Device, "lvname")], [];
+    style = RString (RDevice, "lv"), [String (Device, "lvname")], [];
     tests = [
       InitBasicFSonLVM, IfAvailable "lvm2", TestResultString (
         [["lvm_canonical_lv_name"; "/dev/mapper/VG-LV"]], "/dev/VG/LV"), [];
@@ -6384,7 +6384,7 @@ Note that for large devices this can take a long time to run." };
 
   { defaults with
     name = "list_9p"; added = (1, 11, 12);
-    style = RStringList "mounttags", [], [];
+    style = RStringList (RPlainString, "mounttags"), [], [];
     shortdesc = "list 9p filesystems";
     longdesc = "\
 List all 9p filesystems attached to the guest.  A list of
@@ -6405,7 +6405,7 @@ parameter." };
 
   { defaults with
     name = "list_dm_devices"; added = (1, 11, 15);
-    style = RStringList "devices", [], [];
+    style = RStringList (RDevice, "devices"), [], [];
     shortdesc = "list device mapper devices";
     longdesc = "\
 List all device mapper devices.
@@ -6802,14 +6802,14 @@ If not set, this defaults to C<raid1>.
 
   { defaults with
     name = "list_md_devices"; added = (1, 15, 4);
-    style = RStringList "devices", [], [];
+    style = RStringList (RDevice, "devices"), [], [];
     shortdesc = "list Linux md (RAID) devices";
     longdesc = "\
 List all Linux md devices." };
 
   { defaults with
     name = "md_detail"; added = (1, 15, 6);
-    style = RHashtable "info", [String (Device, "md")], [];
+    style = RHashtable (RPlainString, RPlainString, "info"), [String (Device, "md")], [];
     optional = Some "mdadm";
     shortdesc = "obtain metadata for an MD device";
     longdesc = "\
@@ -6852,7 +6852,7 @@ device is stopped, but it is not destroyed or zeroed." };
 
   { defaults with
     name = "blkid"; added = (1, 15, 9);
-    style = RHashtable "info", [String (Device, "device")], [];
+    style = RHashtable (RPlainString, RPlainString, "info"), [String (Device, "device")], [];
     tests = [
       InitScratchFS, Always, TestResult (
         [["blkid"; "/dev/sdb1"]],
@@ -7217,7 +7217,7 @@ To create general filesystems, use C<guestfs_mkfs>." };
 
   { defaults with
     name = "get_e2attrs"; added = (1, 17, 31);
-    style = RString "attrs", [String (Pathname, "file")], [];
+    style = RString (RPlainString, "attrs"), [String (Pathname, "file")], [];
     tests = [
       InitScratchFS, Always, TestResultString (
         [["touch"; "/e2attrs1"];
@@ -8048,7 +8048,7 @@ silently create an ext2 filesystem instead." };
 
   { defaults with
     name = "list_disk_labels"; added = (1, 19, 49);
-    style = RHashtable "labels", [], [];
+    style = RHashtable (RPlainString, RDevice, "labels"), [], [];
     tests = [
       (* The test disks have no labels, so we can be sure there are
        * no labels.  See in tests/disk-labels/ for tests checking
@@ -8099,7 +8099,7 @@ This function is used internally when hotplugging drives." };
 
   { defaults with
     name = "mktemp"; added = (1, 19, 53);
-    style = RString "path", [String (Pathname, "tmpl")], [OString "suffix"];
+    style = RString (RPlainString, "path"), [String (Pathname, "tmpl")], [OString "suffix"];
     tests = [
       InitScratchFS, Always, TestRun (
         [["mkdir"; "/mktemp"];
@@ -8146,7 +8146,7 @@ which we try to create the C<lost+found> directory." };
 
   { defaults with
     name = "acl_get_file"; added = (1, 19, 63);
-    style = RString "acl", [String (Pathname, "path"); String (PlainString, "acltype")], [];
+    style = RString (RPlainString, "acl"), [String (Pathname, "path"); String (PlainString, "acltype")], [];
     optional = Some "acl";
     shortdesc = "get the POSIX ACL attached to a file";
     longdesc = "\
@@ -8258,7 +8258,7 @@ attached to directory C<dir>." };
 
   { defaults with
     name = "cap_get_file"; added = (1, 19, 63);
-    style = RString "cap", [String (Pathname, "path")], [];
+    style = RString (RPlainString, "cap"), [String (Pathname, "path")], [];
     optional = Some "linuxcaps";
     shortdesc = "get the Linux capabilities attached to a file";
     longdesc = "\
@@ -8285,7 +8285,7 @@ The capabilities set C<cap> should be passed in text form
 
   { defaults with
     name = "list_ldm_volumes"; added = (1, 20, 0);
-    style = RStringList "devices", [], [];
+    style = RStringList (RDevice, "devices"), [], [];
     optional = Some "ldm";
     shortdesc = "list all Windows dynamic disk volumes";
     longdesc = "\
@@ -8295,7 +8295,7 @@ device names." };
 
   { defaults with
     name = "list_ldm_partitions"; added = (1, 20, 0);
-    style = RStringList "devices", [], [];
+    style = RStringList (RDevice, "devices"), [], [];
     optional = Some "ldm";
     shortdesc = "list all Windows dynamic disk partitions";
     longdesc = "\
@@ -8333,7 +8333,7 @@ volumes" };
 
   { defaults with
     name = "ldmtool_scan"; added = (1, 20, 0);
-    style = RStringList "guids", [], [];
+    style = RStringList (RPlainString, "guids"), [], [];
     optional = Some "ldm";
     shortdesc = "scan for Windows dynamic disks";
     longdesc = "\
@@ -8346,7 +8346,7 @@ block devices, call C<guestfs_ldmtool_scan_devices> instead." };
 
   { defaults with
     name = "ldmtool_scan_devices"; added = (1, 20, 0);
-    style = RStringList "guids", [StringList (Device, "devices")], [];
+    style = RStringList (RPlainString, "guids"), [StringList (Device, "devices")], [];
     optional = Some "ldm";
     shortdesc = "scan for Windows dynamic disks";
     longdesc = "\
@@ -8359,7 +8359,7 @@ scanned.  If this list is empty, all block devices are scanned." };
 
   { defaults with
     name = "ldmtool_diskgroup_name"; added = (1, 20, 0);
-    style = RString "name", [String (PlainString, "diskgroup")], [];
+    style = RString (RPlainString, "name"), [String (PlainString, "diskgroup")], [];
     optional = Some "ldm";
     shortdesc = "return the name of a Windows dynamic disk group";
     longdesc = "\
@@ -8369,7 +8369,7 @@ the list returned by C<guestfs_ldmtool_scan>." };
 
   { defaults with
     name = "ldmtool_diskgroup_volumes"; added = (1, 20, 0);
-    style = RStringList "volumes", [String (PlainString, "diskgroup")], [];
+    style = RStringList (RPlainString, "volumes"), [String (PlainString, "diskgroup")], [];
     optional = Some "ldm";
     shortdesc = "return the volumes in a Windows dynamic disk group";
     longdesc = "\
@@ -8379,7 +8379,7 @@ the list returned by C<guestfs_ldmtool_scan>." };
 
   { defaults with
     name = "ldmtool_diskgroup_disks"; added = (1, 20, 0);
-    style = RStringList "disks", [String (PlainString, "diskgroup")], [];
+    style = RStringList (RDevice, "disks"), [String (PlainString, "diskgroup")], [];
     optional = Some "ldm";
     shortdesc = "return the disks in a Windows dynamic disk group";
     longdesc = "\
@@ -8389,7 +8389,7 @@ the list returned by C<guestfs_ldmtool_scan>." };
 
   { defaults with
     name = "ldmtool_volume_type"; added = (1, 20, 0);
-    style = RString "voltype", [String (PlainString, "diskgroup"); String (PlainString, "volume")], [];
+    style = RString (RPlainString, "voltype"), [String (PlainString, "diskgroup"); String (PlainString, "volume")], [];
     optional = Some "ldm";
     shortdesc = "return the type of a Windows dynamic disk volume";
     longdesc = "\
@@ -8402,7 +8402,7 @@ Other types may also be returned." };
 
   { defaults with
     name = "ldmtool_volume_hint"; added = (1, 20, 0);
-    style = RString "hint", [String (PlainString, "diskgroup"); String (PlainString, "volume")], [];
+    style = RString (RPlainString, "hint"), [String (PlainString, "diskgroup"); String (PlainString, "volume")], [];
     optional = Some "ldm";
     shortdesc = "return the hint field of a Windows dynamic disk volume";
     longdesc = "\
@@ -8413,7 +8413,7 @@ not always, the name of a Windows drive, eg. C<E:>." };
 
   { defaults with
     name = "ldmtool_volume_partitions"; added = (1, 20, 0);
-    style = RStringList "partitions", [String (PlainString, "diskgroup"); String (PlainString, "volume")], [];
+    style = RStringList (RDevice, "partitions"), [String (PlainString, "diskgroup"); String (PlainString, "volume")], [];
     optional = Some "ldm";
     shortdesc = "return the partitions in a Windows dynamic disk volume";
     longdesc = "\
@@ -8444,7 +8444,7 @@ for a useful list of type GUIDs." };
 
   { defaults with
     name = "part_get_gpt_type"; added = (1, 21, 1);
-    style = RString "guid", [String (Device, "device"); Int "partnum"], [];
+    style = RString (RPlainString, "guid"), [String (Device, "device"); Int "partnum"], [];
     optional = Some "gdisk";
     tests = [
       InitGPT, Always, TestResultString (
@@ -8751,7 +8751,7 @@ This returns the number of nodes modified." };
 
   { defaults with
     name = "aug_label"; added = (1, 23, 14);
-    style = RString "label", [String (PlainString, "augpath")], [];
+    style = RString (RPlainString, "label"), [String (PlainString, "augpath")], [];
     tests = [
       InitBasicFS, Always, TestResultString (
         [["mkdir"; "/etc"];
@@ -8823,7 +8823,7 @@ enables all the other flags, if they are not specified already.
 
   { defaults with
     name = "part_get_name"; added = (1, 25, 33);
-    style = RString "name", [String (Device, "device"); Int "partnum"], [];
+    style = RString (RPlainString, "name"), [String (Device, "device"); Int "partnum"], [];
     shortdesc = "get partition name";
     longdesc = "\
 This gets the partition name on partition numbered C<partnum> on
@@ -8966,7 +8966,7 @@ Get the default subvolume or snapshot of a filesystem mounted at C<mountpoint>."
 
   { defaults with
     name = "btrfs_subvolume_show"; added = (1, 29, 17);
-    style = RHashtable "btrfssubvolumeinfo", [String (Pathname, "subvolume")], [];
+    style = RHashtable (RPlainString, RPlainString, "btrfssubvolumeinfo"), [String (Pathname, "subvolume")], [];
     optional = Some "btrfs"; camel_name = "BTRFSSubvolumeShow";
     tests = [
       InitPartition, Always, TestRun (
@@ -9262,7 +9262,7 @@ valid GUID." };
 
   { defaults with
     name = "part_get_gpt_guid"; added = (1, 29, 25);
-    style = RString "guid", [String (Device, "device"); Int "partnum"], [];
+    style = RString (RPlainString, "guid"), [String (Device, "device"); Int "partnum"], [];
     optional = Some "gdisk";
     tests = [
       InitGPT, Always, TestResultString (
@@ -9366,7 +9366,7 @@ All data will be zeroed, but metadata and the like is preserved." };
 
   { defaults with
     name = "part_get_mbr_part_type"; added = (1, 29, 32);
-    style = RString "partitiontype", [String (Device, "device"); Int "partnum"], [];
+    style = RString (RPlainString, "partitiontype"), [String (Device, "device"); Int "partnum"], [];
     tests = [
       InitEmpty, Always, TestResultString (
         [["part_init"; "/dev/sda"; "mbr"];
@@ -9479,7 +9479,7 @@ or if C<guid> is not a valid GUID." };
 
   { defaults with
     name = "part_get_disk_guid"; added = (1, 33, 2);
-    style = RString "guid", [String (Device, "device")], [];
+    style = RString (RPlainString, "guid"), [String (Device, "device")], [];
     optional = Some "gdisk";
     tests = [
       InitGPT, Always, TestResultString (
@@ -9537,7 +9537,7 @@ otherwise the call will fail." };
 
   { defaults with
     name = "btrfs_filesystem_show"; added = (1, 33, 29);
-    style = RStringList "devices", [String (Device, "device")], [];
+    style = RStringList (RDevice, "devices"), [String (Device, "device")], [];
     optional = Some "btrfs"; camel_name = "BTRFSFilesystemsShow";
     tests = [
       InitScratchFS, Always, TestLastFail (
diff --git a/generator/actions_core_deprecated.ml b/generator/actions_core_deprecated.ml
index 66bd89409..4f2d919da 100644
--- a/generator/actions_core_deprecated.ml
+++ b/generator/actions_core_deprecated.ml
@@ -229,7 +229,7 @@ to return the existing label on a filesystem." };
 
   { defaults with
     name = "get_e2label"; added = (1, 0, 15);
-    style = RString "label", [String (Device, "device")], [];
+    style = RString (RPlainString, "label"), [String (Device, "device")], [];
     deprecated_by = Replaced_by "vfs_label";
     shortdesc = "get the ext2/3/4 filesystem label";
     longdesc = "\
@@ -266,7 +266,7 @@ of a filesystem." };
 
   { defaults with
     name = "get_e2uuid"; added = (1, 0, 15);
-    style = RString "uuid", [String (Device, "device")], [];
+    style = RString (RPlainString, "uuid"), [String (Device, "device")], [];
     deprecated_by = Replaced_by "vfs_uuid";
     tests = [
       (* We can't predict what UUID will be, so just check
@@ -298,7 +298,7 @@ See also: C<guestfs_part_add>" };
 
   { defaults with
     name = "sfdisk_l"; added = (1, 0, 26);
-    style = RString "partitions", [String (Device, "device")], [];
+    style = RString (RDevice, "partitions"), [String (Device, "device")], [];
     deprecated_by = Replaced_by "part_list";
     shortdesc = "display the partition table";
     longdesc = "\
@@ -366,7 +366,7 @@ and C<guestfs_part_disk>" };
 
   { defaults with
     name = "zfile"; added = (1, 0, 59);
-    style = RString "description", [String (PlainString, "meth"); String (Pathname, "path")], [];
+    style = RString (RPlainString, "description"), [String (PlainString, "meth"); String (Pathname, "path")], [];
     deprecated_by = Replaced_by "file";
     shortdesc = "determine file type inside a compressed file";
     longdesc = "\
@@ -380,7 +380,7 @@ process compressed files." };
 
   { defaults with
     name = "egrep"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -395,7 +395,7 @@ matching lines." };
 
   { defaults with
     name = "fgrep"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "pattern"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "pattern"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -410,7 +410,7 @@ matching lines." };
 
   { defaults with
     name = "grepi"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -425,7 +425,7 @@ matching lines." };
 
   { defaults with
     name = "egrepi"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -440,7 +440,7 @@ matching lines." };
 
   { defaults with
     name = "fgrepi"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "pattern"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "pattern"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -455,7 +455,7 @@ matching lines." };
 
   { defaults with
     name = "zgrep"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -470,7 +470,7 @@ matching lines." };
 
   { defaults with
     name = "zegrep"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -485,7 +485,7 @@ matching lines." };
 
   { defaults with
     name = "zfgrep"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "pattern"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "pattern"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -500,7 +500,7 @@ matching lines." };
 
   { defaults with
     name = "zgrepi"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
 
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
@@ -516,7 +516,7 @@ matching lines." };
 
   { defaults with
     name = "zegrepi"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "regex"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "regex"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -531,7 +531,7 @@ matching lines." };
 
   { defaults with
     name = "zfgrepi"; added = (1, 0, 66);
-    style = RStringList "lines", [String (PlainString, "pattern"); String (Pathname, "path")], [];
+    style = RStringList (RPlainString, "lines"), [String (PlainString, "pattern"); String (Pathname, "path")], [];
     protocol_limit_warning = true;
     deprecated_by = Replaced_by "grep";
     tests = [
@@ -577,7 +577,7 @@ See the documentation about SELINUX in L<guestfs(3)>." };
 
   { defaults with
     name = "getcon"; added = (1, 0, 67);
-    style = RString "context", [], [];
+    style = RString (RPlainString, "context"), [], [];
     optional = Some "selinux";
     deprecated_by = Replaced_by "selinux_relabel";
     shortdesc = "get SELinux security context";
@@ -774,7 +774,7 @@ it to local file C<tarball> (as an xz compressed tar archive)." };
 
   { defaults with
     name = "llz"; added = (1, 17, 6);
-    style = RString "listing", [String (Pathname, "directory")], [];
+    style = RString (RPlainString, "listing"), [String (Pathname, "directory")], [];
     deprecated_by = Replaced_by "lgetxattrs";
     shortdesc = "list the files in a directory (long format with SELinux contexts)";
     longdesc = "\
diff --git a/generator/actions_debug.ml b/generator/actions_debug.ml
index 68fc4ef79..846a7a9eb 100644
--- a/generator/actions_debug.ml
+++ b/generator/actions_debug.ml
@@ -25,7 +25,7 @@ open Types
 let non_daemon_functions = [
   { defaults with
     name = "debug_drives"; added = (1, 13, 22);
-    style = RStringList "cmdline", [], [];
+    style = RStringList (RPlainString, "cmdline"), [], [];
     visibility = VDebug;
     blocking = false;
     shortdesc = "debug the drives (internal use only)";
@@ -38,7 +38,7 @@ not part of the formal API and can be removed or changed at any time." };
 let daemon_functions = [
   { defaults with
     name = "debug"; added = (1, 0, 11);
-    style = RString "result", [String (PlainString, "subcmd"); StringList (PlainString, "extraargs")], [];
+    style = RString (RPlainString, "result"), [String (PlainString, "subcmd"); StringList (PlainString, "extraargs")], [];
     visibility = VDebug;
     shortdesc = "debugging and internals";
     longdesc = "\
diff --git a/generator/actions_hivex.ml b/generator/actions_hivex.ml
index 5369a292d..0a3d6dd20 100644
--- a/generator/actions_hivex.ml
+++ b/generator/actions_hivex.ml
@@ -25,7 +25,7 @@ open Types
 let non_daemon_functions = [
   { defaults with
     name = "hivex_value_utf8"; added = (1, 19, 35);
-    style = RString "databuf", [Int64 "valueh"], [];
+    style = RString (RPlainString, "databuf"), [Int64 "valueh"], [];
     optional = Some "hivex";
     shortdesc = "return the data field from the (key, datatype, data) tuple";
     longdesc = "\
@@ -85,7 +85,7 @@ This is a wrapper around the L<hivex(3)> call of the same name." };
 
   { defaults with
     name = "hivex_node_name"; added = (1, 19, 35);
-    style = RString "name", [Int64 "nodeh"], [];
+    style = RString (RPlainString, "name"), [Int64 "nodeh"], [];
     optional = Some "hivex";
     shortdesc = "return the name of the node";
     longdesc = "\
@@ -148,7 +148,7 @@ This is a wrapper around the L<hivex(3)> call of the same name." };
 
   { defaults with
     name = "hivex_value_key"; added = (1, 19, 35);
-    style = RString "key", [Int64 "valueh"], [];
+    style = RString (RPlainString, "key"), [Int64 "valueh"], [];
     optional = Some "hivex";
     shortdesc = "return the key field from the (key, datatype, data) tuple";
     longdesc = "\
diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml
index 0312fbd8b..b7ea5a4de 100644
--- a/generator/actions_inspection.ml
+++ b/generator/actions_inspection.ml
@@ -25,7 +25,7 @@ open Types
 let non_daemon_functions = [
   { defaults with
     name = "inspect_os"; added = (1, 5, 3);
-    style = RStringList "roots", [], [];
+    style = RStringList (RMountable, "roots"), [], [];
     shortdesc = "inspect disk and return list of operating systems found";
     longdesc = "\
 This function uses other libguestfs functions and certain
@@ -62,7 +62,7 @@ See also C<guestfs_list_filesystems>." };
 
   { defaults with
     name = "inspect_get_type"; added = (1, 5, 3);
-    style = RString "name", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "name"), [String (Mountable, "root")], [];
     shortdesc = "get type of inspected operating system";
     longdesc = "\
 This returns the type of the inspected operating system.
@@ -115,7 +115,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_arch"; added = (1, 5, 3);
-    style = RString "arch", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "arch"), [String (Mountable, "root")], [];
     shortdesc = "get architecture of inspected operating system";
     longdesc = "\
 This returns the architecture of the inspected operating system.
@@ -129,7 +129,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_distro"; added = (1, 5, 3);
-    style = RString "distro", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "distro"), [String (Mountable, "root")], [];
     shortdesc = "get distro of inspected operating system";
     longdesc = "\
 This returns the distro (distribution) of the inspected operating
@@ -317,7 +317,7 @@ See also C<guestfs_inspect_get_major_version>." };
 
   { defaults with
     name = "inspect_get_product_name"; added = (1, 5, 3);
-    style = RString "product", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "product"), [String (Mountable, "root")], [];
     shortdesc = "get product name of inspected operating system";
     longdesc = "\
 This returns the product name of the inspected operating
@@ -332,7 +332,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_mountpoints"; added = (1, 5, 3);
-    style = RHashtable "mountpoints", [String (Mountable, "root")], [];
+    style = RHashtable (RPlainString, RMountable, "mountpoints"), [String (Mountable, "root")], [];
     shortdesc = "get mountpoints of inspected operating system";
     longdesc = "\
 This returns a hash of where we think the filesystems
@@ -363,7 +363,7 @@ See also C<guestfs_inspect_get_filesystems>." };
 
   { defaults with
     name = "inspect_get_filesystems"; added = (1, 5, 3);
-    style = RStringList "filesystems", [String (Mountable, "root")], [];
+    style = RStringList (RMountable, "filesystems"), [String (Mountable, "root")], [];
     shortdesc = "get filesystems associated with inspected operating system";
     longdesc = "\
 This returns a list of all the filesystems that we think
@@ -379,7 +379,7 @@ See also C<guestfs_inspect_get_mountpoints>." };
 
   { defaults with
     name = "inspect_get_windows_systemroot"; added = (1, 5, 25);
-    style = RString "systemroot", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "systemroot"), [String (Mountable, "root")], [];
     shortdesc = "get Windows systemroot of inspected operating system";
     longdesc = "\
 This returns the Windows systemroot of the inspected guest.
@@ -393,7 +393,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_roots"; added = (1, 7, 3);
-    style = RStringList "roots", [], [];
+    style = RStringList (RMountable, "roots"), [], [];
     shortdesc = "return list of operating systems found by last inspection";
     longdesc = "\
 This function is a convenient way to get the list of root
@@ -407,7 +407,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_package_format"; added = (1, 7, 5);
-    style = RString "packageformat", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "packageformat"), [String (Mountable, "root")], [];
     shortdesc = "get package format used by the operating system";
     longdesc = "\
 This function and C<guestfs_inspect_get_package_management> return
@@ -429,7 +429,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_package_management"; added = (1, 7, 5);
-    style = RString "packagemanagement", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "packagemanagement"), [String (Mountable, "root")], [];
     shortdesc = "get package management tool used by the operating system";
     longdesc = "\
 C<guestfs_inspect_get_package_format> and this function return
@@ -554,7 +554,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_hostname"; added = (1, 7, 9);
-    style = RString "hostname", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "hostname"), [String (Mountable, "root")], [];
     shortdesc = "get hostname of the operating system";
     longdesc = "\
 This function returns the hostname of the operating system
@@ -567,7 +567,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_format"; added = (1, 9, 4);
-    style = RString "format", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "format"), [String (Mountable, "root")], [];
     shortdesc = "get format of inspected operating system";
     longdesc = "\
 This returns the format of the inspected operating system.  You
@@ -634,7 +634,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_product_variant"; added = (1, 9, 13);
-    style = RString "variant", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "variant"), [String (Mountable, "root")], [];
     shortdesc = "get product variant of inspected operating system";
     longdesc = "\
 This returns the product variant of the inspected operating
@@ -662,7 +662,7 @@ C<guestfs_inspect_get_major_version>." };
 
   { defaults with
     name = "inspect_get_windows_current_control_set"; added = (1, 9, 17);
-    style = RString "controlset", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "controlset"), [String (Mountable, "root")], [];
     shortdesc = "get Windows CurrentControlSet of inspected operating system";
     longdesc = "\
 This returns the Windows CurrentControlSet of the inspected guest.
@@ -676,7 +676,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_drive_mappings"; added = (1, 9, 17);
-    style = RHashtable "drives", [String (Mountable, "root")], [];
+    style = RHashtable (RPlainString, RDevice, "drives"), [String (Mountable, "root")], [];
     shortdesc = "get drive letter mappings";
     longdesc = "\
 This call is useful for Windows which uses a primitive system
@@ -775,7 +775,7 @@ advice before using trademarks in applications.
 
   { defaults with
     name = "inspect_get_windows_software_hive"; added = (1, 35, 26);
-    style = RString "path", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "path"), [String (Mountable, "root")], [];
     shortdesc = "get the path of the Windows software hive";
     longdesc = "\
 This returns the path to the hive (binary Windows Registry file)
@@ -792,7 +792,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
 
   { defaults with
     name = "inspect_get_windows_system_hive"; added = (1, 35, 26);
-    style = RString "path", [String (Mountable, "root")], [];
+    style = RString (RPlainString, "path"), [String (Mountable, "root")], [];
     shortdesc = "get the path of the Windows system hive";
     longdesc = "\
 This returns the path to the hive (binary Windows Registry file)
diff --git a/generator/actions_internal_tests.ml b/generator/actions_internal_tests.ml
index 7ac7a8934..f5bb82647 100644
--- a/generator/actions_internal_tests.ml
+++ b/generator/actions_internal_tests.ml
@@ -49,11 +49,11 @@ let test_all_rets = [
   "internal_test_rbool",        RBool "valout";
   "internal_test_rconststring", RConstString "valout";
   "internal_test_rconstoptstring", RConstOptString "valout";
-  "internal_test_rstring",      RString "valout";
-  "internal_test_rstringlist",  RStringList "valout";
+  "internal_test_rstring",      RString (RPlainString, "valout");
+  "internal_test_rstringlist",  RStringList (RPlainString, "valout");
   "internal_test_rstruct",      RStruct ("valout", "lvm_pv");
   "internal_test_rstructlist",  RStructList ("valout", "lvm_pv");
-  "internal_test_rhashtable",   RHashtable "valout";
+  "internal_test_rhashtable",   RHashtable (RPlainString, RPlainString, "valout");
   "internal_test_rbufferout",   RBufferOut "valout";
 ]
 
diff --git a/generator/actions_properties.ml b/generator/actions_properties.ml
index ef31ffa5a..2419fd1c9 100644
--- a/generator/actions_properties.ml
+++ b/generator/actions_properties.ml
@@ -51,7 +51,7 @@ the qemu binary at the same time as the handle is created." };
 
   { defaults with
     name = "get_hv"; added = (1, 23, 17);
-    style = RString "hv", [], [];
+    style = RString (RPlainString, "hv"), [], [];
     blocking = false;
     tests = [
       InitNone, Always, TestRun (
@@ -328,7 +328,7 @@ See L<guestfs(3)/BACKEND>." };
 
   { defaults with
     name = "get_backend"; added = (1, 21, 26);
-    style = RString "backend", [], [];
+    style = RString (RPlainString, "backend"), [], [];
     blocking = false;
     tests = [
       InitNone, Always, TestRun (
@@ -405,7 +405,7 @@ the default.  Else F</tmp> is the default." };
 
   { defaults with
     name = "get_tmpdir"; added = (1, 19, 58);
-    style = RString "tmpdir", [], [];
+    style = RString (RPlainString, "tmpdir"), [], [];
     blocking = false;
     shortdesc = "get the temporary directory";
     longdesc = "\
@@ -430,7 +430,7 @@ the default.  Else F</var/tmp> is the default." };
 
   { defaults with
     name = "get_cachedir"; added = (1, 19, 58);
-    style = RString "cachedir", [], [];
+    style = RString (RPlainString, "cachedir"), [], [];
     blocking = false;
     shortdesc = "get the appliance cache directory";
     longdesc = "\
@@ -464,7 +464,7 @@ Get the program name.  See C<guestfs_set_program>." };
 
   { defaults with
     name = "get_backend_settings"; added = (1, 25, 24);
-    style = RStringList "settings", [], [];
+    style = RStringList (RPlainString, "settings"), [], [];
     blocking = false;
     tests = [
       InitNone, Always, TestRun (
@@ -505,7 +505,7 @@ See L<guestfs(3)/BACKEND>, L<guestfs(3)/BACKEND SETTINGS>." };
 
   { defaults with
     name = "get_backend_setting"; added = (1, 27, 2);
-    style = RString "val", [String (PlainString, "name")], [];
+    style = RString (RPlainString, "val"), [String (PlainString, "name")], [];
     blocking = false;
     shortdesc = "get a single per-backend settings string";
     longdesc = "\
@@ -594,7 +594,7 @@ Get the handle identifier.  See C<guestfs_set_identifier>." };
 
   { defaults with
     name = "get_sockdir"; added = (1, 33, 8);
-    style = RString "sockdir", [], [];
+    style = RString (RPlainString, "sockdir"), [], [];
     blocking = false;
     shortdesc = "get the temporary directory for sockets";
     longdesc = "\
diff --git a/generator/actions_properties_deprecated.ml b/generator/actions_properties_deprecated.ml
index c1d988bff..96143b8fe 100644
--- a/generator/actions_properties_deprecated.ml
+++ b/generator/actions_properties_deprecated.ml
@@ -112,7 +112,7 @@ See L<guestfs(3)/BACKEND>." };
 
   { defaults with
     name = "get_attach_method"; added = (1, 9, 8);
-    style = RString "backend", [], [];
+    style = RString (RPlainString, "backend"), [], [];
     blocking = false;
     deprecated_by = Replaced_by "get_backend";
     tests = [
diff --git a/generator/c.ml b/generator/c.ml
index 1c3ad027f..1f099a221 100644
--- a/generator/c.ml
+++ b/generator/c.ml
@@ -2011,9 +2011,9 @@ and generate_client_actions actions () =
       pr "  ret_v = ret.%s;\n" n
     | RConstString _ | RConstOptString _ ->
       failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-    | RString n ->
+    | RString (_, n) ->
       pr "  ret_v = ret.%s; /* caller will free */\n" n
-    | RStringList n | RHashtable n ->
+    | RStringList (_, n) | RHashtable (_, _, n) ->
       pr "  /* caller will free this, but we need to add a NULL entry */\n";
       pr "  ret.%s.%s_val =\n" n n;
       pr "    safe_realloc (g, ret.%s.%s_val,\n" n n;
diff --git a/generator/checks.ml b/generator/checks.ml
index c30790dac..881069489 100644
--- a/generator/checks.ml
+++ b/generator/checks.ml
@@ -126,9 +126,9 @@ let () =
       (match ret with
        | RErr -> ()
        | RInt n | RInt64 n | RBool n
-       | RConstString n | RConstOptString n | RString n
-       | RStringList n | RStruct (n, _) | RStructList (n, _)
-       | RHashtable n | RBufferOut n ->
+       | RConstString n | RConstOptString n | RString (_, n)
+       | RStringList (_, n) | RStruct (n, _) | RStructList (n, _)
+       | RHashtable (_, _, n) | RBufferOut n ->
            check_arg_ret_name n
       );
       List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) args;
diff --git a/generator/csharp.ml b/generator/csharp.ml
index 8181a26a6..6a280011a 100644
--- a/generator/csharp.ml
+++ b/generator/csharp.ml
@@ -148,11 +148,11 @@ namespace Guestfs
         | RInt64 n -> "long"
         | RConstString n
         | RConstOptString n
-        | RString n
+        | RString (_, n)
         | RBufferOut n -> "string"
         | RStruct (_,n) -> "_" ^ n
-        | RHashtable n -> "Hashtable"
-        | RStringList n -> "string[]"
+        | RHashtable _ -> "Hashtable"
+        | RStringList _ -> "string[]"
         | RStructList (_,n) -> sprintf "_%s[]" n
 
       and c_return_type () =
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 9d5ba00b2..84686973c 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -380,13 +380,69 @@ let generate_daemon_stubs actions () =
               name
         | RConstString _ | RConstOptString _ ->
             failwithf "RConstString|RConstOptString cannot be used by daemon functions"
-        | RString n ->
+        | RString (RPlainString, n) ->
             pr "  struct guestfs_%s_ret ret;\n" name;
             pr "  ret.%s = r;\n" n;
             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
               name
-        | RStringList n | RHashtable n ->
+        | RString ((RDevice|RMountable), n) ->
             pr "  struct guestfs_%s_ret ret;\n" name;
+            pr "  CLEANUP_FREE char *rr = reverse_device_name_translation (r);\n";
+            pr "  if (rr == NULL)\n";
+            pr "    /* reverse_device_name_translation has already called reply_with_error */\n";
+            pr "    return;\n";
+            pr "  ret.%s = rr;\n" n;
+            pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
+              name
+        | RStringList (RPlainString, n)
+        | RHashtable (RPlainString, RPlainString, n) ->
+            pr "  struct guestfs_%s_ret ret;\n" name;
+            pr "  ret.%s.%s_len = count_strings (r);\n" n n;
+            pr "  ret.%s.%s_val = r;\n" n n;
+            pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
+              name
+        | RStringList ((RDevice|RMountable), n)
+        | RHashtable ((RDevice|RMountable), (RDevice|RMountable), n) ->
+            pr "  struct guestfs_%s_ret ret;\n" name;
+            pr "  size_t i;\n";
+            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
+            pr "    char *rr = reverse_device_name_translation (r[i]);\n";
+            pr "    if (rr == NULL)\n";
+            pr "      /* reverse_device_name_translation has already called reply_with_error */\n";
+            pr "      return;\n";
+            pr "    free (r[i]);\n";
+            pr "    r[i] = rr;\n";
+            pr "  }\n";
+            pr "  ret.%s.%s_len = count_strings (r);\n" n n;
+            pr "  ret.%s.%s_val = r;\n" n n;
+            pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
+              name
+        | RHashtable ((RDevice|RMountable), RPlainString, n) ->
+            pr "  struct guestfs_%s_ret ret;\n" name;
+            pr "  size_t i;\n";
+            pr "  for (i = 0; r[i] != NULL; i += 2) {\n";
+            pr "    char *rr = reverse_device_name_translation (r[i]);\n";
+            pr "    if (rr == NULL)\n";
+            pr "      /* reverse_device_name_translation has already called reply_with_error */\n";
+            pr "      return;\n";
+            pr "    free (r[i]);\n";
+            pr "    r[i] = rr;\n";
+            pr "  }\n";
+            pr "  ret.%s.%s_len = count_strings (r);\n" n n;
+            pr "  ret.%s.%s_val = r;\n" n n;
+            pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
+              name
+        | RHashtable (RPlainString, (RDevice|RMountable), n) ->
+            pr "  struct guestfs_%s_ret ret;\n" name;
+            pr "  size_t i;\n";
+            pr "  for (i = 0; r[i] != NULL; i += 2) {\n";
+            pr "    char *rr = reverse_device_name_translation (r[i+1]);\n";
+            pr "    if (rr == NULL)\n";
+            pr "      /* reverse_device_name_translation has already called reply_with_error */\n";
+            pr "      return;\n";
+            pr "    free (r[i+1]);\n";
+            pr "    r[i+1] = rr;\n";
+            pr "  }\n";
             pr "  ret.%s.%s_len = count_strings (r);\n" n n;
             pr "  ret.%s.%s_val = r;\n" n n;
             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
diff --git a/generator/perl.ml b/generator/perl.ml
index ec11a0419..6a05d01b7 100644
--- a/generator/perl.ml
+++ b/generator/perl.ml
@@ -1112,11 +1112,11 @@ and generate_perl_prototype name (ret, args, optargs) =
    | RInt64 n
    | RConstString n
    | RConstOptString n
-   | RString n
+   | RString (_, n)
    | RBufferOut n -> pr "$%s = " n
    | RStruct (n,_)
-   | RHashtable n -> pr "%%%s = " n
-   | RStringList n
+   | RHashtable (_, _, n) -> pr "%%%s = " n
+   | RStringList (_, n)
    | RStructList (n,_) -> pr "@%s = " n
   );
   pr "$g->%s (" name;
diff --git a/generator/types.ml b/generator/types.ml
index 91d3d1543..740bc7750 100644
--- a/generator/types.ml
+++ b/generator/types.ml
@@ -81,12 +81,12 @@ and ret =
     (* "RString" is a returned string.  It must NOT be NULL, since
      * a NULL return indicates an error.  The caller frees this.
      *)
-  | RString of string
+  | RString of rstringt * string
 
     (* "RStringList" is a list of strings.  No string in the list
      * can be NULL.  The caller frees the strings and the array.
      *)
-  | RStringList of string
+  | RStringList of rstringt * string
 
     (* "RStruct" is a function which returns a single named structure
      * or an error indication (in C, a struct, and in other languages
@@ -101,13 +101,18 @@ and ret =
   | RStructList of string * string	(* name of retval, name of struct *)
 
     (* Key-value pairs of untyped strings.  Turns into a hashtable or
-     * dictionary in languages which support it.  DON'T use this as a
-     * general "bucket" for results.  Prefer a stronger typed return
-     * value if one is available, or write a custom struct.  Don't use
-     * this if the list could potentially be very long, since it is
-     * inefficient.  Keys should be unique.  NULLs are not permitted.
+     * dictionary in languages which support it.
+     *
+     * DON'T use this as a general "bucket" for results.  Prefer a
+     * stronger typed return value if one is available, or write a
+     * custom struct.  Don't use this if the list could potentially
+     * be very long, since it is inefficient.
+     *
+     * Keys should be unique.  NULLs are not permitted.
+     * The first rstringt is the type of the keys, the second
+     * rstringt is the type of the values.
      *)
-  | RHashtable of string
+  | RHashtable of rstringt * rstringt * string
 
     (* "RBufferOut" is handled almost exactly like RString, but
      * it allows the string to contain arbitrary 8 bit data including
@@ -124,6 +129,11 @@ and ret =
      *)
   | RBufferOut of string
 
+and rstringt =
+  | RPlainString        (* none of the others *)
+  | RDevice             (* /dev device name *)
+  | RMountable          (* location of mountable filesystem *)
+
 and args = argt list	(* Function parameters, guestfs handle is implicit. *)
 
 and argt =
-- 
2.12.0




More information about the Libguestfs mailing list