mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
daemon: Reimplement ‘file_architecture’ API in OCaml.
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.
This commit is contained in:
@@ -247,6 +247,7 @@ SOURCES_MLI = \
|
||||
sysroot.mli \
|
||||
devsparts.mli \
|
||||
file.mli \
|
||||
filearch.mli \
|
||||
is.mli \
|
||||
link.mli \
|
||||
mount.mli \
|
||||
@@ -266,6 +267,7 @@ SOURCES_ML = \
|
||||
blkid.ml \
|
||||
devsparts.ml \
|
||||
file.ml \
|
||||
filearch.ml \
|
||||
is.ml \
|
||||
link.ml \
|
||||
mount.ml \
|
||||
|
||||
140
daemon/filearch.ml
Normal file
140
daemon/filearch.ml
Normal file
@@ -0,0 +1,140 @@
|
||||
(* 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 Unix_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.
|
||||
*)
|
||||
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 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 = Mkdtemp.temp_dir "filearch" in
|
||||
let finally () = ignore (Sys.command (sprintf "rm -rf %s" (quote tmpdir))) in
|
||||
|
||||
protect ~finally ~f:(
|
||||
fun () ->
|
||||
(* 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.sysroot_path 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
|
||||
)
|
||||
19
daemon/filearch.mli
Normal file
19
daemon/filearch.mli
Normal file
@@ -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
|
||||
@@ -300,7 +300,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
|
||||
|
||||
@@ -182,194 +182,6 @@ features from later versions into earlier versions,
|
||||
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")], [];
|
||||
@@ -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" };
|
||||
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
474
|
||||
475
|
||||
|
||||
@@ -89,7 +89,6 @@ libguestfs_la_SOURCES = \
|
||||
event-string.c \
|
||||
events.c \
|
||||
file.c \
|
||||
filearch.c \
|
||||
fuse.c \
|
||||
guid.c \
|
||||
handle.c \
|
||||
@@ -159,7 +158,7 @@ libguestfs_la_LIBADD = \
|
||||
../common/qemuopts/libqemuopts.la \
|
||||
../common/structs/libstructs.la \
|
||||
../common/utils/libutils.la \
|
||||
$(PCRE_LIBS) $(MAGIC_LIBS) \
|
||||
$(PCRE_LIBS) \
|
||||
$(LIBVIRT_LIBS) $(LIBXML2_LIBS) \
|
||||
$(SELINUX_LIBS) \
|
||||
$(YAJL_LIBS) \
|
||||
|
||||
362
lib/filearch.c
362
lib/filearch.c
@@ -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 */
|
||||
}
|
||||
@@ -363,7 +363,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
|
||||
|
||||
Reference in New Issue
Block a user