]> Dogcows Code - chaz/tar/blob - scripts/xsparse.c
Relicense under GPLv3
[chaz/tar] / scripts / xsparse.c
1 /* xsparse - expands compressed sparse file images extracted from GNU tar
2 archives.
3
4 Copyright (C) 2006, 2007 Free Software Foundation, Inc.
5
6 Written by Sergey Poznyakoff
7
8 This program is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3, or (at your option) any later
11 version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <limits.h>
29 #include <errno.h>
30
31 /* Bound on length of the string representing an off_t.
32 See INT_STRLEN_BOUND in intprops.h for explanation */
33 #define OFF_T_STRLEN_BOUND ((sizeof (off_t) * CHAR_BIT) * 146 / 485 + 1)
34 #define OFF_T_STRSIZE_BOUND (OFF_T_STRLEN_BOUND+1)
35
36 #define BLOCKSIZE 512
37
38 struct sp_array
39 {
40 off_t offset;
41 size_t numbytes;
42 };
43
44 char *progname;
45 int verbose;
46
47 void
48 die (int code, char *fmt, ...)
49 {
50 va_list ap;
51
52 fprintf (stderr, "%s: ", progname);
53 va_start (ap, fmt);
54 vfprintf (stderr, fmt, ap);
55 va_end (ap);
56 fprintf (stderr, "\n");
57 exit (code);
58 }
59
60 void *
61 emalloc (size_t size)
62 {
63 char *p = malloc (size);
64 if (!p)
65 die (1, "not enough memory");
66 return p;
67 }
68
69 off_t
70 string_to_off (char *p, char **endp)
71 {
72 off_t v = 0;
73
74 for (; *p; p++)
75 {
76 int digit = *p - '0';
77 off_t x = v * 10;
78 if (9 < (unsigned) digit)
79 {
80 if (endp)
81 {
82 *endp = p;
83 break;
84 }
85 die (1, "number parse error near %s", p);
86 }
87 else if (x / 10 != v)
88 die (1, "number out of allowed range, near %s", p);
89 v = x + digit;
90 if (v < 0)
91 die (1, "negative number");
92 }
93 if (endp)
94 *endp = p;
95 return v;
96 }
97
98 size_t
99 string_to_size (char *p, char **endp)
100 {
101 off_t v = string_to_off (p, endp);
102 size_t ret = v;
103 if (ret != v)
104 die (1, "number too big");
105 return ret;
106 }
107
108 size_t sparse_map_size;
109 struct sp_array *sparse_map;
110
111 void
112 get_line (char *s, int size, FILE *stream)
113 {
114 char *p = fgets (s, size, stream);
115 size_t len;
116
117 if (!p)
118 die (1, "unexpected end of file");
119 len = strlen (p);
120 if (s[len - 1] != '\n')
121 die (1, "buffer overflow");
122 s[len - 1] = 0;
123 }
124
125 int
126 get_var (FILE *fp, char **name, char **value)
127 {
128 static char *buffer;
129 static size_t bufsize = OFF_T_STRSIZE_BOUND;
130 char *p, *q;
131
132 buffer = emalloc (bufsize);
133 do
134 {
135 size_t len, s;
136
137 if (!fgets (buffer, bufsize, fp))
138 return 0;
139 len = strlen (buffer);
140 if (len == 0)
141 return 0;
142
143 s = string_to_size (buffer, &p);
144 if (*p != ' ')
145 die (1, "malformed header: expected space but found %s", p);
146 if (buffer[len-1] != '\n')
147 {
148 if (bufsize < s + 1)
149 {
150 bufsize = s + 1;
151 buffer = realloc (buffer, bufsize);
152 if (!buffer)
153 die (1, "not enough memory");
154 }
155 if (!fgets (buffer + len, s - len + 1, fp))
156 die (1, "unexpected end of file or read error");
157 }
158 p++;
159 }
160 while (memcmp (p, "GNU.sparse.", 11));
161
162 p += 11;
163 q = strchr (p, '=');
164 if (!q)
165 die (1, "malformed header: expected `=' not found");
166 *q++ = 0;
167 q[strlen (q) - 1] = 0;
168 *name = p;
169 *value = q;
170 return 1;
171 }
172
173 char *outname;
174 off_t outsize;
175 unsigned version_major;
176 unsigned version_minor;
177
178 void
179 read_xheader (char *name)
180 {
181 char *kw, *val;
182 FILE *fp = fopen (name, "r");
183 char *expect = NULL;
184 size_t i = 0;
185
186 if (verbose)
187 printf ("Reading extended header file\n");
188
189 while (get_var (fp, &kw, &val))
190 {
191 if (verbose)
192 printf ("Found variable GNU.sparse.%s = %s\n", kw, val);
193
194 if (expect && strcmp (kw, expect))
195 die (1, "bad keyword sequence: expected `%s' but found `%s'",
196 expect, kw);
197 expect = NULL;
198 if (strcmp (kw, "name") == 0)
199 {
200 outname = emalloc (strlen (val) + 1);
201 strcpy (outname, val);
202 }
203 else if (strcmp (kw, "major") == 0)
204 {
205 version_major = string_to_size (val, NULL);
206 }
207 else if (strcmp (kw, "minor") == 0)
208 {
209 version_minor = string_to_size (val, NULL);
210 }
211 else if (strcmp (kw, "realsize") == 0
212 || strcmp (kw, "size") == 0)
213 {
214 outsize = string_to_off (val, NULL);
215 }
216 else if (strcmp (kw, "numblocks") == 0)
217 {
218 sparse_map_size = string_to_size (val, NULL);
219 sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
220 }
221 else if (strcmp (kw, "offset") == 0)
222 {
223 sparse_map[i].offset = string_to_off (val, NULL);
224 expect = "numbytes";
225 }
226 else if (strcmp (kw, "numbytes") == 0)
227 {
228 sparse_map[i++].numbytes = string_to_size (val, NULL);
229 }
230 else if (strcmp (kw, "map") == 0)
231 {
232 for (i = 0; i < sparse_map_size; i++)
233 {
234 sparse_map[i].offset = string_to_off (val, &val);
235 if (*val != ',')
236 die (1, "bad GNU.sparse.map: expected `,' but found `%c'",
237 *val);
238 sparse_map[i].numbytes = string_to_size (val+1, &val);
239 if (*val != ',')
240 {
241 if (!(*val == 0 && i == sparse_map_size-1))
242 die (1, "bad GNU.sparse.map: expected `,' but found `%c'",
243 *val);
244 }
245 else
246 val++;
247 }
248 if (*val)
249 die (1, "bad GNU.sparse.map: garbage at the end");
250 }
251 }
252 if (expect)
253 die (1, "bad keyword sequence: expected `%s' not found", expect);
254 if (version_major == 0 && sparse_map_size == 0)
255 die (1, "size of the sparse map unknown");
256 if (i != sparse_map_size)
257 die (1, "not all sparse entries supplied");
258 fclose (fp);
259 }
260
261 void
262 read_map (FILE *ifp)
263 {
264 size_t i;
265 char nbuf[OFF_T_STRSIZE_BOUND];
266
267 if (verbose)
268 printf ("Reading v.1.0 sparse map\n");
269
270 get_line (nbuf, sizeof nbuf, ifp);
271 sparse_map_size = string_to_size (nbuf, NULL);
272 sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
273
274 for (i = 0; i < sparse_map_size; i++)
275 {
276 get_line (nbuf, sizeof nbuf, ifp);
277 sparse_map[i].offset = string_to_off (nbuf, NULL);
278 get_line (nbuf, sizeof nbuf, ifp);
279 sparse_map[i].numbytes = string_to_size (nbuf, NULL);
280 }
281
282 fseek (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
283 SEEK_SET);
284 }
285
286 void
287 expand_sparse (FILE *sfp, int ofd)
288 {
289 size_t i;
290 size_t maxbytes = 0;
291 char *buffer;
292
293 for (i = 0; i < sparse_map_size; i++)
294 if (maxbytes < sparse_map[i].numbytes)
295 maxbytes = sparse_map[i].numbytes;
296
297 for (buffer = malloc (maxbytes); !buffer; maxbytes /= 2)
298 if (maxbytes == 0)
299 die (1, "not enough memory");
300
301 for (i = 0; i < sparse_map_size; i++)
302 {
303 size_t size = sparse_map[i].numbytes;
304
305 if (size == 0)
306 ftruncate (ofd, sparse_map[i].offset);
307 else
308 {
309 lseek (ofd, sparse_map[i].offset, SEEK_SET);
310 while (size)
311 {
312 size_t rdsize = (size < maxbytes) ? size : maxbytes;
313 if (rdsize != fread (buffer, 1, rdsize, sfp))
314 die (1, "read error (%d)", errno);
315 if (rdsize != write (ofd, buffer, rdsize))
316 die (1, "write error (%d)", errno);
317 size -= rdsize;
318 }
319 }
320 }
321 free (buffer);
322 }
323
324 void
325 usage (int code)
326 {
327 printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
328 printf ("%s: expand sparse files extracted from GNU archives\n",
329 progname);
330 printf ("\nOPTIONS are:\n\n");
331 printf (" -h Display this help list\n");
332 printf (" -n Dry run: do nothing, print what would have been done\n");
333 printf (" -v Increase verbosity level\n");
334 printf (" -x FILE Parse extended header FILE\n\n");
335
336 exit (code);
337 }
338
339 void
340 guess_outname (char *name)
341 {
342 char *p;
343 char *s;
344
345 if (name[0] == '.' && name[1] == '/')
346 name += 2;
347
348 p = name + strlen (name) - 1;
349 s = NULL;
350
351 for (; p > name && *p != '/'; p--)
352 ;
353 if (*p == '/')
354 s = p + 1;
355 if (p != name)
356 {
357 for (p--; p > name && *p != '/'; p--)
358 ;
359 }
360
361 if (*p != '/')
362 {
363 if (s)
364 outname = s;
365 else
366 {
367 outname = emalloc (4 + strlen (name));
368 strcpy (outname, "../");
369 strcpy (outname + 3, name);
370 }
371 }
372 else
373 {
374 size_t len = p - name + 1;
375 outname = emalloc (len + strlen (s) + 1);
376 memcpy (outname, name, len);
377 strcpy (outname + len, s);
378 }
379 }
380
381 int
382 main (int argc, char **argv)
383 {
384 int c;
385 int dry_run = 0;
386 char *xheader_file = NULL;
387 char *inname;
388 FILE *ifp;
389 struct stat st;
390 int ofd;
391
392 progname = argv[0];
393 while ((c = getopt (argc, argv, "hnvx:")) != EOF)
394 {
395 switch (c)
396 {
397 case 'h':
398 usage (0);
399 break;
400
401 case 'x':
402 xheader_file = optarg;
403 break;
404
405 case 'n':
406 dry_run = 1;
407 case 'v':
408 verbose++;
409 break;
410
411 default:
412 exit (1);
413 }
414 }
415
416 argc -= optind;
417 argv += optind;
418
419 if (argc == 0 || argc > 2)
420 usage (1);
421
422 if (xheader_file)
423 read_xheader (xheader_file);
424
425 inname = argv[0];
426 if (argv[1])
427 outname = argv[1];
428
429 if (stat (inname, &st))
430 die (1, "cannot stat %s (%d)", inname, errno);
431
432 ifp = fopen (inname, "r");
433 if (ifp == NULL)
434 die (1, "cannot open file %s (%d)", inname, errno);
435
436 if (!xheader_file || version_major == 1)
437 read_map (ifp);
438
439 if (!outname)
440 guess_outname (inname);
441
442 ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
443 if (ofd == -1)
444 die (1, "cannot open file %s (%d)", outname, errno);
445
446 if (verbose)
447 printf ("Expanding file `%s' to `%s'\n", inname, outname);
448
449 if (dry_run)
450 {
451 printf ("Finished dry run\n");
452 return 0;
453 }
454
455 expand_sparse (ifp, ofd);
456
457 fclose (ifp);
458 close (ofd);
459
460 if (verbose)
461 printf ("Done\n");
462
463 if (outsize)
464 {
465 if (stat (outname, &st))
466 die (1, "cannot stat output file %s (%d)", outname, errno);
467 if (st.st_size != outsize)
468 die (1, "expanded file has wrong size");
469 }
470
471 return 0;
472 }
473
This page took 0.04897 seconds and 5 git commands to generate.