Search
lxdream.org :: lxdream/src/drivers/cdrom/isoread.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cdrom/isoread.c
changeset 1099:566cdeb157ec
author nkeynes
date Wed Feb 10 18:16:19 2010 +1000 (10 years ago)
permissions -rw-r--r--
last change First draft of basic ISO9660 filesystem reader
file annotate diff log raw
nkeynes@1099
     1
/**
nkeynes@1099
     2
 * $Id$
nkeynes@1099
     3
 *
nkeynes@1099
     4
 * ISO9660 filesystem reading support
nkeynes@1099
     5
 *
nkeynes@1099
     6
 * Copyright (c) 2010 Nathan Keynes.
nkeynes@1099
     7
 *
nkeynes@1099
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@1099
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@1099
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@1099
    11
 * (at your option) any later version.
nkeynes@1099
    12
 *
nkeynes@1099
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@1099
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@1099
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@1099
    16
 * GNU General Public License for more details.
nkeynes@1099
    17
 */
nkeynes@1099
    18
nkeynes@1099
    19
#include "drivers/cdrom/cdrom.h"
nkeynes@1099
    20
#include "drivers/cdrom/isoread.h"
nkeynes@1099
    21
#include "drivers/cdrom/iso_impl.h"
nkeynes@1099
    22
nkeynes@1099
    23
#include <string.h>
nkeynes@1099
    24
#include <errno.h>
nkeynes@1099
    25
nkeynes@1099
    26
static char isofs_magic[5] = { 'C', 'D', '0', '0', '1' };
nkeynes@1099
    27
nkeynes@1099
    28
#define ISO_DIR_TAG 0x52494449
nkeynes@1099
    29
nkeynes@1099
    30
struct isofs_reader_dir {
nkeynes@1099
    31
    uint32_t tag;
nkeynes@1099
    32
    isofs_reader_dir_t parent;
nkeynes@1099
    33
    size_t num_entries;
nkeynes@1099
    34
    struct isofs_reader_dirent entries[];
nkeynes@1099
    35
};
nkeynes@1099
    36
nkeynes@1099
    37
struct isofs_reader {
nkeynes@1099
    38
    /**
nkeynes@1099
    39
     * Base sector source to read the filesystem from (must support Mode 1 reads)
nkeynes@1099
    40
     */
nkeynes@1099
    41
    sector_source_t source;
nkeynes@1099
    42
nkeynes@1099
    43
    /**
nkeynes@1099
    44
     * Offset of the source relative to the start of the (notional) disc -
nkeynes@1099
    45
     * this is subtracted from all source lba addresses.
nkeynes@1099
    46
     */
nkeynes@1099
    47
    cdrom_lba_t source_offset;
nkeynes@1099
    48
nkeynes@1099
    49
    /**
nkeynes@1099
    50
     * Start of the ISO9660 filesystem relative to the start of the disc.
nkeynes@1099
    51
     * (The actual superblock is at fs_start+16)
nkeynes@1099
    52
     */
nkeynes@1099
    53
    cdrom_lba_t fs_start;
nkeynes@1099
    54
nkeynes@1099
    55
    /**
nkeynes@1099
    56
     * If TRUE, read the little-endian side of the FS, otherwise the big-endian
nkeynes@1099
    57
     * side. (They should normally give the same result, but in case it matters...)
nkeynes@1099
    58
     */
nkeynes@1099
    59
    gboolean little_endian;
nkeynes@1099
    60
nkeynes@1099
    61
    /**
nkeynes@1099
    62
     * The volume sequence number, for multi-volume sets.
nkeynes@1099
    63
     */
nkeynes@1099
    64
    uint16_t volume_seq_no;
nkeynes@1099
    65
nkeynes@1099
    66
    char volume_label[33];
nkeynes@1099
    67
nkeynes@1099
    68
    /**
nkeynes@1099
    69
     * Filesystem root directory
nkeynes@1099
    70
     */
nkeynes@1099
    71
    isofs_reader_dir_t root_dir;
nkeynes@1099
    72
};
nkeynes@1099
    73
nkeynes@1099
    74
/**
nkeynes@1099
    75
 * Read a 16-bit dual-endian field using the defined endianness of the reader
nkeynes@1099
    76
 */
nkeynes@1099
    77
