archiver_iso9660.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. /*
  2. * ISO9660 support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE.txt in the source's root directory.
  5. *
  6. * This file written by Christoph Nelles.
  7. */
  8. /* !!! FIXME: this file needs Ryanification. */
  9. /*
  10. * Handles CD-ROM disk images (and raw CD-ROM devices).
  11. *
  12. * Not supported:
  13. * - RockRidge
  14. * - Non 2048 Sectors
  15. * - UDF
  16. *
  17. * Deviations from the standard
  18. * - Ignores mandatory sort order
  19. * - Allows various invalid file names
  20. *
  21. * Problems
  22. * - Ambiguities in the standard
  23. */
  24. #if (defined PHYSFS_SUPPORTS_ISO9660)
  25. #define __PHYSICSFS_INTERNAL__
  26. #include "physfs_internal.h"
  27. #include <time.h>
  28. /* cache files smaller than this completely in memory */
  29. #define ISO9660_FULLCACHEMAXSIZE 2048
  30. /* !!! FIXME: this is going to cause trouble. */
  31. #pragma pack(push) /* push current alignment to stack */
  32. #pragma pack(1) /* set alignment to 1 byte boundary */
  33. /* This is the format as defined by the standard
  34. typedef struct
  35. {
  36. PHYSFS_uint32 lsb;
  37. PHYSFS_uint32 msb;
  38. } ISOBB32bit; // 32byte Both Byte type, means the value first in LSB then in MSB
  39. typedef struct
  40. {
  41. PHYSFS_uint16 lsb;
  42. PHYSFS_uint16 msb;
  43. } ISOBB16bit; // 16byte Both Byte type, means the value first in LSB then in MSB
  44. */
  45. /* define better ones to simplify coding (less if's) */
  46. #if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN
  47. #define ISOBB32bit(name) PHYSFS_uint32 name; PHYSFS_uint32 __dummy_##name;
  48. #define ISOBB16bit(name) PHYSFS_uint16 name; PHYSFS_uint16 __dummy_##name;
  49. #else
  50. #define ISOBB32bit(name) PHYSFS_uint32 __dummy_##name; PHYSFS_uint32 name;
  51. #define ISOBB16bit(name) PHYSFS_uint16 __dummy_##name; PHYSFS_uint16 name;
  52. #endif
  53. typedef struct
  54. {
  55. char year[4];
  56. char month[2];
  57. char day[2];
  58. char hour[2];
  59. char minute[2];
  60. char second[2];
  61. char centisec[2];
  62. PHYSFS_sint8 offset; /* in 15min from GMT */
  63. } ISO9660VolumeTimestamp;
  64. typedef struct
  65. {
  66. PHYSFS_uint8 year;
  67. PHYSFS_uint8 month;
  68. PHYSFS_uint8 day;
  69. PHYSFS_uint8 hour;
  70. PHYSFS_uint8 minute;
  71. PHYSFS_uint8 second;
  72. PHYSFS_sint8 offset;
  73. } ISO9660FileTimestamp;
  74. typedef struct
  75. {
  76. unsigned existence:1;
  77. unsigned directory:1;
  78. unsigned associated_file:1;
  79. unsigned record:1;
  80. unsigned protection:1;
  81. unsigned reserved:2;
  82. unsigned multiextent:1;
  83. } ISO9660FileFlags;
  84. typedef struct
  85. {
  86. PHYSFS_uint8 length;
  87. PHYSFS_uint8 attribute_length;
  88. ISOBB32bit(extent_location)
  89. ISOBB32bit(data_length)
  90. ISO9660FileTimestamp timestamp;
  91. ISO9660FileFlags file_flags;
  92. PHYSFS_uint8 file_unit_size;
  93. PHYSFS_uint8 gap_size;
  94. ISOBB16bit(vol_seq_no)
  95. PHYSFS_uint8 len_fi;
  96. char unused;
  97. } ISO9660RootDirectoryRecord;
  98. /* this structure is combined for all Volume descriptor types */
  99. typedef struct
  100. {
  101. PHYSFS_uint8 type;
  102. char identifier[5];
  103. PHYSFS_uint8 version;
  104. PHYSFS_uint8 flags;
  105. char system_identifier[32];
  106. char volume_identifier[32];
  107. char unused2[8];
  108. ISOBB32bit(space_size)
  109. PHYSFS_uint8 escape_sequences[32];
  110. ISOBB16bit(vol_set_size)
  111. ISOBB16bit(vol_seq_no)
  112. ISOBB16bit(block_size)
  113. ISOBB32bit(path_table_size)
  114. /* PHYSFS_uint32 path_table_start_lsb; // why didn't they use both byte type?
  115. PHYSFS_uint32 opt_path_table_start_lsb;
  116. PHYSFS_uint32 path_table_start_msb;
  117. PHYSFS_uint32 opt_path_table_start_msb;*/
  118. #if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN
  119. PHYSFS_uint32 path_table_start;
  120. PHYSFS_uint32 opt_path_table_start;
  121. PHYSFS_uint32 unused6;
  122. PHYSFS_uint32 unused7;
  123. #else
  124. PHYSFS_uint32 unused6;
  125. PHYSFS_uint32 unused7;
  126. PHYSFS_uint32 path_table_start;
  127. PHYSFS_uint32 opt_path_table_start;
  128. #endif
  129. ISO9660RootDirectoryRecord rootdirectory;
  130. char set_identifier[128];
  131. char publisher_identifier[128];
  132. char preparer_identifer[128];
  133. char application_identifier[128];
  134. char copyright_file_identifier[37];
  135. char abstract_file_identifier[37];
  136. char bibliographic_file_identifier[37];
  137. ISO9660VolumeTimestamp creation_timestamp;
  138. ISO9660VolumeTimestamp modification_timestamp;
  139. ISO9660VolumeTimestamp expiration_timestamp;
  140. ISO9660VolumeTimestamp effective_timestamp;
  141. PHYSFS_uint8 file_structure_version;
  142. char unused4;
  143. char application_use[512];
  144. char unused5[653];
  145. } ISO9660VolumeDescriptor;
  146. typedef struct
  147. {
  148. PHYSFS_uint8 recordlen;
  149. PHYSFS_uint8 extattributelen;
  150. ISOBB32bit(extentpos)
  151. ISOBB32bit(datalen)
  152. ISO9660FileTimestamp recordtime;
  153. ISO9660FileFlags flags;
  154. PHYSFS_uint8 file_unit_size;
  155. PHYSFS_uint8 interleave_gap;
  156. ISOBB16bit(volseqno)
  157. PHYSFS_uint8 filenamelen;
  158. char filename[222]; /* This is not exact, but makes reading easier */
  159. } ISO9660FileDescriptor;
  160. typedef struct
  161. {
  162. ISOBB16bit(owner)
  163. ISOBB16bit(group)
  164. PHYSFS_uint16 flags; /* not implemented*/
  165. ISO9660VolumeTimestamp create_time; /* yes, not file timestamp */
  166. ISO9660VolumeTimestamp mod_time;
  167. ISO9660VolumeTimestamp expire_time;
  168. ISO9660VolumeTimestamp effective_time;
  169. PHYSFS_uint8 record_format;
  170. PHYSFS_uint8 record_attributes;
  171. ISOBB16bit(record_len)
  172. char system_identifier[32];
  173. char system_use[64];
  174. PHYSFS_uint8 version;
  175. ISOBB16bit(escape_len)
  176. char reserved[64];
  177. /** further fields not implemented */
  178. } ISO9660ExtAttributeRec;
  179. #pragma pack(pop) /* restore original alignment from stack */
  180. typedef struct
  181. {
  182. PHYSFS_Io *io;
  183. PHYSFS_uint32 rootdirstart;
  184. PHYSFS_uint32 rootdirsize;
  185. PHYSFS_uint64 currpos;
  186. int isjoliet;
  187. char *path;
  188. void *mutex;
  189. } ISO9660Handle;
  190. typedef struct __ISO9660FileHandle
  191. {
  192. PHYSFS_sint64 filesize;
  193. PHYSFS_uint64 currpos;
  194. PHYSFS_uint64 startblock;
  195. ISO9660Handle *isohandle;
  196. PHYSFS_uint32 (*read) (struct __ISO9660FileHandle *filehandle, void *buffer,
  197. PHYSFS_uint64 len);
  198. int (*seek)(struct __ISO9660FileHandle *filehandle, PHYSFS_sint64 offset);
  199. void (*close)(struct __ISO9660FileHandle *filehandle);
  200. /* !!! FIXME: anonymouse union is going to cause problems. */
  201. union
  202. {
  203. /* !!! FIXME: just use a memory PHYSFS_Io here, unify all this code. */
  204. char *cacheddata; /* data of file when cached */
  205. PHYSFS_Io *io; /* handle to separate opened file */
  206. };
  207. } ISO9660FileHandle;
  208. /*******************************************************************************
  209. * Time conversion functions
  210. ******************************************************************************/
  211. static PHYSFS_sint64 iso_mktime(ISO9660FileTimestamp *timestamp)
  212. {
  213. struct tm tm;
  214. tm.tm_year = timestamp->year;
  215. tm.tm_mon = timestamp->month - 1;
  216. tm.tm_mday = timestamp->day;
  217. tm.tm_hour = timestamp->hour;
  218. tm.tm_min = timestamp->minute;
  219. tm.tm_sec = timestamp->second;
  220. /* Ignore GMT offset for now... */
  221. return mktime(&tm);
  222. } /* iso_mktime */
  223. static int iso_atoi2(char *text)
  224. {
  225. return ((text[0] - 40) * 10) + (text[1] - 40);
  226. } /* iso_atoi2 */
  227. static int iso_atoi4(char *text)
  228. {
  229. return ((text[0] - 40) * 1000) + ((text[1] - 40) * 100) +
  230. ((text[2] - 40) * 10) + (text[3] - 40);
  231. } /* iso_atoi4 */
  232. static PHYSFS_sint64 iso_volume_mktime(ISO9660VolumeTimestamp *timestamp)
  233. {
  234. struct tm tm;
  235. tm.tm_year = iso_atoi4(timestamp->year);
  236. tm.tm_mon = iso_atoi2(timestamp->month) - 1;
  237. tm.tm_mday = iso_atoi2(timestamp->day);
  238. tm.tm_hour = iso_atoi2(timestamp->hour);
  239. tm.tm_min = iso_atoi2(timestamp->minute);
  240. tm.tm_sec = iso_atoi2(timestamp->second);
  241. /* this allows values outside the range of a unix timestamp... sanitize them */
  242. PHYSFS_sint64 value = mktime(&tm);
  243. return value == -1 ? 0 : value;
  244. } /* iso_volume_mktime */
  245. /*******************************************************************************
  246. * Filename extraction
  247. ******************************************************************************/
  248. static int iso_extractfilenameISO(ISO9660FileDescriptor *descriptor,
  249. char *filename, int *version)
  250. {
  251. *filename = '\0';
  252. if (descriptor->flags.directory)
  253. {
  254. strncpy(filename, descriptor->filename, descriptor->filenamelen);
  255. filename[descriptor->filenamelen] = '\0';
  256. *version = 0;
  257. } /* if */
  258. else
  259. {
  260. /* find last SEPARATOR2 */
  261. int pos = 0;
  262. int lastfound = -1;
  263. for(;pos < descriptor->filenamelen; pos++)
  264. if (descriptor->filename[pos] == ';')
  265. lastfound = pos;
  266. BAIL_IF_MACRO(lastfound < 1, ERR_BAD_FILENAME, -1);
  267. BAIL_IF_MACRO(lastfound == (descriptor->filenamelen -1), ERR_BAD_FILENAME, -1);
  268. strncpy(filename, descriptor->filename, lastfound);
  269. if (filename[lastfound - 1] == '.')
  270. filename[lastfound - 1] = '\0'; /* consume trailing ., as done in all implementations */
  271. else
  272. filename[lastfound] = '\0';
  273. *version = atoi(descriptor->filename + lastfound);
  274. } /* else */
  275. return 0;
  276. } /* iso_extractfilenameISO */
  277. static int iso_extractfilenameUCS2(ISO9660FileDescriptor *descriptor,
  278. char *filename, int *version)
  279. {
  280. PHYSFS_uint16 tmp[128];
  281. PHYSFS_uint16 *src;
  282. int len;
  283. *filename = '\0';
  284. *version = 1; /* Joliet does not have versions.. at least not on my images */
  285. src = (PHYSFS_uint16*) descriptor->filename;
  286. len = descriptor->filenamelen / 2;
  287. tmp[len] = 0;
  288. while(len--)
  289. tmp[len] = PHYSFS_swapUBE16(src[len]);
  290. PHYSFS_utf8FromUcs2(tmp, filename, 255);
  291. return 0;
  292. } /* iso_extractfilenameUCS2 */
  293. static int iso_extractfilename(ISO9660Handle *handle,
  294. ISO9660FileDescriptor *descriptor, char *filename,int *version)
  295. {
  296. if (handle->isjoliet)
  297. return iso_extractfilenameUCS2(descriptor, filename, version);
  298. else
  299. return iso_extractfilenameISO(descriptor, filename, version);
  300. } /* iso_extractfilename */
  301. /*******************************************************************************
  302. * Basic image read functions
  303. ******************************************************************************/
  304. static int iso_readimage(ISO9660Handle *handle, PHYSFS_uint64 where,
  305. void *buffer, PHYSFS_uint64 len)
  306. {
  307. BAIL_IF_MACRO(!__PHYSFS_platformGrabMutex(handle->mutex),
  308. ERR_LOCK_VIOLATION, -1);
  309. int rc = -1;
  310. if (where != handle->currpos)
  311. GOTO_IF_MACRO(!handle->io->seek(handle->io,where), NULL, unlockme);
  312. rc = handle->io->read(handle->io, buffer, len);
  313. if (rc == -1)
  314. {
  315. handle->currpos = (PHYSFS_uint64) -1;
  316. GOTO_MACRO(NULL, unlockme);
  317. } /* if */
  318. handle->currpos += rc;
  319. unlockme:
  320. __PHYSFS_platformReleaseMutex(handle->mutex);
  321. return rc;
  322. } /* iso_readimage */
  323. static PHYSFS_sint64 iso_readfiledescriptor(ISO9660Handle *handle,
  324. PHYSFS_uint64 where,
  325. ISO9660FileDescriptor *descriptor)
  326. {
  327. PHYSFS_sint64 rc = iso_readimage(handle, where, descriptor,
  328. sizeof (descriptor->recordlen));
  329. BAIL_IF_MACRO(rc == -1, NULL, -1);
  330. BAIL_IF_MACRO(rc != 1, ERR_CORRUPTED, -1);
  331. if (descriptor->recordlen == 0)
  332. return 0; /* fill bytes at the end of a sector */
  333. rc = iso_readimage(handle, where + 1, &descriptor->extattributelen,
  334. descriptor->recordlen - sizeof(descriptor->recordlen));
  335. BAIL_IF_MACRO(rc == -1, NULL, -1);
  336. BAIL_IF_MACRO(rc != 1, ERR_CORRUPTED, -1);
  337. return 0;
  338. } /* iso_readfiledescriptor */
  339. static void iso_extractsubpath(char *path, char **subpath)
  340. {
  341. *subpath = strchr(path,'/');
  342. if (*subpath != 0)
  343. {
  344. **subpath = 0;
  345. *subpath +=1;
  346. } /* if */
  347. } /* iso_extractsubpath */
  348. /*
  349. * Don't use path tables, they are not necessarily faster, but more complicated
  350. * to implement as they store only directories and not files, so searching for
  351. * a file needs to branch to the directory extent sooner or later.
  352. */
  353. static int iso_find_dir_entry(ISO9660Handle *handle,const char *path,
  354. ISO9660FileDescriptor *descriptor, int *exists)
  355. {
  356. char *subpath = 0;
  357. PHYSFS_uint64 readpos, end_of_dir;
  358. char filename[255];
  359. char pathcopy[256];
  360. char *mypath;
  361. int version = 0;
  362. strcpy(pathcopy, path);
  363. mypath = pathcopy;
  364. *exists = 0;
  365. readpos = handle->rootdirstart;
  366. end_of_dir = handle->rootdirstart + handle->rootdirsize;
  367. iso_extractsubpath(mypath, &subpath);
  368. while (1)
  369. {
  370. BAIL_IF_MACRO(iso_readfiledescriptor(handle, readpos, descriptor), NULL, -1);
  371. /* recordlen = 0 -> no more entries or fill entry */
  372. if (!descriptor->recordlen)
  373. {
  374. /* if we are in the last sector of the directory & it's 0 -> end */
  375. if ((end_of_dir - 2048) <= (readpos -1))
  376. break; /* finished */
  377. /* else skip to the next sector & continue; */
  378. readpos = (((readpos - 1) / 2048) + 1) * 2048;
  379. continue;
  380. } /* if */
  381. readpos += descriptor->recordlen;
  382. if (descriptor->filenamelen == 1 && (descriptor->filename[0] == 0
  383. || descriptor->filename[0] == 1))
  384. continue; /* special ones, ignore */
  385. BAIL_IF_MACRO(
  386. iso_extractfilename(handle, descriptor, filename, &version),
  387. NULL, -1);
  388. if (strcmp(filename, mypath) == 0)
  389. {
  390. if ( (subpath == 0) || (subpath[0] == 0) )
  391. {
  392. *exists = 1;
  393. return 0; /* no subpaths left and we found the entry */
  394. } /* if */
  395. if (descriptor->flags.directory)
  396. {
  397. /* shorten the path to the subpath */
  398. mypath = subpath;
  399. iso_extractsubpath(mypath, &subpath);
  400. /* gosub to the new directory extent */
  401. readpos = descriptor->extentpos * 2048;
  402. end_of_dir = readpos + descriptor->datalen;
  403. } /* if */
  404. else
  405. {
  406. /* we're at a file but have a remaining subpath -> no match */
  407. return 0;
  408. } /* else */
  409. } /* if */
  410. } /* while */
  411. return 0;
  412. } /* iso_find_dir_entry */
  413. static int iso_read_ext_attributes(ISO9660Handle *handle, int block,
  414. ISO9660ExtAttributeRec *attributes)
  415. {
  416. return iso_readimage(handle, block * 2048, attributes,
  417. sizeof(ISO9660ExtAttributeRec));
  418. } /* iso_read_ext_attributes */
  419. static int ISO9660_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
  420. static PHYSFS_Io *ISO9660_duplicate(PHYSFS_Io *_io)
  421. {
  422. BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); /* !!! FIXME: write me. */
  423. } /* ISO9660_duplicate */
  424. static void ISO9660_destroy(PHYSFS_Io *io)
  425. {
  426. ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque;
  427. fhandle->close(fhandle);
  428. allocator.Free(io);
  429. } /* ISO9660_destroy */
  430. static PHYSFS_sint64 ISO9660_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
  431. {
  432. ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque;
  433. return fhandle->read(fhandle, buf, len);
  434. } /* ISO9660_read */
  435. static PHYSFS_sint64 ISO9660_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 l)
  436. {
  437. BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
  438. } /* ISO9660_write */
  439. static PHYSFS_sint64 ISO9660_tell(PHYSFS_Io *io)
  440. {
  441. return ((ISO9660FileHandle*) io->opaque)->currpos;
  442. } /* ISO9660_tell */
  443. static int ISO9660_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
  444. {
  445. ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque;
  446. return fhandle->seek(fhandle, offset);
  447. } /* ISO9660_seek */
  448. static PHYSFS_sint64 ISO9660_length(PHYSFS_Io *io)
  449. {
  450. return ((ISO9660FileHandle*) io->opaque)->filesize;
  451. } /* ISO9660_length */
  452. static const PHYSFS_Io ISO9660_Io =
  453. {
  454. ISO9660_read,
  455. ISO9660_write,
  456. ISO9660_seek,
  457. ISO9660_tell,
  458. ISO9660_length,
  459. ISO9660_duplicate,
  460. ISO9660_flush,
  461. ISO9660_destroy,
  462. NULL
  463. };
  464. /*******************************************************************************
  465. * Archive management functions
  466. ******************************************************************************/
  467. static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, int forWriting)
  468. {
  469. char magicnumber[6];
  470. ISO9660Handle *handle;
  471. int founddescriptor = 0;
  472. int foundjoliet = 0;
  473. assert(io != NULL); /* shouldn't ever happen. */
  474. BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
  475. /* Skip system area to magic number in Volume descriptor */
  476. BAIL_IF_MACRO(!io->seek(io, 32769), NULL, NULL);
  477. BAIL_IF_MACRO(!io->read(io, magicnumber, 5) != 5, NULL, NULL);
  478. if (memcmp(magicnumber, "CD001", 6) != 0)
  479. BAIL_MACRO(ERR_NOT_AN_ARCHIVE, NULL);
  480. handle = allocator.Malloc(sizeof(ISO9660Handle));
  481. GOTO_IF_MACRO(!handle, ERR_OUT_OF_MEMORY, errorcleanup);
  482. handle->path = 0;
  483. handle->mutex= 0;
  484. handle->io = NULL;
  485. handle->path = allocator.Malloc(strlen(filename) + 1);
  486. GOTO_IF_MACRO(!handle->path, ERR_OUT_OF_MEMORY, errorcleanup);
  487. strcpy(handle->path, filename);
  488. handle->mutex = __PHYSFS_platformCreateMutex();
  489. GOTO_IF_MACRO(!handle->mutex, "Cannot create Mutex", errorcleanup);
  490. handle->io = io;
  491. /* seek Primary Volume Descriptor */
  492. GOTO_IF_MACRO(!io->seek(io, 32768), ERR_SEEK_ERROR, errorcleanup);
  493. while (1)
  494. {
  495. ISO9660VolumeDescriptor descriptor;
  496. GOTO_IF_MACRO(io->read(io, &descriptor, sizeof(ISO9660VolumeDescriptor)) != sizeof(ISO9660VolumeDescriptor), "Cannot read from image", errorcleanup);
  497. GOTO_IF_MACRO(strncmp(descriptor.identifier, "CD001", 5) != 0, ERR_NOT_AN_ARCHIVE, errorcleanup);
  498. if (descriptor.type == 255)
  499. {
  500. /* type 255 terminates the volume descriptor list */
  501. if (founddescriptor)
  502. return handle; /* ok, we've found one volume descriptor */
  503. else
  504. GOTO_MACRO(ERR_CORRUPTED, errorcleanup);
  505. } /* if */
  506. if (descriptor.type == 1 && !founddescriptor)
  507. {
  508. handle->currpos = io->tell(io);
  509. handle->rootdirstart =
  510. descriptor.rootdirectory.extent_location * 2048;
  511. handle->rootdirsize =
  512. descriptor.rootdirectory.data_length;
  513. handle->isjoliet = 0;
  514. founddescriptor = 1; /* continue search for joliet */
  515. } /* if */
  516. if (descriptor.type == 2 && !foundjoliet)
  517. {
  518. /* check if is joliet */
  519. PHYSFS_uint8 *s = descriptor.escape_sequences;
  520. int joliet = !(descriptor.flags & 1)
  521. && (s[0] == 0x25)
  522. && (s[1] == 0x2F)
  523. && ((s[2] == 0x40) || (s[2] == 0x43) || (s[2] == 0x45));
  524. if (!joliet)
  525. continue;
  526. handle->currpos = io->tell(io);
  527. handle->rootdirstart =
  528. descriptor.rootdirectory.extent_location * 2048;
  529. handle->rootdirsize =
  530. descriptor.rootdirectory.data_length;
  531. handle->isjoliet = 1;
  532. founddescriptor = 1;
  533. foundjoliet = 1;
  534. } /* if */
  535. } /* while */
  536. GOTO_MACRO(ERR_CORRUPTED, errorcleanup); /* not found. */
  537. errorcleanup:
  538. if (handle)
  539. {
  540. if (handle->path)
  541. allocator.Free(handle->path);
  542. if (handle->mutex)
  543. __PHYSFS_platformDestroyMutex(handle->mutex);
  544. allocator.Free(handle);
  545. } /* if */
  546. return NULL;
  547. } /* ISO9660_openArchive */
  548. static void ISO9660_dirClose(dvoid *opaque)
  549. {
  550. ISO9660Handle *handle = (ISO9660Handle*) opaque;
  551. handle->io->destroy(handle->io);
  552. __PHYSFS_platformDestroyMutex(handle->mutex);
  553. allocator.Free(handle->path);
  554. allocator.Free(handle);
  555. } /* ISO9660_dirClose */
  556. /*******************************************************************************
  557. * Read functions
  558. ******************************************************************************/
  559. static PHYSFS_uint32 iso_file_read_mem(ISO9660FileHandle *filehandle,
  560. void *buffer, PHYSFS_uint64 len)
  561. {
  562. /* check remaining bytes & max obj which can be fetched */
  563. const PHYSFS_sint64 bytesleft = filehandle->filesize - filehandle->currpos;
  564. if (bytesleft < len)
  565. len = bytesleft;
  566. if (len == 0)
  567. return 0;
  568. memcpy(buffer, filehandle->cacheddata + filehandle->currpos, (size_t) len);
  569. filehandle->currpos += len;
  570. return (PHYSFS_uint32) len;
  571. } /* iso_file_read_mem */
  572. static int iso_file_seek_mem(ISO9660FileHandle *fhandle, PHYSFS_sint64 offset)
  573. {
  574. BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
  575. BAIL_IF_MACRO(offset >= fhandle->filesize, ERR_PAST_EOF, 0);
  576. fhandle->currpos = offset;
  577. return 0;
  578. } /* iso_file_seek_mem */
  579. static void iso_file_close_mem(ISO9660FileHandle *fhandle)
  580. {
  581. allocator.Free(fhandle->cacheddata);
  582. allocator.Free(fhandle);
  583. } /* iso_file_close_mem */
  584. static PHYSFS_uint32 iso_file_read_foreign(ISO9660FileHandle *filehandle,
  585. void *buffer, PHYSFS_uint64 len)
  586. {
  587. /* check remaining bytes & max obj which can be fetched */
  588. const PHYSFS_sint64 bytesleft = filehandle->filesize - filehandle->currpos;
  589. if (bytesleft < len)
  590. len = bytesleft;
  591. const PHYSFS_sint64 rc = filehandle->io->read(filehandle->io, buffer, len);
  592. BAIL_IF_MACRO(rc == -1, NULL, -1);
  593. filehandle->currpos += rc; /* i trust my internal book keeping */
  594. BAIL_IF_MACRO(rc < len, ERR_CORRUPTED, -1);
  595. return rc;
  596. } /* iso_file_read_foreign */
  597. static int iso_file_seek_foreign(ISO9660FileHandle *fhandle,
  598. PHYSFS_sint64 offset)
  599. {
  600. BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
  601. BAIL_IF_MACRO(offset >= fhandle->filesize, ERR_PAST_EOF, 0);
  602. PHYSFS_sint64 pos = fhandle->startblock * 2048 + offset;
  603. BAIL_IF_MACRO(!fhandle->io->seek(fhandle->io, pos), NULL, -1);
  604. fhandle->currpos = offset;
  605. return 0;
  606. } /* iso_file_seek_foreign */
  607. static void iso_file_close_foreign(ISO9660FileHandle *fhandle)
  608. {
  609. fhandle->io->destroy(fhandle->io);
  610. allocator.Free(fhandle);
  611. } /* iso_file_close_foreign */
  612. static int iso_file_open_mem(ISO9660Handle *handle, ISO9660FileHandle *fhandle)
  613. {
  614. fhandle->cacheddata = allocator.Malloc(fhandle->filesize);
  615. BAIL_IF_MACRO(!fhandle->cacheddata, ERR_OUT_OF_MEMORY, -1);
  616. int rc = iso_readimage(handle, fhandle->startblock * 2048,
  617. fhandle->cacheddata, fhandle->filesize);
  618. GOTO_IF_MACRO(rc < 0, NULL, freemem);
  619. GOTO_IF_MACRO(rc == 0, ERR_CORRUPTED, freemem);
  620. fhandle->read = iso_file_read_mem;
  621. fhandle->seek = iso_file_seek_mem;
  622. fhandle->close = iso_file_close_mem;
  623. return 0;
  624. freemem:
  625. allocator.Free(fhandle->cacheddata);
  626. return -1;
  627. } /* iso_file_open_mem */
  628. static int iso_file_open_foreign(ISO9660Handle *handle,
  629. ISO9660FileHandle *fhandle)
  630. {
  631. int rc;
  632. fhandle->io = __PHYSFS_createNativeIo(handle->path, 'r');
  633. BAIL_IF_MACRO(!fhandle->io, NULL, -1);
  634. rc = fhandle->io->seek(fhandle->io, fhandle->startblock * 2048);
  635. GOTO_IF_MACRO(!rc, NULL, closefile);
  636. fhandle->read = iso_file_read_foreign;
  637. fhandle->seek = iso_file_seek_foreign;
  638. fhandle->close = iso_file_close_foreign;
  639. return 0;
  640. closefile:
  641. fhandle->io->destroy(fhandle->io);
  642. return -1;
  643. } /* iso_file_open_foreign */
  644. static PHYSFS_Io *ISO9660_openRead(dvoid *opaque, const char *filename, int *exists)
  645. {
  646. PHYSFS_Io *retval = NULL;
  647. ISO9660Handle *handle = (ISO9660Handle*) opaque;
  648. ISO9660FileHandle *fhandle;
  649. ISO9660FileDescriptor descriptor;
  650. int rc;
  651. fhandle = allocator.Malloc(sizeof(ISO9660FileHandle));
  652. BAIL_IF_MACRO(fhandle == 0, ERR_OUT_OF_MEMORY, NULL);
  653. fhandle->cacheddata = 0;
  654. retval = allocator.Malloc(sizeof(PHYSFS_Io));
  655. GOTO_IF_MACRO(retval == 0, ERR_OUT_OF_MEMORY, errorhandling);
  656. /* find file descriptor */
  657. rc = iso_find_dir_entry(handle, filename, &descriptor, exists);
  658. GOTO_IF_MACRO(rc, NULL, errorhandling);
  659. GOTO_IF_MACRO(!*exists, ERR_NO_SUCH_FILE, errorhandling);
  660. fhandle->startblock = descriptor.extentpos + descriptor.extattributelen;
  661. fhandle->filesize = descriptor.datalen;
  662. fhandle->currpos = 0;
  663. fhandle->isohandle = handle;
  664. fhandle->cacheddata = NULL;
  665. fhandle->io = NULL;
  666. if (descriptor.datalen <= ISO9660_FULLCACHEMAXSIZE)
  667. rc = iso_file_open_mem(handle, fhandle);
  668. else
  669. rc = iso_file_open_foreign(handle, fhandle);
  670. GOTO_IF_MACRO(rc, NULL, errorhandling);
  671. memcpy(retval, &ISO9660_Io, sizeof (PHYSFS_Io));
  672. retval->opaque = fhandle;
  673. return retval;
  674. errorhandling:
  675. if (retval) allocator.Free(retval);
  676. if (fhandle) allocator.Free(fhandle);
  677. return NULL;
  678. } /* ISO9660_openRead */
  679. /*******************************************************************************
  680. * Information gathering functions
  681. ******************************************************************************/
  682. static void ISO9660_enumerateFiles(dvoid *opaque, const char *dname,
  683. int omitSymLinks,
  684. PHYSFS_EnumFilesCallback cb,
  685. const char *origdir, void *callbackdata)
  686. {
  687. ISO9660Handle *handle = (ISO9660Handle*) opaque;
  688. ISO9660FileDescriptor descriptor;
  689. PHYSFS_uint64 readpos;
  690. PHYSFS_uint64 end_of_dir;
  691. char filename[130]; /* ISO allows 31, Joliet 128 -> 128 + 2 eol bytes */
  692. int version = 0;
  693. if (*dname == '\0')
  694. {
  695. readpos = handle->rootdirstart;
  696. end_of_dir = readpos + handle->rootdirsize;
  697. } /* if */
  698. else
  699. {
  700. printf("pfad %s\n",dname);
  701. int exists = 0;
  702. BAIL_IF_MACRO(iso_find_dir_entry(handle,dname, &descriptor, &exists), NULL,);
  703. BAIL_IF_MACRO(exists == 0, NULL, );
  704. BAIL_IF_MACRO(!descriptor.flags.directory, NULL,);
  705. readpos = descriptor.extentpos * 2048;
  706. end_of_dir = readpos + descriptor.datalen;
  707. } /* else */
  708. while (1)
  709. {
  710. BAIL_IF_MACRO(iso_readfiledescriptor(handle, readpos, &descriptor), NULL, );
  711. /* recordlen = 0 -> no more entries or fill entry */
  712. if (!descriptor.recordlen)
  713. {
  714. /* if we are in the last sector of the directory & it's 0 -> end */
  715. if ((end_of_dir - 2048) <= (readpos -1))
  716. break; /* finished */
  717. /* else skip to the next sector & continue; */
  718. readpos = (((readpos - 1) / 2048) + 1) * 2048;
  719. continue;
  720. } /* if */
  721. readpos += descriptor.recordlen;
  722. if (descriptor.filenamelen == 1 && (descriptor.filename[0] == 0
  723. || descriptor.filename[0] == 1))
  724. continue; /* special ones, ignore */
  725. strncpy(filename,descriptor.filename,descriptor.filenamelen);
  726. iso_extractfilename(handle, &descriptor, filename, &version);
  727. cb(callbackdata, origdir,filename);
  728. } /* while */
  729. } /* ISO9660_enumerateFiles */
  730. static int ISO9660_stat(dvoid *opaque, const char *name, int *exists,
  731. PHYSFS_Stat *stat)
  732. {
  733. ISO9660Handle *handle = (ISO9660Handle*) opaque;
  734. ISO9660FileDescriptor descriptor;
  735. ISO9660ExtAttributeRec extattr;
  736. BAIL_IF_MACRO(iso_find_dir_entry(handle, name, &descriptor, exists), NULL, -1);
  737. if (!*exists)
  738. return 0;
  739. stat->readonly = 1;
  740. /* try to get extended info */
  741. if (descriptor.extattributelen)
  742. {
  743. BAIL_IF_MACRO(iso_read_ext_attributes(handle,
  744. descriptor.extentpos, &extattr), NULL, -1);
  745. stat->createtime = iso_volume_mktime(&extattr.create_time);
  746. stat->modtime = iso_volume_mktime(&extattr.mod_time);
  747. stat->accesstime = iso_volume_mktime(&extattr.mod_time);
  748. } /* if */
  749. else
  750. {
  751. stat->createtime = iso_mktime(&descriptor.recordtime);
  752. stat->modtime = iso_mktime(&descriptor.recordtime);
  753. stat->accesstime = iso_mktime(&descriptor.recordtime);
  754. } /* else */
  755. if (descriptor.flags.directory)
  756. {
  757. stat->filesize = 0;
  758. stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
  759. } /* if */
  760. else
  761. {
  762. stat->filesize = descriptor.datalen;
  763. stat->filetype = PHYSFS_FILETYPE_REGULAR;
  764. } /* else */
  765. return 1;
  766. } /* ISO9660_stat */
  767. /*******************************************************************************
  768. * Not supported functions
  769. ******************************************************************************/
  770. static PHYSFS_Io *ISO9660_openWrite(dvoid *opaque, const char *name)
  771. {
  772. BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
  773. } /* ISO9660_openWrite */
  774. static PHYSFS_Io *ISO9660_openAppend(dvoid *opaque, const char *name)
  775. {
  776. BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
  777. } /* ISO9660_openAppend */
  778. static int ISO9660_remove(dvoid *opaque, const char *name)
  779. {
  780. BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
  781. } /* ISO9660_remove */
  782. static int ISO9660_mkdir(dvoid *opaque, const char *name)
  783. {
  784. BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
  785. } /* ISO9660_mkdir */
  786. const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ISO9660 =
  787. {
  788. "ISO",
  789. "ISO9660 image file",
  790. "Christoph Nelles <evilazrael@evilazrael.de>",
  791. "http://www.evilazrael.de",
  792. };
  793. const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 =
  794. {
  795. &__PHYSFS_ArchiveInfo_ISO9660,
  796. ISO9660_openArchive, /* openArchive() method */
  797. ISO9660_enumerateFiles, /* enumerateFiles() method */
  798. ISO9660_openRead, /* openRead() method */
  799. ISO9660_openWrite, /* openWrite() method */
  800. ISO9660_openAppend, /* openAppend() method */
  801. ISO9660_remove, /* remove() method */
  802. ISO9660_mkdir, /* mkdir() method */
  803. ISO9660_dirClose, /* dirClose() method */
  804. ISO9660_stat /* stat() method */
  805. };
  806. #endif /* defined PHYSFS_SUPPORTS_ISO9660 */
  807. /* end of archiver_iso9660.c ... */