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