[Libguestfs] [PATCH 1/2] generator: Implement BufferIn parameter type (RHBZ#501889).

Richard W.M. Jones
Tue, 18 May 2010 21:03:28 -0000

Read my programming blog: http://rwmj.wordpress.com
Fedora now supports 80 OCaml packages (the OPEN alternative to F#)
http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
-------------- next part --------------
>From e171bab70195bcc80f777a863150acbe5f106593 Mon Sep 17 00:00:00 2001
From: Richard Jones <rjones at redhat.com>
Date: Tue, 18 May 2010 21:47:19 +0100
Subject: [PATCH 1/2] generator: Implement BufferIn parameter type (RHBZ#501889).

The BufferIn argument turns into various things:

 in C      const char *, size_t parameter pair

 in XDR    an opaque<> type (instead of string) which allows \0 chars

 in other bindings
           mostly just a string, since most languages except for C
           permit strings to contain any 8 bit data
---
 TODO             |    9 ---
 bindtests        |   13 ++++
 src/generator.ml |  203 ++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 178 insertions(+), 47 deletions(-)

diff --git a/TODO b/TODO
index b06ae71..2209619 100644
--- a/TODO
+++ b/TODO
@@ -17,15 +17,6 @@ IDs and the host.  It's not easy to automate this because
you need
 extra details about the guest itself in order to get to its
 UID->username map (eg. /etc/passwd from the guest).

-BufferIn
---------
-
-BufferIn should turn into <char *, int> and simple strings in other
-languages that can handle 8 bit clean strings.
-
-Limit on transfers would still be 2MB for these types.
- - then implement write-file properly
-
 febootstrap / debootstrap inside appliance
 ------------------------------------------

diff --git a/bindtests b/bindtests
index e1772db..23da165 100644
--- a/bindtests
+++ b/bindtests
@@ -6,6 +6,7 @@ false
 0
 123
 456
+<61><62><63><00><61><62><63>
 abc
 null
 []
@@ -14,6 +15,7 @@ false
 0
 123
 456
+<61><62><63><00><61><62><63>

 def
 []
@@ -22,6 +24,7 @@ false
 0
 123
 456
+<61><62><63><00><61><62><63>


 []
@@ -30,6 +33,7 @@ false
 0
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -38,6 +42,7 @@ false
 0
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1", "2"]
@@ -46,6 +51,7 @@ false
 0
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -54,6 +60,7 @@ true
 0
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -62,6 +69,7 @@ false
 -1
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -70,6 +78,7 @@ false
 -2
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -78,6 +87,7 @@ false
 1
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -86,6 +96,7 @@ false
 2
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -94,6 +105,7 @@ false
 4095
 123
 456
+<61><62><63><00><61><62><63>
 abc
 def
 ["1"]
@@ -102,4 +114,5 @@ false
 0


+<61><62><63><00><61><62><63>
 EOF
diff --git a/src/generator.ml b/src/generator.ml
index 73ab8fa..090c280 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -164,9 +164,8 @@ and argt       *)
   | FileIn of string
   | FileOut of string
-(* Not implemented:
     (* Opaque buffer which can contain arbitrary 8 bit data.
-     * In the C API, this is expressed as <char *, int> pair.
+     * In the C API, this is expressed as <const char *, size_t> pair.
      * Most other languages have a string type which can contain
      * ASCII NUL.  We use whatever type is appropriate for each
      * language.
@@ -175,7 +174,6 @@ and argt       * To return an arbitrary buffer, use
RBufferOut.
      *)
   | BufferIn of string
-*)

 type flags    | ProtocolLimitWarning  (* display warning about protocol size
limits *)
@@ -384,6 +382,7 @@ let test_all_args = [
   Int64 "integer64";
   FileIn "filein";
   FileOut "fileout";
+  BufferIn "bufferin";
 ]

 let test_all_rets = [
@@ -4915,6 +4914,7 @@ type callt    | CallInt of int
   | CallInt64 of int64
   | CallBool of bool
+  | CallBuffer of string

 (* Used to memoize the result of pod2text. *)
 let pod2text_memo_filename = "src/.pod2text.data"
@@ -5060,10 +5060,21 @@ let count_chars c str    done;
   !count

+let explode str +  let r = ref [] in
+  for i = 0 to String.length str - 1 do
+    let c = String.unsafe_get str i in
+    r := c :: !r;
+  done;
+  List.rev !r
+
+let map_chars f str +  List.map f (explode str)
+
 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 | Int64 n
-  | FileIn n | FileOut n -> n
+  | FileIn n | FileOut n | BufferIn n -> n

 let java_name_of_struct typ    try List.assoc typ java_structs
@@ -5536,6 +5547,8 @@ and generate_xdr ()               | Bool n -> pr "
bool %s;\n" n
              | Int n -> pr "  int %s;\n" n
              | Int64 n -> pr "  hyper %s;\n" n
+             | BufferIn n ->
+                 pr "  opaque %s<>;\n" n
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "};\n\n"
@@ -5810,7 +5823,8 @@ check_state (guestfs_h *g, const char *caller)
       | Pathname n
       | Dev_or_Path n
       | FileIn n
-      | FileOut n ->
+      | FileOut n
+      | BufferIn n ->
           (* guestfish doesn't support string escaping, so neither do we *)
           pr "    printf (\" \\\"%%s\\\"\",
%s);\n" n
       | OptString n ->			(* string option *)
@@ -5924,6 +5938,16 @@ check_state (guestfs_h *g, const char *caller)
              | Int64 n ->
                  pr "  args.%s = %s;\n" n n
              | FileIn _ | FileOut _ -> ()
+             | BufferIn n ->
+                 pr "  /* Just catch grossly large sizes. XDR encoding
will make this precise. */\n";
+                 pr "  if (%s_size >= GUESTFS_MESSAGE_MAX) {\n" n;
+                 pr "    error (g, \"%%s: size of input buffer too
large\", \"%s\");\n"
+                   shortname;
+                 pr "    guestfs___end_busy (g);\n";
+                 pr "    return %s;\n" error_code;
+                 pr "  }\n";
+                 pr "  args.%s.%s_val = (char *) %s;\n" n n n;
+                 pr "  args.%s.%s_len = %s_size;\n" n n n
            ) args;
            pr "  serial = guestfs___send (g, GUESTFS_PROC_%s,\n"
              (String.uppercase shortname);
@@ -6184,6 +6208,9 @@ and generate_daemon_actions ()               | Int n ->
pr "  int %s;\n" n
              | Int64 n -> pr "  int64_t %s;\n" n
              | FileIn _ | FileOut _ -> ()
+             | BufferIn n ->
+                 pr "  const char *%s;\n" n;
+                 pr "  size_t %s_size;\n" n
            ) args
       );
       pr "\n";
