X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Finspect_fs_unix.c;h=3046e026fa9b2a58a3347bfc29b2a5612a6e0c4a;hp=cf1affa99c4b75dc081bc36df924f789c0e12472;hb=373dca7d12a8741e116205b4a3ac67a7d82cc782;hpb=3336b5448f12c9eb25bf7a0cecda1315c4cf07ea diff --git a/src/inspect_fs_unix.c b/src/inspect_fs_unix.c index cf1affa..3046e02 100644 --- a/src/inspect_fs_unix.c +++ b/src/inspect_fs_unix.c @@ -29,9 +29,7 @@ #include #include -#ifdef HAVE_PCRE #include -#endif #ifdef HAVE_HIVEX #include @@ -46,7 +44,7 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_PCRE) && defined(HAVE_HIVEX) +#if 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 @@ -57,11 +55,19 @@ static pcre *re_fedora; static pcre *re_rhel_old; static pcre *re_rhel; static pcre *re_rhel_no_minor; +static pcre *re_centos_old; +static pcre *re_centos; +static pcre *re_centos_no_minor; +static pcre *re_scientific_linux_old; +static pcre *re_scientific_linux; +static pcre *re_scientific_linux_no_minor; static pcre *re_major_minor; static pcre *re_aug_seq; static pcre *re_xdev; +static pcre *re_cciss; static pcre *re_first_partition; static pcre *re_freebsd; +static pcre *re_netbsd; static void compile_regexps (void) __attribute__((constructor)); static void free_regexps (void) __attribute__((destructor)); @@ -83,15 +89,29 @@ compile_regexps (void) COMPILE (re_fedora, "Fedora release (\\d+)", 0); COMPILE (re_rhel_old, - "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0); + "Red Hat.*release (\\d+).*Update (\\d+)", 0); COMPILE (re_rhel, - "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0); + "Red Hat.*release (\\d+)\\.(\\d+)", 0); COMPILE (re_rhel_no_minor, - "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+)", 0); + "Red Hat.*release (\\d+)", 0); + COMPILE (re_centos_old, + "CentOS.*release (\\d+).*Update (\\d+)", 0); + COMPILE (re_centos, + "CentOS.*release (\\d+)\\.(\\d+)", 0); + COMPILE (re_centos_no_minor, + "CentOS.*release (\\d+)", 0); + COMPILE (re_scientific_linux_old, + "Scientific Linux.*release (\\d+).*Update (\\d+)", 0); + COMPILE (re_scientific_linux, + "Scientific Linux.*release (\\d+)\\.(\\d+)", 0); + 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_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); + COMPILE (re_netbsd, "^NetBSD (\\d+)\\.(\\d+)", 0); } static void @@ -101,10 +121,18 @@ free_regexps (void) pcre_free (re_rhel_old); pcre_free (re_rhel); pcre_free (re_rhel_no_minor); + pcre_free (re_centos_old); + pcre_free (re_centos); + pcre_free (re_centos_no_minor); + pcre_free (re_scientific_linux_old); + 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); + pcre_free (re_netbsd); } static void check_architecture (guestfs_h *g, struct inspect_fs *fs); @@ -115,7 +143,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 @@ -192,6 +220,11 @@ parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) fs->distro = OS_DISTRO_MANDRIVA; r = 1; } + else if (fs->distro == 0 && + STREQ (lines[i], "DISTRIB_ID=\"Mageia\"")) { + fs->distro = OS_DISTRO_MAGEIA; + r = 1; + } else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) { char *major, *minor; if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) { @@ -285,6 +318,50 @@ guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs) return -1; fs->minor_version = 0; } + else if (match2 (g, fs->product_name, re_centos_old, &major, &minor) || + match2 (g, fs->product_name, re_centos, &major, &minor)) { + fs->distro = OS_DISTRO_CENTOS; + fs->major_version = guestfs___parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) { + free (minor); + return -1; + } + fs->minor_version = guestfs___parse_unsigned_int (g, minor); + free (minor); + if (fs->minor_version == -1) + return -1; + } + else if ((major = match1 (g, fs->product_name, re_centos_no_minor)) != NULL) { + fs->distro = OS_DISTRO_CENTOS; + fs->major_version = guestfs___parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) + return -1; + fs->minor_version = 0; + } + else if (match2 (g, fs->product_name, re_scientific_linux_old, &major, &minor) || + match2 (g, fs->product_name, re_scientific_linux, &major, &minor)) { + fs->distro = OS_DISTRO_SCIENTIFIC_LINUX; + fs->major_version = guestfs___parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) { + free (minor); + return -1; + } + fs->minor_version = guestfs___parse_unsigned_int (g, minor); + free (minor); + if (fs->minor_version == -1) + return -1; + } + else if ((major = match1 (g, fs->product_name, re_scientific_linux_no_minor)) != NULL) { + fs->distro = OS_DISTRO_SCIENTIFIC_LINUX; + fs->major_version = guestfs___parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) + return -1; + fs->minor_version = 0; + } } else if (guestfs_exists (g, "/etc/debian_version") > 0) { fs->distro = OS_DISTRO_DEBIAN; @@ -338,6 +415,26 @@ guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs) if (guestfs___parse_major_minor (g, fs) == -1) return -1; } + else if (guestfs_exists (g, "/etc/ttylinux-target") > 0) { + fs->distro = OS_DISTRO_TTYLINUX; + + fs->product_name = guestfs___first_line_of_file (g, "/etc/ttylinux-target"); + if (fs->product_name == NULL) + return -1; + + if (guestfs___parse_major_minor (g, fs) == -1) + return -1; + } + else if (guestfs_exists (g, "/etc/SuSE-release") > 0) { + fs->distro = OS_DISTRO_OPENSUSE; + + if (parse_release_file (g, fs, "/etc/SuSE-release") == -1) + return -1; + + if (guestfs___parse_major_minor (g, fs) == -1) + return -1; + } + skip_release_checks:; @@ -349,7 +446,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. */ @@ -382,7 +480,50 @@ 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. */ + if (check_hostname_unix (g, fs) == -1) + return -1; + + return 0; +} + +/* The currently mounted device is maybe to be a *BSD root. */ +int +guestfs___check_netbsd_root (guestfs_h *g, struct inspect_fs *fs) +{ + + if (guestfs_exists (g, "/etc/release") > 0) { + char *major, *minor; + if (parse_release_file (g, fs, "/etc/release") == -1) + return -1; + + if (match2 (g, fs->product_name, re_netbsd, &major, &minor)) { + fs->type = OS_TYPE_NETBSD; + fs->major_version = guestfs___parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) { + free (minor); + return -1; + } + fs->minor_version = guestfs___parse_unsigned_int (g, minor); + free (minor); + if (fs->minor_version == -1) + return -1; + } + } else { + return -1; + } + + /* Determine the architecture. */ + check_architecture (g, fs); + + /* We already know /etc/fstab exists because it's part of the test above. */ + const char *configfiles[] = { "/etc/fstab", NULL }; + if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1) return -1; /* Determine hostname. */ @@ -392,6 +533,7 @@ guestfs___check_freebsd_root (guestfs_h *g, struct inspect_fs *fs) return 0; } + static void check_architecture (guestfs_h *g, struct inspect_fs *fs) { @@ -444,13 +586,15 @@ 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; } break; case OS_TYPE_FREEBSD: + case OS_TYPE_NETBSD: /* /etc/rc.conf contains the hostname, but there is no Augeas lens * for this file. */ @@ -477,11 +621,18 @@ check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs) { char *hostname; + /* Errors here are not fatal (RHBZ#726739), since it could be + * just missing HOSTNAME field in the file. + */ + guestfs_error_handler_cb old_error_cb = g->error_cb; + g->error_cb = NULL; hostname = guestfs_aug_get (g, "/files/etc/sysconfig/network/HOSTNAME"); - if (!hostname) - return -1; + g->error_cb = old_error_cb; - fs->hostname = hostname; /* freed by guestfs___free_inspect_info */ + /* This is freed by guestfs___free_inspect_info. Note that hostname + * could be NULL because we ignored errors above. + */ + fs->hostname = hostname; return 0; } @@ -538,13 +689,11 @@ static int check_fstab (guestfs_h *g, struct inspect_fs *fs) { char **lines = guestfs_aug_ls (g, "/files/etc/fstab"); - if (lines == NULL) - return -1; + if (lines == NULL) goto error; if (lines[0] == NULL) { error (g, _("could not parse /etc/fstab or empty file")); - guestfs___free_string_list (lines); - return -1; + goto error; } size_t i; @@ -556,32 +705,29 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) 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) { - guestfs___free_string_list (lines); - return -1; - } + if (spec == NULL) goto error; snprintf (augpath, sizeof augpath, "%s/file", lines[i]); char *mp = guestfs_aug_get (g, augpath); if (mp == NULL) { - guestfs___free_string_list (lines); free (spec); - return -1; + goto error; } int r = add_fstab_entry (g, fs, spec, mp); free (spec); free (mp); - if (r == -1) { - guestfs___free_string_list (lines); - return -1; - } + if (r == -1) goto error; } } guestfs___free_string_list (lines); return 0; + +error: + if (lines) guestfs___free_string_list (lines); + return -1; } /* Add a filesystem and possibly a mountpoint entry for @@ -621,6 +767,9 @@ add_fstab_entry (guestfs_h *g, struct inspect_fs *fs, else if (STRPREFIX (spec, "LABEL=")) device = guestfs_findfs_label (g, &spec[6]); /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */ + else if (STREQ (spec, "/dev/root")) + /* Resolve /dev/root to the current device. */ + device = safe_strdup (g, fs->device); else if (STRPREFIX (spec, "/dev/")) /* Resolve guest block device names. */ device = resolve_fstab_device (g, spec); @@ -669,9 +818,8 @@ add_fstab_entry (guestfs_h *g, struct inspect_fs *fs, static char * resolve_fstab_device (guestfs_h *g, const char *spec) { - char *a1; char *device = NULL; - char *bsddisk, *bsdslice, *bsdpart; + char *type, *slice, *disk, *part; if (STRPREFIX (spec, "/dev/mapper/")) { /* LVM2 does some strange munging on /dev/mapper paths for VGs and @@ -686,48 +834,104 @@ resolve_fstab_device (guestfs_h *g, const char *spec) */ device = guestfs_lvm_canonical_lv_name (g, spec); } - else if ((a1 = match1 (g, spec, re_xdev)) != NULL) { + else if (match3 (g, spec, re_xdev, &type, &disk, &part)) { + /* type: (h|s|v|xv) + * disk: ([a-z]+) + * part: (\d*) */ + char **devices = guestfs_list_devices (g); + if (devices == NULL) + return NULL; + + /* Check any hints we were passed for a non-heuristic mapping */ + char *name = safe_asprintf (g, "%sd%s", type, disk); + size_t i = 0; + struct drive *drive = g->drives; + while (drive) { + if (drive->name && STREQ(drive->name, name)) { + device = safe_asprintf (g, "%s%s", devices[i], part); + break; + } + + i++; drive = drive->next; + } + free (name); + + /* Guess the appliance device name if we didn't find a matching hint */ + if (!device) { + /* Count how many disks the libguestfs appliance has */ + size_t count; + for (count = 0; devices[count] != NULL; count++) + ; + + /* Calculate the numerical index of the disk */ + i = disk[0] - 'a'; + for (char *p = disk + 1; *p != '\0'; p++) { + i += 1; i *= 26; + i += *p - 'a'; + } + + /* Check the index makes sense wrt the number of disks the appliance has. + * If it does, map it to an appliance disk. */ + if (i < count) { + device = safe_asprintf (g, "%s%s", devices[i], part); + } + } + + free (type); + free (disk); + free (part); + guestfs___free_string_list (devices); + } + else if (match2 (g, spec, re_cciss, &disk, &part)) { + /* disk: (cciss/c\d+d\d+) + * part: (\d+)? */ char **devices = guestfs_list_devices (g); if (devices == NULL) return NULL; - size_t count; - for (count = 0; devices[count] != NULL; count++) - ; + /* Check any hints we were passed for a non-heuristic mapping */ + size_t i = 0; + struct drive *drive = g->drives; + while (drive) { + if (drive->name && STREQ(drive->name, disk)) { + if (part) { + device = safe_asprintf (g, "%s%s", devices[i], part); + } else { + device = safe_strdup (g, devices[i]); + } + break; + } - size_t i = a1[0] - 'a'; /* a1[0] is always [a-z] because of regex. */ - if (i < count) { - size_t len = strlen (devices[i]) + strlen (a1) + 16; - device = safe_malloc (g, len); - snprintf (device, len, "%s%s", devices[i], &a1[1]); + i++; drive = drive->next; } - free (a1); + /* We don't try to guess mappings for cciss devices */ + + free (disk); + free (part); guestfs___free_string_list (devices); } - else if (match3 (g, spec, re_freebsd, &bsddisk, &bsdslice, &bsdpart)) { + else if (match3 (g, spec, re_freebsd, &disk, &slice, &part)) { /* FreeBSD disks are organized quite differently. See: * http://www.freebsd.org/doc/handbook/disk-organization.html * FreeBSD "partitions" are exposed as quasi-extended partitions * numbered from 5 in Linux. I have no idea what happens when you * have multiple "slices" (the FreeBSD term for MBR partitions). */ - int disk = guestfs___parse_unsigned_int (g, bsddisk); - int slice = guestfs___parse_unsigned_int (g, bsdslice); - int part = bsdpart[0] - 'a' /* counting from 0 */; - free (bsddisk); - free (bsdslice); - free (bsdpart); - - if (disk == -1 || disk > 26 || - slice <= 0 || slice > 1 /* > 4 .. see comment above */ || - part < 0 || part >= 26) - goto out; - - device = safe_asprintf (g, "/dev/sd%c%d", disk + 'a', part + 5); + int disk_i = guestfs___parse_unsigned_int (g, disk); + int slice_i = guestfs___parse_unsigned_int (g, slice); + int part_i = part[0] - 'a' /* counting from 0 */; + free (disk); + free (slice); + free (part); + + if (disk_i != -1 && disk_i <= 26 && + slice_i > 0 && slice_i <= 1 /* > 4 .. see comment above */ && + part_i >= 0 && part_i < 26) { + device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a', part_i + 5); + } } - out: /* Didn't match device pattern, return original spec unchanged. */ if (device == NULL) device = safe_strdup (g, spec); @@ -741,18 +945,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. @@ -765,11 +974,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; @@ -782,4 +1022,4 @@ inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename, return r; } -#endif /* defined(HAVE_PCRE) && defined(HAVE_HIVEX) */ +#endif /* defined(HAVE_HIVEX) */