inspect: Check /etc/lsb-release is not too large before calling head on it.
[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 static int download_to_tmp (guestfs_h *g, const char *filename, char *localtmp, int64_t max_size);
211 static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename, int (*f) (guestfs_h *, struct inspect_fs *));
212 static char *first_line_of_file (guestfs_h *g, const char *filename);
213
214 static int
215 check_for_filesystem_on (guestfs_h *g, const char *device)
216 {
217   /* Get vfs-type in order to check if it's a Linux(?) swap device.
218    * If there's an error we should ignore it, so to do that we have to
219    * temporarily replace the error handler with a null one.
220    */
221   guestfs_error_handler_cb old_error_cb = g->error_cb;
222   g->error_cb = NULL;
223   char *vfs_type = guestfs_vfs_type (g, device);
224   g->error_cb = old_error_cb;
225
226   int is_swap = vfs_type && STREQ (vfs_type, "swap");
227
228   if (g->verbose)
229     fprintf (stderr, "check_for_filesystem_on: %s (%s)\n",
230              device, vfs_type ? vfs_type : "failed to get vfs type");
231
232   if (is_swap) {
233     free (vfs_type);
234     if (extend_fses (g) == -1)
235       return -1;
236     g->fses[g->nr_fses-1].is_swap = 1;
237     return 0;
238   }
239
240   /* Try mounting the device.  As above, ignore errors. */
241   g->error_cb = NULL;
242   int r = guestfs_mount_ro (g, device, "/");
243   if (r == -1 && vfs_type && STREQ (vfs_type, "ufs")) /* Hack for the *BSDs. */
244     r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", device, "/");
245   free (vfs_type);
246   g->error_cb = old_error_cb;
247   if (r == -1)
248     return 0;
249
250   /* Do the rest of the checks. */
251   r = check_filesystem (g, device);
252
253   /* Unmount the filesystem. */
254   if (guestfs_umount_all (g) == -1)
255     return -1;
256
257   return r;
258 }
259
260 static int
261 check_filesystem (guestfs_h *g, const char *device)
262 {
263   if (extend_fses (g) == -1)
264     return -1;
265
266   struct inspect_fs *fs = &g->fses[g->nr_fses-1];
267
268   fs->device = safe_strdup (g, device);
269   fs->is_mountable = 1;
270
271   /* Optimize some of the tests by avoiding multiple tests of the same thing. */
272   int is_dir_etc = guestfs_is_dir (g, "/etc") > 0;
273   int is_dir_bin = guestfs_is_dir (g, "/bin") > 0;
274   int is_dir_share = guestfs_is_dir (g, "/share") > 0;
275
276   /* Grub /boot? */
277   if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
278       guestfs_is_file (g, "/grub/grub.conf") > 0)
279     fs->content = FS_CONTENT_LINUX_BOOT;
280   /* FreeBSD root? */
281   else if (is_dir_etc &&
282            is_dir_bin &&
283            guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 &&
284            guestfs_is_file (g, "/etc/fstab") > 0) {
285     /* Ignore /dev/sda1 which is a shadow of the real root filesystem
286      * that is probably /dev/sda5 (see:
287      * http://www.freebsd.org/doc/handbook/disk-organization.html)
288      */
289     if (match (g, device, re_first_partition))
290       return 0;
291
292     fs->is_root = 1;
293     fs->content = FS_CONTENT_FREEBSD_ROOT;
294     if (check_freebsd_root (g, fs) == -1)
295       return -1;
296   }
297   /* Linux root? */
298   else if (is_dir_etc &&
299            is_dir_bin &&
300            guestfs_is_file (g, "/etc/fstab") > 0) {
301     fs->is_root = 1;
302     fs->content = FS_CONTENT_LINUX_ROOT;
303     if (check_linux_root (g, fs) == -1)
304       return -1;
305   }
306   /* Linux /usr/local? */
307   else if (is_dir_etc &&
308            is_dir_bin &&
309            is_dir_share &&
310            guestfs_exists (g, "/local") == 0 &&
311            guestfs_is_file (g, "/etc/fstab") == 0)
312     fs->content = FS_CONTENT_LINUX_USR_LOCAL;
313   /* Linux /usr? */
314   else if (is_dir_etc &&
315            is_dir_bin &&
316            is_dir_share &&
317            guestfs_exists (g, "/local") > 0 &&
318            guestfs_is_file (g, "/etc/fstab") == 0)
319     fs->content = FS_CONTENT_LINUX_USR;
320   /* Linux /var? */
321   else if (guestfs_is_dir (g, "/log") > 0 &&
322            guestfs_is_dir (g, "/run") > 0 &&
323            guestfs_is_dir (g, "/spool") > 0)
324     fs->content = FS_CONTENT_LINUX_VAR;
325   /* Windows root? */
326   else if (guestfs_is_file (g, "/AUTOEXEC.BAT") > 0 ||
327            guestfs_is_file (g, "/autoexec.bat") > 0 ||
328            guestfs_is_dir (g, "/Program Files") > 0 ||
329            guestfs_is_dir (g, "/WINDOWS") > 0 ||
330            guestfs_is_dir (g, "/Windows") > 0 ||
331            guestfs_is_dir (g, "/windows") > 0 ||
332            guestfs_is_dir (g, "/WIN32") > 0 ||
333            guestfs_is_dir (g, "/Win32") > 0 ||
334            guestfs_is_dir (g, "/WINNT") > 0 ||
335            guestfs_is_file (g, "/boot.ini") > 0 ||
336            guestfs_is_file (g, "/ntldr") > 0) {
337     fs->is_root = 1;
338     fs->content = FS_CONTENT_WINDOWS_ROOT;
339     if (check_windows_root (g, fs) == -1)
340       return -1;
341   }
342
343   return 0;
344 }
345
346 /* Set fs->product_name to the first line of the release file. */
347 static int
348 parse_release_file (guestfs_h *g, struct inspect_fs *fs,
349                     const char *release_filename)
350 {
351   fs->product_name = first_line_of_file (g, release_filename);
352   if (fs->product_name == NULL)
353     return -1;
354   return 0;
355 }
356
357 /* Parse generic MAJOR.MINOR from the fs->product_name string. */
358 static int
359 parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
360 {
361   char *major, *minor;
362
363   if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) {
364     fs->major_version = parse_unsigned_int (g, major);
365     free (major);
366     if (fs->major_version == -1) {
367       free (minor);
368       return -1;
369     }
370     fs->minor_version = parse_unsigned_int (g, minor);
371     free (minor);
372     if (fs->minor_version == -1)
373       return -1;
374   }
375   return 0;
376 }
377
378 /* Ubuntu has /etc/lsb-release containing:
379  *   DISTRIB_ID=Ubuntu                                # Distro
380  *   DISTRIB_RELEASE=10.04                            # Version
381  *   DISTRIB_CODENAME=lucid
382  *   DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"         # Product name
383  *
384  * [Ubuntu-derived ...] Linux Mint was found to have this:
385  *   DISTRIB_ID=LinuxMint
386  *   DISTRIB_RELEASE=10
387  *   DISTRIB_CODENAME=julia
388  *   DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
389  * Linux Mint also has /etc/linuxmint/info with more information,
390  * but we can use the LSB file.
391  *
392  * Mandriva has:
393  *   LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
394  *   DISTRIB_ID=MandrivaLinux
395  *   DISTRIB_RELEASE=2010.1
396  *   DISTRIB_CODENAME=Henry_Farman
397  *   DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
398  * Mandriva also has a normal release file called /etc/mandriva-release.
399  */
400 static int
401 parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
402 {
403   const char *filename = "/etc/lsb-release";
404   int64_t size;
405   char **lines;
406   size_t i;
407   int r = 0;
408
409   /* Don't trust guestfs_head_n not to break with very large files.
410    * Check the file size is something reasonable first.
411    */
412   size = guestfs_filesize (g, filename);
413   if (size == -1)
414     /* guestfs_filesize failed and has already set error in handle */
415     return -1;
416   if (size > 1000000) {
417     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
418            filename, size);
419     return -1;
420   }
421
422   lines = guestfs_head_n (g, 10, filename);
423   if (lines == NULL)
424     return -1;
425
426   for (i = 0; lines[i] != NULL; ++i) {
427     if (fs->distro == 0 &&
428         STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
429       fs->distro = OS_DISTRO_UBUNTU;
430       r = 1;
431     }
432     else if (fs->distro == 0 &&
433              STREQ (lines[i], "DISTRIB_ID=LinuxMint")) {
434       fs->distro = OS_DISTRO_LINUX_MINT;
435       r = 1;
436     }
437     else if (fs->distro == 0 &&
438              STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) {
439       fs->distro = OS_DISTRO_MANDRIVA;
440       r = 1;
441     }
442     else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
443       char *major, *minor;
444       if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) {
445         fs->major_version = parse_unsigned_int (g, major);
446         free (major);
447         if (fs->major_version == -1) {
448           free (minor);
449           guestfs___free_string_list (lines);
450           return -1;
451         }
452         fs->minor_version = parse_unsigned_int (g, minor);
453         free (minor);
454         if (fs->minor_version == -1) {
455           guestfs___free_string_list (lines);
456           return -1;
457         }
458       }
459     }
460     else if (fs->product_name == NULL &&
461              (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
462               STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
463       size_t len = strlen (lines[i]) - 21 - 1;
464       fs->product_name = safe_strndup (g, &lines[i][21], len);
465       r = 1;
466     }
467     else if (fs->product_name == NULL &&
468              STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
469       size_t len = strlen (lines[i]) - 20;
470       fs->product_name = safe_strndup (g, &lines[i][20], len);
471       r = 1;
472     }
473   }
474
475   guestfs___free_string_list (lines);
476   return r;
477 }
478
479 /* The currently mounted device is known to be a Linux root.  Try to
480  * determine from this the distro, version, etc.  Also parse
481  * /etc/fstab to determine the arrangement of mountpoints and
482  * associated devices.
483  */
484 static int
485 check_linux_root (guestfs_h *g, struct inspect_fs *fs)
486 {
487   int r;
488
489   fs->type = OS_TYPE_LINUX;
490
491   if (guestfs_exists (g, "/etc/lsb-release") > 0) {
492     r = parse_lsb_release (g, fs);
493     if (r == -1)        /* error */
494       return -1;
495     if (r == 1)         /* ok - detected the release from this file */
496       goto skip_release_checks;
497   }
498
499   if (guestfs_exists (g, "/etc/redhat-release") > 0) {
500     fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */
501
502     if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
503       return -1;
504
505     char *major, *minor;
506     if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
507       fs->distro = OS_DISTRO_FEDORA;
508       fs->major_version = parse_unsigned_int (g, major);
509       free (major);
510       if (fs->major_version == -1)
511         return -1;
512     }
513     else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) ||
514              match2 (g, fs->product_name, re_rhel, &major, &minor)) {
515       fs->distro = OS_DISTRO_RHEL;
516       fs->major_version = parse_unsigned_int (g, major);
517       free (major);
518       if (fs->major_version == -1) {
519         free (minor);
520         return -1;
521       }
522       fs->minor_version = parse_unsigned_int (g, minor);
523       free (minor);
524       if (fs->minor_version == -1)
525         return -1;
526     }
527     else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) {
528       fs->distro = OS_DISTRO_RHEL;
529       fs->major_version = parse_unsigned_int (g, major);
530       free (major);
531       if (fs->major_version == -1)
532         return -1;
533       fs->minor_version = 0;
534     }
535   }
536   else if (guestfs_exists (g, "/etc/debian_version") > 0) {
537     fs->distro = OS_DISTRO_DEBIAN;
538
539     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
540       return -1;
541
542     if (parse_major_minor (g, fs) == -1)
543       return -1;
544   }
545   else if (guestfs_exists (g, "/etc/pardus-release") > 0) {
546     fs->distro = OS_DISTRO_PARDUS;
547
548     if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
549       return -1;
550
551     if (parse_major_minor (g, fs) == -1)
552       return -1;
553   }
554   else if (guestfs_exists (g, "/etc/arch-release") > 0) {
555     fs->distro = OS_DISTRO_ARCHLINUX;
556
557     /* /etc/arch-release file is empty and I can't see a way to
558      * determine the actual release or product string.
559      */
560   }
561   else if (guestfs_exists (g, "/etc/gentoo-release") > 0) {
562     fs->distro = OS_DISTRO_GENTOO;
563
564     if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
565       return -1;
566
567     if (parse_major_minor (g, fs) == -1)
568       return -1;
569   }
570   else if (guestfs_exists (g, "/etc/meego-release") > 0) {
571     fs->distro = OS_DISTRO_MEEGO;
572
573     if (parse_release_file (g, fs, "/etc/meego-release") == -1)
574       return -1;
575
576     if (parse_major_minor (g, fs) == -1)
577       return -1;
578   }
579
580  skip_release_checks:;
581
582   /* If distro test above was successful, work out the package format. */
583   check_package_format (g, fs);
584   check_package_management (g, fs);
585
586   /* Determine the architecture. */
587   check_architecture (g, fs);
588
589   /* We already know /etc/fstab exists because it's part of the test
590    * for Linux root above.  We must now parse this file to determine
591    * which filesystems are used by the operating system and how they
592    * are mounted.
593    */
594   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
595     return -1;
596
597   return 0;
598 }
599
600 /* The currently mounted device is known to be a FreeBSD root. */
601 static int
602 check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
603 {
604   int r;
605
606   fs->type = OS_TYPE_FREEBSD;
607
608   /* FreeBSD has no authoritative version file.  The version number is
609    * in /etc/motd, which the system administrator might edit, but
610    * we'll use that anyway.
611    */
612
613   if (guestfs_exists (g, "/etc/motd") > 0) {
614     if (parse_release_file (g, fs, "/etc/motd") == -1)
615       return -1;
616
617     if (parse_major_minor (g, fs) == -1)
618       return -1;
619   }
620
621   /* Determine the architecture. */
622   check_architecture (g, fs);
623
624   /* We already know /etc/fstab exists because it's part of the test above. */
625   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
626     return -1;
627
628   return 0;
629 }
630
631 static void
632 check_architecture (guestfs_h *g, struct inspect_fs *fs)
633 {
634   const char *binaries[] =
635     { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" };
636   size_t i;
637
638   for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
639     if (guestfs_is_file (g, binaries[i]) > 0) {
640       /* Ignore errors from file_architecture call. */
641       guestfs_error_handler_cb old_error_cb = g->error_cb;
642       g->error_cb = NULL;
643       char *arch = guestfs_file_architecture (g, binaries[i]);
644       g->error_cb = old_error_cb;
645
646       if (arch) {
647         /* String will be owned by handle, freed by
648          * guestfs___free_inspect_info.
649          */
650         fs->arch = arch;
651         break;
652       }
653     }
654   }
655 }
656
657 static int
658 check_fstab (guestfs_h *g, struct inspect_fs *fs)
659 {
660   char **lines = guestfs_aug_ls (g, "/files/etc/fstab");
661   if (lines == NULL)
662     return -1;
663
664   if (lines[0] == NULL) {
665     error (g, _("could not parse /etc/fstab or empty file"));
666     guestfs___free_string_list (lines);
667     return -1;
668   }
669
670   size_t i;
671   char augpath[256];
672   for (i = 0; lines[i] != NULL; ++i) {
673     /* Ignore comments.  Only care about sequence lines which
674      * match m{/\d+$}.
675      */
676     if (match (g, lines[i], re_aug_seq)) {
677       snprintf (augpath, sizeof augpath, "%s/spec", lines[i]);
678       char *spec = guestfs_aug_get (g, augpath);
679       if (spec == NULL) {
680         guestfs___free_string_list (lines);
681         return -1;
682       }
683
684       snprintf (augpath, sizeof augpath, "%s/file", lines[i]);
685       char *mp = guestfs_aug_get (g, augpath);
686       if (mp == NULL) {
687         guestfs___free_string_list (lines);
688         free (spec);
689         return -1;
690       }
691
692       int r = add_fstab_entry (g, fs, spec, mp);
693       free (spec);
694       free (mp);
695
696       if (r == -1) {
697         guestfs___free_string_list (lines);
698         return -1;
699       }
700     }
701   }
702
703   guestfs___free_string_list (lines);
704   return 0;
705 }
706
707 /* Add a filesystem and possibly a mountpoint entry for
708  * the root filesystem 'fs'.
709  *
710  * 'spec' is the fstab spec field, which might be a device name or a
711  * pseudodevice or 'UUID=...' or 'LABEL=...'.
712  *
713  * 'mp' is the mount point, which could also be 'swap' or 'none'.
714  */
715 static int
716 add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
717                  const char *spec, const char *mp)
718 {
719   /* Ignore certain mountpoints. */
720   if (STRPREFIX (mp, "/dev/") ||
721       STREQ (mp, "/dev") ||
722       STRPREFIX (mp, "/media/") ||
723       STRPREFIX (mp, "/proc/") ||
724       STREQ (mp, "/proc") ||
725       STRPREFIX (mp, "/selinux/") ||
726       STREQ (mp, "/selinux") ||
727       STRPREFIX (mp, "/sys/") ||
728       STREQ (mp, "/sys"))
729     return 0;
730
731   /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */
732   if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) ||
733       STREQ (spec, "/dev/floppy") ||
734       STREQ (spec, "/dev/cdrom"))
735     return 0;
736
737   /* Resolve UUID= and LABEL= to the actual device. */
738   char *device = NULL;
739   if (STRPREFIX (spec, "UUID="))
740     device = guestfs_findfs_uuid (g, &spec[5]);
741   else if (STRPREFIX (spec, "LABEL="))
742     device = guestfs_findfs_label (g, &spec[6]);
743   /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */
744   else if (STRPREFIX (spec, "/dev/"))
745     /* Resolve guest block device names. */
746     device = resolve_fstab_device (g, spec);
747
748   /* If we haven't resolved the device successfully by this point,
749    * we don't care, just ignore it.
750    */
751   if (device == NULL)
752     return 0;
753
754   char *mountpoint = safe_strdup (g, mp);
755
756   /* Add this to the fstab entry in 'fs'.
757    * Note these are further filtered by guestfs_inspect_get_mountpoints
758    * and guestfs_inspect_get_filesystems.
759    */
760   size_t n = fs->nr_fstab + 1;
761   struct inspect_fstab_entry *p;
762
763   p = realloc (fs->fstab, n * sizeof (struct inspect_fstab_entry));
764   if (p == NULL) {
765     perrorf (g, "realloc");
766     free (device);
767     free (mountpoint);
768     return -1;
769   }
770
771   fs->fstab = p;
772   fs->nr_fstab = n;
773
774   /* These are owned by the handle and freed by guestfs___free_inspect_info. */
775   fs->fstab[n-1].device = device;
776   fs->fstab[n-1].mountpoint = mountpoint;
777
778   if (g->verbose)
779     fprintf (stderr, "fstab: device=%s mountpoint=%s\n", device, mountpoint);
780
781   return 0;
782 }
783
784 /* Resolve block device name to the libguestfs device name, eg.
785  * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV.  This
786  * assumes that disks were added in the same order as they appear to
787  * the real VM, which is a reasonable assumption to make.  Return
788  * anything we don't recognize unchanged.
789  */
790 static char *
791 resolve_fstab_device (guestfs_h *g, const char *spec)
792 {
793   char *a1;
794   char *device = NULL;
795   char *bsddisk, *bsdslice, *bsdpart;
796
797   if (STRPREFIX (spec, "/dev/mapper/")) {
798     /* LVM2 does some strange munging on /dev/mapper paths for VGs and
799      * LVs which contain '-' character:
800      *
801      * ><fs> lvcreate LV--test VG--test 32
802      * ><fs> debug ls /dev/mapper
803      * VG----test-LV----test
804      *
805      * This makes it impossible to reverse those paths directly, so
806      * we have implemented lvm_canonical_lv_name in the daemon.
807      */
808     device = guestfs_lvm_canonical_lv_name (g, spec);
809   }
810   else if ((a1 = match1 (g, spec, re_xdev)) != NULL) {
811     char **devices = guestfs_list_devices (g);
812     if (devices == NULL)
813       return NULL;
814
815     size_t count;
816     for (count = 0; devices[count] != NULL; count++)
817       ;
818
819     size_t i = a1[0] - 'a'; /* a1[0] is always [a-z] because of regex. */
820     if (i < count) {
821       size_t len = strlen (devices[i]) + strlen (a1) + 16;
822       device = safe_malloc (g, len);
823       snprintf (device, len, "%s%s", devices[i], &a1[1]);
824     }
825
826     free (a1);
827     guestfs___free_string_list (devices);
828   }
829   else if (match3 (g, spec, re_freebsd, &bsddisk, &bsdslice, &bsdpart)) {
830     /* FreeBSD disks are organized quite differently.  See:
831      * http://www.freebsd.org/doc/handbook/disk-organization.html
832      * FreeBSD "partitions" are exposed as quasi-extended partitions
833      * numbered from 5 in Linux.  I have no idea what happens when you
834      * have multiple "slices" (the FreeBSD term for MBR partitions).
835      */
836     int disk = parse_unsigned_int (g, bsddisk);
837     int slice = parse_unsigned_int (g, bsdslice);
838     int part = bsdpart[0] - 'a' /* counting from 0 */;
839     free (bsddisk);
840     free (bsdslice);
841     free (bsdpart);
842
843     if (disk == -1 || disk > 26 ||
844         slice <= 0 || slice > 1 /* > 4 .. see comment above */ ||
845         part < 0 || part >= 26)
846       goto out;
847
848     device = safe_asprintf (g, "/dev/sd%c%d", disk + 'a', part + 5);
849   }
850
851  out:
852   /* Didn't match device pattern, return original spec unchanged. */
853   if (device == NULL)
854     device = safe_strdup (g, spec);
855
856   return device;
857 }
858
859 /* XXX Handling of boot.ini in the Perl version was pretty broken.  It
860  * essentially didn't do anything for modern Windows guests.
861  * Therefore I've omitted all that code.
862  */
863 static int
864 check_windows_root (guestfs_h *g, struct inspect_fs *fs)
865 {
866   fs->type = OS_TYPE_WINDOWS;
867   fs->distro = OS_DISTRO_WINDOWS;
868
869   /* Try to find Windows systemroot using some common locations. */
870   const char *systemroots[] =
871     { "/windows", "/winnt", "/win32", "/win" };
872   size_t i;
873   char *systemroot = NULL;
874   for (i = 0;
875        systemroot == NULL && i < sizeof systemroots / sizeof systemroots[0];
876        ++i) {
877     systemroot = resolve_windows_path_silently (g, systemroots[i]);
878   }
879
880   if (!systemroot) {
881     error (g, _("cannot resolve Windows %%SYSTEMROOT%%"));
882     return -1;
883   }
884
885   if (g->verbose)
886     fprintf (stderr, "windows %%SYSTEMROOT%% = %s", systemroot);
887
888   /* Freed by guestfs___free_inspect_info. */
889   fs->windows_systemroot = systemroot;
890
891   if (check_windows_arch (g, fs) == -1)
892     return -1;
893
894   if (check_windows_registry (g, fs) == -1)
895     return -1;
896
897   check_package_format (g, fs);
898   check_package_management (g, fs);
899
900   return 0;
901 }
902
903 static int
904 check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
905 {
906   size_t len = strlen (fs->windows_systemroot) + 32;
907   char cmd_exe[len];
908   snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
909
910   char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe);
911   if (!cmd_exe_path)
912     return 0;
913
914   char *arch = guestfs_file_architecture (g, cmd_exe_path);
915   free (cmd_exe_path);
916
917   if (arch)
918     fs->arch = arch;        /* freed by guestfs___free_inspect_info */
919
920   return 0;
921 }
922
923 /* At the moment, pull just the ProductName and version numbers from
924  * the registry.  In future there is a case for making many more
925  * registry fields available to callers.
926  */
927 static int
928 check_windows_registry (guestfs_h *g, struct inspect_fs *fs)
929 {
930   TMP_TEMPLATE_ON_STACK (software_local);
931
932   size_t len = strlen (fs->windows_systemroot) + 64;
933   char software[len];
934   snprintf (software, len, "%s/system32/config/software",
935             fs->windows_systemroot);
936
937   char *software_path = resolve_windows_path_silently (g, software);
938   if (!software_path)
939     /* If the software hive doesn't exist, just accept that we cannot
940      * find product_name etc.
941      */
942     return 0;
943
944   int ret = -1;
945   hive_h *h = NULL;
946   hive_value_h *values = NULL;
947
948   if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
949     goto out;
950
951   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
952   if (h == NULL) {
953     perrorf (g, "hivex_open");
954     goto out;
955   }
956
957   hive_node_h node = hivex_root (h);
958   const char *hivepath[] =
959     { "Microsoft", "Windows NT", "CurrentVersion" };
960   size_t i;
961   for (i = 0;
962        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
963        ++i) {
964     node = hivex_node_get_child (h, node, hivepath[i]);
965   }
966
967   if (node == 0) {
968     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
969     goto out;
970   }
971
972   values = hivex_node_values (h, node);
973
974   for (i = 0; values[i] != 0; ++i) {
975     char *key = hivex_value_key (h, values[i]);
976     if (key == NULL) {
977       perrorf (g, "hivex_value_key");
978       goto out;
979     }
980
981     if (STRCASEEQ (key, "ProductName")) {
982       fs->product_name = hivex_value_string (h, values[i]);
983       if (!fs->product_name) {
984         perrorf (g, "hivex_value_string");
985         free (key);
986         goto out;
987       }
988     }
989     else if (STRCASEEQ (key, "CurrentVersion")) {
990       char *version = hivex_value_string (h, values[i]);
991       if (!version) {
992         perrorf (g, "hivex_value_string");
993         free (key);
994         goto out;
995       }
996       char *major, *minor;
997       if (match2 (g, version, re_windows_version, &major, &minor)) {
998         fs->major_version = parse_unsigned_int (g, major);
999         free (major);
1000         if (fs->major_version == -1) {
1001           free (minor);
1002           free (key);
1003           free (version);
1004           goto out;
1005         }
1006         fs->minor_version = parse_unsigned_int (g, minor);
1007         free (minor);
1008         if (fs->minor_version == -1) {
1009           free (key);
1010           free (version);
1011           goto out;
1012         }
1013       }
1014
1015       free (version);
1016     }
1017
1018     free (key);
1019   }
1020
1021   ret = 0;
1022
1023  out:
1024   if (h) hivex_close (h);
1025   free (values);
1026   free (software_path);
1027
1028   /* Free up the temporary file. */
1029   unlink (software_local);
1030 #undef software_local_len
1031
1032   return ret;
1033 }
1034
1035 static char *
1036 resolve_windows_path_silently (guestfs_h *g, const char *path)
1037 {
1038   guestfs_error_handler_cb old_error_cb = g->error_cb;
1039   g->error_cb = NULL;
1040   char *ret = guestfs_case_sensitive_path (g, path);
1041   g->error_cb = old_error_cb;
1042   return ret;
1043 }
1044
1045 static int
1046 extend_fses (guestfs_h *g)
1047 {
1048   size_t n = g->nr_fses + 1;
1049   struct inspect_fs *p;
1050
1051   p = realloc (g->fses, n * sizeof (struct inspect_fs));
1052   if (p == NULL) {
1053     perrorf (g, "realloc");
1054     return -1;
1055   }
1056
1057   g->fses = p;
1058   g->nr_fses = n;
1059
1060   memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
1061
1062   return 0;
1063 }
1064
1065 /* Parse small, unsigned ints, as used in version numbers. */
1066 static int
1067 parse_unsigned_int (guestfs_h *g, const char *str)
1068 {
1069   long ret;
1070   int r = xstrtol (str, NULL, 10, &ret, "");
1071   if (r != LONGINT_OK) {
1072     error (g, _("could not parse integer in version number: %s"), str);
1073     return -1;
1074   }
1075   return ret;
1076 }
1077
1078 /* At the moment, package format and package management is just a
1079  * simple function of the distro and major_version fields, so these
1080  * can never return an error.  We might be cleverer in future.
1081  */
1082 static void
1083 check_package_format (guestfs_h *g, struct inspect_fs *fs)
1084 {
1085   switch (fs->distro) {
1086   case OS_DISTRO_FEDORA:
1087   case OS_DISTRO_MEEGO:
1088   case OS_DISTRO_REDHAT_BASED:
1089   case OS_DISTRO_RHEL:
1090   case OS_DISTRO_MANDRIVA:
1091     fs->package_format = OS_PACKAGE_FORMAT_RPM;
1092     break;
1093
1094   case OS_DISTRO_DEBIAN:
1095   case OS_DISTRO_UBUNTU:
1096   case OS_DISTRO_LINUX_MINT:
1097     fs->package_format = OS_PACKAGE_FORMAT_DEB;
1098     break;
1099
1100   case OS_DISTRO_ARCHLINUX:
1101     fs->package_format = OS_PACKAGE_FORMAT_PACMAN;
1102     break;
1103   case OS_DISTRO_GENTOO:
1104     fs->package_format = OS_PACKAGE_FORMAT_EBUILD;
1105     break;
1106   case OS_DISTRO_PARDUS:
1107     fs->package_format = OS_PACKAGE_FORMAT_PISI;
1108     break;
1109
1110   case OS_DISTRO_WINDOWS:
1111   case OS_DISTRO_UNKNOWN:
1112   default:
1113     fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN;
1114     break;
1115   }
1116 }
1117
1118 static void
1119 check_package_management (guestfs_h *g, struct inspect_fs *fs)
1120 {
1121   switch (fs->distro) {
1122   case OS_DISTRO_FEDORA:
1123   case OS_DISTRO_MEEGO:
1124     fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1125     break;
1126
1127   case OS_DISTRO_REDHAT_BASED:
1128   case OS_DISTRO_RHEL:
1129     if (fs->major_version >= 5)
1130       fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1131     else
1132       fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE;
1133     break;
1134
1135   case OS_DISTRO_DEBIAN:
1136   case OS_DISTRO_UBUNTU:
1137   case OS_DISTRO_LINUX_MINT:
1138     fs->package_management = OS_PACKAGE_MANAGEMENT_APT;
1139     break;
1140
1141   case OS_DISTRO_ARCHLINUX:
1142     fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN;
1143     break;
1144   case OS_DISTRO_GENTOO:
1145     fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE;
1146     break;
1147   case OS_DISTRO_PARDUS:
1148     fs->package_management = OS_PACKAGE_MANAGEMENT_PISI;
1149     break;
1150   case OS_DISTRO_MANDRIVA:
1151     fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI;
1152     break;
1153
1154   case OS_DISTRO_WINDOWS:
1155   case OS_DISTRO_UNKNOWN:
1156   default:
1157     fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
1158     break;
1159   }
1160 }
1161
1162 static struct inspect_fs *
1163 search_for_root (guestfs_h *g, const char *root)
1164 {
1165   if (g->nr_fses == 0) {
1166     error (g, _("no inspection data: call guestfs_inspect_os first"));
1167     return NULL;
1168   }
1169
1170   size_t i;
1171   struct inspect_fs *fs;
1172   for (i = 0; i < g->nr_fses; ++i) {
1173     fs = &g->fses[i];
1174     if (fs->is_root && STREQ (root, fs->device))
1175       return fs;
1176   }
1177
1178   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
1179          root);
1180   return NULL;
1181 }
1182
1183 char **
1184 guestfs__inspect_get_roots (guestfs_h *g)
1185 {
1186   /* NB. Doesn't matter if g->nr_fses == 0.  We just return an empty
1187    * list in this case.
1188    */
1189
1190   size_t i;
1191   size_t count = 0;
1192   for (i = 0; i < g->nr_fses; ++i)
1193     if (g->fses[i].is_root)
1194       count++;
1195
1196   char **ret = calloc (count+1, sizeof (char *));
1197   if (ret == NULL) {
1198     perrorf (g, "calloc");
1199     return NULL;
1200   }
1201
1202   count = 0;
1203   for (i = 0; i < g->nr_fses; ++i) {
1204     if (g->fses[i].is_root) {
1205       ret[count] = safe_strdup (g, g->fses[i].device);
1206       count++;
1207     }
1208   }
1209   ret[count] = NULL;
1210
1211   return ret;
1212 }
1213
1214 char *
1215 guestfs__inspect_get_type (guestfs_h *g, const char *root)
1216 {
1217   struct inspect_fs *fs = search_for_root (g, root);
1218   if (!fs)
1219     return NULL;
1220
1221   char *ret;
1222   switch (fs->type) {
1223   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
1224   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
1225   case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
1226   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1227   }
1228
1229   return ret;
1230 }
1231
1232 char *
1233 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1234 {
1235   struct inspect_fs *fs = search_for_root (g, root);
1236   if (!fs)
1237     return NULL;
1238
1239   return safe_strdup (g, fs->arch ? : "unknown");
1240 }
1241
1242 char *
1243 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1244 {
1245   struct inspect_fs *fs = search_for_root (g, root);
1246   if (!fs)
1247     return NULL;
1248
1249   char *ret;
1250   switch (fs->distro) {
1251   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
1252   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
1253   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
1254   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
1255   case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
1256   case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
1257   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
1258   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
1259   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
1260   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
1261   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
1262   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
1263   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1264   }
1265
1266   return ret;
1267 }
1268
1269 int
1270 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1271 {
1272   struct inspect_fs *fs = search_for_root (g, root);
1273   if (!fs)
1274     return -1;
1275
1276   return fs->major_version;
1277 }
1278
1279 int
1280 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1281 {
1282   struct inspect_fs *fs = search_for_root (g, root);
1283   if (!fs)
1284     return -1;
1285
1286   return fs->minor_version;
1287 }
1288
1289 char *
1290 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1291 {
1292   struct inspect_fs *fs = search_for_root (g, root);
1293   if (!fs)
1294     return NULL;
1295
1296   return safe_strdup (g, fs->product_name ? : "unknown");
1297 }
1298
1299 char *
1300 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1301 {
1302   struct inspect_fs *fs = search_for_root (g, root);
1303   if (!fs)
1304     return NULL;
1305
1306   if (!fs->windows_systemroot) {
1307     error (g, _("not a Windows guest, or systemroot could not be determined"));
1308     return NULL;
1309   }
1310
1311   return safe_strdup (g, fs->windows_systemroot);
1312 }
1313
1314 char **
1315 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1316 {
1317   struct inspect_fs *fs = search_for_root (g, root);
1318   if (!fs)
1319     return NULL;
1320
1321   char **ret;
1322
1323   /* If no fstab information (Windows) return just the root. */
1324   if (fs->nr_fstab == 0) {
1325     ret = calloc (3, sizeof (char *));
1326     ret[0] = safe_strdup (g, "/");
1327     ret[1] = safe_strdup (g, root);
1328     ret[2] = NULL;
1329     return ret;
1330   }
1331
1332 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
1333   size_t i, count = 0;
1334   for (i = 0; i < fs->nr_fstab; ++i)
1335     if (CRITERION)
1336       count++;
1337
1338   /* Hashtables have 2N+1 entries. */
1339   ret = calloc (2*count+1, sizeof (char *));
1340   if (ret == NULL) {
1341     perrorf (g, "calloc");
1342     return NULL;
1343   }
1344
1345   count = 0;
1346   for (i = 0; i < fs->nr_fstab; ++i)
1347     if (CRITERION) {
1348       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
1349       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
1350       count++;
1351     }
1352 #undef CRITERION
1353
1354   return ret;
1355 }
1356
1357 char **
1358 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
1359 {
1360   struct inspect_fs *fs = search_for_root (g, root);
1361   if (!fs)
1362     return NULL;
1363
1364   char **ret;
1365
1366   /* If no fstab information (Windows) return just the root. */
1367   if (fs->nr_fstab == 0) {
1368     ret = calloc (2, sizeof (char *));
1369     ret[0] = safe_strdup (g, root);
1370     ret[1] = NULL;
1371     return ret;
1372   }
1373
1374   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
1375   if (ret == NULL) {
1376     perrorf (g, "calloc");
1377     return NULL;
1378   }
1379
1380   size_t i;
1381   for (i = 0; i < fs->nr_fstab; ++i)
1382     ret[i] = safe_strdup (g, fs->fstab[i].device);
1383
1384   return ret;
1385 }
1386
1387 char *
1388 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
1389 {
1390   struct inspect_fs *fs = search_for_root (g, root);
1391   if (!fs)
1392     return NULL;
1393
1394   char *ret;
1395   switch (fs->package_format) {
1396   case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
1397   case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
1398   case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
1399   case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
1400   case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
1401   case OS_PACKAGE_FORMAT_UNKNOWN:
1402   default:
1403     ret = safe_strdup (g, "unknown");
1404     break;
1405   }
1406
1407   return ret;
1408 }
1409
1410 char *
1411 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
1412 {
1413   struct inspect_fs *fs = search_for_root (g, root);
1414   if (!fs)
1415     return NULL;
1416
1417   char *ret;
1418   switch (fs->package_management) {
1419   case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
1420   case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
1421   case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
1422   case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
1423   case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
1424   case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
1425   case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
1426   case OS_PACKAGE_MANAGEMENT_UNKNOWN:
1427   default:
1428     ret = safe_strdup (g, "unknown");
1429     break;
1430   }
1431
1432   return ret;
1433 }
1434
1435 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
1436 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
1437 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
1438 static void add_application (guestfs_h *g, struct guestfs_application_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *install_path, const char *publisher, const char *url, const char *description);
1439 static void sort_applications (struct guestfs_application_list *);
1440
1441 /* Unlike the simple inspect-get-* calls, this one assumes that the
1442  * disks are mounted up, and reads files from the mounted disks.
1443  */
1444 struct guestfs_application_list *
1445 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
1446 {
1447   struct inspect_fs *fs = search_for_root (g, root);
1448   if (!fs)
1449     return NULL;
1450
1451   struct guestfs_application_list *ret = NULL;
1452
1453   switch (fs->type) {
1454   case OS_TYPE_LINUX:
1455     switch (fs->package_format) {
1456     case OS_PACKAGE_FORMAT_RPM:
1457       ret = list_applications_rpm (g, fs);
1458       if (ret == NULL)
1459         return NULL;
1460       break;
1461
1462     case OS_PACKAGE_FORMAT_DEB:
1463       ret = list_applications_deb (g, fs);
1464       if (ret == NULL)
1465         return NULL;
1466       break;
1467
1468     case OS_PACKAGE_FORMAT_PACMAN:
1469     case OS_PACKAGE_FORMAT_EBUILD:
1470     case OS_PACKAGE_FORMAT_PISI:
1471     case OS_PACKAGE_FORMAT_UNKNOWN:
1472     default:
1473       /* nothing - keep GCC happy */;
1474     }
1475     break;
1476
1477   case OS_TYPE_WINDOWS:
1478     ret = list_applications_windows (g, fs);
1479     if (ret == NULL)
1480       return NULL;
1481     break;
1482
1483   case OS_TYPE_FREEBSD:
1484   case OS_TYPE_UNKNOWN:
1485   default:
1486       /* nothing - keep GCC happy */;
1487   }
1488
1489   if (ret == NULL) {
1490     /* Don't know how to do inspection.  Not an error, return an
1491      * empty list.
1492      */
1493     ret = safe_malloc (g, sizeof *ret);
1494     ret->len = 0;
1495     ret->val = NULL;
1496   }
1497
1498   sort_applications (ret);
1499
1500   return ret;
1501 }
1502
1503 static struct guestfs_application_list *
1504 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
1505 {
1506   TMP_TEMPLATE_ON_STACK (tmpfile);
1507
1508   if (download_to_tmp (g, "/var/lib/rpm/Name", tmpfile, 10000000) == -1)
1509     return NULL;
1510
1511   struct guestfs_application_list *apps = NULL, *ret = NULL;
1512 #define cmd_len (strlen (tmpfile) + 64)
1513   char cmd[cmd_len];
1514   FILE *pp = NULL;
1515   char line[1024];
1516   size_t len;
1517
1518   snprintf (cmd, cmd_len, "db_dump -p '%s'", tmpfile);
1519
1520   if (g->verbose)
1521     fprintf (stderr, "list_applications_rpm: %s\n", cmd);
1522
1523   pp = popen (cmd, "r");
1524   if (pp == NULL) {
1525     perrorf (g, "popen: %s", cmd);
1526     goto out;
1527   }
1528
1529   /* Ignore everything to end-of-header marker. */
1530   for (;;) {
1531     if (fgets (line, sizeof line, pp) == NULL) {
1532       error (g, _("unexpected end of output from db_dump command"));
1533       goto out;
1534     }
1535
1536     len = strlen (line);
1537     if (len > 0 && line[len-1] == '\n') {
1538       line[len-1] = '\0';
1539       len--;
1540     }
1541
1542     if (STREQ (line, "HEADER=END"))
1543       break;
1544   }
1545
1546   /* Allocate 'apps' list. */
1547   apps = safe_malloc (g, sizeof *apps);
1548   apps->len = 0;
1549   apps->val = NULL;
1550
1551   /* Read alternate lines until end of data marker. */
1552   for (;;) {
1553     if (fgets (line, sizeof line, pp) == NULL) {
1554       error (g, _("unexpected end of output from db_dump command"));
1555       goto out;
1556     }
1557
1558     len = strlen (line);
1559     if (len > 0 && line[len-1] == '\n') {
1560       line[len-1] = '\0';
1561       len--;
1562     }
1563
1564     if (STREQ (line, "DATA=END"))
1565       break;
1566
1567     char *p = line;
1568     if (len > 0 && line[0] == ' ')
1569       p = line+1;
1570     /* Ignore any application name that contains non-printable chars.
1571      * In the db_dump output these would be escaped with backslash, so
1572      * we can just ignore any such line.
1573      */
1574     if (strchr (p, '\\') == NULL)
1575       add_application (g, apps, p, "", 0, "", "", "", "", "", "");
1576
1577     /* Discard next line. */
1578     if (fgets (line, sizeof line, pp) == NULL) {
1579       error (g, _("unexpected end of output from db_dump command"));
1580       goto out;
1581     }
1582   }
1583
1584   /* Catch errors from the db_dump command. */
1585   if (pclose (pp) == -1) {
1586     perrorf (g, "pclose: %s", cmd);
1587     goto out;
1588   }
1589   pp = NULL;
1590
1591   ret = apps;
1592
1593  out:
1594   if (ret == NULL && apps != NULL)
1595     guestfs_free_application_list (apps);
1596   if (pp)
1597     pclose (pp);
1598   unlink (tmpfile);
1599 #undef cmd_len
1600
1601   return ret;
1602 }
1603
1604 static struct guestfs_application_list *
1605 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
1606 {
1607   TMP_TEMPLATE_ON_STACK (tmpfile);
1608
1609   if (download_to_tmp (g, "/var/lib/dpkg/status", tmpfile, 10000000) == -1)
1610     return NULL;
1611
1612   struct guestfs_application_list *apps = NULL, *ret = NULL;
1613   FILE *fp = NULL;
1614   char line[1024];
1615   size_t len;
1616   char *name = NULL, *version = NULL, *release = NULL;
1617   int installed_flag = 0;
1618
1619   fp = fopen (tmpfile, "r");
1620   if (fp == NULL) {
1621     perrorf (g, "fopen: %s", tmpfile);
1622     goto out;
1623   }
1624
1625   /* Allocate 'apps' list. */
1626   apps = safe_malloc (g, sizeof *apps);
1627   apps->len = 0;
1628   apps->val = NULL;
1629
1630   /* Read the temporary file.  Each package entry is separated by
1631    * a blank line.
1632    * XXX Strictly speaking this is in mailbox header format, so it
1633    * would be possible for fields to spread across multiple lines,
1634    * although for the short fields that we are concerned about this is
1635    * unlikely and not seen in practice.
1636    */
1637   while (fgets (line, sizeof line, fp) != NULL) {
1638     len = strlen (line);
1639     if (len > 0 && line[len-1] == '\n') {
1640       line[len-1] = '\0';
1641       len--;
1642     }
1643
1644     if (STRPREFIX (line, "Package: ")) {
1645       free (name);
1646       name = safe_strdup (g, &line[9]);
1647     }
1648     else if (STRPREFIX (line, "Status: ")) {
1649       installed_flag = strstr (&line[8], "installed") != NULL;
1650     }
1651     else if (STRPREFIX (line, "Version: ")) {
1652       free (version);
1653       free (release);
1654       char *p = strchr (&line[9], '-');
1655       if (p) {
1656         *p = '\0';
1657         version = safe_strdup (g, &line[9]);
1658         release = safe_strdup (g, p+1);
1659       } else {
1660         version = safe_strdup (g, &line[9]);
1661         release = NULL;
1662       }
1663     }
1664     else if (STREQ (line, "")) {
1665       if (installed_flag && name && version)
1666         add_application (g, apps, name, "", 0, version, release ? : "",
1667                          "", "", "", "");
1668       free (name);
1669       free (version);
1670       free (release);
1671       name = version = release = NULL;
1672       installed_flag = 0;
1673     }
1674   }
1675
1676   if (fclose (fp) == -1) {
1677     perrorf (g, "fclose: %s", tmpfile);
1678     goto out;
1679   }
1680   fp = NULL;
1681
1682   ret = apps;
1683
1684  out:
1685   if (ret == NULL && apps != NULL)
1686     guestfs_free_application_list (apps);
1687   if (fp)
1688     fclose (fp);
1689   free (name);
1690   free (version);
1691   free (release);
1692   unlink (tmpfile);
1693   return ret;
1694 }
1695
1696 /* XXX We already download the SOFTWARE hive when doing general
1697  * inspection.  We could avoid this second download of the same file
1698  * by caching these entries in the handle.
1699  */
1700 static struct guestfs_application_list *
1701 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
1702 {
1703   TMP_TEMPLATE_ON_STACK (software_local);
1704
1705   size_t len = strlen (fs->windows_systemroot) + 64;
1706   char software[len];
1707   snprintf (software, len, "%s/system32/config/software",
1708             fs->windows_systemroot);
1709
1710   char *software_path = resolve_windows_path_silently (g, software);
1711   if (!software_path)
1712     /* If the software hive doesn't exist, just accept that we cannot
1713      * find product_name etc.
1714      */
1715     return 0;
1716
1717   struct guestfs_application_list *apps = NULL, *ret = NULL;
1718   hive_h *h = NULL;
1719   hive_node_h *children = NULL;
1720
1721   if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
1722     goto out;
1723
1724   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
1725   if (h == NULL) {
1726     perrorf (g, "hivex_open");
1727     goto out;
1728   }
1729
1730   hive_node_h node = hivex_root (h);
1731   const char *hivepath[] =
1732     { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
1733   size_t i;
1734   for (i = 0;
1735        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
1736        ++i) {
1737     node = hivex_node_get_child (h, node, hivepath[i]);
1738   }
1739
1740   if (node == 0) {
1741     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
1742     goto out;
1743   }
1744
1745   children = hivex_node_children (h, node);
1746   if (children == NULL) {
1747     perrorf (g, "hivex_node_children");
1748     goto out;
1749   }
1750
1751   /* Allocate 'apps' list. */
1752   apps = safe_malloc (g, sizeof *apps);
1753   apps->len = 0;
1754   apps->val = NULL;
1755
1756   /* Consider any child node that has a DisplayName key.
1757    * See also:
1758    * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
1759    */
1760   for (i = 0; children[i] != 0; ++i) {
1761     hive_value_h value;
1762     char *name = NULL;
1763     char *display_name = NULL;
1764     char *version = NULL;
1765     char *install_path = NULL;
1766     char *publisher = NULL;
1767     char *url = NULL;
1768     char *comments = NULL;
1769
1770     /* Use the node name as a proxy for the package name in Linux.  The
1771      * display name is not language-independent, so it cannot be used.
1772      */
1773     name = hivex_node_name (h, children[i]);
1774     if (name == NULL) {
1775       perrorf (g, "hivex_node_get_name");
1776       goto out;
1777     }
1778
1779     value = hivex_node_get_value (h, children[i], "DisplayName");
1780     if (value) {
1781       display_name = hivex_value_string (h, value);
1782       if (display_name) {
1783         value = hivex_node_get_value (h, children[i], "DisplayVersion");
1784         if (value)
1785           version = hivex_value_string (h, value);
1786         value = hivex_node_get_value (h, children[i], "InstallLocation");
1787         if (value)
1788           install_path = hivex_value_string (h, value);
1789         value = hivex_node_get_value (h, children[i], "Publisher");
1790         if (value)
1791           publisher = hivex_value_string (h, value);
1792         value = hivex_node_get_value (h, children[i], "URLInfoAbout");
1793         if (value)
1794           url = hivex_value_string (h, value);
1795         value = hivex_node_get_value (h, children[i], "Comments");
1796         if (value)
1797           comments = hivex_value_string (h, value);
1798
1799         add_application (g, apps, name, display_name, 0,
1800                          version ? : "",
1801                          "",
1802                          install_path ? : "",
1803                          publisher ? : "",
1804                          url ? : "",
1805                          comments ? : "");
1806       }
1807     }
1808
1809     free (name);
1810     free (display_name);
1811     free (version);
1812     free (install_path);
1813     free (publisher);
1814     free (url);
1815     free (comments);
1816   }
1817
1818   ret = apps;
1819
1820  out:
1821   if (ret == NULL && apps != NULL)
1822     guestfs_free_application_list (apps);
1823   if (h) hivex_close (h);
1824   free (children);
1825   free (software_path);
1826
1827   /* Free up the temporary file. */
1828   unlink (software_local);
1829 #undef software_local_len
1830
1831   return ret;
1832 }
1833
1834 static void
1835 add_application (guestfs_h *g, struct guestfs_application_list *apps,
1836                  const char *name, const char *display_name, int32_t epoch,
1837                  const char *version, const char *release,
1838                  const char *install_path,
1839                  const char *publisher, const char *url,
1840                  const char *description)
1841 {
1842   apps->len++;
1843   apps->val = safe_realloc (g, apps->val,
1844                             apps->len * sizeof (struct guestfs_application));
1845   apps->val[apps->len-1].app_name = safe_strdup (g, name);
1846   apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
1847   apps->val[apps->len-1].app_epoch = epoch;
1848   apps->val[apps->len-1].app_version = safe_strdup (g, version);
1849   apps->val[apps->len-1].app_release = safe_strdup (g, release);
1850   apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
1851   /* XXX Translated path is not implemented yet. */
1852   apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
1853   apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
1854   apps->val[apps->len-1].app_url = safe_strdup (g, url);
1855   /* XXX The next two are not yet implemented for any package
1856    * format, but we could easily support them for rpm and deb.
1857    */
1858   apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
1859   apps->val[apps->len-1].app_summary = safe_strdup (g, "");
1860   apps->val[apps->len-1].app_description = safe_strdup (g, description);
1861 }
1862
1863 /* Sort applications by name before returning the list. */
1864 static int
1865 compare_applications (const void *vp1, const void *vp2)
1866 {
1867   const struct guestfs_application *v1 = vp1;
1868   const struct guestfs_application *v2 = vp2;
1869
1870   return strcmp (v1->app_name, v2->app_name);
1871 }
1872
1873 static void
1874 sort_applications (struct guestfs_application_list *apps)
1875 {
1876   if (apps && apps->val)
1877     qsort (apps->val, apps->len, sizeof (struct guestfs_application),
1878            compare_applications);
1879 }
1880
1881 /* Download to a guest file to a local temporary file.  Refuse to
1882  * download the guest file if it is larger than max_size.  The caller
1883  * is responsible for deleting the temporary file after use.
1884  */
1885 static int
1886 download_to_tmp (guestfs_h *g, const char *filename,
1887                  char *localtmp, int64_t max_size)
1888 {
1889   int fd;
1890   char buf[32];
1891   int64_t size;
1892
1893   size = guestfs_filesize (g, filename);
1894   if (size == -1)
1895     /* guestfs_filesize failed and has already set error in handle */
1896     return -1;
1897   if (size > max_size) {
1898     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1899            filename, size);
1900     return -1;
1901   }
1902
1903   fd = mkstemp (localtmp);
1904   if (fd == -1) {
1905     perrorf (g, "mkstemp");
1906     return -1;
1907   }
1908
1909   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
1910
1911   if (guestfs_download (g, filename, buf) == -1) {
1912     close (fd);
1913     unlink (localtmp);
1914     return -1;
1915   }
1916
1917   if (close (fd) == -1) {
1918     perrorf (g, "close: %s", localtmp);
1919     unlink (localtmp);
1920     return -1;
1921   }
1922
1923   return 0;
1924 }
1925
1926 /* Call 'f' with Augeas opened and having parsed 'filename' (this file
1927  * must exist).  As a security measure, this bails if the file is too
1928  * large for a reasonable configuration file.  After the call to 'f'
1929  * Augeas is closed.
1930  */
1931 static int
1932 inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
1933                      int (*f) (guestfs_h *, struct inspect_fs *))
1934 {
1935   /* Security: Refuse to do this if filename is too large. */
1936   int64_t size = guestfs_filesize (g, filename);
1937   if (size == -1)
1938     /* guestfs_filesize failed and has already set error in handle */
1939     return -1;
1940   if (size > 100000) {
1941     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1942            filename, size);
1943     return -1;
1944   }
1945
1946   /* If !feature_available (g, "augeas") then the next call will fail.
1947    * Arguably we might want to fall back to a non-Augeas method in
1948    * this case.
1949    */
1950   if (guestfs_aug_init (g, "/", 16|32) == -1)
1951     return -1;
1952
1953   int r = -1;
1954
1955   /* Tell Augeas to only load one file (thanks Raphaël Pinson). */
1956   char buf[strlen (filename) + 64];
1957   snprintf (buf, strlen (filename) + 64, "/augeas/load//incl[. != \"%s\"]",
1958             filename);
1959   if (guestfs_aug_rm (g, buf) == -1)
1960     goto out;
1961
1962   if (guestfs_aug_load (g) == -1)
1963     goto out;
1964
1965   r = f (g, fs);
1966
1967  out:
1968   guestfs_aug_close (g);
1969
1970   return r;
1971 }
1972
1973 /* Get the first line of a small file, without any trailing newline
1974  * character.
1975  */
1976 static char *
1977 first_line_of_file (guestfs_h *g, const char *filename)
1978 {
1979   char **lines;
1980   int64_t size;
1981   char *ret;
1982
1983   /* Don't trust guestfs_head_n not to break with very large files.
1984    * Check the file size is something reasonable first.
1985    */
1986   size = guestfs_filesize (g, filename);
1987   if (size == -1)
1988     /* guestfs_filesize failed and has already set error in handle */
1989     return NULL;
1990   if (size > 1000000) {
1991     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1992            filename, size);
1993     return NULL;
1994   }
1995
1996   lines = guestfs_head_n (g, 1, filename);
1997   if (lines == NULL)
1998     return NULL;
1999   if (lines[0] == NULL) {
2000     error (g, _("%s: file is empty"), filename);
2001     guestfs___free_string_list (lines);
2002     return NULL;
2003   }
2004   /* lines[1] should be NULL because of '1' argument above ... */
2005
2006   ret = lines[0];               /* caller frees */
2007   free (lines);                 /* free the array */
2008
2009   return ret;
2010 }
2011
2012 #else /* no PCRE or hivex at compile time */
2013
2014 /* XXX These functions should be in an optgroup. */
2015
2016 #define NOT_IMPL(r)                                                     \
2017   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
2018   return r
2019
2020 char **
2021 guestfs__inspect_os (guestfs_h *g)
2022 {
2023   NOT_IMPL(NULL);
2024 }
2025
2026 char **
2027 guestfs__inspect_get_roots (guestfs_h *g)
2028 {
2029   NOT_IMPL(NULL);
2030 }
2031
2032 char *
2033 guestfs__inspect_get_type (guestfs_h *g, const char *root)
2034 {
2035   NOT_IMPL(NULL);
2036 }
2037
2038 char *
2039 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
2040 {
2041   NOT_IMPL(NULL);
2042 }
2043
2044 char *
2045 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
2046 {
2047   NOT_IMPL(NULL);
2048 }
2049
2050 int
2051 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
2052 {
2053   NOT_IMPL(-1);
2054 }
2055
2056 int
2057 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
2058 {
2059   NOT_IMPL(-1);
2060 }
2061
2062 char *
2063 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
2064 {
2065   NOT_IMPL(NULL);
2066 }
2067
2068 char *
2069 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
2070 {
2071   NOT_IMPL(NULL);
2072 }
2073
2074 char **
2075 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
2076 {
2077   NOT_IMPL(NULL);
2078 }
2079
2080 char **
2081 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
2082 {
2083   NOT_IMPL(NULL);
2084 }
2085
2086 char *
2087 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
2088 {
2089   NOT_IMPL(NULL);
2090 }
2091
2092 char *
2093 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
2094 {
2095   NOT_IMPL(NULL);
2096 }
2097
2098 struct guestfs_application_list *
2099 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
2100 {
2101   NOT_IMPL(NULL);
2102 }
2103
2104 #endif /* no PCRE or hivex at compile time */
2105
2106 void
2107 guestfs___free_inspect_info (guestfs_h *g)
2108 {
2109   size_t i;
2110   for (i = 0; i < g->nr_fses; ++i) {
2111     free (g->fses[i].device);
2112     free (g->fses[i].product_name);
2113     free (g->fses[i].arch);
2114     free (g->fses[i].windows_systemroot);
2115     size_t j;
2116     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
2117       free (g->fses[i].fstab[j].device);
2118       free (g->fses[i].fstab[j].mountpoint);
2119     }
2120     free (g->fses[i].fstab);
2121   }
2122   free (g->fses);
2123   g->nr_fses = 0;
2124   g->fses = NULL;
2125 }
2126
2127 /* In the Perl code this is a public function. */
2128 int
2129 guestfs___feature_available (guestfs_h *g, const char *feature)
2130 {
2131   /* If there's an error we should ignore it, so to do that we have to
2132    * temporarily replace the error handler with a null one.
2133    */
2134   guestfs_error_handler_cb old_error_cb = g->error_cb;
2135   g->error_cb = NULL;
2136
2137   const char *groups[] = { feature, NULL };
2138   int r = guestfs_available (g, (char * const *) groups);
2139
2140   g->error_cb = old_error_cb;
2141
2142   return r == 0 ? 1 : 0;
2143 }
2144
2145 #ifdef HAVE_PCRE
2146
2147 /* Match a regular expression which contains no captures.  Returns
2148  * true if it matches or false if it doesn't.
2149  */
2150 int
2151 guestfs___match (guestfs_h *g, const char *str, const pcre *re)
2152 {
2153   size_t len = strlen (str);
2154   int vec[30], r;
2155
2156   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
2157   if (r == PCRE_ERROR_NOMATCH)
2158     return 0;
2159   if (r != 1) {
2160     /* Internal error -- should not happen. */
2161     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2162              __FILE__, __func__, r, str);
2163     return 0;
2164   }
2165
2166   return 1;
2167 }
2168
2169 /* Match a regular expression which contains exactly one capture.  If
2170  * the string matches, return the capture, otherwise return NULL.  The
2171  * caller must free the result.
2172  */
2173 char *
2174 guestfs___match1 (guestfs_h *g, const char *str, const pcre *re)
2175 {
2176   size_t len = strlen (str);
2177   int vec[30], r;
2178
2179   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
2180   if (r == PCRE_ERROR_NOMATCH)
2181     return NULL;
2182   if (r != 2) {
2183     /* Internal error -- should not happen. */
2184     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2185              __FILE__, __func__, r, str);
2186     return NULL;
2187   }
2188
2189   return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2190 }
2191
2192 /* Match a regular expression which contains exactly two captures. */
2193 int
2194 guestfs___match2 (guestfs_h *g, const char *str, const pcre *re,
2195                   char **ret1, char **ret2)
2196 {
2197   size_t len = strlen (str);
2198   int vec[30], r;
2199
2200   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
2201   if (r == PCRE_ERROR_NOMATCH)
2202     return 0;
2203   if (r != 3) {
2204     /* Internal error -- should not happen. */
2205     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2206              __FILE__, __func__, r, str);
2207     return 0;
2208   }
2209
2210   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2211   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
2212
2213   return 1;
2214 }
2215
2216 /* Match a regular expression which contains exactly three captures. */
2217 int
2218 guestfs___match3 (guestfs_h *g, const char *str, const pcre *re,
2219                   char **ret1, char **ret2, char **ret3)
2220 {
2221   size_t len = strlen (str);
2222   int vec[30], r;
2223
2224   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
2225   if (r == PCRE_ERROR_NOMATCH)
2226     return 0;
2227   if (r != 4) {
2228     /* Internal error -- should not happen. */
2229     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2230              __FILE__, __func__, r, str);
2231     return 0;
2232   }
2233
2234   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2235   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
2236   *ret3 = safe_strndup (g, &str[vec[6]], vec[7]-vec[6]);
2237
2238   return 1;
2239 }
2240
2241 #endif /* HAVE_PCRE */