misc/libphysfs/archiver_iso9660.c
changeset 13886 99b265e0d1d0
parent 13885 5f819b90d479
child 13887 b172a5d40eee
equal deleted inserted replaced
13885:5f819b90d479 13886:99b265e0d1d0
     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