67eb65f541956d9a35e279dc1b53d16be0ca534b
[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 <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
42 #include "guestfs.h"
43 #include "guestfs-internal.h"
44 #include "guestfs-internal-actions.h"
45 #include "guestfs_protocol.h"
46
47 #if defined(HAVE_HIVEX)
48
49 /* The main inspection code. */
50 char **
51 guestfs__inspect_os (guestfs_h *g)
52 {
53   /* Remove any information previously stored in the handle. */
54   guestfs___free_inspect_info (g);
55
56   if (guestfs_umount_all (g) == -1)
57     return NULL;
58
59   /* Iterate over all possible devices.  Try to mount each
60    * (read-only).  Examine ones which contain filesystems and add that
61    * information to the handle.
62    */
63   /* Look to see if any devices directly contain filesystems (RHBZ#590167). */
64   char **devices;
65   devices = guestfs_list_devices (g);
66   if (devices == NULL)
67     return NULL;
68
69   size_t i;
70   for (i = 0; devices[i] != NULL; ++i) {
71     if (guestfs___check_for_filesystem_on (g, devices[i], 1, 0) == -1) {
72       guestfs___free_string_list (devices);
73       guestfs___free_inspect_info (g);
74       return NULL;
75     }
76   }
77   guestfs___free_string_list (devices);
78
79   /* Look at all partitions. */
80   char **partitions;
81   partitions = guestfs_list_partitions (g);
82   if (partitions == NULL) {
83     guestfs___free_inspect_info (g);
84     return NULL;
85   }
86
87   for (i = 0; partitions[i] != NULL; ++i) {
88     if (guestfs___check_for_filesystem_on (g, partitions[i], 0, i+1) == -1) {
89       guestfs___free_string_list (partitions);
90       guestfs___free_inspect_info (g);
91       return NULL;
92     }
93   }
94   guestfs___free_string_list (partitions);
95
96   /* Look at all LVs. */
97   if (guestfs___feature_available (g, "lvm2")) {
98     char **lvs;
99     lvs = guestfs_lvs (g);
100     if (lvs == NULL) {
101       guestfs___free_inspect_info (g);
102       return NULL;
103     }
104
105     for (i = 0; lvs[i] != NULL; ++i) {
106       if (guestfs___check_for_filesystem_on (g, lvs[i], 0, 0) == -1) {
107         guestfs___free_string_list (lvs);
108         guestfs___free_inspect_info (g);
109         return NULL;
110       }
111     }
112     guestfs___free_string_list (lvs);
113   }
114
115   /* At this point we have, in the handle, a list of all filesystems
116    * found and data about each one.  Now we assemble the list of
117    * filesystems which are root devices and return that to the user.
118    * Fall through to guestfs__inspect_get_roots to do that.
119    */
120   char **ret = guestfs__inspect_get_roots (g);
121   if (ret == NULL)
122     guestfs___free_inspect_info (g);
123   return ret;
124 }
125
126 static int
127 compare_strings (const void *vp1, const void *vp2)
128 {
129   const char *s1 = * (char * const *) vp1;
130   const char *s2 = * (char * const *) vp2;
131
132   return strcmp (s1, s2);
133 }
134
135 char **
136 guestfs__inspect_get_roots (guestfs_h *g)
137 {
138   /* NB. Doesn't matter if g->nr_fses == 0.  We just return an empty
139    * list in this case.
140    */
141
142   size_t i;
143   size_t count = 0;
144   for (i = 0; i < g->nr_fses; ++i)
145     if (g->fses[i].is_root)
146       count++;
147
148   char **ret = calloc (count+1, sizeof (char *));
149   if (ret == NULL) {
150     perrorf (g, "calloc");
151     return NULL;
152   }
153
154   count = 0;
155   for (i = 0; i < g->nr_fses; ++i) {
156     if (g->fses[i].is_root) {
157       ret[count] = safe_strdup (g, g->fses[i].device);
158       count++;
159     }
160   }
161   ret[count] = NULL;
162
163   qsort (ret, count, sizeof (char *), compare_strings);
164
165   return ret;
166 }
167
168 char *
169 guestfs__inspect_get_type (guestfs_h *g, const char *root)
170 {
171   struct inspect_fs *fs = guestfs___search_for_root (g, root);
172   if (!fs)
173     return NULL;
174
175   char *ret;
176   switch (fs->type) {
177   case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
178   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
179   case OS_TYPE_NETBSD: ret = safe_strdup (g, "netbsd"); break;
180   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
181   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
182   }
183
184   return ret;
185 }
186
187 char *
188 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
189 {
190   struct inspect_fs *fs = guestfs___search_for_root (g, root);
191   if (!fs)
192     return NULL;
193
194   return safe_strdup (g, fs->arch ? : "unknown");
195 }
196
197 char *
198 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
199 {
200   struct inspect_fs *fs = guestfs___search_for_root (g, root);
201   if (!fs)
202     return NULL;
203
204   char *ret;
205   switch (fs->distro) {
206   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
207   case OS_DISTRO_CENTOS: ret = safe_strdup (g, "centos"); break;
208   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
209   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
210   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
211   case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
212   case OS_DISTRO_MAGEIA: ret = safe_strdup (g, "mageia"); break;
213   case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
214   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
215   case OS_DISTRO_OPENSUSE: ret = safe_strdup (g, "opensuse"); break;
216   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
217   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
218   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
219   case OS_DISTRO_SCIENTIFIC_LINUX: ret = safe_strdup (g, "scientificlinux"); break;
220   case OS_DISTRO_SLACKWARE: ret = safe_strdup (g, "slackware"); break;
221   case OS_DISTRO_TTYLINUX: ret = safe_strdup (g, "ttylinux"); break;
222   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
223   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
224   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
225   }
226
227   return ret;
228 }
229
230 int
231 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
232 {
233   struct inspect_fs *fs = guestfs___search_for_root (g, root);
234   if (!fs)
235     return -1;
236
237   return fs->major_version;
238 }
239
240 int
241 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
242 {
243   struct inspect_fs *fs = guestfs___search_for_root (g, root);
244   if (!fs)
245     return -1;
246
247   return fs->minor_version;
248 }
249
250 char *
251 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
252 {
253   struct inspect_fs *fs = guestfs___search_for_root (g, root);
254   if (!fs)
255     return NULL;
256
257   return safe_strdup (g, fs->product_name ? : "unknown");
258 }
259
260 char *
261 guestfs__inspect_get_product_variant (guestfs_h *g, const char *root)
262 {
263   struct inspect_fs *fs = guestfs___search_for_root (g, root);
264   if (!fs)
265     return NULL;
266
267   return safe_strdup (g, fs->product_variant ? : "unknown");
268 }
269
270 char *
271 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
272 {
273   struct inspect_fs *fs = guestfs___search_for_root (g, root);
274   if (!fs)
275     return NULL;
276
277   if (!fs->windows_systemroot) {
278     error (g, _("not a Windows guest, or systemroot could not be determined"));
279     return NULL;
280   }
281
282   return safe_strdup (g, fs->windows_systemroot);
283 }
284
285 char *
286 guestfs__inspect_get_windows_current_control_set (guestfs_h *g,
287                                                   const char *root)
288 {
289   struct inspect_fs *fs = guestfs___search_for_root (g, root);
290   if (!fs)
291     return NULL;
292
293   if (!fs->windows_current_control_set) {
294     error (g, _("not a Windows guest, or CurrentControlSet could not be determined"));
295     return NULL;
296   }
297
298   return safe_strdup (g, fs->windows_current_control_set);
299 }
300
301 char *
302 guestfs__inspect_get_format (guestfs_h *g, const char *root)
303 {
304   struct inspect_fs *fs = guestfs___search_for_root (g, root);
305   if (!fs)
306     return NULL;
307
308   char *ret;
309   switch (fs->format) {
310   case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break;
311   case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break;
312   case OS_FORMAT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
313   }
314
315   return ret;
316 }
317
318 int
319 guestfs__inspect_is_live (guestfs_h *g, const char *root)
320 {
321   struct inspect_fs *fs = guestfs___search_for_root (g, root);
322   if (!fs)
323     return -1;
324
325   return fs->is_live_disk;
326 }
327
328 int
329 guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
330 {
331   struct inspect_fs *fs = guestfs___search_for_root (g, root);
332   if (!fs)
333     return -1;
334
335   return fs->is_netinst_disk;
336 }
337
338 int
339 guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
340 {
341   struct inspect_fs *fs = guestfs___search_for_root (g, root);
342   if (!fs)
343     return -1;
344
345   return fs->is_multipart_disk;
346 }
347
348 char **
349 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
350 {
351   struct inspect_fs *fs = guestfs___search_for_root (g, root);
352   if (!fs)
353     return NULL;
354
355   char **ret;
356
357   /* If no fstab information (Windows) return just the root. */
358   if (fs->nr_fstab == 0) {
359     ret = calloc (3, sizeof (char *));
360     ret[0] = safe_strdup (g, "/");
361     ret[1] = safe_strdup (g, root);
362     ret[2] = NULL;
363     return ret;
364   }
365
366 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
367   size_t i, count = 0;
368   for (i = 0; i < fs->nr_fstab; ++i)
369     if (CRITERION)
370       count++;
371
372   /* Hashtables have 2N+1 entries. */
373   ret = calloc (2*count+1, sizeof (char *));
374   if (ret == NULL) {
375     perrorf (g, "calloc");
376     return NULL;
377   }
378
379   count = 0;
380   for (i = 0; i < fs->nr_fstab; ++i)
381     if (CRITERION) {
382       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
383       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
384       count++;
385     }
386 #undef CRITERION
387
388   return ret;
389 }
390
391 char **
392 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
393 {
394   struct inspect_fs *fs = guestfs___search_for_root (g, root);
395   if (!fs)
396     return NULL;
397
398   char **ret;
399
400   /* If no fstab information (Windows) return just the root. */
401   if (fs->nr_fstab == 0) {
402     ret = calloc (2, sizeof (char *));
403     ret[0] = safe_strdup (g, root);
404     ret[1] = NULL;
405     return ret;
406   }
407
408   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
409   if (ret == NULL) {
410     perrorf (g, "calloc");
411     return NULL;
412   }
413
414   size_t i;
415   for (i = 0; i < fs->nr_fstab; ++i)
416     ret[i] = safe_strdup (g, fs->fstab[i].device);
417
418   return ret;
419 }
420
421 char **
422 guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root)
423 {
424   char **ret;
425   size_t i, count;
426   struct inspect_fs *fs;
427
428   fs = guestfs___search_for_root (g, root);
429   if (!fs)
430     return NULL;
431
432   /* If no drive mappings, return an empty hashtable. */
433   if (!fs->drive_mappings)
434     count = 0;
435   else {
436     for (count = 0; fs->drive_mappings[count] != NULL; count++)
437       ;
438   }
439
440   ret = calloc (count+1, sizeof (char *));
441   if (ret == NULL) {
442     perrorf (g, "calloc");
443     return NULL;
444   }
445
446   /* We need to make a deep copy of the hashtable since the caller
447    * will free it.
448    */
449   for (i = 0; i < count; ++i)
450     ret[i] = safe_strdup (g, fs->drive_mappings[i]);
451
452   ret[count] = NULL;
453
454   return ret;
455 }
456
457 char *
458 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
459 {
460   struct inspect_fs *fs = guestfs___search_for_root (g, root);
461   if (!fs)
462     return NULL;
463
464   char *ret;
465   switch (fs->package_format) {
466   case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
467   case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
468   case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
469   case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
470   case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
471   case OS_PACKAGE_FORMAT_PKGSRC: ret = safe_strdup (g, "pkgsrc"); break;
472   case OS_PACKAGE_FORMAT_UNKNOWN:
473   default:
474     ret = safe_strdup (g, "unknown");
475     break;
476   }
477
478   return ret;
479 }
480
481 char *
482 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
483 {
484   struct inspect_fs *fs = guestfs___search_for_root (g, root);
485   if (!fs)
486     return NULL;
487
488   char *ret;
489   switch (fs->package_management) {
490   case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
491   case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
492   case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
493   case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
494   case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
495   case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
496   case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
497   case OS_PACKAGE_MANAGEMENT_ZYPPER: ret = safe_strdup (g, "zypper"); break;
498   case OS_PACKAGE_MANAGEMENT_UNKNOWN:
499   default:
500     ret = safe_strdup (g, "unknown");
501     break;
502   }
503
504   return ret;
505 }
506
507 char *
508 guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
509 {
510   struct inspect_fs *fs = guestfs___search_for_root (g, root);
511   if (!fs)
512     return NULL;
513
514   return safe_strdup (g, fs->hostname ? : "unknown");
515 }
516
517 /* Download a guest file to a local temporary file.  The file is
518  * cached in the temporary directory, and is not downloaded again.
519  *
520  * The name of the temporary (downloaded) file is returned.  The
521  * caller must free the pointer, but does *not* need to delete the
522  * temporary file.  It will be deleted when the handle is closed.
523  *
524  * Refuse to download the guest file if it is larger than max_size.
525  * On this and other errors, NULL is returned.
526  *
527  * There is actually one cache per 'struct inspect_fs *' in order
528  * to handle the case of multiple roots.
529  */
530 char *
531 guestfs___download_to_tmp (guestfs_h *g, struct inspect_fs *fs,
532                            const char *filename,
533                            const char *basename, int64_t max_size)
534 {
535   char *r;
536   int fd;
537   char devfd[32];
538   int64_t size;
539
540   /* Make the basename unique by prefixing it with the fs number. */
541   if (asprintf (&r, "%s/%ld-%s", g->tmpdir, fs - g->fses, basename) == -1) {
542     perrorf (g, "asprintf");
543     return NULL;
544   }
545
546   /* If the file has already been downloaded, return. */
547   if (access (r, R_OK) == 0)
548     return r;
549
550   /* Check size of remote file. */
551   size = guestfs_filesize (g, filename);
552   if (size == -1)
553     /* guestfs_filesize failed and has already set error in handle */
554     goto error;
555   if (size > max_size) {
556     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
557            filename, size);
558     goto error;
559   }
560
561   fd = open (r, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0600);
562   if (fd == -1) {
563     perrorf (g, "open: %s", r);
564     goto error;
565   }
566
567   snprintf (devfd, sizeof devfd, "/dev/fd/%d", fd);
568
569   if (guestfs_download (g, filename, devfd) == -1) {
570     unlink (r);
571     close (fd);
572     goto error;
573   }
574
575   if (close (fd) == -1) {
576     perrorf (g, "close: %s", r);
577     unlink (r);
578     goto error;
579   }
580
581   return r;
582
583  error:
584   free (r);
585   return NULL;
586 }
587
588 struct inspect_fs *
589 guestfs___search_for_root (guestfs_h *g, const char *root)
590 {
591   if (g->nr_fses == 0) {
592     error (g, _("no inspection data: call guestfs_inspect_os first"));
593     return NULL;
594   }
595
596   size_t i;
597   struct inspect_fs *fs;
598   for (i = 0; i < g->nr_fses; ++i) {
599     fs = &g->fses[i];
600     if (fs->is_root && STREQ (root, fs->device))
601       return fs;
602   }
603
604   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
605          root);
606   return NULL;
607 }
608
609 #else /* no hivex at compile time */
610
611 /* XXX These functions should be in an optgroup. */
612
613 #define NOT_IMPL(r)                                                     \
614   error (g, _("inspection API not available since this version of libguestfs was compiled without the hivex library")); \
615   return r
616
617 char **
618 guestfs__inspect_os (guestfs_h *g)
619 {
620   NOT_IMPL(NULL);
621 }
622
623 char **
624 guestfs__inspect_get_roots (guestfs_h *g)
625 {
626   NOT_IMPL(NULL);
627 }
628
629 char *
630 guestfs__inspect_get_type (guestfs_h *g, const char *root)
631 {
632   NOT_IMPL(NULL);
633 }
634
635 char *
636 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
637 {
638   NOT_IMPL(NULL);
639 }
640
641 char *
642 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
643 {
644   NOT_IMPL(NULL);
645 }
646
647 int
648 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
649 {
650   NOT_IMPL(-1);
651 }
652
653 int
654 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
655 {
656   NOT_IMPL(-1);
657 }
658
659 char *
660 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
661 {
662   NOT_IMPL(NULL);
663 }
664
665 char *
666 guestfs__inspect_get_product_variant (guestfs_h *g, const char *root)
667 {
668   NOT_IMPL(NULL);
669 }
670
671 char *
672 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
673 {
674   NOT_IMPL(NULL);
675 }
676
677 char *
678 guestfs__inspect_get_windows_current_control_set (guestfs_h *g,
679                                                   const char *root)
680 {
681   NOT_IMPL(NULL);
682 }
683
684 char **
685 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
686 {
687   NOT_IMPL(NULL);
688 }
689
690 char **
691 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
692 {
693   NOT_IMPL(NULL);
694 }
695
696 char **
697 guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root)
698 {
699   NOT_IMPL(NULL);
700 }
701
702 char *
703 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
704 {
705   NOT_IMPL(NULL);
706 }
707
708 char *
709 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
710 {
711   NOT_IMPL(NULL);
712 }
713
714 char *
715 guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
716 {
717   NOT_IMPL(NULL);
718 }
719
720 char *
721 guestfs__inspect_get_format (guestfs_h *g, const char *root)
722 {
723   NOT_IMPL(NULL);
724 }
725
726 int
727 guestfs__inspect_is_live (guestfs_h *g, const char *root)
728 {
729   NOT_IMPL(-1);
730 }
731
732 int
733 guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
734 {
735   NOT_IMPL(-1);
736 }
737
738 int
739 guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
740 {
741   NOT_IMPL(-1);
742 }
743
744 #endif /* no hivex at compile time */
745
746 void
747 guestfs___free_inspect_info (guestfs_h *g)
748 {
749   size_t i;
750   for (i = 0; i < g->nr_fses; ++i) {
751     free (g->fses[i].device);
752     free (g->fses[i].product_name);
753     free (g->fses[i].product_variant);
754     free (g->fses[i].arch);
755     free (g->fses[i].hostname);
756     free (g->fses[i].windows_systemroot);
757     free (g->fses[i].windows_current_control_set);
758     size_t j;
759     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
760       free (g->fses[i].fstab[j].device);
761       free (g->fses[i].fstab[j].mountpoint);
762     }
763     free (g->fses[i].fstab);
764     if (g->fses[i].drive_mappings)
765       guestfs___free_string_list (g->fses[i].drive_mappings);
766   }
767   free (g->fses);
768   g->nr_fses = 0;
769   g->fses = NULL;
770 }
771
772 /* In the Perl code this is a public function. */
773 int
774 guestfs___feature_available (guestfs_h *g, const char *feature)
775 {
776   /* If there's an error we should ignore it, so to do that we have to
777    * temporarily replace the error handler with a null one.
778    */
779   guestfs_error_handler_cb old_error_cb = g->error_cb;
780   g->error_cb = NULL;
781
782   const char *groups[] = { feature, NULL };
783   int r = guestfs_available (g, (char * const *) groups);
784
785   g->error_cb = old_error_cb;
786
787   return r == 0 ? 1 : 0;
788 }