#define ISO_GET_DE16( iso, field ) \
nkeynes@1099
    78
    ( ((iso)->little_endian) ? GINT16_FROM_LE(field) : GINT16_FROM_BE(*((&field)+1)) )
nkeynes@1099
    79
nkeynes@1099
    80
/**
nkeynes@1099
    81
 * Read a 32-bit dual-endian field using the defined endianness of the reader
nkeynes@1099
    82
 */
nkeynes@1099
    83
#define ISO_GET_DE32( iso, field ) \
nkeynes@1099
    84
    ( ((iso)->little_endian) ? GINT32_FROM_LE(field) : GINT32_FROM_BE(*((&field)+1)) )
nkeynes@1099
    85
nkeynes@1099
    86
nkeynes@1099
    87
static void isofs_reader_convert_dirent( isofs_reader_t iso, isofs_reader_dirent_t dest, iso_dirent_t src,
nkeynes@1099
    88
                                         char **strp )
nkeynes@1099
    89
{
nkeynes@1099
    90
    dest->start_lba = ISO_GET_DE32(iso, src->file_lba_le);
nkeynes@1099
    91
    dest->size = ISO_GET_DE32(iso, src->file_size_le);
nkeynes@1099
    92
    dest->is_dir = (src->flags & ISO_FILE_DIR) ? TRUE : FALSE;
nkeynes@1099
    93
    dest->interleave_gap = src->gap_size;
nkeynes@1099
    94
    dest->interleave_size = src->unit_size;
nkeynes@1099
    95
    dest->name = *strp;
nkeynes@1099
    96
    memcpy( *strp, src->file_id, src->file_id_len );
nkeynes@1099
    97
    (*strp)[src->file_id_len] = '\0';
nkeynes@1099
    98
    *strp += src->file_id_len + 1;
nkeynes@1099
    99
}
nkeynes@1099
   100
nkeynes@1099
   101
/**
nkeynes@1099
   102
 * Read a directory from the disc into memory.
nkeynes@1099
   103
 */
nkeynes@1099
   104
isofs_reader_dir_t isofs_reader_read_dir( isofs_reader_t iso, cdrom_lba_t lba, size_t size )
nkeynes@1099
   105
{
nkeynes@1099
   106
    cdrom_count_t count = (size+2047)/2048;
nkeynes@1099
   107
nkeynes@1099
   108
    char buf[count*2048];
nkeynes@1099
   109
nkeynes@1099
   110
    if( isofs_reader_read_sectors( iso, lba, count, buf ) != CDROM_ERROR_OK )
nkeynes@1099
   111
        return NULL;
nkeynes@1099
   112
nkeynes@1099
   113
    size_t len = 0;
nkeynes@1099
   114
    unsigned num_entries = 0, i=0, offset=0;
nkeynes@1099
   115
    /* Compute number of entries and total string length */
nkeynes@1099
   116
    while( offset < size ) {
nkeynes@1099
   117
        struct iso_dirent *p = (struct iso_dirent *)&buf[offset];
nkeynes@1099
   118
        offset += p->record_len;
nkeynes@1099
   119
        if( offset > size || p->record_len < sizeof(struct iso_dirent) )
nkeynes@1099
   120
            break; // Bad record length
nkeynes@1099
   121
        if( p->file_id_len + sizeof(struct iso_dirent)-1 > p->record_len )
nkeynes@1099
   122
            break; // Bad fileid length
nkeynes@1099
   123
        if( p->file_id_len == 1 && (p->file_id[0] == 0 || p->file_id[0] == 1 ) )
nkeynes@1099
   124
            continue; /* self and parent-dir references */
nkeynes@1099
   125
        num_entries++;
nkeynes@1099
   126
        len += p->file_id_len + 1;
nkeynes@1099
   127
    }
nkeynes@1099
   128
nkeynes@1099
   129
    size_t table_len = num_entries * sizeof(struct isofs_reader_dirent);
nkeynes@1099
   130
    isofs_reader_dir_t dir = g_malloc0( sizeof(struct isofs_reader_dir) + table_len + len );
nkeynes@1099
   131
    dir->tag = ISO_DIR_TAG;
nkeynes@1099
   132
    dir->num_entries = num_entries;
nkeynes@1099
   133
nkeynes@1099
   134
    char *strp = (char *)&dir->entries[num_entries];
nkeynes@1099
   135
    offset = 0;
nkeynes@1099
   136
    for( i=0; i < num_entries; i++ ) {
nkeynes@1099
   137
        struct iso_dirent *p;
nkeynes@1099
   138
        do {
nkeynes@1099
   139
            p = (struct iso_dirent *)&buf[offset];
nkeynes@1099
   140
            offset += p->record_len;
nkeynes@1099
   141
            /* Skip over self and parent-dir references */
nkeynes@1099
   142
        } while( p->file_id_len == 1 && (p->file_id[0] == 0 || p->file_id[0] == 1 ) );
nkeynes@1099
   143
nkeynes@1099
   144
        isofs_reader_convert_dirent( iso, &dir->entries[i], p, &strp );
nkeynes@1099
   145
nkeynes@1099
   146
    }
nkeynes@1099
   147
    return dir;
nkeynes@1099
   148
}
nkeynes@1099
   149
