zip.c 17 KB

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