Add section "Notes on disk format" to the manual.
[virt-bmap.git] / visit.c
1 /* virt-ls visitor function (from libguestfs)
2  * Copyright (C) 2010-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 /* Adapted from
20 https://rwmj.wordpress.com/2010/12/15/tip-audit-virtual-machine-for-setuid-files/
21 */
22
23 #include <config.h>
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <libintl.h>
31
32 #include <guestfs.h>
33
34 #include "cleanups.h"
35 #include "visit.h"
36
37 static int _visit (guestfs_h *g, int depth, const char *dir, visitor_function f, void *opaque);
38
39 int
40 visit (guestfs_h *g, const char *dir, visitor_function f, void *opaque)
41 {
42   return _visit (g, 0, dir, f, opaque);
43 }
44
45 static int
46 _visit (guestfs_h *g, int depth, const char *dir,
47         visitor_function f, void *opaque)
48 {
49   /* Call 'f' with the top directory.  Note that ordinary recursive
50    * visits will not otherwise do this, so we have to have a special
51    * case.
52    */
53   if (depth == 0) {
54     CLEANUP_FREE_STATNS struct guestfs_statns *stat = NULL;
55     CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
56     int r;
57
58     stat = guestfs_lstatns (g, dir);
59     if (stat == NULL)
60       return -1;
61
62     xattrs = guestfs_lgetxattrs (g, dir);
63     if (xattrs == NULL)
64       return -1;
65
66     r = f (dir, NULL, stat, xattrs, opaque);
67
68     if (r == -1)
69       return -1;
70   }
71
72   size_t i, xattrp;
73   CLEANUP_FREE_STRING_LIST char **names = NULL;
74   CLEANUP_FREE_STAT_LIST struct guestfs_statns_list *stats = NULL;
75   CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
76
77   names = guestfs_ls (g, dir);
78   if (names == NULL)
79     return -1;
80
81   stats = guestfs_lstatnslist (g, dir, names);
82   if (stats == NULL)
83     return -1;
84
85   xattrs = guestfs_lxattrlist (g, dir, names);
86   if (xattrs == NULL)
87     return -1;
88
89   /* Call function on everything in this directory. */
90   for (i = 0, xattrp = 0; names[i] != NULL; ++i, ++xattrp) {
91     CLEANUP_FREE char *path = NULL;
92     struct guestfs_xattr_list file_xattrs;
93     size_t nr_xattrs;
94
95     assert (stats->len >= i);
96     assert (xattrs->len >= xattrp);
97
98     /* Find the list of extended attributes for this file. */
99     assert (strlen (xattrs->val[xattrp].attrname) == 0);
100
101     if (xattrs->val[xattrp].attrval_len == 0) {
102       fprintf (stderr, "virt-bmap: error getting extended attrs for %s %s\n",
103                dir, names[i]);
104       return -1;
105     }
106     /* attrval is not \0-terminated. */
107     char attrval[xattrs->val[xattrp].attrval_len+1];
108     memcpy (attrval, xattrs->val[xattrp].attrval,
109             xattrs->val[xattrp].attrval_len);
110     attrval[xattrs->val[xattrp].attrval_len] = '\0';
111     if (sscanf (attrval, "%zu", &nr_xattrs) != 1) {
112       fprintf (stderr, "virt-bmap: error: cannot parse xattr count for %s %s\n",
113                dir, names[i]);
114       return -1;
115     }
116
117     file_xattrs.len = nr_xattrs;
118     file_xattrs.val = &xattrs->val[xattrp+1];
119     xattrp += nr_xattrs;
120
121     /* Call the function. */
122     if (f (dir, names[i], &stats->val[i], &file_xattrs, opaque) == -1)
123       return -1;
124
125     /* Recursively call visit, but only on directories. */
126     if (is_dir (stats->val[i].st_mode)) {
127       path = full_path (dir, names[i]);
128       if (path == NULL)
129         return -1;
130       if (_visit (g, depth + 1, path, f, opaque) == -1)
131         return -1;
132     }
133   }
134
135   return 0;
136 }
137
138 char *
139 full_path (const char *dir, const char *name)
140 {
141   int r;
142   char *path;
143   int len;
144
145   len = strlen (dir);
146   if (len > 0 && dir[len - 1] == '/')
147     --len;
148
149   if (strcmp (dir, "/") == 0)
150     r = asprintf (&path, "/%s", name ? name : "");
151   else if (name)
152     r = asprintf (&path, "%.*s/%s", len, dir, name);
153   else
154     r = asprintf (&path, "%.*s", len, dir);
155
156   if (r == -1) {
157     perror ("asprintf");
158     return NULL;
159   }
160
161   return path;
162 }
163
164 /* In the libguestfs API, modes returned by lstat and friends are
165  * defined to contain Linux ABI values.  However since the "current
166  * operating system" might not be Linux, we have to hard-code those
167  * numbers here.
168  */
169 int
170 is_reg (int64_t mode)
171 {
172   return (mode & 0170000) == 0100000;
173 }
174
175 int
176 is_dir (int64_t mode)
177 {
178   return (mode & 0170000) == 0040000;
179 }
180
181 int
182 is_chr (int64_t mode)
183 {
184   return (mode & 0170000) == 0020000;
185 }
186
187 int
188 is_blk (int64_t mode)
189 {
190   return (mode & 0170000) == 0060000;
191 }
192
193 int
194 is_fifo (int64_t mode)
195 {
196   return (mode & 0170000) == 0010000;
197 }
198
199 /* symbolic link */
200 int
201 is_lnk (int64_t mode)
202 {
203   return (mode & 0170000) == 0120000;
204 }
205
206 int
207 is_sock (int64_t mode)
208 {
209   return (mode & 0170000) == 0140000;
210 }