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