nkeynes@1099
   150
static gboolean isofs_reader_dirent_match_exact( const char *file, const char *find )
nkeynes@1099
   151
{
nkeynes@1099
   152
    return strcasecmp( file, find ) == 0;
nkeynes@1099
   153
}
nkeynes@1099
   154
nkeynes@1099
   155
static gboolean isofs_reader_dirent_match_unversioned( const char *file, const char *find )
nkeynes@1099
   156
{
nkeynes@1099
   157
    char *semi = strchr(file, ';');
nkeynes@1099
   158
    if( semi == NULL ) {
nkeynes@1099
   159
        /* Unversioned ISO file */
nkeynes@1099
   160
        return strcasecmp( file, find ) == 0;
nkeynes@1099
   161
    } else {
nkeynes@1099
   162
        int len = semi - file;
nkeynes@1099
   163
        return strncasecmp( file, find, len ) == 0 && strlen(find) == len;
nkeynes@1099
   164
    }
nkeynes@1099
   165
}
nkeynes@1099
   166
nkeynes@1099
   167
/**
nkeynes@1099
   168
 * Search a directory for a given filename. If found, return the corresponding
nkeynes@1099
   169
 * dirent structure, otherwise NULL. Comparison is case-insensitive, and returns
nkeynes@1099
   170
 * the most recent (highest numbered) version of a file in case of multiple
nkeynes@1099
   171
 * versions unless the requested component is also explicitly versioned.
nkeynes@1099
   172
 *
nkeynes@1099
   173
 * For now just do a linear search, although we could do a binary search given
nkeynes@1099
   174
 * that the directory should be sorted.
nkeynes@1099
   175
 */
nkeynes@1099
   176
static isofs_reader_dirent_t isofs_reader_get_file_component( isofs_reader_dir_t dir, const char *component )
nkeynes@1099
   177
{
nkeynes@1099
   178
nkeynes@1099
   179
    if( strchr( component, ';' ) != NULL ) {
nkeynes@1099
   180
        for( unsigned i=0; i<dir->num_entries; i++ ) {
nkeynes@1099
   181
            if( isofs_reader_dirent_match_exact(dir->entries[i].name,component) ) {
nkeynes@1099
   182
                return &dir->entries[i];
nkeynes@1099
   183
            }
nkeynes@1099
   184
        }
nkeynes@1099
   185
    } else {
nkeynes@1099
   186
        for( unsigned i=0; i<dir->num_entries; i++ ) {
nkeynes@1099
   187
            if( isofs_reader_dirent_match_unversioned(dir->entries[i].name,component) ) {
nkeynes@1099
   188
                return &dir->entries[i];
nkeynes@1099
   189
            }
nkeynes@1099
   190
        }
nkeynes@1099
   191
    }
nkeynes@1099
   192
    return NULL;
nkeynes@1099
   193
}
nkeynes@1099
   194
nkeynes@1099
   195
