f9d06aa860fed843e5abf8bf5c13b6a9cf2d847b
[libguestfs.git] / src / dbdump.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 #ifdef HAVE_PCRE
33 #include <pcre.h>
34 #endif
35
36 #ifdef HAVE_HIVEX
37 #include <hivex.h>
38 #endif
39
40 #include "c-ctype.h"
41 #include "ignore-value.h"
42
43 #include "guestfs.h"
44 #include "guestfs-internal.h"
45
46 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX) && defined(DB_DUMP)
47
48 static unsigned char *convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen, size_t *binlen_rtn);
49
50 /* This helper function is specialized to just reading the hash-format
51  * output from db_dump/db4_dump.  It's just enough to support the RPM
52  * database format.  Note that the filename must not contain any shell
53  * characters (this is guaranteed by the caller).
54  */
55 int
56 guestfs___read_db_dump (guestfs_h *g,
57                         const char *dumpfile, void *opaque,
58                         guestfs___db_dump_callback callback)
59 {
60 #define cmd_len (strlen (dumpfile) + 64)
61   char cmd[cmd_len];
62   FILE *pp = NULL;
63   char *line = NULL;
64   size_t len = 0;
65   ssize_t linelen;
66   unsigned char *key = NULL, *value = NULL;
67   size_t keylen, valuelen;
68   int ret = -1;
69
70   snprintf (cmd, cmd_len, DB_DUMP " -k '%s'", dumpfile);
71
72   debug (g, "read_db_dump command: %s", cmd);
73
74   pp = popen (cmd, "r");
75   if (pp == NULL) {
76     perrorf (g, "popen: %s", cmd);
77     goto out;
78   }
79
80   /* Ignore everything to end-of-header marker. */
81   while ((linelen = getline (&line, &len, pp)) != -1) {
82     if (STRPREFIX (line, "HEADER=END"))
83       break;
84   }
85
86   if (linelen == -1) {
87     error (g, _("unexpected end of output from db_dump command before end of header"));
88     goto out;
89   }
90
91   /* Now read the key, value pairs.  They are prefixed with a space and
92    * printed as hex strings, so convert those strings to binary.  Pass
93    * the strings up to the callback function.
94    */
95   while ((linelen = getline (&line, &len, pp)) != -1) {
96     if (STRPREFIX (line, "DATA=END"))
97       break;
98
99     if (linelen < 1 || line[0] != ' ') {
100       error (g, _("unexpected line from db_dump command, no space prefix"));
101       goto out;
102     }
103
104     if ((key = convert_hex_to_binary (g, &line[1], linelen-1,
105                                       &keylen)) == NULL)
106       goto out;
107
108     if ((linelen = getline (&line, &len, pp)) == -1)
109       break;
110
111     if (linelen < 1 || line[0] != ' ') {
112       error (g, _("unexpected line from db_dump command, no space prefix"));
113       goto out;
114     }
115
116     if ((value = convert_hex_to_binary (g, &line[1], linelen-1,
117                                         &valuelen)) == NULL)
118       goto out;
119
120     if (callback (g, key, keylen, value, valuelen, opaque) == -1)
121       goto out;
122
123     free (key);
124     free (value);
125     key = value = NULL;
126   }
127
128   if (linelen == -1) {
129     error (g, _("unexpected end of output from db_dump command before end of data"));
130     goto out;
131   }
132
133   /* Catch errors from the db_dump command. */
134   if (pclose (pp) == -1) {
135     perrorf (g, "pclose: %s", cmd);
136     goto out;
137   }
138   pp = NULL;
139
140   ret = 0;
141
142  out:
143   if (pp)
144     pclose (pp);
145
146   free (line);
147   free (key);
148   free (value);
149
150   return ret;
151 #undef cmd_len
152 }
153
154 static int
155 convert_hex_octet (const char *h)
156 {
157   int r;
158
159   switch (h[0]) {
160   case 'a'...'f':
161     r = (h[0] - 'a' + 10) << 4;
162     break;
163   case 'A'...'F':
164     r = (h[0] - 'A' + 10) << 4;
165     break;
166   case '0'...'9':
167     r = (h[0] - '0') << 4;
168     break;
169   default:
170     return -1;
171   }
172
173   switch (h[1]) {
174   case 'a'...'f':
175     r |= h[1] - 'a' + 10;
176     break;
177   case 'A'...'F':
178     r |= h[1] - 'A' + 10;
179     break;
180   case '0'...'9':
181     r |= h[1] - '0';
182     break;
183   default:
184     return -1;
185   }
186
187   return r;
188 }
189
190 static unsigned char *
191 convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen,
192                        size_t *binlen_rtn)
193 {
194   unsigned char *bin;
195   size_t binlen;
196   size_t i, o;
197   int b;
198
199   if (hexlen > 0 && hex[hexlen-1] == '\n')
200     hexlen--;
201
202   binlen = hexlen / 2;
203   bin = safe_malloc (g, binlen);
204
205   for (i = o = 0; i+1 < hexlen && o < binlen; i += 2, ++o) {
206     b = convert_hex_octet (&hex[i]);
207     if (b >= 0)
208       bin[o] = b;
209     else {
210       error (g, _("unexpected non-hex digits in output of db_dump command"));
211       free (bin);
212       return NULL;
213     }
214   }
215
216   *binlen_rtn = binlen;
217   return bin;
218 }
219
220 #endif /* defined(HAVE_PCRE) && defined(HAVE_HIVEX) && defined(DB_DUMP) */