1 /* xsparse - expands compressed sparse file images extracted from GNU tar
4 Copyright 2006-2007, 2010, 2013-2014 Free Software Foundation, Inc.
6 This file is part of GNU tar.
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.
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.
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/>.
21 Written by Sergey Poznyakoff */
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)
51 die (int code, char *fmt, ...)
55 fprintf (stderr, "%s: ", progname);
57 vfprintf (stderr, fmt, ap);
59 fprintf (stderr, "\n");
66 char *p = malloc (size);
68 die (1, "not enough memory");
73 string_to_off (char *p, char **endp)
81 if (9 < (unsigned) digit)
88 die (1, "number parse error near %s", p);
91 die (1, "number out of allowed range, near %s", p);
94 die (1, "negative number");
102 string_to_size (char *p, char **endp)
104 off_t v = string_to_off (p, endp);
107 die (1, "number too big");
111 size_t sparse_map_size;
112 struct sp_array *sparse_map;
115 get_line (char *s, int size, FILE *stream)
117 char *p = fgets (s, size, stream);
121 die (1, "unexpected end of file");
123 if (s[len - 1] != '\n')
124 die (1, "buffer overflow");
129 get_var (FILE *fp, char **name, char **value)
132 static size_t bufsize = OFF_T_STRSIZE_BOUND;
135 buffer = emalloc (bufsize);
140 if (!fgets (buffer, bufsize, fp))
142 len = strlen (buffer);
146 s = string_to_size (buffer, &p);
148 die (1, "malformed header: expected space but found %s", p);
149 if (buffer[len-1] != '\n')
154 buffer = realloc (buffer, bufsize);
156 die (1, "not enough memory");
158 if (!fgets (buffer + len, s - len + 1, fp))
159 die (1, "unexpected end of file or read error");
163 while (memcmp (p, "GNU.sparse.", 11));
168 die (1, "malformed header: expected '=' not found");
170 q[strlen (q) - 1] = 0;
178 unsigned version_major;
179 unsigned version_minor;
182 read_xheader (char *name)
185 FILE *fp = fopen (name, "r");
190 printf ("Reading extended header file\n");
192 while (get_var (fp, &kw, &val))
195 printf ("Found variable GNU.sparse.%s = %s\n", kw, val);
197 if (expect && strcmp (kw, expect))
198 die (1, "bad keyword sequence: expected '%s' but found '%s'",
201 if (strcmp (kw, "name") == 0)
203 outname = emalloc (strlen (val) + 1);
204 strcpy (outname, val);
206 else if (strcmp (kw, "major") == 0)
208 version_major = string_to_size (val, NULL);
210 else if (strcmp (kw, "minor") == 0)
212 version_minor = string_to_size (val, NULL);
214 else if (strcmp (kw, "realsize") == 0
215 || strcmp (kw, "size") == 0)
217 outsize = string_to_off (val, NULL);
219 else if (strcmp (kw, "numblocks") == 0)
221 sparse_map_size = string_to_size (val, NULL);
222 sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
224 else if (strcmp (kw, "offset") == 0)
226 sparse_map[i].offset = string_to_off (val, NULL);
229 else if (strcmp (kw, "numbytes") == 0)
231 sparse_map[i++].numbytes = string_to_off (val, NULL);
233 else if (strcmp (kw, "map") == 0)
235 for (i = 0; i < sparse_map_size; i++)
237 sparse_map[i].offset = string_to_off (val, &val);
239 die (1, "bad GNU.sparse.map: expected ',' but found '%c'",
241 sparse_map[i].numbytes = string_to_off (val+1, &val);
244 if (!(*val == 0 && i == sparse_map_size-1))
245 die (1, "bad GNU.sparse.map: expected ',' but found '%c'",
252 die (1, "bad GNU.sparse.map: garbage at the end");
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");
268 char nbuf[OFF_T_STRSIZE_BOUND];
271 printf ("Reading v.1.0 sparse map\n");
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);
277 for (i = 0; i < sparse_map_size; i++)
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);
285 fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
290 expand_sparse (FILE *sfp, int ofd)
293 off_t max_numbytes = 0;
297 for (i = 0; i < sparse_map_size; i++)
298 if (max_numbytes < sparse_map[i].numbytes)
299 max_numbytes = sparse_map[i].numbytes;
301 maxbytes = max_numbytes < SIZE_MAX ? max_numbytes : SIZE_MAX;
303 for (buffer = malloc (maxbytes); !buffer; maxbytes /= 2)
305 die (1, "not enough memory");
307 for (i = 0; i < sparse_map_size; i++)
309 off_t size = sparse_map[i].numbytes;
312 ftruncate (ofd, sparse_map[i].offset);
315 lseek (ofd, sparse_map[i].offset, SEEK_SET);
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);
333 printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
334 printf ("%s: expand sparse files extracted from GNU archives\n",
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");
346 guess_outname (char *name)
351 if (name[0] == '.' && name[1] == '/')
354 p = name + strlen (name) - 1;
357 for (; p > name && *p != '/'; p--)
363 for (p--; p > name && *p != '/'; p--)
373 outname = emalloc (4 + strlen (name));
374 strcpy (outname, "../");
375 strcpy (outname + 3, name);
380 size_t len = p - name + 1;
381 outname = emalloc (len + strlen (s) + 1);
382 memcpy (outname, name, len);
383 strcpy (outname + len, s);
388 main (int argc, char **argv)
392 char *xheader_file = NULL;
399 while ((c = getopt (argc, argv, "hnvx:")) != EOF)
408 xheader_file = optarg;
425 if (argc == 0 || argc > 2)
429 read_xheader (xheader_file);
435 if (stat (inname, &st))
436 die (1, "cannot stat %s (%d)", inname, errno);
438 ifp = fopen (inname, "r");
440 die (1, "cannot open file %s (%d)", inname, errno);
442 if (!xheader_file || version_major == 1)
446 guess_outname (inname);
448 ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
450 die (1, "cannot open file %s (%d)", outname, errno);
453 printf ("Expanding file '%s' to '%s'\n", inname, outname);
457 printf ("Finished dry run\n");
461 expand_sparse (ifp, ofd);
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");