@@ -6247,11 +6274,13 @@ and generate_daemon_actions ()               | Int n
-> pr "  %s = args.%s;\n" n n
              | Int64 n -> pr "  %s = args.%s;\n" n n
              | FileIn _ | FileOut _ -> ()
+             | BufferIn n ->
+                 pr "  %s = args.%s.%s_val;\n" n n n;
+                 pr "  %s_size = args.%s.%s_len;\n" n n n
            ) args;
            pr "\n"
       );

-
       (* this is used at least for do_equal *)
       if List.exists (function Pathname _ -> true | _ -> false) (snd
style) then (
         (* Emit NEED_ROOT just once, even when there are two or
@@ -7212,6 +7241,9 @@ and generate_test_command_call ?(expect_error = false)
?test test_name cmd          | String n, arg
         | OptString n, arg ->
             pr "    const char *%s = \"%s\";\n" n (c_quote
arg);
+        | BufferIn n, arg ->
+            pr "    const char *%s = \"%s\";\n" n (c_quote
arg);
+            pr "    size_t %s_size = %d;\n" n (String.length arg)
         | Int _, _
         | Int64 _, _
         | Bool _, _
@@ -7264,6 +7296,8 @@ and generate_test_command_call ?(expect_error = false)
?test test_name cmd          | String n, _
         | OptString n, _ ->
             pr ", %s" n
+        | BufferIn n, _ ->
+            pr ", %s, %s_size" n n
         | FileIn _, arg | FileOut _, arg ->
             pr ", \"%s\"" (c_quote arg)
         | StringList n, _ | DeviceList n, _ ->
@@ -7547,6 +7581,9 @@ and generate_fish_cmds ()          | Dev_or_Path n
         | FileIn n
         | FileOut n -> pr "  char *%s;\n" n
+        | BufferIn n ->
+            pr "  const char *%s;\n" n;
+            pr "  size_t %s_size;\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
@@ -7602,6 +7639,9 @@ and generate_fish_cmds ()            | OptString name
->
               pr "  %s = STRNEQ (argv[%d], \"\") ? argv[%d] :
NULL;\n"
                 name i i
+          | BufferIn name ->
+              pr "  %s = argv[%d];\n" name i;
+              pr "  %s_size = strlen (argv[%d]);\n" name i
           | FileIn name ->
               pr "  %s = file_in (argv[%d]);\n" name i;
               pr "  if (%s == NULL) return -1;\n" name
@@ -7634,7 +7674,8 @@ and generate_fish_cmds ()          function
         | Device name | String name
         | OptString name | Bool name
-        | Int name | Int64 name -> ()
+        | Int name | Int64 name
+        | BufferIn name -> ()
         | Pathname name | Dev_or_Path name | FileOut name ->
             pr "  free (%s);\n" name
         | FileIn name ->
@@ -7895,6 +7936,7 @@ and generate_fish_actions_pod ()          | Int n -> pr
" %s" n
         | Int64 n -> pr " %s" n
         | FileIn n | FileOut n -> pr " (%s|-)" n
+        | BufferIn n -> pr " %s" n
       ) (snd style);
       pr "\n";
       pr "\n";
@@ -7970,6 +8012,11 @@ and generate_prototype ?(extern = true) ?(static = false)
?(semicolon = true)
       | FileIn n
       | FileOut n ->
           if not in_daemon then (next (); pr "const char *%s" n)
+      | BufferIn n ->
+          next ();
+          pr "const char *%s" n;
+          next ();
+          pr "size_t %s_size" n
     ) (snd style);
     if is_RBufferOut then (next (); pr "size_t *size_r");
   );
