[Libguestfs] [PATCH v6 25/41] daemon: Reimplement ‘file_architecture’ API in OCaml.

Richard W.M. Jones rjones at redhat.com
Thu Jun 15 17:06:15 UTC 2017


The previously library-side ‘file_architecture’ API is reimplemented
in the daemon, in OCaml.

There are some significant differences compared to the C
implementation:

 - The C code used libmagic.  That is replaced by calling the ‘file’
   command (because that is simpler than using the library).

 - The C code had extra cases to deal with compressed files.  This is
   not necessary since the ‘file’ command supports the ‘-z’ option
   which transparently looks inside compressed content (this is a
   consequence of the change above).

This commit demonstrates a number of techniques which will be useful
for moving inspection code to the daemon:

 - Moving an API from the C library to the OCaml daemon.

 - Calling from one OCaml API inside the daemon to another (from
   ‘Filearch.file_architecture’ to ‘File.file’).  This can be done and
   is done with C daemon APIs but correct reply_with_error handling is
   more difficult in C.

 - Use of Str for regular expression matching within the appliance.
---
 daemon/Makefile.am        |   2 +
 daemon/filearch.ml        | 137 +++++++++++++++++
 daemon/filearch.mli       |  19 +++
 docs/C_SOURCE_FILES       |   4 +-
 generator/actions_core.ml | 377 +++++++++++++++++++++++-----------------------
 generator/proc_nr.ml      |   1 +
 lib/MAX_PROC_NR           |   2 +-
 lib/Makefile.am           |   3 +-
 lib/filearch.c            | 362 --------------------------------------------
 po/POTFILES               |   1 -
 10 files changed, 353 insertions(+), 555 deletions(-)

diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index e49d78dd4..101ad877e 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -263,6 +263,7 @@ SOURCES_MLI = \
 	sysroot.mli \
 	devsparts.mli \
 	file.mli \
+	filearch.mli \
 	is.mli \
 	link.mli \
 	mount.mli \
@@ -280,6 +281,7 @@ SOURCES_ML = \
 	blkid.ml \
 	devsparts.ml \
 	file.ml \
+	filearch.ml \
 	is.ml \
 	link.ml \
 	mount.ml \
