99097eeac129495e3c37f09fbb49a660b5671d79
[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 /* Some limits on what we will read, for safety. */
49
50 /* Small text configuration files.
51  *
52  * The upper limit is for general files that we grep or download.  The
53  * largest such file is probably "txtsetup.sif" from Windows CDs
54  * (~500K).  This number has to be larger than any legitimate file and
55  * smaller than the protocol message size.
56  *
57  * The lower limit is for files parsed by Augeas on the daemon side,
58  * where Augeas is running in reduced memory and can potentially
59  * create a lot of metadata so we really need to be careful about
60  * those.
61  */
62 #define MAX_SMALL_FILE_SIZE    (2 * 1000 * 1000)
63 #define MAX_AUGEAS_FILE_SIZE        (100 * 1000)
64
65 /* Maximum Windows Registry hive that we will download to /tmp.  Some
66  * registries can be legitimately very large.
67  */
68 #define MAX_REGISTRY_SIZE    (100 * 1000 * 1000)
69
70 /* Maximum RPM or dpkg database we will download to /tmp. */
71 #define MAX_PKG_DB_SIZE       (10 * 1000 * 1000)
72
73 /* Compile all the regular expressions once when the shared library is
74  * loaded.  PCRE is thread safe so we're supposedly OK here if
75  * multiple threads call into the libguestfs API functions below
76  * simultaneously.
77  */
78 static pcre *re_fedora;
79 static pcre *re_rhel_old;
80 static pcre *re_rhel;
81 static pcre *re_rhel_no_minor;
82 static pcre *re_major_minor;
83 static pcre *re_aug_seq;
84 static pcre *re_xdev;
85 static pcre *re_first_partition;
86 static pcre *re_freebsd;
87 static pcre *re_windows_version;
88
89 static void compile_regexps (void) __attribute__((constructor));
90 static void free_regexps (void) __attribute__((destructor));
91
92 static void
93 compile_regexps (void)
94 {
95   const char *err;
96   int offset;
97
98 #define COMPILE(re,pattern,options)                                     \
99   do {                                                                  \
100     re = pcre_compile ((pattern), (options), &err, &offset, NULL);      \
101     if (re == NULL) {                                                   \
102       ignore_value (write (2, err, strlen (err)));                      \
103       abort ();                                                         \
104     }                                                                   \
105   } while (0)
106
107   COMPILE (re_fedora, "Fedora release (\\d+)", 0);
108   COMPILE (re_rhel_old,
109            "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0);
110   COMPILE (re_rhel,
111            "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0);
112   COMPILE (re_rhel_no_minor,
113            "(?:Red Hat|CentOS|Scientific Linux).*release (\\d+)", 0);
114   COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
115   COMPILE (re_aug_seq, "/\\d+$", 0);
116   COMPILE (re_xdev, "^/dev/(?:h|s|v|xv)d([a-z]\\d*)$", 0);
117   COMPILE (re_first_partition, "^/dev/(?:h|s|v)d.1$", 0);
118   COMPILE (re_freebsd, "^/dev/ad(\\d+)s(\\d+)([a-z])$", 0);
119   COMPILE (re_windows_version, "^(\\d+)\\.(\\d+)", 0);
120 }
121
122 static void
123 free_regexps (void)
124 {
125   pcre_free (re_fedora);
126   pcre_free (re_rhel_old);
127   pcre_free (re_rhel);
128   pcre_free (re_rhel_no_minor);
129   pcre_free (re_major_minor);
130   pcre_free (re_aug_seq);
131   pcre_free (re_xdev);
132   pcre_free (re_first_partition);
133   pcre_free (re_freebsd);
134   pcre_free (re_windows_version);
135 }
136
137 /* The main inspection code. */
138 static int check_for_filesystem_on (guestfs_h *g, const char *device, int is_block, int is_partnum);
139
140 char **
141 guestfs__inspect_os (guestfs_h *g)
142 {
143   /* Remove any information previously stored in the handle. */
144   guestfs___free_inspect_info (g);
145
146   if (guestfs_umount_all (g) == -1)
147     return NULL;
148
149   /* Iterate over all possible devices.  Try to mount each
150    * (read-only).  Examine ones which contain filesystems and add that
151    * information to the handle.
152    */
153   /* Look to see if any devices directly contain filesystems (RHBZ#590167). */
154   char **devices;
155   devices = guestfs_list_devices (g);
156   if (devices == NULL)
157     return NULL;
158
159   size_t i;
160   for (i = 0; devices[i] != NULL; ++i) {
161     if (check_for_filesystem_on (g, devices[i], 1, 0) == -1) {
162       guestfs___free_string_list (devices);
163       guestfs___free_inspect_info (g);
164       return NULL;
165     }
166   }
167   guestfs___free_string_list (devices);
168
169   /* Look at all partitions. */
170   char **partitions;
171   partitions = guestfs_list_partitions (g);
172   if (partitions == NULL) {
173     guestfs___free_inspect_info (g);
174     return NULL;
175   }
176
177   for (i = 0; partitions[i] != NULL; ++i) {
178     if (check_for_filesystem_on (g, partitions[i], 0, i+1) == -1) {
179       guestfs___free_string_list (partitions);
180       guestfs___free_inspect_info (g);
181       return NULL;
182     }
183   }
184   guestfs___free_string_list (partitions);
185
186   /* Look at all LVs. */
187   if (guestfs___feature_available (g, "lvm2")) {
188     char **lvs;
189     lvs = guestfs_lvs (g);
190     if (lvs == NULL) {
191       guestfs___free_inspect_info (g);
192       return NULL;
193     }
194
195     for (i = 0; lvs[i] != NULL; ++i) {
196       if (check_for_filesystem_on (g, lvs[i], 0, 0) == -1) {
197         guestfs___free_string_list (lvs);
198         guestfs___free_inspect_info (g);
199         return NULL;
200       }
201     }
202     guestfs___free_string_list (lvs);
203   }
204
205   /* At this point we have, in the handle, a list of all filesystems
206    * found and data about each one.  Now we assemble the list of
207    * filesystems which are root devices and return that to the user.
208    * Fall through to guestfs__inspect_get_roots to do that.
209    */
210   char **ret = guestfs__inspect_get_roots (g);
211   if (ret == NULL)
212     guestfs___free_inspect_info (g);
213   return ret;
214 }
215
216 /* Find out if 'device' contains a filesystem.  If it does, add
217  * another entry in g->fses.
218  */
219 static int check_filesystem (guestfs_h *g, const char *device, int is_block, int is_partnum);
220 static int check_linux_root (guestfs_h *g, struct inspect_fs *fs);
221 static int check_freebsd_root (guestfs_h *g, struct inspect_fs *fs);
222 static int check_installer_root (guestfs_h *g, struct inspect_fs *fs);
223 static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
224 static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs);
225 static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs);
226 static int check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs);
227 static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
228 static int check_windows_root (guestfs_h *g, struct inspect_fs *fs);
229 static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
230 static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs);
231 static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs);
232 static char *resolve_windows_path_silently (guestfs_h *g, const char *);
233 static int extend_fses (guestfs_h *g);
234 static int parse_unsigned_int (guestfs_h *g, const char *str);
235 static int parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str);
236 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
237                             const char *spec, const char *mp);
238 static char *resolve_fstab_device (guestfs_h *g, const char *spec);
239 static void check_package_format (guestfs_h *g, struct inspect_fs *fs);
240 static void check_package_management (guestfs_h *g, struct inspect_fs *fs);
241 static int download_to_tmp (guestfs_h *g, const char *filename, char *localtmp, int64_t max_size);
242 static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename, int (*f) (guestfs_h *, struct inspect_fs *));
243 static char *first_line_of_file (guestfs_h *g, const char *filename);
244 static int first_egrep_of_file (guestfs_h *g, const char *filename, const char *eregex, int iflag, char **ret);
245
246 static int
247 check_for_filesystem_on (guestfs_h *g, const char *device,
248                          int is_block, int is_partnum)
249 {
250   /* Get vfs-type in order to check if it's a Linux(?) swap device.
251    * If there's an error we should ignore it, so to do that we have to
252    * temporarily replace the error handler with a null one.
253    */
254   guestfs_error_handler_cb old_error_cb = g->error_cb;
255   g->error_cb = NULL;
256   char *vfs_type = guestfs_vfs_type (g, device);
257   g->error_cb = old_error_cb;
258
259   int is_swap = vfs_type && STREQ (vfs_type, "swap");
260
261   if (g->verbose)
262     fprintf (stderr, "check_for_filesystem_on: %s %d %d (%s)\n",
263              device, is_block, is_partnum,
264              vfs_type ? vfs_type : "failed to get vfs type");
265
266   if (is_swap) {
267     free (vfs_type);
268     if (extend_fses (g) == -1)
269       return -1;
270     g->fses[g->nr_fses-1].is_swap = 1;
271     return 0;
272   }
273
274   /* Try mounting the device.  As above, ignore errors. */
275   g->error_cb = NULL;
276   int r = guestfs_mount_ro (g, device, "/");
277   if (r == -1 && vfs_type && STREQ (vfs_type, "ufs")) /* Hack for the *BSDs. */
278     r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", device, "/");
279   free (vfs_type);
280   g->error_cb = old_error_cb;
281   if (r == -1)
282     return 0;
283
284   /* Do the rest of the checks. */
285   r = check_filesystem (g, device, is_block, is_partnum);
286
287   /* Unmount the filesystem. */
288   if (guestfs_umount_all (g) == -1)
289     return -1;
290
291   return r;
292 }
293
294 /* is_block and is_partnum are just hints: is_block is true if the
295  * filesystem is a whole block device (eg. /dev/sda).  is_partnum
296  * is > 0 if the filesystem is a direct partition, and in this case
297  * it is the partition number counting from 1
298  * (eg. /dev/sda1 => is_partnum == 1).
299  */
300 static int
301 check_filesystem (guestfs_h *g, const char *device,
302                   int is_block, int is_partnum)
303 {
304   if (extend_fses (g) == -1)
305     return -1;
306
307   struct inspect_fs *fs = &g->fses[g->nr_fses-1];
308
309   fs->device = safe_strdup (g, device);
310   fs->is_mountable = 1;
311
312   /* Optimize some of the tests by avoiding multiple tests of the same thing. */
313   int is_dir_etc = guestfs_is_dir (g, "/etc") > 0;
314   int is_dir_bin = guestfs_is_dir (g, "/bin") > 0;
315   int is_dir_share = guestfs_is_dir (g, "/share") > 0;
316
317   /* Grub /boot? */
318   if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
319       guestfs_is_file (g, "/grub/grub.conf") > 0)
320     fs->content = FS_CONTENT_LINUX_BOOT;
321   /* FreeBSD root? */
322   else if (is_dir_etc &&
323            is_dir_bin &&
324            guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 &&
325            guestfs_is_file (g, "/etc/fstab") > 0) {
326     /* Ignore /dev/sda1 which is a shadow of the real root filesystem
327      * that is probably /dev/sda5 (see:
328      * http://www.freebsd.org/doc/handbook/disk-organization.html)
329      */
330     if (match (g, device, re_first_partition))
331       return 0;
332
333     fs->is_root = 1;
334     fs->content = FS_CONTENT_FREEBSD_ROOT;
335     fs->format = OS_FORMAT_INSTALLED;
336     if (check_freebsd_root (g, fs) == -1)
337       return -1;
338   }
339   /* Linux root? */
340   else if (is_dir_etc &&
341            is_dir_bin &&
342            guestfs_is_file (g, "/etc/fstab") > 0) {
343     fs->is_root = 1;
344     fs->content = FS_CONTENT_LINUX_ROOT;
345     fs->format = OS_FORMAT_INSTALLED;
346     if (check_linux_root (g, fs) == -1)
347       return -1;
348   }
349   /* Linux /usr/local? */
350   else if (is_dir_etc &&
351            is_dir_bin &&
352            is_dir_share &&
353            guestfs_exists (g, "/local") == 0 &&
354            guestfs_is_file (g, "/etc/fstab") == 0)
355     fs->content = FS_CONTENT_LINUX_USR_LOCAL;
356   /* Linux /usr? */
357   else if (is_dir_etc &&
358            is_dir_bin &&
359            is_dir_share &&
360            guestfs_exists (g, "/local") > 0 &&
361            guestfs_is_file (g, "/etc/fstab") == 0)
362     fs->content = FS_CONTENT_LINUX_USR;
363   /* Linux /var? */
364   else if (guestfs_is_dir (g, "/log") > 0 &&
365            guestfs_is_dir (g, "/run") > 0 &&
366            guestfs_is_dir (g, "/spool") > 0)
367     fs->content = FS_CONTENT_LINUX_VAR;
368   /* Windows root? */
369   else if (guestfs_is_file (g, "/AUTOEXEC.BAT") > 0 ||
370            guestfs_is_file (g, "/autoexec.bat") > 0 ||
371            guestfs_is_dir (g, "/Program Files") > 0 ||
372            guestfs_is_dir (g, "/WINDOWS") > 0 ||
373            guestfs_is_dir (g, "/Windows") > 0 ||
374            guestfs_is_dir (g, "/windows") > 0 ||
375            guestfs_is_dir (g, "/WIN32") > 0 ||
376            guestfs_is_dir (g, "/Win32") > 0 ||
377            guestfs_is_dir (g, "/WINNT") > 0 ||
378            guestfs_is_file (g, "/boot.ini") > 0 ||
379            guestfs_is_file (g, "/ntldr") > 0) {
380     fs->is_root = 1;
381     fs->content = FS_CONTENT_WINDOWS_ROOT;
382     fs->format = OS_FORMAT_INSTALLED;
383     if (check_windows_root (g, fs) == -1)
384       return -1;
385   }
386   /* Install CD/disk?  Skip these checks if it's not a whole device
387    * (eg. CD) or the first partition (eg. bootable USB key).
388    */
389   else if ((is_block || is_partnum == 1) &&
390            (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 ||
391             guestfs_is_dir (g, "/EFI/BOOT") > 0 ||
392             guestfs_is_file (g, "/images/install.img") > 0 ||
393             guestfs_is_dir (g, "/.disk") > 0 ||
394             guestfs_is_file (g, "/.discinfo") > 0 ||
395             guestfs_is_file (g, "/i386/txtsetup.sif") > 0 ||
396             guestfs_is_file (g, "/amd64/txtsetup.sif")) > 0) {
397     fs->is_root = 1;
398     fs->content = FS_CONTENT_INSTALLER;
399     fs->format = OS_FORMAT_INSTALLER;
400     if (check_installer_root (g, fs) == -1)
401       return -1;
402   }
403
404   return 0;
405 }
406
407 /* Set fs->product_name to the first line of the release file. */
408 static int
409 parse_release_file (guestfs_h *g, struct inspect_fs *fs,
410                     const char *release_filename)
411 {
412   fs->product_name = first_line_of_file (g, release_filename);
413   if (fs->product_name == NULL)
414     return -1;
415   return 0;
416 }
417
418 /* Parse generic MAJOR.MINOR from the fs->product_name string. */
419 static int
420 parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
421 {
422   char *major, *minor;
423
424   if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) {
425     fs->major_version = parse_unsigned_int (g, major);
426     free (major);
427     if (fs->major_version == -1) {
428       free (minor);
429       return -1;
430     }
431     fs->minor_version = parse_unsigned_int (g, minor);
432     free (minor);
433     if (fs->minor_version == -1)
434       return -1;
435   }
436   return 0;
437 }
438
439 /* Ubuntu has /etc/lsb-release containing:
440  *   DISTRIB_ID=Ubuntu                                # Distro
441  *   DISTRIB_RELEASE=10.04                            # Version
442  *   DISTRIB_CODENAME=lucid
443  *   DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"         # Product name
444  *
445  * [Ubuntu-derived ...] Linux Mint was found to have this:
446  *   DISTRIB_ID=LinuxMint
447  *   DISTRIB_RELEASE=10
448  *   DISTRIB_CODENAME=julia
449  *   DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
450  * Linux Mint also has /etc/linuxmint/info with more information,
451  * but we can use the LSB file.
452  *
453  * Mandriva has:
454  *   LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
455  *   DISTRIB_ID=MandrivaLinux
456  *   DISTRIB_RELEASE=2010.1
457  *   DISTRIB_CODENAME=Henry_Farman
458  *   DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
459  * Mandriva also has a normal release file called /etc/mandriva-release.
460  */
461 static int
462 parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
463 {
464   const char *filename = "/etc/lsb-release";
465   int64_t size;
466   char **lines;
467   size_t i;
468   int r = 0;
469
470   /* Don't trust guestfs_head_n not to break with very large files.
471    * Check the file size is something reasonable first.
472    */
473   size = guestfs_filesize (g, filename);
474   if (size == -1)
475     /* guestfs_filesize failed and has already set error in handle */
476     return -1;
477   if (size > MAX_SMALL_FILE_SIZE) {
478     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
479            filename, size);
480     return -1;
481   }
482
483   lines = guestfs_head_n (g, 10, filename);
484   if (lines == NULL)
485     return -1;
486
487   for (i = 0; lines[i] != NULL; ++i) {
488     if (fs->distro == 0 &&
489         STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
490       fs->distro = OS_DISTRO_UBUNTU;
491       r = 1;
492     }
493     else if (fs->distro == 0 &&
494              STREQ (lines[i], "DISTRIB_ID=LinuxMint")) {
495       fs->distro = OS_DISTRO_LINUX_MINT;
496       r = 1;
497     }
498     else if (fs->distro == 0 &&
499              STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) {
500       fs->distro = OS_DISTRO_MANDRIVA;
501       r = 1;
502     }
503     else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
504       char *major, *minor;
505       if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) {
506         fs->major_version = parse_unsigned_int (g, major);
507         free (major);
508         if (fs->major_version == -1) {
509           free (minor);
510           guestfs___free_string_list (lines);
511           return -1;
512         }
513         fs->minor_version = parse_unsigned_int (g, minor);
514         free (minor);
515         if (fs->minor_version == -1) {
516           guestfs___free_string_list (lines);
517           return -1;
518         }
519       }
520     }
521     else if (fs->product_name == NULL &&
522              (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
523               STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
524       size_t len = strlen (lines[i]) - 21 - 1;
525       fs->product_name = safe_strndup (g, &lines[i][21], len);
526       r = 1;
527     }
528     else if (fs->product_name == NULL &&
529              STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
530       size_t len = strlen (lines[i]) - 20;
531       fs->product_name = safe_strndup (g, &lines[i][20], len);
532       r = 1;
533     }
534   }
535
536   guestfs___free_string_list (lines);
537   return r;
538 }
539
540 /* The currently mounted device is known to be a Linux root.  Try to
541  * determine from this the distro, version, etc.  Also parse
542  * /etc/fstab to determine the arrangement of mountpoints and
543  * associated devices.
544  */
545 static int
546 check_linux_root (guestfs_h *g, struct inspect_fs *fs)
547 {
548   int r;
549
550   fs->type = OS_TYPE_LINUX;
551
552   if (guestfs_exists (g, "/etc/lsb-release") > 0) {
553     r = parse_lsb_release (g, fs);
554     if (r == -1)        /* error */
555       return -1;
556     if (r == 1)         /* ok - detected the release from this file */
557       goto skip_release_checks;
558   }
559
560   if (guestfs_exists (g, "/etc/redhat-release") > 0) {
561     fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */
562
563     if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
564       return -1;
565
566     char *major, *minor;
567     if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
568       fs->distro = OS_DISTRO_FEDORA;
569       fs->major_version = parse_unsigned_int (g, major);
570       free (major);
571       if (fs->major_version == -1)
572         return -1;
573     }
574     else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) ||
575              match2 (g, fs->product_name, re_rhel, &major, &minor)) {
576       fs->distro = OS_DISTRO_RHEL;
577       fs->major_version = parse_unsigned_int (g, major);
578       free (major);
579       if (fs->major_version == -1) {
580         free (minor);
581         return -1;
582       }
583       fs->minor_version = parse_unsigned_int (g, minor);
584       free (minor);
585       if (fs->minor_version == -1)
586         return -1;
587     }
588     else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) {
589       fs->distro = OS_DISTRO_RHEL;
590       fs->major_version = parse_unsigned_int (g, major);
591       free (major);
592       if (fs->major_version == -1)
593         return -1;
594       fs->minor_version = 0;
595     }
596   }
597   else if (guestfs_exists (g, "/etc/debian_version") > 0) {
598     fs->distro = OS_DISTRO_DEBIAN;
599
600     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
601       return -1;
602
603     if (parse_major_minor (g, fs) == -1)
604       return -1;
605   }
606   else if (guestfs_exists (g, "/etc/pardus-release") > 0) {
607     fs->distro = OS_DISTRO_PARDUS;
608
609     if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
610       return -1;
611
612     if (parse_major_minor (g, fs) == -1)
613       return -1;
614   }
615   else if (guestfs_exists (g, "/etc/arch-release") > 0) {
616     fs->distro = OS_DISTRO_ARCHLINUX;
617
618     /* /etc/arch-release file is empty and I can't see a way to
619      * determine the actual release or product string.
620      */
621   }
622   else if (guestfs_exists (g, "/etc/gentoo-release") > 0) {
623     fs->distro = OS_DISTRO_GENTOO;
624
625     if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
626       return -1;
627
628     if (parse_major_minor (g, fs) == -1)
629       return -1;
630   }
631   else if (guestfs_exists (g, "/etc/meego-release") > 0) {
632     fs->distro = OS_DISTRO_MEEGO;
633
634     if (parse_release_file (g, fs, "/etc/meego-release") == -1)
635       return -1;
636
637     if (parse_major_minor (g, fs) == -1)
638       return -1;
639   }
640
641  skip_release_checks:;
642
643   /* If distro test above was successful, work out the package format. */
644   check_package_format (g, fs);
645   check_package_management (g, fs);
646
647   /* Determine the architecture. */
648   check_architecture (g, fs);
649
650   /* We already know /etc/fstab exists because it's part of the test
651    * for Linux root above.  We must now parse this file to determine
652    * which filesystems are used by the operating system and how they
653    * are mounted.
654    */
655   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
656     return -1;
657
658   /* Determine hostname. */
659   if (check_hostname_unix (g, fs) == -1)
660     return -1;
661
662   return 0;
663 }
664
665 /* The currently mounted device is known to be a FreeBSD root. */
666 static int
667 check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
668 {
669   fs->type = OS_TYPE_FREEBSD;
670
671   /* FreeBSD has no authoritative version file.  The version number is
672    * in /etc/motd, which the system administrator might edit, but
673    * we'll use that anyway.
674    */
675
676   if (guestfs_exists (g, "/etc/motd") > 0) {
677     if (parse_release_file (g, fs, "/etc/motd") == -1)
678       return -1;
679
680     if (parse_major_minor (g, fs) == -1)
681       return -1;
682   }
683
684   /* Determine the architecture. */
685   check_architecture (g, fs);
686
687   /* We already know /etc/fstab exists because it's part of the test above. */
688   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
689     return -1;
690
691   /* Determine hostname. */
692   if (check_hostname_unix (g, fs) == -1)
693     return -1;
694
695   return 0;
696 }
697
698 /* Debian/Ubuntu install disks are easy ...
699  *
700  * These files are added by the debian-cd program, and it is worth
701  * looking at the source code to determine exact values, in
702  * particular '/usr/share/debian-cd/tools/start_new_disc'
703  *
704  * XXX Architecture?  We could parse it out of the product name
705  * string, but that seems quite hairy.  We could look for the names
706  * of packages.  Also note that some Debian install disks are
707  * multiarch.
708  */
709 static int
710 check_debian_installer_root (guestfs_h *g, struct inspect_fs *fs)
711 {
712   fs->product_name = first_line_of_file (g, "/.disk/info");
713   if (!fs->product_name)
714     return -1;
715
716   fs->type = OS_TYPE_LINUX;
717   if (STRPREFIX (fs->product_name, "Ubuntu"))
718     fs->distro = OS_DISTRO_UBUNTU;
719   else if (STRPREFIX (fs->product_name, "Debian"))
720     fs->distro = OS_DISTRO_DEBIAN;
721
722   (void) parse_major_minor (g, fs);
723
724   if (guestfs_is_file (g, "/.disk/cd_type") > 0) {
725     char *cd_type = first_line_of_file (g, "/.disk/cd_type");
726     if (!cd_type)
727       return -1;
728
729     if (STRPREFIX (cd_type, "dvd/single") ||
730         STRPREFIX (cd_type, "full_cd/single")) {
731       fs->is_multipart_disk = 0;
732       fs->is_netinst_disk = 0;
733     }
734     else if (STRPREFIX (cd_type, "dvd") ||
735              STRPREFIX (cd_type, "full_cd")) {
736       fs->is_multipart_disk = 1;
737       fs->is_netinst_disk = 0;
738     }
739     else if (STRPREFIX (cd_type, "not_complete")) {
740       fs->is_multipart_disk = 0;
741       fs->is_netinst_disk = 1;
742     }
743
744     free (cd_type);
745   }
746
747   return 0;
748 }
749
750 /* Take string which must look like "key = value" and find the value.
751  * There may or may not be spaces before and after the equals sign.
752  * This function is used by both check_fedora_installer_root and
753  * check_w2k3_installer_root.
754  */
755 static const char *
756 find_value (const char *kv)
757 {
758   const char *p;
759
760   p = strchr (kv, '=');
761   if (!p)
762     abort ();
763
764   do {
765     ++p;
766   } while (c_isspace (*p));
767
768   return p;
769 }
770
771 /* Fedora CDs and DVD (not netinst).  The /.treeinfo file contains
772  * an initial section somewhat like this:
773  *
774  * [general]
775  * version = 14
776  * arch = x86_64
777  * family = Fedora
778  * variant = Fedora
779  * discnum = 1
780  * totaldiscs = 1
781  */
782 static int
783 check_fedora_installer_root (guestfs_h *g, struct inspect_fs *fs)
784 {
785   char *str;
786   const char *v;
787   int r;
788   int discnum = 0, totaldiscs = 0;
789
790   fs->type = OS_TYPE_LINUX;
791
792   r = first_egrep_of_file (g, "/.treeinfo",
793                            "^family = Fedora$", 0, &str);
794   if (r == -1)
795     return -1;
796   if (r > 0) {
797     fs->distro = OS_DISTRO_FEDORA;
798     free (str);
799   }
800
801   r = first_egrep_of_file (g, "/.treeinfo",
802                            "^family = Red Hat Enterprise Linux$", 0, &str);
803   if (r == -1)
804     return -1;
805   if (r > 0) {
806     fs->distro = OS_DISTRO_RHEL;
807     free (str);
808   }
809
810   /* XXX should do major.minor before this */
811   r = first_egrep_of_file (g, "/.treeinfo",
812                            "^version = [[:digit:]]+", 0, &str);
813   if (r == -1)
814     return -1;
815   if (r > 0) {
816     v = find_value (str);
817     fs->major_version = parse_unsigned_int_ignore_trailing (g, v);
818     free (str);
819     if (fs->major_version == -1)
820       return -1;
821   }
822
823   r = first_egrep_of_file (g, "/.treeinfo",
824                            "^arch = [-_[:alnum:]]+$", 0, &str);
825   if (r == -1)
826     return -1;
827   if (r > 0) {
828     v = find_value (str);
829     fs->arch = safe_strdup (g, v);
830     free (str);
831   }
832
833   r = first_egrep_of_file (g, "/.treeinfo",
834                            "^discnum = [[:digit:]]+$", 0, &str);
835   if (r == -1)
836     return -1;
837   if (r > 0) {
838     v = find_value (str);
839     discnum = parse_unsigned_int (g, v);
840     free (str);
841     if (discnum == -1)
842       return -1;
843   }
844
845   r = first_egrep_of_file (g, "/.treeinfo",
846                            "^totaldiscs = [[:digit:]]+$", 0, &str);
847   if (r == -1)
848     return -1;
849   if (r > 0) {
850     v = find_value (str);
851     totaldiscs = parse_unsigned_int (g, v);
852     free (str);
853     if (totaldiscs == -1)
854       return -1;
855   }
856
857   fs->is_multipart_disk = totaldiscs > 0;
858   /* and what about discnum? */
859
860   return 0;
861 }
862
863 /* Linux with /isolinux/isolinux.cfg.
864  *
865  * This file is not easily parsable so we have to do our best.
866  * Look for the "menu title" line which contains:
867  *   menu title Welcome to Fedora 14!   # since at least Fedora 10
868  *   menu title Welcome to Red Hat Enterprise Linux 6.0!
869  */
870 static int
871 check_isolinux_installer_root (guestfs_h *g, struct inspect_fs *fs)
872 {
873   char *str;
874   int r;
875
876   fs->type = OS_TYPE_LINUX;
877
878   r = first_egrep_of_file (g, "/isolinux/isolinux.cfg",
879                            "^menu title Welcome to Fedora [[:digit:]]+",
880                            0, &str);
881   if (r == -1)
882     return -1;
883   if (r > 0) {
884     fs->distro = OS_DISTRO_FEDORA;
885     fs->major_version = parse_unsigned_int_ignore_trailing (g, &str[29]);
886     free (str);
887     if (fs->major_version == -1)
888       return -1;
889   }
890
891   /* XXX parse major.minor */
892   r = first_egrep_of_file (g, "/isolinux/isolinux.cfg",
893                            "^menu title Welcome to Red Hat Enterprise Linux [[:digit:]]+",
894                            0, &str);
895   if (r == -1)
896     return -1;
897   if (r > 0) {
898     fs->distro = OS_DISTRO_RHEL;
899     fs->major_version = parse_unsigned_int_ignore_trailing (g, &str[47]);
900     free (str);
901     if (fs->major_version == -1)
902       return -1;
903   }
904
905   return 0;
906 }
907
908 /* Windows 2003 and similar versions.
909  *
910  * NB: txtsetup file contains Windows \r\n line endings, which guestfs_grep
911  * does not remove.  We have to remove them by hand here.
912  */
913 static void
914 trim_cr (char *str)
915 {
916   size_t n = strlen (str);
917   if (n > 0 && str[n-1] == '\r')
918     str[n-1] = '\0';
919 }
920
921 static void
922 trim_quot (char *str)
923 {
924   size_t n = strlen (str);
925   if (n > 0 && str[n-1] == '"')
926     str[n-1] = '\0';
927 }
928
929 static int
930 check_w2k3_installer_root (guestfs_h *g, struct inspect_fs *fs,
931                            const char *txtsetup)
932 {
933   char *str;
934   const char *v;
935   int r;
936
937   fs->type = OS_TYPE_WINDOWS;
938   fs->distro = OS_DISTRO_WINDOWS;
939
940   r = first_egrep_of_file (g, txtsetup,
941                            "^productname[[:space:]]*=[[:space:]]*\"", 1, &str);
942   if (r == -1)
943     return -1;
944   if (r > 0) {
945     trim_cr (str);
946     trim_quot (str);
947     v = find_value (str);
948     fs->product_name = safe_strdup (g, v+1);
949     free (str);
950   }
951
952   r = first_egrep_of_file (g, txtsetup,
953                            "^majorversion[[:space:]]*=[[:space:]]*[[:digit:]]+",
954                            1, &str);
955   if (r == -1)
956     return -1;
957   if (r > 0) {
958     trim_cr (str);
959     v = find_value (str);
960     fs->major_version = parse_unsigned_int_ignore_trailing (g, v);
961     free (str);
962     if (fs->major_version == -1)
963       return -1;
964   }
965
966   r = first_egrep_of_file (g, txtsetup,
967                            "^minorversion[[:space:]]*=[[:space:]]*[[:digit:]]+",
968                            1, &str);
969   if (r == -1)
970     return -1;
971   if (r > 0) {
972     trim_cr (str);
973     v = find_value (str);
974     fs->minor_version = parse_unsigned_int_ignore_trailing (g, v);
975     free (str);
976     if (fs->minor_version == -1)
977       return -1;
978   }
979
980   /* This is the windows systemroot that would be chosen on
981    * installation by default, although not necessarily the one that
982    * the user will finally choose.
983    */
984   r = first_egrep_of_file (g, txtsetup, "^defaultpath[[:space:]]*=[[:space:]]*",
985                            1, &str);
986   if (r == -1)
987     return -1;
988   if (r > 0) {
989     trim_cr (str);
990     v = find_value (str);
991     fs->windows_systemroot = safe_strdup (g, v);
992     free (str);
993   }
994
995   return 0;
996 }
997
998 /* The currently mounted device is very likely to be an installer. */
999 static int
1000 check_installer_root (guestfs_h *g, struct inspect_fs *fs)
1001 {
1002   /* The presence of certain files indicates a live CD.
1003    *
1004    * XXX Fedora netinst contains a ~120MB squashfs called
1005    * /images/install.img.  However this is not a live CD (unlike the
1006    * Fedora live CDs which contain the same, but larger file).  We
1007    * need to unpack this and look inside to tell the difference.
1008    */
1009   if (guestfs_is_file (g, "/casper/filesystem.squashfs") > 0)
1010     fs->is_live_disk = 1;
1011
1012   /* Debian/Ubuntu. */
1013   if (guestfs_is_file (g, "/.disk/info") > 0) {
1014     if (check_debian_installer_root (g, fs) == -1)
1015       return -1;
1016   }
1017
1018   /* Fedora CDs and DVD (not netinst). */
1019   else if (guestfs_is_file (g, "/.treeinfo") > 0) {
1020     if (check_fedora_installer_root (g, fs) == -1)
1021       return -1;
1022   }
1023
1024   /* Linux with /isolinux/isolinux.cfg. */
1025   else if (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0) {
1026     if (check_isolinux_installer_root (g, fs) == -1)
1027       return -1;
1028   }
1029
1030   /* Windows 2003 64 bit */
1031   else if (guestfs_is_file (g, "/amd64/txtsetup.sif") > 0) {
1032     fs->arch = safe_strdup (g, "x86_64");
1033     if (check_w2k3_installer_root (g, fs, "/amd64/txtsetup.sif") == -1)
1034       return -1;
1035   }
1036
1037   /* Windows 2003 32 bit */
1038   else if (guestfs_is_file (g, "/i386/txtsetup.sif") > 0) {
1039     fs->arch = safe_strdup (g, "i386");
1040     if (check_w2k3_installer_root (g, fs, "/i386/txtsetup.sif") == -1)
1041       return -1;
1042   }
1043
1044   return 0;
1045 }
1046
1047 static void
1048 check_architecture (guestfs_h *g, struct inspect_fs *fs)
1049 {
1050   const char *binaries[] =
1051     { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" };
1052   size_t i;
1053
1054   for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
1055     if (guestfs_is_file (g, binaries[i]) > 0) {
1056       /* Ignore errors from file_architecture call. */
1057       guestfs_error_handler_cb old_error_cb = g->error_cb;
1058       g->error_cb = NULL;
1059       char *arch = guestfs_file_architecture (g, binaries[i]);
1060       g->error_cb = old_error_cb;
1061
1062       if (arch) {
1063         /* String will be owned by handle, freed by
1064          * guestfs___free_inspect_info.
1065          */
1066         fs->arch = arch;
1067         break;
1068       }
1069     }
1070   }
1071 }
1072
1073 /* Try several methods to determine the hostname from a Linux or
1074  * FreeBSD guest.  Note that type and distro have been set, so we can
1075  * use that information to direct the search.
1076  */
1077 static int
1078 check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
1079 {
1080   switch (fs->type) {
1081   case OS_TYPE_LINUX:
1082     /* Red Hat-derived would be in /etc/sysconfig/network, and
1083      * Debian-derived in the file /etc/hostname.  Very old Debian and
1084      * SUSE use /etc/HOSTNAME.  It's best to just look for each of
1085      * these files in turn, rather than try anything clever based on
1086      * distro.
1087      */
1088     if (guestfs_is_file (g, "/etc/HOSTNAME")) {
1089       fs->hostname = first_line_of_file (g, "/etc/HOSTNAME");
1090       if (fs->hostname == NULL)
1091         return -1;
1092     }
1093     else if (guestfs_is_file (g, "/etc/hostname")) {
1094       fs->hostname = first_line_of_file (g, "/etc/hostname");
1095       if (fs->hostname == NULL)
1096         return -1;
1097     }
1098     else if (guestfs_is_file (g, "/etc/sysconfig/network")) {
1099       if (inspect_with_augeas (g, fs, "/etc/sysconfig/network",
1100                                check_hostname_redhat) == -1)
1101         return -1;
1102     }
1103     break;
1104
1105   case OS_TYPE_FREEBSD:
1106     /* /etc/rc.conf contains the hostname, but there is no Augeas lens
1107      * for this file.
1108      */
1109     if (guestfs_is_file (g, "/etc/rc.conf")) {
1110       if (check_hostname_freebsd (g, fs) == -1)
1111         return -1;
1112     }
1113     break;
1114
1115   case OS_TYPE_WINDOWS: /* not here, see check_windows_system_registry */
1116   case OS_TYPE_UNKNOWN:
1117   default:
1118     /* nothing, keep GCC warnings happy */;
1119   }
1120
1121   return 0;
1122 }
1123
1124 /* Parse the hostname from /etc/sysconfig/network.  This must be called
1125  * from the inspect_with_augeas wrapper.
1126  */
1127 static int
1128 check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs)
1129 {
1130   char *hostname;
1131
1132   hostname = guestfs_aug_get (g, "/files/etc/sysconfig/network/HOSTNAME");
1133   if (!hostname)
1134     return -1;
1135
1136   fs->hostname = hostname;  /* freed by guestfs___free_inspect_info */
1137   return 0;
1138 }
1139
1140 /* Parse the hostname from /etc/rc.conf.  On FreeBSD this file
1141  * contains comments, blank lines and:
1142  *   hostname="freebsd8.example.com"
1143  *   ifconfig_re0="DHCP"
1144  *   keymap="uk.iso"
1145  *   sshd_enable="YES"
1146  */
1147 static int
1148 check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs)
1149 {
1150   const char *filename = "/etc/rc.conf";
1151   int64_t size;
1152   char **lines;
1153   size_t i;
1154
1155   /* Don't trust guestfs_read_lines not to break with very large files.
1156    * Check the file size is something reasonable first.
1157    */
1158   size = guestfs_filesize (g, filename);
1159   if (size == -1)
1160     /* guestfs_filesize failed and has already set error in handle */
1161     return -1;
1162   if (size > MAX_SMALL_FILE_SIZE) {
1163     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1164            filename, size);
1165     return -1;
1166   }
1167
1168   lines = guestfs_read_lines (g, filename);
1169   if (lines == NULL)
1170     return -1;
1171
1172   for (i = 0; lines[i] != NULL; ++i) {
1173     if (STRPREFIX (lines[i], "hostname=\"") ||
1174         STRPREFIX (lines[i], "hostname='")) {
1175       size_t len = strlen (lines[i]) - 10 - 1;
1176       fs->hostname = safe_strndup (g, &lines[i][10], len);
1177       break;
1178     } else if (STRPREFIX (lines[i], "hostname=")) {
1179       size_t len = strlen (lines[i]) - 9;
1180       fs->hostname = safe_strndup (g, &lines[i][9], len);
1181       break;
1182     }
1183   }
1184
1185   guestfs___free_string_list (lines);
1186   return 0;
1187 }
1188
1189 static int
1190 check_fstab (guestfs_h *g, struct inspect_fs *fs)
1191 {
1192   char **lines = guestfs_aug_ls (g, "/files/etc/fstab");
1193   if (lines == NULL)
1194     return -1;
1195
1196   if (lines[0] == NULL) {
1197     error (g, _("could not parse /etc/fstab or empty file"));
1198     guestfs___free_string_list (lines);
1199     return -1;
1200   }
1201
1202   size_t i;
1203   char augpath[256];
1204   for (i = 0; lines[i] != NULL; ++i) {
1205     /* Ignore comments.  Only care about sequence lines which
1206      * match m{/\d+$}.
1207      */
1208     if (match (g, lines[i], re_aug_seq)) {
1209       snprintf (augpath, sizeof augpath, "%s/spec", lines[i]);
1210       char *spec = guestfs_aug_get (g, augpath);
1211       if (spec == NULL) {
1212         guestfs___free_string_list (lines);
1213         return -1;
1214       }
1215
1216       snprintf (augpath, sizeof augpath, "%s/file", lines[i]);
1217       char *mp = guestfs_aug_get (g, augpath);
1218       if (mp == NULL) {
1219         guestfs___free_string_list (lines);
1220         free (spec);
1221         return -1;
1222       }
1223
1224       int r = add_fstab_entry (g, fs, spec, mp);
1225       free (spec);
1226       free (mp);
1227
1228       if (r == -1) {
1229         guestfs___free_string_list (lines);
1230         return -1;
1231       }
1232     }
1233   }
1234
1235   guestfs___free_string_list (lines);
1236   return 0;
1237 }
1238
1239 /* Add a filesystem and possibly a mountpoint entry for
1240  * the root filesystem 'fs'.
1241  *
1242  * 'spec' is the fstab spec field, which might be a device name or a
1243  * pseudodevice or 'UUID=...' or 'LABEL=...'.
1244  *
1245  * 'mp' is the mount point, which could also be 'swap' or 'none'.
1246  */
1247 static int
1248 add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
1249                  const char *spec, const char *mp)
1250 {
1251   /* Ignore certain mountpoints. */
1252   if (STRPREFIX (mp, "/dev/") ||
1253       STREQ (mp, "/dev") ||
1254       STRPREFIX (mp, "/media/") ||
1255       STRPREFIX (mp, "/proc/") ||
1256       STREQ (mp, "/proc") ||
1257       STRPREFIX (mp, "/selinux/") ||
1258       STREQ (mp, "/selinux") ||
1259       STRPREFIX (mp, "/sys/") ||
1260       STREQ (mp, "/sys"))
1261     return 0;
1262
1263   /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */
1264   if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) ||
1265       STREQ (spec, "/dev/floppy") ||
1266       STREQ (spec, "/dev/cdrom"))
1267     return 0;
1268
1269   /* Resolve UUID= and LABEL= to the actual device. */
1270   char *device = NULL;
1271   if (STRPREFIX (spec, "UUID="))
1272     device = guestfs_findfs_uuid (g, &spec[5]);
1273   else if (STRPREFIX (spec, "LABEL="))
1274     device = guestfs_findfs_label (g, &spec[6]);
1275   /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */
1276   else if (STRPREFIX (spec, "/dev/"))
1277     /* Resolve guest block device names. */
1278     device = resolve_fstab_device (g, spec);
1279
1280   /* If we haven't resolved the device successfully by this point,
1281    * we don't care, just ignore it.
1282    */
1283   if (device == NULL)
1284     return 0;
1285
1286   char *mountpoint = safe_strdup (g, mp);
1287
1288   /* Add this to the fstab entry in 'fs'.
1289    * Note these are further filtered by guestfs_inspect_get_mountpoints
1290    * and guestfs_inspect_get_filesystems.
1291    */
1292   size_t n = fs->nr_fstab + 1;
1293   struct inspect_fstab_entry *p;
1294
1295   p = realloc (fs->fstab, n * sizeof (struct inspect_fstab_entry));
1296   if (p == NULL) {
1297     perrorf (g, "realloc");
1298     free (device);
1299     free (mountpoint);
1300     return -1;
1301   }
1302
1303   fs->fstab = p;
1304   fs->nr_fstab = n;
1305
1306   /* These are owned by the handle and freed by guestfs___free_inspect_info. */
1307   fs->fstab[n-1].device = device;
1308   fs->fstab[n-1].mountpoint = mountpoint;
1309
1310   if (g->verbose)
1311     fprintf (stderr, "fstab: device=%s mountpoint=%s\n", device, mountpoint);
1312
1313   return 0;
1314 }
1315
1316 /* Resolve block device name to the libguestfs device name, eg.
1317  * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV.  This
1318  * assumes that disks were added in the same order as they appear to
1319  * the real VM, which is a reasonable assumption to make.  Return
1320  * anything we don't recognize unchanged.
1321  */
1322 static char *
1323 resolve_fstab_device (guestfs_h *g, const char *spec)
1324 {
1325   char *a1;
1326   char *device = NULL;
1327   char *bsddisk, *bsdslice, *bsdpart;
1328
1329   if (STRPREFIX (spec, "/dev/mapper/")) {
1330     /* LVM2 does some strange munging on /dev/mapper paths for VGs and
1331      * LVs which contain '-' character:
1332      *
1333      * ><fs> lvcreate LV--test VG--test 32
1334      * ><fs> debug ls /dev/mapper
1335      * VG----test-LV----test
1336      *
1337      * This makes it impossible to reverse those paths directly, so
1338      * we have implemented lvm_canonical_lv_name in the daemon.
1339      */
1340     device = guestfs_lvm_canonical_lv_name (g, spec);
1341   }
1342   else if ((a1 = match1 (g, spec, re_xdev)) != NULL) {
1343     char **devices = guestfs_list_devices (g);
1344     if (devices == NULL)
1345       return NULL;
1346
1347     size_t count;
1348     for (count = 0; devices[count] != NULL; count++)
1349       ;
1350
1351     size_t i = a1[0] - 'a'; /* a1[0] is always [a-z] because of regex. */
1352     if (i < count) {
1353       size_t len = strlen (devices[i]) + strlen (a1) + 16;
1354       device = safe_malloc (g, len);
1355       snprintf (device, len, "%s%s", devices[i], &a1[1]);
1356     }
1357
1358     free (a1);
1359     guestfs___free_string_list (devices);
1360   }
1361   else if (match3 (g, spec, re_freebsd, &bsddisk, &bsdslice, &bsdpart)) {
1362     /* FreeBSD disks are organized quite differently.  See:
1363      * http://www.freebsd.org/doc/handbook/disk-organization.html
1364      * FreeBSD "partitions" are exposed as quasi-extended partitions
1365      * numbered from 5 in Linux.  I have no idea what happens when you
1366      * have multiple "slices" (the FreeBSD term for MBR partitions).
1367      */
1368     int disk = parse_unsigned_int (g, bsddisk);
1369     int slice = parse_unsigned_int (g, bsdslice);
1370     int part = bsdpart[0] - 'a' /* counting from 0 */;
1371     free (bsddisk);
1372     free (bsdslice);
1373     free (bsdpart);
1374
1375     if (disk == -1 || disk > 26 ||
1376         slice <= 0 || slice > 1 /* > 4 .. see comment above */ ||
1377         part < 0 || part >= 26)
1378       goto out;
1379
1380     device = safe_asprintf (g, "/dev/sd%c%d", disk + 'a', part + 5);
1381   }
1382
1383  out:
1384   /* Didn't match device pattern, return original spec unchanged. */
1385   if (device == NULL)
1386     device = safe_strdup (g, spec);
1387
1388   return device;
1389 }
1390
1391 /* XXX Handling of boot.ini in the Perl version was pretty broken.  It
1392  * essentially didn't do anything for modern Windows guests.
1393  * Therefore I've omitted all that code.
1394  */
1395 static int
1396 check_windows_root (guestfs_h *g, struct inspect_fs *fs)
1397 {
1398   fs->type = OS_TYPE_WINDOWS;
1399   fs->distro = OS_DISTRO_WINDOWS;
1400
1401   /* Try to find Windows systemroot using some common locations. */
1402   const char *systemroots[] =
1403     { "/windows", "/winnt", "/win32", "/win" };
1404   size_t i;
1405   char *systemroot = NULL;
1406   for (i = 0;
1407        systemroot == NULL && i < sizeof systemroots / sizeof systemroots[0];
1408        ++i) {
1409     systemroot = resolve_windows_path_silently (g, systemroots[i]);
1410   }
1411
1412   if (!systemroot) {
1413     error (g, _("cannot resolve Windows %%SYSTEMROOT%%"));
1414     return -1;
1415   }
1416
1417   if (g->verbose)
1418     fprintf (stderr, "windows %%SYSTEMROOT%% = %s", systemroot);
1419
1420   /* Freed by guestfs___free_inspect_info. */
1421   fs->windows_systemroot = systemroot;
1422
1423   if (check_windows_arch (g, fs) == -1)
1424     return -1;
1425
1426   /* Product name and version. */
1427   if (check_windows_software_registry (g, fs) == -1)
1428     return -1;
1429
1430   check_package_format (g, fs);
1431   check_package_management (g, fs);
1432
1433   /* Hostname. */
1434   if (check_windows_system_registry (g, fs) == -1)
1435     return -1;
1436
1437   return 0;
1438 }
1439
1440 static int
1441 check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
1442 {
1443   size_t len = strlen (fs->windows_systemroot) + 32;
1444   char cmd_exe[len];
1445   snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
1446
1447   char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe);
1448   if (!cmd_exe_path)
1449     return 0;
1450
1451   char *arch = guestfs_file_architecture (g, cmd_exe_path);
1452   free (cmd_exe_path);
1453
1454   if (arch)
1455     fs->arch = arch;        /* freed by guestfs___free_inspect_info */
1456
1457   return 0;
1458 }
1459
1460 /* At the moment, pull just the ProductName and version numbers from
1461  * the registry.  In future there is a case for making many more
1462  * registry fields available to callers.
1463  */
1464 static int
1465 check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
1466 {
1467   TMP_TEMPLATE_ON_STACK (software_local);
1468
1469   size_t len = strlen (fs->windows_systemroot) + 64;
1470   char software[len];
1471   snprintf (software, len, "%s/system32/config/software",
1472             fs->windows_systemroot);
1473
1474   char *software_path = resolve_windows_path_silently (g, software);
1475   if (!software_path)
1476     /* If the software hive doesn't exist, just accept that we cannot
1477      * find product_name etc.
1478      */
1479     return 0;
1480
1481   int ret = -1;
1482   hive_h *h = NULL;
1483   hive_value_h *values = NULL;
1484
1485   if (download_to_tmp (g, software_path, software_local,
1486                        MAX_REGISTRY_SIZE) == -1)
1487     goto out;
1488
1489   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
1490   if (h == NULL) {
1491     perrorf (g, "hivex_open");
1492     goto out;
1493   }
1494
1495   hive_node_h node = hivex_root (h);
1496   const char *hivepath[] =
1497     { "Microsoft", "Windows NT", "CurrentVersion" };
1498   size_t i;
1499   for (i = 0;
1500        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
1501        ++i) {
1502     node = hivex_node_get_child (h, node, hivepath[i]);
1503   }
1504
1505   if (node == 0) {
1506     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
1507     goto out;
1508   }
1509
1510   values = hivex_node_values (h, node);
1511
1512   for (i = 0; values[i] != 0; ++i) {
1513     char *key = hivex_value_key (h, values[i]);
1514     if (key == NULL) {
1515       perrorf (g, "hivex_value_key");
1516       goto out;
1517     }
1518
1519     if (STRCASEEQ (key, "ProductName")) {
1520       fs->product_name = hivex_value_string (h, values[i]);
1521       if (!fs->product_name) {
1522         perrorf (g, "hivex_value_string");
1523         free (key);
1524         goto out;
1525       }
1526     }
1527     else if (STRCASEEQ (key, "CurrentVersion")) {
1528       char *version = hivex_value_string (h, values[i]);
1529       if (!version) {
1530         perrorf (g, "hivex_value_string");
1531         free (key);
1532         goto out;
1533       }
1534       char *major, *minor;
1535       if (match2 (g, version, re_windows_version, &major, &minor)) {
1536         fs->major_version = parse_unsigned_int (g, major);
1537         free (major);
1538         if (fs->major_version == -1) {
1539           free (minor);
1540           free (key);
1541           free (version);
1542           goto out;
1543         }
1544         fs->minor_version = parse_unsigned_int (g, minor);
1545         free (minor);
1546         if (fs->minor_version == -1) {
1547           free (key);
1548           free (version);
1549           goto out;
1550         }
1551       }
1552
1553       free (version);
1554     }
1555
1556     free (key);
1557   }
1558
1559   ret = 0;
1560
1561  out:
1562   if (h) hivex_close (h);
1563   free (values);
1564   free (software_path);
1565
1566   /* Free up the temporary file. */
1567   unlink (software_local);
1568 #undef software_local_len
1569
1570   return ret;
1571 }
1572
1573 static int
1574 check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
1575 {
1576   TMP_TEMPLATE_ON_STACK (system_local);
1577
1578   size_t len = strlen (fs->windows_systemroot) + 64;
1579   char system[len];
1580   snprintf (system, len, "%s/system32/config/system",
1581             fs->windows_systemroot);
1582
1583   char *system_path = resolve_windows_path_silently (g, system);
1584   if (!system_path)
1585     /* If the system hive doesn't exist, just accept that we cannot
1586      * find hostname etc.
1587      */
1588     return 0;
1589
1590   int ret = -1;
1591   hive_h *h = NULL;
1592   hive_value_h *values = NULL;
1593
1594   if (download_to_tmp (g, system_path, system_local, MAX_REGISTRY_SIZE) == -1)
1595     goto out;
1596
1597   h = hivex_open (system_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
1598   if (h == NULL) {
1599     perrorf (g, "hivex_open");
1600     goto out;
1601   }
1602
1603   hive_node_h node = hivex_root (h);
1604   /* XXX Don't hard-code ControlSet001.  The current control set would
1605    * be another good thing to expose up through the inspection API.
1606    */
1607   const char *hivepath[] =
1608     { "ControlSet001", "Services", "Tcpip", "Parameters" };
1609   size_t i;
1610   for (i = 0;
1611        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
1612        ++i) {
1613     node = hivex_node_get_child (h, node, hivepath[i]);
1614   }
1615
1616   if (node == 0) {
1617     perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters");
1618     goto out;
1619   }
1620
1621   values = hivex_node_values (h, node);
1622
1623   for (i = 0; values[i] != 0; ++i) {
1624     char *key = hivex_value_key (h, values[i]);
1625     if (key == NULL) {
1626       perrorf (g, "hivex_value_key");
1627       goto out;
1628     }
1629
1630     if (STRCASEEQ (key, "Hostname")) {
1631       fs->hostname = hivex_value_string (h, values[i]);
1632       if (!fs->hostname) {
1633         perrorf (g, "hivex_value_string");
1634         free (key);
1635         goto out;
1636       }
1637     }
1638     /* many other interesting fields here ... */
1639
1640     free (key);
1641   }
1642
1643   ret = 0;
1644
1645  out:
1646   if (h) hivex_close (h);
1647   free (values);
1648   free (system_path);
1649
1650   /* Free up the temporary file. */
1651   unlink (system_local);
1652 #undef system_local_len
1653
1654   return ret;
1655 }
1656
1657 static char *
1658 resolve_windows_path_silently (guestfs_h *g, const char *path)
1659 {
1660   guestfs_error_handler_cb old_error_cb = g->error_cb;
1661   g->error_cb = NULL;
1662   char *ret = guestfs_case_sensitive_path (g, path);
1663   g->error_cb = old_error_cb;
1664   return ret;
1665 }
1666
1667 static int
1668 extend_fses (guestfs_h *g)
1669 {
1670   size_t n = g->nr_fses + 1;
1671   struct inspect_fs *p;
1672
1673   p = realloc (g->fses, n * sizeof (struct inspect_fs));
1674   if (p == NULL) {
1675     perrorf (g, "realloc");
1676     return -1;
1677   }
1678
1679   g->fses = p;
1680   g->nr_fses = n;
1681
1682   memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
1683
1684   return 0;
1685 }
1686
1687 /* Parse small, unsigned ints, as used in version numbers. */
1688 static int
1689 parse_unsigned_int (guestfs_h *g, const char *str)
1690 {
1691   long ret;
1692   int r = xstrtol (str, NULL, 10, &ret, "");
1693   if (r != LONGINT_OK) {
1694     error (g, _("could not parse integer in version number: %s"), str);
1695     return -1;
1696   }
1697   return ret;
1698 }
1699
1700 /* Like parse_unsigned_int, but ignore trailing stuff. */
1701 static int
1702 parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str)
1703 {
1704   long ret;
1705   int r = xstrtol (str, NULL, 10, &ret, NULL);
1706   if (r != LONGINT_OK) {
1707     error (g, _("could not parse integer in version number: %s"), str);
1708     return -1;
1709   }
1710   return ret;
1711 }
1712
1713 /* At the moment, package format and package management is just a
1714  * simple function of the distro and major_version fields, so these
1715  * can never return an error.  We might be cleverer in future.
1716  */
1717 static void
1718 check_package_format (guestfs_h *g, struct inspect_fs *fs)
1719 {
1720   switch (fs->distro) {
1721   case OS_DISTRO_FEDORA:
1722   case OS_DISTRO_MEEGO:
1723   case OS_DISTRO_REDHAT_BASED:
1724   case OS_DISTRO_RHEL:
1725   case OS_DISTRO_MANDRIVA:
1726     fs->package_format = OS_PACKAGE_FORMAT_RPM;
1727     break;
1728
1729   case OS_DISTRO_DEBIAN:
1730   case OS_DISTRO_UBUNTU:
1731   case OS_DISTRO_LINUX_MINT:
1732     fs->package_format = OS_PACKAGE_FORMAT_DEB;
1733     break;
1734
1735   case OS_DISTRO_ARCHLINUX:
1736     fs->package_format = OS_PACKAGE_FORMAT_PACMAN;
1737     break;
1738   case OS_DISTRO_GENTOO:
1739     fs->package_format = OS_PACKAGE_FORMAT_EBUILD;
1740     break;
1741   case OS_DISTRO_PARDUS:
1742     fs->package_format = OS_PACKAGE_FORMAT_PISI;
1743     break;
1744
1745   case OS_DISTRO_WINDOWS:
1746   case OS_DISTRO_UNKNOWN:
1747   default:
1748     fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN;
1749     break;
1750   }
1751 }
1752
1753 static void
1754 check_package_management (guestfs_h *g, struct inspect_fs *fs)
1755 {
1756   switch (fs->distro) {
1757   case OS_DISTRO_FEDORA:
1758   case OS_DISTRO_MEEGO:
1759     fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1760     break;
1761
1762   case OS_DISTRO_REDHAT_BASED:
1763   case OS_DISTRO_RHEL:
1764     if (fs->major_version >= 5)
1765       fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1766     else
1767       fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE;
1768     break;
1769
1770   case OS_DISTRO_DEBIAN:
1771   case OS_DISTRO_UBUNTU:
1772   case OS_DISTRO_LINUX_MINT:
1773     fs->package_management = OS_PACKAGE_MANAGEMENT_APT;
1774     break;
1775
1776   case OS_DISTRO_ARCHLINUX:
1777     fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN;
1778     break;
1779   case OS_DISTRO_GENTOO:
1780     fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE;
1781     break;
1782   case OS_DISTRO_PARDUS:
1783     fs->package_management = OS_PACKAGE_MANAGEMENT_PISI;
1784     break;
1785   case OS_DISTRO_MANDRIVA:
1786     fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI;
1787     break;
1788
1789   case OS_DISTRO_WINDOWS:
1790   case OS_DISTRO_UNKNOWN:
1791   default:
1792     fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
1793     break;
1794   }
1795 }
1796
1797 static struct inspect_fs *
1798 search_for_root (guestfs_h *g, const char *root)
1799 {
1800   if (g->nr_fses == 0) {
1801     error (g, _("no inspection data: call guestfs_inspect_os first"));
1802     return NULL;
1803   }
1804
1805   size_t i;
1806   struct inspect_fs *fs;
1807   for (i = 0; i < g->nr_fses; ++i) {
1808     fs = &g->fses[i];
1809     if (fs->is_root && STREQ (root, fs->device))
1810       return fs;
1811   }
1812
1813   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
1814          root);
1815   return NULL;
1816 }
1817
1818 char **
1819 guestfs__inspect_get_roots (guestfs_h *g)
1820 {
1821   /* NB. Doesn't matter if g->nr_fses == 0.  We just return an empty
1822    * list in this case.
1823    */
1824
1825   size_t i;
1826   size_t count = 0;
1827   for (i = 0; i < g->nr_fses; ++i)
1828     if (g->fses[i].is_root)
1829       count++;
1830
1831   char **ret = calloc (count+1, sizeof (char *));
1832   if (ret == NULL) {
1833     perrorf (g, "calloc");
1834     return NULL;
1835   }
1836
1837   count = 0;
1838   for (i = 0; i < g->nr_fses; ++i) {
1839     if (g->fses[i].is_root) {
1840       ret[count] = safe_strdup (g, g->fses[i].device);
1841       count++;
1842     }
1843   }
1844   ret[count] = NULL;
1845
1846   return ret;
1847 }
1848
1849 char *
1850 guestfs__inspect_get_type (guestfs_h *g, const char *root)
1851 {
1852   struct inspect_fs *fs = search_for_root (g, root);
1853   if (!fs)
1854     return NULL;
1855
1856   char *ret;
1857   switch (fs->type) {
1858   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
1859   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
1860   case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
1861   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1862   }
1863
1864   return ret;
1865 }
1866
1867 char *
1868 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1869 {
1870   struct inspect_fs *fs = search_for_root (g, root);
1871   if (!fs)
1872     return NULL;
1873
1874   return safe_strdup (g, fs->arch ? : "unknown");
1875 }
1876
1877 char *
1878 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1879 {
1880   struct inspect_fs *fs = search_for_root (g, root);
1881   if (!fs)
1882     return NULL;
1883
1884   char *ret;
1885   switch (fs->distro) {
1886   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
1887   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
1888   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
1889   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
1890   case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
1891   case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
1892   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
1893   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
1894   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
1895   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
1896   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
1897   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
1898   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1899   }
1900
1901   return ret;
1902 }
1903
1904 int
1905 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1906 {
1907   struct inspect_fs *fs = search_for_root (g, root);
1908   if (!fs)
1909     return -1;
1910
1911   return fs->major_version;
1912 }
1913
1914 int
1915 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1916 {
1917   struct inspect_fs *fs = search_for_root (g, root);
1918   if (!fs)
1919     return -1;
1920
1921   return fs->minor_version;
1922 }
1923
1924 char *
1925 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1926 {
1927   struct inspect_fs *fs = search_for_root (g, root);
1928   if (!fs)
1929     return NULL;
1930
1931   return safe_strdup (g, fs->product_name ? : "unknown");
1932 }
1933
1934 char *
1935 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1936 {
1937   struct inspect_fs *fs = search_for_root (g, root);
1938   if (!fs)
1939     return NULL;
1940
1941   if (!fs->windows_systemroot) {
1942     error (g, _("not a Windows guest, or systemroot could not be determined"));
1943     return NULL;
1944   }
1945
1946   return safe_strdup (g, fs->windows_systemroot);
1947 }
1948
1949 char *
1950 guestfs__inspect_get_format (guestfs_h *g, const char *root)
1951 {
1952   struct inspect_fs *fs = search_for_root (g, root);
1953   if (!fs)
1954     return NULL;
1955
1956   char *ret;
1957   switch (fs->format) {
1958   case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break;
1959   case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break;
1960   case OS_FORMAT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1961   }
1962
1963   return ret;
1964 }
1965
1966 int
1967 guestfs__inspect_is_live (guestfs_h *g, const char *root)
1968 {
1969   struct inspect_fs *fs = search_for_root (g, root);
1970   if (!fs)
1971     return -1;
1972
1973   return fs->is_live_disk;
1974 }
1975
1976 int
1977 guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
1978 {
1979   struct inspect_fs *fs = search_for_root (g, root);
1980   if (!fs)
1981     return -1;
1982
1983   return fs->is_netinst_disk;
1984 }
1985
1986 int
1987 guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
1988 {
1989   struct inspect_fs *fs = search_for_root (g, root);
1990   if (!fs)
1991     return -1;
1992
1993   return fs->is_multipart_disk;
1994 }
1995
1996 char **
1997 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1998 {
1999   struct inspect_fs *fs = search_for_root (g, root);
2000   if (!fs)
2001     return NULL;
2002
2003   char **ret;
2004
2005   /* If no fstab information (Windows) return just the root. */
2006   if (fs->nr_fstab == 0) {
2007     ret = calloc (3, sizeof (char *));
2008     ret[0] = safe_strdup (g, "/");
2009     ret[1] = safe_strdup (g, root);
2010     ret[2] = NULL;
2011     return ret;
2012   }
2013
2014 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
2015   size_t i, count = 0;
2016   for (i = 0; i < fs->nr_fstab; ++i)
2017     if (CRITERION)
2018       count++;
2019
2020   /* Hashtables have 2N+1 entries. */
2021   ret = calloc (2*count+1, sizeof (char *));
2022   if (ret == NULL) {
2023     perrorf (g, "calloc");
2024     return NULL;
2025   }
2026
2027   count = 0;
2028   for (i = 0; i < fs->nr_fstab; ++i)
2029     if (CRITERION) {
2030       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
2031       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
2032       count++;
2033     }
2034 #undef CRITERION
2035
2036   return ret;
2037 }
2038
2039 char **
2040 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
2041 {
2042   struct inspect_fs *fs = search_for_root (g, root);
2043   if (!fs)
2044     return NULL;
2045
2046   char **ret;
2047
2048   /* If no fstab information (Windows) return just the root. */
2049   if (fs->nr_fstab == 0) {
2050     ret = calloc (2, sizeof (char *));
2051     ret[0] = safe_strdup (g, root);
2052     ret[1] = NULL;
2053     return ret;
2054   }
2055
2056   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
2057   if (ret == NULL) {
2058     perrorf (g, "calloc");
2059     return NULL;
2060   }
2061
2062   size_t i;
2063   for (i = 0; i < fs->nr_fstab; ++i)
2064     ret[i] = safe_strdup (g, fs->fstab[i].device);
2065
2066   return ret;
2067 }
2068
2069 char *
2070 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
2071 {
2072   struct inspect_fs *fs = search_for_root (g, root);
2073   if (!fs)
2074     return NULL;
2075
2076   char *ret;
2077   switch (fs->package_format) {
2078   case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
2079   case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
2080   case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
2081   case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
2082   case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
2083   case OS_PACKAGE_FORMAT_UNKNOWN:
2084   default:
2085     ret = safe_strdup (g, "unknown");
2086     break;
2087   }
2088
2089   return ret;
2090 }
2091
2092 char *
2093 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
2094 {
2095   struct inspect_fs *fs = search_for_root (g, root);
2096   if (!fs)
2097     return NULL;
2098
2099   char *ret;
2100   switch (fs->package_management) {
2101   case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
2102   case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
2103   case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
2104   case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
2105   case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
2106   case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
2107   case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
2108   case OS_PACKAGE_MANAGEMENT_UNKNOWN:
2109   default:
2110     ret = safe_strdup (g, "unknown");
2111     break;
2112   }
2113
2114   return ret;
2115 }
2116
2117 char *
2118 guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
2119 {
2120   struct inspect_fs *fs = search_for_root (g, root);
2121   if (!fs)
2122     return NULL;
2123
2124   return safe_strdup (g, fs->hostname ? : "unknown");
2125 }
2126
2127 #ifdef DB_DUMP
2128 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
2129 #endif
2130 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
2131 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
2132 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);
2133 static void sort_applications (struct guestfs_application_list *);
2134
2135 /* Unlike the simple inspect-get-* calls, this one assumes that the
2136  * disks are mounted up, and reads files from the mounted disks.
2137  */
2138 struct guestfs_application_list *
2139 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
2140 {
2141   struct inspect_fs *fs = search_for_root (g, root);
2142   if (!fs)
2143     return NULL;
2144
2145   struct guestfs_application_list *ret = NULL;
2146
2147   /* Presently we can only list applications for installed disks.  It
2148    * is possible in future to get lists of packages from installers.
2149    */
2150   if (fs->format == OS_FORMAT_INSTALLED) {
2151     switch (fs->type) {
2152     case OS_TYPE_LINUX:
2153       switch (fs->package_format) {
2154       case OS_PACKAGE_FORMAT_RPM:
2155 #ifdef DB_DUMP
2156         ret = list_applications_rpm (g, fs);
2157         if (ret == NULL)
2158           return NULL;
2159 #endif
2160         break;
2161
2162       case OS_PACKAGE_FORMAT_DEB:
2163         ret = list_applications_deb (g, fs);
2164         if (ret == NULL)
2165           return NULL;
2166         break;
2167
2168       case OS_PACKAGE_FORMAT_PACMAN:
2169       case OS_PACKAGE_FORMAT_EBUILD:
2170       case OS_PACKAGE_FORMAT_PISI:
2171       case OS_PACKAGE_FORMAT_UNKNOWN:
2172       default:
2173         /* nothing - keep GCC happy */;
2174       }
2175       break;
2176
2177     case OS_TYPE_WINDOWS:
2178       ret = list_applications_windows (g, fs);
2179       if (ret == NULL)
2180         return NULL;
2181       break;
2182
2183     case OS_TYPE_FREEBSD:
2184     case OS_TYPE_UNKNOWN:
2185     default:
2186       /* nothing - keep GCC happy */;
2187     }
2188   }
2189
2190   if (ret == NULL) {
2191     /* Don't know how to do inspection.  Not an error, return an
2192      * empty list.
2193      */
2194     ret = safe_malloc (g, sizeof *ret);
2195     ret->len = 0;
2196     ret->val = NULL;
2197   }
2198
2199   sort_applications (ret);
2200
2201   return ret;
2202 }
2203
2204 #ifdef DB_DUMP
2205 static struct guestfs_application_list *
2206 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
2207 {
2208   TMP_TEMPLATE_ON_STACK (tmpfile);
2209
2210   if (download_to_tmp (g, "/var/lib/rpm/Name", tmpfile, MAX_PKG_DB_SIZE) == -1)
2211     return NULL;
2212
2213   struct guestfs_application_list *apps = NULL, *ret = NULL;
2214 #define cmd_len (strlen (tmpfile) + 64)
2215   char cmd[cmd_len];
2216   FILE *pp = NULL;
2217   char line[1024];
2218   size_t len;
2219
2220   snprintf (cmd, cmd_len, DB_DUMP " -p '%s'", tmpfile);
2221
2222   if (g->verbose)
2223     fprintf (stderr, "list_applications_rpm: %s\n", cmd);
2224
2225   pp = popen (cmd, "r");
2226   if (pp == NULL) {
2227     perrorf (g, "popen: %s", cmd);
2228     goto out;
2229   }
2230
2231   /* Ignore everything to end-of-header marker. */
2232   for (;;) {
2233     if (fgets (line, sizeof line, pp) == NULL) {
2234       error (g, _("unexpected end of output from db_dump command"));
2235       goto out;
2236     }
2237
2238     len = strlen (line);
2239     if (len > 0 && line[len-1] == '\n') {
2240       line[len-1] = '\0';
2241       len--;
2242     }
2243
2244     if (STREQ (line, "HEADER=END"))
2245       break;
2246   }
2247
2248   /* Allocate 'apps' list. */
2249   apps = safe_malloc (g, sizeof *apps);
2250   apps->len = 0;
2251   apps->val = NULL;
2252
2253   /* Read alternate lines until end of data marker. */
2254   for (;;) {
2255     if (fgets (line, sizeof line, pp) == NULL) {
2256       error (g, _("unexpected end of output from db_dump command"));
2257       goto out;
2258     }
2259
2260     len = strlen (line);
2261     if (len > 0 && line[len-1] == '\n') {
2262       line[len-1] = '\0';
2263       len--;
2264     }
2265
2266     if (STREQ (line, "DATA=END"))
2267       break;
2268
2269     char *p = line;
2270     if (len > 0 && line[0] == ' ')
2271       p = line+1;
2272     /* Ignore any application name that contains non-printable chars.
2273      * In the db_dump output these would be escaped with backslash, so
2274      * we can just ignore any such line.
2275      */
2276     if (strchr (p, '\\') == NULL)
2277       add_application (g, apps, p, "", 0, "", "", "", "", "", "");
2278
2279     /* Discard next line. */
2280     if (fgets (line, sizeof line, pp) == NULL) {
2281       error (g, _("unexpected end of output from db_dump command"));
2282       goto out;
2283     }
2284   }
2285
2286   /* Catch errors from the db_dump command. */
2287   if (pclose (pp) == -1) {
2288     perrorf (g, "pclose: %s", cmd);
2289     goto out;
2290   }
2291   pp = NULL;
2292
2293   ret = apps;
2294
2295  out:
2296   if (ret == NULL && apps != NULL)
2297     guestfs_free_application_list (apps);
2298   if (pp)
2299     pclose (pp);
2300   unlink (tmpfile);
2301 #undef cmd_len
2302
2303   return ret;
2304 }
2305 #endif /* defined DB_DUMP */
2306
2307 static struct guestfs_application_list *
2308 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
2309 {
2310   TMP_TEMPLATE_ON_STACK (tmpfile);
2311
2312   if (download_to_tmp (g, "/var/lib/dpkg/status", tmpfile,
2313                        MAX_PKG_DB_SIZE) == -1)
2314     return NULL;
2315
2316   struct guestfs_application_list *apps = NULL, *ret = NULL;
2317   FILE *fp = NULL;
2318   char line[1024];
2319   size_t len;
2320   char *name = NULL, *version = NULL, *release = NULL;
2321   int installed_flag = 0;
2322
2323   fp = fopen (tmpfile, "r");
2324   if (fp == NULL) {
2325     perrorf (g, "fopen: %s", tmpfile);
2326     goto out;
2327   }
2328
2329   /* Allocate 'apps' list. */
2330   apps = safe_malloc (g, sizeof *apps);
2331   apps->len = 0;
2332   apps->val = NULL;
2333
2334   /* Read the temporary file.  Each package entry is separated by
2335    * a blank line.
2336    * XXX Strictly speaking this is in mailbox header format, so it
2337    * would be possible for fields to spread across multiple lines,
2338    * although for the short fields that we are concerned about this is
2339    * unlikely and not seen in practice.
2340    */
2341   while (fgets (line, sizeof line, fp) != NULL) {
2342     len = strlen (line);
2343     if (len > 0 && line[len-1] == '\n') {
2344       line[len-1] = '\0';
2345       len--;
2346     }
2347
2348     if (STRPREFIX (line, "Package: ")) {
2349       free (name);
2350       name = safe_strdup (g, &line[9]);
2351     }
2352     else if (STRPREFIX (line, "Status: ")) {
2353       installed_flag = strstr (&line[8], "installed") != NULL;
2354     }
2355     else if (STRPREFIX (line, "Version: ")) {
2356       free (version);
2357       free (release);
2358       char *p = strchr (&line[9], '-');
2359       if (p) {
2360         *p = '\0';
2361         version = safe_strdup (g, &line[9]);
2362         release = safe_strdup (g, p+1);
2363       } else {
2364         version = safe_strdup (g, &line[9]);
2365         release = NULL;
2366       }
2367     }
2368     else if (STREQ (line, "")) {
2369       if (installed_flag && name && version)
2370         add_application (g, apps, name, "", 0, version, release ? : "",
2371                          "", "", "", "");
2372       free (name);
2373       free (version);
2374       free (release);
2375       name = version = release = NULL;
2376       installed_flag = 0;
2377     }
2378   }
2379
2380   if (fclose (fp) == -1) {
2381     perrorf (g, "fclose: %s", tmpfile);
2382     goto out;
2383   }
2384   fp = NULL;
2385
2386   ret = apps;
2387
2388  out:
2389   if (ret == NULL && apps != NULL)
2390     guestfs_free_application_list (apps);
2391   if (fp)
2392     fclose (fp);
2393   free (name);
2394   free (version);
2395   free (release);
2396   unlink (tmpfile);
2397   return ret;
2398 }
2399
2400 /* XXX We already download the SOFTWARE hive when doing general
2401  * inspection.  We could avoid this second download of the same file
2402  * by caching these entries in the handle.
2403  */
2404 static struct guestfs_application_list *
2405 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
2406 {
2407   TMP_TEMPLATE_ON_STACK (software_local);
2408
2409   size_t len = strlen (fs->windows_systemroot) + 64;
2410   char software[len];
2411   snprintf (software, len, "%s/system32/config/software",
2412             fs->windows_systemroot);
2413
2414   char *software_path = resolve_windows_path_silently (g, software);
2415   if (!software_path)
2416     /* If the software hive doesn't exist, just accept that we cannot
2417      * find product_name etc.
2418      */
2419     return 0;
2420
2421   struct guestfs_application_list *apps = NULL, *ret = NULL;
2422   hive_h *h = NULL;
2423   hive_node_h *children = NULL;
2424
2425   if (download_to_tmp (g, software_path, software_local,
2426                        MAX_REGISTRY_SIZE) == -1)
2427     goto out;
2428
2429   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
2430   if (h == NULL) {
2431     perrorf (g, "hivex_open");
2432     goto out;
2433   }
2434
2435   hive_node_h node = hivex_root (h);
2436   const char *hivepath[] =
2437     { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
2438   size_t i;
2439   for (i = 0;
2440        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
2441        ++i) {
2442     node = hivex_node_get_child (h, node, hivepath[i]);
2443   }
2444
2445   if (node == 0) {
2446     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
2447     goto out;
2448   }
2449
2450   children = hivex_node_children (h, node);
2451   if (children == NULL) {
2452     perrorf (g, "hivex_node_children");
2453     goto out;
2454   }
2455
2456   /* Allocate 'apps' list. */
2457   apps = safe_malloc (g, sizeof *apps);
2458   apps->len = 0;
2459   apps->val = NULL;
2460
2461   /* Consider any child node that has a DisplayName key.
2462    * See also:
2463    * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
2464    */
2465   for (i = 0; children[i] != 0; ++i) {
2466     hive_value_h value;
2467     char *name = NULL;
2468     char *display_name = NULL;
2469     char *version = NULL;
2470     char *install_path = NULL;
2471     char *publisher = NULL;
2472     char *url = NULL;
2473     char *comments = NULL;
2474
2475     /* Use the node name as a proxy for the package name in Linux.  The
2476      * display name is not language-independent, so it cannot be used.
2477      */
2478     name = hivex_node_name (h, children[i]);
2479     if (name == NULL) {
2480       perrorf (g, "hivex_node_get_name");
2481       goto out;
2482     }
2483
2484     value = hivex_node_get_value (h, children[i], "DisplayName");
2485     if (value) {
2486       display_name = hivex_value_string (h, value);
2487       if (display_name) {
2488         value = hivex_node_get_value (h, children[i], "DisplayVersion");
2489         if (value)
2490           version = hivex_value_string (h, value);
2491         value = hivex_node_get_value (h, children[i], "InstallLocation");
2492         if (value)
2493           install_path = hivex_value_string (h, value);
2494         value = hivex_node_get_value (h, children[i], "Publisher");
2495         if (value)
2496           publisher = hivex_value_string (h, value);
2497         value = hivex_node_get_value (h, children[i], "URLInfoAbout");
2498         if (value)
2499           url = hivex_value_string (h, value);
2500         value = hivex_node_get_value (h, children[i], "Comments");
2501         if (value)
2502           comments = hivex_value_string (h, value);
2503
2504         add_application (g, apps, name, display_name, 0,
2505                          version ? : "",
2506                          "",
2507                          install_path ? : "",
2508                          publisher ? : "",
2509                          url ? : "",
2510                          comments ? : "");
2511       }
2512     }
2513
2514     free (name);
2515     free (display_name);
2516     free (version);
2517     free (install_path);
2518     free (publisher);
2519     free (url);
2520     free (comments);
2521   }
2522
2523   ret = apps;
2524
2525  out:
2526   if (ret == NULL && apps != NULL)
2527     guestfs_free_application_list (apps);
2528   if (h) hivex_close (h);
2529   free (children);
2530   free (software_path);
2531
2532   /* Free up the temporary file. */
2533   unlink (software_local);
2534 #undef software_local_len
2535
2536   return ret;
2537 }
2538
2539 static void
2540 add_application (guestfs_h *g, struct guestfs_application_list *apps,
2541                  const char *name, const char *display_name, int32_t epoch,
2542                  const char *version, const char *release,
2543                  const char *install_path,
2544                  const char *publisher, const char *url,
2545                  const char *description)
2546 {
2547   apps->len++;
2548   apps->val = safe_realloc (g, apps->val,
2549                             apps->len * sizeof (struct guestfs_application));
2550   apps->val[apps->len-1].app_name = safe_strdup (g, name);
2551   apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
2552   apps->val[apps->len-1].app_epoch = epoch;
2553   apps->val[apps->len-1].app_version = safe_strdup (g, version);
2554   apps->val[apps->len-1].app_release = safe_strdup (g, release);
2555   apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
2556   /* XXX Translated path is not implemented yet. */
2557   apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
2558   apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
2559   apps->val[apps->len-1].app_url = safe_strdup (g, url);
2560   /* XXX The next two are not yet implemented for any package
2561    * format, but we could easily support them for rpm and deb.
2562    */
2563   apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
2564   apps->val[apps->len-1].app_summary = safe_strdup (g, "");
2565   apps->val[apps->len-1].app_description = safe_strdup (g, description);
2566 }
2567
2568 /* Sort applications by name before returning the list. */
2569 static int
2570 compare_applications (const void *vp1, const void *vp2)
2571 {
2572   const struct guestfs_application *v1 = vp1;
2573   const struct guestfs_application *v2 = vp2;
2574
2575   return strcmp (v1->app_name, v2->app_name);
2576 }
2577
2578 static void
2579 sort_applications (struct guestfs_application_list *apps)
2580 {
2581   if (apps && apps->val)
2582     qsort (apps->val, apps->len, sizeof (struct guestfs_application),
2583            compare_applications);
2584 }
2585
2586 /* Download to a guest file to a local temporary file.  Refuse to
2587  * download the guest file if it is larger than max_size.  The caller
2588  * is responsible for deleting the temporary file after use.
2589  */
2590 static int
2591 download_to_tmp (guestfs_h *g, const char *filename,
2592                  char *localtmp, int64_t max_size)
2593 {
2594   int fd;
2595   char buf[32];
2596   int64_t size;
2597
2598   size = guestfs_filesize (g, filename);
2599   if (size == -1)
2600     /* guestfs_filesize failed and has already set error in handle */
2601     return -1;
2602   if (size > max_size) {
2603     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
2604            filename, size);
2605     return -1;
2606   }
2607
2608   fd = mkstemp (localtmp);
2609   if (fd == -1) {
2610     perrorf (g, "mkstemp");
2611     return -1;
2612   }
2613
2614   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
2615
2616   if (guestfs_download (g, filename, buf) == -1) {
2617     close (fd);
2618     unlink (localtmp);
2619     return -1;
2620   }
2621
2622   if (close (fd) == -1) {
2623     perrorf (g, "close: %s", localtmp);
2624     unlink (localtmp);
2625     return -1;
2626   }
2627
2628   return 0;
2629 }
2630
2631 /* Call 'f' with Augeas opened and having parsed 'filename' (this file
2632  * must exist).  As a security measure, this bails if the file is too
2633  * large for a reasonable configuration file.  After the call to 'f'
2634  * Augeas is closed.
2635  */
2636 static int
2637 inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
2638                      int (*f) (guestfs_h *, struct inspect_fs *))
2639 {
2640   /* Security: Refuse to do this if filename is too large. */
2641   int64_t size = guestfs_filesize (g, filename);
2642   if (size == -1)
2643     /* guestfs_filesize failed and has already set error in handle */
2644     return -1;
2645   if (size > MAX_AUGEAS_FILE_SIZE) {
2646     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
2647            filename, size);
2648     return -1;
2649   }
2650
2651   /* If !feature_available (g, "augeas") then the next call will fail.
2652    * Arguably we might want to fall back to a non-Augeas method in
2653    * this case.
2654    */
2655   if (guestfs_aug_init (g, "/", 16|32) == -1)
2656     return -1;
2657
2658   int r = -1;
2659
2660   /* Tell Augeas to only load one file (thanks RaphaĆ«l Pinson). */
2661   char buf[strlen (filename) + 64];
2662   snprintf (buf, strlen (filename) + 64, "/augeas/load//incl[. != \"%s\"]",
2663             filename);
2664   if (guestfs_aug_rm (g, buf) == -1)
2665     goto out;
2666
2667   if (guestfs_aug_load (g) == -1)
2668     goto out;
2669
2670   r = f (g, fs);
2671
2672  out:
2673   guestfs_aug_close (g);
2674
2675   return r;
2676 }
2677
2678 /* Get the first line of a small file, without any trailing newline
2679  * character.
2680  */
2681 static char *
2682 first_line_of_file (guestfs_h *g, const char *filename)
2683 {
2684   char **lines;
2685   int64_t size;
2686   char *ret;
2687
2688   /* Don't trust guestfs_head_n not to break with very large files.
2689    * Check the file size is something reasonable first.
2690    */
2691   size = guestfs_filesize (g, filename);
2692   if (size == -1)
2693     /* guestfs_filesize failed and has already set error in handle */
2694     return NULL;
2695   if (size > MAX_SMALL_FILE_SIZE) {
2696     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
2697            filename, size);
2698     return NULL;
2699   }
2700
2701   lines = guestfs_head_n (g, 1, filename);
2702   if (lines == NULL)
2703     return NULL;
2704   if (lines[0] == NULL) {
2705     error (g, _("%s: file is empty"), filename);
2706     guestfs___free_string_list (lines);
2707     return NULL;
2708   }
2709   /* lines[1] should be NULL because of '1' argument above ... */
2710
2711   ret = lines[0];               /* caller frees */
2712   free (lines);                 /* free the array */
2713
2714   return ret;
2715 }
2716
2717 /* Get the first matching line (using guestfs_egrep{,i}) of a small file,
2718  * without any trailing newline character.
2719  *
2720  * Returns: 1 = returned a line (in *ret)
2721  *          0 = no match
2722  *          -1 = error
2723  */
2724 static int
2725 first_egrep_of_file (guestfs_h *g, const char *filename,
2726                      const char *eregex, int iflag, char **ret)
2727 {
2728   char **lines;
2729   int64_t size;
2730   size_t i;
2731
2732   /* Don't trust guestfs_egrep not to break with very large files.
2733    * Check the file size is something reasonable first.
2734    */
2735   size = guestfs_filesize (g, filename);
2736   if (size == -1)
2737     /* guestfs_filesize failed and has already set error in handle */
2738     return -1;
2739   if (size > MAX_SMALL_FILE_SIZE) {
2740     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
2741            filename, size);
2742     return -1;
2743   }
2744
2745   lines = (!iflag ? guestfs_egrep : guestfs_egrepi) (g, eregex, filename);
2746   if (lines == NULL)
2747     return -1;
2748   if (lines[0] == NULL) {
2749     guestfs___free_string_list (lines);
2750     return 0;
2751   }
2752
2753   *ret = lines[0];              /* caller frees */
2754
2755   /* free up any other matches and the array itself */
2756   for (i = 1; lines[i] != NULL; ++i)
2757     free (lines[i]);
2758   free (lines);
2759
2760   return 1;
2761 }
2762
2763 #else /* no PCRE or hivex at compile time */
2764
2765 /* XXX These functions should be in an optgroup. */
2766
2767 #define NOT_IMPL(r)                                                     \
2768   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
2769   return r
2770
2771 char **
2772 guestfs__inspect_os (guestfs_h *g)
2773 {
2774   NOT_IMPL(NULL);
2775 }
2776
2777 char **
2778 guestfs__inspect_get_roots (guestfs_h *g)
2779 {
2780   NOT_IMPL(NULL);
2781 }
2782
2783 char *
2784 guestfs__inspect_get_type (guestfs_h *g, const char *root)
2785 {
2786   NOT_IMPL(NULL);
2787 }
2788
2789 char *
2790 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
2791 {
2792   NOT_IMPL(NULL);
2793 }
2794
2795 char *
2796 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
2797 {
2798   NOT_IMPL(NULL);
2799 }
2800
2801 int
2802 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
2803 {
2804   NOT_IMPL(-1);
2805 }
2806
2807 int
2808 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
2809 {
2810   NOT_IMPL(-1);
2811 }
2812
2813 char *
2814 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
2815 {
2816   NOT_IMPL(NULL);
2817 }
2818
2819 char *
2820 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
2821 {
2822   NOT_IMPL(NULL);
2823 }
2824
2825 char **
2826 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
2827 {
2828   NOT_IMPL(NULL);
2829 }
2830
2831 char **
2832 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
2833 {
2834   NOT_IMPL(NULL);
2835 }
2836
2837 char *
2838 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
2839 {
2840   NOT_IMPL(NULL);
2841 }
2842
2843 char *
2844 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
2845 {
2846   NOT_IMPL(NULL);
2847 }
2848
2849 char *
2850 guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
2851 {
2852   NOT_IMPL(NULL);
2853 }
2854
2855 struct guestfs_application_list *
2856 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
2857 {
2858   NOT_IMPL(NULL);
2859 }
2860
2861 char *
2862 guestfs__inspect_get_format (guestfs_h *g, const char *root)
2863 {
2864   NOT_IMPL(NULL);
2865 }
2866
2867 int
2868 guestfs__inspect_is_live (guestfs_h *g, const char *root)
2869 {
2870   NOT_IMPL(-1);
2871 }
2872
2873 int
2874 guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
2875 {
2876   NOT_IMPL(-1);
2877 }
2878
2879 int
2880 guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
2881 {
2882   NOT_IMPL(-1);
2883 }
2884
2885 #endif /* no PCRE or hivex at compile time */
2886
2887 void
2888 guestfs___free_inspect_info (guestfs_h *g)
2889 {
2890   size_t i;
2891   for (i = 0; i < g->nr_fses; ++i) {
2892     free (g->fses[i].device);
2893     free (g->fses[i].product_name);
2894     free (g->fses[i].arch);
2895     free (g->fses[i].hostname);
2896     free (g->fses[i].windows_systemroot);
2897     size_t j;
2898     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
2899       free (g->fses[i].fstab[j].device);
2900       free (g->fses[i].fstab[j].mountpoint);
2901     }
2902     free (g->fses[i].fstab);
2903   }
2904   free (g->fses);
2905   g->nr_fses = 0;
2906   g->fses = NULL;
2907 }
2908
2909 /* In the Perl code this is a public function. */
2910 int
2911 guestfs___feature_available (guestfs_h *g, const char *feature)
2912 {
2913   /* If there's an error we should ignore it, so to do that we have to
2914    * temporarily replace the error handler with a null one.
2915    */
2916   guestfs_error_handler_cb old_error_cb = g->error_cb;
2917   g->error_cb = NULL;
2918
2919   const char *groups[] = { feature, NULL };
2920   int r = guestfs_available (g, (char * const *) groups);
2921
2922   g->error_cb = old_error_cb;
2923
2924   return r == 0 ? 1 : 0;
2925 }
2926
2927 #ifdef HAVE_PCRE
2928
2929 /* Match a regular expression which contains no captures.  Returns
2930  * true if it matches or false if it doesn't.
2931  */
2932 int
2933 guestfs___match (guestfs_h *g, const char *str, const pcre *re)
2934 {
2935   size_t len = strlen (str);
2936   int vec[30], r;
2937
2938   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
2939   if (r == PCRE_ERROR_NOMATCH)
2940     return 0;
2941   if (r != 1) {
2942     /* Internal error -- should not happen. */
2943     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2944              __FILE__, __func__, r, str);
2945     return 0;
2946   }
2947
2948   return 1;
2949 }
2950
2951 /* Match a regular expression which contains exactly one capture.  If
2952  * the string matches, return the capture, otherwise return NULL.  The
2953  * caller must free the result.
2954  */
2955 char *
2956 guestfs___match1 (guestfs_h *g, const char *str, const pcre *re)
2957 {
2958   size_t len = strlen (str);
2959   int vec[30], r;
2960
2961   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
2962   if (r == PCRE_ERROR_NOMATCH)
2963     return NULL;
2964   if (r != 2) {
2965     /* Internal error -- should not happen. */
2966     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2967              __FILE__, __func__, r, str);
2968     return NULL;
2969   }
2970
2971   return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2972 }
2973
2974 /* Match a regular expression which contains exactly two captures. */
2975 int
2976 guestfs___match2 (guestfs_h *g, const char *str, const pcre *re,
2977                   char **ret1, char **ret2)
2978 {
2979   size_t len = strlen (str);
2980   int vec[30], r;
2981
2982   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
2983   if (r == PCRE_ERROR_NOMATCH)
2984     return 0;
2985   if (r != 3) {
2986     /* Internal error -- should not happen. */
2987     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2988              __FILE__, __func__, r, str);
2989     return 0;
2990   }
2991
2992   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2993   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
2994
2995   return 1;
2996 }
2997
2998 /* Match a regular expression which contains exactly three captures. */
2999 int
3000 guestfs___match3 (guestfs_h *g, const char *str, const pcre *re,
3001                   char **ret1, char **ret2, char **ret3)
3002 {
3003   size_t len = strlen (str);
3004   int vec[30], r;
3005
3006   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
3007   if (r == PCRE_ERROR_NOMATCH)
3008     return 0;
3009   if (r != 4) {
3010     /* Internal error -- should not happen. */
3011     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
3012              __FILE__, __func__, r, str);
3013     return 0;
3014   }
3015
3016   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
3017   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
3018   *ret3 = safe_strndup (g, &str[vec[6]], vec[7]-vec[6]);
3019
3020   return 1;
3021 }
3022
3023 #endif /* HAVE_PCRE */