inspection: Cleanup iteration over fstab entries in inspect_fs_unix.c
[libguestfs.git] / src / inspect_fs_unix.c
index 51dfa67..b68dde1 100644 (file)
@@ -107,7 +107,6 @@ compile_regexps (void)
   COMPILE (re_scientific_linux_no_minor,
            "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);
   COMPILE (re_cciss, "^/dev/(cciss/c\\d+d\\d+)(?:p(\\d+))?$", 0);
   COMPILE (re_freebsd, "^/dev/ad(\\d+)s(\\d+)([a-z])$", 0);
@@ -128,7 +127,6 @@ free_regexps (void)
   pcre_free (re_scientific_linux);
   pcre_free (re_scientific_linux_no_minor);
   pcre_free (re_major_minor);
-  pcre_free (re_aug_seq);
   pcre_free (re_xdev);
   pcre_free (re_cciss);
   pcre_free (re_freebsd);
@@ -143,7 +141,7 @@ static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
                             const char *spec, const char *mp);
 static char *resolve_fstab_device (guestfs_h *g, const char *spec);
-static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename, int (*f) (guestfs_h *, struct inspect_fs *));
+static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char **configfiles, int (*f) (guestfs_h *, struct inspect_fs *));
 
 /* Set fs->product_name to the first line of the release file. */
 static int
@@ -446,7 +444,8 @@ guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs)
    * which filesystems are used by the operating system and how they
    * are mounted.
    */
-  if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
+  const char *configfiles[] = { "/etc/fstab", NULL };
+  if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
     return -1;
 
   /* Determine hostname. */
@@ -479,7 +478,8 @@ guestfs___check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
   check_architecture (g, fs);
 
   /* We already know /etc/fstab exists because it's part of the test above. */
-  if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
+  const char *configfiles[] = { "/etc/fstab", NULL };
+  if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
     return -1;
 
   /* Determine hostname. */
@@ -520,7 +520,8 @@ guestfs___check_netbsd_root (guestfs_h *g, struct inspect_fs *fs)
   check_architecture (g, fs);
 
   /* We already know /etc/fstab exists because it's part of the test above. */
-  if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
+  const char *configfiles[] = { "/etc/fstab", NULL };
+  if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
     return -1;
 
   /* Determine hostname. */
@@ -530,6 +531,41 @@ guestfs___check_netbsd_root (guestfs_h *g, struct inspect_fs *fs)
   return 0;
 }
 
+/* The currently mounted device may be a Hurd root.  Hurd has distros
+ * just like Linux.
+ */
+int
+guestfs___check_hurd_root (guestfs_h *g, struct inspect_fs *fs)
+{
+  int r;
+
+  fs->type = OS_TYPE_HURD;
+
+  if (guestfs_exists (g, "/etc/debian_version") > 0) {
+    fs->distro = OS_DISTRO_DEBIAN;
+
+    if (parse_release_file (g, fs, "/etc/debian_version") == -1)
+      return -1;
+
+    if (guestfs___parse_major_minor (g, fs) == -1)
+      return -1;
+  }
+
+  /* Arch Hurd also exists, but inconveniently it doesn't have
+   * the normal /etc/arch-release file.  XXX
+   */
+
+  /* Determine the architecture. */
+  check_architecture (g, fs);
+
+  /* XXX Check for /etc/fstab. */
+
+  /* Determine hostname. */
+  if (check_hostname_unix (g, fs) == -1)
+    return -1;
+
+  return 0;
+}
 
 static void
 check_architecture (guestfs_h *g, struct inspect_fs *fs)
