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_NOT_FOUND /* !!! FIXME: PHYSFS_ERR_BAD_FILENAME */, -1); |
|
295 BAIL_IF_MACRO(lastfound == (descriptor->filenamelen -1), PHYSFS_ERR_NOT_FOUND /* !!! 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) |
|
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 |
|
413 readpos = handle->rootdirstart; |
|
414 end_of_dir = handle->rootdirstart + handle->rootdirsize; |
|
415 iso_extractsubpath(mypath, &subpath); |
|
416 while (1) |
|
417 { |
|
418 BAIL_IF_MACRO(iso_readfiledescriptor(handle, readpos, descriptor), ERRPASS, -1); |
|
419 |
|
420 /* recordlen = 0 -> no more entries or fill entry */ |
|
421 if (!descriptor->recordlen) |
|
422 { |
|
423 /* if we are in the last sector of the directory & it's 0 -> end */ |
|
424 if ((end_of_dir - 2048) <= (readpos -1)) |
|
425 break; /* finished */ |
|
426 |
|
427 /* else skip to the next sector & continue; */ |
|
428 readpos = (((readpos - 1) / 2048) + 1) * 2048; |
|
429 continue; |
|
430 } /* if */ |
|
431 |
|
432 readpos += descriptor->recordlen; |
|
433 if (descriptor->filenamelen == 1 && (descriptor->filename[0] == 0 |
|
434 || descriptor->filename[0] == 1)) |
|
435 continue; /* special ones, ignore */ |
|
436 |
|
437 BAIL_IF_MACRO( |
|
438 iso_extractfilename(handle, descriptor, filename, &version), |
|
439 ERRPASS, -1); |
|
440 |
|
441 if (strcmp(filename, mypath) == 0) |
|
442 { |
|
443 if ( (subpath == 0) || (subpath[0] == 0) ) |
|
444 return 0; /* no subpaths left and we found the entry */ |
|
445 |
|
446 if (descriptor->flags.directory) |
|
447 { |
|
448 /* shorten the path to the subpath */ |
|
449 mypath = subpath; |
|
450 iso_extractsubpath(mypath, &subpath); |
|
451 /* gosub to the new directory extent */ |
|
452 readpos = descriptor->extentpos * 2048; |
|
453 end_of_dir = readpos + descriptor->datalen; |
|
454 } /* if */ |
|
455 else |
|
456 { |
|
457 /* !!! FIXME: set PHYSFS_ERR_NOT_FOUND? */ |
|
458 /* we're at a file but have a remaining subpath -> no match */ |
|
459 return 0; |
|
460 } /* else */ |
|
461 } /* if */ |
|
462 } /* while */ |
|
463 |
|
464 /* !!! FIXME: set PHYSFS_ERR_NOT_FOUND? */ |
|
465 return 0; |
|
466 } /* iso_find_dir_entry */ |
|
467 |
|
468 |
|
469 static int iso_read_ext_attributes(ISO9660Handle *handle, int block, |
|
470 ISO9660ExtAttributeRec *attributes) |
|
471 { |
|
472 return iso_readimage(handle, block * 2048, attributes, |
|
473 sizeof(ISO9660ExtAttributeRec)); |
|
474 } /* iso_read_ext_attributes */ |
|
475 |
|
476 |
|
477 static int ISO9660_flush(PHYSFS_Io *io) { return 1; /* no write support. */ } |
|
478 |
|
479 static PHYSFS_Io *ISO9660_duplicate(PHYSFS_Io *_io) |
|
480 { |
|
481 BAIL_MACRO(PHYSFS_ERR_UNSUPPORTED, NULL); /* !!! FIXME: write me. */ |
|
482 } /* ISO9660_duplicate */ |
|
483 |
|
484 |
|
485 static void ISO9660_destroy(PHYSFS_Io *io) |
|
486 { |
|
487 ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque; |
|
488 fhandle->close(fhandle); |
|
489 allocator.Free(io); |
|
490 } /* ISO9660_destroy */ |
|
491 |
|
492 |
|
493 static PHYSFS_sint64 ISO9660_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len) |
|
494 { |
|
495 ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque; |
|
496 return fhandle->read(fhandle, buf, len); |
|
497 } /* ISO9660_read */ |
|
498 |
|
499 |
|
500 static PHYSFS_sint64 ISO9660_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 l) |
|
501 { |
|
502 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1); |
|
503 } /* ISO9660_write */ |
|
504 |
|
505 |
|
506 static PHYSFS_sint64 ISO9660_tell(PHYSFS_Io *io) |
|
507 { |
|
508 return ((ISO9660FileHandle*) io->opaque)->currpos; |
|
509 } /* ISO9660_tell */ |
|
510 |
|
511 |
|
512 static int ISO9660_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) |
|
513 { |
|
514 ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque; |
|
515 return fhandle->seek(fhandle, offset); |
|
516 } /* ISO9660_seek */ |
|
517 |
|
518 |
|
519 static PHYSFS_sint64 ISO9660_length(PHYSFS_Io *io) |
|
520 { |
|
521 return ((ISO9660FileHandle*) io->opaque)->filesize; |
|
522 } /* ISO9660_length */ |
|
523 |
|
524 |
|
525 static const PHYSFS_Io ISO9660_Io = |
|
526 { |
|
527 CURRENT_PHYSFS_IO_API_VERSION, NULL, |
|
528 ISO9660_read, |
|
529 ISO9660_write, |
|
530 ISO9660_seek, |
|
531 ISO9660_tell, |
|
532 ISO9660_length, |
|
533 ISO9660_duplicate, |
|
534 ISO9660_flush, |
|
535 ISO9660_destroy |
|
536 }; |
|
537 |
|
538 |
|
539 /******************************************************************************* |
|
540 * Archive management functions |
|
541 ******************************************************************************/ |
|
542 |
|
543 static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, int forWriting) |
|
544 { |
|
545 char magicnumber[6]; |
|
546 ISO9660Handle *handle; |
|
547 int founddescriptor = 0; |
|
548 int foundjoliet = 0; |
|
549 |
|
550 assert(io != NULL); /* shouldn't ever happen. */ |
|
551 |
|
552 BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL); |
|
553 |
|
554 /* Skip system area to magic number in Volume descriptor */ |
|
555 BAIL_IF_MACRO(!io->seek(io, 32769), ERRPASS, NULL); |
|
556 BAIL_IF_MACRO(io->read(io, magicnumber, 5) != 5, ERRPASS, NULL); |
|
557 if (memcmp(magicnumber, "CD001", 6) != 0) |
|
558 BAIL_MACRO(PHYSFS_ERR_UNSUPPORTED, NULL); |
|
559 |
|
560 handle = allocator.Malloc(sizeof(ISO9660Handle)); |
|
561 GOTO_IF_MACRO(!handle, PHYSFS_ERR_OUT_OF_MEMORY, errorcleanup); |
|
562 handle->path = 0; |
|
563 handle->mutex= 0; |
|
564 handle->io = NULL; |
|
565 |
|
566 handle->path = allocator.Malloc(strlen(filename) + 1); |
|
567 GOTO_IF_MACRO(!handle->path, PHYSFS_ERR_OUT_OF_MEMORY, errorcleanup); |
|
568 strcpy(handle->path, filename); |
|
569 |
|
570 handle->mutex = __PHYSFS_platformCreateMutex(); |
|
571 GOTO_IF_MACRO(!handle->mutex, ERRPASS, errorcleanup); |
|
572 |
|
573 handle->io = io; |
|
574 |
|
575 /* seek Primary Volume Descriptor */ |
|
576 GOTO_IF_MACRO(!io->seek(io, 32768), PHYSFS_ERR_IO, errorcleanup); |
|
577 |
|
578 while (1) |
|
579 { |
|
580 ISO9660VolumeDescriptor descriptor; |
|
581 GOTO_IF_MACRO(io->read(io, &descriptor, sizeof(ISO9660VolumeDescriptor)) != sizeof(ISO9660VolumeDescriptor), PHYSFS_ERR_IO, errorcleanup); |
|
582 GOTO_IF_MACRO(strncmp(descriptor.identifier, "CD001", 5) != 0, PHYSFS_ERR_UNSUPPORTED, errorcleanup); |
|
583 |
|
584 if (descriptor.type == 255) |
|
585 { |
|
586 /* type 255 terminates the volume descriptor list */ |
|
587 if (founddescriptor) |
|
588 return handle; /* ok, we've found one volume descriptor */ |
|
589 else |
|
590 GOTO_MACRO(PHYSFS_ERR_CORRUPT, errorcleanup); |
|
591 } /* if */ |
|
592 if (descriptor.type == 1 && !founddescriptor) |
|
593 { |
|
594 handle->currpos = io->tell(io); |
|
595 handle->rootdirstart = |
|
596 descriptor.rootdirectory.extent_location * 2048; |
|
597 handle->rootdirsize = |
|
598 descriptor.rootdirectory.data_length; |
|
599 handle->isjoliet = 0; |
|
600 founddescriptor = 1; /* continue search for joliet */ |
|
601 } /* if */ |
|
602 if (descriptor.type == 2 && !foundjoliet) |
|
603 { |
|
604 /* check if is joliet */ |
|
605 PHYSFS_uint8 *s = descriptor.escape_sequences; |
|
606 int joliet = !(descriptor.flags & 1) |
|
607 && (s[0] == 0x25) |
|
608 && (s[1] == 0x2F) |
|
609 && ((s[2] == 0x40) || (s[2] == 0x43) || (s[2] == 0x45)); |
|
610 if (!joliet) |
|
611 continue; |
|
612 |
|
613 handle->currpos = io->tell(io); |
|
614 handle->rootdirstart = |
|
615 descriptor.rootdirectory.extent_location * 2048; |
|
616 handle->rootdirsize = |
|
617 descriptor.rootdirectory.data_length; |
|
618 handle->isjoliet = 1; |
|
619 founddescriptor = 1; |
|
620 foundjoliet = 1; |
|
621 } /* if */ |
|
622 } /* while */ |
|
623 |
|
624 GOTO_MACRO(PHYSFS_ERR_CORRUPT, errorcleanup); /* not found. */ |
|
625 |
|
626 errorcleanup: |
|
627 if (handle) |
|
628 { |
|
629 if (handle->path) |
|
630 allocator.Free(handle->path); |
|
631 if (handle->mutex) |
|
632 __PHYSFS_platformDestroyMutex(handle->mutex); |
|
633 allocator.Free(handle); |
|
634 } /* if */ |
|
635 return NULL; |
|
636 } /* ISO9660_openArchive */ |
|
637 |
|
638 |
|
639 static void ISO9660_closeArchive(void *opaque) |
|
640 { |
|
641 ISO9660Handle *handle = (ISO9660Handle*) opaque; |
|
642 handle->io->destroy(handle->io); |
|
643 __PHYSFS_platformDestroyMutex(handle->mutex); |
|
644 allocator.Free(handle->path); |
|
645 allocator.Free(handle); |
|
646 } /* ISO9660_closeArchive */ |
|
647 |
|
648 |
|
649 /******************************************************************************* |
|
650 * Read functions |
|
651 ******************************************************************************/ |
|
652 |
|
653 |
|
654 static PHYSFS_uint32 iso_file_read_mem(ISO9660FileHandle *filehandle, |
|
655 void *buffer, PHYSFS_uint64 len) |
|
656 { |
|
657 /* check remaining bytes & max obj which can be fetched */ |
|
658 const PHYSFS_sint64 bytesleft = filehandle->filesize - filehandle->currpos; |
|
659 if (bytesleft < len) |
|
660 len = bytesleft; |
|
661 |
|
662 if (len == 0) |
|
663 return 0; |
|
664 |
|
665 memcpy(buffer, filehandle->cacheddata + filehandle->currpos, (size_t) len); |
|
666 |
|
667 filehandle->currpos += len; |
|
668 return (PHYSFS_uint32) len; |
|
669 } /* iso_file_read_mem */ |
|
670 |
|
671 |
|
672 static int iso_file_seek_mem(ISO9660FileHandle *fhandle, PHYSFS_sint64 offset) |
|
673 { |
|
674 BAIL_IF_MACRO(offset < 0, PHYSFS_ERR_INVALID_ARGUMENT, 0); |
|
675 BAIL_IF_MACRO(offset >= fhandle->filesize, PHYSFS_ERR_PAST_EOF, 0); |
|
676 |
|
677 fhandle->currpos = offset; |
|
678 return 0; |
|
679 } /* iso_file_seek_mem */ |
|
680 |
|
681 |
|
682 static void iso_file_close_mem(ISO9660FileHandle *fhandle) |
|
683 { |
|
684 allocator.Free(fhandle->cacheddata); |
|
685 allocator.Free(fhandle); |
|
686 } /* iso_file_close_mem */ |
|
687 |
|
688 |
|
689 static PHYSFS_uint32 iso_file_read_foreign(ISO9660FileHandle *filehandle, |
|
690 void *buffer, PHYSFS_uint64 len) |
|
691 { |
|
692 /* check remaining bytes & max obj which can be fetched */ |
|
693 const PHYSFS_sint64 bytesleft = filehandle->filesize - filehandle->currpos; |
|
694 if (bytesleft < len) |
|
695 len = bytesleft; |
|
696 |
|
697 const PHYSFS_sint64 rc = filehandle->io->read(filehandle->io, buffer, len); |
|
698 BAIL_IF_MACRO(rc == -1, ERRPASS, -1); |
|
699 |
|
700 filehandle->currpos += rc; /* i trust my internal book keeping */ |
|
701 BAIL_IF_MACRO(rc < len, PHYSFS_ERR_CORRUPT, -1); |
|
702 return rc; |
|
703 } /* iso_file_read_foreign */ |
|
704 |
|
705 |
|
706 static int iso_file_seek_foreign(ISO9660FileHandle *fhandle, |
|
707 PHYSFS_sint64 offset) |
|
708 { |
|
709 BAIL_IF_MACRO(offset < 0, PHYSFS_ERR_INVALID_ARGUMENT, 0); |
|
710 BAIL_IF_MACRO(offset >= fhandle->filesize, PHYSFS_ERR_PAST_EOF, 0); |
|
711 |
|
712 PHYSFS_sint64 pos = fhandle->startblock * 2048 + offset; |
|
713 BAIL_IF_MACRO(!fhandle->io->seek(fhandle->io, pos), ERRPASS, -1); |
|
714 |
|
715 fhandle->currpos = offset; |
|
716 return 0; |
|
717 } /* iso_file_seek_foreign */ |
|
718 |
|
719 |
|
720 static void iso_file_close_foreign(ISO9660FileHandle *fhandle) |
|
721 { |
|
722 fhandle->io->destroy(fhandle->io); |
|
723 allocator.Free(fhandle); |
|
724 } /* iso_file_close_foreign */ |
|
725 |
|
726 |
|
727 static int iso_file_open_mem(ISO9660Handle *handle, ISO9660FileHandle *fhandle) |
|
728 { |
|
729 fhandle->cacheddata = allocator.Malloc(fhandle->filesize); |
|
730 BAIL_IF_MACRO(!fhandle->cacheddata, PHYSFS_ERR_OUT_OF_MEMORY, -1); |
|
731 int rc = iso_readimage(handle, fhandle->startblock * 2048, |
|
732 fhandle->cacheddata, fhandle->filesize); |
|
733 GOTO_IF_MACRO(rc < 0, ERRPASS, freemem); |
|
734 GOTO_IF_MACRO(rc == 0, PHYSFS_ERR_CORRUPT, freemem); |
|
735 |
|
736 fhandle->read = iso_file_read_mem; |
|
737 fhandle->seek = iso_file_seek_mem; |
|
738 fhandle->close = iso_file_close_mem; |
|
739 return 0; |
|
740 |
|
741 freemem: |
|
742 allocator.Free(fhandle->cacheddata); |
|
743 return -1; |
|
744 } /* iso_file_open_mem */ |
|
745 |
|
746 |
|
747 static int iso_file_open_foreign(ISO9660Handle *handle, |
|
748 ISO9660FileHandle *fhandle) |
|
749 { |
|
750 int rc; |
|
751 fhandle->io = __PHYSFS_createNativeIo(handle->path, 'r'); |
|
752 BAIL_IF_MACRO(!fhandle->io, ERRPASS, -1); |
|
753 rc = fhandle->io->seek(fhandle->io, fhandle->startblock * 2048); |
|
754 GOTO_IF_MACRO(!rc, ERRPASS, closefile); |
|
755 |
|
756 fhandle->read = iso_file_read_foreign; |
|
757 fhandle->seek = iso_file_seek_foreign; |
|
758 fhandle->close = iso_file_close_foreign; |
|
759 return 0; |
|
760 |
|
761 closefile: |
|
762 fhandle->io->destroy(fhandle->io); |
|
763 return -1; |
|
764 } /* iso_file_open_foreign */ |
|
765 |
|
766 |
|
767 static PHYSFS_Io *ISO9660_openRead(void *opaque, const char *filename) |
|
768 { |
|
769 PHYSFS_Io *retval = NULL; |
|
770 ISO9660Handle *handle = (ISO9660Handle*) opaque; |
|
771 ISO9660FileHandle *fhandle; |
|
772 ISO9660FileDescriptor descriptor; |
|
773 int rc; |
|
774 |
|
775 fhandle = allocator.Malloc(sizeof(ISO9660FileHandle)); |
|
776 BAIL_IF_MACRO(fhandle == 0, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
|
777 fhandle->cacheddata = 0; |
|
778 |
|
779 retval = allocator.Malloc(sizeof(PHYSFS_Io)); |
|
780 GOTO_IF_MACRO(retval == 0, PHYSFS_ERR_OUT_OF_MEMORY, errorhandling); |
|
781 |
|
782 /* find file descriptor */ |
|
783 rc = iso_find_dir_entry(handle, filename, &descriptor); |
|
784 GOTO_IF_MACRO(rc, ERRPASS, errorhandling); |
|
785 |
|
786 fhandle->startblock = descriptor.extentpos + descriptor.extattributelen; |
|
787 fhandle->filesize = descriptor.datalen; |
|
788 fhandle->currpos = 0; |
|
789 fhandle->isohandle = handle; |
|
790 fhandle->cacheddata = NULL; |
|
791 fhandle->io = NULL; |
|
792 |
|
793 if (descriptor.datalen <= ISO9660_FULLCACHEMAXSIZE) |
|
794 rc = iso_file_open_mem(handle, fhandle); |
|
795 else |
|
796 rc = iso_file_open_foreign(handle, fhandle); |
|
797 GOTO_IF_MACRO(rc, ERRPASS, errorhandling); |
|
798 |
|
799 memcpy(retval, &ISO9660_Io, sizeof (PHYSFS_Io)); |
|
800 retval->opaque = fhandle; |
|
801 return retval; |
|
802 |
|
803 errorhandling: |
|
804 if (retval) allocator.Free(retval); |
|
805 if (fhandle) allocator.Free(fhandle); |
|
806 return NULL; |
|
807 } /* ISO9660_openRead */ |
|
808 |
|
809 |
|
810 |
|
811 /******************************************************************************* |
|
812 * Information gathering functions |
|
813 ******************************************************************************/ |
|
814 |
|
815 static void ISO9660_enumerateFiles(void *opaque, const char *dname, |
|
816 PHYSFS_EnumFilesCallback cb, |
|
817 const char *origdir, void *callbackdata) |
|
818 { |
|
819 ISO9660Handle *handle = (ISO9660Handle*) opaque; |
|
820 ISO9660FileDescriptor descriptor; |
|
821 PHYSFS_uint64 readpos; |
|
822 PHYSFS_uint64 end_of_dir; |
|
823 char filename[130]; /* ISO allows 31, Joliet 128 -> 128 + 2 eol bytes */ |
|
824 int version = 0; |
|
825 |
|
826 if (*dname == '\0') |
|
827 { |
|
828 readpos = handle->rootdirstart; |
|
829 end_of_dir = readpos + handle->rootdirsize; |
|
830 } /* if */ |
|
831 else |
|
832 { |
|
833 printf("pfad %s\n",dname); |
|
834 BAIL_IF_MACRO(iso_find_dir_entry(handle,dname, &descriptor), ERRPASS,); |
|
835 BAIL_IF_MACRO(!descriptor.flags.directory, ERRPASS,); |
|
836 |
|
837 readpos = descriptor.extentpos * 2048; |
|
838 end_of_dir = readpos + descriptor.datalen; |
|
839 } /* else */ |
|
840 |
|
841 while (1) |
|
842 { |
|
843 BAIL_IF_MACRO(iso_readfiledescriptor(handle, readpos, &descriptor), ERRPASS, ); |
|
844 |
|
845 /* recordlen = 0 -> no more entries or fill entry */ |
|
846 if (!descriptor.recordlen) |
|
847 { |
|
848 /* if we are in the last sector of the directory & it's 0 -> end */ |
|
849 if ((end_of_dir - 2048) <= (readpos -1)) |
|
850 break; /* finished */ |
|
851 |
|
852 /* else skip to the next sector & continue; */ |
|
853 readpos = (((readpos - 1) / 2048) + 1) * 2048; |
|
854 continue; |
|
855 } /* if */ |
|
856 |
|
857 readpos += descriptor.recordlen; |
|
858 if (descriptor.filenamelen == 1 && (descriptor.filename[0] == 0 |
|
859 || descriptor.filename[0] == 1)) |
|
860 continue; /* special ones, ignore */ |
|
861 |
|
862 strncpy(filename,descriptor.filename,descriptor.filenamelen); |
|
863 iso_extractfilename(handle, &descriptor, filename, &version); |
|
864 cb(callbackdata, origdir,filename); |
|
865 } /* while */ |
|
866 } /* ISO9660_enumerateFiles */ |
|
867 |
|
868 |
|
869 static int ISO9660_stat(void *opaque, const char *name, PHYSFS_Stat *stat) |
|
870 { |
|
871 ISO9660Handle *handle = (ISO9660Handle*) opaque; |
|
872 ISO9660FileDescriptor descriptor; |
|
873 ISO9660ExtAttributeRec extattr; |
|
874 BAIL_IF_MACRO(iso_find_dir_entry(handle, name, &descriptor), ERRPASS, -1); |
|
875 |
|
876 stat->readonly = 1; |
|
877 |
|
878 /* try to get extended info */ |
|
879 if (descriptor.extattributelen) |
|
880 { |
|
881 BAIL_IF_MACRO(iso_read_ext_attributes(handle, |
|
882 descriptor.extentpos, &extattr), ERRPASS, -1); |
|
883 stat->createtime = iso_volume_mktime(&extattr.create_time); |
|
884 stat->modtime = iso_volume_mktime(&extattr.mod_time); |
|
885 stat->accesstime = iso_volume_mktime(&extattr.mod_time); |
|
886 } /* if */ |
|
887 else |
|
888 { |
|
889 stat->createtime = iso_mktime(&descriptor.recordtime); |
|
890 stat->modtime = iso_mktime(&descriptor.recordtime); |
|
891 stat->accesstime = iso_mktime(&descriptor.recordtime); |
|
892 } /* else */ |
|
893 |
|
894 if (descriptor.flags.directory) |
|
895 { |
|
896 stat->filesize = 0; |
|
897 stat->filetype = PHYSFS_FILETYPE_DIRECTORY; |
|
898 } /* if */ |
|
899 else |
|
900 { |
|
901 stat->filesize = descriptor.datalen; |
|
902 stat->filetype = PHYSFS_FILETYPE_REGULAR; |
|
903 } /* else */ |
|
904 |
|
905 return 1; |
|
906 } /* ISO9660_stat */ |
|
907 |
|
908 |
|
909 /******************************************************************************* |
|
910 * Not supported functions |
|
911 ******************************************************************************/ |
|
912 |
|
913 static PHYSFS_Io *ISO9660_openWrite(void *opaque, const char *name) |
|
914 { |
|
915 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
|
916 } /* ISO9660_openWrite */ |
|
917 |
|
918 |
|
919 static PHYSFS_Io *ISO9660_openAppend(void *opaque, const char *name) |
|
920 { |
|
921 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
|
922 } /* ISO9660_openAppend */ |
|
923 |
|
924 |
|
925 static int ISO9660_remove(void *opaque, const char *name) |
|
926 { |
|
927 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
|
928 } /* ISO9660_remove */ |
|
929 |
|
930 |
|
931 static int ISO9660_mkdir(void *opaque, const char *name) |
|
932 { |
|
933 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
|
934 } /* ISO9660_mkdir */ |
|
935 |
|
936 |
|
937 const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 = |
|
938 { |
|
939 CURRENT_PHYSFS_ARCHIVER_API_VERSION, |
|
940 { |
|
941 "ISO", |
|
942 "ISO9660 image file", |
|
943 "Christoph Nelles <evilazrael@evilazrael.de>", |
|
944 "https://www.evilazrael.de/", |
|
945 0, /* supportsSymlinks */ |
|
946 }, |
|
947 ISO9660_openArchive, |
|
948 ISO9660_enumerateFiles, |
|
949 ISO9660_openRead, |
|
950 ISO9660_openWrite, |
|
951 ISO9660_openAppend, |
|
952 ISO9660_remove, |
|
953 ISO9660_mkdir, |
|
954 ISO9660_stat, |
|
955 ISO9660_closeArchive |
|
956 }; |
|
957 |
|
958 #endif /* defined PHYSFS_SUPPORTS_ISO9660 */ |
|
959 |
|
960 /* end of archiver_iso9660.c ... */ |
|
961 |
|