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