@@ -566,6 +602,7 @@ check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
 {
   switch (fs->type) {
   case OS_TYPE_LINUX:
+  case OS_TYPE_HURD:
     /* Red Hat-derived would be in /etc/sysconfig/network, and
      * Debian-derived in the file /etc/hostname.  Very old Debian and
      * SUSE use /etc/HOSTNAME.  It's best to just look for each of
@@ -583,7 +620,8 @@ check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
         return -1;
     }
     else if (guestfs_is_file (g, "/etc/sysconfig/network")) {
-      if (inspect_with_augeas (g, fs, "/etc/sysconfig/network",
+      const char *configfiles[] = { "/etc/sysconfig/network", NULL };
+      if (inspect_with_augeas (g, fs, configfiles,
                                check_hostname_redhat) == -1)
         return -1;
     }
@@ -684,45 +722,43 @@ check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs)
 static int
 check_fstab (guestfs_h *g, struct inspect_fs *fs)
 {
-  char **lines = guestfs_aug_ls (g, "/files/etc/fstab");
-  if (lines == NULL) goto error;
+  char **entries, **entry;
+  char augpath[256];
+  char *spec, *mp;
+  int r;
 
-  if (lines[0] == NULL) {
+  entries = guestfs_aug_match (g, "/files/etc/fstab/*[label() != '#comment']");
+  if (entries == NULL) goto error;
+
+  if (entries[0] == NULL) {
     error (g, _("could not parse /etc/fstab or empty file"));
     goto error;
   }
 
-  size_t i;
-  char augpath[256];
-  for (i = 0; lines[i] != NULL; ++i) {
-    /* Ignore comments.  Only care about sequence lines which
-     * match m{/\d+$}.
-     */
-    if (match (g, lines[i], re_aug_seq)) {
-      snprintf (augpath, sizeof augpath, "%s/spec", lines[i]);
-      char *spec = guestfs_aug_get (g, augpath);
-      if (spec == NULL) goto error;
-
-      snprintf (augpath, sizeof augpath, "%s/file", lines[i]);
-      char *mp = guestfs_aug_get (g, augpath);
-      if (mp == NULL) {
-        free (spec);
-        goto error;
-      }
+  for (entry = entries; *entry != NULL; entry++) {
+    snprintf (augpath, sizeof augpath, "%s/spec", *entry);
+    spec = guestfs_aug_get (g, augpath);
+    if (spec == NULL) goto error;
 
-      int r = add_fstab_entry (g, fs, spec, mp);
+    snprintf (augpath, sizeof augpath, "%s/file", *entry);
+    mp = guestfs_aug_get (g, augpath);
+    if (mp == NULL) {
       free (spec);
-      free (mp);
-
-      if (r == -1) goto error;
+      goto error;
     }
+
+    r = add_fstab_entry (g, fs, spec, mp);
+    free (spec);
+    free (mp);
+
+    if (r == -1) goto error;
   }
 
-  guestfs___free_string_list (lines);
+  guestfs___free_string_list (entries);
   return 0;
 
 error:
-  if (lines) guestfs___free_string_list (lines);
+  if (entries) guestfs___free_string_list (entries);
   return -1;
 }
 
@@ -941,18 +977,23 @@ resolve_fstab_device (guestfs_h *g, const char *spec)
  * Augeas is closed.
  */
 static int
-inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
+inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs,
+                     const char **configfiles,
                      int (*f) (guestfs_h *, struct inspect_fs *))
 {
-  /* Security: Refuse to do this if filename is too large. */
-  int64_t size = guestfs_filesize (g, filename);
-  if (size == -1)
-    /* guestfs_filesize failed and has already set error in handle */
-    return -1;
-  if (size > MAX_AUGEAS_FILE_SIZE) {
-    error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
-           filename, size);
-    return -1;
+  /* Security: Refuse to do this if a config file is too large. */
+  for (const char **i = configfiles; *i != NULL; i++) {
+    if (guestfs_exists(g, *i) == 0) continue;
+
+    int64_t size = guestfs_filesize (g, *i);
+    if (size == -1)
+      /* guestfs_filesize failed and has already set error in handle */
+      return -1;
+    if (size > MAX_AUGEAS_FILE_SIZE) {
+      error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
+             *i, size);
+      return -1;
+    }
   }
 
   /* If !feature_available (g, "augeas") then the next call will fail.
@@ -965,11 +1006,42 @@ inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
   int r = -1;
 
   /* Tell Augeas to only load one file (thanks RaphaĆ«l Pinson). */
-  char buf[strlen (filename) + 64];
-  snprintf (buf, strlen (filename) + 64, "/augeas/load//incl[. != \"%s\"]",
-            filename);
-  if (guestfs_aug_rm (g, buf) == -1)
+#define AUGEAS_LOAD "/augeas/load//incl[. != \""
+#define AUGEAS_LOAD_LEN (strlen(AUGEAS_LOAD))
+  size_t conflen = strlen(configfiles[0]);
+  size_t buflen = AUGEAS_LOAD_LEN + conflen + 1 /* Closing " */;
+  char *buf = safe_malloc(g, buflen + 2 /* Closing ] + null terminator */);
+
+  memcpy(buf, AUGEAS_LOAD, AUGEAS_LOAD_LEN);
+  memcpy(buf + AUGEAS_LOAD_LEN, configfiles[0], conflen);
+  buf[buflen - 1] = '"';
+#undef AUGEAS_LOAD_LEN
+#undef AUGEAS_LOAD
+
+#define EXCL " and . != \""
+#define EXCL_LEN (strlen(EXCL))
+  for (const char **i = &configfiles[1]; *i != NULL; i++) {
+    size_t orig_buflen = buflen;
+    conflen = strlen(*i);
+    buflen += EXCL_LEN + conflen + 1 /* Closing " */;
+    buf = safe_realloc(g, buf, buflen + 2 /* Closing ] + null terminator */);
+    char *s = buf + orig_buflen;
+
+    memcpy(s, EXCL, EXCL_LEN);
+    memcpy(s + EXCL_LEN, *i, conflen);
+    buf[buflen - 1] = '"';
+  }
+#undef EXCL_LEN
+#undef EXCL
+
+  buf[buflen] = ']';
+  buf[buflen + 1] = '\0';
+
+  if (guestfs_aug_rm (g, buf) == -1) {
+    free(buf);
     goto out;
+  }
+  free(buf);
 
   if (guestfs_aug_load (g) == -1)
     goto out;