2 * Copyright (C) 2010-2011 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
39 #include "ignore-value.h"
43 #include "guestfs-internal.h"
44 #include "guestfs-internal-actions.h"
45 #include "guestfs_protocol.h"
47 #if defined(HAVE_HIVEX)
49 /* Compile all the regular expressions once when the shared library is
50 * loaded. PCRE is thread safe so we're supposedly OK here if
51 * multiple threads call into the libguestfs API functions below
54 static pcre *re_first_partition;
55 static pcre *re_major_minor;
57 static void compile_regexps (void) __attribute__((constructor));
58 static void free_regexps (void) __attribute__((destructor));
61 compile_regexps (void)
66 #define COMPILE(re,pattern,options) \
68 re = pcre_compile ((pattern), (options), &err, &offset, NULL); \
70 ignore_value (write (2, err, strlen (err))); \
75 COMPILE (re_first_partition, "^/dev/(?:h|s|v)d.1$", 0);
76 COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
82 pcre_free (re_first_partition);
83 pcre_free (re_major_minor);
86 static int check_filesystem (guestfs_h *g, const char *device, int is_block, int is_partnum);
87 static void check_package_format (guestfs_h *g, struct inspect_fs *fs);
88 static void check_package_management (guestfs_h *g, struct inspect_fs *fs);
89 static int extend_fses (guestfs_h *g);
91 /* Find out if 'device' contains a filesystem. If it does, add
92 * another entry in g->fses.
95 guestfs___check_for_filesystem_on (guestfs_h *g, const char *device,
96 int is_block, int is_partnum)
98 /* Get vfs-type in order to check if it's a Linux(?) swap device.
99 * If there's an error we should ignore it, so to do that we have to
100 * temporarily replace the error handler with a null one.
102 guestfs_error_handler_cb old_error_cb = g->error_cb;
104 char *vfs_type = guestfs_vfs_type (g, device);
105 g->error_cb = old_error_cb;
107 int is_swap = vfs_type && STREQ (vfs_type, "swap");
109 debug (g, "check_for_filesystem_on: %s %d %d (%s)",
110 device, is_block, is_partnum,
111 vfs_type ? vfs_type : "failed to get vfs type");
115 if (extend_fses (g) == -1)
117 g->fses[g->nr_fses-1].is_swap = 1;
121 /* Try mounting the device. As above, ignore errors. */
124 if (vfs_type && STREQ (vfs_type, "ufs")) { /* Hack for the *BSDs. */
125 /* FreeBSD fs is a variant of ufs called ufs2 ... */
126 r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", device, "/");
128 /* while NetBSD and OpenBSD use another variant labeled 44bsd */
129 r = guestfs_mount_vfs (g, "ro,ufstype=44bsd", "ufs", device, "/");
131 r = guestfs_mount_ro (g, device, "/");
134 g->error_cb = old_error_cb;
138 /* Do the rest of the checks. */
139 r = check_filesystem (g, device, is_block, is_partnum);
141 /* Unmount the filesystem. */
142 if (guestfs_umount_all (g) == -1)
148 /* is_block and is_partnum are just hints: is_block is true if the
149 * filesystem is a whole block device (eg. /dev/sda). is_partnum
150 * is > 0 if the filesystem is a direct partition, and in this case
151 * it is the partition number counting from 1
152 * (eg. /dev/sda1 => is_partnum == 1).
155 check_filesystem (guestfs_h *g, const char *device,
156 int is_block, int is_partnum)
158 if (extend_fses (g) == -1)
161 struct inspect_fs *fs = &g->fses[g->nr_fses-1];
163 fs->device = safe_strdup (g, device);
164 fs->is_mountable = 1;
166 /* Optimize some of the tests by avoiding multiple tests of the same thing. */
167 int is_dir_etc = guestfs_is_dir (g, "/etc") > 0;
168 int is_dir_bin = guestfs_is_dir (g, "/bin") > 0;
169 int is_dir_share = guestfs_is_dir (g, "/share") > 0;
172 if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
173 guestfs_is_file (g, "/grub/grub.conf") > 0)
174 fs->content = FS_CONTENT_LINUX_BOOT;
176 else if (is_dir_etc &&
178 guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 &&
179 guestfs_is_file (g, "/etc/fstab") > 0) {
180 /* Ignore /dev/sda1 which is a shadow of the real root filesystem
181 * that is probably /dev/sda5 (see:
182 * http://www.freebsd.org/doc/handbook/disk-organization.html)
184 if (match (g, device, re_first_partition))
188 fs->content = FS_CONTENT_FREEBSD_ROOT;
189 fs->format = OS_FORMAT_INSTALLED;
190 if (guestfs___check_freebsd_root (g, fs) == -1)
193 else if (is_dir_etc &&
195 guestfs_is_file (g, "/etc/fstab") > 0 &&
196 guestfs_is_file (g, "/etc/release") > 0) {
197 /* Ignore /dev/sda1 which is a shadow of the real root filesystem
198 * that is probably /dev/sda5 (see:
199 * http://www.freebsd.org/doc/handbook/disk-organization.html)
201 if (match (g, device, re_first_partition))
205 fs->content = FS_CONTENT_NETBSD_ROOT;
206 fs->format = OS_FORMAT_INSTALLED;
207 if (guestfs___check_netbsd_root (g, fs) == -1)
211 else if (guestfs_is_file (g, "/hurd/console") > 0 &&
212 guestfs_is_file (g, "/hurd/hello") > 0 &&
213 guestfs_is_file (g, "/hurd/null") > 0) {
215 fs->content = FS_CONTENT_HURD_ROOT;
216 fs->format = OS_FORMAT_INSTALLED; /* XXX could be more specific */
217 if (guestfs___check_hurd_root (g, fs) == -1)
221 else if (is_dir_etc &&
223 guestfs_is_file (g, "/etc/fstab") > 0) {
225 fs->content = FS_CONTENT_LINUX_ROOT;
226 fs->format = OS_FORMAT_INSTALLED;
227 if (guestfs___check_linux_root (g, fs) == -1)
230 /* Linux /usr/local? */
231 else if (is_dir_etc &&
234 guestfs_exists (g, "/local") == 0 &&
235 guestfs_is_file (g, "/etc/fstab") == 0)
236 fs->content = FS_CONTENT_LINUX_USR_LOCAL;
238 else if (is_dir_etc &&
241 guestfs_exists (g, "/local") > 0 &&
242 guestfs_is_file (g, "/etc/fstab") == 0)
243 fs->content = FS_CONTENT_LINUX_USR;
245 else if (guestfs_is_dir (g, "/log") > 0 &&
246 guestfs_is_dir (g, "/run") > 0 &&
247 guestfs_is_dir (g, "/spool") > 0)
248 fs->content = FS_CONTENT_LINUX_VAR;
250 else if (guestfs___has_windows_systemroot (g) >= 0) {
252 fs->content = FS_CONTENT_WINDOWS_ROOT;
253 fs->format = OS_FORMAT_INSTALLED;
254 if (guestfs___check_windows_root (g, fs) == -1)
257 /* Windows volume with installed applications (but not root)? */
258 else if (guestfs___is_dir_nocase (g, "/System Volume Information") > 0 &&
259 guestfs___is_dir_nocase (g, "/Program Files") > 0)
260 fs->content = FS_CONTENT_WINDOWS_VOLUME_WITH_APPS;
261 /* Windows volume (but not root)? */
262 else if (guestfs___is_dir_nocase (g, "/System Volume Information") > 0)
263 fs->content = FS_CONTENT_WINDOWS_VOLUME;
264 /* Install CD/disk? Skip these checks if it's not a whole device
265 * (eg. CD) or the first partition (eg. bootable USB key).
267 else if ((is_block || is_partnum == 1) &&
268 (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 ||
269 guestfs_is_dir (g, "/EFI/BOOT") > 0 ||
270 guestfs_is_file (g, "/images/install.img") > 0 ||
271 guestfs_is_dir (g, "/.disk") > 0 ||
272 guestfs_is_file (g, "/.discinfo") > 0 ||
273 guestfs_is_file (g, "/i386/txtsetup.sif") > 0 ||
274 guestfs_is_file (g, "/amd64/txtsetup.sif")) > 0) {
276 fs->content = FS_CONTENT_INSTALLER;
277 fs->format = OS_FORMAT_INSTALLER;
278 if (guestfs___check_installer_root (g, fs) == -1)
282 /* The above code should have set fs->type and fs->distro fields, so
283 * we can now guess the package management system.
285 check_package_format (g, fs);
286 check_package_management (g, fs);
292 extend_fses (guestfs_h *g)
294 size_t n = g->nr_fses + 1;
295 struct inspect_fs *p;
297 p = realloc (g->fses, n * sizeof (struct inspect_fs));
299 perrorf (g, "realloc");
306 memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
312 guestfs___is_file_nocase (guestfs_h *g, const char *path)
317 p = guestfs___case_sensitive_path_silently (g, path);
320 r = guestfs_is_file (g, p);
326 guestfs___is_dir_nocase (guestfs_h *g, const char *path)
331 p = guestfs___case_sensitive_path_silently (g, path);
334 r = guestfs_is_dir (g, p);
339 /* Parse small, unsigned ints, as used in version numbers. */
341 guestfs___parse_unsigned_int (guestfs_h *g, const char *str)
344 int r = xstrtol (str, NULL, 10, &ret, "");
345 if (r != LONGINT_OK) {
346 error (g, _("could not parse integer in version number: %s"), str);
352 /* Like parse_unsigned_int, but ignore trailing stuff. */
354 guestfs___parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str)
357 int r = xstrtol (str, NULL, 10, &ret, NULL);
358 if (r != LONGINT_OK) {
359 error (g, _("could not parse integer in version number: %s"), str);
365 /* Parse generic MAJOR.MINOR from the fs->product_name string. */
367 guestfs___parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
371 if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) {
372 fs->major_version = guestfs___parse_unsigned_int (g, major);
374 if (fs->major_version == -1) {
378 fs->minor_version = guestfs___parse_unsigned_int (g, minor);
380 if (fs->minor_version == -1)
386 /* At the moment, package format and package management is just a
387 * simple function of the distro and major_version fields, so these
388 * can never return an error. We might be cleverer in future.
391 check_package_format (guestfs_h *g, struct inspect_fs *fs)
393 switch (fs->distro) {
394 case OS_DISTRO_FEDORA:
395 case OS_DISTRO_MEEGO:
396 case OS_DISTRO_REDHAT_BASED:
398 case OS_DISTRO_MAGEIA:
399 case OS_DISTRO_MANDRIVA:
400 case OS_DISTRO_OPENSUSE:
401 case OS_DISTRO_CENTOS:
402 case OS_DISTRO_SCIENTIFIC_LINUX:
403 fs->package_format = OS_PACKAGE_FORMAT_RPM;
406 case OS_DISTRO_DEBIAN:
407 case OS_DISTRO_UBUNTU:
408 case OS_DISTRO_LINUX_MINT:
409 fs->package_format = OS_PACKAGE_FORMAT_DEB;
412 case OS_DISTRO_ARCHLINUX:
413 fs->package_format = OS_PACKAGE_FORMAT_PACMAN;
415 case OS_DISTRO_GENTOO:
416 fs->package_format = OS_PACKAGE_FORMAT_EBUILD;
418 case OS_DISTRO_PARDUS:
419 fs->package_format = OS_PACKAGE_FORMAT_PISI;
422 case OS_DISTRO_SLACKWARE:
423 case OS_DISTRO_TTYLINUX:
424 case OS_DISTRO_WINDOWS:
425 case OS_DISTRO_UNKNOWN:
427 fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN;
433 check_package_management (guestfs_h *g, struct inspect_fs *fs)
435 switch (fs->distro) {
436 case OS_DISTRO_FEDORA:
437 case OS_DISTRO_MEEGO:
438 fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
441 case OS_DISTRO_REDHAT_BASED:
443 case OS_DISTRO_CENTOS:
444 case OS_DISTRO_SCIENTIFIC_LINUX:
445 if (fs->major_version >= 5)
446 fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
448 fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE;
451 case OS_DISTRO_DEBIAN:
452 case OS_DISTRO_UBUNTU:
453 case OS_DISTRO_LINUX_MINT:
454 fs->package_management = OS_PACKAGE_MANAGEMENT_APT;
457 case OS_DISTRO_ARCHLINUX:
458 fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN;
460 case OS_DISTRO_GENTOO:
461 fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE;
463 case OS_DISTRO_PARDUS:
464 fs->package_management = OS_PACKAGE_MANAGEMENT_PISI;
466 case OS_DISTRO_MAGEIA:
467 case OS_DISTRO_MANDRIVA:
468 fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI;
471 case OS_DISTRO_OPENSUSE:
472 fs->package_management = OS_PACKAGE_MANAGEMENT_ZYPPER;
475 case OS_DISTRO_SLACKWARE:
476 case OS_DISTRO_TTYLINUX:
477 case OS_DISTRO_WINDOWS:
478 case OS_DISTRO_UNKNOWN:
480 fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
485 /* Get the first line of a small file, without any trailing newline
489 guestfs___first_line_of_file (guestfs_h *g, const char *filename)
495 /* Don't trust guestfs_head_n not to break with very large files.
496 * Check the file size is something reasonable first.
498 size = guestfs_filesize (g, filename);
500 /* guestfs_filesize failed and has already set error in handle */
502 if (size > MAX_SMALL_FILE_SIZE) {
503 error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
508 lines = guestfs_head_n (g, 1, filename);
511 if (lines[0] == NULL) {
512 error (g, _("%s: file is empty"), filename);
513 guestfs___free_string_list (lines);
516 /* lines[1] should be NULL because of '1' argument above ... */
518 ret = lines[0]; /* caller frees */
519 free (lines); /* free the array */
524 /* Get the first matching line (using guestfs_egrep{,i}) of a small file,
525 * without any trailing newline character.
527 * Returns: 1 = returned a line (in *ret)
532 guestfs___first_egrep_of_file (guestfs_h *g, const char *filename,
533 const char *eregex, int iflag, char **ret)
539 /* Don't trust guestfs_egrep not to break with very large files.
540 * Check the file size is something reasonable first.
542 size = guestfs_filesize (g, filename);
544 /* guestfs_filesize failed and has already set error in handle */
546 if (size > MAX_SMALL_FILE_SIZE) {
547 error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
552 lines = (!iflag ? guestfs_egrep : guestfs_egrepi) (g, eregex, filename);
555 if (lines[0] == NULL) {
556 guestfs___free_string_list (lines);
560 *ret = lines[0]; /* caller frees */
562 /* free up any other matches and the array itself */
563 for (i = 1; lines[i] != NULL; ++i)
570 #endif /* defined(HAVE_HIVEX) */