diff options
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | configure.ac | 45 | ||||
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/filearch.c | 267 | ||||
-rw-r--r-- | src/guestfs-internal.h | 16 | ||||
-rw-r--r-- | src/guestfs.c | 9 | ||||
-rw-r--r-- | src/inspect.c | 607 | ||||
-rw-r--r-- | src/listfs.c | 169 |
9 files changed, 674 insertions, 451 deletions
@@ -47,9 +47,9 @@ Requirements - XDR, rpcgen (on Linux these are provided by glibc) -- pcre (Perl Compatible Regular Expressions C library) +- pcre (Perl Compatible Regular Expressions C library) (optional) -- libmagic (the library that corresponds to the 'file' command) +- libmagic (the library that corresponds to the 'file' command) (optional) - libvirt diff --git a/configure.ac b/configure.ac index 4458d498..e7761c99 100644 --- a/configure.ac +++ b/configure.ac @@ -188,15 +188,6 @@ AC_ARG_ENABLE([appliance], AM_CONDITIONAL([ENABLE_APPLIANCE],[test "x$enable_appliance" = "xyes"]) AC_MSG_RESULT([$enable_appliance]) -dnl Check for PCRE. -AC_CHECK_LIB([pcre],[pcre_compile], - [AC_SUBST([LIBPCRE], ["-lpcre"])], - [AC_MSG_FAILURE( - [Perl Compatible Regular Expressions library (PCRE) is required])]) -AC_CHECK_HEADER([pcre.h],[], - [AC_MSG_FAILURE( - [Perl Compatible Regular Expressions library (PCRE) header file pcre.h is required])]) - dnl Check for rpcgen and XDR library. rpcgen is optional. AC_CHECK_PROG([RPCGEN],[rpcgen],[rpcgen],[no]) AM_CONDITIONAL([HAVE_RPCGEN],[test "x$RPCGEN" != "xno"]) @@ -445,33 +436,43 @@ dnl For i18n. AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.17]) -dnl libmagic (required) -AC_CHECK_LIB([magic],[magic_file],[ - AC_SUBST([LIBMAGIC], ["-lmagic"]) - ],[ - AC_MSG_FAILURE([libmagic is required]) - ]) -AC_CHECK_HEADER([magic.h],[],[ - AC_MSG_FAILURE([magic.h header file is required]) - ]) +dnl libmagic (highly recommended) +AC_CHECK_LIB([magic],[magic_file], + [AC_CHECK_HEADER([magic.h], + [AC_SUBST([MAGIC_LIBS], ["-lmagic"]) + AC_DEFINE([HAVE_LIBMAGIC],[1],[libmagic found at compile time.]) + ], []) + ], + [AC_MSG_WARN([libmagic not found, some core features will be disabled])]) dnl libvirt (required) PKG_CHECK_MODULES([LIBVIRT], [libvirt]) AC_SUBST([LIBVIRT_CFLAGS]) AC_SUBST([LIBVIRT_LIBS]) +dnl Check for PCRE (highly recommended) +PKG_CHECK_MODULES([PCRE], [libpcre], + [AC_SUBST([PCRE_CFLAGS]) + AC_SUBST([PCRE_LIBS]) + AC_DEFINE([HAVE_PCRE],[1],[PCRE found at compile time.]) + ], + [AC_MSG_WARN([PCRE not found, some core features will be disabled])]) + dnl libxml2 (required) PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) AC_SUBST([LIBXML2_CFLAGS]) AC_SUBST([LIBXML2_LIBS]) -dnl hivex library (required) +dnl hivex library (highly recommended) dnl This used to be a part of libguestfs, but was spun off into its dnl own separate upstream project in libguestfs 1.0.85. -PKG_CHECK_MODULES([HIVEX], [hivex]) -AC_SUBST([HIVEX_CFLAGS]) -AC_SUBST([HIVEX_LIBS]) +PKG_CHECK_MODULES([HIVEX], [hivex], + [AC_SUBST([HIVEX_CFLAGS]) + AC_SUBST([HIVEX_LIBS]) + AC_DEFINE([HAVE_HIVEX],[1],[hivex library found at compile time.]) + ], + [AC_MSG_WARN([hivex not found, some core features will be disabled])]) dnl FUSE is optional to build the FUSE module. AC_ARG_ENABLE([fuse], diff --git a/po/POTFILES.in b/po/POTFILES.in index 20132810..e8d95873 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -125,9 +125,11 @@ src/appliance.c src/bindtests.c src/errnostring.c src/errnostring_gperf.c +src/filearch.c src/guestfs.c src/inspect.c src/launch.c +src/listfs.c src/proto.c test-tool/helper.c test-tool/test-tool.c diff --git a/src/Makefile.am b/src/Makefile.am index f23464e6..4be61a59 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -128,12 +128,14 @@ libguestfs_la_SOURCES = \ actions.c \ appliance.c \ bindtests.c \ + filearch.c \ inspect.c \ launch.c \ + listfs.c \ proto.c \ libguestfs.syms -libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(LIBPCRE) $(LIBMAGIC) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la +libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la # Make libguestfs include the convenience libraries. noinst_LTLIBRARIES = liberrnostring.la libprotocol.la @@ -141,7 +143,7 @@ libguestfs_la_LIBADD += liberrnostring.la libprotocol.la libguestfs_la_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ - $(HIVEX_CFLAGS) $(AUGEAS_CFLAGS) \ + $(HIVEX_CFLAGS) $(AUGEAS_CFLAGS) $(PCRE_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) libguestfs_la_CPPFLAGS = -I$(top_srcdir)/gnulib/lib diff --git a/src/filearch.c b/src/filearch.c new file mode 100644 index 00000000..35a2ceb1 --- /dev/null +++ b/src/filearch.c @@ -0,0 +1,267 @@ +/* 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 <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> + +#ifdef HAVE_PCRE +#include <pcre.h> +#endif +#ifdef HAVE_LIBMAGIC +#include <magic.h> +#endif + +#include "ignore-value.h" + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" +#include "guestfs_protocol.h" + +#if defined(HAVE_PCRE) && defined(HAVE_LIBMAGIC) + +static pcre *re_file_elf; +static pcre *re_elf_ppc64; + +static void compile_regexps (void) __attribute__((constructor)); +static void free_regexps (void) __attribute__((destructor)); + +static void +compile_regexps (void) +{ + const char *err; + int offset; + +#define COMPILE(re,pattern,options) \ + do { \ + re = pcre_compile ((pattern), (options), &err, &offset, NULL); \ + if (re == NULL) { \ + ignore_value (write (2, err, strlen (err))); \ + abort (); \ + } \ + } while (0) + + COMPILE (re_file_elf, + "ELF.*(?:executable|shared object|relocatable), (.+?),", 0); + COMPILE (re_elf_ppc64, "64.*PowerPC", 0); +} + +static void +free_regexps (void) +{ + pcre_free (re_file_elf); + pcre_free (re_elf_ppc64); +} + +/* 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 *elf_arch) +{ + const char *r; + + if (strstr (elf_arch, "Intel 80386")) + r = "i386"; + else if (strstr (elf_arch, "Intel 80486")) + r = "i486"; + else if (strstr (elf_arch, "x86-64")) + r = "x86_64"; + else if (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)) + r = "ppc64"; + else if (strstr (elf_arch, "PowerPC")) + r = "ppc"; + else + r = elf_arch; + + char *ret = safe_strdup (g, r); + return ret; +} + +static int +is_regular_file (const char *filename) +{ + struct stat statbuf; + + return lstat (filename, &statbuf) == 0 && S_ISREG (statbuf.st_mode); +} + +/* Download and uncompress the cpio file to find binaries within. + * Notes: + * (1) Two lists must be identical. + * (2) Implicit limit of 31 bytes for length of each element (see code + * below). + */ +#define INITRD_BINARIES1 "bin/ls bin/rm bin/modprobe sbin/modprobe bin/sh bin/bash bin/dash bin/nash" +#define INITRD_BINARIES2 {"bin/ls", "bin/rm", "bin/modprobe", "sbin/modprobe", "bin/sh", "bin/bash", "bin/dash", "bin/nash"} + +static char * +cpio_arch (guestfs_h *g, const char *file, const char *path) +{ + TMP_TEMPLATE_ON_STACK (dir); +#define dir_len (strlen (dir)) +#define initrd_len (dir_len + 16) + char initrd[initrd_len]; +#define cmd_len (dir_len + 256) + char cmd[cmd_len]; +#define bin_len (dir_len + 32) + char bin[bin_len]; + + char *ret = NULL; + + const char *method; + if (strstr (file, "gzip")) + method = "zcat"; + else if (strstr (file, "bzip2")) + method = "bzcat"; + else + method = "cat"; + + if (mkdtemp (dir) == NULL) { + perrorf (g, "mkdtemp"); + goto out; + } + + snprintf (initrd, initrd_len, "%s/initrd", dir); + if (guestfs_download (g, path, initrd) == -1) + goto out; + + snprintf (cmd, cmd_len, + "cd %s && %s initrd | cpio --quiet -id " INITRD_BINARIES1, + dir, method); + int r = system (cmd); + if (r == -1 || WEXITSTATUS (r) != 0) { + perrorf (g, "cpio command failed"); + goto out; + } + + const char *bins[] = INITRD_BINARIES2; + size_t i; + for (i = 0; i < sizeof bins / sizeof bins[0]; ++i) { + snprintf (bin, bin_len, "%s/%s", dir, bins[i]); + + if (is_regular_file (bin)) { + int flags = g->verbose ? MAGIC_DEBUG : 0; + flags |= MAGIC_ERROR | MAGIC_RAW; + + magic_t m = magic_open (flags); + if (m == NULL) { + perrorf (g, "magic_open"); + goto out; + } + + if (magic_load (m, NULL) == -1) { + perrorf (g, "magic_load: default magic database file"); + magic_close (m); + goto out; + } + + const char *line = magic_file (m, bin); + if (line == NULL) { + perrorf (g, "magic_file: %s", bin); + magic_close (m); + goto out; + } + + char *elf_arch; + if ((elf_arch = match1 (g, line, re_file_elf)) != NULL) { + ret = canonical_elf_arch (g, elf_arch); + free (elf_arch); + magic_close (m); + goto out; + } + magic_close (m); + } + } + error (g, "file_architecture: could not determine architecture of cpio archive"); + + out: + /* Free up the temporary directory. Note the directory name cannot + * contain shell meta-characters because of the way it was + * constructed above. + */ + snprintf (cmd, cmd_len, "rm -rf %s", dir); + ignore_value (system (cmd)); + + return ret; +#undef dir_len +#undef initrd_len +#undef cmd_len +#undef bin_len +} + +char * +guestfs__file_architecture (guestfs_h *g, const char *path) +{ + char *file = NULL; + char *elf_arch = 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 ((elf_arch = match1 (g, file, re_file_elf)) != NULL) + ret = canonical_elf_arch (g, 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 + error (g, "file_architecture: unknown architecture: %s", path); + + free (file); + free (elf_arch); + return ret; /* caller frees */ +} + +#else /* no PCRE or libmagic at compile time */ + +/* XXX Should be an optgroup. */ + +#define NOT_IMPL(r) \ + error (g, _("file-architecture API not available since this version of libguestfs was compiled without PCRE or libmagic libraries")); \ + return r + +char * +guestfs__file_architecture (guestfs_h *g, const char *path) +{ + NOT_IMPL(NULL); +} + +#endif /* no PCRE or libmagic at compile time */ diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 9c7c96cc..067251c2 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -19,6 +19,10 @@ #ifndef GUESTFS_INTERNAL_H_ #define GUESTFS_INTERNAL_H_ +#ifdef HAVE_PCRE +#include <pcre.h> +#endif + #define STREQ(a,b) (strcmp((a),(b)) == 0) #define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) #define STRNEQ(a,b) (strcmp((a),(b)) != 0) @@ -223,6 +227,13 @@ extern int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void ** extern int guestfs___accept_from_daemon (guestfs_h *g); extern int guestfs___build_appliance (guestfs_h *g, char **kernel, char **initrd, char **appliance); extern void guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size); +#ifdef HAVE_PCRE +extern int guestfs___match (guestfs_h *g, const char *str, const pcre *re); +extern char *guestfs___match1 (guestfs_h *g, const char *str, const pcre *re); +extern int guestfs___match2 (guestfs_h *g, const char *str, const pcre *re, char **ret1, char **ret2); +#endif +extern int guestfs___feature_available (guestfs_h *g, const char *feature); +extern void guestfs___free_string_list (char **); #define error(g,...) guestfs_error_errno((g),0,__VA_ARGS__) #define perrorf guestfs_perrorf @@ -232,5 +243,10 @@ extern void guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_siz #define safe_strdup guestfs_safe_strdup #define safe_strndup guestfs_safe_strndup #define safe_memdup guestfs_safe_memdup +#ifdef HAVE_PCRE +#define match guestfs___match +#define match1 guestfs___match1 +#define match2 guestfs___match2 +#endif #endif /* GUESTFS_INTERNAL_H_ */ diff --git a/src/guestfs.c b/src/guestfs.c index e4f74e0e..b1512268 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -774,3 +774,12 @@ guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size) fprintf (out, _("<truncated, original size %zu bytes>"), orig_size); } + +void +guestfs___free_string_list (char **argv) +{ + size_t i; + for (i = 0; argv[i] != NULL; ++i) + free (argv[i]); + free (argv); +} diff --git a/src/inspect.c b/src/inspect.c index 531e3f76..2b61a757 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -26,9 +26,13 @@ #include <string.h> #include <sys/stat.h> +#ifdef HAVE_PCRE #include <pcre.h> -#include <magic.h> +#endif + +#ifdef HAVE_HIVEX #include <hivex.h> +#endif #include "c-ctype.h" #include "ignore-value.h" @@ -39,13 +43,13 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" +#if defined(HAVE_PCRE) && defined(HAVE_HIVEX) + /* Compile all the regular expressions once when the shared library is * loaded. PCRE is thread safe so we're supposedly OK here if * multiple threads call into the libguestfs API functions below * simultaneously. */ -static pcre *re_file_elf; -static pcre *re_elf_ppc64; static pcre *re_fedora; static pcre *re_rhel_old; static pcre *re_rhel; @@ -73,9 +77,6 @@ compile_regexps (void) } \ } while (0) - COMPILE (re_file_elf, - "ELF.*(?:executable|shared object|relocatable), (.+?),", 0); - COMPILE (re_elf_ppc64, "64.*PowerPC", 0); COMPILE (re_fedora, "Fedora release (\\d+)", 0); COMPILE (re_rhel_old, "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0); @@ -92,8 +93,6 @@ compile_regexps (void) static void free_regexps (void) { - pcre_free (re_file_elf); - pcre_free (re_elf_ppc64); pcre_free (re_fedora); pcre_free (re_rhel_old); pcre_free (re_rhel); @@ -104,252 +103,7 @@ free_regexps (void) pcre_free (re_windows_version); } -/* Match a regular expression which contains no captures. Returns - * true if it matches or false if it doesn't. - */ -static int -match (guestfs_h *g, const char *str, const pcre *re) -{ - size_t len = strlen (str); - int vec[30], r; - - r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); - if (r == PCRE_ERROR_NOMATCH) - return 0; - if (r != 1) { - /* Internal error -- should not happen. */ - fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", - __FILE__, __func__, r, str); - return 0; - } - - return 1; -} - -/* Match a regular expression which contains exactly one capture. If - * the string matches, return the capture, otherwise return NULL. The - * caller must free the result. - */ -static char * -match1 (guestfs_h *g, const char *str, const pcre *re) -{ - size_t len = strlen (str); - int vec[30], r; - - r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); - if (r == PCRE_ERROR_NOMATCH) - return NULL; - if (r != 2) { - /* Internal error -- should not happen. */ - fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", - __FILE__, __func__, r, str); - return NULL; - } - - return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); -} - -/* Match a regular expression which contains exactly two captures. */ -static int -match2 (guestfs_h *g, const char *str, const pcre *re, char **ret1, char **ret2) -{ - size_t len = strlen (str); - int vec[30], r; - - r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30); - if (r == PCRE_ERROR_NOMATCH) - return 0; - if (r != 3) { - /* Internal error -- should not happen. */ - fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", - __FILE__, __func__, r, str); - return 0; - } - - *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); - *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]); - - return 1; -} - -/* 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 *elf_arch) -{ - const char *r; - - if (strstr (elf_arch, "Intel 80386")) - r = "i386"; - else if (strstr (elf_arch, "Intel 80486")) - r = "i486"; - else if (strstr (elf_arch, "x86-64")) - r = "x86_64"; - else if (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)) - r = "ppc64"; - else if (strstr (elf_arch, "PowerPC")) - r = "ppc"; - else - r = elf_arch; - - char *ret = safe_strdup (g, r); - return ret; -} - -static int -is_regular_file (const char *filename) -{ - struct stat statbuf; - - return lstat (filename, &statbuf) == 0 && S_ISREG (statbuf.st_mode); -} - -/* Download and uncompress the cpio file to find binaries within. - * Notes: - * (1) Two lists must be identical. - * (2) Implicit limit of 31 bytes for length of each element (see code - * below). - */ -#define INITRD_BINARIES1 "bin/ls bin/rm bin/modprobe sbin/modprobe bin/sh bin/bash bin/dash bin/nash" -#define INITRD_BINARIES2 {"bin/ls", "bin/rm", "bin/modprobe", "sbin/modprobe", "bin/sh", "bin/bash", "bin/dash", "bin/nash"} - -static char * -cpio_arch (guestfs_h *g, const char *file, const char *path) -{ - TMP_TEMPLATE_ON_STACK (dir); -#define dir_len (strlen (dir)) -#define initrd_len (dir_len + 16) - char initrd[initrd_len]; -#define cmd_len (dir_len + 256) - char cmd[cmd_len]; -#define bin_len (dir_len + 32) - char bin[bin_len]; - - char *ret = NULL; - - const char *method; - if (strstr (file, "gzip")) - method = "zcat"; - else if (strstr (file, "bzip2")) - method = "bzcat"; - else - method = "cat"; - - if (mkdtemp (dir) == NULL) { - perrorf (g, "mkdtemp"); - goto out; - } - - snprintf (initrd, initrd_len, "%s/initrd", dir); - if (guestfs_download (g, path, initrd) == -1) - goto out; - - snprintf (cmd, cmd_len, - "cd %s && %s initrd | cpio --quiet -id " INITRD_BINARIES1, - dir, method); - int r = system (cmd); - if (r == -1 || WEXITSTATUS (r) != 0) { - perrorf (g, "cpio command failed"); - goto out; - } - - const char *bins[] = INITRD_BINARIES2; - size_t i; - for (i = 0; i < sizeof bins / sizeof bins[0]; ++i) { - snprintf (bin, bin_len, "%s/%s", dir, bins[i]); - - if (is_regular_file (bin)) { - int flags = g->verbose ? MAGIC_DEBUG : 0; - flags |= MAGIC_ERROR | MAGIC_RAW; - - magic_t m = magic_open (flags); - if (m == NULL) { - perrorf (g, "magic_open"); - goto out; - } - - if (magic_load (m, NULL) == -1) { - perrorf (g, "magic_load: default magic database file"); - magic_close (m); - goto out; - } - - const char *line = magic_file (m, bin); - if (line == NULL) { - perrorf (g, "magic_file: %s", bin); - magic_close (m); - goto out; - } - - char *elf_arch; - if ((elf_arch = match1 (g, line, re_file_elf)) != NULL) { - ret = canonical_elf_arch (g, elf_arch); - free (elf_arch); - magic_close (m); - goto out; - } - magic_close (m); - } - } - error (g, "file_architecture: could not determine architecture of cpio archive"); - - out: - /* Free up the temporary directory. Note the directory name cannot - * contain shell meta-characters because of the way it was - * constructed above. - */ - snprintf (cmd, cmd_len, "rm -rf %s", dir); - ignore_value (system (cmd)); - - return ret; -#undef dir_len -#undef initrd_len -#undef cmd_len -#undef bin_len -} - -char * -guestfs__file_architecture (guestfs_h *g, const char *path) -{ - char *file = NULL; - char *elf_arch = 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 ((elf_arch = match1 (g, file, re_file_elf)) != NULL) - ret = canonical_elf_arch (g, 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 - error (g, "file_architecture: unknown architecture: %s", path); - - free (file); - free (elf_arch); - return ret; /* caller frees */ -} - /* The main inspection code. */ -static int feature_available (guestfs_h *g, const char *feature); -static void free_string_list (char **); static int check_for_filesystem_on (guestfs_h *g, const char *device); char ** @@ -374,12 +128,12 @@ guestfs__inspect_os (guestfs_h *g) size_t i; for (i = 0; devices[i] != NULL; ++i) { if (check_for_filesystem_on (g, devices[i]) == -1) { - free_string_list (devices); + guestfs___free_string_list (devices); guestfs___free_inspect_info (g); return NULL; } } - free_string_list (devices); + guestfs___free_string_list (devices); /* Look at all partitions. */ char **partitions; @@ -391,15 +145,15 @@ guestfs__inspect_os (guestfs_h *g) for (i = 0; partitions[i] != NULL; ++i) { if (check_for_filesystem_on (g, partitions[i]) == -1) { - free_string_list (partitions); + guestfs___free_string_list (partitions); guestfs___free_inspect_info (g); return NULL; } } - free_string_list (partitions); + guestfs___free_string_list (partitions); /* Look at all LVs. */ - if (feature_available (g, "lvm2")) { + if (guestfs___feature_available (g, "lvm2")) { char **lvs; lvs = guestfs_lvs (g); if (lvs == NULL) { @@ -409,12 +163,12 @@ guestfs__inspect_os (guestfs_h *g) for (i = 0; lvs[i] != NULL; ++i) { if (check_for_filesystem_on (g, lvs[i]) == -1) { - free_string_list (lvs); + guestfs___free_string_list (lvs); guestfs___free_inspect_info (g); return NULL; } } - free_string_list (lvs); + guestfs___free_string_list (lvs); } /* At this point we have, in the handle, a list of all filesystems @@ -428,54 +182,6 @@ guestfs__inspect_os (guestfs_h *g) return ret; } -void -guestfs___free_inspect_info (guestfs_h *g) -{ - size_t i; - for (i = 0; i < g->nr_fses; ++i) { - free (g->fses[i].device); - free (g->fses[i].product_name); - free (g->fses[i].arch); - free (g->fses[i].windows_systemroot); - size_t j; - for (j = 0; j < g->fses[i].nr_fstab; ++j) { - free (g->fses[i].fstab[j].device); - free (g->fses[i].fstab[j].mountpoint); - } - free (g->fses[i].fstab); - } - free (g->fses); - g->nr_fses = 0; - g->fses = NULL; -} - -static void -free_string_list (char **argv) -{ - size_t i; - for (i = 0; argv[i] != NULL; ++i) - free (argv[i]); - free (argv); -} - -/* In the Perl code this is a public function. */ -static int -feature_available (guestfs_h *g, const char *feature) -{ - /* If there's an error we should ignore it, so to do that we have to - * temporarily replace the error handler with a null one. - */ - guestfs_error_handler_cb old_error_cb = g->error_cb; - g->error_cb = NULL; - - const char *groups[] = { feature, NULL }; - int r = guestfs_available (g, (char * const *) groups); - - g->error_cb = old_error_cb; - - return r == 0 ? 1 : 0; -} - /* Find out if 'device' contains a filesystem. If it does, add * another entry in g->fses. */ @@ -612,7 +318,7 @@ parse_release_file (guestfs_h *g, struct inspect_fs *fs, return -1; if (product_name[0] == NULL) { error (g, "%s: file is empty", release_filename); - free_string_list (product_name); + guestfs___free_string_list (product_name); return -1; } @@ -677,13 +383,13 @@ parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) free (major); if (fs->major_version == -1) { free (minor); - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } fs->minor_version = parse_unsigned_int (g, minor); free (minor); if (fs->minor_version == -1) { - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } } @@ -703,7 +409,7 @@ parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) } } - free_string_list (lines); + guestfs___free_string_list (lines); return r; } @@ -862,7 +568,7 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) if (lines[0] == NULL) { error (g, "could not parse /etc/fstab or empty file"); - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } @@ -876,14 +582,14 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) snprintf (augpath, sizeof augpath, "%s/spec", lines[i]); char *spec = guestfs_aug_get (g, augpath); if (spec == NULL) { - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } snprintf (augpath, sizeof augpath, "%s/file", lines[i]); char *mp = guestfs_aug_get (g, augpath); if (mp == NULL) { - free_string_list (lines); + guestfs___free_string_list (lines); free (spec); return -1; } @@ -893,13 +599,13 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) free (mp); if (r == -1) { - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } } } - free_string_list (lines); + guestfs___free_string_list (lines); return 0; } @@ -1022,7 +728,7 @@ resolve_fstab_device (guestfs_h *g, const char *spec) } free (a1); - free_string_list (devices); + guestfs___free_string_list (devices); } else { /* Didn't match device pattern, return original spec unchanged. */ @@ -1488,139 +1194,190 @@ guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) return ret; } -/* List filesystems. - * - * The current implementation just uses guestfs_vfs_type and doesn't - * try mounting anything, but we reserve the right in future to try - * mounting filesystems. - */ +#else /* no PCRE or hivex at compile time */ + +/* XXX These functions should be in an optgroup. */ -static void remove_from_list (char **list, const char *item); -static void check_with_vfs_type (guestfs_h *g, const char *dev, char ***ret, size_t *ret_size); +#define NOT_IMPL(r) \ + error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \ + return r char ** -guestfs__list_filesystems (guestfs_h *g) +guestfs__inspect_os (guestfs_h *g) { - size_t i; - char **ret; - size_t ret_size; + NOT_IMPL(NULL); +} - ret = safe_malloc (g, sizeof (char *)); - ret[0] = NULL; - ret_size = 0; +char ** +guestfs__inspect_get_roots (guestfs_h *g) +{ + NOT_IMPL(NULL); +} - /* Look to see if any devices directly contain filesystems - * (RHBZ#590167). However vfs-type will fail to tell us anything - * useful about devices which just contain partitions, so we also - * get the list of partitions and exclude the corresponding devices - * by using part-to-dev. - */ - char **devices; - devices = guestfs_list_devices (g); - if (devices == NULL) { - free_string_list (ret); - return NULL; - } - char **partitions; - partitions = guestfs_list_partitions (g); - if (partitions == NULL) { - free_string_list (devices); - free_string_list (ret); - return NULL; - } +char * +guestfs__inspect_get_type (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - for (i = 0; partitions[i] != NULL; ++i) { - char *dev = guestfs_part_to_dev (g, partitions[i]); - if (dev) - remove_from_list (devices, dev); - free (dev); - } +char * +guestfs__inspect_get_arch (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - /* Use vfs-type to check for filesystems on devices. */ - for (i = 0; devices[i] != NULL; ++i) - check_with_vfs_type (g, devices[i], &ret, &ret_size); - free_string_list (devices); +char * +guestfs__inspect_get_distro (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - /* Use vfs-type to check for filesystems on partitions. */ - for (i = 0; partitions[i] != NULL; ++i) - check_with_vfs_type (g, partitions[i], &ret, &ret_size); - free_string_list (partitions); +int +guestfs__inspect_get_major_version (guestfs_h *g, const char *root) +{ + NOT_IMPL(-1); +} - if (feature_available (g, "lvm2")) { - /* Use vfs-type to check for filesystems on LVs. */ - char **lvs; - lvs = guestfs_lvs (g); - if (lvs == NULL) { - free_string_list (ret); - return NULL; - } +int +guestfs__inspect_get_minor_version (guestfs_h *g, const char *root) +{ + NOT_IMPL(-1); +} - for (i = 0; lvs[i] != NULL; ++i) - check_with_vfs_type (g, lvs[i], &ret, &ret_size); - free_string_list (lvs); - } +char * +guestfs__inspect_get_product_name (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - return ret; +char * +guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); } -/* If 'item' occurs in 'list', remove and free it. */ -static void -remove_from_list (char **list, const char *item) +char ** +guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) { - size_t i; + NOT_IMPL(NULL); +} - for (i = 0; list[i] != NULL; ++i) - if (STREQ (list[i], item)) { - free (list[i]); - for (; list[i+1] != NULL; ++i) - list[i] = list[i+1]; - list[i] = NULL; - return; - } +char ** +guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); } -/* Use vfs-type to look for a filesystem of some sort on 'dev'. - * Apart from some types which we ignore, add the result to the - * 'ret' string list. - */ -static void -check_with_vfs_type (guestfs_h *g, const char *device, - char ***ret, size_t *ret_size) +#endif /* no PCRE or hivex at compile time */ + +void +guestfs___free_inspect_info (guestfs_h *g) { - char *v; + size_t i; + for (i = 0; i < g->nr_fses; ++i) { + free (g->fses[i].device); + free (g->fses[i].product_name); + free (g->fses[i].arch); + free (g->fses[i].windows_systemroot); + size_t j; + for (j = 0; j < g->fses[i].nr_fstab; ++j) { + free (g->fses[i].fstab[j].device); + free (g->fses[i].fstab[j].mountpoint); + } + free (g->fses[i].fstab); + } + free (g->fses); + g->nr_fses = 0; + g->fses = NULL; +} +/* In the Perl code this is a public function. */ +int +guestfs___feature_available (guestfs_h *g, const char *feature) +{ + /* If there's an error we should ignore it, so to do that we have to + * temporarily replace the error handler with a null one. + */ guestfs_error_handler_cb old_error_cb = g->error_cb; g->error_cb = NULL; - char *vfs_type = guestfs_vfs_type (g, device); + + const char *groups[] = { feature, NULL }; + int r = guestfs_available (g, (char * const *) groups); + g->error_cb = old_error_cb; - if (!vfs_type) - v = safe_strdup (g, "unknown"); - else { - /* Ignore all "*_member" strings. In libblkid these are returned - * for things which are members of some RAID or LVM set, most - * importantly "LVM2_member" which is a PV. - */ - size_t n = strlen (vfs_type); - if (n >= 7 && STREQ (&vfs_type[n-7], "_member")) { - free (vfs_type); - return; - } + return r == 0 ? 1 : 0; +} - /* Ignore LUKS-encrypted partitions. These are also containers. */ - if (STREQ (vfs_type, "crypto_LUKS")) { - free (vfs_type); - return; - } +#ifdef HAVE_PCRE + +/* Match a regular expression which contains no captures. Returns + * true if it matches or false if it doesn't. + */ +int +guestfs___match (guestfs_h *g, const char *str, const pcre *re) +{ + size_t len = strlen (str); + int vec[30], r; + + r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); + if (r == PCRE_ERROR_NOMATCH) + return 0; + if (r != 1) { + /* Internal error -- should not happen. */ + fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", + __FILE__, __func__, r, str); + return 0; + } + + return 1; +} + +/* Match a regular expression which contains exactly one capture. If + * the string matches, return the capture, otherwise return NULL. The + * caller must free the result. + */ +char * +guestfs___match1 (guestfs_h *g, const char *str, const pcre *re) +{ + size_t len = strlen (str); + int vec[30], r; - v = vfs_type; + r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); + if (r == PCRE_ERROR_NOMATCH) + return NULL; + if (r != 2) { + /* Internal error -- should not happen. */ + fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", + __FILE__, __func__, r, str); + return NULL; } - /* Extend the return array. */ - size_t i = *ret_size; - *ret_size += 2; - *ret = safe_realloc (g, *ret, (*ret_size + 1) * sizeof (char *)); - (*ret)[i] = safe_strdup (g, device); - (*ret)[i+1] = v; - (*ret)[i+2] = NULL; + return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); } + +/* Match a regular expression which contains exactly two captures. */ +int +guestfs___match2 (guestfs_h *g, const char *str, const pcre *re, + char **ret1, char **ret2) +{ + size_t len = strlen (str); + int vec[30], r; + + r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30); + if (r == PCRE_ERROR_NOMATCH) + return 0; + if (r != 3) { + /* Internal error -- should not happen. */ + fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", + __FILE__, __func__, r, str); + return 0; + } + + *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); + *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]); + + return 1; +} + +#endif /* HAVE_PCRE */ diff --git a/src/listfs.c b/src/listfs.c new file mode 100644 index 00000000..a89cd9bd --- /dev/null +++ b/src/listfs.c @@ -0,0 +1,169 @@ +/* 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 <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" +#include "guestfs_protocol.h" + +/* List filesystems. + * + * The current implementation just uses guestfs_vfs_type and doesn't + * try mounting anything, but we reserve the right in future to try + * mounting filesystems. + */ + +static void remove_from_list (char **list, const char *item); +static void check_with_vfs_type (guestfs_h *g, const char *dev, char ***ret, size_t *ret_size); + +char ** +guestfs__list_filesystems (guestfs_h *g) +{ + size_t i; + char **ret; + size_t ret_size; + + ret = safe_malloc (g, sizeof (char *)); + ret[0] = NULL; + ret_size = 0; + + /* Look to see if any devices directly contain filesystems + * (RHBZ#590167). However vfs-type will fail to tell us anything + * useful about devices which just contain partitions, so we also + * get the list of partitions and exclude the corresponding devices + * by using part-to-dev. + */ + char **devices; + devices = guestfs_list_devices (g); + if (devices == NULL) { + guestfs___free_string_list (ret); + return NULL; + } + char **partitions; + partitions = guestfs_list_partitions (g); + if (partitions == NULL) { + guestfs___free_string_list (devices); + guestfs___free_string_list (ret); + return NULL; + } + + for (i = 0; partitions[i] != NULL; ++i) { + char *dev = guestfs_part_to_dev (g, partitions[i]); + if (dev) + remove_from_list (devices, dev); + free (dev); + } + + /* Use vfs-type to check for filesystems on devices. */ + for (i = 0; devices[i] != NULL; ++i) + check_with_vfs_type (g, devices[i], &ret, &ret_size); + guestfs___free_string_list (devices); + + /* Use vfs-type to check for filesystems on partitions. */ + for (i = 0; partitions[i] != NULL; ++i) + check_with_vfs_type (g, partitions[i], &ret, &ret_size); + guestfs___free_string_list (partitions); + + if (guestfs___feature_available (g, "lvm2")) { + /* Use vfs-type to check for filesystems on LVs. */ + char **lvs; + lvs = guestfs_lvs (g); + if (lvs == NULL) { + guestfs___free_string_list (ret); + return NULL; + } + + for (i = 0; lvs[i] != NULL; ++i) + check_with_vfs_type (g, lvs[i], &ret, &ret_size); + guestfs___free_string_list (lvs); + } + + return ret; +} + +/* If 'item' occurs in 'list', remove and free it. */ +static void +remove_from_list (char **list, const char *item) +{ + size_t i; + + for (i = 0; list[i] != NULL; ++i) + if (STREQ (list[i], item)) { + free (list[i]); + for (; list[i+1] != NULL; ++i) + list[i] = list[i+1]; + list[i] = NULL; + return; + } +} + +/* Use vfs-type to look for a filesystem of some sort on 'dev'. + * Apart from some types which we ignore, add the result to the + * 'ret' string list. + */ +static void +check_with_vfs_type (guestfs_h *g, const char *device, + char ***ret, size_t *ret_size) +{ + char *v; + + guestfs_error_handler_cb old_error_cb = g->error_cb; + g->error_cb = NULL; + char *vfs_type = guestfs_vfs_type (g, device); + g->error_cb = old_error_cb; + + if (!vfs_type) + v = safe_strdup (g, "unknown"); + else { + /* Ignore all "*_member" strings. In libblkid these are returned + * for things which are members of some RAID or LVM set, most + * importantly "LVM2_member" which is a PV. + */ + size_t n = strlen (vfs_type); + if (n >= 7 && STREQ (&vfs_type[n-7], "_member")) { + free (vfs_type); + return; + } + + /* Ignore LUKS-encrypted partitions. These are also containers. */ + if (STREQ (vfs_type, "crypto_LUKS")) { + free (vfs_type); + return; + } + + v = vfs_type; + } + + /* Extend the return array. */ + size_t i = *ret_size; + *ret_size += 2; + *ret = safe_realloc (g, *ret, (*ret_size + 1) * sizeof (char *)); + (*ret)[i] = safe_strdup (g, device); + (*ret)[i+1] = v; + (*ret)[i+2] = NULL; +} |