misc/libphysfs/src/archiver_iso9660.c
branchphysfslayer
changeset 8524 a65e9bcf0a03
parent 8522 1853628ae285
child 8526 9f2bd885d773
equal deleted inserted replaced
8522:1853628ae285 8524:a65e9bcf0a03
     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