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