e8c29a364f376249a4d9ef4b94a6081c33117379
[libguestfs.git] / src / inspect.c
1 /* libguestfs
2  * Copyright (C) 2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <inttypes.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #ifdef HAVE_PCRE
30 #include <pcre.h>
31 #endif
32
33 #ifdef HAVE_HIVEX
34 #include <hivex.h>
35 #endif
36
37 #include "c-ctype.h"
38 #include "ignore-value.h"
39 #include "xstrtol.h"
40
41 #include "guestfs.h"
42 #include "guestfs-internal.h"
43 #include "guestfs-internal-actions.h"
44 #include "guestfs_protocol.h"
45
46 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
47
48 /* Compile all the regular expressions once when the shared library is
49  * loaded.  PCRE is thread safe so we're supposedly OK here if
50  * multiple threads call into the libguestfs API functions below
51  * simultaneously.
52  */
53 static pcre *re_fedora;
54 static pcre *re_rhel_old;
55 static pcre *re_rhel;
56 static pcre *re_rhel_no_minor;
57 static pcre *re_major_minor;
58 static pcre *re_aug_seq;
59 static pcre *re_xdev;
60 static pcre *re_first_partition;
61 static pcre *re_freebsd;
62 static pcre *re_windows_version;
63
64 static void compile_regexps (void) __attribute__((constructor));
65 static void free_regexps (void) __attribute__((destructor));
66
67 static void
68 compile_regexps (void)
69 {
70   const char *err;
71   int offset;
72
73 #define COMPILE(re,pattern,options)                                     \
74   do {                                                                  \
75     re = pcre_compile ((pattern), (options), &err, &offset, NULL);      \
76     if (re == NULL) {                                                   \
77       ignore_value (write (2, err, strlen (err)));                      \
78       abort ();                                                         \
79     }                                                                   \
80   } while (0)
81
82   COMPILE (re_fedora, "Fedora release (\\d+)", 0);
83   COMPILE (re_rhel_old,
84            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0);
85   COMPILE (re_rhel,
86            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0);
87   COMPILE (re_rhel_no_minor,
88            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)", 0);
89   COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
90   COMPILE (re_aug_seq, "/\\d+$", 0);
91   COMPILE (re_xdev, "^/dev/(?:h|s|v|xv)d([a-z]\\d*)$", 0);
92   COMPILE (re_first_partition, "^/dev/(?:h|s|v)d.1$", 0);
93   COMPILE (re_freebsd, "^/dev/ad(\\d+)s(\\d+)([a-z])$", 0);
94   COMPILE (re_windows_version, "^(\\d+)\\.(\\d+)", 0);
95 }
96
97 static void
98 free_regexps (void)
99 {
100   pcre_free (re_fedora);
101   pcre_free (re_rhel_old);
102   pcre_free (re_rhel);
103   pcre_free (re_rhel_no_minor);
104   pcre_free (re_major_minor);
105   pcre_free (re_aug_seq);
106   pcre_free (re_xdev);
107   pcre_free (re_first_partition);
108   pcre_free (re_freebsd);
109   pcre_free (re_windows_version);
110 }
111
112 /* The main inspection code. */
113 static int check_for_filesystem_on (guestfs_h *g, const char *device);
114
115 char **
116 guestfs__inspect_os (guestfs_h *g)
117 {
118   /* Remove any information previously stored in the handle. */
119   guestfs___free_inspect_info (g);
120
121   if (guestfs_umount_all (g) == -1)
122     return NULL;
123
124   /* Iterate over all possible devices.  Try to mount each
125    * (read-only).  Examine ones which contain filesystems and add that
126    * information to the handle.
127    */
128   /* Look to see if any devices directly contain filesystems (RHBZ#590167). */
129   char **devices;
130   devices = guestfs_list_devices (g);
131   if (devices == NULL)
132     return NULL;
133
134   size_t i;
135   for (i = 0; devices[i] != NULL; ++i) {
136     if (check_for_filesystem_on (g, devices[i]) == -1) {
137       guestfs___free_string_list (devices);
138       guestfs___free_inspect_info (g);
139       return NULL;
140     }
141   }
142   guestfs___free_string_list (devices);
143
144   /* Look at all partitions. */
145   char **partitions;
146   partitions = guestfs_list_partitions (g);
147   if (partitions == NULL) {
148     guestfs___free_inspect_info (g);
149     return NULL;
150   }
151
152   for (i = 0; partitions[i] != NULL; ++i) {
153     if (check_for_filesystem_on (g, partitions[i]) == -1) {
154       guestfs___free_string_list (partitions);
155       guestfs___free_inspect_info (g);
156       return NULL;
157     }
158   }
159   guestfs___free_string_list (partitions);
160
161   /* Look at all LVs. */
162   if (guestfs___feature_available (g, "lvm2")) {
163     char **lvs;
164     lvs = guestfs_lvs (g);
165     if (lvs == NULL) {
166       guestfs___free_inspect_info (g);
167       return NULL;
168     }
169
170     for (i = 0; lvs[i] != NULL; ++i) {
171       if (check_for_filesystem_on (g, lvs[i]) == -1) {
172         guestfs___free_string_list (lvs);
173         guestfs___free_inspect_info (g);
174         return NULL;
175       }
176     }
177     guestfs___free_string_list (lvs);
178   }
179
180   /* At this point we have, in the handle, a list of all filesystems
181    * found and data about each one.  Now we assemble the list of
182    * filesystems which are root devices and return that to the user.
183    * Fall through to guestfs__inspect_get_roots to do that.
184    */
185   char **ret = guestfs__inspect_get_roots (g);
186   if (ret == NULL)
187     guestfs___free_inspect_info (g);
188   return ret;
189 }
190
191 /* Find out if 'device' contains a filesystem.  If it does, add
192  * another entry in g->fses.
193  */
194 static int check_filesystem (guestfs_h *g, const char *device);
195 static int check_linux_root (guestfs_h *g, struct inspect_fs *fs);
196 static int check_freebsd_root (guestfs_h *g, struct inspect_fs *fs);
197 static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
198 static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
199 static int check_windows_root (guestfs_h *g, struct inspect_fs *fs);
200 static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
201 static int check_windows_registry (guestfs_h *g, struct inspect_fs *fs);
202 static char *resolve_windows_path_silently (guestfs_h *g, const char *);
203 static int extend_fses (guestfs_h *g);
204 static int parse_unsigned_int (guestfs_h *g, const char *str);
205 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
206                             const char *spec, const char *mp);
207 static char *resolve_fstab_device (guestfs_h *g, const char *spec);
208 static void check_package_format (guestfs_h *g, struct inspect_fs *fs);
209 static void check_package_management (guestfs_h *g, struct inspect_fs *fs);
210
211 static int
212 check_for_filesystem_on (guestfs_h *g, const char *device)
213 {
214   /* Get vfs-type in order to check if it's a Linux(?) swap device.
215    * If there's an error we should ignore it, so to do that we have to
216    * temporarily replace the error handler with a null one.
217    */
218   guestfs_error_handler_cb old_error_cb = g->error_cb;
219   g->error_cb = NULL;
220   char *vfs_type = guestfs_vfs_type (g, device);
221   g->error_cb = old_error_cb;
222
223   int is_swap = vfs_type && STREQ (vfs_type, "swap");
224
225   if (g->verbose)
226     fprintf (stderr, "check_for_filesystem_on: %s (%s)\n",
227              device, vfs_type ? vfs_type : "failed to get vfs type");
228
229   if (is_swap) {
230     free (vfs_type);
231     if (extend_fses (g) == -1)
232       return -1;
233     g->fses[g->nr_fses-1].is_swap = 1;
234     return 0;
235   }
236
237   /* Try mounting the device.  As above, ignore errors. */
238   g->error_cb = NULL;
239   int r = guestfs_mount_ro (g, device, "/");
240   if (r == -1 && vfs_type && STREQ (vfs_type, "ufs")) /* Hack for the *BSDs. */
241     r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", device, "/");
242   free (vfs_type);
243   g->error_cb = old_error_cb;
244   if (r == -1)
245     return 0;
246
247   /* Do the rest of the checks. */
248   r = check_filesystem (g, device);
249
250   /* Unmount the filesystem. */
251   if (guestfs_umount_all (g) == -1)
252     return -1;
253
254   return r;
255 }
256
257 static int
258 check_filesystem (guestfs_h *g, const char *device)
259 {
260   if (extend_fses (g) == -1)
261     return -1;
262
263   struct inspect_fs *fs = &g->fses[g->nr_fses-1];
264
265   fs->device = safe_strdup (g, device);
266   fs->is_mountable = 1;
267
268   /* Optimize some of the tests by avoiding multiple tests of the same thing. */
269   int is_dir_etc = guestfs_is_dir (g, "/etc") > 0;
270   int is_dir_bin = guestfs_is_dir (g, "/bin") > 0;
271   int is_dir_share = guestfs_is_dir (g, "/share") > 0;
272
273   /* Grub /boot? */
274   if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
275       guestfs_is_file (g, "/grub/grub.conf") > 0)
276     fs->content = FS_CONTENT_LINUX_BOOT;
277   /* FreeBSD root? */
278   else if (is_dir_etc &&
279            is_dir_bin &&
280            guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 &&
281            guestfs_is_file (g, "/etc/fstab") > 0) {
282     /* Ignore /dev/sda1 which is a shadow of the real root filesystem
283      * that is probably /dev/sda5 (see:
284      * http://www.freebsd.org/doc/handbook/disk-organization.html)
285      */
286     if (match (g, device, re_first_partition))
287       return 0;
288
289     fs->is_root = 1;
290     fs->content = FS_CONTENT_FREEBSD_ROOT;
291     if (check_freebsd_root (g, fs) == -1)
292       return -1;
293   }
294   /* Linux root? */
295   else if (is_dir_etc &&
296            is_dir_bin &&
297            guestfs_is_file (g, "/etc/fstab") > 0) {
298     fs->is_root = 1;
299     fs->content = FS_CONTENT_LINUX_ROOT;
300     if (check_linux_root (g, fs) == -1)
301       return -1;
302   }
303   /* Linux /usr/local? */
304   else if (is_dir_etc &&
305            is_dir_bin &&
306            is_dir_share &&
307            guestfs_exists (g, "/local") == 0 &&
308            guestfs_is_file (g, "/etc/fstab") == 0)
309     fs->content = FS_CONTENT_LINUX_USR_LOCAL;
310   /* Linux /usr? */
311   else if (is_dir_etc &&
312            is_dir_bin &&
313            is_dir_share &&
314            guestfs_exists (g, "/local") > 0 &&
315            guestfs_is_file (g, "/etc/fstab") == 0)
316     fs->content = FS_CONTENT_LINUX_USR;
317   /* Linux /var? */
318   else if (guestfs_is_dir (g, "/log") > 0 &&
319            guestfs_is_dir (g, "/run") > 0 &&
320            guestfs_is_dir (g, "/spool") > 0)
321     fs->content = FS_CONTENT_LINUX_VAR;
322   /* Windows root? */
323   else if (guestfs_is_file (g, "/AUTOEXEC.BAT") > 0 ||
324            guestfs_is_file (g, "/autoexec.bat") > 0 ||
325            guestfs_is_dir (g, "/Program Files") > 0 ||
326            guestfs_is_dir (g, "/WINDOWS") > 0 ||
327            guestfs_is_dir (g, "/Windows") > 0 ||
328            guestfs_is_dir (g, "/windows") > 0 ||
329            guestfs_is_dir (g, "/WIN32") > 0 ||
330            guestfs_is_dir (g, "/Win32") > 0 ||
331            guestfs_is_dir (g, "/WINNT") > 0 ||
332            guestfs_is_file (g, "/boot.ini") > 0 ||
333            guestfs_is_file (g, "/ntldr") > 0) {
334     fs->is_root = 1;
335     fs->content = FS_CONTENT_WINDOWS_ROOT;
336     if (check_windows_root (g, fs) == -1)
337       return -1;
338   }
339
340   return 0;
341 }
342
343 /* Set fs->product_name to the first line of the release file. */
344 static int
345 parse_release_file (guestfs_h *g, struct inspect_fs *fs,
346                     const char *release_filename)
347 {
348   char **product_name = guestfs_head_n (g, 1, release_filename);
349   if (product_name == NULL)
350     return -1;
351   if (product_name[0] == NULL) {
352     error (g, _("%s: file is empty"), release_filename);
353     guestfs___free_string_list (product_name);
354     return -1;
355   }
356
357   /* Note that this string becomes owned by the handle and will
358    * be freed by guestfs___free_inspect_info.
359    */
360   fs->product_name = product_name[0];
361   free (product_name);
362
363   return 0;
364 }
365
366 /* Parse generic MAJOR.MINOR from the fs->product_name string. */
367 static int
368 parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
369 {
370   char *major, *minor;
371
372   if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) {
373     fs->major_version = parse_unsigned_int (g, major);
374     free (major);
375     if (fs->major_version == -1) {
376       free (minor);
377       return -1;
378     }
379     fs->minor_version = parse_unsigned_int (g, minor);
380     free (minor);
381     if (fs->minor_version == -1)
382       return -1;
383   }
384   return 0;
385 }
386
387 /* Ubuntu has /etc/lsb-release containing:
388  *   DISTRIB_ID=Ubuntu                                # Distro
389  *   DISTRIB_RELEASE=10.04                            # Version
390  *   DISTRIB_CODENAME=lucid
391  *   DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"         # Product name
392  *
393  * [Ubuntu-derived ...] Linux Mint was found to have this:
394  *   DISTRIB_ID=LinuxMint
395  *   DISTRIB_RELEASE=10
396  *   DISTRIB_CODENAME=julia
397  *   DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
398  * Linux Mint also has /etc/linuxmint/info with more information,
399  * but we can use the LSB file.
400  *
401  * Mandriva has:
402  *   LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
403  *   DISTRIB_ID=MandrivaLinux
404  *   DISTRIB_RELEASE=2010.1
405  *   DISTRIB_CODENAME=Henry_Farman
406  *   DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
407  * Mandriva also has a normal release file called /etc/mandriva-release.
408  */
409 static int
410 parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
411 {
412   char **lines;
413   size_t i;
414   int r = 0;
415
416   lines = guestfs_head_n (g, 10, "/etc/lsb-release");
417   if (lines == NULL)
418     return -1;
419
420   for (i = 0; lines[i] != NULL; ++i) {
421     if (fs->distro == 0 &&
422         STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
423       fs->distro = OS_DISTRO_UBUNTU;
424       r = 1;
425     }
426     else if (fs->distro == 0 &&
427              STREQ (lines[i], "DISTRIB_ID=LinuxMint")) {
428       fs->distro = OS_DISTRO_LINUX_MINT;
429       r = 1;
430     }
431     else if (fs->distro == 0 &&
432              STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) {
433       fs->distro = OS_DISTRO_MANDRIVA;
434       r = 1;
435     }
436     else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
437       char *major, *minor;
438       if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) {
439         fs->major_version = parse_unsigned_int (g, major);
440         free (major);
441         if (fs->major_version == -1) {
442           free (minor);
443           guestfs___free_string_list (lines);
444           return -1;
445         }
446         fs->minor_version = parse_unsigned_int (g, minor);
447         free (minor);
448         if (fs->minor_version == -1) {
449           guestfs___free_string_list (lines);
450           return -1;
451         }
452       }
453     }
454     else if (fs->product_name == NULL &&
455              (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
456               STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
457       size_t len = strlen (lines[i]) - 21 - 1;
458       fs->product_name = safe_strndup (g, &lines[i][21], len);
459       r = 1;
460     }
461     else if (fs->product_name == NULL &&
462              STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
463       size_t len = strlen (lines[i]) - 20;
464       fs->product_name = safe_strndup (g, &lines[i][20], len);
465       r = 1;
466     }
467   }
468
469   guestfs___free_string_list (lines);
470   return r;
471 }
472
473 /* The currently mounted device is known to be a Linux root.  Try to
474  * determine from this the distro, version, etc.  Also parse
475  * /etc/fstab to determine the arrangement of mountpoints and
476  * associated devices.
477  */
478 static int
479 check_linux_root (guestfs_h *g, struct inspect_fs *fs)
480 {
481   int r;
482
483   fs->type = OS_TYPE_LINUX;
484
485   if (guestfs_exists (g, "/etc/lsb-release") > 0) {
486     r = parse_lsb_release (g, fs);
487     if (r == -1)        /* error */
488       return -1;
489     if (r == 1)         /* ok - detected the release from this file */
490       goto skip_release_checks;
491   }
492
493   if (guestfs_exists (g, "/etc/redhat-release") > 0) {
494     fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */
495
496     if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
497       return -1;
498
499     char *major, *minor;
500     if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
501       fs->distro = OS_DISTRO_FEDORA;
502       fs->major_version = parse_unsigned_int (g, major);
503       free (major);
504       if (fs->major_version == -1)
505         return -1;
506     }
507     else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) ||
508              match2 (g, fs->product_name, re_rhel, &major, &minor)) {
509       fs->distro = OS_DISTRO_RHEL;
510       fs->major_version = parse_unsigned_int (g, major);
511       free (major);
512       if (fs->major_version == -1) {
513         free (minor);
514         return -1;
515       }
516       fs->minor_version = parse_unsigned_int (g, minor);
517       free (minor);
518       if (fs->minor_version == -1)
519         return -1;
520     }
521     else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) {
522       fs->distro = OS_DISTRO_RHEL;
523       fs->major_version = parse_unsigned_int (g, major);
524       free (major);
525       if (fs->major_version == -1)
526         return -1;
527       fs->minor_version = 0;
528     }
529   }
530   else if (guestfs_exists (g, "/etc/debian_version") > 0) {
531     fs->distro = OS_DISTRO_DEBIAN;
532
533     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
534       return -1;
535
536     if (parse_major_minor (g, fs) == -1)
537       return -1;
538   }
539   else if (guestfs_exists (g, "/etc/pardus-release") > 0) {
540     fs->distro = OS_DISTRO_PARDUS;
541
542     if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
543       return -1;
544
545     if (parse_major_minor (g, fs) == -1)
546       return -1;
547   }
548   else if (guestfs_exists (g, "/etc/arch-release") > 0) {
549     fs->distro = OS_DISTRO_ARCHLINUX;
550
551     /* /etc/arch-release file is empty and I can't see a way to
552      * determine the actual release or product string.
553      */
554   }
555   else if (guestfs_exists (g, "/etc/gentoo-release") > 0) {
556     fs->distro = OS_DISTRO_GENTOO;
557
558     if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
559       return -1;
560
561     if (parse_major_minor (g, fs) == -1)
562       return -1;
563   }
564   else if (guestfs_exists (g, "/etc/meego-release") > 0) {
565     fs->distro = OS_DISTRO_MEEGO;
566
567     if (parse_release_file (g, fs, "/etc/meego-release") == -1)
568       return -1;
569
570     if (parse_major_minor (g, fs) == -1)
571       return -1;
572   }
573
574  skip_release_checks:;
575
576   /* If distro test above was successful, work out the package format. */
577   check_package_format (g, fs);
578   check_package_management (g, fs);
579
580   /* Determine the architecture. */
581   check_architecture (g, fs);
582
583   /* We already know /etc/fstab exists because it's part of the test
584    * for Linux root above.  We must now parse this file to determine
585    * which filesystems are used by the operating system and how they
586    * are mounted.
587    */
588   if (check_fstab (g, fs) == -1)
589     return -1;
590
591   return 0;
592 }
593
594 /* The currently mounted device is known to be a FreeBSD root. */
595 static int
596 check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
597 {
598   int r;
599
600   fs->type = OS_TYPE_FREEBSD;
601
602   /* FreeBSD has no authoritative version file.  The version number is
603    * in /etc/motd, which the system administrator might edit, but
604    * we'll use that anyway.
605    */
606
607   if (guestfs_exists (g, "/etc/motd") > 0) {
608     if (parse_release_file (g, fs, "/etc/motd") == -1)
609       return -1;
610
611     if (parse_major_minor (g, fs) == -1)
612       return -1;
613   }
614
615   /* Determine the architecture. */
616   check_architecture (g, fs);
617
618   /* We already know /etc/fstab exists because it's part of the test above. */
619   if (check_fstab (g, fs) == -1)
620     return -1;
621
622   return 0;
623 }
624
625 static void
626 check_architecture (guestfs_h *g, struct inspect_fs *fs)
627 {
628   const char *binaries[] =
629     { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" };
630   size_t i;
631
632   for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
633     if (guestfs_is_file (g, binaries[i]) > 0) {
634       /* Ignore errors from file_architecture call. */
635       guestfs_error_handler_cb old_error_cb = g->error_cb;
636       g->error_cb = NULL;
637       char *arch = guestfs_file_architecture (g, binaries[i]);
638       g->error_cb = old_error_cb;
639
640       if (arch) {
641         /* String will be owned by handle, freed by
642          * guestfs___free_inspect_info.
643          */
644         fs->arch = arch;
645         break;
646       }
647     }
648   }
649 }
650
651 static int check_fstab_aug_open (guestfs_h *g, struct inspect_fs *fs);
652
653 static int
654 check_fstab (guestfs_h *g, struct inspect_fs *fs)
655 {
656   int r;
657   int64_t size;
658
659   /* Security: Refuse to do this if /etc/fstab is huge. */
660   size = guestfs_filesize (g, "/etc/fstab");
661   if (size == -1 || size > 100000) {
662     error (g, _("size of /etc/fstab unreasonable (%" PRIi64 " bytes)"), size);
663     return -1;
664   }
665
666   /* XXX What if !feature_available (g, "augeas")? */
667   if (guestfs_aug_init (g, "/", 16|32) == -1)
668     return -1;
669
670   /* Tell Augeas to only load /etc/fstab (thanks Raphaël Pinson). */
671   guestfs_aug_rm (g, "/augeas/load//incl[. != \"/etc/fstab\"]");
672   guestfs_aug_load (g);
673
674   r = check_fstab_aug_open (g, fs);
675   guestfs_aug_close (g);
676   if (r == -1)
677     return -1;
678
679   return 0;
680 }
681
682 static int
683 check_fstab_aug_open (guestfs_h *g, struct inspect_fs *fs)
684 {
685   char **lines = guestfs_aug_ls (g, "/files/etc/fstab");
686   if (lines == NULL)
687     return -1;
688
689   if (lines[0] == NULL) {
690     error (g, _("could not parse /etc/fstab or empty file"));
691     guestfs___free_string_list (lines);
692     return -1;
693   }
694
695   size_t i;
696   char augpath[256];
697   for (i = 0; lines[i] != NULL; ++i) {
698     /* Ignore comments.  Only care about sequence lines which
699      * match m{/\d+$}.
700      */
701     if (match (g, lines[i], re_aug_seq)) {
702       snprintf (augpath, sizeof augpath, "%s/spec", lines[i]);
703       char *spec = guestfs_aug_get (g, augpath);
704       if (spec == NULL) {
705         guestfs___free_string_list (lines);
706         return -1;
707       }
708
709       snprintf (augpath, sizeof augpath, "%s/file", lines[i]);
710       char *mp = guestfs_aug_get (g, augpath);
711       if (mp == NULL) {
712         guestfs___free_string_list (lines);
713         free (spec);
714         return -1;
715       }
716
717       int r = add_fstab_entry (g, fs, spec, mp);
718       free (spec);
719       free (mp);
720
721       if (r == -1) {
722         guestfs___free_string_list (lines);
723         return -1;
724       }
725     }
726   }
727
728   guestfs___free_string_list (lines);
729   return 0;
730 }
731
732 /* Add a filesystem and possibly a mountpoint entry for
733  * the root filesystem 'fs'.
734  *
735  * 'spec' is the fstab spec field, which might be a device name or a
736  * pseudodevice or 'UUID=...' or 'LABEL=...'.
737  *
738  * 'mp' is the mount point, which could also be 'swap' or 'none'.
739  */
740 static int
741 add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
742                  const char *spec, const char *mp)
743 {
744   /* Ignore certain mountpoints. */
745   if (STRPREFIX (mp, "/dev/") ||
746       STREQ (mp, "/dev") ||
747       STRPREFIX (mp, "/media/") ||
748       STRPREFIX (mp, "/proc/") ||
749       STREQ (mp, "/proc") ||
750       STRPREFIX (mp, "/selinux/") ||
751       STREQ (mp, "/selinux") ||
752       STRPREFIX (mp, "/sys/") ||
753       STREQ (mp, "/sys"))
754     return 0;
755
756   /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */
757   if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) ||
758       STREQ (spec, "/dev/floppy") ||
759       STREQ (spec, "/dev/cdrom"))
760     return 0;
761
762   /* Resolve UUID= and LABEL= to the actual device. */
763   char *device = NULL;
764   if (STRPREFIX (spec, "UUID="))
765     device = guestfs_findfs_uuid (g, &spec[5]);
766   else if (STRPREFIX (spec, "LABEL="))
767     device = guestfs_findfs_label (g, &spec[6]);
768   /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */
769   else if (STRPREFIX (spec, "/dev/"))
770     /* Resolve guest block device names. */
771     device = resolve_fstab_device (g, spec);
772
773   /* If we haven't resolved the device successfully by this point,
774    * we don't care, just ignore it.
775    */
776   if (device == NULL)
777     return 0;
778
779   char *mountpoint = safe_strdup (g, mp);
780
781   /* Add this to the fstab entry in 'fs'.
782    * Note these are further filtered by guestfs_inspect_get_mountpoints
783    * and guestfs_inspect_get_filesystems.
784    */
785   size_t n = fs->nr_fstab + 1;
786   struct inspect_fstab_entry *p;
787
788   p = realloc (fs->fstab, n * sizeof (struct inspect_fstab_entry));
789   if (p == NULL) {
790     perrorf (g, "realloc");
791     free (device);
792     free (mountpoint);
793     return -1;
794   }
795
796   fs->fstab = p;
797   fs->nr_fstab = n;
798
799   /* These are owned by the handle and freed by guestfs___free_inspect_info. */
800   fs->fstab[n-1].device = device;
801   fs->fstab[n-1].mountpoint = mountpoint;
802
803   if (g->verbose)
804     fprintf (stderr, "fstab: device=%s mountpoint=%s\n", device, mountpoint);
805
806   return 0;
807 }
808
809 /* Resolve block device name to the libguestfs device name, eg.
810  * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV.  This
811  * assumes that disks were added in the same order as they appear to
812  * the real VM, which is a reasonable assumption to make.  Return
813  * anything we don't recognize unchanged.
814  */
815 static char *
816 resolve_fstab_device (guestfs_h *g, const char *spec)
817 {
818   char *a1;
819   char *device = NULL;
820   char *bsddisk, *bsdslice, *bsdpart;
821
822   if (STRPREFIX (spec, "/dev/mapper/")) {
823     /* LVM2 does some strange munging on /dev/mapper paths for VGs and
824      * LVs which contain '-' character:
825      *
826      * ><fs> lvcreate LV--test VG--test 32
827      * ><fs> debug ls /dev/mapper
828      * VG----test-LV----test
829      *
830      * This makes it impossible to reverse those paths directly, so
831      * we have implemented lvm_canonical_lv_name in the daemon.
832      */
833     device = guestfs_lvm_canonical_lv_name (g, spec);
834   }
835   else if ((a1 = match1 (g, spec, re_xdev)) != NULL) {
836     char **devices = guestfs_list_devices (g);
837     if (devices == NULL)
838       return NULL;
839
840     size_t count;
841     for (count = 0; devices[count] != NULL; count++)
842       ;
843
844     size_t i = a1[0] - 'a'; /* a1[0] is always [a-z] because of regex. */
845     if (i < count) {
846       size_t len = strlen (devices[i]) + strlen (a1) + 16;
847       device = safe_malloc (g, len);
848       snprintf (device, len, "%s%s", devices[i], &a1[1]);
849     }
850
851     free (a1);
852     guestfs___free_string_list (devices);
853   }
854   else if (match3 (g, spec, re_freebsd, &bsddisk, &bsdslice, &bsdpart)) {
855     /* FreeBSD disks are organized quite differently.  See:
856      * http://www.freebsd.org/doc/handbook/disk-organization.html
857      * FreeBSD "partitions" are exposed as quasi-extended partitions
858      * numbered from 5 in Linux.  I have no idea what happens when you
859      * have multiple "slices" (the FreeBSD term for MBR partitions).
860      */
861     int disk = parse_unsigned_int (g, bsddisk);
862     int slice = parse_unsigned_int (g, bsdslice);
863     int part = bsdpart[0] - 'a' /* counting from 0 */;
864     free (bsddisk);
865     free (bsdslice);
866     free (bsdpart);
867
868     if (disk == -1 || disk > 26 ||
869         slice <= 0 || slice > 1 /* > 4 .. see comment above */ ||
870         part < 0 || part >= 26)
871       goto out;
872
873     device = safe_asprintf (g, "/dev/sd%c%d", disk + 'a', part + 5);
874   }
875
876  out:
877   /* Didn't match device pattern, return original spec unchanged. */
878   if (device == NULL)
879     device = safe_strdup (g, spec);
880
881   return device;
882 }
883
884 /* XXX Handling of boot.ini in the Perl version was pretty broken.  It
885  * essentially didn't do anything for modern Windows guests.
886  * Therefore I've omitted all that code.
887  */
888 static int
889 check_windows_root (guestfs_h *g, struct inspect_fs *fs)
890 {
891   fs->type = OS_TYPE_WINDOWS;
892   fs->distro = OS_DISTRO_WINDOWS;
893
894   /* Try to find Windows systemroot using some common locations. */
895   const char *systemroots[] =
896     { "/windows", "/winnt", "/win32", "/win" };
897   size_t i;
898   char *systemroot = NULL;
899   for (i = 0;
900        systemroot == NULL && i < sizeof systemroots / sizeof systemroots[0];
901        ++i) {
902     systemroot = resolve_windows_path_silently (g, systemroots[i]);
903   }
904
905   if (!systemroot) {
906     error (g, _("cannot resolve Windows %%SYSTEMROOT%%"));
907     return -1;
908   }
909
910   if (g->verbose)
911     fprintf (stderr, "windows %%SYSTEMROOT%% = %s", systemroot);
912
913   /* Freed by guestfs___free_inspect_info. */
914   fs->windows_systemroot = systemroot;
915
916   if (check_windows_arch (g, fs) == -1)
917     return -1;
918
919   if (check_windows_registry (g, fs) == -1)
920     return -1;
921
922   check_package_format (g, fs);
923   check_package_management (g, fs);
924
925   return 0;
926 }
927
928 static int
929 check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
930 {
931   size_t len = strlen (fs->windows_systemroot) + 32;
932   char cmd_exe[len];
933   snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
934
935   char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe);
936   if (!cmd_exe_path)
937     return 0;
938
939   char *arch = guestfs_file_architecture (g, cmd_exe_path);
940   free (cmd_exe_path);
941
942   if (arch)
943     fs->arch = arch;        /* freed by guestfs___free_inspect_info */
944
945   return 0;
946 }
947
948 /* At the moment, pull just the ProductName and version numbers from
949  * the registry.  In future there is a case for making many more
950  * registry fields available to callers.
951  */
952 static int
953 check_windows_registry (guestfs_h *g, struct inspect_fs *fs)
954 {
955   TMP_TEMPLATE_ON_STACK (dir);
956 #define dir_len (strlen (dir))
957 #define software_hive_len (dir_len + 16)
958   char software_hive[software_hive_len];
959 #define cmd_len (dir_len + 16)
960   char cmd[cmd_len];
961
962   size_t len = strlen (fs->windows_systemroot) + 64;
963   char software[len];
964   snprintf (software, len, "%s/system32/config/software",
965             fs->windows_systemroot);
966
967   char *software_path = resolve_windows_path_silently (g, software);
968   if (!software_path)
969     /* If the software hive doesn't exist, just accept that we cannot
970      * find product_name etc.
971      */
972     return 0;
973
974   int ret = -1;
975   hive_h *h = NULL;
976   hive_value_h *values = NULL;
977
978   /* Security: Refuse to download registry if it is huge. */
979   int64_t size = guestfs_filesize (g, software_path);
980   if (size == -1 || size > 100000000) {
981     error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"),
982            software_path, size);
983     goto out;
984   }
985
986   if (mkdtemp (dir) == NULL) {
987     perrorf (g, "mkdtemp");
988     goto out;
989   }
990
991   snprintf (software_hive, software_hive_len, "%s/software", dir);
992
993   if (guestfs_download (g, software_path, software_hive) == -1)
994     goto out;
995
996   h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
997   if (h == NULL) {
998     perrorf (g, "hivex_open");
999     goto out;
1000   }
1001
1002   hive_node_h node = hivex_root (h);
1003   const char *hivepath[] =
1004     { "Microsoft", "Windows NT", "CurrentVersion" };
1005   size_t i;
1006   for (i = 0;
1007        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
1008        ++i) {
1009     node = hivex_node_get_child (h, node, hivepath[i]);
1010   }
1011
1012   if (node == 0) {
1013     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
1014     goto out;
1015   }
1016
1017   values = hivex_node_values (h, node);
1018
1019   for (i = 0; values[i] != 0; ++i) {
1020     char *key = hivex_value_key (h, values[i]);
1021     if (key == NULL) {
1022       perrorf (g, "hivex_value_key");
1023       goto out;
1024     }
1025
1026     if (STRCASEEQ (key, "ProductName")) {
1027       fs->product_name = hivex_value_string (h, values[i]);
1028       if (!fs->product_name) {
1029         perrorf (g, "hivex_value_string");
1030         free (key);
1031         goto out;
1032       }
1033     }
1034     else if (STRCASEEQ (key, "CurrentVersion")) {
1035       char *version = hivex_value_string (h, values[i]);
1036       if (!version) {
1037         perrorf (g, "hivex_value_string");
1038         free (key);
1039         goto out;
1040       }
1041       char *major, *minor;
1042       if (match2 (g, version, re_windows_version, &major, &minor)) {
1043         fs->major_version = parse_unsigned_int (g, major);
1044         free (major);
1045         if (fs->major_version == -1) {
1046           free (minor);
1047           free (key);
1048           free (version);
1049           goto out;
1050         }
1051         fs->minor_version = parse_unsigned_int (g, minor);
1052         free (minor);
1053         if (fs->minor_version == -1) {
1054           free (key);
1055           free (version);
1056           goto out;
1057         }
1058       }
1059
1060       free (version);
1061     }
1062
1063     free (key);
1064   }
1065
1066   ret = 0;
1067
1068  out:
1069   if (h) hivex_close (h);
1070   free (values);
1071   free (software_path);
1072
1073   /* Free up the temporary directory.  Note the directory name cannot
1074    * contain shell meta-characters because of the way it was
1075    * constructed above.
1076    */
1077   snprintf (cmd, cmd_len, "rm -rf %s", dir);
1078   ignore_value (system (cmd));
1079 #undef dir_len
1080 #undef software_hive_len
1081 #undef cmd_len
1082
1083   return ret;
1084 }
1085
1086 static char *
1087 resolve_windows_path_silently (guestfs_h *g, const char *path)
1088 {
1089   guestfs_error_handler_cb old_error_cb = g->error_cb;
1090   g->error_cb = NULL;
1091   char *ret = guestfs_case_sensitive_path (g, path);
1092   g->error_cb = old_error_cb;
1093   return ret;
1094 }
1095
1096 static int
1097 extend_fses (guestfs_h *g)
1098 {
1099   size_t n = g->nr_fses + 1;
1100   struct inspect_fs *p;
1101
1102   p = realloc (g->fses, n * sizeof (struct inspect_fs));
1103   if (p == NULL) {
1104     perrorf (g, "realloc");
1105     return -1;
1106   }
1107
1108   g->fses = p;
1109   g->nr_fses = n;
1110
1111   memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
1112
1113   return 0;
1114 }
1115
1116 /* Parse small, unsigned ints, as used in version numbers. */
1117 static int
1118 parse_unsigned_int (guestfs_h *g, const char *str)
1119 {
1120   long ret;
1121   int r = xstrtol (str, NULL, 10, &ret, "");
1122   if (r != LONGINT_OK) {
1123     error (g, _("could not parse integer in version number: %s"), str);
1124     return -1;
1125   }
1126   return ret;
1127 }
1128
1129 /* At the moment, package format and package management is just a
1130  * simple function of the distro and major_version fields, so these
1131  * can never return an error.  We might be cleverer in future.
1132  */
1133 static void
1134 check_package_format (guestfs_h *g, struct inspect_fs *fs)
1135 {
1136   switch (fs->distro) {
1137   case OS_DISTRO_FEDORA:
1138   case OS_DISTRO_MEEGO:
1139   case OS_DISTRO_REDHAT_BASED:
1140   case OS_DISTRO_RHEL:
1141   case OS_DISTRO_MANDRIVA:
1142     fs->package_format = OS_PACKAGE_FORMAT_RPM;
1143     break;
1144
1145   case OS_DISTRO_DEBIAN:
1146   case OS_DISTRO_UBUNTU:
1147   case OS_DISTRO_LINUX_MINT:
1148     fs->package_format = OS_PACKAGE_FORMAT_DEB;
1149     break;
1150
1151   case OS_DISTRO_ARCHLINUX:
1152     fs->package_format = OS_PACKAGE_FORMAT_PACMAN;
1153     break;
1154   case OS_DISTRO_GENTOO:
1155     fs->package_format = OS_PACKAGE_FORMAT_EBUILD;
1156     break;
1157   case OS_DISTRO_PARDUS:
1158     fs->package_format = OS_PACKAGE_FORMAT_PISI;
1159     break;
1160
1161   case OS_DISTRO_WINDOWS:
1162   case OS_DISTRO_UNKNOWN:
1163   default:
1164     fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN;
1165     break;
1166   }
1167 }
1168
1169 static void
1170 check_package_management (guestfs_h *g, struct inspect_fs *fs)
1171 {
1172   switch (fs->distro) {
1173   case OS_DISTRO_FEDORA:
1174   case OS_DISTRO_MEEGO:
1175     fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1176     break;
1177
1178   case OS_DISTRO_REDHAT_BASED:
1179   case OS_DISTRO_RHEL:
1180     if (fs->major_version >= 5)
1181       fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1182     else
1183       fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE;
1184     break;
1185
1186   case OS_DISTRO_DEBIAN:
1187   case OS_DISTRO_UBUNTU:
1188   case OS_DISTRO_LINUX_MINT:
1189     fs->package_management = OS_PACKAGE_MANAGEMENT_APT;
1190     break;
1191
1192   case OS_DISTRO_ARCHLINUX:
1193     fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN;
1194     break;
1195   case OS_DISTRO_GENTOO:
1196     fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE;
1197     break;
1198   case OS_DISTRO_PARDUS:
1199     fs->package_management = OS_PACKAGE_MANAGEMENT_PISI;
1200     break;
1201   case OS_DISTRO_MANDRIVA:
1202     fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI;
1203     break;
1204
1205   case OS_DISTRO_WINDOWS:
1206   case OS_DISTRO_UNKNOWN:
1207   default:
1208     fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
1209     break;
1210   }
1211 }
1212
1213 static struct inspect_fs *
1214 search_for_root (guestfs_h *g, const char *root)
1215 {
1216   if (g->nr_fses == 0) {
1217     error (g, _("no inspection data: call guestfs_inspect_os first"));
1218     return NULL;
1219   }
1220
1221   size_t i;
1222   struct inspect_fs *fs;
1223   for (i = 0; i < g->nr_fses; ++i) {
1224     fs = &g->fses[i];
1225     if (fs->is_root && STREQ (root, fs->device))
1226       return fs;
1227   }
1228
1229   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
1230          root);
1231   return NULL;
1232 }
1233
1234 char **
1235 guestfs__inspect_get_roots (guestfs_h *g)
1236 {
1237   /* NB. Doesn't matter if g->nr_fses == 0.  We just return an empty
1238    * list in this case.
1239    */
1240
1241   size_t i;
1242   size_t count = 0;
1243   for (i = 0; i < g->nr_fses; ++i)
1244     if (g->fses[i].is_root)
1245       count++;
1246
1247   char **ret = calloc (count+1, sizeof (char *));
1248   if (ret == NULL) {
1249     perrorf (g, "calloc");
1250     return NULL;
1251   }
1252
1253   count = 0;
1254   for (i = 0; i < g->nr_fses; ++i) {
1255     if (g->fses[i].is_root) {
1256       ret[count] = safe_strdup (g, g->fses[i].device);
1257       count++;
1258     }
1259   }
1260   ret[count] = NULL;
1261
1262   return ret;
1263 }
1264
1265 char *
1266 guestfs__inspect_get_type (guestfs_h *g, const char *root)
1267 {
1268   struct inspect_fs *fs = search_for_root (g, root);
1269   if (!fs)
1270     return NULL;
1271
1272   char *ret;
1273   switch (fs->type) {
1274   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
1275   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
1276   case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
1277   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1278   }
1279
1280   return ret;
1281 }
1282
1283 char *
1284 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1285 {
1286   struct inspect_fs *fs = search_for_root (g, root);
1287   if (!fs)
1288     return NULL;
1289
1290   return safe_strdup (g, fs->arch ? : "unknown");
1291 }
1292
1293 char *
1294 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1295 {
1296   struct inspect_fs *fs = search_for_root (g, root);
1297   if (!fs)
1298     return NULL;
1299
1300   char *ret;
1301   switch (fs->distro) {
1302   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
1303   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
1304   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
1305   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
1306   case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
1307   case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
1308   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
1309   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
1310   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
1311   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
1312   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
1313   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
1314   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1315   }
1316
1317   return ret;
1318 }
1319
1320 int
1321 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1322 {
1323   struct inspect_fs *fs = search_for_root (g, root);
1324   if (!fs)
1325     return -1;
1326
1327   return fs->major_version;
1328 }
1329
1330 int
1331 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1332 {
1333   struct inspect_fs *fs = search_for_root (g, root);
1334   if (!fs)
1335     return -1;
1336
1337   return fs->minor_version;
1338 }
1339
1340 char *
1341 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1342 {
1343   struct inspect_fs *fs = search_for_root (g, root);
1344   if (!fs)
1345     return NULL;
1346
1347   return safe_strdup (g, fs->product_name ? : "unknown");
1348 }
1349
1350 char *
1351 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1352 {
1353   struct inspect_fs *fs = search_for_root (g, root);
1354   if (!fs)
1355     return NULL;
1356
1357   if (!fs->windows_systemroot) {
1358     error (g, _("not a Windows guest, or systemroot could not be determined"));
1359     return NULL;
1360   }
1361
1362   return safe_strdup (g, fs->windows_systemroot);
1363 }
1364
1365 char **
1366 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1367 {
1368   struct inspect_fs *fs = search_for_root (g, root);
1369   if (!fs)
1370     return NULL;
1371
1372   char **ret;
1373
1374   /* If no fstab information (Windows) return just the root. */
1375   if (fs->nr_fstab == 0) {
1376     ret = calloc (3, sizeof (char *));
1377     ret[0] = safe_strdup (g, "/");
1378     ret[1] = safe_strdup (g, root);
1379     ret[2] = NULL;
1380     return ret;
1381   }
1382
1383 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
1384   size_t i, count = 0;
1385   for (i = 0; i < fs->nr_fstab; ++i)
1386     if (CRITERION)
1387       count++;
1388
1389   /* Hashtables have 2N+1 entries. */
1390   ret = calloc (2*count+1, sizeof (char *));
1391   if (ret == NULL) {
1392     perrorf (g, "calloc");
1393     return NULL;
1394   }
1395
1396   count = 0;
1397   for (i = 0; i < fs->nr_fstab; ++i)
1398     if (CRITERION) {
1399       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
1400       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
1401       count++;
1402     }
1403 #undef CRITERION
1404
1405   return ret;
1406 }
1407
1408 char **
1409 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
1410 {
1411   struct inspect_fs *fs = search_for_root (g, root);
1412   if (!fs)
1413     return NULL;
1414
1415   char **ret;
1416
1417   /* If no fstab information (Windows) return just the root. */
1418   if (fs->nr_fstab == 0) {
1419     ret = calloc (2, sizeof (char *));
1420     ret[0] = safe_strdup (g, root);
1421     ret[1] = NULL;
1422     return ret;
1423   }
1424
1425   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
1426   if (ret == NULL) {
1427     perrorf (g, "calloc");
1428     return NULL;
1429   }
1430
1431   size_t i;
1432   for (i = 0; i < fs->nr_fstab; ++i)
1433     ret[i] = safe_strdup (g, fs->fstab[i].device);
1434
1435   return ret;
1436 }
1437
1438 char *
1439 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
1440 {
1441   struct inspect_fs *fs = search_for_root (g, root);
1442   if (!fs)
1443     return NULL;
1444
1445   char *ret;
1446   switch (fs->package_format) {
1447   case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
1448   case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
1449   case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
1450   case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
1451   case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
1452   case OS_PACKAGE_FORMAT_UNKNOWN:
1453   default:
1454     ret = safe_strdup (g, "unknown");
1455     break;
1456   }
1457
1458   return ret;
1459 }
1460
1461 char *
1462 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
1463 {
1464   struct inspect_fs *fs = search_for_root (g, root);
1465   if (!fs)
1466     return NULL;
1467
1468   char *ret;
1469   switch (fs->package_management) {
1470   case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
1471   case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
1472   case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
1473   case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
1474   case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
1475   case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
1476   case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
1477   case OS_PACKAGE_MANAGEMENT_UNKNOWN:
1478   default:
1479     ret = safe_strdup (g, "unknown");
1480     break;
1481   }
1482
1483   return ret;
1484 }
1485
1486 #else /* no PCRE or hivex at compile time */
1487
1488 /* XXX These functions should be in an optgroup. */
1489
1490 #define NOT_IMPL(r)                                                     \
1491   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
1492   return r
1493
1494 char **
1495 guestfs__inspect_os (guestfs_h *g)
1496 {
1497   NOT_IMPL(NULL);
1498 }
1499
1500 char **
1501 guestfs__inspect_get_roots (guestfs_h *g)
1502 {
1503   NOT_IMPL(NULL);
1504 }
1505
1506 char *
1507 guestfs__inspect_get_type (guestfs_h *g, const char *root)
1508 {
1509   NOT_IMPL(NULL);
1510 }
1511
1512 char *
1513 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1514 {
1515   NOT_IMPL(NULL);
1516 }
1517
1518 char *
1519 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1520 {
1521   NOT_IMPL(NULL);
1522 }
1523
1524 int
1525 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1526 {
1527   NOT_IMPL(-1);
1528 }
1529
1530 int
1531 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1532 {
1533   NOT_IMPL(-1);
1534 }
1535
1536 char *
1537 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1538 {
1539   NOT_IMPL(NULL);
1540 }
1541
1542 char *
1543 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1544 {
1545   NOT_IMPL(NULL);
1546 }
1547
1548 char **
1549 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1550 {
1551   NOT_IMPL(NULL);
1552 }
1553
1554 char **
1555 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
1556 {
1557   NOT_IMPL(NULL);
1558 }
1559
1560 char *
1561 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
1562 {
1563   NOT_IMPL(NULL);
1564 }
1565
1566 char *
1567 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
1568 {
1569   NOT_IMPL(NULL);
1570 }
1571
1572 #endif /* no PCRE or hivex at compile time */
1573
1574 void
1575 guestfs___free_inspect_info (guestfs_h *g)
1576 {
1577   size_t i;
1578   for (i = 0; i < g->nr_fses; ++i) {
1579     free (g->fses[i].device);
1580     free (g->fses[i].product_name);
1581     free (g->fses[i].arch);
1582     free (g->fses[i].windows_systemroot);
1583     size_t j;
1584     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
1585       free (g->fses[i].fstab[j].device);
1586       free (g->fses[i].fstab[j].mountpoint);
1587     }
1588     free (g->fses[i].fstab);
1589   }
1590   free (g->fses);
1591   g->nr_fses = 0;
1592   g->fses = NULL;
1593 }
1594
1595 /* In the Perl code this is a public function. */
1596 int
1597 guestfs___feature_available (guestfs_h *g, const char *feature)
1598 {
1599   /* If there's an error we should ignore it, so to do that we have to
1600    * temporarily replace the error handler with a null one.
1601    */
1602   guestfs_error_handler_cb old_error_cb = g->error_cb;
1603   g->error_cb = NULL;
1604
1605   const char *groups[] = { feature, NULL };
1606   int r = guestfs_available (g, (char * const *) groups);
1607
1608   g->error_cb = old_error_cb;
1609
1610   return r == 0 ? 1 : 0;
1611 }
1612
1613 #ifdef HAVE_PCRE
1614
1615 /* Match a regular expression which contains no captures.  Returns
1616  * true if it matches or false if it doesn't.
1617  */
1618 int
1619 guestfs___match (guestfs_h *g, const char *str, const pcre *re)
1620 {
1621   size_t len = strlen (str);
1622   int vec[30], r;
1623
1624   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
1625   if (r == PCRE_ERROR_NOMATCH)
1626     return 0;
1627   if (r != 1) {
1628     /* Internal error -- should not happen. */
1629     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1630              __FILE__, __func__, r, str);
1631     return 0;
1632   }
1633
1634   return 1;
1635 }
1636
1637 /* Match a regular expression which contains exactly one capture.  If
1638  * the string matches, return the capture, otherwise return NULL.  The
1639  * caller must free the result.
1640  */
1641 char *
1642 guestfs___match1 (guestfs_h *g, const char *str, const pcre *re)
1643 {
1644   size_t len = strlen (str);
1645   int vec[30], r;
1646
1647   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
1648   if (r == PCRE_ERROR_NOMATCH)
1649     return NULL;
1650   if (r != 2) {
1651     /* Internal error -- should not happen. */
1652     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1653              __FILE__, __func__, r, str);
1654     return NULL;
1655   }
1656
1657   return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
1658 }
1659
1660 /* Match a regular expression which contains exactly two captures. */
1661 int
1662 guestfs___match2 (guestfs_h *g, const char *str, const pcre *re,
1663                   char **ret1, char **ret2)
1664 {
1665   size_t len = strlen (str);
1666   int vec[30], r;
1667
1668   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
1669   if (r == PCRE_ERROR_NOMATCH)
1670     return 0;
1671   if (r != 3) {
1672     /* Internal error -- should not happen. */
1673     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1674              __FILE__, __func__, r, str);
1675     return 0;
1676   }
1677
1678   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
1679   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
1680
1681   return 1;
1682 }
1683
1684 /* Match a regular expression which contains exactly three captures. */
1685 int
1686 guestfs___match3 (guestfs_h *g, const char *str, const pcre *re,
1687                   char **ret1, char **ret2, char **ret3)
1688 {
1689   size_t len = strlen (str);
1690   int vec[30], r;
1691
1692   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
1693   if (r == PCRE_ERROR_NOMATCH)
1694     return 0;
1695   if (r != 4) {
1696     /* Internal error -- should not happen. */
1697     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1698              __FILE__, __func__, r, str);
1699     return 0;
1700   }
1701
1702   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
1703   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
1704   *ret3 = safe_strndup (g, &str[vec[6]], vec[7]-vec[6]);
1705
1706   return 1;
1707 }
1708
1709 #endif /* HAVE_PCRE */