isofs_reader_dirent_t isofs_reader_get_file( isofs_reader_t iso, const char *pathname )
nkeynes@1099
   196
{
nkeynes@1099
   197
    int pathlen = strlen(pathname);
nkeynes@1099
   198
    char tmp[pathlen+1];
nkeynes@1099
   199
    char *p = tmp;
nkeynes@1099
   200
    isofs_reader_dir_t dir = iso->root_dir;
nkeynes@1099
   201
nkeynes@1099
   202
    memcpy( tmp, pathname, pathlen+1 );
nkeynes@1099
   203
    char *q = strchr(p, '/');
nkeynes@1099
   204
    while( q != NULL ) {
nkeynes@1099
   205
        *q = '\0';
nkeynes@1099
   206
        isofs_reader_dirent_t ent = isofs_reader_get_file_component( dir, p );
nkeynes@1099
   207
        if( ent == NULL || !ent->is_dir ) {
nkeynes@1099
   208
            return NULL;
nkeynes@1099
   209
        }
nkeynes@1099
   210
        if( ent->subdir == NULL ) {
nkeynes@1099
   211
            ent->subdir = dir = isofs_reader_read_dir( iso, ent->start_lba, ent->size );
nkeynes@1099
   212
            if( dir == NULL ) {
nkeynes@1099
   213
                return NULL;
nkeynes@1099
   214
            }
nkeynes@1099
   215
        }
nkeynes@1099
   216
nkeynes@1099
   217
        p = q+1;
nkeynes@1099
   218
        q = strchr(p, '/');
nkeynes@1099
   219
nkeynes@1099
   220
    }
nkeynes@1099
   221
    return isofs_reader_get_file_component( dir, p );
nkeynes@1099
   222
}
nkeynes@1099
   223
nkeynes@1099
   224
cdrom_error_t isofs_reader_read_file( isofs_reader_t iso, isofs_reader_dirent_t file,
nkeynes@1099
   225
                                      size_t offset, size_t byte_count, unsigned char *buf )
nkeynes@1099
   226
{
nkeynes@1099
   227
    char tmp[2048];
nkeynes@1099
   228
nkeynes@1099
   229
    if( offset + byte_count > file->size )
nkeynes@1099
   230
        return CDROM_ERROR_BADREAD;
nkeynes@1099
   231
nkeynes@1099
   232
    if( file->interleave_gap == 0 ) {
nkeynes@1099
   233
        cdrom_lba_t lba = file->start_lba + (offset>>11);
nkeynes@1099
   234
        lba += ((file->xa_size+2047)>>11); /* Skip XA record if present */
nkeynes@1099
   235
nkeynes@1099
   236
        if( (offset & 2047) != 0 ) {
nkeynes@1099
   237
            /* Read an unaligned start block */
nkeynes@1099
   238
            cdrom_error_t status = isofs_reader_read_sectors( iso, lba, 1, tmp );
nkeynes@1099
   239
            if( status != CDROM_ERROR_OK )
nkeynes@1099
   240
                return status;
nkeynes@1099
   241
            unsigned align = offset & 2047;
nkeynes@1099
   242
            size_t length = 2048 - align;
nkeynes@1099
   243
            if( length >= byte_count ) {
nkeynes@1099
   244
                memcpy( buf, &tmp[align], byte_count );
nkeynes@1099
   245
                return CDROM_ERROR_OK;
nkeynes@1099
   246
            } else {
nkeynes@1099
   247
                memcpy( buf, &tmp[align], length );
nkeynes@1099
   248
                byte_count -= length;
nkeynes@1099
   249
                buf += length;
nkeynes@1099
   250
                lba++;
nkeynes@1099
   251
            }
nkeynes@1099
   252
        }
nkeynes@1099
   253
        /* Read the bulk of the data */
nkeynes@1099
   254
        cdrom_count_t sector_count = byte_count >> 11;
nkeynes@1099
   255
        if( sector_count > 0 ) {
nkeynes@1099
   256
            cdrom_error_t status = isofs_reader_read_sectors( iso, lba, sector_count, buf );
nkeynes@1099
   257
            if( status != CDROM_ERROR_OK )
nkeynes@1099
   258
                return status;
nkeynes@1099
   259
            buf += (sector_count << 11);
nkeynes@1099
   260
            lba += sector_count;
nkeynes@1099
   261
        }
nkeynes@1099
   262
        /* Finally read a partial final block */
nkeynes@1099
   263
        if( (byte_count & 2047) != 0 ) {
nkeynes@1099
   264
            cdrom_error_t status = isofs_reader_read_sectors( iso, lba, 1, tmp );
nkeynes@1099
   265
            if( status != CDROM_ERROR_OK )
nkeynes@1099
   266
                return status;
nkeynes@1099
   267
            memcpy( buf, tmp, byte_count & 2047 );
nkeynes@1099
   268
        }
nkeynes@1099
   269
        return CDROM_ERROR_OK;
nkeynes@1099
   270
    } else {
nkeynes@1099
   271
        // ERROR("Interleaved files not supported");
nkeynes@1099
   272
        return CDROM_ERROR_BADREAD;
nkeynes@1099
   273
    }
nkeynes@1099
   274
}
nkeynes@1099
   275