diff --git a/daemon/filearch.ml b/daemon/filearch.ml
new file mode 100644
index 000000000..68ddd61ea
--- /dev/null
+++ b/daemon/filearch.ml
@@ -0,0 +1,137 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Unix
+open Printf
+
+open Std_utils
+
+open Utils
+
+let re_file_elf =
+  Str.regexp "ELF \\([0-9]+\\)-bit \\(MSB\\|LSB\\).*\\(executable\\|shared object\\|relocatable\\), \\([^,]+\\),"
+
+let re_file_elf_ppc64 = Str.regexp ".*64.*PowerPC"
+
+let initrd_binaries = [
+  "bin/ls";
+  "bin/rm";
+  "bin/modprobe";
+  "sbin/modprobe";
+  "bin/sh";
+  "bin/bash";
+  "bin/dash";
+  "bin/nash";
+]
+
+let rec file_architecture orig_path =
+  (* Get the output of the "file" command.  Note that because this
+   * is running in the daemon, LANG=C so it's in English.
+   *)
+  let magic = File.file orig_path in
+  file_architecture_of_magic magic orig_path orig_path
+
+and file_architecture_of_magic magic orig_path path =
+  if Str.string_match re_file_elf magic 0 then (
+    let bits = Str.matched_group 1 magic in
+    let endianness = Str.matched_group 2 magic in
+    let elf_arch = Str.matched_group 4 magic in
+    canonical_elf_arch bits endianness elf_arch
+  )
+  else if String.find magic "PE32 executable" >= 0 then
+    "i386"
+  else if String.find magic "PE32+ executable" >= 0 then
+    "x86_64"
+  else if String.find magic "cpio archive" >= 0 then
+    cpio_arch magic orig_path path
+  else
+    failwithf "unknown architecture: %s" path
+
+(* Convert output from 'file' command on ELF files to the canonical
+ * architecture string.  Caller must free the result.
+ *)
+and canonical_elf_arch bits endianness elf_arch =
+  let substr s = String.find elf_arch s >= 0 in
+  if substr "Intel 80386" || substr "Intel 80486" then
+    "i386"
+  else if substr "x86-64" || substr "AMD x86-64" then
+    "x86_64"
+  else if substr "SPARC32" then
+    "sparc"
+  else if substr "SPARC V9" then
+    "sparc64"
+  else if substr "IA-64" then
+    "ia64"
+  else if Str.string_match re_file_elf_ppc64 elf_arch 0 then (
+    match endianness with
+    | "MSB" -> "ppc64"
+    | "LSB" -> "ppc64le"
+    | _ -> failwithf "unknown endianness '%s'" endianness
+  )
+  else if substr "PowerPC" then
+    "ppc"
+  else if substr "ARM aarch64" then
+    "aarch64"
+  else if substr "ARM" then
+    "arm"
+  else if substr "UCB RISC-V" then
+    sprintf "riscv%s" bits
+  else if substr "IBM S/390" then (
+    match bits with
+    | "32" -> "s390"
+    | "64" -> "s390x"
+    | _ -> failwithf "unknown S/390 bit size: %s" bits
+  )
+  else
+    elf_arch
+
+and cpio_arch magic orig_path path =
+  let sysroot = Sysroot.sysroot () in
+
+  let zcat =
+    if String.find magic "gzip" >= 0 then "zcat"
+    else if String.find magic "bzip2" >= 0 then "bzcat"
+    else if String.find magic "XZ compressed" >= 0 then "xzcat"
+    else "cat" in
+
+  let tmpdir = sprintf "/tmp/%s" (String.random8 ()) in
+  mkdir tmpdir 0o700;
+
+  (* Construct a command to extract named binaries from the initrd file. *)
+  let cmd =
+    sprintf "cd %s && %s %s | cpio --quiet -id %s"
+            tmpdir zcat (quote (sysroot // path))
+            (String.concat " " (List.map quote initrd_binaries)) in
+  if verbose () then eprintf "%s\n%!" cmd;
+  if Sys.command cmd <> 0 then
+    failwith "cpio command failed";
+
+  (* See if any of the binaries were present in the output. *)
+  let rec loop = function
+    | bin :: bins ->
+       let bin_path = tmpdir // bin in
+       if is_regular_file bin_path then (
+         let out = command "file" ["-zb"; bin_path] in
+         file_architecture_of_magic out orig_path bin_path
+       )
+       else
+         loop bins
+    | [] ->
+       failwithf "could not determine architecture of cpio archive: %s" path
+  in
+  loop initrd_binaries
diff --git a/daemon/filearch.mli b/daemon/filearch.mli
new file mode 100644
index 000000000..c4630225b
--- /dev/null
+++ b/daemon/filearch.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val file_architecture : string -> string
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 7e8770a9b..64518f1ae 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -72,6 +72,7 @@ daemon/blkdiscard.c
 daemon/blkid.c
 daemon/blockdev.c
 daemon/btrfs.c
+daemon/caml-stubs.c
 daemon/cap.c
 daemon/checksum.c
 daemon/cleanups.c
@@ -82,6 +83,7 @@ daemon/compress.c
 daemon/copy.c
 daemon/cpio.c
 daemon/cpmv.c
+daemon/daemon-c.c
 daemon/daemon.h
 daemon/dd.c
 daemon/debug-bmap.c
@@ -173,6 +175,7 @@ daemon/stubs.h
 daemon/swap.c
 daemon/sync.c
 daemon/syslinux.c
+daemon/sysroot-c.c
 daemon/tar.c
 daemon/truncate.c
 daemon/tsk.c
@@ -296,7 +299,6 @@ lib/errors.c
 lib/event-string.c
 lib/events.c
 lib/file.c
-lib/filearch.c
 lib/fuse.c
 lib/guestfs-internal-actions.h
 lib/guestfs-internal-all.h
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 54d0a6ca8..bfd96589e 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -183,194 +183,6 @@ making this an unreliable way to test for features.
 Use C<guestfs_available> or C<guestfs_feature_available> instead." };
 
   { defaults with
-    name = "file_architecture"; added = (1, 5, 3);
-    style = RString (RPlainString, "arch"), [String (Pathname, "filename")], [];
-    tests = [
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-aarch64-dynamic"]], "aarch64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-armv7-dynamic"]], "arm"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-i586-dynamic"]], "i386"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-ppc64-dynamic"]], "ppc64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-ppc64le-dynamic"]], "ppc64le"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-riscv64-dynamic"]], "riscv64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-s390x-dynamic"]], "s390x"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-sparc-dynamic"]], "sparc"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-win32.exe"]], "i386"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-win64.exe"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-x86_64-dynamic"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-aarch64.so"]], "aarch64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-armv7.so"]], "arm"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-i586.so"]], "i386"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-ppc64.so"]], "ppc64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-ppc64le.so"]], "ppc64le"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-riscv64.so"]], "riscv64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-s390x.so"]], "s390x"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-sparc.so"]], "sparc"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-win32.dll"]], "i386"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-win64.dll"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-x86_64.so"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/initrd-x86_64.img"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/initrd-x86_64.img.gz"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/bin-x86_64-dynamic.gz"]], "x86_64"), [];
-      InitISOFS, Always, TestResultString (
-        [["file_architecture"; "/lib-i586.so.xz"]], "i386"), [];
-    ];
-    shortdesc = "detect the architecture of a binary file";
-    longdesc = "\
-This detects the architecture of the binary F<filename>,
-and returns it if known.
-
-Currently defined architectures are:
-
-=over 4
-
-=item \"aarch64\"
-
-64 bit ARM.
-
-=item \"arm\"
-
-32 bit ARM.
-
-=item \"i386\"
-
-This string is returned for all 32 bit i386, i486, i586, i686 binaries
-irrespective of the precise processor requirements of the binary.
-
-=item \"ia64\"
-
-Intel Itanium.
-
-=item \"ppc\"
-
-32 bit Power PC.
-
-=item \"ppc64\"
-
-64 bit Power PC (big endian).
-
-=item \"ppc64le\"
-
-64 bit Power PC (little endian).
-
-=item \"riscv32\"
-
-=item \"riscv64\"
-
-=item \"riscv128\"
-
-RISC-V 32-, 64- or 128-bit variants.
-
-=item \"s390\"
-
-31 bit IBM S/390.
-
-=item \"s390x\"
-
-64 bit IBM S/390.
-
-=item \"sparc\"
-
-32 bit SPARC.
-
-=item \"sparc64\"
-
-64 bit SPARC V9 and above.
-
-=item \"x86_64\"
-
-64 bit x86-64.
-
-=back
-
-Libguestfs may return other architecture strings in future.
-
-The function works on at least the following types of files:
-
-=over 4
-
-=item *
-
-many types of Un*x and Linux binary
-
-=item *
-
-many types of Un*x and Linux shared library
-
-=item *
-
-Windows Win32 and Win64 binaries
-
-=item *
-
-Windows Win32 and Win64 DLLs
-
-Win32 binaries and DLLs return C<i386>.
-
-Win64 binaries and DLLs return C<x86_64>.
-
-=item *
-
-Linux kernel modules
-
-=item *
-
-Linux new-style initrd images
-
-=item *
-
-some non-x86 Linux vmlinuz kernels
-
-=back
-
-What it can't do currently:
-
-=over 4
-
-=item *
-
-static libraries (libfoo.a)
-
-=item *
-
-Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
-
-=item *
-
-x86 Linux vmlinuz kernels
-
-x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
-compressed code, and are horribly hard to unpack.  If you want to find
-the architecture of a kernel, use the architecture of the associated
-initrd or kernel module(s) instead.
-
-=back" };
-
-  { defaults with
     name = "mountable_device"; added = (1, 33, 15);
     style = RString (RDevice, "device"), [String (Mountable, "mountable")], [];
     shortdesc = "extract the device part of a mountable";
@@ -9628,4 +9440,193 @@ wildcards.
 Please note that this API may fail when used to compress directories
 with large files, such as the resulting squashfs will be over 3GB big." };
 
+  { defaults with
+    name = "file_architecture"; added = (1, 5, 3);
+    style = RString (RPlainString, "arch"), [String (Pathname, "filename")], [];
+    impl = OCaml "Filearch.file_architecture";
+    tests = [
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-aarch64-dynamic"]], "aarch64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-armv7-dynamic"]], "arm"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-i586-dynamic"]], "i386"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-ppc64-dynamic"]], "ppc64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-ppc64le-dynamic"]], "ppc64le"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-riscv64-dynamic"]], "riscv64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-s390x-dynamic"]], "s390x"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-sparc-dynamic"]], "sparc"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-win32.exe"]], "i386"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-win64.exe"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-x86_64-dynamic"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-aarch64.so"]], "aarch64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-armv7.so"]], "arm"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-i586.so"]], "i386"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-ppc64.so"]], "ppc64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-ppc64le.so"]], "ppc64le"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-riscv64.so"]], "riscv64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-s390x.so"]], "s390x"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-sparc.so"]], "sparc"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-win32.dll"]], "i386"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-win64.dll"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-x86_64.so"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/initrd-x86_64.img"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/initrd-x86_64.img.gz"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/bin-x86_64-dynamic.gz"]], "x86_64"), [];
+      InitISOFS, Always, TestResultString (
+        [["file_architecture"; "/lib-i586.so.xz"]], "i386"), [];
+    ];
+    shortdesc = "detect the architecture of a binary file";
+    longdesc = "\
+This detects the architecture of the binary F<filename>,
+and returns it if known.
+
+Currently defined architectures are:
+
+=over 4
+
+=item \"aarch64\"
+
+64 bit ARM.
+
+=item \"arm\"
+
+32 bit ARM.
+
+=item \"i386\"
+
+This string is returned for all 32 bit i386, i486, i586, i686 binaries
+irrespective of the precise processor requirements of the binary.
+
+=item \"ia64\"
+
+Intel Itanium.
+
+=item \"ppc\"
+
+32 bit Power PC.
+
+=item \"ppc64\"
+
+64 bit Power PC (big endian).
+
+=item \"ppc64le\"
+
+64 bit Power PC (little endian).
+
+=item \"riscv32\"
+
+=item \"riscv64\"
+
+=item \"riscv128\"
+
+RISC-V 32-, 64- or 128-bit variants.
+
+=item \"s390\"
+
+31 bit IBM S/390.
+
+=item \"s390x\"
+
+64 bit IBM S/390.
+
+=item \"sparc\"
+
+32 bit SPARC.
+
+=item \"sparc64\"
+
+64 bit SPARC V9 and above.
+
+=item \"x86_64\"
+
+64 bit x86-64.
+
+=back
+
+Libguestfs may return other architecture strings in future.
+
+The function works on at least the following types of files:
+
+=over 4
+
+=item *
+
+many types of Un*x and Linux binary
+
+=item *
+
+many types of Un*x and Linux shared library
+
+=item *
+
+Windows Win32 and Win64 binaries
+
+=item *
+
+Windows Win32 and Win64 DLLs
+
+Win32 binaries and DLLs return C<i386>.
+
+Win64 binaries and DLLs return C<x86_64>.
+
+=item *
+
+Linux kernel modules
+
+=item *
+
+Linux new-style initrd images
+
+=item *
+
+some non-x86 Linux vmlinuz kernels
+
+=back
+
+What it can't do currently:
+
+=over 4
+
+=item *
+
+static libraries (libfoo.a)
+
+=item *
+
+Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
+
+=item *
+
+x86 Linux vmlinuz kernels
+
+x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
+compressed code, and are horribly hard to unpack.  If you want to find
+the architecture of a kernel, use the architecture of the associated
+initrd or kernel module(s) instead.
+
+=back" };
+
 ]
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index c7619638a..1b0feae87 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -482,6 +482,7 @@ let proc_nr = [
 472, "yara_load";
 473, "yara_destroy";
 474, "internal_yara_scan";
+475, "file_architecture";
 ]
 
 (* End of list.  If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 5f3bb9813..7573eff88 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-474
+475
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b1cb39105..c7a9880eb 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -89,7 +89,6 @@ libguestfs_la_SOURCES = \
 	event-string.c \
 	events.c \
 	file.c \
-	filearch.c \
 	fuse.c \
 	guid.c \
 	handle.c \
@@ -160,7 +159,7 @@ libguestfs_la_LIBADD = \
 	../common/structs/libstructs.la \
 	../common/utils/libutils.la \
 	../common/cleanups/libcleanups.la \
-	$(PCRE_LIBS) $(MAGIC_LIBS) \
+	$(PCRE_LIBS) \
 	$(LIBVIRT_LIBS) $(LIBXML2_LIBS) \
 	$(SELINUX_LIBS) \
 	$(YAJL_LIBS) \
diff --git a/lib/filearch.c b/lib/filearch.c
deleted file mode 100644
index e1d3daeef..000000000
--- a/lib/filearch.c
+++ /dev/null
@@ -1,362 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <libintl.h>
-
-#include <magic.h>
-
-#include "ignore-value.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-#include "guestfs-internal-actions.h"
-
-# ifdef HAVE_ATTRIBUTE_CLEANUP
-# define CLEANUP_MAGIC_T_FREE __attribute__((cleanup(cleanup_magic_t_free)))
-
-static void
-cleanup_magic_t_free (void *ptr)
-{
-  magic_t m = *(magic_t *) ptr;
-
-  if (m)
-    magic_close (m);
-}
-
-# else
-# define CLEANUP_MAGIC_T_FREE
-# endif
-
-COMPILE_REGEXP (re_file_elf,
-                "ELF (\\d+)-bit (MSB|LSB).*(?:executable|shared object|relocatable), (.+?),", 0)
-COMPILE_REGEXP (re_elf_ppc64, ".*64.*PowerPC", 0)
-
-/* Convert output from 'file' command on ELF files to the canonical
- * architecture string.  Caller must free the result.
- */
-static char *
-canonical_elf_arch (guestfs_h *g,
-                    const char *bits, const char *endianness,
-                    const char *elf_arch)
-{
-  const char *r;
-  char *ret;
-
-  if (strstr (elf_arch, "Intel 80386") ||
-      strstr (elf_arch, "Intel 80486"))
-    r = "i386";
-  else if (strstr (elf_arch, "x86-64") ||
-           strstr (elf_arch, "AMD x86-64"))
-    r = "x86_64";
-  else if (strstr (elf_arch, "SPARC32"))
-    r = "sparc";
-  else if (strstr (elf_arch, "SPARC V9"))
-    r = "sparc64";
-  else if (strstr (elf_arch, "IA-64"))
-    r = "ia64";
-  else if (match (g, elf_arch, re_elf_ppc64)) {
-    if (strstr (endianness, "MSB"))
-      r = "ppc64";
-    else if (strstr (endianness, "LSB"))
-      r = "ppc64le";
-    else {
-      error (g, "file_architecture: unknown endianness '%s'", endianness);
-      return NULL;
-    }
-  }
-  else if (strstr (elf_arch, "PowerPC"))
-    r = "ppc";
-  else if (strstr (elf_arch, "ARM aarch64"))
-    r = "aarch64";
-  else if (strstr (elf_arch, "ARM"))
-    r = "arm";
-  else if (strstr (elf_arch, "UCB RISC-V")) {
-    ret = safe_asprintf (g, "riscv%s", bits);
-    goto no_strdup;
-  }
-  else if (strstr (elf_arch, "IBM S/390")) {
-    if (STREQ (bits, "32"))
-      r = "s390";
-    else if (STREQ (bits, "64"))
-      r = "s390x";
-    else {
-      error (g, "file_architecture: unknown S/390 bit size: %s", bits);
-      return NULL;
-    }
-  }
-  else
-    r = elf_arch;
-
-  ret = safe_strdup (g, r);
- no_strdup:
-  return ret;
-}
-
-static int
-is_regular_file (const char *filename)
-{
-  struct stat statbuf;
-
-  return lstat (filename, &statbuf) == 0 && S_ISREG (statbuf.st_mode);
-}
-
-static char *
-magic_for_file (guestfs_h *g, const char *filename, bool *loading_ok,
-                bool *matched)
-{
-  int flags;
-  CLEANUP_MAGIC_T_FREE magic_t m = NULL;
-  const char *line;
-  CLEANUP_FREE char *bits = NULL;
-  CLEANUP_FREE char *elf_arch = NULL;
-  CLEANUP_FREE char *endianness = NULL;
-
-  flags = g->verbose ? MAGIC_DEBUG : 0;
-  flags |= MAGIC_ERROR | MAGIC_RAW;
-
-  if (loading_ok)
-    *loading_ok = false;
-  if (matched)
-    *matched = false;
-
-  m = magic_open (flags);
-  if (m == NULL) {
-    perrorf (g, "magic_open");
-    return NULL;
-  }
-
-  if (magic_load (m, NULL) == -1) {
-    perrorf (g, "magic_load: default magic database file");
-    return NULL;
-  }
-
-  line = magic_file (m, filename);
-  if (line == NULL) {
-    perrorf (g, "magic_file: %s", filename);
-    return NULL;
-  }
-
-  if (loading_ok)
-    *loading_ok = true;
-
-  if (!match3 (g, line, re_file_elf, &bits, &endianness, &elf_arch)) {
-    error (g, "no re_file_elf match in '%s'", line);
-    return NULL;
-  }
-
-  if (matched)
-    *matched = true;
-
-  return canonical_elf_arch (g, bits, endianness, elf_arch);
-}
-
-/* Download and uncompress the cpio file to find binaries within. */
-static const char *initrd_binaries[] = {
-  "bin/ls",
-  "bin/rm",
-  "bin/modprobe",
-  "sbin/modprobe",
-  "bin/sh",
-  "bin/bash",
-  "bin/dash",
-  "bin/nash",
-  NULL
-};
-
-static char *
-cpio_arch (guestfs_h *g, const char *file, const char *path)
-{
-  CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL;
-  CLEANUP_FREE char *initrd = NULL;
-  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
-  char *ret = NULL;
-  const char *method;
-  int64_t size;
-  int r;
-  size_t i;
-
-  if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) {
-    perrorf (g, "asprintf");
-    return NULL;
-  }
-
-  if (strstr (file, "gzip"))
-    method = "zcat";
-  else if (strstr (file, "bzip2"))
-    method = "bzcat";
-  else
-    method = "cat";
-
-  /* Security: Refuse to download initrd if it is huge. */
-  size = guestfs_filesize (g, path);
-  if (size == -1 || size > 100000000) {
-    error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"),
-           path, size);
-    goto out;
-  }
-
-  if (mkdtemp (dir) == NULL) {
-    perrorf (g, "mkdtemp");
-    goto out;
-  }
-
-  initrd = safe_asprintf (g, "%s/initrd", dir);
-  if (guestfs_download (g, path, initrd) == -1)
-    goto out;
-
-  /* Construct a command to extract named binaries from the initrd file. */
-  guestfs_int_cmd_add_string_unquoted (cmd, "cd ");
-  guestfs_int_cmd_add_string_quoted   (cmd, dir);
-  guestfs_int_cmd_add_string_unquoted (cmd, " && ");
-  guestfs_int_cmd_add_string_unquoted (cmd, method);
-  guestfs_int_cmd_add_string_unquoted (cmd, " initrd | cpio --quiet -id");
-  for (i = 0; initrd_binaries[i] != NULL; ++i) {
-    guestfs_int_cmd_add_string_unquoted (cmd, " ");
-    guestfs_int_cmd_add_string_quoted (cmd, initrd_binaries[i]);
-  }
-
-  r = guestfs_int_cmd_run (cmd);
-  if (r == -1)
-    goto out;
-  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
-    guestfs_int_external_command_failed (g, r, "cpio", path);
-    goto out;
-  }
-
-  for (i = 0; initrd_binaries[i] != NULL; ++i) {
-    CLEANUP_FREE char *bin =
-      safe_asprintf (g, "%s/%s", dir, initrd_binaries[i]);
-
-    if (is_regular_file (bin)) {
-      bool loading_ok, matched;
-
-      ret = magic_for_file (g, bin, &loading_ok, &matched);
-      if (!loading_ok || matched)
-        goto out;
-    }
-  }
-  error (g, "file_architecture: could not determine architecture of cpio archive");
-
- out:
-  guestfs_int_recursive_remove_dir (g, dir);
-
-  return ret;
-}
-
-static char *
-compressed_file_arch (guestfs_h *g, const char *path, const char *method)
-{
-  CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL;
-  CLEANUP_FREE char *tempfile = NULL, *tempfile_extracted = NULL;
-  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
-  char *ret = NULL;
-  int64_t size;
-  int r;
-  bool matched;
-
-  if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) {
-    perrorf (g, "asprintf");
-    return NULL;
-  }
-
-  /* Security: Refuse to download file if it is huge. */
-  size = guestfs_filesize (g, path);
-  if (size == -1 || size > 10000000) {
-    error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"),
-           path, size);
-    goto out;
-  }
-
-  if (mkdtemp (dir) == NULL) {
-    perrorf (g, "mkdtemp");
-    goto out;
-  }
-
-  tempfile = safe_asprintf (g, "%s/file", dir);
-  if (guestfs_download (g, path, tempfile) == -1)
-    goto out;
-
-  tempfile_extracted = safe_asprintf (g, "%s/file_extracted", dir);
-
-  /* Construct a command to extract named binaries from the initrd file. */
-  guestfs_int_cmd_add_string_unquoted (cmd, method);
-  guestfs_int_cmd_add_string_unquoted (cmd, " ");
-  guestfs_int_cmd_add_string_quoted (cmd, tempfile);
-  guestfs_int_cmd_add_string_unquoted (cmd, " > ");
-  guestfs_int_cmd_add_string_quoted (cmd, tempfile_extracted);
-
-  r = guestfs_int_cmd_run (cmd);
-  if (r == -1)
-    goto out;
-  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
-    guestfs_int_external_command_failed (g, r, method, path);
-    goto out;
-  }
-
-  ret = magic_for_file (g, tempfile_extracted, NULL, &matched);
-  if (!matched)
-    error (g, "file_architecture: could not determine architecture of compressed file");
-
- out:
-  guestfs_int_recursive_remove_dir (g, dir);
-
-  return ret;
-}
-
-char *
-guestfs_impl_file_architecture (guestfs_h *g, const char *path)
-{
-  CLEANUP_FREE char *file = NULL;
-  CLEANUP_FREE char *bits = NULL;
-  CLEANUP_FREE char *elf_arch = NULL;
-  CLEANUP_FREE char *endianness = NULL;
-  char *ret = NULL;
-
-  /* Get the output of the "file" command.  Note that because this
-   * runs in the daemon, LANG=C so it's in English.
-   */
-  file = guestfs_file (g, path);
-  if (file == NULL)
-    return NULL;
-
-  if ((match3 (g, file, re_file_elf, &bits, &endianness, &elf_arch)) != 0)
-    ret = canonical_elf_arch (g, bits, endianness, elf_arch);
-  else if (strstr (file, "PE32 executable"))
-    ret = safe_strdup (g, "i386");
-  else if (strstr (file, "PE32+ executable"))
-    ret = safe_strdup (g, "x86_64");
-  else if (strstr (file, "cpio archive"))
-    ret = cpio_arch (g, file, path);
-  else if (strstr (file, "gzip compressed data"))
-    ret = compressed_file_arch (g, path, "zcat");
-  else if (strstr (file, "XZ compressed data"))
-    ret = compressed_file_arch (g, path, "xzcat");
-  else
-    error (g, "file_architecture: unknown architecture: %s", path);
-
-  return ret;                   /* caller frees */
-}
diff --git a/po/POTFILES b/po/POTFILES
index 0d8a924b6..1a38e8ed4 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -354,7 +354,6 @@ lib/errors.c
 lib/event-string.c
 lib/events.c
 lib/file.c
-lib/filearch.c
 lib/fuse.c
 lib/guid.c
 lib/handle.c
-- 
2.13.0




More information about the Libguestfs mailing list