@@ -7990,9 +8037,13 @@ and generate_c_call_args ?handle ?(decl = false) style
| Some handle -> pr "%s" handle; comma := true
   );
   List.iter (
-    fun arg ->
-      next ();
-      pr "%s" (name_of_argt arg)
+    function
+    | BufferIn n ->
+        next ();
+        pr "%s, %s_size" n n
+    | arg ->
+        next ();
+        pr "%s" (name_of_argt arg)
   ) (snd style);
   (* For RBufferOut calls, add implicit &size parameter. *)
   if not decl then (
@@ -8264,6 +8315,9 @@ copy_table (char * const * argv)
             pr "  const char *%s =\n" n;
             pr "    %sv != Val_int (0) ? String_val (Field (%sv, 0)) :
NULL;\n"
               n n
+        | BufferIn n ->
+            pr "  const char *%s = String_val (%sv);\n" n n;
+            pr "  size_t %s_size = caml_string_length (%sv);\n" n n
         | StringList n | DeviceList n ->
             pr "  char **%s = ocaml_guestfs_strings_val (g, %sv);\n"
n n
         | Bool n ->
@@ -8312,7 +8366,7 @@ copy_table (char * const * argv)
             pr "  ocaml_guestfs_free_strings (%s);\n" n;
         | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
         | Bool _ | Int _ | Int64 _
-        | FileIn _ | FileOut _ -> ()
+        | FileIn _ | FileOut _ | BufferIn _ -> ()
       ) (snd style);

       pr "  if (r == %s)\n" error_code;
@@ -8398,7 +8452,8 @@ and generate_ocaml_prototype ?(is_external = false) name
style    pr "%s : t -> " name;
   List.iter (
     function
-    | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _
-> pr "string -> "
+    | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _
+    | BufferIn _ -> pr "string -> "
     | OptString _ -> pr "string option -> "
     | StringList _ | DeviceList _ -> pr "string array -> "
     | Bool _ -> pr "bool -> "
@@ -8537,15 +8592,21 @@ DESTROY (g)
            pr "void\n" (* all lists returned implictly on the stack
*)
       );
       (* Call and arguments. *)
