fish: Fix test-guestfish-events.sh so it works when LIBGUESTFS_DEBUG=1 is set.
[libguestfs.git] / src / inspect_fs_unix.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 <fcntl.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <endian.h>
31
32 #include <pcre.h>
33
34 #ifdef HAVE_HIVEX
35 #include <hivex.h>
36 #endif
37
38 #include "c-ctype.h"
39 #include "ignore-value.h"
40 #include "xstrtol.h"
41 #include "hash.h"
42 #include "hash-pjw.h"
43
44 #include "guestfs.h"
45 #include "guestfs-internal.h"
46 #include "guestfs-internal-actions.h"
47 #include "guestfs_protocol.h"
48
49 #if defined(HAVE_HIVEX)
50
51 /* Compile all the regular expressions once when the shared library is
52  * loaded.  PCRE is thread safe so we're supposedly OK here if
53  * multiple threads call into the libguestfs API functions below
54  * simultaneously.
55  */
56 static pcre *re_fedora;
57 static pcre *re_rhel_old;
58 static pcre *re_rhel;
59 static pcre *re_rhel_no_minor;
60 static pcre *re_centos_old;
61 static pcre *re_centos;
62 static pcre *re_centos_no_minor;
63 static pcre *re_scientific_linux_old;
64 static pcre *re_scientific_linux;
65 static pcre *re_scientific_linux_no_minor;
66 static pcre *re_major_minor;
67 static pcre *re_aug_seq;
68 static pcre *re_xdev;
69 static pcre *re_cciss;
70 static pcre *re_mdN;
71 static pcre *re_first_partition;
72 static pcre *re_freebsd;
73 static pcre *re_netbsd;
74
75 static void compile_regexps (void) __attribute__((constructor));
76 static void free_regexps (void) __attribute__((destructor));
77
78 static void
79 compile_regexps (void)
80 {
81   const char *err;
82   int offset;
83
84 #define COMPILE(re,pattern,options)                                     \
85   do {                                                                  \
86     re = pcre_compile ((pattern), (options), &err, &offset, NULL);      \
87     if (re == NULL) {                                                   \
88       ignore_value (write (2, err, strlen (err)));                      \
89       abort ();                                                         \
90     }                                                                   \
91   } while (0)
92
93   COMPILE (re_fedora, "Fedora release (\\d+)", 0);
94   COMPILE (re_rhel_old,
95            "Red Hat.*release (\\d+).*Update (\\d+)", 0);
96   COMPILE (re_rhel,
97            "Red Hat.*release (\\d+)\\.(\\d+)", 0);
98   COMPILE (re_rhel_no_minor,
99            "Red Hat.*release (\\d+)", 0);
100   COMPILE (re_centos_old,
101            "CentOS.*release (\\d+).*Update (\\d+)", 0);
102   COMPILE (re_centos,
103            "CentOS.*release (\\d+)\\.(\\d+)", 0);
104   COMPILE (re_centos_no_minor,
105            "CentOS.*release (\\d+)", 0);
106   COMPILE (re_scientific_linux_old,
107            "Scientific Linux.*release (\\d+).*Update (\\d+)", 0);
108   COMPILE (re_scientific_linux,
109            "Scientific Linux.*release (\\d+)\\.(\\d+)", 0);
110   COMPILE (re_scientific_linux_no_minor,
111            "Scientific Linux.*release (\\d+)", 0);
112   COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
113   COMPILE (re_xdev, "^/dev/(h|s|v|xv)d([a-z]+)(\\d*)$", 0);
114   COMPILE (re_cciss, "^/dev/(cciss/c\\d+d\\d+)(?:p(\\d+))?$", 0);
115   COMPILE (re_mdN, "^(/dev/md\\d+)$", 0);
116   COMPILE (re_freebsd, "^/dev/ad(\\d+)s(\\d+)([a-z])$", 0);
117   COMPILE (re_netbsd, "^NetBSD (\\d+)\\.(\\d+)", 0);
118 }
119
120 static void
121 free_regexps (void)
122 {
123   pcre_free (re_fedora);
124   pcre_free (re_rhel_old);
125   pcre_free (re_rhel);
126   pcre_free (re_rhel_no_minor);
127   pcre_free (re_centos_old);
128   pcre_free (re_centos);
129   pcre_free (re_centos_no_minor);
130   pcre_free (re_scientific_linux_old);
131   pcre_free (re_scientific_linux);
132   pcre_free (re_scientific_linux_no_minor);
133   pcre_free (re_major_minor);
134   pcre_free (re_xdev);
135   pcre_free (re_cciss);
136   pcre_free (re_mdN);
137   pcre_free (re_freebsd);
138   pcre_free (re_netbsd);
139 }
140
141 static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
142 static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs);
143 static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs);
144 static int check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs);
145 static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
146 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
147                             const char *spec, const char *mp,
148                             Hash_table *md_map);
149 static char *resolve_fstab_device (guestfs_h *g, const char *spec,
150                                    Hash_table *md_map);
151 static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char **configfiles, int (*f) (guestfs_h *, struct inspect_fs *));
152
153 /* Hash structure for uuid->path lookups */
154 typedef struct md_uuid {
155   uint32_t uuid[4];
156   char *path;
157 } md_uuid;
158
159 static size_t uuid_hash(const void *x, size_t table_size);
160 static bool uuid_cmp(const void *x, const void *y);
161 static void md_uuid_free(void *x);
162
163 static int parse_uuid(const char *str, uint32_t *uuid);
164
165 /* Hash structure for path(mdadm)->path(appliance) lookup */
166 typedef struct {
167   char *mdadm;
168   char *app;
169 } mdadm_app;
170
171 static size_t mdadm_app_hash(const void *x, size_t table_size);
172 static bool mdadm_app_cmp(const void *x, const void *y);
173 static void mdadm_app_free(void *x);
174
175 static int map_app_md_devices (guestfs_h *g, Hash_table **map);
176 static int map_md_devices(guestfs_h *g, Hash_table **map);
177
178 /* Set fs->product_name to the first line of the release file. */
179 static int
180 parse_release_file (guestfs_h *g, struct inspect_fs *fs,
181                     const char *release_filename)
182 {
183   fs->product_name = guestfs___first_line_of_file (g, release_filename);
184   if (fs->product_name == NULL)
185     return -1;
186   return 0;
187 }
188
189 /* Ubuntu has /etc/lsb-release containing:
190  *   DISTRIB_ID=Ubuntu                                # Distro
191  *   DISTRIB_RELEASE=10.04                            # Version
192  *   DISTRIB_CODENAME=lucid
193  *   DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"         # Product name
194  *
195  * [Ubuntu-derived ...] Linux Mint was found to have this:
196  *   DISTRIB_ID=LinuxMint
197  *   DISTRIB_RELEASE=10
198  *   DISTRIB_CODENAME=julia
199  *   DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
200  * Linux Mint also has /etc/linuxmint/info with more information,
201  * but we can use the LSB file.
202  *
203  * Mandriva has:
204  *   LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
205  *   DISTRIB_ID=MandrivaLinux
206  *   DISTRIB_RELEASE=2010.1
207  *   DISTRIB_CODENAME=Henry_Farman
208  *   DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
209  * Mandriva also has a normal release file called /etc/mandriva-release.
210  */
211 static int
212 parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
213 {
214   const char *filename = "/etc/lsb-release";
215   int64_t size;
216   char **lines;
217   size_t i;
218   int r = 0;
219
220   /* Don't trust guestfs_head_n not to break with very large files.
221    * Check the file size is something reasonable first.
222    */
223   size = guestfs_filesize (g, filename);
224   if (size == -1)
225     /* guestfs_filesize failed and has already set error in handle */
226     return -1;
227   if (size > MAX_SMALL_FILE_SIZE) {
228     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
229            filename, size);
230     return -1;
231   }
232
233   lines = guestfs_head_n (g, 10, filename);
234   if (lines == NULL)
235     return -1;
236
237   for (i = 0; lines[i] != NULL; ++i) {
238     if (fs->distro == 0 &&
239         STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
240       fs->distro = OS_DISTRO_UBUNTU;
241       r = 1;
242     }
243     else if (fs->distro == 0 &&
244              STREQ (lines[i], "DISTRIB_ID=LinuxMint")) {
245       fs->distro = OS_DISTRO_LINUX_MINT;
246       r = 1;
247     }
248     else if (fs->distro == 0 &&
249              STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) {
250       fs->distro = OS_DISTRO_MANDRIVA;
251       r = 1;
252     }
253     else if (fs->distro == 0 &&
254              STREQ (lines[i], "DISTRIB_ID=\"Mageia\"")) {
255       fs->distro = OS_DISTRO_MAGEIA;
256       r = 1;
257     }
258     else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
259       char *major, *minor;
260       if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) {
261         fs->major_version = guestfs___parse_unsigned_int (g, major);
262         free (major);
263         if (fs->major_version == -1) {
264           free (minor);
265           guestfs___free_string_list (lines);
266           return -1;
267         }
268         fs->minor_version = guestfs___parse_unsigned_int (g, minor);
269         free (minor);
270         if (fs->minor_version == -1) {
271           guestfs___free_string_list (lines);
272           return -1;
273         }
274       }
275     }
276     else if (fs->product_name == NULL &&
277              (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
278               STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
279       size_t len = strlen (lines[i]) - 21 - 1;
280       fs->product_name = safe_strndup (g, &lines[i][21], len);
281       r = 1;
282     }
283     else if (fs->product_name == NULL &&
284              STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
285       size_t len = strlen (lines[i]) - 20;
286       fs->product_name = safe_strndup (g, &lines[i][20], len);
287       r = 1;
288     }
289   }
290
291   guestfs___free_string_list (lines);
292   return r;
293 }
294
295 /* The currently mounted device is known to be a Linux root.  Try to
296  * determine from this the distro, version, etc.  Also parse
297  * /etc/fstab to determine the arrangement of mountpoints and
298  * associated devices.
299  */
300 int
301 guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs)
302 {
303   int r;
304
305   fs->type = OS_TYPE_LINUX;
306
307   if (guestfs_exists (g, "/etc/lsb-release") > 0) {
308     r = parse_lsb_release (g, fs);
309     if (r == -1)        /* error */
310       return -1;
311     if (r == 1)         /* ok - detected the release from this file */
312       goto skip_release_checks;
313   }
314
315   if (guestfs_exists (g, "/etc/redhat-release") > 0) {
316     fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */
317
318     if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
319       return -1;
320
321     char *major, *minor;
322     if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
323       fs->distro = OS_DISTRO_FEDORA;
324       fs->major_version = guestfs___parse_unsigned_int (g, major);
325       free (major);
326       if (fs->major_version == -1)
327         return -1;
328     }
329     else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) ||
330              match2 (g, fs->product_name, re_rhel, &major, &minor)) {
331       fs->distro = OS_DISTRO_RHEL;
332       fs->major_version = guestfs___parse_unsigned_int (g, major);
333       free (major);
334       if (fs->major_version == -1) {
335         free (minor);
336         return -1;
337       }
338       fs->minor_version = guestfs___parse_unsigned_int (g, minor);
339       free (minor);
340       if (fs->minor_version == -1)
341         return -1;
342     }
343     else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) {
344       fs->distro = OS_DISTRO_RHEL;
345       fs->major_version = guestfs___parse_unsigned_int (g, major);
346       free (major);
347       if (fs->major_version == -1)
348         return -1;
349       fs->minor_version = 0;
350     }
351     else if (match2 (g, fs->product_name, re_centos_old, &major, &minor) ||
352              match2 (g, fs->product_name, re_centos, &major, &minor)) {
353       fs->distro = OS_DISTRO_CENTOS;
354       fs->major_version = guestfs___parse_unsigned_int (g, major);
355       free (major);
356       if (fs->major_version == -1) {
357         free (minor);
358         return -1;
359       }
360       fs->minor_version = guestfs___parse_unsigned_int (g, minor);
361       free (minor);
362       if (fs->minor_version == -1)
363         return -1;
364     }
365     else if ((major = match1 (g, fs->product_name, re_centos_no_minor)) != NULL) {
366       fs->distro = OS_DISTRO_CENTOS;
367       fs->major_version = guestfs___parse_unsigned_int (g, major);
368       free (major);
369       if (fs->major_version == -1)
370         return -1;
371       fs->minor_version = 0;
372     }
373     else if (match2 (g, fs->product_name, re_scientific_linux_old, &major, &minor) ||
374              match2 (g, fs->product_name, re_scientific_linux, &major, &minor)) {
375       fs->distro = OS_DISTRO_SCIENTIFIC_LINUX;
376       fs->major_version = guestfs___parse_unsigned_int (g, major);
377       free (major);
378       if (fs->major_version == -1) {
379         free (minor);
380         return -1;
381       }
382       fs->minor_version = guestfs___parse_unsigned_int (g, minor);
383       free (minor);
384       if (fs->minor_version == -1)
385         return -1;
386     }
387     else if ((major = match1 (g, fs->product_name, re_scientific_linux_no_minor)) != NULL) {
388       fs->distro = OS_DISTRO_SCIENTIFIC_LINUX;
389       fs->major_version = guestfs___parse_unsigned_int (g, major);
390       free (major);
391       if (fs->major_version == -1)
392         return -1;
393       fs->minor_version = 0;
394     }
395   }
396   else if (guestfs_exists (g, "/etc/debian_version") > 0) {
397     fs->distro = OS_DISTRO_DEBIAN;
398
399     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
400       return -1;
401
402     if (guestfs___parse_major_minor (g, fs) == -1)
403       return -1;
404   }
405   else if (guestfs_exists (g, "/etc/pardus-release") > 0) {
406     fs->distro = OS_DISTRO_PARDUS;
407
408     if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
409       return -1;
410
411     if (guestfs___parse_major_minor (g, fs) == -1)
412       return -1;
413   }
414   else if (guestfs_exists (g, "/etc/arch-release") > 0) {
415     fs->distro = OS_DISTRO_ARCHLINUX;
416
417     /* /etc/arch-release file is empty and I can't see a way to
418      * determine the actual release or product string.
419      */
420   }
421   else if (guestfs_exists (g, "/etc/gentoo-release") > 0) {
422     fs->distro = OS_DISTRO_GENTOO;
423
424     if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
425       return -1;
426
427     if (guestfs___parse_major_minor (g, fs) == -1)
428       return -1;
429   }
430   else if (guestfs_exists (g, "/etc/meego-release") > 0) {
431     fs->distro = OS_DISTRO_MEEGO;
432
433     if (parse_release_file (g, fs, "/etc/meego-release") == -1)
434       return -1;
435
436     if (guestfs___parse_major_minor (g, fs) == -1)
437       return -1;
438   }
439   else if (guestfs_exists (g, "/etc/slackware-version") > 0) {
440     fs->distro = OS_DISTRO_SLACKWARE;
441
442     if (parse_release_file (g, fs, "/etc/slackware-version") == -1)
443       return -1;
444
445     if (guestfs___parse_major_minor (g, fs) == -1)
446       return -1;
447   }
448   else if (guestfs_exists (g, "/etc/ttylinux-target") > 0) {
449     fs->distro = OS_DISTRO_TTYLINUX;
450
451     fs->product_name = guestfs___first_line_of_file (g, "/etc/ttylinux-target");
452     if (fs->product_name == NULL)
453       return -1;
454
455     if (guestfs___parse_major_minor (g, fs) == -1)
456       return -1;
457   }
458   else if (guestfs_exists (g, "/etc/SuSE-release") > 0) {
459     fs->distro = OS_DISTRO_OPENSUSE;
460
461     if (parse_release_file (g, fs, "/etc/SuSE-release") == -1)
462       return -1;
463
464     if (guestfs___parse_major_minor (g, fs) == -1)
465       return -1;
466   }
467
468
469  skip_release_checks:;
470
471   /* Determine the architecture. */
472   check_architecture (g, fs);
473
474   /* We already know /etc/fstab exists because it's part of the test
475    * for Linux root above.  We must now parse this file to determine
476    * which filesystems are used by the operating system and how they
477    * are mounted.
478    */
479   const char *configfiles[] = { "/etc/fstab", "/etc/mdadm.conf", NULL };
480   if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
481     return -1;
482
483   /* Determine hostname. */
484   if (check_hostname_unix (g, fs) == -1)
485     return -1;
486
487   return 0;
488 }
489
490 /* The currently mounted device is known to be a FreeBSD root. */
491 int
492 guestfs___check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
493 {
494   fs->type = OS_TYPE_FREEBSD;
495
496   /* FreeBSD has no authoritative version file.  The version number is
497    * in /etc/motd, which the system administrator might edit, but
498    * we'll use that anyway.
499    */
500
501   if (guestfs_exists (g, "/etc/motd") > 0) {
502     if (parse_release_file (g, fs, "/etc/motd") == -1)
503       return -1;
504
505     if (guestfs___parse_major_minor (g, fs) == -1)
506       return -1;
507   }
508
509   /* Determine the architecture. */
510   check_architecture (g, fs);
511
512   /* We already know /etc/fstab exists because it's part of the test above. */
513   const char *configfiles[] = { "/etc/fstab", NULL };
514   if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
515     return -1;
516
517   /* Determine hostname. */
518   if (check_hostname_unix (g, fs) == -1)
519     return -1;
520
521   return 0;
522 }
523
524 /* The currently mounted device is maybe to be a *BSD root. */
525 int
526 guestfs___check_netbsd_root (guestfs_h *g, struct inspect_fs *fs)
527 {
528
529   if (guestfs_exists (g, "/etc/release") > 0) {
530     char *major, *minor;
531     if (parse_release_file (g, fs, "/etc/release") == -1)
532       return -1;
533
534     if (match2 (g, fs->product_name, re_netbsd, &major, &minor)) {
535       fs->type = OS_TYPE_NETBSD;
536       fs->major_version = guestfs___parse_unsigned_int (g, major);
537       free (major);
538       if (fs->major_version == -1) {
539         free (minor);
540         return -1;
541       }
542       fs->minor_version = guestfs___parse_unsigned_int (g, minor);
543       free (minor);
544       if (fs->minor_version == -1)
545         return -1;
546     }
547   } else {
548     return -1;
549   }
550
551   /* Determine the architecture. */
552   check_architecture (g, fs);
553
554   /* We already know /etc/fstab exists because it's part of the test above. */
555   const char *configfiles[] = { "/etc/fstab", NULL };
556   if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
557     return -1;
558
559   /* Determine hostname. */
560   if (check_hostname_unix (g, fs) == -1)
561     return -1;
562
563   return 0;
564 }
565
566 /* The currently mounted device may be a Hurd root.  Hurd has distros
567  * just like Linux.
568  */
569 int
570 guestfs___check_hurd_root (guestfs_h *g, struct inspect_fs *fs)
571 {
572   int r;
573
574   fs->type = OS_TYPE_HURD;
575
576   if (guestfs_exists (g, "/etc/debian_version") > 0) {
577     fs->distro = OS_DISTRO_DEBIAN;
578
579     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
580       return -1;
581
582     if (guestfs___parse_major_minor (g, fs) == -1)
583       return -1;
584   }
585
586   /* Arch Hurd also exists, but inconveniently it doesn't have
587    * the normal /etc/arch-release file.  XXX
588    */
589
590   /* Determine the architecture. */
591   check_architecture (g, fs);
592
593   /* XXX Check for /etc/fstab. */
594
595   /* Determine hostname. */
596   if (check_hostname_unix (g, fs) == -1)
597     return -1;
598
599   return 0;
600 }
601
602 static void
603 check_architecture (guestfs_h *g, struct inspect_fs *fs)
604 {
605   const char *binaries[] =
606     { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" };
607   size_t i;
608
609   for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
610     if (guestfs_is_file (g, binaries[i]) > 0) {
611       /* Ignore errors from file_architecture call. */
612       guestfs_error_handler_cb old_error_cb = g->error_cb;
613       g->error_cb = NULL;
614       char *arch = guestfs_file_architecture (g, binaries[i]);
615       g->error_cb = old_error_cb;
616
617       if (arch) {
618         /* String will be owned by handle, freed by
619          * guestfs___free_inspect_info.
620          */
621         fs->arch = arch;
622         break;
623       }
624     }
625   }
626 }
627
628 /* Try several methods to determine the hostname from a Linux or
629  * FreeBSD guest.  Note that type and distro have been set, so we can
630  * use that information to direct the search.
631  */
632 static int
633 check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
634 {
635   switch (fs->type) {
636   case OS_TYPE_LINUX:
637   case OS_TYPE_HURD:
638     /* Red Hat-derived would be in /etc/sysconfig/network, and
639      * Debian-derived in the file /etc/hostname.  Very old Debian and
640      * SUSE use /etc/HOSTNAME.  It's best to just look for each of
641      * these files in turn, rather than try anything clever based on
642      * distro.
643      */
644     if (guestfs_is_file (g, "/etc/HOSTNAME")) {
645       fs->hostname = guestfs___first_line_of_file (g, "/etc/HOSTNAME");
646       if (fs->hostname == NULL)
647         return -1;
648     }
649     else if (guestfs_is_file (g, "/etc/hostname")) {
650       fs->hostname = guestfs___first_line_of_file (g, "/etc/hostname");
651       if (fs->hostname == NULL)
652         return -1;
653     }
654     else if (guestfs_is_file (g, "/etc/sysconfig/network")) {
655       const char *configfiles[] = { "/etc/sysconfig/network", NULL };
656       if (inspect_with_augeas (g, fs, configfiles,
657                                check_hostname_redhat) == -1)
658         return -1;
659     }
660     break;
661
662   case OS_TYPE_FREEBSD:
663   case OS_TYPE_NETBSD:
664     /* /etc/rc.conf contains the hostname, but there is no Augeas lens
665      * for this file.
666      */
667     if (guestfs_is_file (g, "/etc/rc.conf")) {
668       if (check_hostname_freebsd (g, fs) == -1)
669         return -1;
670     }
671     break;
672
673   case OS_TYPE_WINDOWS: /* not here, see check_windows_system_registry */
674   case OS_TYPE_UNKNOWN:
675   default:
676     /* nothing, keep GCC warnings happy */;
677   }
678
679   return 0;
680 }
681
682 /* Parse the hostname from /etc/sysconfig/network.  This must be called
683  * from the inspect_with_augeas wrapper.
684  */
685 static int
686 check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs)
687 {
688   char *hostname;
689
690   /* Errors here are not fatal (RHBZ#726739), since it could be
691    * just missing HOSTNAME field in the file.
692    */
693   guestfs_error_handler_cb old_error_cb = g->error_cb;
694   g->error_cb = NULL;
695   hostname = guestfs_aug_get (g, "/files/etc/sysconfig/network/HOSTNAME");
696   g->error_cb = old_error_cb;
697
698   /* This is freed by guestfs___free_inspect_info.  Note that hostname
699    * could be NULL because we ignored errors above.
700    */
701   fs->hostname = hostname;
702   return 0;
703 }
704
705 /* Parse the hostname from /etc/rc.conf.  On FreeBSD this file
706  * contains comments, blank lines and:
707  *   hostname="freebsd8.example.com"
708  *   ifconfig_re0="DHCP"
709  *   keymap="uk.iso"
710  *   sshd_enable="YES"
711  */
712 static int
713 check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs)
714 {
715   const char *filename = "/etc/rc.conf";
716   int64_t size;
717   char **lines;
718   size_t i;
719
720   /* Don't trust guestfs_read_lines not to break with very large files.
721    * Check the file size is something reasonable first.
722    */
723   size = guestfs_filesize (g, filename);
724   if (size == -1)
725     /* guestfs_filesize failed and has already set error in handle */
726     return -1;
727   if (size > MAX_SMALL_FILE_SIZE) {
728     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
729            filename, size);
730     return -1;
731   }
732
733   lines = guestfs_read_lines (g, filename);
734   if (lines == NULL)
735     return -1;
736
737   for (i = 0; lines[i] != NULL; ++i) {
738     if (STRPREFIX (lines[i], "hostname=\"") ||
739         STRPREFIX (lines[i], "hostname='")) {
740       size_t len = strlen (lines[i]) - 10 - 1;
741       fs->hostname = safe_strndup (g, &lines[i][10], len);
742       break;
743     } else if (STRPREFIX (lines[i], "hostname=")) {
744       size_t len = strlen (lines[i]) - 9;
745       fs->hostname = safe_strndup (g, &lines[i][9], len);
746       break;
747     }
748   }
749
750   guestfs___free_string_list (lines);
751   return 0;
752 }
753
754 static int
755 check_fstab (guestfs_h *g, struct inspect_fs *fs)
756 {
757   char **entries, **entry;
758   char augpath[256];
759   char *spec, *mp;
760   int r;
761
762   /* Generate a map of MD device paths listed in /etc/mdadm.conf to MD device
763    * paths in the guestfs appliance */
764   Hash_table *md_map;
765   if (map_md_devices (g, &md_map) == -1) return -1;
766
767   entries = guestfs_aug_match (g, "/files/etc/fstab/*[label() != '#comment']");
768   if (entries == NULL) goto error;
769
770   if (entries[0] == NULL) {
771     error (g, _("could not parse /etc/fstab or empty file"));
772     goto error;
773   }
774
775   for (entry = entries; *entry != NULL; entry++) {
776     snprintf (augpath, sizeof augpath, "%s/spec", *entry);
777     spec = guestfs_aug_get (g, augpath);
778     if (spec == NULL) goto error;
779
780     snprintf (augpath, sizeof augpath, "%s/file", *entry);
781     mp = guestfs_aug_get (g, augpath);
782     if (mp == NULL) {
783       free (spec);
784       goto error;
785     }
786
787     r = add_fstab_entry (g, fs, spec, mp, md_map);
788     free (spec);
789     free (mp);
790
791     if (r == -1) goto error;
792   }
793
794   if (md_map) hash_free (md_map);
795   guestfs___free_string_list (entries);
796   return 0;
797
798 error:
799   if (md_map) hash_free (md_map);
800   if (entries) guestfs___free_string_list (entries);
801   return -1;
802 }
803
804 /* Add a filesystem and possibly a mountpoint entry for
805  * the root filesystem 'fs'.
806  *
807  * 'spec' is the fstab spec field, which might be a device name or a
808  * pseudodevice or 'UUID=...' or 'LABEL=...'.
809  *
810  * 'mp' is the mount point, which could also be 'swap' or 'none'.
811  */
812 static int
813 add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
814                  const char *spec, const char *mp, Hash_table *md_map)
815 {
816   /* Ignore certain mountpoints. */
817   if (STRPREFIX (mp, "/dev/") ||
818       STREQ (mp, "/dev") ||
819       STRPREFIX (mp, "/media/") ||
820       STRPREFIX (mp, "/proc/") ||
821       STREQ (mp, "/proc") ||
822       STRPREFIX (mp, "/selinux/") ||
823       STREQ (mp, "/selinux") ||
824       STRPREFIX (mp, "/sys/") ||
825       STREQ (mp, "/sys"))
826     return 0;
827
828   /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */
829   if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) ||
830       STREQ (spec, "/dev/floppy") ||
831       STREQ (spec, "/dev/cdrom"))
832     return 0;
833
834   /* Resolve UUID= and LABEL= to the actual device. */
835   char *device = NULL;
836   if (STRPREFIX (spec, "UUID="))
837     device = guestfs_findfs_uuid (g, &spec[5]);
838   else if (STRPREFIX (spec, "LABEL="))
839     device = guestfs_findfs_label (g, &spec[6]);
840   /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */
841   else if (STREQ (spec, "/dev/root"))
842     /* Resolve /dev/root to the current device. */
843     device = safe_strdup (g, fs->device);
844   else if (STRPREFIX (spec, "/dev/"))
845     /* Resolve guest block device names. */
846     device = resolve_fstab_device (g, spec, md_map);
847
848   /* If we haven't resolved the device successfully by this point,
849    * we don't care, just ignore it.
850    */
851   if (device == NULL)
852     return 0;
853
854   char *mountpoint = safe_strdup (g, mp);
855
856   /* Add this to the fstab entry in 'fs'.
857    * Note these are further filtered by guestfs_inspect_get_mountpoints
858    * and guestfs_inspect_get_filesystems.
859    */
860   size_t n = fs->nr_fstab + 1;
861   struct inspect_fstab_entry *p;
862
863   p = realloc (fs->fstab, n * sizeof (struct inspect_fstab_entry));
864   if (p == NULL) {
865     perrorf (g, "realloc");
866     free (device);
867     free (mountpoint);
868     return -1;
869   }
870
871   fs->fstab = p;
872   fs->nr_fstab = n;
873
874   /* These are owned by the handle and freed by guestfs___free_inspect_info. */
875   fs->fstab[n-1].device = device;
876   fs->fstab[n-1].mountpoint = mountpoint;
877
878   debug (g, "fstab: device=%s mountpoint=%s", device, mountpoint);
879
880   return 0;
881 }
882
883 /* Compute a uuid hash as a simple xor of of its 4 32bit components */
884 static size_t
885 uuid_hash(const void *x, size_t table_size)
886 {
887   const md_uuid *a = x;
888
889   size_t h = a->uuid[0];
890   for (size_t i = 1; i < 4; i++) {
891     h ^= a->uuid[i];
892   }
893
894   return h % table_size;
895 }
896
897 static bool
898 uuid_cmp(const void *x, const void *y)
899 {
900   const md_uuid *a = x;
901   const md_uuid *b = y;
902
903   for (size_t i = 0; i < 1; i++) {
904     if (a->uuid[i] != b->uuid[i]) return false;
905   }
906
907   return true;
908 }
909
910 static void
911 md_uuid_free(void *x)
912 {
913   md_uuid *a = x;
914   free(a->path);
915   free(a);
916 }
917
918 /* Taken from parse_uuid in mdadm */
919 static int
920 parse_uuid(const char *str, uint32_t *uuid)
921 {
922   for (size_t i = 0; i < 4; i++) uuid[i] = 0;
923
924   int hit = 0; /* number of Hex digIT */
925   char c;
926   while ((c = *str++)) {
927     int n;
928     if (c >= '0' && c <= '9')
929       n = c - '0';
930     else if (c >= 'a' && c <= 'f')
931       n = 10 + c - 'a';
932     else if (c >= 'A' && c <= 'F')
933       n = 10 + c - 'A';
934     else if (strchr(":. -", c))
935       continue;
936     else return false;
937
938     if (hit < 32) {
939       uuid[hit / 8] <<= 4;
940       uuid[hit / 8] += n;
941     }
942     hit++;
943   }
944   if (hit == 32) return 0;
945
946   return -1;
947 }
948
949 /* Create a mapping of uuids to appliance md device names */
950 static int
951 map_app_md_devices (guestfs_h *g, Hash_table **map)
952 {
953   char **mds = NULL;
954   int n = 0;
955
956   /* A hash mapping uuids to md device names */
957   *map = hash_initialize(16, NULL, uuid_hash, uuid_cmp, md_uuid_free);
958   if (*map == NULL) g->abort_cb();
959
960   mds = guestfs_list_md_devices(g);
961   if (mds == NULL) goto error;
962
963   for (char **md = mds; *md != NULL; md++) {
964     char **detail = guestfs_md_detail(g, *md);
965     if (detail == NULL) goto error;
966
967     /* Iterate over keys until we find uuid */
968     char **i;
969     for (i = detail; *i != NULL; i += 2) {
970       if (STREQ(*i, "uuid")) break;
971     }
972
973     /* We found it */
974     if (*i) {
975       /* Next item is the uuid value */
976       i++;
977
978       md_uuid *entry = safe_malloc(g, sizeof(md_uuid));
979       entry->path = safe_strdup(g, *md);
980
981       if (parse_uuid(*i, entry->uuid) == -1) {
982         /* Invalid UUID is weird, but not fatal. */
983         debug(g, "inspect-os: guestfs_md_detail returned invalid "
984                  "uuid for %s: %s", *md, *i);
985         guestfs___free_string_list(detail);
986         md_uuid_free(entry);
987         continue;
988       }
989
990       const void *matched = NULL;
991       switch (hash_insert_if_absent(*map, entry, &matched)) {
992         case -1:
993           g->abort_cb();
994
995         case 0:
996           /* Duplicate uuid in for md device is weird, but not fatal. */
997           debug(g, "inspect-os: md devices %s and %s have the same uuid",
998                 ((md_uuid *)matched)->path, entry->path);
999           md_uuid_free(entry);
1000           break;
1001
1002         default:
1003           n++;
1004       }
1005     }
1006
1007     guestfs___free_string_list(detail);
1008   }
1009
1010   guestfs___free_string_list(mds);
1011
1012   return n;
1013
1014 error:
1015   hash_free(*map); *map = NULL;
1016   guestfs___free_string_list(mds);
1017
1018   return -1;
1019 }
1020
1021 static size_t
1022 mdadm_app_hash(const void *x, size_t table_size)
1023 {
1024   const mdadm_app *a = x;
1025   return hash_pjw(a->mdadm, table_size);
1026 }
1027
1028 static bool
1029 mdadm_app_cmp(const void *x, const void *y)
1030 {
1031   const mdadm_app *a = x;
1032   const mdadm_app *b = y;
1033
1034   return strcmp(a->mdadm, b->mdadm) == 0;
1035 }
1036
1037 static void
1038 mdadm_app_free(void *x)
1039 {
1040   mdadm_app *a = x;
1041   free(a->mdadm);
1042   free(a->app);
1043   free(a);
1044 }
1045
1046 /* Get a map of md device names in mdadm.conf to their device names in the
1047  * appliance */
1048 static int
1049 map_md_devices(guestfs_h *g, Hash_table **map)
1050 {
1051   Hash_table *app_map = NULL;
1052   char **matches = NULL;
1053   *map = NULL;
1054
1055   /* Get a map of md device uuids to their device names in the appliance */
1056   int n_app_md_devices = map_app_md_devices(g, &app_map);
1057   if (n_app_md_devices == -1) goto error;
1058
1059   /* Nothing to do if there are no md devices */
1060   if (n_app_md_devices == 0) {
1061     hash_free(app_map);
1062     return 0;
1063   }
1064
1065   /* Get all arrays listed in mdadm.conf */
1066   matches = guestfs_aug_match(g, "/files/etc/mdadm.conf/array");
1067   if (!matches) goto error;
1068
1069   /* Log a debug message if we've got md devices, but nothing in mdadm.conf */
1070   if (matches[0] == NULL) {
1071     debug(g, "Appliance has MD devices, but augeas returned no array matches "
1072              "in mdadm.conf");
1073     guestfs___free_string_list(matches);
1074     hash_free(app_map);
1075     return 0;
1076   }
1077
1078   *map = hash_initialize(16, NULL, mdadm_app_hash, mdadm_app_cmp,
1079                                    mdadm_app_free);
1080   if (!*map) g->abort_cb();
1081
1082   for (char **match = matches; *match != NULL; match++) {
1083     /* Get device name and uuid for each array */
1084     char *dev_path = safe_asprintf(g, "%s/devicename", *match);
1085     char *dev = guestfs_aug_get(g, dev_path);
1086     free(dev_path);
1087     if (!dev) goto error;
1088
1089     char *uuid_path = safe_asprintf(g, "%s/uuid", *match);
1090     char *uuid = guestfs_aug_get(g, uuid_path);
1091     free(uuid_path);
1092     if (!uuid) {
1093       free(dev);
1094       goto error;
1095     }
1096
1097     /* Parse the uuid into an md_uuid structure so we can look it up in the
1098      * uuid->appliance device map */
1099     md_uuid mdadm;
1100     mdadm.path = dev;
1101     if (parse_uuid(uuid, mdadm.uuid) == -1) {
1102       /* Invalid uuid. Weird, but not fatal. */
1103       debug(g, "inspect-os: mdadm.conf contains invalid uuid for %s: %s",
1104             dev, uuid);
1105       free(dev);
1106       free(uuid);
1107       continue;
1108     }
1109     free(uuid);
1110
1111     /* If there's a corresponding uuid in the appliance, create a new
1112      * entry in the transitive map */
1113     md_uuid *app = hash_lookup(app_map, &mdadm);
1114     if (app) {
1115       mdadm_app *entry = safe_malloc(g, sizeof(mdadm_app));
1116       entry->mdadm = dev;
1117       entry->app = safe_strdup(g, app->path);
1118
1119       switch (hash_insert_if_absent(*map, entry, NULL)) {
1120         case -1:
1121           g->abort_cb();
1122
1123         case 0:
1124           /* Duplicate uuid in for md device is weird, but not fatal. */
1125           debug(g, "inspect-os: mdadm.conf contains multiple entries for %s",
1126                 app->path);
1127           mdadm_app_free(entry);
1128           continue;
1129
1130         default:
1131           ;;
1132       }
1133     } else {
1134       free(dev);
1135     }
1136   }
1137
1138   hash_free(app_map);
1139   guestfs___free_string_list(matches);
1140
1141   return 0;
1142
1143 error:
1144   if (app_map) hash_free(app_map);
1145   if (matches) guestfs___free_string_list(matches);
1146   if (*map) hash_free(*map);
1147
1148   return -1;
1149 }
1150
1151 /* Resolve block device name to the libguestfs device name, eg.
1152  * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV.  This
1153  * assumes that disks were added in the same order as they appear to
1154  * the real VM, which is a reasonable assumption to make.  Return
1155  * anything we don't recognize unchanged.
1156  */
1157 static char *
1158 resolve_fstab_device (guestfs_h *g, const char *spec, Hash_table *md_map)
1159 {
1160   char *device = NULL;
1161   char *type, *slice, *disk, *part;
1162
1163   if (STRPREFIX (spec, "/dev/mapper/")) {
1164     /* LVM2 does some strange munging on /dev/mapper paths for VGs and
1165      * LVs which contain '-' character:
1166      *
1167      * ><fs> lvcreate LV--test VG--test 32
1168      * ><fs> debug ls /dev/mapper
1169      * VG----test-LV----test
1170      *
1171      * This makes it impossible to reverse those paths directly, so
1172      * we have implemented lvm_canonical_lv_name in the daemon.
1173      */
1174     device = guestfs_lvm_canonical_lv_name (g, spec);
1175   }
1176   else if (match3 (g, spec, re_xdev, &type, &disk, &part)) {
1177     /* type: (h|s|v|xv)
1178      * disk: ([a-z]+)
1179      * part: (\d*) */
1180     char **devices = guestfs_list_devices (g);
1181     if (devices == NULL)
1182       return NULL;
1183
1184     /* Check any hints we were passed for a non-heuristic mapping */
1185     char *name = safe_asprintf (g, "%sd%s", type, disk);
1186     size_t i = 0;
1187     struct drive *drive = g->drives;
1188     while (drive) {
1189       if (drive->name && STREQ(drive->name, name)) {
1190         device = safe_asprintf (g, "%s%s", devices[i], part);
1191         break;
1192       }
1193
1194       i++; drive = drive->next;
1195     }
1196     free (name);
1197
1198     /* Guess the appliance device name if we didn't find a matching hint */
1199     if (!device) {
1200       /* Count how many disks the libguestfs appliance has */
1201       size_t count;
1202       for (count = 0; devices[count] != NULL; count++)
1203         ;
1204
1205       /* Calculate the numerical index of the disk */
1206       i = disk[0] - 'a';
1207       for (char *p = disk + 1; *p != '\0'; p++) {
1208         i += 1; i *= 26;
1209         i += *p - 'a';
1210       }
1211
1212       /* Check the index makes sense wrt the number of disks the appliance has.
1213        * If it does, map it to an appliance disk. */
1214       if (i < count) {
1215         device = safe_asprintf (g, "%s%s", devices[i], part);
1216       }
1217     }
1218
1219     free (type);
1220     free (disk);
1221     free (part);
1222     guestfs___free_string_list (devices);
1223   }
1224   else if (match2 (g, spec, re_cciss, &disk, &part)) {
1225     /* disk: (cciss/c\d+d\d+)
1226      * part: (\d+)? */
1227     char **devices = guestfs_list_devices (g);
1228     if (devices == NULL)
1229       return NULL;
1230
1231     /* Check any hints we were passed for a non-heuristic mapping */
1232     size_t i = 0;
1233     struct drive *drive = g->drives;
1234     while (drive) {
1235       if (drive->name && STREQ(drive->name, disk)) {
1236         if (part) {
1237           device = safe_asprintf (g, "%s%s", devices[i], part);
1238         } else {
1239           device = safe_strdup (g, devices[i]);
1240         }
1241         break;
1242       }
1243
1244       i++; drive = drive->next;
1245     }
1246
1247     /* We don't try to guess mappings for cciss devices */
1248
1249     free (disk);
1250     free (part);
1251     guestfs___free_string_list (devices);
1252   }
1253   else if (md_map && (disk = match1 (g, spec, re_mdN)) != NULL) {
1254     mdadm_app entry;
1255     entry.mdadm = disk;
1256
1257     mdadm_app *app = hash_lookup (md_map, &entry);
1258     if (app) device = safe_strdup (g, app->app);
1259
1260     free(disk);
1261   }
1262   else if (match3 (g, spec, re_freebsd, &disk, &slice, &part)) {
1263     /* FreeBSD disks are organized quite differently.  See:
1264      * http://www.freebsd.org/doc/handbook/disk-organization.html
1265      * FreeBSD "partitions" are exposed as quasi-extended partitions
1266      * numbered from 5 in Linux.  I have no idea what happens when you
1267      * have multiple "slices" (the FreeBSD term for MBR partitions).
1268      */
1269     int disk_i = guestfs___parse_unsigned_int (g, disk);
1270     int slice_i = guestfs___parse_unsigned_int (g, slice);
1271     int part_i = part[0] - 'a' /* counting from 0 */;
1272     free (disk);
1273     free (slice);
1274     free (part);
1275
1276     if (disk_i != -1 && disk_i <= 26 &&
1277         slice_i > 0 && slice_i <= 1 /* > 4 .. see comment above */ &&
1278         part_i >= 0 && part_i < 26) {
1279       device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a', part_i + 5);
1280     }
1281   }
1282
1283   /* Didn't match device pattern, return original spec unchanged. */
1284   if (device == NULL)
1285     device = safe_strdup (g, spec);
1286
1287   return device;
1288 }
1289
1290 /* Call 'f' with Augeas opened and having parsed 'filename' (this file
1291  * must exist).  As a security measure, this bails if the file is too
1292  * large for a reasonable configuration file.  After the call to 'f'
1293  * Augeas is closed.
1294  */
1295 static int
1296 inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs,
1297                      const char **configfiles,
1298                      int (*f) (guestfs_h *, struct inspect_fs *))
1299 {
1300   /* Security: Refuse to do this if a config file is too large. */
1301   for (const char **i = configfiles; *i != NULL; i++) {
1302     if (guestfs_exists(g, *i) == 0) continue;
1303
1304     int64_t size = guestfs_filesize (g, *i);
1305     if (size == -1)
1306       /* guestfs_filesize failed and has already set error in handle */
1307       return -1;
1308     if (size > MAX_AUGEAS_FILE_SIZE) {
1309       error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1310              *i, size);
1311       return -1;
1312     }
1313   }
1314
1315   /* If !feature_available (g, "augeas") then the next call will fail.
1316    * Arguably we might want to fall back to a non-Augeas method in
1317    * this case.
1318    */
1319   if (guestfs_aug_init (g, "/", 16|32) == -1)
1320     return -1;
1321
1322   int r = -1;
1323
1324   /* Tell Augeas to only load one file (thanks RaphaĆ«l Pinson). */
1325 #define AUGEAS_LOAD "/augeas/load//incl[. != \""
1326 #define AUGEAS_LOAD_LEN (strlen(AUGEAS_LOAD))
1327   size_t conflen = strlen(configfiles[0]);
1328   size_t buflen = AUGEAS_LOAD_LEN + conflen + 1 /* Closing " */;
1329   char *buf = safe_malloc(g, buflen + 2 /* Closing ] + null terminator */);
1330
1331   memcpy(buf, AUGEAS_LOAD, AUGEAS_LOAD_LEN);
1332   memcpy(buf + AUGEAS_LOAD_LEN, configfiles[0], conflen);
1333   buf[buflen - 1] = '"';
1334 #undef AUGEAS_LOAD_LEN
1335 #undef AUGEAS_LOAD
1336
1337 #define EXCL " and . != \""
1338 #define EXCL_LEN (strlen(EXCL))
1339   for (const char **i = &configfiles[1]; *i != NULL; i++) {
1340     size_t orig_buflen = buflen;
1341     conflen = strlen(*i);
1342     buflen += EXCL_LEN + conflen + 1 /* Closing " */;
1343     buf = safe_realloc(g, buf, buflen + 2 /* Closing ] + null terminator */);
1344     char *s = buf + orig_buflen;
1345
1346     memcpy(s, EXCL, EXCL_LEN);
1347     memcpy(s + EXCL_LEN, *i, conflen);
1348     buf[buflen - 1] = '"';
1349   }
1350 #undef EXCL_LEN
1351 #undef EXCL
1352
1353   buf[buflen] = ']';
1354   buf[buflen + 1] = '\0';
1355
1356   if (guestfs_aug_rm (g, buf) == -1) {
1357     free(buf);
1358     goto out;
1359   }
1360   free(buf);
1361
1362   if (guestfs_aug_load (g) == -1)
1363     goto out;
1364
1365   r = f (g, fs);
1366
1367  out:
1368   guestfs_aug_close (g);
1369
1370   return r;
1371 }
1372
1373 #endif /* defined(HAVE_HIVEX) */