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