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