Coverity: Don't close fd_cwd if fd_cwd == -1.
[libguestfs.git] / src / inspect.c
index e42dca8..e7408b6 100644 (file)
@@ -1,5 +1,5 @@
 /* libguestfs
- * Copyright (C) 2010 Red Hat Inc.
+ * Copyright (C) 2010-2011 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
 
 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
 
+/* Some limits on what we will read, for safety. */
+
+/* Small text configuration files.
+ *
+ * The upper limit is for general files that we grep or download.  The
+ * largest such file is probably "txtsetup.sif" from Windows CDs
+ * (~500K).  This number has to be larger than any legitimate file and
+ * smaller than the protocol message size.
+ *
+ * The lower limit is for files parsed by Augeas on the daemon side,
+ * where Augeas is running in reduced memory and can potentially
+ * create a lot of metadata so we really need to be careful about
+ * those.
+ */
+#define MAX_SMALL_FILE_SIZE    (2 * 1000 * 1000)
+#define MAX_AUGEAS_FILE_SIZE        (100 * 1000)
+
+/* Maximum Windows Registry hive that we will download to /tmp.  Some
+ * registries can be legitimately very large.
+ */
+#define MAX_REGISTRY_SIZE    (100 * 1000 * 1000)
+
+/* Maximum RPM or dpkg database we will download to /tmp. */
+#define MAX_PKG_DB_SIZE       (10 * 1000 * 1000)
+
 /* 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
@@ -81,11 +106,11 @@ compile_regexps (void)
 
   COMPILE (re_fedora, "Fedora release (\\d+)", 0);
   COMPILE (re_rhel_old,
-           "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0);
+           "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0);
   COMPILE (re_rhel,
-           "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0);
+           "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0);
   COMPILE (re_rhel_no_minor,
-           "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)", 0);
+           "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+)", 0);
   COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
   COMPILE (re_aug_seq, "/\\d+$", 0);
   COMPILE (re_xdev, "^/dev/(?:h|s|v|xv)d([a-z]\\d*)$", 0);
@@ -203,7 +228,9 @@ static int check_windows_root (guestfs_h *g, struct inspect_fs *fs);
 static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
 static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs);
 static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs);
-static char *resolve_windows_path_silently (guestfs_h *g, const char *);
+static char *case_sensitive_path_silently (guestfs_h *g, const char *);
+static int is_file_nocase (guestfs_h *g, const char *);
+static int is_dir_nocase (guestfs_h *g, const char *);
 static int extend_fses (guestfs_h *g);
 static int parse_unsigned_int (guestfs_h *g, const char *str);
 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
@@ -326,23 +353,30 @@ check_filesystem (guestfs_h *g, const char *device)
            guestfs_is_dir (g, "/run") > 0 &&
            guestfs_is_dir (g, "/spool") > 0)
     fs->content = FS_CONTENT_LINUX_VAR;
-  /* Windows root? */
-  else if (guestfs_is_file (g, "/AUTOEXEC.BAT") > 0 ||
-           guestfs_is_file (g, "/autoexec.bat") > 0 ||
-           guestfs_is_dir (g, "/Program Files") > 0 ||
-           guestfs_is_dir (g, "/WINDOWS") > 0 ||
-           guestfs_is_dir (g, "/Windows") > 0 ||
-           guestfs_is_dir (g, "/windows") > 0 ||
-           guestfs_is_dir (g, "/WIN32") > 0 ||
-           guestfs_is_dir (g, "/Win32") > 0 ||
-           guestfs_is_dir (g, "/WINNT") > 0 ||
-           guestfs_is_file (g, "/boot.ini") > 0 ||
-           guestfs_is_file (g, "/ntldr") > 0) {
+  /* Windows root?
+   * Note that if a Windows guest has multiple disks and applications
+   * are installed on those other disks, then those other disks will
+   * contain "/Program Files" and "/System Volume Information".  Those
+   * would *not* be Windows root disks.  (RHBZ#674130)
+   */
+  else if (is_file_nocase (g, "/AUTOEXEC.BAT") > 0 ||
+           is_dir_nocase (g, "/WINDOWS/SYSTEM32") > 0 ||
+           is_dir_nocase (g, "/WIN32/SYSTEM32") > 0 ||
+           is_dir_nocase (g, "/WINNT/SYSTEM32") > 0 ||
+           is_file_nocase (g, "/boot.ini") > 0 ||
+           is_file_nocase (g, "/ntldr") > 0) {
     fs->is_root = 1;
     fs->content = FS_CONTENT_WINDOWS_ROOT;
     if (check_windows_root (g, fs) == -1)
       return -1;
   }
+  /* Windows volume with installed applications (but not root)? */
+  else if (is_dir_nocase (g, "/System Volume Information") > 0 &&
+           is_dir_nocase (g, "/Program Files") > 0)
+    fs->content = FS_CONTENT_WINDOWS_VOLUME_WITH_APPS;
+  /* Windows volume (but not root)? */
+  else if (is_dir_nocase (g, "/System Volume Information") > 0)
+    fs->content = FS_CONTENT_WINDOWS_VOLUME;
 
   return 0;
 }
