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