zip.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. /*
  2. * ZIP support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE in the source's root directory.
  5. *
  6. * This file written by Ryan C. Gordon.
  7. */
  8. /*
  9. * !!! FIXME: overall design bugs.
  10. *
  11. * Make unz_file_info.version into two fields of unsigned char. That's what
  12. * they are in the zipfile; heavens knows why unzip.c casts it...this causes
  13. * a byte ordering headache for me in entry_is_symlink().
  14. *
  15. * Maybe add a seekToStartOfCurrentFile() in unzip.c if complete seek
  16. * semantics are impossible.
  17. */
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <errno.h>
  22. #include "physfs.h"
  23. #include "unzip.h"
  24. #define __PHYSICSFS_INTERNAL__
  25. #include "physfs_internal.h"
  26. #if (!defined PHYSFS_SUPPORTS_ZIP)
  27. #error PHYSFS_SUPPORTS_ZIP must be defined.
  28. #endif
  29. #define MAXZIPENTRYSIZE 256
  30. typedef struct
  31. {
  32. char *name;
  33. unz_file_info info;
  34. char *symlink;
  35. } ZIPentry;
  36. typedef struct
  37. {
  38. char *archiveName;
  39. unz_global_info global;
  40. ZIPentry *entries;
  41. } ZIPinfo;
  42. typedef struct
  43. {
  44. unzFile handle;
  45. } ZIPfileinfo;
  46. extern const DirFunctions __PHYSFS_DirFunctions_ZIP;
  47. static const FileFunctions __PHYSFS_FileFunctions_ZIP;
  48. /* Number of symlinks to follow before we assume it's a recursive link... */
  49. #define SYMLINK_RECURSE_COUNT 20
  50. static int ZIP_read(FileHandle *handle, void *buffer,
  51. unsigned int objSize, unsigned int objCount)
  52. {
  53. unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle;
  54. int bytes = objSize * objCount;
  55. int rc = unzReadCurrentFile(fh, buffer, bytes);
  56. if (rc < bytes)
  57. __PHYSFS_setError(ERR_PAST_EOF);
  58. else if (rc == UNZ_ERRNO)
  59. __PHYSFS_setError(ERR_IO_ERROR);
  60. else if (rc < 0)
  61. __PHYSFS_setError(ERR_COMPRESSION);
  62. return(rc / objSize);
  63. } /* ZIP_read */
  64. static int ZIP_eof(FileHandle *handle)
  65. {
  66. return(unzeof(((ZIPfileinfo *) (handle->opaque))->handle));
  67. } /* ZIP_eof */
  68. static int ZIP_tell(FileHandle *handle)
  69. {
  70. return(unztell(((ZIPfileinfo *) (handle->opaque))->handle));
  71. } /* ZIP_tell */
  72. static int ZIP_fileLength(FileHandle *handle);
  73. static int ZIP_seek(FileHandle *handle, int offset)
  74. {
  75. /* this blows. */
  76. unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle;
  77. char *buf = NULL;
  78. int bufsize = 4096 * 2;
  79. BAIL_IF_MACRO(unztell(fh) == offset, NULL, 1);
  80. BAIL_IF_MACRO(ZIP_fileLength(handle) <= offset, ERR_PAST_EOF, 0);
  81. /* reset to the start of the zipfile. */
  82. unzCloseCurrentFile(fh);
  83. BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, 0);
  84. while ((buf == NULL) && (bufsize >= 512))
  85. {
  86. bufsize >>= 1; /* divides by two. */
  87. buf = (char *) malloc(bufsize);
  88. } /* while */
  89. BAIL_IF_MACRO(buf == NULL, ERR_OUT_OF_MEMORY, 0);
  90. while (offset > 0)
  91. {
  92. int chunk = (offset > bufsize) ? bufsize : offset;
  93. int rc = unzReadCurrentFile(fh, buf, chunk);
  94. BAIL_IF_MACRO(rc == 0, ERR_IO_ERROR, 0); /* shouldn't happen. */
  95. BAIL_IF_MACRO(rc == UNZ_ERRNO, ERR_IO_ERROR, 0);
  96. BAIL_IF_MACRO(rc < 0, ERR_COMPRESSION, 0);
  97. offset -= rc;
  98. } /* while */
  99. free(buf);
  100. return(offset == 0);
  101. } /* ZIP_seek */
  102. static int ZIP_fileLength(FileHandle *handle)
  103. {
  104. ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque);
  105. unz_file_info info;
  106. unzGetCurrentFileInfo(finfo->handle, &info, NULL, 0, NULL, 0, NULL, 0);
  107. return(info.uncompressed_size);
  108. } /* ZIP_fileLength */
  109. static int ZIP_fileClose(FileHandle *handle)
  110. {
  111. ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque);
  112. unzClose(finfo->handle);
  113. free(finfo);
  114. free(handle);
  115. return(1);
  116. } /* ZIP_fileClose */
  117. static int ZIP_isArchive(const char *filename, int forWriting)
  118. {
  119. int retval = 0;
  120. unzFile unz = unzOpen(filename);
  121. unz_global_info global;
  122. if (unz != NULL)
  123. {
  124. if (unzGetGlobalInfo(unz, &global) == UNZ_OK)
  125. retval = 1;
  126. unzClose(unz);
  127. } /* if */
  128. return(retval);
  129. } /* ZIP_isArchive */
  130. static void freeEntries(ZIPinfo *info, int count, const char *errmsg)
  131. {
  132. int i;
  133. for (i = 0; i < count; i++)
  134. {
  135. free(info->entries[i].name);
  136. if (info->entries[i].symlink != NULL)
  137. free(info->entries[i].symlink);
  138. } /* for */
  139. free(info->entries);
  140. if (errmsg != NULL)
  141. __PHYSFS_setError(errmsg);
  142. } /* freeEntries */
  143. static char *ZIP_realpath(unzFile fh, unz_file_info *info)
  144. {
  145. char *retval = NULL;
  146. int size;
  147. BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL);
  148. size = info->uncompressed_size;
  149. retval = (char *) malloc(size + 1);
  150. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  151. if (unzReadCurrentFile(fh, retval, size) != size)
  152. {
  153. free(retval);
  154. __PHYSFS_setError(ERR_IO_ERROR);
  155. retval = NULL;
  156. } /* if */
  157. retval[size] = '\0';
  158. unzCloseCurrentFile(fh);
  159. return(retval);
  160. } /* ZIP_realpath */
  161. /* "uLong" is defined by zlib and/or unzip.h ... */
  162. typedef union
  163. {
  164. unsigned char uchar4[4];
  165. uLong ul;
  166. } uchar4_uLong;
  167. static int version_does_symlinks(uLong version)
  168. {
  169. int retval = 0;
  170. unsigned char hosttype;
  171. uchar4_uLong converter;
  172. converter.ul = version;
  173. hosttype = converter.uchar4[1]; /* !!! BYTE ORDERING ALERT! */
  174. /*
  175. * These are the platforms that can build an archive with symlinks,
  176. * according to the Info-ZIP project.
  177. */
  178. switch (hosttype)
  179. {
  180. case 3: /* Unix */
  181. case 16: /* BeOS */
  182. case 5: /* Atari */
  183. retval = 1;
  184. break;
  185. } /* switch */
  186. return(retval);
  187. } /* version_does_symlinks */
  188. static int entry_is_symlink(unz_file_info *info)
  189. {
  190. return (
  191. (version_does_symlinks(info->version)) &&
  192. (info->uncompressed_size > 0) &&
  193. (info->external_fa & 0x0120000) /* symlink flag. */
  194. );
  195. } /* entry_is_symlink */
  196. static int loadZipEntries(ZIPinfo *info, unzFile unz)
  197. {
  198. int i, max;
  199. BAIL_IF_MACRO(unzGetGlobalInfo(unz, &(info->global)) != UNZ_OK,
  200. ERR_IO_ERROR, 0);
  201. BAIL_IF_MACRO(unzGoToFirstFile(unz) != UNZ_OK, ERR_IO_ERROR, 0);
  202. max = info->global.number_entry;
  203. info->entries = (ZIPentry *) malloc(sizeof (ZIPentry) * max);
  204. BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
  205. for (i = 0; i < max; i++)
  206. {
  207. unz_file_info *d = &((info->entries[i]).info);
  208. if (unzGetCurrentFileInfo(unz, d, NULL, 0, NULL, 0, NULL, 0) != UNZ_OK)
  209. {
  210. freeEntries(info, i, ERR_IO_ERROR);
  211. return(0);
  212. } /* if */
  213. (info->entries[i]).name = (char *) malloc(d->size_filename + 1);
  214. if ((info->entries[i]).name == NULL)
  215. {
  216. freeEntries(info, i, ERR_OUT_OF_MEMORY);
  217. return(0);
  218. } /* if */
  219. info->entries[i].symlink = NULL;
  220. if (unzGetCurrentFileInfo(unz, NULL, (info->entries[i]).name,
  221. d->size_filename + 1, NULL, 0,
  222. NULL, 0) != UNZ_OK)
  223. {
  224. freeEntries(info, i + 1, ERR_IO_ERROR);
  225. return(0);
  226. } /* if */
  227. if (entry_is_symlink(d))
  228. {
  229. info->entries[i].symlink = ZIP_realpath(unz, d);
  230. if (info->entries[i].symlink == NULL)
  231. {
  232. freeEntries(info, i + 1, NULL);
  233. return(0);
  234. } /* if */
  235. } /* if */
  236. if ((unzGoToNextFile(unz) != UNZ_OK) && (i + 1 < max))
  237. {
  238. freeEntries(info, i + 1, ERR_IO_ERROR);
  239. return(0);
  240. } /* if */
  241. } /* for */
  242. return(1);
  243. } /* loadZipEntries */
  244. static DirHandle *ZIP_openArchive(const char *name, int forWriting)
  245. {
  246. unzFile unz = NULL;
  247. DirHandle *retval = NULL;
  248. BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
  249. retval = malloc(sizeof (DirHandle));
  250. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  251. unz = unzOpen(name);
  252. if (unz == NULL)
  253. {
  254. free(retval);
  255. BAIL_IF_MACRO(1, ERR_UNSUPPORTED_ARCHIVE, NULL);
  256. } /* if */
  257. retval->opaque = malloc(sizeof (ZIPinfo));
  258. if (retval->opaque == NULL)
  259. {
  260. free(retval);
  261. unzClose(unz);
  262. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  263. } /* if */
  264. ((ZIPinfo *) (retval->opaque))->archiveName = malloc(strlen(name) + 1);
  265. if ( (((ZIPinfo *) (retval->opaque))->archiveName == NULL) ||
  266. (!loadZipEntries( (ZIPinfo *) (retval->opaque), unz)) )
  267. {
  268. if (((ZIPinfo *) (retval->opaque))->archiveName != NULL)
  269. free(((ZIPinfo *) (retval->opaque))->archiveName);
  270. free(retval->opaque);
  271. free(retval);
  272. unzClose(unz);
  273. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  274. } /* if */
  275. unzClose(unz);
  276. strcpy(((ZIPinfo *) (retval->opaque))->archiveName, name);
  277. retval->funcs = &__PHYSFS_DirFunctions_ZIP;
  278. return(retval);
  279. } /* ZIP_openArchive */
  280. /* !!! This is seriously ugly. */
  281. static LinkedStringList *ZIP_enumerateFiles(DirHandle *h,
  282. const char *dirname,
  283. int omitSymLinks)
  284. {
  285. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  286. unsigned int i;
  287. int dlen;
  288. LinkedStringList *retval = NULL;
  289. LinkedStringList *l = NULL;
  290. LinkedStringList *prev = NULL;
  291. char *d;
  292. ZIPentry *entry;
  293. char buf[MAXZIPENTRYSIZE];
  294. dlen = strlen(dirname);
  295. d = malloc(dlen + 1);
  296. BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, NULL);
  297. strcpy(d, dirname);
  298. if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
  299. {
  300. dlen--;
  301. d[dlen] = '\0';
  302. } /* if */
  303. for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++)
  304. {
  305. char *ptr;
  306. char *add_file;
  307. int this_dlen;
  308. if ((omitSymLinks) && (entry->symlink != NULL))
  309. continue;
  310. this_dlen = strlen(entry->name);
  311. if (this_dlen + 1 > MAXZIPENTRYSIZE)
  312. continue; /* ugh. */
  313. strcpy(buf, entry->name);
  314. if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
  315. {
  316. this_dlen--;
  317. buf[this_dlen] = '\0';
  318. } /* if */
  319. if (this_dlen <= dlen) /* not in this dir. */
  320. continue;
  321. if (*d == '\0')
  322. add_file = buf;
  323. else
  324. {
  325. if (buf[dlen] != '/') /* can't be in same directory? */
  326. continue;
  327. buf[dlen] = '\0';
  328. if (__PHYSFS_platformStricmp(d, buf) != 0) /* not same directory? */
  329. continue;
  330. add_file = buf + dlen + 1;
  331. } /* else */
  332. /* handle subdirectories... */
  333. ptr = strchr(add_file, '/');
  334. if (ptr != NULL)
  335. {
  336. LinkedStringList *j;
  337. *ptr = '\0';
  338. for (j = retval; j != NULL; j = j->next)
  339. {
  340. if (__PHYSFS_platformStricmp(j->str, ptr) == 0)
  341. break;
  342. } /* for */
  343. if (j != NULL)
  344. continue;
  345. } /* if */
  346. l = (LinkedStringList *) malloc(sizeof (LinkedStringList));
  347. if (l == NULL)
  348. break;
  349. l->str = (char *) malloc(strlen(add_file) + 1);
  350. if (l->str == NULL)
  351. {
  352. free(l);
  353. break;
  354. } /* if */
  355. strcpy(l->str, add_file);
  356. if (retval == NULL)
  357. retval = l;
  358. else
  359. prev->next = l;
  360. prev = l;
  361. l->next = NULL;
  362. } /* for */
  363. free(d);
  364. return(retval);
  365. } /* ZIP_enumerateFiles */
  366. /* !!! This is seriously ugly. */
  367. static int ZIP_exists_symcheck(DirHandle *h, const char *name, int follow)
  368. {
  369. char buf[MAXZIPENTRYSIZE];
  370. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  371. int dlen;
  372. char *d;
  373. unsigned int i;
  374. ZIPentry *entry;
  375. dlen = strlen(name);
  376. d = malloc(dlen + 1);
  377. BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, -1);
  378. strcpy(d, name);
  379. if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
  380. {
  381. dlen--;
  382. d[dlen] = '\0';
  383. } /* if */
  384. for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++)
  385. {
  386. int this_dlen = strlen(entry->name);
  387. if (this_dlen + 1 > MAXZIPENTRYSIZE)
  388. continue; /* ugh. */
  389. strcpy(buf, entry->name);
  390. if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
  391. {
  392. this_dlen--;
  393. buf[this_dlen] = '\0';
  394. } /* if */
  395. if ( ((buf[dlen] == '/') || (buf[dlen] == '\0')) &&
  396. (strncmp(d, buf, dlen) == 0) )
  397. {
  398. int retval = i;
  399. free(d);
  400. if (follow) /* follow symlinks? */
  401. {
  402. if (entry->symlink != NULL)
  403. retval = ZIP_exists_symcheck(h, entry->symlink, follow-1);
  404. } /* if */
  405. return(retval);
  406. } /* if */
  407. } /* for */
  408. free(d);
  409. return(-1);
  410. } /* ZIP_exists_symcheck */
  411. static int ZIP_exists(DirHandle *h, const char *name)
  412. {
  413. int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
  414. int is_sym;
  415. if (retval == -1)
  416. return(0);
  417. /* if it's a symlink, then we ran into a possible symlink loop. */
  418. is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
  419. BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0);
  420. return(1);
  421. } /* ZIP_exists */
  422. static int ZIP_isDirectory(DirHandle *h, const char *name)
  423. {
  424. int dlen;
  425. int is_sym;
  426. int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
  427. if (retval == -1)
  428. return(0);
  429. /* if it's a symlink, then we ran into a possible symlink loop. */
  430. is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
  431. BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0);
  432. dlen = strlen(name);
  433. /* !!! yikes. Better way to check? */
  434. retval = (((ZIPinfo *)(h->opaque))->entries[retval].name[dlen] == '/');
  435. return(retval);
  436. } /* ZIP_isDirectory */
  437. static int ZIP_isSymLink(DirHandle *h, const char *name)
  438. {
  439. int retval = ZIP_exists_symcheck(h, name, 0);
  440. if (retval == -1)
  441. return(0);
  442. retval = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
  443. return(retval);
  444. } /* ZIP_isSymLink */
  445. static FileHandle *ZIP_openRead(DirHandle *h, const char *filename)
  446. {
  447. FileHandle *retval = NULL;
  448. ZIPinfo *zi = ((ZIPinfo *) (h->opaque));
  449. ZIPfileinfo *finfo = NULL;
  450. int pos = ZIP_exists_symcheck(h, filename, SYMLINK_RECURSE_COUNT);
  451. unzFile f;
  452. BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, NULL);
  453. f = unzOpen(zi->archiveName);
  454. BAIL_IF_MACRO(f == NULL, ERR_IO_ERROR, NULL);
  455. if (unzGoToFirstFile(f) != UNZ_OK)
  456. {
  457. unzClose(f);
  458. BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
  459. } /* if */
  460. for (; pos > 0; pos--)
  461. {
  462. if (unzGoToNextFile(f) != UNZ_OK)
  463. {
  464. unzClose(f);
  465. BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
  466. } /* if */
  467. } /* for */
  468. if ( (unzOpenCurrentFile(f) != UNZ_OK) ||
  469. ( (finfo = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo))) == NULL ) )
  470. {
  471. unzClose(f);
  472. BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
  473. } /* if */
  474. if ( (!(retval = (FileHandle *) malloc(sizeof (FileHandle)))) ||
  475. (!(retval->opaque = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo)))) )
  476. {
  477. if (retval)
  478. free(retval);
  479. unzClose(f);
  480. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  481. } /* if */
  482. finfo->handle = f;
  483. retval->opaque = (void *) finfo;
  484. retval->funcs = &__PHYSFS_FileFunctions_ZIP;
  485. retval->dirHandle = h;
  486. return(retval);
  487. } /* ZIP_openRead */
  488. static void ZIP_dirClose(DirHandle *h)
  489. {
  490. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  491. freeEntries(zi, zi->global.number_entry, NULL);
  492. free(zi->archiveName);
  493. free(zi);
  494. free(h);
  495. } /* ZIP_dirClose */
  496. static const FileFunctions __PHYSFS_FileFunctions_ZIP =
  497. {
  498. ZIP_read, /* read() method */
  499. NULL, /* write() method */
  500. ZIP_eof, /* eof() method */
  501. ZIP_tell, /* tell() method */
  502. ZIP_seek, /* seek() method */
  503. ZIP_fileLength, /* fileLength() method */
  504. ZIP_fileClose /* fileClose() method */
  505. };
  506. const DirFunctions __PHYSFS_DirFunctions_ZIP =
  507. {
  508. ZIP_isArchive, /* isArchive() method */
  509. ZIP_openArchive, /* openArchive() method */
  510. ZIP_enumerateFiles, /* enumerateFiles() method */
  511. ZIP_exists, /* exists() method */
  512. ZIP_isDirectory, /* isDirectory() method */
  513. ZIP_isSymLink, /* isSymLink() method */
  514. ZIP_openRead, /* openRead() method */
  515. NULL, /* openWrite() method */
  516. NULL, /* openAppend() method */
  517. NULL, /* remove() method */
  518. NULL, /* mkdir() method */
  519. ZIP_dirClose /* dirClose() method */
  520. };
  521. const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP =
  522. {
  523. "ZIP",
  524. "PkZip/WinZip/Info-Zip compatible",
  525. "Ryan C. Gordon (icculus@clutteredmind.org)",
  526. "http://www.icculus.org/physfs/",
  527. };
  528. /* end of zip.c ... */