/* Parse generic MAJOR.MINOR from the fs->product_name string. */ int guestfs___parse_major_minor (guestfs_h *g, struct inspect_fs *fs) { char *major, *minor; if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) { 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; } 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_is_file_opts (g, "/etc/release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 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. */ if (check_hostname_unix (g, fs) == -1) return -1; return 0; }
/* At the moment, pull just the ProductName and version numbers from * the registry. In future there is a case for making many more * registry fields available to callers. */ static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) { int ret = -1; int r; size_t len = strlen (fs->windows_systemroot) + 64; char software[len]; snprintf (software, len, "%s/system32/config/software", fs->windows_systemroot); CLEANUP_FREE char *software_path = guestfs_case_sensitive_path (g, software); if (!software_path) return -1; r = guestfs_is_file (g, software_path); if (r == -1) return -1; /* If the software hive doesn't exist, just accept that we cannot * find product_name etc. */ if (r == 0) return 0; int64_t node; const char *hivepath[] = { "Microsoft", "Windows NT", "CurrentVersion" }; size_t i; CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL; if (guestfs_hivex_open (g, software_path, GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) return -1; node = guestfs_hivex_root (g); for (i = 0; node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) node = guestfs_hivex_node_get_child (g, node, hivepath[i]); if (node == -1) goto out; if (node == 0) { perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); goto out; } values = guestfs_hivex_node_values (g, node); for (i = 0; i < values->len; ++i) { int64_t value = values->val[i].hivex_value_h; CLEANUP_FREE char *key = guestfs_hivex_value_key (g, value); if (key == NULL) goto out; if (STRCASEEQ (key, "ProductName")) { fs->product_name = guestfs_hivex_value_utf8 (g, value); if (!fs->product_name) goto out; } else if (STRCASEEQ (key, "CurrentVersion")) { CLEANUP_FREE char *version = guestfs_hivex_value_utf8 (g, value); if (!version) goto out; char *major, *minor; if (match2 (g, version, re_windows_version, &major, &minor)) { fs->major_version = guestfs___parse_unsigned_int (g, major); free (major); if (fs->major_version == -1) { free (minor); goto out; } fs->minor_version = guestfs___parse_unsigned_int (g, minor); free (minor); if (fs->minor_version == -1) goto out; } } else if (STRCASEEQ (key, "InstallationType")) { fs->product_variant = guestfs_hivex_value_utf8 (g, value); if (!fs->product_variant) goto out; } } ret = 0; out: guestfs_hivex_close (g); return ret; }
/* The currently mounted device is known to be a Linux root. Try to * determine from this the distro, version, etc. Also parse * /etc/fstab to determine the arrangement of mountpoints and * associated devices. */ int guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs) { int r; fs->type = OS_TYPE_LINUX; if (guestfs_is_file_opts (g, "/etc/lsb-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { r = parse_lsb_release (g, fs); if (r == -1) /* error */ return -1; if (r == 1) /* ok - detected the release from this file */ goto skip_release_checks; } if (guestfs_is_file_opts (g, "/etc/redhat-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */ if (parse_release_file (g, fs, "/etc/redhat-release") == -1) return -1; char *major, *minor; if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) { fs->distro = OS_DISTRO_FEDORA; fs->major_version = guestfs___parse_unsigned_int (g, major); free (major); if (fs->major_version == -1) return -1; } else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) || match2 (g, fs->product_name, re_rhel, &major, &minor)) { fs->distro = OS_DISTRO_RHEL; 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_rhel_no_minor)) != NULL) { fs->distro = OS_DISTRO_RHEL; 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_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_is_file_opts (g, "/etc/debian_version", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 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; } else if (guestfs_is_file_opts (g, "/etc/pardus-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_PARDUS; if (parse_release_file (g, fs, "/etc/pardus-release") == -1) return -1; if (guestfs___parse_major_minor (g, fs) == -1) return -1; } else if (guestfs_is_file_opts (g, "/etc/arch-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_ARCHLINUX; /* /etc/arch-release file is empty and I can't see a way to * determine the actual release or product string. */ } else if (guestfs_is_file_opts (g, "/etc/gentoo-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_GENTOO; if (parse_release_file (g, fs, "/etc/gentoo-release") == -1) return -1; if (guestfs___parse_major_minor (g, fs) == -1) return -1; } else if (guestfs_is_file_opts (g, "/etc/meego-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_MEEGO; if (parse_release_file (g, fs, "/etc/meego-release") == -1) return -1; if (guestfs___parse_major_minor (g, fs) == -1) return -1; } else if (guestfs_is_file_opts (g, "/etc/slackware-version", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_SLACKWARE; if (parse_release_file (g, fs, "/etc/slackware-version") == -1) return -1; if (guestfs___parse_major_minor (g, fs) == -1) return -1; } else if (guestfs_is_file_opts (g, "/etc/ttylinux-target", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_TTYLINUX; if (parse_release_file (g, fs, "/etc/ttylinux-target") == -1) return -1; if (guestfs___parse_major_minor (g, fs) == -1) return -1; } else if (guestfs_is_file_opts (g, "/etc/SuSE-release", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { fs->distro = OS_DISTRO_SUSE_BASED; if (parse_suse_release (g, fs, "/etc/SuSE-release") == -1) return -1; } /* Buildroot (http://buildroot.net) is an embedded Linux distro * toolkit. It is used by specific distros such as Cirros. */ else if (guestfs_is_file_opts (g, "/etc/br-version", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { if (guestfs_is_file_opts (g, "/usr/share/cirros/logo", GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) fs->distro = OS_DISTRO_CIRROS; else fs->distro = OS_DISTRO_BUILDROOT; /* /etc/br-version has the format YYYY.MM[-git/hg/svn release] */ if (parse_release_file (g, fs, "/etc/br-version") == -1) return -1; if (guestfs___parse_major_minor (g, fs) == -1) return -1; } skip_release_checks:; /* Determine the architecture. */ check_architecture (g, fs); /* We already know /etc/fstab exists because it's part of the test * for Linux root above. We must now parse this file to determine * which filesystems are used by the operating system and how they * are mounted. */ const char *configfiles[] = { "/etc/fstab", "/etc/mdadm.conf", 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; }
static int parse_suse_release (guestfs_h *g, struct inspect_fs *fs, const char *filename) { int64_t size; char *major, *minor; CLEANUP_FREE_STRING_LIST char **lines = NULL; int r = -1; /* Don't trust guestfs_head_n not to break with very large files. * Check the file size is something reasonable first. */ size = guestfs_filesize (g, filename); if (size == -1) /* guestfs_filesize failed and has already set error in handle */ return -1; if (size > MAX_SMALL_FILE_SIZE) { error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), filename, size); return -1; } lines = guestfs_head_n (g, 10, filename); if (lines == NULL) return -1; if (lines[0] == NULL) goto out; /* First line is dist release name */ fs->product_name = safe_strdup (g, lines[0]); /* Match SLES first because openSuSE regex overlaps some SLES release strings */ if (match (g, fs->product_name, re_sles) || match (g, fs->product_name, re_nld)) { fs->distro = OS_DISTRO_SLES; /* Second line contains version string */ if (lines[1] == NULL) goto out; major = match1 (g, lines[1], re_sles_version); if (major == NULL) goto out; fs->major_version = guestfs___parse_unsigned_int (g, major); free (major); if (fs->major_version == -1) goto out; /* Third line contains service pack string */ if (lines[2] == NULL) goto out; minor = match1 (g, lines[2], re_sles_patchlevel); if (minor == NULL) goto out; fs->minor_version = guestfs___parse_unsigned_int (g, minor); free (minor); if (fs->minor_version == -1) goto out; } else if (match (g, fs->product_name, re_opensuse)) { fs->distro = OS_DISTRO_OPENSUSE; /* Second line contains version string */ if (lines[1] == NULL) goto out; if (match2 (g, lines[1], re_opensuse_version, &major, &minor)) { fs->major_version = guestfs___parse_unsigned_int (g, major); fs->minor_version = guestfs___parse_unsigned_int (g, minor); free (major); free (minor); if (fs->major_version == -1 || fs->minor_version == -1) goto out; } } r = 0; out: return r; }
/* Ubuntu has /etc/lsb-release containing: * DISTRIB_ID=Ubuntu # Distro * DISTRIB_RELEASE=10.04 # Version * DISTRIB_CODENAME=lucid * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name * * [Ubuntu-derived ...] Linux Mint was found to have this: * DISTRIB_ID=LinuxMint * DISTRIB_RELEASE=10 * DISTRIB_CODENAME=julia * DISTRIB_DESCRIPTION="Linux Mint 10 Julia" * Linux Mint also has /etc/linuxmint/info with more information, * but we can use the LSB file. * * Mandriva has: * LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch * DISTRIB_ID=MandrivaLinux * DISTRIB_RELEASE=2010.1 * DISTRIB_CODENAME=Henry_Farman * DISTRIB_DESCRIPTION="Mandriva Linux 2010.1" * Mandriva also has a normal release file called /etc/mandriva-release. */ static int parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) { const char *filename = "/etc/lsb-release"; int64_t size; CLEANUP_FREE_STRING_LIST char **lines = NULL; size_t i; int r = 0; /* Don't trust guestfs_head_n not to break with very large files. * Check the file size is something reasonable first. */ size = guestfs_filesize (g, filename); if (size == -1) /* guestfs_filesize failed and has already set error in handle */ return -1; if (size > MAX_SMALL_FILE_SIZE) { error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), filename, size); return -1; } lines = guestfs_head_n (g, 10, filename); if (lines == NULL) return -1; for (i = 0; lines[i] != NULL; ++i) { if (fs->distro == 0 && STREQ (lines[i], "DISTRIB_ID=Ubuntu")) { fs->distro = OS_DISTRO_UBUNTU; r = 1; } else if (fs->distro == 0 && STREQ (lines[i], "DISTRIB_ID=LinuxMint")) { fs->distro = OS_DISTRO_LINUX_MINT; r = 1; } else if (fs->distro == 0 && STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) { 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)) { 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 (fs->product_name == NULL && (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") || STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) { size_t len = strlen (lines[i]) - 21 - 1; fs->product_name = safe_strndup (g, &lines[i][21], len); r = 1; } else if (fs->product_name == NULL && STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) { size_t len = strlen (lines[i]) - 20; fs->product_name = safe_strndup (g, &lines[i][20], len); r = 1; } } /* The unnecessary construct in the next line is required to * workaround -Wstrict-overflow warning in gcc 4.5.1. */ return r ? 1 : 0; }
/* Fedora CDs and DVD (not netinst). The /.treeinfo file contains * an initial section somewhat like this: * * [general] * version = 14 * arch = x86_64 * family = Fedora * variant = Fedora * discnum = 1 * totaldiscs = 1 */ static int check_fedora_installer_root (guestfs_h *g, struct inspect_fs *fs) { char *str; const char *v; int r; int discnum = 0, totaldiscs = 0; fs->type = OS_TYPE_LINUX; r = guestfs___first_egrep_of_file (g, "/.treeinfo", "^family = Fedora$", 0, &str); if (r == -1) return -1; if (r > 0) { fs->distro = OS_DISTRO_FEDORA; free (str); } r = guestfs___first_egrep_of_file (g, "/.treeinfo", "^family = Red Hat Enterprise Linux$", 0, &str); if (r == -1) return -1; if (r > 0) { fs->distro = OS_DISTRO_RHEL; free (str); } /* XXX should do major.minor before this */ r = guestfs___first_egrep_of_file (g, "/.treeinfo", "^version = [[:digit:]]+", 0, &str); if (r == -1) return -1; if (r > 0) { v = find_value (str); fs->major_version = guestfs___parse_unsigned_int_ignore_trailing (g, v); free (str); if (fs->major_version == -1) return -1; } r = guestfs___first_egrep_of_file (g, "/.treeinfo", "^arch = [-_[:alnum:]]+$", 0, &str); if (r == -1) return -1; if (r > 0) { v = find_value (str); fs->arch = safe_strdup (g, v); free (str); } r = guestfs___first_egrep_of_file (g, "/.treeinfo", "^discnum = [[:digit:]]+$", 0, &str); if (r == -1) return -1; if (r > 0) { v = find_value (str); discnum = guestfs___parse_unsigned_int (g, v); free (str); if (discnum == -1) return -1; } r = guestfs___first_egrep_of_file (g, "/.treeinfo", "^totaldiscs = [[:digit:]]+$", 0, &str); if (r == -1) return -1; if (r > 0) { v = find_value (str); totaldiscs = guestfs___parse_unsigned_int (g, v); free (str); if (totaldiscs == -1) return -1; } fs->is_multipart_disk = totaldiscs > 0; /* and what about discnum? */ return 0; }