nkeynes@1099
   276
void isofs_reader_destroy_dir( isofs_reader_dir_t dir )
nkeynes@1099
   277
{
nkeynes@1099
   278
    dir->tag = 0;
nkeynes@1099
   279
    for( unsigned i=0; i<dir->num_entries; i++ ) {
nkeynes@1099
   280
        if( dir->entries[i].subdir != NULL ) {
nkeynes@1099
   281
            isofs_reader_dir_t subdir = dir->entries[i].subdir;
nkeynes@1099
   282
            dir->entries[i].subdir = NULL;
nkeynes@1099
   283
            isofs_reader_destroy_dir(dir);
nkeynes@1099
   284
        }
nkeynes@1099
   285
    }
nkeynes@1099
   286
    g_free(dir);
nkeynes@1099
   287
}
nkeynes@1099
   288
nkeynes@1099
   289
cdrom_error_t isofs_reader_read_sectors( isofs_reader_t iso, cdrom_lba_t lba, cdrom_count_t count,
nkeynes@1099
   290
                                         unsigned char *buf )
nkeynes@1099
   291
{
nkeynes@1099
   292
    if( lba < iso->source_offset )
nkeynes@1099
   293
        return CDROM_ERROR_BADREAD;
nkeynes@1099
   294
    return sector_source_read_sectors( iso->source, lba - iso->source_offset, count,
nkeynes@1099
   295
           CDROM_READ_MODE2_FORM1|CDROM_READ_DATA, buf, NULL );
nkeynes@1099
   296
}
nkeynes@1099
   297
nkeynes@1099
   298
nkeynes@1099
   299
isofs_reader_t isofs_reader_new( sector_source_t source, cdrom_lba_t offset, cdrom_lba_t start, ERROR *err )
nkeynes@1099
   300
{
nkeynes@1099
   301
    char buf[2048];
nkeynes@1099
   302
    iso_pvd_t pvd = (iso_pvd_t)&buf;
nkeynes@1099
   303
    unsigned i = 0;
nkeynes@1099
   304
nkeynes@1099
   305
    isofs_reader_t iso = g_malloc0( sizeof(struct isofs_reader) );
nkeynes@1099
   306
    if( iso == NULL ) {
nkeynes@1099
   307
        SET_ERROR( err, ENOMEM, "Unable to allocate memory" );
nkeynes@1099
   308
        return NULL;
nkeynes@1099
   309
    }
nkeynes@1099
   310
    iso->source = source;
nkeynes@1099
   311
    iso->source_offset = offset;
nkeynes@1099
   312
    iso->fs_start = start;
nkeynes@1099
   313
    iso->little_endian = TRUE;
nkeynes@1099
   314
nkeynes@1099
   315
    do {
nkeynes@1099
   316
        /* Find the primary volume descriptor */
nkeynes@1099
   317
        cdrom_error_t status = isofs_reader_read_sectors( iso, iso->fs_start + ISO_SUPERBLOCK_OFFSET + i, 1, buf );
nkeynes@1099
   318
        if( status != CDROM_ERROR_OK ) {
nkeynes@1099
   319
            SET_ERROR( err, EBADF, "Unable to read superblock from ISO9660 filesystem" );
nkeynes@1099
   320
            g_free(iso);
nkeynes@1099
   321
            return NULL;
nkeynes@1099
   322
        }
nkeynes@1099
   323
        if( memcmp(pvd->tag, isofs_magic, 5) != 0 || /* Not an ISO volume descriptor */
nkeynes@1099
   324
                pvd->desc_type == ISO_TERMINAL_DESCRIPTOR ) { /* Reached the end of the descriptor list */
nkeynes@1099
   325
            SET_ERROR( err, EINVAL, "ISO9660 filesystem not found" );
nkeynes@1099
   326
            g_free(iso);
nkeynes@1099
   327
            return NULL;
nkeynes@1099
   328
        }
nkeynes@1099
   329
        i++;
nkeynes@1099
   330
    } while( pvd->desc_type != ISO_PRIMARY_DESCRIPTOR );
nkeynes@1099
   331
nkeynes@1099
   332
    if( pvd->desc_version != 1 ) {
nkeynes@1099
   333
        SET_ERROR( err, EINVAL, "Incompatible ISO9660 filesystem" );
nkeynes@1099
   334
        g_free(iso);
nkeynes@1099
   335
        return NULL;
nkeynes@1099
   336
    }
nkeynes@1099
   337
nkeynes@1099
   338
    iso->volume_seq_no = ISO_GET_DE16(iso, pvd->volume_seq_le);
nkeynes@1099
   339
    memcpy( iso->volume_label, pvd->volume_id, 32 );
nkeynes@1099
   340
    for( i=32; i>0 && iso->volume_label[i-1] == ' '; i-- );
nkeynes@1099
   341
    iso->volume_label[i] = '\0';
nkeynes@1099
   342
nkeynes@1099
   343
    iso->root_dir = isofs_reader_read_dir( iso,
nkeynes@1099
   344
            ISO_GET_DE32(iso, pvd->root_dirent.file_lba_le),
nkeynes@1099
   345
            ISO_GET_DE32(iso, pvd->root_dirent.file_size_le) );
nkeynes@1099
   346
    if( iso->root_dir == NULL ) {
nkeynes@1099
   347
        SET_ERROR( err, EINVAL, "Unable to read root directory from ISO9660 filesystem" );
nkeynes@1099
   348
        g_free(iso);
nkeynes@1099
   349
        return NULL;
nkeynes@1099
   350
    }
nkeynes@1099
   351
nkeynes@1099
   352
    sector_source_ref( source );
nkeynes@1099
   353
    return iso;
nkeynes@1099
   354
}
nkeynes@1099
   355
