diff --git a/src/guestfs-internal-frontend.h b/src/guestfs-internal-frontend.h index 16bb6a06a..8ba8a25f8 100644 --- a/src/guestfs-internal-frontend.h +++ b/src/guestfs-internal-frontend.h @@ -90,6 +90,7 @@ extern void guestfs_int_fadvise_random (int fd); extern void guestfs_int_fadvise_noreuse (int fd); //extern void guestfs_int_fadvise_dontneed (int fd); //extern void guestfs_int_fadvise_willneed (int fd); +extern char *guestfs_int_shell_unquote (const char *str); struct uefi_firmware { const char *code; /* code file (NULL = end of list) */ diff --git a/src/utils.c b/src/utils.c index e646852ef..4faff9eb4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -539,3 +539,63 @@ guestfs_int_fadvise_willneed (int fd) #endif } #endif + +/** + * Unquote a shell-quoted string. + * + * Augeas passes strings to us which may be quoted, eg. if they come + * from files in F. This function can do simple + * unquoting of these strings. + * + * Note this function does not do variable substitution, since that is + * impossible without knowing the file context and indeed the + * environment under which the shell script is run. Configuration + * files should not use complex quoting. + * + * C is the input string from Augeas, a string that may be + * single- or double-quoted or may not be quoted. The returned string + * is unquoted, and must be freed by the caller. C is returned + * on error and C is set accordingly. + * + * For information on double-quoting in bash, see + * L + */ +char * +guestfs_int_shell_unquote (const char *str) +{ + size_t len = strlen (str); + char *ret; + + if (len >= 2) { + if (str[0] == '\'' && str[len-1] == '\'') { + /* single quoting */ + ret = strndup (&str[1], len-2); + if (ret == NULL) + return NULL; + return ret; + } + else if (str[0] == '"' && str[len-1] == '"') { + /* double quoting */ + size_t i, j; + + ret = malloc (len + 1); /* strings always get smaller */ + if (ret == NULL) + return NULL; + + for (i = 1, j = 0; i < len-1 /* ignore final quote */; ++i, ++j) { + if (i < len-2 /* ignore final char before final quote */ && + str[i] == '\\' && + (str[i+1] == '$' || str[i+1] == '`' || str[i+1] == '"' || + str[i+1] == '\\' || str[i+1] == '\n')) + ++i; + ret[j] = str[i]; + } + + ret[j] = '\0'; + + return ret; + } + } + + return strdup (str); +} diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index e5778ef49..e23cd6476 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -516,7 +516,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps = fun line -> if Str.string_match rex line 0 then ( let path = Str.matched_group 1 line in - let path = Linux.shell_unquote path in + let path = Utils.shell_unquote path in if String.length path >= 1 && path.[0] = '/' then ( let vboxuninstall = path ^ "/uninstall.sh" in Some vboxuninstall diff --git a/v2v/linux.ml b/v2v/linux.ml index f15416232..7cf31ff94 100644 --- a/v2v/linux.ml +++ b/v2v/linux.ml @@ -178,47 +178,3 @@ let rec file_owner g inspect path = and is_file_owned g inspect path = try file_owner g inspect path; true with Not_found -> false - -let rec shell_unquote str = - let len = String.length str in - if len >= 2 then ( - if String.is_prefix str "'" && String.is_suffix str "'" then - String.sub str 1 (len-2) - else if String.is_prefix str "\"" && String.is_suffix str "\"" then - shell_unquote_double str len - else - str - ) - else str - -(* https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html - * but note we don't do any variable expansion etc so really we just - * handle backslash here. - *) -and shell_unquote_double str len = - let i = ref 1 and j = ref 0 in - while !i < len-1 (* ignore final quote *) do - if is_backslash_sequence str !i len then - incr i; - incr i; - incr j - done; - - let outlen = !j in - let outstr = String.create outlen in - let i = ref 1 and j = ref 0 in - while !i < len-1 do - if is_backslash_sequence str !i len then - incr i; - outstr.[!j] <- str.[!i]; - incr i; - incr j - done; - - outstr - -and is_backslash_sequence str i len = - i < len-2 (* ignore final character before the final quote *) && - str.[i] = '\\' && - (str.[i+1] = '$' || str.[i+1] = '`' || str.[i+1] = '"' - || str.[i+1] = '\\' || str.[i+1] = '\n') diff --git a/v2v/linux.mli b/v2v/linux.mli index 6268805ef..43ab9d656 100644 --- a/v2v/linux.mli +++ b/v2v/linux.mli @@ -39,11 +39,3 @@ val file_owner : Guestfs.guestfs -> Types.inspect -> string -> string val is_file_owned : Guestfs.guestfs -> Types.inspect -> string -> bool (** Returns true if the file is owned by an installed package. *) - -val shell_unquote : string -> string -(** If the string looks like a shell quoted string, then attempt to - unquote it. - - This is just intended to deal with quoting in configuration files - (like ones under /etc/sysconfig), and it doesn't deal with some - situations such as $variable interpolation. *) diff --git a/v2v/utils-c.c b/v2v/utils-c.c index e203dd212..2812a3dc7 100644 --- a/v2v/utils-c.c +++ b/v2v/utils-c.c @@ -21,12 +21,20 @@ #include #include #include +#include #include #include #include #include +#ifdef HAVE_CAML_UNIXSUPPORT_H +#include +#else +#define Nothing ((value) 0) +extern void unix_error (int errcode, char * cmdname, value arg) Noreturn; +#endif + #include "guestfs.h" #include "guestfs-internal-frontend.h" @@ -104,3 +112,19 @@ v2v_utils_aavmf_firmware (value unitv) { return get_firmware (guestfs_int_aavmf_firmware); } + +value +v2v_utils_shell_unquote (value strv) +{ + CAMLparam1 (strv); + CAMLlocal1 (retv); + char *ret; + + ret = guestfs_int_shell_unquote (String_val (strv)); + if (ret == NULL) + unix_error (errno, (char *) "guestfs_int_shell_unquote", Nothing); + + retv = caml_copy_string (ret); + free (ret); + CAMLreturn (retv); +} diff --git a/v2v/utils.ml b/v2v/utils.ml index 850a35592..fc1c502af 100644 --- a/v2v/utils.ml +++ b/v2v/utils.ml @@ -142,6 +142,8 @@ let du filename = | line::_ -> Int64.of_string line | [] -> invalid_arg filename +external shell_unquote : string -> string = "v2v_utils_shell_unquote" + (* The following functions are only exported for unit tests. *) module UNIT_TESTS = struct let ovmf_i386_firmware = ovmf_i386_firmware diff --git a/v2v/utils.mli b/v2v/utils.mli index 7d8d4ee76..a00e60b07 100644 --- a/v2v/utils.mli +++ b/v2v/utils.mli @@ -66,6 +66,14 @@ val du : string -> int64 This can raise either [Failure] or [Invalid_argument] in case of errors. *) +val shell_unquote : string -> string +(** If the string looks like a shell quoted string, then attempt to + unquote it. + + This is just intended to deal with quoting in configuration files + (like ones under /etc/sysconfig), and it doesn't deal with some + situations such as $variable interpolation. *) + (**/**) (* The following functions are only exported for unit tests. *) diff --git a/v2v/v2v_unit_tests.ml b/v2v/v2v_unit_tests.ml index 56860a6ff..f1cef27cc 100644 --- a/v2v/v2v_unit_tests.ml +++ b/v2v/v2v_unit_tests.ml @@ -767,16 +767,16 @@ let test_virtio_iso_path_matches_guest_os ctx = let test_shell_unquote ctx = let printer = identity in - assert_equal ~printer "a" (Linux.shell_unquote "a"); - assert_equal ~printer "b" (Linux.shell_unquote "'b'"); - assert_equal ~printer "c" (Linux.shell_unquote "\"c\""); - assert_equal ~printer "dd" (Linux.shell_unquote "\"dd\""); - assert_equal ~printer "e\\e" (Linux.shell_unquote "\"e\\\\e\""); - assert_equal ~printer "f\\" (Linux.shell_unquote "\"f\\\\\""); - assert_equal ~printer "\\g" (Linux.shell_unquote "\"\\\\g\""); - assert_equal ~printer "h\\-h" (Linux.shell_unquote "\"h\\-h\""); - assert_equal ~printer "i`" (Linux.shell_unquote "\"i\\`\""); - assert_equal ~printer "j\"" (Linux.shell_unquote "\"j\\\"\"") + assert_equal ~printer "a" (Utils.shell_unquote "a"); + assert_equal ~printer "b" (Utils.shell_unquote "'b'"); + assert_equal ~printer "c" (Utils.shell_unquote "\"c\""); + assert_equal ~printer "dd" (Utils.shell_unquote "\"dd\""); + assert_equal ~printer "e\\e" (Utils.shell_unquote "\"e\\\\e\""); + assert_equal ~printer "f\\" (Utils.shell_unquote "\"f\\\\\""); + assert_equal ~printer "\\g" (Utils.shell_unquote "\"\\\\g\""); + assert_equal ~printer "h\\-h" (Utils.shell_unquote "\"h\\-h\""); + assert_equal ~printer "i`" (Utils.shell_unquote "\"i\\`\""); + assert_equal ~printer "j\"" (Utils.shell_unquote "\"j\\\"\"") let test_find_uefi_firmware ctx = let rec printer = function @@ -815,7 +815,7 @@ let suite = "Utils.drive_index" >:: test_drive_index; "Windows_virtio.virtio_iso_path_matches_guest_os" >:: test_virtio_iso_path_matches_guest_os; - "Linux.shell_unquote" >:: test_shell_unquote; + "Utils.shell_unquote" >:: test_shell_unquote; "Utils.find_uefi_firmware" >:: test_find_uefi_firmware; ]