@@ -417,7 +451,7 @@ parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
   if (size == -1)
     /* guestfs_filesize failed and has already set error in handle */
     return -1;
-  if (size > 1000000) {
+  if (size > MAX_SMALL_FILE_SIZE) {
     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
            filename, size);
     return -1;
@@ -609,8 +643,6 @@ check_linux_root (guestfs_h *g, struct inspect_fs *fs)
 static int
 check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
 {
-  int r;
-
   fs->type = OS_TYPE_FREEBSD;
 
   /* FreeBSD has no authoritative version file.  The version number is
@@ -673,8 +705,6 @@ check_architecture (guestfs_h *g, struct inspect_fs *fs)
 static int
 check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
 {
-  char **lines;
-
   switch (fs->type) {
   case OS_TYPE_LINUX:
     /* Red Hat-derived would be in /etc/sysconfig/network, and
@@ -757,7 +787,7 @@ check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs)
   if (size == -1)
     /* guestfs_filesize failed and has already set error in handle */
     return -1;
-  if (size > 1000000) {
+  if (size > MAX_SMALL_FILE_SIZE) {
     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
            filename, size);
     return -1;
@@ -1004,7 +1034,7 @@ check_windows_root (guestfs_h *g, struct inspect_fs *fs)
   for (i = 0;
        systemroot == NULL && i < sizeof systemroots / sizeof systemroots[0];
        ++i) {
-    systemroot = resolve_windows_path_silently (g, systemroots[i]);
+    systemroot = case_sensitive_path_silently (g, systemroots[i]);
   }
 
   if (!systemroot) {
@@ -1042,7 +1072,7 @@ check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
   char cmd_exe[len];
   snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
 
-  char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe);
+  char *cmd_exe_path = case_sensitive_path_silently (g, cmd_exe);
   if (!cmd_exe_path)
     return 0;
 
@@ -1069,7 +1099,7 @@ check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
   snprintf (software, len, "%s/system32/config/software",
             fs->windows_systemroot);
 
-  char *software_path = resolve_windows_path_silently (g, software);
+  char *software_path = case_sensitive_path_silently (g, software);
   if (!software_path)
     /* If the software hive doesn't exist, just accept that we cannot
      * find product_name etc.
@@ -1080,7 +1110,8 @@ check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
   hive_h *h = NULL;
   hive_value_h *values = NULL;
 
-  if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
+  if (download_to_tmp (g, software_path, software_local,
+                       MAX_REGISTRY_SIZE) == -1)
     goto out;
 
   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
@@ -1177,7 +1208,7 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
   snprintf (system, len, "%s/system32/config/system",
             fs->windows_systemroot);
 
-  char *system_path = resolve_windows_path_silently (g, system);
+  char *system_path = case_sensitive_path_silently (g, system);
   if (!system_path)
     /* If the system hive doesn't exist, just accept that we cannot
      * find hostname etc.
@@ -1188,7 +1219,7 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
   hive_h *h = NULL;
   hive_value_h *values = NULL;
 
-  if (download_to_tmp (g, system_path, system_local, 100000000) == -1)
+  if (download_to_tmp (g, system_path, system_local, MAX_REGISTRY_SIZE) == -1)
     goto out;
 
   h = hivex_open (system_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
@@ -1252,7 +1283,7 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
 }
 
 static char *
-resolve_windows_path_silently (guestfs_h *g, const char *path)
+case_sensitive_path_silently (guestfs_h *g, const char *path)
 {
   guestfs_error_handler_cb old_error_cb = g->error_cb;
   g->error_cb = NULL;
@@ -1262,6 +1293,34 @@ resolve_windows_path_silently (guestfs_h *g, const char *path)
 }
 
 static int
+is_file_nocase (guestfs_h *g, const char *path)
+{
+  char *p;
+  int r;
+
+  p = case_sensitive_path_silently (g, path);
+  if (!p)
+    return 0;
+  r = guestfs_is_file (g, p);
+  free (p);
+  return r > 0;
+}
+
+static int
+is_dir_nocase (guestfs_h *g, const char *path)
+{
+  char *p;
+  int r;
+
+  p = case_sensitive_path_silently (g, path);
+  if (!p)
+    return 0;
+  r = guestfs_is_dir (g, p);
+  free (p);
+  return r > 0;
+}
+
+static int
 extend_fses (guestfs_h *g)
 {
   size_t n = g->nr_fses + 1;
@@ -1661,7 +1720,9 @@ guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
   return safe_strdup (g, fs->hostname ? : "unknown");
 }
 
+#ifdef DB_DUMP
 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
+#endif
 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
 static void add_application (guestfs_h *g, struct guestfs_application_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *install_path, const char *publisher, const char *url, const char *description);
@@ -1683,9 +1744,11 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root)
   case OS_TYPE_LINUX:
     switch (fs->package_format) {
     case OS_PACKAGE_FORMAT_RPM:
+#ifdef DB_DUMP
       ret = list_applications_rpm (g, fs);
       if (ret == NULL)
         return NULL;
+#endif
       break;
 
     case OS_PACKAGE_FORMAT_DEB:
@@ -1729,12 +1792,13 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root)
   return ret;
 }
 
+#ifdef DB_DUMP
 static struct guestfs_application_list *
 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
 {
   TMP_TEMPLATE_ON_STACK (tmpfile);
 
-  if (download_to_tmp (g, "/var/lib/rpm/Name", tmpfile, 10000000) == -1)
+  if (download_to_tmp (g, "/var/lib/rpm/Name", tmpfile, MAX_PKG_DB_SIZE) == -1)
     return NULL;
 
   struct guestfs_application_list *apps = NULL, *ret = NULL;
@@ -1744,7 +1808,7 @@ list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
   char line[1024];
   size_t len;
 
-  snprintf (cmd, cmd_len, "db_dump -p '%s'", tmpfile);
+  snprintf (cmd, cmd_len, DB_DUMP " -p '%s'", tmpfile);
 
   if (g->verbose)
     fprintf (stderr, "list_applications_rpm: %s\n", cmd);
@@ -1829,13 +1893,15 @@ list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
 
   return ret;
 }
+#endif /* defined DB_DUMP */
 
 static struct guestfs_application_list *
 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
 {
   TMP_TEMPLATE_ON_STACK (tmpfile);
 
-  if (download_to_tmp (g, "/var/lib/dpkg/status", tmpfile, 10000000) == -1)
+  if (download_to_tmp (g, "/var/lib/dpkg/status", tmpfile,
+                       MAX_PKG_DB_SIZE) == -1)
     return NULL;
 
   struct guestfs_application_list *apps = NULL, *ret = NULL;
@@ -1936,18 +2002,19 @@ list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
   snprintf (software, len, "%s/system32/config/software",
             fs->windows_systemroot);
 
-  char *software_path = resolve_windows_path_silently (g, software);
-  if (!software_path)
-    /* If the software hive doesn't exist, just accept that we cannot
-     * find product_name etc.
-     */
-    return 0;
+  char *software_path = case_sensitive_path_silently (g, software);
+  if (!software_path) {
+    /* Missing software hive is a problem. */
+    error (g, "no HKLM\\SOFTWARE hive found in the guest");
+    return NULL;
+  }
 
   struct guestfs_application_list *apps = NULL, *ret = NULL;
   hive_h *h = NULL;
   hive_node_h *children = NULL;
 
-  if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
+  if (download_to_tmp (g, software_path, software_local,
+                       MAX_REGISTRY_SIZE) == -1)
     goto out;
 
   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
@@ -2166,7 +2233,7 @@ inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
   if (size == -1)
     /* guestfs_filesize failed and has already set error in handle */
     return -1;
-  if (size > 100000) {
+  if (size > MAX_AUGEAS_FILE_SIZE) {
     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
            filename, size);
     return -1;
@@ -2216,7 +2283,7 @@ first_line_of_file (guestfs_h *g, const char *filename)
   if (size == -1)
     /* guestfs_filesize failed and has already set error in handle */
     return NULL;
-  if (size > 1000000) {
+  if (size > MAX_SMALL_FILE_SIZE) {
     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
            filename, size);
     return NULL;
@@ -2336,6 +2403,30 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root)
   NOT_IMPL(NULL);
 }
 
+char *
+guestfs__inspect_get_format (guestfs_h *g, const char *root)
+{
+  NOT_IMPL(NULL);
+}
+
+int
+guestfs__inspect_is_live (guestfs_h *g, const char *root)
+{
+  NOT_IMPL(-1);
+}
+
+int
+guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
+{
+  NOT_IMPL(-1);
+}
+
+int
+guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
+{
+  NOT_IMPL(-1);
+}
+
 #endif /* no PCRE or hivex at compile time */
 
 void