zip.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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. #undef __STRICT_ANSI__
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <sys/stat.h>
  14. #include <errno.h>
  15. #include "physfs.h"
  16. #include "unzip.h"
  17. #define __PHYSICSFS_INTERNAL__
  18. #include "physfs_internal.h"
  19. #if (!defined PHYSFS_SUPPORTS_ZIP)
  20. #error PHYSFS_SUPPORTS_ZIP must be defined.
  21. #endif
  22. #define MAXZIPENTRYSIZE 256
  23. typedef struct
  24. {
  25. unzFile handle;
  26. uLong totalEntries;
  27. } ZIPinfo;
  28. typedef struct
  29. {
  30. int i;
  31. } ZIPfileinfo;
  32. extern const DirFunctions __PHYSFS_DirFunctions_ZIP;
  33. static const FileFunctions __PHYSFS_FileFunctions_ZIP;
  34. static int ZIP_read(FileHandle *handle, void *buffer,
  35. unsigned int objSize, unsigned int objCount)
  36. {
  37. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 0); /* !!! write me! */
  38. } /* ZIP_read */
  39. static int ZIP_eof(FileHandle *handle)
  40. {
  41. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 1); /* !!! write me! */
  42. } /* ZIP_eof */
  43. static int ZIP_tell(FileHandle *handle)
  44. {
  45. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, -1); /* !!! write me! */
  46. } /* ZIP_tell */
  47. static int ZIP_seek(FileHandle *handle, int offset)
  48. {
  49. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 0); /* !!! write me! */
  50. } /* ZIP_seek */
  51. static int ZIP_fileLength(FileHandle *handle)
  52. {
  53. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, -1); /* !!! write me! */
  54. } /* ZIP_fileLength */
  55. static int ZIP_fileClose(FileHandle *handle)
  56. {
  57. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 0); /* !!! write me! */
  58. } /* ZIP_fileClose */
  59. static int ZIP_isArchive(const char *filename, int forWriting)
  60. {
  61. int retval = 0;
  62. unzFile unz = unzOpen(filename);
  63. unz_global_info global;
  64. if (unz != NULL)
  65. {
  66. if (unzGetGlobalInfo(unz, &global) == UNZ_OK)
  67. retval = 1;
  68. unzClose(unz);
  69. } /* if */
  70. return(retval);
  71. } /* ZIP_isArchive */
  72. static DirHandle *ZIP_openArchive(const char *name, int forWriting)
  73. {
  74. unzFile unz = NULL;
  75. DirHandle *retval = NULL;
  76. unz_global_info global;
  77. BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
  78. errno = 0;
  79. BAIL_IF_MACRO(access(name, R_OK) != 0, strerror(errno), NULL);
  80. retval = malloc(sizeof (DirHandle));
  81. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  82. unz = unzOpen(name);
  83. if ((unz == NULL) || (unzGetGlobalInfo(unz, &global) != UNZ_OK))
  84. {
  85. if (unz)
  86. unzClose(unz);
  87. free(retval);
  88. BAIL_IF_MACRO(1, ERR_UNSUPPORTED_ARCHIVE, NULL);
  89. } /* if */
  90. retval->opaque = malloc(sizeof (ZIPinfo));
  91. if (retval->opaque == NULL)
  92. {
  93. free(retval);
  94. unzClose(unz);
  95. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  96. } /* if */
  97. ((ZIPinfo *) (retval->opaque))->handle = unz;
  98. ((ZIPinfo *) (retval->opaque))->totalEntries = global.number_entry;
  99. retval->funcs = &__PHYSFS_DirFunctions_ZIP;
  100. return(retval);
  101. } /* ZIP_openArchive */
  102. /* "uLong" is defined by zlib and/or unzip.h ... */
  103. typedef union
  104. {
  105. unsigned char uchar4[4];
  106. uLong ul;
  107. } uchar4_uLong;
  108. static int version_does_symlinks(uLong version)
  109. {
  110. int retval = 0;
  111. unsigned char hosttype;
  112. uchar4_uLong converter;
  113. converter.ul = version;
  114. hosttype = converter.uchar4[1]; /* !!! BYTE ORDERING ALERT! */
  115. /*
  116. * These are the platforms that can build an archive with symlinks,
  117. * according to the Info-ZIP project.
  118. */
  119. switch (hosttype)
  120. {
  121. case 3: /* Unix */
  122. case 16: /* BeOS */
  123. case 5: /* Atari */
  124. retval = 1;
  125. break;
  126. } /* switch */
  127. return(retval);
  128. } /* version_does_symlinks */
  129. static int entry_is_symlink(unz_file_info *info)
  130. {
  131. return (
  132. (version_does_symlinks(info->version)) &&
  133. (info->uncompressed_size > 0) &&
  134. (info->external_fa & 0x0120000) /* symlink flag. */
  135. );
  136. } /* entry_is_symlink */
  137. static char *ZIP_realpath(unzFile fh, unz_file_info *info)
  138. {
  139. char *retval = NULL;
  140. int size;
  141. if (entry_is_symlink(info))
  142. {
  143. BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL);
  144. size = info->uncompressed_size;
  145. retval = (char *) malloc(size + 1);
  146. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  147. if (unzReadCurrentFile(fh, retval, size) != size)
  148. {
  149. free(retval);
  150. __PHYSFS_setError(ERR_IO_ERROR);
  151. retval = NULL;
  152. } /* if */
  153. retval[size] = '\0';
  154. unzCloseCurrentFile(fh);
  155. } /* if */
  156. return(retval);
  157. } /* ZIP_realpath */
  158. /* !!! This is seriously ugly. */
  159. static LinkedStringList *ZIP_enumerateFiles(DirHandle *h,
  160. const char *dirname,
  161. int omitSymLinks)
  162. {
  163. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  164. unzFile fh = zi->handle;
  165. int i;
  166. int dlen;
  167. LinkedStringList *retval = NULL;
  168. LinkedStringList *l = NULL;
  169. LinkedStringList *prev = NULL;
  170. char buf[MAXZIPENTRYSIZE];
  171. char *d;
  172. unz_file_info info;
  173. /* jump to first file entry... */
  174. BAIL_IF_MACRO(unzGoToFirstFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL);
  175. dlen = strlen(dirname);
  176. d = malloc(dlen + 1);
  177. BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, NULL);
  178. strcpy(d, dirname);
  179. if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
  180. {
  181. dlen--;
  182. d[dlen] = '\0';
  183. } /* if */
  184. for (i = 0; i < zi->totalEntries; i++, unzGoToNextFile(fh))
  185. {
  186. char *ptr;
  187. char *add_file;
  188. int this_dlen;
  189. unzGetCurrentFileInfo(fh, &info, buf, sizeof (buf), NULL, 0, NULL, 0);
  190. if ((omitSymLinks) && (entry_is_symlink(&info)))
  191. continue;
  192. buf[sizeof (buf) - 1] = '\0'; /* safety. */
  193. this_dlen = strlen(buf);
  194. if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
  195. {
  196. this_dlen--;
  197. buf[this_dlen] = '\0';
  198. } /* if */
  199. if (this_dlen <= dlen) /* not in this dir. */
  200. continue;
  201. if (*d == '\0')
  202. add_file = buf;
  203. else
  204. {
  205. if (buf[dlen] != '/') /* can't be in same directory? */
  206. continue;
  207. buf[dlen] = '\0';
  208. if (strcmp(d, buf) != 0) /* not same directory? */
  209. continue;
  210. add_file = buf + dlen + 1;
  211. } /* else */
  212. /* handle subdirectories... */
  213. ptr = strchr(add_file, '/');
  214. if (ptr != NULL)
  215. {
  216. LinkedStringList *j;
  217. *ptr = '\0';
  218. for (j = retval; j != NULL; j = j->next)
  219. {
  220. if (strcmp(j->str, ptr) == 0)
  221. break;
  222. } /* for */
  223. if (j != NULL)
  224. continue;
  225. } /* if */
  226. l = (LinkedStringList *) malloc(sizeof (LinkedStringList));
  227. if (l == NULL)
  228. break;
  229. l->str = (char *) malloc(strlen(add_file) + 1);
  230. if (l->str == NULL)
  231. {
  232. free(l);
  233. break;
  234. } /* if */
  235. strcpy(l->str, add_file);
  236. if (retval == NULL)
  237. retval = l;
  238. else
  239. prev->next = l;
  240. prev = l;
  241. l->next = NULL;
  242. } /* for */
  243. free(d);
  244. return(retval);
  245. } /* ZIP_enumerateFiles */
  246. /* !!! This is seriously ugly. */
  247. static int ZIP_exists_symcheck(DirHandle *h, const char *name, int follow)
  248. {
  249. char buf[MAXZIPENTRYSIZE];
  250. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  251. unzFile fh = zi->handle;
  252. int dlen;
  253. char *d;
  254. int i;
  255. unz_file_info info;
  256. BAIL_IF_MACRO(unzGoToFirstFile(fh) != UNZ_OK, ERR_IO_ERROR, 0);
  257. dlen = strlen(name);
  258. d = malloc(dlen + 1);
  259. BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, 0);
  260. strcpy(d, name);
  261. if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
  262. {
  263. dlen--;
  264. d[dlen] = '\0';
  265. } /* if */
  266. for (i = 0; i < zi->totalEntries; i++, unzGoToNextFile(fh))
  267. {
  268. int this_dlen;
  269. unzGetCurrentFileInfo(fh, &info, buf, sizeof (buf), NULL, 0, NULL, 0);
  270. buf[sizeof (buf) - 1] = '\0'; /* safety. */
  271. this_dlen = strlen(buf);
  272. if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
  273. {
  274. this_dlen--;
  275. buf[this_dlen] = '\0';
  276. } /* if */
  277. if ( ((buf[dlen] == '/') || (buf[dlen] == '\0')) &&
  278. (strncmp(d, buf, dlen) == 0) )
  279. {
  280. int retval = 1;
  281. free(d);
  282. if (follow) /* follow symlinks? */
  283. {
  284. char *real = ZIP_realpath(fh, &info);
  285. if (real != NULL)
  286. {
  287. retval = ZIP_exists_symcheck(h, real, follow - 1);
  288. free(real);
  289. } /* if */
  290. } /* if */
  291. return(retval);
  292. } /* if */
  293. } /* for */
  294. free(d);
  295. return(0);
  296. } /* ZIP_exists_symcheck */
  297. static int ZIP_exists_nofollow(DirHandle *h, const char *name)
  298. {
  299. return(ZIP_exists_symcheck(h, name, 0));
  300. } /* ZIP_exists_nofollow */
  301. static int ZIP_exists(DirHandle *h, const char *name)
  302. {
  303. /* follow at most 20 links to prevent recursion... */
  304. int retval = ZIP_exists_symcheck(h, name, 20);
  305. unz_file_info info;
  306. unzFile fh = ((ZIPinfo *) (h->opaque))->handle;
  307. if (retval)
  308. {
  309. /* current zip entry will be the file in question. */
  310. unzGetCurrentFileInfo(fh, &info, NULL, 0, NULL, 0, NULL, 0);
  311. /* if it's a symlink, then we ran into a possible symlink loop. */
  312. BAIL_IF_MACRO(entry_is_symlink(&info), ERR_TOO_MANY_SYMLINKS, 0);
  313. } /* if */
  314. return(retval);
  315. } /* ZIP_exists */
  316. static int ZIP_isDirectory(DirHandle *h, const char *name)
  317. {
  318. char buf[MAXZIPENTRYSIZE];
  319. unzFile fh = ((ZIPinfo *) (h->opaque))->handle;
  320. int retval = ZIP_exists(h, name);
  321. int dlen = strlen(name);
  322. if (retval)
  323. {
  324. /* current zip entry will be the file in question. */
  325. unzGetCurrentFileInfo(fh, NULL, buf, sizeof (buf), NULL, 0, NULL, 0);
  326. retval = (buf[dlen] == '/'); /* !!! yikes. */
  327. } /* if */
  328. return(retval);
  329. } /* ZIP_isDirectory */
  330. static int ZIP_isSymLink(DirHandle *h, const char *name)
  331. {
  332. unzFile fh = ((ZIPinfo *) (h->opaque))->handle;
  333. int retval = ZIP_exists_nofollow(h, name);
  334. unz_file_info info;
  335. if (retval)
  336. {
  337. /* current zip entry will be the file in question. */
  338. unzGetCurrentFileInfo(fh, &info, NULL, 0, NULL, 0, NULL, 0);
  339. retval = entry_is_symlink(&info);
  340. } /* if */
  341. return(retval);
  342. } /* ZIP_isSymLink */
  343. static FileHandle *ZIP_openRead(DirHandle *h, const char *filename)
  344. {
  345. /*
  346. unzFile fh = ((ZIPinfo *) (h->opaque))->handle;
  347. unz_file_info info;
  348. */
  349. FileHandle *retval = NULL;
  350. BAIL_IF_MACRO(!ZIP_exists(h, filename), ERR_NO_SUCH_FILE, NULL);
  351. /*
  352. finfo->startPos = ;
  353. finfo->curPos = offset;
  354. finfo->size = size;
  355. retval->opaque = (void *) finfo;
  356. retval->funcs = &__PHYSFS_FileFunctions_GRP;
  357. retval->dirHandle = h;
  358. */
  359. return(retval);
  360. } /* ZIP_openRead */
  361. static void ZIP_dirClose(DirHandle *h)
  362. {
  363. unzClose(((ZIPinfo *) (h->opaque))->handle);
  364. free(h->opaque);
  365. free(h);
  366. } /* ZIP_dirClose */
  367. static const FileFunctions __PHYSFS_FileFunctions_ZIP =
  368. {
  369. ZIP_read, /* read() method */
  370. NULL, /* write() method */
  371. ZIP_eof, /* eof() method */
  372. ZIP_tell, /* tell() method */
  373. ZIP_seek, /* seek() method */
  374. ZIP_fileLength, /* fileLength() method */
  375. ZIP_fileClose /* fileClose() method */
  376. };
  377. const DirFunctions __PHYSFS_DirFunctions_ZIP =
  378. {
  379. ZIP_isArchive, /* isArchive() method */
  380. ZIP_openArchive, /* openArchive() method */
  381. ZIP_enumerateFiles, /* enumerateFiles() method */
  382. ZIP_exists, /* exists() method */
  383. ZIP_isDirectory, /* isDirectory() method */
  384. ZIP_isSymLink, /* isSymLink() method */
  385. ZIP_openRead, /* openRead() method */
  386. NULL, /* openWrite() method */
  387. NULL, /* openAppend() method */
  388. NULL, /* remove() method */
  389. NULL, /* mkdir() method */
  390. ZIP_dirClose /* dirClose() method */
  391. };
  392. const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP =
  393. {
  394. "ZIP",
  395. "PkZip/WinZip/Info-Zip compatible",
  396. "Ryan C. Gordon (icculus@linuxgames.com)",
  397. "http://www.icculus.org/~icculus/",
  398. };
  399. /* end of zip.c ... */