c2db970ff9b8d04af22c767c85c836e12c0139e3
[libguestfs.git] / df / df.c
1 /* virt-df
2  * Copyright (C) 2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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
26 #ifdef HAVE_LIBVIRT
27 #include <libvirt/libvirt.h>
28 #include <libvirt/virterror.h>
29 #endif
30
31 #include "progname.h"
32
33 #include "guestfs.h"
34 #include "options.h"
35 #include "virt-df.h"
36
37 static void try_df (const char *name, const char *uuid, const char *dev, int offset);
38 static int find_dev_in_devices (const char *dev, char **devices);
39
40 /* Since we want this function to be robust against very bad failure
41  * cases (hello, https://bugzilla.kernel.org/show_bug.cgi?id=18792) it
42  * won't exit on guestfs failures.
43  */
44 int
45 df_on_handle (const char *name, const char *uuid, char **devices, int offset)
46 {
47   int ret = -1;
48   size_t i;
49   char **fses = NULL;
50   int free_devices = 0, is_lv;
51
52   if (verbose) {
53     fprintf (stderr, "df_on_handle %s devices=", name);
54     if (devices) {
55       fputc ('[', stderr);
56       for (i = 0; devices[i] != NULL; ++i) {
57         if (i > 0)
58           fputc (' ', stderr);
59         fputs (devices[i], stderr);
60       }
61       fputc (']', stderr);
62     }
63     else
64       fprintf (stderr, "null");
65     fputc ('\n', stderr);
66   }
67
68   if (devices == NULL) {
69     devices = guestfs_list_devices (g);
70     if (devices == NULL)
71       goto cleanup;
72     free_devices = 1;
73   } else {
74     /* Mask LVM for just the devices in the set. */
75     if (guestfs_lvm_set_filter (g, devices) == -1)
76       goto cleanup;
77   }
78
79   /* list-filesystems will return filesystems on every device ... */
80   fses = guestfs_list_filesystems (g);
81   if (fses == NULL)
82     goto cleanup;
83
84   /* ... so we need to filter out only the devices we are interested in. */
85   for (i = 0; fses[i] != NULL; i += 2) {
86     if (STRNEQ (fses[i+1], "") &&
87         STRNEQ (fses[i+1], "swap") &&
88         STRNEQ (fses[i+1], "unknown")) {
89       is_lv = guestfs_is_lv (g, fses[i]);
90       if (is_lv > 0)        /* LVs are OK because of the LVM filter */
91         try_df (name, uuid, fses[i], -1);
92       else if (is_lv == 0) {
93         if (find_dev_in_devices (fses[i], devices))
94           try_df (name, uuid, fses[i], offset);
95       }
96     }
97   }
98
99   ret = 0;
100
101  cleanup:
102   if (fses) {
103     for (i = 0; fses[i] != NULL; ++i)
104       free (fses[i]);
105     free (fses);
106   }
107
108   if (free_devices) {
109     for (i = 0; devices[i] != NULL; ++i)
110       free (devices[i]);
111     free (devices);
112   }
113
114   return ret;
115 }
116
117 static int
118 find_dev_in_devices (const char *dev, char **devices)
119 {
120   size_t i;
121
122   for (i = 0; devices[i] != NULL; ++i) {
123     if (STRPREFIX (dev, devices[i]))
124       return 1;
125   }
126
127   return 0;
128 }
129
130 static void
131 try_df (const char *name, const char *uuid,
132         const char *dev, int offset)
133 {
134   struct guestfs_statvfs *stat = NULL;
135   guestfs_error_handler_cb old_error_cb;
136   void *old_error_data;
137
138   if (verbose)
139     fprintf (stderr, "try_df %s %s %d\n", name, dev, offset);
140
141   /* Try mounting and stating the device.  This might reasonably fail,
142    * so don't show errors.
143    */
144   old_error_cb = guestfs_get_error_handler (g, &old_error_data);
145   guestfs_set_error_handler (g, NULL, NULL);
146
147   if (guestfs_mount_ro (g, dev, "/") == 0) {
148     stat = guestfs_statvfs (g, "/");
149     guestfs_umount_all (g);
150   }
151
152   guestfs_set_error_handler (g, old_error_cb, old_error_data);
153
154   if (stat) {
155     print_stat (name, uuid, dev, offset, stat);
156     guestfs_free_statvfs (stat);
157   }
158 }