xsparse.c 9.9 KB

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