nkeynes@1099
   356
isofs_reader_t isofs_reader_new_from_disc( cdrom_disc_t disc, cdrom_lba_t lba, ERROR *err )
nkeynes@1099
   357
{
nkeynes@1099
   358
    return isofs_reader_new( &disc->source, 0, lba, err );
nkeynes@1099
   359
}
nkeynes@1099
   360
nkeynes@1099
   361
isofs_reader_t isofs_reader_new_from_track( cdrom_disc_t disc, cdrom_track_t track, ERROR *err )
nkeynes@1099
   362
{
nkeynes@1099
   363
    return isofs_reader_new( &disc->source, 0, track->lba, err );
nkeynes@1099
   364
}
nkeynes@1099
   365
nkeynes@1099
   366
isofs_reader_t isofs_reader_new_from_source( sector_source_t source, ERROR *err )
nkeynes@1099
   367
{
nkeynes@1099
   368
    return isofs_reader_new( source, 0, 0, err );
nkeynes@1099
   369
}
nkeynes@1099
   370
nkeynes@1099
   371
void isofs_reader_destroy( isofs_reader_t iso )
nkeynes@1099
   372
{
nkeynes@1099
   373
    isofs_reader_destroy_dir( iso->root_dir );
nkeynes@1099
   374
    iso->root_dir = NULL;
nkeynes@1099
   375
    sector_source_unref( iso->source );
nkeynes@1099
   376
    iso->source = NULL;
nkeynes@1099
   377
    g_free( iso );
nkeynes@1099
   378
}
nkeynes@1099
   379
nkeynes@1099
   380
isofs_reader_dir_t isofs_reader_get_root_dir( isofs_reader_t iso )
nkeynes@1099
   381
{
nkeynes@1099
   382
    return iso->root_dir;
nkeynes@1099
   383
}
nkeynes@1099
   384
nkeynes@1099
   385
void isofs_reader_print_dir( FILE *f, isofs_reader_dir_t dir )
nkeynes@1099
   386
{
nkeynes@1099
   387
    fprintf( f, "Total %d files\n", dir->num_entries );
nkeynes@1099
   388
    for( unsigned i=0; i<dir->num_entries; i++ ) {
nkeynes@1099
   389
        fprintf( f, "%7d %s\n", dir->entries[i].size, dir->entries[i].name );
nkeynes@1099
   390
    }
nkeynes@1099
   391
nkeynes@1099
   392
}
.