-      pr "%s " name;
-      generate_c_call_args ~handle:"g" ~decl:true style;
-      pr "\n";
+      pr "%s (g" name;
+      List.iter (
+        fun arg -> pr ", %s" (name_of_argt arg)
+      ) (snd style);
+      pr ")\n";
       pr "      guestfs_h *g;\n";
       iteri (
         fun i ->
           function
-          | Pathname n | Device n | Dev_or_Path n | String n | FileIn n |
FileOut n ->
+          | Pathname n | Device n | Dev_or_Path n | String n
+          | FileIn n | FileOut n ->
               pr "      char *%s;\n" n
+          | BufferIn n ->
+              pr "      char *%s;\n" n;
+              pr "      size_t %s_size = SvCUR (ST(%d));\n" n (i+1)
           | OptString n ->
               (* http://www.perlmonks.org/?node_id=554277
                * Note that the implicit handle argument means we have
@@ -8563,7 +8624,8 @@ DESTROY (g)
           function
           | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
           | Bool _ | Int _ | Int64 _
-          | FileIn _ | FileOut _ -> ()
+          | FileIn _ | FileOut _
+          | BufferIn _ -> ()
           | StringList n | DeviceList n -> pr "      free (%s);\n"
n
         ) (snd style)
       in
@@ -8941,7 +9003,8 @@ and generate_perl_prototype name style        comma :=
true;
       match arg with
       | Pathname n | Device n | Dev_or_Path n | String n
-      | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n ->
+      | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n
+      | BufferIn n ->
           pr "$%s" n
       | StringList n | DeviceList n ->
           pr "\\@%s" n
@@ -8953,6 +9016,7 @@ and generate_python_c ()    generate_header CStyle
LGPLv2plus;

   pr "\
+#define PY_SSIZE_T_CLEAN 1
 #include <Python.h>

 #include <stdio.h>
@@ -9200,9 +9264,13 @@ py_guestfs_close (PyObject *self, PyObject *args)

       List.iter (
         function
-        | Pathname n | Device n | Dev_or_Path n | String n | FileIn n | FileOut
n ->
+        | Pathname n | Device n | Dev_or_Path n | String n
+        | FileIn n | FileOut n ->
             pr "  const char *%s;\n" n
         | OptString n -> pr "  const char *%s;\n" n
+        | BufferIn n ->
+            pr "  const char *%s;\n" n;
+            pr "  Py_ssize_t %s_size;\n" n
         | StringList n | DeviceList n ->
             pr "  PyObject *py_%s;\n" n;
             pr "  char **%s;\n" n
@@ -9225,6 +9293,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
         | Int64 _ -> pr "L" (* XXX Whoever thought it was a good
idea to
                              * emulate C's int/long/long long in Python?
                              *)
+        | BufferIn _ -> pr "s#"
       ) (snd style);
       pr ":guestfs_%s\",\n" name;
       pr "                         &py_g";
@@ -9236,6 +9305,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
         | Bool n -> pr ", &%s" n
         | Int n -> pr ", &%s" n
         | Int64 n -> pr ", &%s" n
+        | BufferIn n -> pr ", &%s, &%s_size" n n
       ) (snd style);

       pr "))\n";
@@ -9245,7 +9315,8 @@ py_guestfs_close (PyObject *self, PyObject *args)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ ->
()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
+        | BufferIn _ -> ()
         | StringList n | DeviceList n ->
             pr "  %s = get_string_list (py_%s);\n" n n;
             pr "  if (!%s) return NULL;\n" n
@@ -9260,7 +9331,8 @@ py_guestfs_close (PyObject *self, PyObject *args)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ ->
()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
+        | BufferIn _ -> ()
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
@@ -9571,6 +9643,13 @@ static VALUE ruby_guestfs_close (VALUE gv)
             pr "  if (!%s)\n" n;
             pr "    rb_raise (rb_eTypeError, \"expected string for
parameter %%s of %%s\",\n";
             pr "              \"%s\", \"%s\");\n"
n name
+        | BufferIn n ->
+            pr "  Check_Type (%sv, T_STRING);\n" n;
+            pr "  const char *%s = RSTRING (%sv)->ptr;\n" n n;
+            pr "  if (!%s)\n" n;
+            pr "    rb_raise (rb_eTypeError, \"expected string for
parameter %%s of %%s\",\n";
+            pr "              \"%s\", \"%s\");\n"
n name;
+            pr "  size_t %s_size = RSTRING (%sv)->len;\n" n n
         | OptString n ->
             pr "  const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) :
NULL;\n" n n n
         | StringList n | DeviceList n ->
