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:
Richard W.M. Jones
2017-06-03 14:56:57 +01:00
parent 626a7fe80a
commit b48da89dd6
10 changed files with 353 additions and 555 deletions

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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" };
]

View File

@@ -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

View File

@@ -1 +1 @@
474
475

View File

@@ -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) \

View File

@@ -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 */
}

View File

@@ -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