xsparse.c 9.8 KB

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