/* libguestfs * 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 * 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 #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PCRE #include #endif #ifdef HAVE_HIVEX #include #endif #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" #if defined(HAVE_PCRE) && defined(HAVE_HIVEX) /* The main inspection code. */ char ** guestfs__inspect_os (guestfs_h *g) { /* Remove any information previously stored in the handle. */ guestfs___free_inspect_info (g); if (guestfs_umount_all (g) == -1) return NULL; /* Iterate over all possible devices. Try to mount each * (read-only). Examine ones which contain filesystems and add that * information to the handle. */ /* Look to see if any devices directly contain filesystems (RHBZ#590167). */ char **devices; devices = guestfs_list_devices (g); if (devices == NULL) return NULL; size_t i; for (i = 0; devices[i] != NULL; ++i) { if (guestfs___check_for_filesystem_on (g, devices[i], 1, 0) == -1) { guestfs___free_string_list (devices); guestfs___free_inspect_info (g); return NULL; } } guestfs___free_string_list (devices); /* Look at all partitions. */ char **partitions; partitions = guestfs_list_partitions (g); if (partitions == NULL) { guestfs___free_inspect_info (g); return NULL; } for (i = 0; partitions[i] != NULL; ++i) { if (guestfs___check_for_filesystem_on (g, partitions[i], 0, i+1) == -1) { guestfs___free_string_list (partitions); guestfs___free_inspect_info (g); return NULL; } } guestfs___free_string_list (partitions); /* Look at all LVs. */ if (guestfs___feature_available (g, "lvm2")) { char **lvs; lvs = guestfs_lvs (g); if (lvs == NULL) { guestfs___free_inspect_info (g); return NULL; } for (i = 0; lvs[i] != NULL; ++i) { if (guestfs___check_for_filesystem_on (g, lvs[i], 0, 0) == -1) { guestfs___free_string_list (lvs); guestfs___free_inspect_info (g); return NULL; } } guestfs___free_string_list (lvs); } /* At this point we have, in the handle, a list of all filesystems * found and data about each one. Now we assemble the list of * filesystems which are root devices and return that to the user. * Fall through to guestfs__inspect_get_roots to do that. */ char **ret = guestfs__inspect_get_roots (g); if (ret == NULL) guestfs___free_inspect_info (g); return ret; } char ** guestfs__inspect_get_roots (guestfs_h *g) { /* NB. Doesn't matter if g->nr_fses == 0. We just return an empty * list in this case. */ size_t i; size_t count = 0; for (i = 0; i < g->nr_fses; ++i) if (g->fses[i].is_root) count++; char **ret = calloc (count+1, sizeof (char *)); if (ret == NULL) { perrorf (g, "calloc"); return NULL; } count = 0; for (i = 0; i < g->nr_fses; ++i) { if (g->fses[i].is_root) { ret[count] = safe_strdup (g, g->fses[i].device); count++; } } ret[count] = NULL; return ret; } char * guestfs__inspect_get_type (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char *ret; switch (fs->type) { case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break; case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break; case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break; case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break; } return ret; } char * guestfs__inspect_get_arch (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; return safe_strdup (g, fs->arch ? : "unknown"); } char * guestfs__inspect_get_distro (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char *ret; switch (fs->distro) { case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break; case OS_DISTRO_CENTOS: ret = safe_strdup (g, "centos"); break; case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break; case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break; case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break; case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break; case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break; case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break; case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break; case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break; case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break; case OS_DISTRO_SCIENTIFIC_LINUX: ret = safe_strdup (g, "scientificlinux"); break; case OS_DISTRO_SLACKWARE: ret = safe_strdup (g, "slackware"); break; case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break; case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break; case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break; } return ret; } int guestfs__inspect_get_major_version (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return -1; return fs->major_version; } int guestfs__inspect_get_minor_version (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return -1; return fs->minor_version; } char * guestfs__inspect_get_product_name (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; return safe_strdup (g, fs->product_name ? : "unknown"); } char * guestfs__inspect_get_product_variant (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; return safe_strdup (g, fs->product_variant ? : "unknown"); } char * guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; if (!fs->windows_systemroot) { error (g, _("not a Windows guest, or systemroot could not be determined")); return NULL; } return safe_strdup (g, fs->windows_systemroot); } char * guestfs__inspect_get_windows_current_control_set (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; if (!fs->windows_current_control_set) { error (g, _("not a Windows guest, or CurrentControlSet could not be determined")); return NULL; } return safe_strdup (g, fs->windows_current_control_set); } char * guestfs__inspect_get_format (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char *ret; switch (fs->format) { case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break; case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break; case OS_FORMAT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break; } return ret; } int guestfs__inspect_is_live (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return -1; return fs->is_live_disk; } int guestfs__inspect_is_netinst (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return -1; return fs->is_netinst_disk; } int guestfs__inspect_is_multipart (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return -1; return fs->is_multipart_disk; } char ** guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char **ret; /* If no fstab information (Windows) return just the root. */ if (fs->nr_fstab == 0) { ret = calloc (3, sizeof (char *)); ret[0] = safe_strdup (g, "/"); ret[1] = safe_strdup (g, root); ret[2] = NULL; return ret; } #define CRITERION fs->fstab[i].mountpoint[0] == '/' size_t i, count = 0; for (i = 0; i < fs->nr_fstab; ++i) if (CRITERION) count++; /* Hashtables have 2N+1 entries. */ ret = calloc (2*count+1, sizeof (char *)); if (ret == NULL) { perrorf (g, "calloc"); return NULL; } count = 0; for (i = 0; i < fs->nr_fstab; ++i) if (CRITERION) { ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint); ret[2*count+1] = safe_strdup (g, fs->fstab[i].device); count++; } #undef CRITERION return ret; } char ** guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char **ret; /* If no fstab information (Windows) return just the root. */ if (fs->nr_fstab == 0) { ret = calloc (2, sizeof (char *)); ret[0] = safe_strdup (g, root); ret[1] = NULL; return ret; } ret = calloc (fs->nr_fstab + 1, sizeof (char *)); if (ret == NULL) { perrorf (g, "calloc"); return NULL; } size_t i; for (i = 0; i < fs->nr_fstab; ++i) ret[i] = safe_strdup (g, fs->fstab[i].device); return ret; } char ** guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root) { char **ret; size_t i, count; struct inspect_fs *fs; fs = guestfs___search_for_root (g, root); if (!fs) return NULL; /* If no drive mappings, return an empty hashtable. */ if (!fs->drive_mappings) count = 0; else { for (count = 0; fs->drive_mappings[count] != NULL; count++) ; } ret = calloc (count+1, sizeof (char *)); if (ret == NULL) { perrorf (g, "calloc"); return NULL; } /* We need to make a deep copy of the hashtable since the caller * will free it. */ for (i = 0; i < count; ++i) ret[i] = safe_strdup (g, fs->drive_mappings[i]); ret[count] = NULL; return ret; } char * guestfs__inspect_get_package_format (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char *ret; switch (fs->package_format) { case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break; case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break; case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break; case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break; case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break; case OS_PACKAGE_FORMAT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break; } return ret; } char * guestfs__inspect_get_package_management (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; char *ret; switch (fs->package_management) { case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break; case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break; case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break; case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break; case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break; case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break; case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break; case OS_PACKAGE_MANAGEMENT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break; } return ret; } char * guestfs__inspect_get_hostname (guestfs_h *g, const char *root) { struct inspect_fs *fs = guestfs___search_for_root (g, root); if (!fs) return NULL; return safe_strdup (g, fs->hostname ? : "unknown"); } /* Download a guest file to a local temporary file. The file is * downloaded into g->tmpdir, unless it already exists in g->tmpdir. * The final name will be g->tmpdir + "/" + basename. Refuse to * download the guest file if it is larger than max_size. The caller * does not need to delete the temporary file after use: it will be * deleted when the handle is cleaned up. */ int guestfs___download_to_tmp (guestfs_h *g, const char *filename, const char *basename, int64_t max_size) { int tmpdirfd, fd, r = -1; char buf[32]; int64_t size; tmpdirfd = open (g->tmpdir, O_RDONLY); if (tmpdirfd == -1) { perrorf (g, _("%s: temporary directory not found"), g->tmpdir); return -1; } /* If the file has already been downloaded, return. */ if (faccessat (tmpdirfd, basename, R_OK, 0) == 0) { r = 0; goto out; } /* Check size of remote file. */ size = guestfs_filesize (g, filename); if (size == -1) /* guestfs_filesize failed and has already set error in handle */ goto out; if (size > max_size) { error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), filename, size); goto out; } fd = openat (tmpdirfd, basename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0600); if (fd == -1) { perrorf (g, "openat: %s/%s", g->tmpdir, basename); goto out; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, filename, buf) == -1) { unlinkat (tmpdirfd, basename, 0); close (fd); goto out; } if (close (fd) == -1) { perrorf (g, "close: %s/%s", g->tmpdir, basename); unlinkat (tmpdirfd, basename, 0); goto out; } r = 0; out: if (tmpdirfd >= 0) close (tmpdirfd); return r; } struct inspect_fs * guestfs___search_for_root (guestfs_h *g, const char *root) { if (g->nr_fses == 0) { error (g, _("no inspection data: call guestfs_inspect_os first")); return NULL; } size_t i; struct inspect_fs *fs; for (i = 0; i < g->nr_fses; ++i) { fs = &g->fses[i]; if (fs->is_root && STREQ (root, fs->device)) return fs; } error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"), root); return NULL; } #else /* no PCRE or hivex at compile time */ /* XXX These functions should be in an optgroup. */ #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__inspect_os (guestfs_h *g) { NOT_IMPL(NULL); } char ** guestfs__inspect_get_roots (guestfs_h *g) { NOT_IMPL(NULL); } char * guestfs__inspect_get_type (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_arch (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_distro (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } int guestfs__inspect_get_major_version (guestfs_h *g, const char *root) { NOT_IMPL(-1); } int guestfs__inspect_get_minor_version (guestfs_h *g, const char *root) { NOT_IMPL(-1); } char * guestfs__inspect_get_product_name (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_product_variant (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_windows_current_control_set (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char ** guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char ** guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char ** guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_package_format (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_package_management (guestfs_h *g, const char *root) { NOT_IMPL(NULL); } char * guestfs__inspect_get_hostname (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 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].product_variant); free (g->fses[i].arch); free (g->fses[i].hostname); free (g->fses[i].windows_systemroot); free (g->fses[i].windows_current_control_set); 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); if (g->fses[i].drive_mappings) guestfs___free_string_list (g->fses[i].drive_mappings); } 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; const char *groups[] = { feature, NULL }; int r = guestfs_available (g, (char * const *) groups); g->error_cb = old_error_cb; return r == 0 ? 1 : 0; }