@@ -9620,7 +9699,8 @@ static VALUE ruby_guestfs_close (VALUE gv)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ ->
()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
+        | BufferIn _ -> ()
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
@@ -9937,6 +10017,8 @@ and generate_java_prototype ?(public=false)
?(privat=false) ?(native=false)
       | FileIn n
       | FileOut n ->
           pr "String %s" n
+      | BufferIn n ->
+          pr "byte[] %s" n
       | StringList n | DeviceList n ->
           pr "String[] %s" n
       | Bool n ->
@@ -10058,6 +10140,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
         | FileIn n
         | FileOut n ->
             pr ", jstring j%s" n
+        | BufferIn n ->
+            pr ", jbyteArray j%s" n
         | StringList n | DeviceList n ->
             pr ", jobjectArray j%s" n
         | Bool n ->
@@ -10113,6 +10197,9 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
         | FileIn n
         | FileOut n ->
             pr "  const char *%s;\n" n
+        | BufferIn n ->
+            pr "  jbyte *%s;\n" n;
+            pr "  size_t %s_size;\n" n
         | StringList n | DeviceList n ->
             pr "  int %s_len;\n" n;
             pr "  const char **%s;\n" n
@@ -10152,6 +10239,9 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
              * a NULL parameter.
              *)
             pr "  %s = j%s ? (*env)->GetStringUTFChars (env, j%s, NULL)
: NULL;\n" n n n
+        | BufferIn n ->
+            pr "  %s = (*env)->GetByteArrayElements (env, j%s,
NULL);\n" n n;
+            pr "  %s_size = (*env)->GetArrayLength (env, j%s);\n"
n n
         | StringList n | DeviceList n ->
             pr "  %s_len = (*env)->GetArrayLength (env, j%s);\n" n
n;
             pr "  %s = guestfs_safe_malloc (g, sizeof (char *) *
(%s_len+1));\n" n n;
@@ -10184,6 +10274,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
         | OptString n ->
             pr "  if (j%s)\n" n;
             pr "    (*env)->ReleaseStringUTFChars (env, j%s,
%s);\n" n n
+        | BufferIn n ->
+            pr "  (*env)->ReleaseByteArrayElements (env, j%s, %s,
0);\n" n n
         | StringList n | DeviceList n ->
             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
             pr "    jobject o = (*env)->GetObjectArrayElement (env,
j%s, i);\n"
@@ -10458,7 +10550,10 @@ last_error h = do
           function
           | FileIn n
           | FileOut n
-          | Pathname n | Device n | Dev_or_Path n | String n -> pr
"withCString %s $ \\%s -> " n n
+          | Pathname n | Device n | Dev_or_Path n | String n ->
+              pr "withCString %s $ \\%s -> " n n
+          | BufferIn n ->
+              pr "withCStringLen %s $ \\(%s, %s_size) -> " n 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 _ | Int64 _ -> ()
@@ -10472,6 +10567,7 @@ last_error h = do
             | 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
+            | BufferIn n -> sprintf "%s (fromIntegral %s_size)" n
n
           ) (snd style) in
         pr "withForeignPtr h (\\p -> c_%s %s)\n" name
           (String.concat " " ("p" :: args));
