Richard W.M. Jones
2010-May-18 21:01 UTC
[Libguestfs] [PATCH 0/2] Implement BufferIn argument, and new 'write' call to replace 'write-file'
This is a fix for: https://bugzilla.redhat.com/show_bug.cgi?id=501889 -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
Richard W.M. Jones
2010-May-18 21:03 UTC
[Libguestfs] [PATCH 1/2] generator: Implement BufferIn parameter type (RHBZ#501889).
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones 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 2001From: 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
Richard W.M. Jones
2010-May-18 21:04 UTC
[Libguestfs] [PATCH 2/2] New API: write for creating files with fixed content (RHBZ#501889).
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/ -------------- next part -------------->From 40a88c206f7759bcd13f8e483b23373d49958056 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Tue, 18 May 2010 21:51:05 +0100 Subject: [PATCH 2/2] New API: write for creating files with fixed content (RHBZ#501889). The guestfs_write call can be used to create small files with arbitrary 8 bit content, including \0 bytes. This replaces and deprecates write-file, which cannot be modified to use BufferIn because of an unfortunate choice in the ABI: the size parameter to write-file, if zero, means that the daemon tries to calculate the length of the buffer using strlen. However this fails if we pass a zero-length buffer using BufferIn because then the daemon tries to do strlen on a (really) zero length buffer, not even containing a terminating \0 character, thus segfaulting. --- daemon/file.c | 28 +++++++++++++ fish/edit.c | 2 +- fish/fish.c | 2 +- fish/guestfish.pod | 4 +- fuse/test-fuse.sh | 4 +- recipes/clone.sh | 4 +- src/MAX_PROC_NR | 2 +- src/generator.ml | 99 +++++++++++++++++++++++++----------------------- src/guestfs.pod | 4 +- tools/make-test-img.sh | 12 +++--- 10 files changed, 96 insertions(+), 65 deletions(-) diff --git a/daemon/file.c b/daemon/file.c index 2399828..7d37f56 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -314,6 +314,34 @@ do_write_file (const char *path, const char *content, int size) return 0; } +int +do_write (const char *path, const char *content, size_t size) +{ + int fd; + + CHROOT_IN; + fd = open (path, O_WRONLY | O_TRUNC | O_CREAT | O_NOCTTY, 0666); + CHROOT_OUT; + + if (fd == -1) { + reply_with_perror ("open: %s", path); + return -1; + } + + if (xwrite (fd, content, size) == -1) { + reply_with_perror ("write"); + close (fd); + return -1; + } + + if (close (fd) == -1) { + reply_with_perror ("close: %s", path); + return -1; + } + + return 0; +} + char * do_read_file (const char *path, size_t *size_r) { diff --git a/fish/edit.c b/fish/edit.c index 3fc41fb..a010057 100644 --- a/fish/edit.c +++ b/fish/edit.c @@ -156,7 +156,7 @@ do_edit (const char *cmd, int argc, char *argv[]) } /* Write new content. */ - if (guestfs_write_file (g, argv[0], content_new, size) == -1) { + if (guestfs_write (g, argv[0], content_new, size) == -1) { free (content); free (content_new); return -1; diff --git a/fish/fish.c b/fish/fish.c index a630cdc..5d89d60 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -1091,7 +1091,7 @@ display_builtin_command (const char *cmd) " This is used to edit a file.\n" "\n" " It is the equivalent of (and is implemented by)\n" - " running \"cat\", editing locally, and then \"write-file\".\n" + " running \"cat\", editing locally, and then \"write\".\n" "\n" " Normally it uses $EDITOR, but if you use the aliases\n" " \"vi\" or \"emacs\" you will get those editors.\n" diff --git a/fish/guestfish.pod b/fish/guestfish.pod index 7260caf..274f1d4 100644 --- a/fish/guestfish.pod +++ b/fish/guestfish.pod @@ -41,7 +41,7 @@ Create a new C</etc/motd> file in a guest: add disk.img run mount /dev/vg_guest/lv_root / - write_file /etc/motd "Welcome, new users" 0 + write /etc/motd "Welcome, new users" _EOF_ List the LVM logical volumes in a guest: @@ -57,7 +57,7 @@ Update C</etc/resolv.conf> in a guest: guestfish \ add disk.img : run : mount /dev/vg_guest/lv_root / : \ - write-file /etc/resolv.conf "nameserver 1.2.3.4" 0 + write /etc/resolv.conf "nameserver 1.2.3.4" Edit C</boot/grub/grub.conf> interactively: diff --git a/fuse/test-fuse.sh b/fuse/test-fuse.sh index 7dc8e4b..e31ea9d 100755 --- a/fuse/test-fuse.sh +++ b/fuse/test-fuse.sh @@ -93,8 +93,8 @@ $guestfish <<EOF part-disk /dev/sda mbr mkfs ext2 /dev/sda1 mount /dev/sda1 / - write-file /hello.txt hello 0 - write-file /world.txt "hello world" 0 + write /hello.txt hello + write /world.txt "hello world" touch /empty EOF diff --git a/recipes/clone.sh b/recipes/clone.sh index e3fc11c..d2b9d99 100755 --- a/recipes/clone.sh +++ b/recipes/clone.sh @@ -9,7 +9,7 @@ hostname="$5" dd if="$preimage" of="$newimage" guestfish -a "$newimage" -m "$root" <<EOF -write-file /etc/resolv.conf "nameserver $nameserver" 0 -write-file /etc/HOSTNAME "$hostname" 0 +write /etc/resolv.conf "nameserver $nameserver" +write /etc/HOSTNAME "$hostname" sync EOF diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 2c2b1af..5d165ff 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -245 +246 diff --git a/src/generator.ml b/src/generator.ml index 090c280..dfab8dc 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -938,7 +938,7 @@ let daemon_functions = [ [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; + ["write"; "/new"; "new file contents"]; ["cat"; "/new"]], "new file contents")], "mount a guest disk at a position in the filesystem", "\ @@ -1500,7 +1500,7 @@ on the volume group C<volgroup>, with C<size> megabytes."); [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount_options"; ""; "/dev/sda1"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; + ["write"; "/new"; "new file contents"]; ["cat"; "/new"]], "new file contents")], "make a filesystem", "\ @@ -1537,25 +1537,8 @@ the string C<,> (comma). See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>, C<guestfs_part_init>"); - ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning], - [InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "new file contents"; "0"]; - ["cat"; "/new"]], "new file contents"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "\nnew file contents\n"; "0"]; - ["cat"; "/new"]], "\nnew file contents\n"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "\n\n"; "0"]; - ["cat"; "/new"]], "\n\n"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; ""; "0"]; - ["cat"; "/new"]], ""); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "\n\n\n"; "0"]; - ["cat"; "/new"]], "\n\n\n"); - InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "\n"; "0"]; - ["cat"; "/new"]], "\n")], + ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning; DeprecatedBy "write"], + [], "create a file", "\ This call creates a file called C<path>. The contents of the @@ -1567,9 +1550,7 @@ then the length is calculated using C<strlen> (so in this case the content cannot contain embedded ASCII NULs). I<NB.> Owing to a bug, writing content containing ASCII NUL -characters does I<not> work, even if the length is specified. -We hope to resolve this bug in a future version. In the meantime -use C<guestfs_upload>."); +characters does I<not> work, even if the length is specified."); ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"], [InitEmpty, Always, TestOutputListOfDevices ( @@ -2088,7 +2069,7 @@ To download an uncompressed tarball, use C<guestfs_tar_out>."); ["mount_ro"; "/dev/sda1"; "/"]; ["touch"; "/new"]]); InitBasicFS, Always, TestOutput ( - [["write_file"; "/new"; "data"; "0"]; + [["write"; "/new"; "data"]; ["umount"; "/"]; ["mount_ro"; "/dev/sda1"; "/"]; ["cat"; "/new"]], "data")], @@ -2340,15 +2321,15 @@ C<device>, with the root directory being C<root>."); ("cp", (RErr, [Pathname "src"; Pathname "dest"]), 87, [], [InitBasicFS, Always, TestOutput ( - [["write_file"; "/old"; "file content"; "0"]; + [["write"; "/old"; "file content"]; ["cp"; "/old"; "/new"]; ["cat"; "/new"]], "file content"); InitBasicFS, Always, TestOutputTrue ( - [["write_file"; "/old"; "file content"; "0"]; + [["write"; "/old"; "file content"]; ["cp"; "/old"; "/new"]; ["is_file"; "/old"]]); InitBasicFS, Always, TestOutput ( - [["write_file"; "/old"; "file content"; "0"]; + [["write"; "/old"; "file content"]; ["mkdir"; "/dir"]; ["cp"; "/old"; "/dir/new"]; ["cat"; "/dir/new"]], "file content")], @@ -2361,7 +2342,7 @@ either a destination filename or destination directory."); [InitBasicFS, Always, TestOutput ( [["mkdir"; "/olddir"]; ["mkdir"; "/newdir"]; - ["write_file"; "/olddir/file"; "file content"; "0"]; + ["write"; "/olddir/file"; "file content"]; ["cp_a"; "/olddir"; "/newdir"]; ["cat"; "/newdir/olddir/file"]], "file content")], "copy a file or directory recursively", @@ -2371,11 +2352,11 @@ recursively using the C<cp -a> command."); ("mv", (RErr, [Pathname "src"; Pathname "dest"]), 89, [], [InitBasicFS, Always, TestOutput ( - [["write_file"; "/old"; "file content"; "0"]; + [["write"; "/old"; "file content"]; ["mv"; "/old"; "/new"]; ["cat"; "/new"]], "file content"); InitBasicFS, Always, TestOutputFalse ( - [["write_file"; "/old"; "file content"; "0"]; + [["write"; "/old"; "file content"]; ["mv"; "/old"; "/new"]; ["is_file"; "/old"]])], "move a file", @@ -2424,12 +2405,12 @@ or attached block device(s) in any other way."); ("equal", (RBool "equality", [Pathname "file1"; Pathname "file2"]), 93, [], [InitBasicFS, Always, TestOutputTrue ( - [["write_file"; "/file1"; "contents of a file"; "0"]; + [["write"; "/file1"; "contents of a file"]; ["cp"; "/file1"; "/file2"]; ["equal"; "/file1"; "/file2"]]); InitBasicFS, Always, TestOutputFalse ( - [["write_file"; "/file1"; "contents of a file"; "0"]; - ["write_file"; "/file2"; "contents of another file"; "0"]; + [["write"; "/file1"; "contents of a file"]; + ["write"; "/file2"; "contents of another file"]; ["equal"; "/file1"; "/file2"]]); InitBasicFS, Always, TestLastFail ( [["equal"; "/file1"; "/file2"]])], @@ -2456,8 +2437,8 @@ the list of printable strings found."); ("strings_e", (RStringList "stringsout", [String "encoding"; Pathname "path"]), 95, [ProtocolLimitWarning], [InitISOFS, Always, TestOutputList ( [["strings_e"; "b"; "/known-5"]], []); - InitBasicFS, Disabled, TestOutputList ( - [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"]; + InitBasicFS, Always, TestOutputList ( + [["write"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"]; ["strings_e"; "b"; "/new"]], ["hello"; "world"])], "print the printable strings in a file", "\ @@ -2521,7 +2502,7 @@ the human-readable, canonical hex dump of the file."); [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext3"; "/dev/sda1"]; ["mount_options"; ""; "/dev/sda1"; "/"]; - ["write_file"; "/new"; "test file"; "0"]; + ["write"; "/new"; "test file"]; ["umount"; "/dev/sda1"]; ["zerofree"; "/dev/sda1"]; ["mount_options"; ""; "/dev/sda1"; "/"]; @@ -2626,7 +2607,7 @@ are activated or deactivated."); ["lvcreate"; "LV"; "VG"; "10"]; ["mkfs"; "ext2"; "/dev/VG/LV"]; ["mount_options"; ""; "/dev/VG/LV"; "/"]; - ["write_file"; "/new"; "test content"; "0"]; + ["write"; "/new"; "test content"]; ["umount"; "/"]; ["lvresize"; "/dev/VG/LV"; "20"]; ["e2fsck_f"; "/dev/VG/LV"]; @@ -2813,7 +2794,7 @@ manual page for more details."); ("scrub_file", (RErr, [Pathname "file"]), 115, [Optional "scrub"], [InitBasicFS, Always, TestRun ( - [["write_file"; "/file"; "content"; "0"]; + [["write"; "/file"; "content"]; ["scrub_file"; "/file"]])], "scrub (securely wipe) a file", "\ @@ -3721,7 +3702,7 @@ and C<guestfs_setcon>"); [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs_b"; "ext2"; "4096"; "/dev/sda1"]; ["mount_options"; ""; "/dev/sda1"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; + ["write"; "/new"; "new file contents"]; ["cat"; "/new"]], "new file contents")], "make a filesystem with block size", "\ @@ -3736,7 +3717,7 @@ are C<1024>, C<2048> or C<4096> only."); ["mke2journal"; "4096"; "/dev/sda1"]; ["mke2fs_J"; "ext2"; "4096"; "/dev/sda2"; "/dev/sda1"]; ["mount_options"; ""; "/dev/sda2"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; + ["write"; "/new"; "new file contents"]; ["cat"; "/new"]], "new file contents")], "make ext2/3/4 external journal", "\ @@ -3751,7 +3732,7 @@ to the command: ["mke2journal_L"; "4096"; "JOURNAL"; "/dev/sda1"]; ["mke2fs_JL"; "ext2"; "4096"; "/dev/sda2"; "JOURNAL"]; ["mount_options"; ""; "/dev/sda2"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; + ["write"; "/new"; "new file contents"]; ["cat"; "/new"]], "new file contents")], "make ext2/3/4 external journal with label", "\ @@ -3764,7 +3745,7 @@ This creates an ext2 external journal on C<device> with label C<label>."); ["mke2journal_U"; "4096"; uuid; "/dev/sda1"]; ["mke2fs_JU"; "ext2"; "4096"; "/dev/sda2"; uuid]; ["mount_options"; ""; "/dev/sda2"; "/"]; - ["write_file"; "/new"; "new file contents"; "0"]; + ["write"; "/new"; "new file contents"]; ["cat"; "/new"]], "new file contents")]), "make ext2/3/4 external journal with UUID", "\ @@ -3927,7 +3908,7 @@ if you used the C<guestfs_mount> call)."); ("truncate", (RErr, [Pathname "path"]), 199, [], [InitBasicFS, Always, TestOutputStruct ( - [["write_file"; "/test"; "some stuff so size is not zero"; "0"]; + [["write"; "/test"; "some stuff so size is not zero"]; ["truncate"; "/test"]; ["stat"; "/test"]], [CompareWithInt ("size", 0)])], "truncate a file to zero size", @@ -4345,7 +4326,7 @@ See also C<guestfs_version>. ("dd", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"]), 217, [], [InitBasicFS, Always, TestOutputBuffer ( - [["write_file"; "/src"; "hello, world"; "0"]; + [["write"; "/src"; "hello, world"]; ["dd"; "/src"; "/dest"]; ["read_file"; "/dest"]], "hello, world")], "copy from source to destination using dd", @@ -4361,7 +4342,7 @@ This command cannot do partial copies (see C<guestfs_copy_size>)."); ("filesize", (RInt64 "size", [Pathname "file"]), 218, [], [InitBasicFS, Always, TestOutputInt ( - [["write_file"; "/file"; "hello, world"; "0"]; + [["write"; "/file"; "hello, world"]; ["filesize"; "/file"]], 12)], "return the size of the file in bytes", "\ @@ -4452,7 +4433,7 @@ See also C<guestfs_vgpvuuids>."); ("copy_size", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"; Int64 "size"]), 227, [], [InitBasicFS, Always, TestOutputBuffer ( - [["write_file"; "/src"; "hello, world"; "0"]; + [["write"; "/src"; "hello, world"]; ["copy_size"; "/src"; "/dest"; "5"]; ["read_file"; "/dest"]], "hello")], "copy size bytes from source to destination using dd", @@ -4653,6 +4634,30 @@ a new file of length C<len> containing the repeating pattern of bytes in C<pattern>. The pattern is truncated if necessary to ensure the length of the file is exactly C<len> bytes."); + ("write", (RErr, [Pathname "path"; BufferIn "content"]), 246, [ProtocolLimitWarning], + [InitBasicFS, Always, TestOutput ( + [["write"; "/new"; "new file contents"]; + ["cat"; "/new"]], "new file contents"); + InitBasicFS, Always, TestOutput ( + [["write"; "/new"; "\nnew file contents\n"]; + ["cat"; "/new"]], "\nnew file contents\n"); + InitBasicFS, Always, TestOutput ( + [["write"; "/new"; "\n\n"]; + ["cat"; "/new"]], "\n\n"); + InitBasicFS, Always, TestOutput ( + [["write"; "/new"; ""]; + ["cat"; "/new"]], ""); + InitBasicFS, Always, TestOutput ( + [["write"; "/new"; "\n\n\n"]; + ["cat"; "/new"]], "\n\n\n"); + InitBasicFS, Always, TestOutput ( + [["write"; "/new"; "\n"]; + ["cat"; "/new"]], "\n")], + "create a new file", + "\ +This call creates a file called C<path>. The content of the +file is the string C<content> (which can contain any 8 bit data)."); + ] let all_functions = non_daemon_functions @ daemon_functions diff --git a/src/guestfs.pod b/src/guestfs.pod index e1fa4f5..8404e74 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -258,9 +258,7 @@ L</guestfs_tgz_out>. It's often the case that you want to write a file or files to the disk image. -For small, single files, use L</guestfs_write_file>. This call -currently contains a bug which limits the call to plain text files -(not containing ASCII NUL characters). +For small, single files, use L</guestfs_write>. To upload a single file, use L</guestfs_upload>. This call has no limits on file content or size (even files larger than 4 GB). diff --git a/tools/make-test-img.sh b/tools/make-test-img.sh index 9752f1d..fddd8a2 100755 --- a/tools/make-test-img.sh +++ b/tools/make-test-img.sh @@ -68,12 +68,12 @@ mkdir /boot/grub touch /boot/grub/grub.conf # Test files. -write-file /etc/test1 "abcdefg" 0 -write-file /etc/test2 "" 0 -write-file /bin/test1 "abcdefg" 0 -write-file /bin/test2 "zxcvbnm" 0 -write-file /bin/test3 "1234567" 0 -write-file /bin/test4 "" 0 +write /etc/test1 "abcdefg" +write /etc/test2 "" +write /bin/test1 "abcdefg" +write /bin/test2 "zxcvbnm" +write /bin/test3 "1234567" +write /bin/test4 "" ln-s /bin/test1 /bin/test5 mkfifo 0777 /bin/test6 mknod 0777 10 10 /bin/test7 -- 1.6.6.1
Reasonably Related Threads
- [PATCH 0/9] FOR DISCUSSION ONLY: daemon error handling
- [PATCH 0/2] First part of fix for CVE-2010-3851
- [PATCH 0/8] Implement user cancellation
- [PATCH 0/9] Enhance virt-resize so it can really expand Linux and Windows guests
- [PATCH 0/6] Various Java bindings fixes.