@@ -10522,6 +10618,9 @@ and generate_haskell_prototype ~handle ?(hs = false)
style      fun arg ->
       (match arg with
        | Pathname _ | Device _ | Dev_or_Path _ | String _ -> pr
"%s" string
+       | BufferIn _ ->
+           if hs then pr "String"
+           else pr "CString -> CInt"
        | OptString _ -> if hs then pr "Maybe String" else pr
"CString"
        | StringList _ | DeviceList _ -> if hs then pr "[String]"
else pr "Ptr CString"
        | Bool _ -> pr "%s" bool
@@ -10711,7 +10810,8 @@ namespace Guestfs
         List.iter (
           function
           | Pathname n | Device n | Dev_or_Path n | String n | OptString n
-          | FileIn n | FileOut n ->
+          | FileIn n | FileOut n
+          | BufferIn n ->
               pr ", [In] string %s" n
           | StringList n | DeviceList n ->
               pr ", [In] string[] %s" n
@@ -10734,7 +10834,8 @@ namespace Guestfs
         List.iter (
           function
           | Pathname n | Device n | Dev_or_Path n | String n | OptString n
-          | FileIn n | FileOut n ->
+          | FileIn n | FileOut n
+          | BufferIn n ->
               next (); pr "string %s" n
           | StringList n | DeviceList n ->
               next (); pr "string[] %s" n
@@ -10839,6 +10940,10 @@ print_strings (char *const *argv)
       | String n
       | FileIn n
       | FileOut n -> pr "  printf (\"%%s\\n\", %s);\n" n
+      | BufferIn n ->
+          pr "  for (size_t i = 0; i < %s_size; ++i)\n" n;
+          pr "    printf (\"<%%02x>\", %s[i]);\n" n;
+          pr "  printf (\"\\n\");\n"
       | OptString n -> pr "  printf (\"%%s\\n\", %s ? %s :
\"null\");\n" n n
       | StringList n | DeviceList n -> pr "  print_strings
(%s);\n" n
       | Bool n -> pr "  printf (\"%%s\\n\", %s ?
\"true\" : \"false\");\n" n
@@ -10962,6 +11067,7 @@ let ()          | 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
+        | CallBuffer s -> sprintf "%S" s
       ) args
     )
   in
@@ -10996,6 +11102,7 @@ my $g = Sys::Guestfs->new ();
         | CallInt i -> string_of_int i
         | CallInt64 i -> Int64.to_string i
         | CallBool b -> if b then "1" else "0"
+        | CallBuffer s -> "\"" ^ c_quote s ^
"\""
       ) args
     )
   in
@@ -11027,6 +11134,7 @@ g = guestfs.GuestFS ()
         | CallInt i -> string_of_int i
         | CallInt64 i -> Int64.to_string i
         | CallBool b -> if b then "1" else "0"
+        | CallBuffer s -> "\"" ^ c_quote s ^
"\""
       ) args
     )
   in
@@ -11058,6 +11166,7 @@ g = Guestfs::create()
         | CallInt i -> string_of_int i
         | CallInt64 i -> Int64.to_string i
         | CallBool b -> string_of_bool b
+        | CallBuffer s -> "\"" ^ c_quote s ^
"\""
       ) args
     )
   in
@@ -11094,6 +11203,10 @@ public class Bindtests {
         | CallInt i -> string_of_int i
         | CallInt64 i -> Int64.to_string i
         | CallBool b -> string_of_bool b
+        | CallBuffer s ->
+            "new byte[] { " ^ String.concat "," (
+              map_chars (fun c -> string_of_int (Char.code c)) s
+            ) ^ " }"
       ) args
     )
   in
@@ -11139,6 +11252,7 @@ main = do
         | CallInt64 i -> Int64.to_string i
         | CallBool true -> "True"
         | CallBool false -> "False"
+        | CallBuffer s -> "\"" ^ c_quote s ^
"\""
       ) args
     )
   in
@@ -11155,43 +11269,56 @@ main = do
 and generate_lang_bindtests call    call "test0" [CallString
"abc"; CallOptString (Some "def");
                 CallStringList []; CallBool false;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString None;
                 CallStringList []; CallBool false;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString ""; CallOptString (Some
"def");
                 CallStringList []; CallBool false;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString ""; CallOptString (Some
"");
                 CallStringList []; CallBool false;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"; "2"]; CallBool false;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool true;
-                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt (-1); CallInt64 (-1L); CallString "123";
CallString "456"];
+                CallInt (-1); CallInt64 (-1L); CallString "123";
CallString "456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt (-2); CallInt64 (-2L); CallString "123";
CallString "456"];
+                CallInt (-2); CallInt64 (-2L); CallString "123";
CallString "456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 1; CallInt64 1L; CallString "123"; CallString
"456"];
+                CallInt 1; CallInt64 1L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 2; CallInt64 2L; CallString "123"; CallString
"456"];
+                CallInt 2; CallInt64 2L; CallString "123"; CallString
"456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 4095; CallInt64 4095L; CallString "123";
CallString "456"];
+                CallInt 4095; CallInt64 4095L; CallString "123";
CallString "456";
+                CallBuffer "abc\000abc"];
   call "test0" [CallString "abc"; CallOptString (Some
"def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 0; CallInt64 0L; CallString ""; CallString
""]
+                CallInt 0; CallInt64 0L; CallString ""; CallString
"";
+                CallBuffer "abc\000abc"]

 (* XXX Add here tests of the return and error functions. *)

--
1.6.6.1