Blame contrib/libudfread/src/udfread.c

Packit 5e46da
/*
Packit 5e46da
 * This file is part of libudfread
Packit 5e46da
 * Copyright (C) 2014-2015 VLC authors and VideoLAN
Packit 5e46da
 *
Packit 5e46da
 * Authors: Petri Hintukainen <phintuka@users.sourceforge.net>
Packit 5e46da
 *
Packit 5e46da
 * This library is free software; you can redistribute it and/or
Packit 5e46da
 * modify it under the terms of the GNU Lesser General Public
Packit 5e46da
 * License as published by the Free Software Foundation; either
Packit 5e46da
 * version 2.1 of the License, or (at your option) any later version.
Packit 5e46da
 *
Packit 5e46da
 * This library is distributed in the hope that it will be useful,
Packit 5e46da
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5e46da
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 5e46da
 * Lesser General Public License for more details.
Packit 5e46da
 *
Packit 5e46da
 * You should have received a copy of the GNU Lesser General Public
Packit 5e46da
 * License along with this library. If not, see
Packit 5e46da
 * <http://www.gnu.org/licenses/>.
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if HAVE_CONFIG_H
Packit 5e46da
#include "config.h"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include "udfread.h"
Packit 5e46da
Packit 5e46da
#ifdef HAVE_UDFREAD_VERSION_H
Packit 5e46da
#include "udfread-version.h"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include "blockinput.h"
Packit 5e46da
#include "default_blockinput.h"
Packit 5e46da
#include "ecma167.h"
Packit 5e46da
Packit 5e46da
#include <stdint.h>
Packit 5e46da
#include <stdlib.h>
Packit 5e46da
#include <string.h>
Packit 5e46da
Packit 5e46da
#ifdef _WIN32
Packit 5e46da
#define strtok_r strtok_s
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Logging
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#include <stdio.h>
Packit 5e46da
Packit 5e46da
static int enable_log   = 0;
Packit 5e46da
static int enable_trace = 0;
Packit 5e46da
Packit 5e46da
#define udf_error(...)   do {                   fprintf(stderr, "udfread ERROR: " __VA_ARGS__); } while (0)
Packit 5e46da
#define udf_log(...)     do { if (enable_log)   fprintf(stderr, "udfread LOG  : " __VA_ARGS__); } while (0)
Packit 5e46da
#define udf_trace(...)   do { if (enable_trace) fprintf(stderr, "udfread TRACE: " __VA_ARGS__); } while (0)
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * atomic operations
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || (defined (__clang__) && (defined (__x86_64__) || defined (__i386__)))
Packit 5e46da
Packit 5e46da
#  define atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
Packit 5e46da
    __sync_bool_compare_and_swap((atomic), (oldval), (newval))
Packit 5e46da
Packit 5e46da
#elif defined(_WIN32)
Packit 5e46da
Packit 5e46da
#include <windows.h>
Packit 5e46da
Packit 5e46da
static int atomic_pointer_compare_and_exchange(void *atomic, void *oldval, void *newval)
Packit 5e46da
{
Packit 5e46da
    static int init = 0;
Packit 5e46da
    static CRITICAL_SECTION cs = {0};
Packit 5e46da
    if (!init) {
Packit 5e46da
        init = 1;
Packit 5e46da
        InitializeCriticalSection(&cs);
Packit 5e46da
    }
Packit 5e46da
    int result;
Packit 5e46da
    EnterCriticalSection(&cs);
Packit 5e46da
    result = *(void**)atomic == oldval;
Packit 5e46da
    if (result) {
Packit 5e46da
        *(void**)atomic = newval;
Packit 5e46da
    }
Packit 5e46da
    LeaveCriticalSection(&cs);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
#elif defined(HAVE_PTHREAD_H)
Packit 5e46da
Packit 5e46da
#include <pthread.h>
Packit 5e46da
Packit 5e46da
static int atomic_pointer_compare_and_exchange(void *atomic, void *oldval, void *newval)
Packit 5e46da
{
Packit 5e46da
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
Packit 5e46da
    int result;
Packit 5e46da
    pthread_mutex_lock(&lock);
Packit 5e46da
    result = *(void**)atomic == oldval;
Packit 5e46da
    if (result) {
Packit 5e46da
        *(void**)atomic = newval;
Packit 5e46da
    }
Packit 5e46da
    pthread_mutex_unlock(&lock);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
#else
Packit 5e46da
# error no atomic operation support
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * utils
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static char *_str_dup(const char *s)
Packit 5e46da
{
Packit 5e46da
    size_t len = strlen(s);
Packit 5e46da
    char *p = (char *)malloc(len + 1);
Packit 5e46da
    if (p) {
Packit 5e46da
        memcpy(p, s, len + 1);
Packit 5e46da
    } else {
Packit 5e46da
        udf_error("out of memory\n");
Packit 5e46da
    }
Packit 5e46da
    return p;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void *_safe_realloc(void *p, size_t s)
Packit 5e46da
{
Packit 5e46da
    void *result = realloc(p, s);
Packit 5e46da
    if (!result) {
Packit 5e46da
        udf_error("out of memory\n");
Packit 5e46da
        free(p);
Packit 5e46da
    }
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Decoding
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#define utf16lo_to_utf8(out, out_pos, out_size, ch) \
Packit 5e46da
  do {                                              \
Packit 5e46da
    if (ch < 0x80) {                                \
Packit 5e46da
      out[out_pos++] = (uint8_t)ch;                 \
Packit 5e46da
    } else {                                        \
Packit 5e46da
      out_size++;                                   \
Packit 5e46da
      out = (uint8_t *)_safe_realloc(out, out_size);\
Packit 5e46da
      if (!out) return NULL;                        \
Packit 5e46da
                                                    \
Packit 5e46da
      out[out_pos++] = 0xc0 | (ch >> 6);            \
Packit 5e46da
      out[out_pos++] = 0x80 | (ch & 0x3f);          \
Packit 5e46da
    }                                               \
Packit 5e46da
  } while (0)
Packit 5e46da
Packit 5e46da
#define utf16_to_utf8(out, out_pos, out_size, ch)       \
Packit 5e46da
  do {                                                  \
Packit 5e46da
    if (ch < 0x7ff) {                                   \
Packit 5e46da
      utf16lo_to_utf8(out, out_pos, out_size, ch);      \
Packit 5e46da
    } else {                                            \
Packit 5e46da
      out_size += 2;                                    \
Packit 5e46da
      out = (uint8_t *)_safe_realloc(out, out_size);    \
Packit 5e46da
      if (!out) return NULL;                            \
Packit 5e46da
                                                        \
Packit 5e46da
      out[out_pos++] = 0xe0 | (ch >> 12);               \
Packit 5e46da
      out[out_pos++] = 0x80 | ((ch >> 6) & 0x3f);       \
Packit 5e46da
      out[out_pos++] = 0x80 | (ch & 0x3f);              \
Packit 5e46da
                                                        \
Packit 5e46da
    }                                                   \
Packit 5e46da
  } while (0)
Packit 5e46da
Packit 5e46da
/* Strings, CS0 (UDF 2.1.1) */
Packit 5e46da
static char *_cs0_to_utf8(const uint8_t *cs0, size_t size)
Packit 5e46da
{
Packit 5e46da
    size_t   out_pos = 0;
Packit 5e46da
    size_t   out_size = size;
Packit 5e46da
    size_t   i;
Packit 5e46da
    uint8_t *out;
Packit 5e46da
Packit 5e46da
    if (size < 1) {
Packit 5e46da
        /* empty string */
Packit 5e46da
        return calloc(1, 1);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    out = (uint8_t *)malloc(size);
Packit 5e46da
    if (!out) {
Packit 5e46da
        udf_error("out of memory\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    switch (cs0[0]) {
Packit 5e46da
    case 8:
Packit 5e46da
        /*udf_trace("string in utf-8\n");*/
Packit 5e46da
        for (i = 1; i < size; i++) {
Packit 5e46da
            utf16lo_to_utf8(out, out_pos, out_size, cs0[i]);
Packit 5e46da
        }
Packit 5e46da
        break;
Packit 5e46da
    case 16:
Packit 5e46da
        for (i = 1; i < size - 1; i+=2) {
Packit 5e46da
            uint16_t ch = cs0[i + 1] | (cs0[i] << 8);
Packit 5e46da
            utf16_to_utf8(out, out_pos, out_size, ch);
Packit 5e46da
        }
Packit 5e46da
        break;
Packit 5e46da
    default:
Packit 5e46da
        udf_error("unregonized string encoding %u\n", cs0[0]);
Packit 5e46da
        free(out);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    out[out_pos] = 0;
Packit 5e46da
    return (char*)out;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/* Domain Identifiers, UDF 2.1.5.2 */
Packit 5e46da
Packit 5e46da
static const char lvd_domain_id[]  = "*OSTA UDF Compliant";
Packit 5e46da
static const char meta_domain_id[] = "*UDF Metadata Partition";
Packit 5e46da
Packit 5e46da
static int _check_domain_identifier(const struct entity_id *eid, const char *value)
Packit 5e46da
{
Packit 5e46da
    return (!memcmp(value, eid->identifier, strlen(value))) ? 0 : -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* Additional File Types (UDF 2.60, 2.3.5.2) */
Packit 5e46da
Packit 5e46da
enum udf_file_type {
Packit 5e46da
    UDF_FT_METADATA        = 250,
Packit 5e46da
    UDF_FT_METADATA_MIRROR = 251,
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Block access
Packit 5e46da
 *
Packit 5e46da
 * read block(s) from absolute lba
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static uint32_t _read_blocks(udfread_block_input *input,
Packit 5e46da
                             uint32_t lba, void *buf, uint32_t nblocks,
Packit 5e46da
                             int flags)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (!input || (int)nblocks < 1) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = input->read(input, lba, buf, nblocks, flags);
Packit 5e46da
Packit 5e46da
    return result < 0 ? 0 : (uint32_t)result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _read_descriptor_block(udfread_block_input *input, uint32_t lba, uint8_t *buf)
Packit 5e46da
{
Packit 5e46da
    if (_read_blocks(input, lba, buf, 1, 0) == 1) {
Packit 5e46da
        return decode_descriptor_tag(buf);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Disc probing
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _probe_volume(udfread_block_input *input)
Packit 5e46da
{
Packit 5e46da
    /* Volume Recognition (ECMA 167 2/8, UDF 2.60 2.1.7) */
Packit 5e46da
Packit 5e46da
    static const char bea[]    = {'\0',  'B',  'E',  'A',  '0',  '1', '\1'};
Packit 5e46da
    static const char nsr_02[] = {'\0',  'N',  'S',  'R',  '0',  '2', '\1'};
Packit 5e46da
    static const char nsr_03[] = {'\0',  'N',  'S',  'R',  '0',  '3', '\1'};
Packit 5e46da
    static const char tea[]    = {'\0',  'T',  'E',  'A',  '0',  '1', '\1'};
Packit 5e46da
    static const char nul[]    = {'\0', '\0', '\0', '\0', '\0', '\0', '\0'};
Packit 5e46da
Packit 5e46da
    uint8_t  buf[UDF_BLOCK_SIZE];
Packit 5e46da
    uint32_t lba;
Packit 5e46da
    int      bea_seen = 0;
Packit 5e46da
Packit 5e46da
    for (lba = 16; lba < 256; lba++) {
Packit 5e46da
        if (_read_blocks(input, lba, buf, 1, 0) == 1) {
Packit 5e46da
Packit 5e46da
            /* Terminating Extended Area Descriptor */
Packit 5e46da
            if (!memcmp(buf, tea, sizeof(tea))) {
Packit 5e46da
                udf_error("ECMA 167 Volume Recognition failed (no NSR descriptor)\n");
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
            if (!memcmp(buf, nul, sizeof(nul))) {
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
            if (!memcmp(buf, bea, sizeof(bea))) {
Packit 5e46da
                udf_trace("ECMA 167 Volume, BEA01\n");
Packit 5e46da
                bea_seen = 1;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (bea_seen) {
Packit 5e46da
                if (!memcmp(buf, nsr_02, sizeof(nsr_02))) {
Packit 5e46da
                    udf_trace("ECMA 167 Volume, NSR02\n");
Packit 5e46da
                    return 0;
Packit 5e46da
                }
Packit 5e46da
                if (!memcmp(buf, nsr_03, sizeof(nsr_03))) {
Packit 5e46da
                    udf_trace("ECMA 167 Volume, NSR03\n");
Packit 5e46da
                    return 0;
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    udf_error("ECMA 167 Volume Recognition failed\n");
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _read_avdp(udfread_block_input *input, struct anchor_volume_descriptor *avdp)
Packit 5e46da
{
Packit 5e46da
    uint8_t  buf[UDF_BLOCK_SIZE];
Packit 5e46da
    int      tag_id;
Packit 5e46da
    uint32_t lba = 256;
Packit 5e46da
Packit 5e46da
    /*
Packit 5e46da
     * Find Anchor Volume Descriptor Pointer.
Packit 5e46da
     * It is in block 256, last block or (last block - 256)
Packit 5e46da
     * (UDF 2.60, 2.2.3)
Packit 5e46da
     */
Packit 5e46da
Packit 5e46da
    /* try block 256 */
Packit 5e46da
    tag_id = _read_descriptor_block(input, lba, buf);
Packit 5e46da
    if (tag_id != ECMA_AnchorVolumeDescriptorPointer) {
Packit 5e46da
Packit 5e46da
        /* try last block */
Packit 5e46da
        if (!input->size) {
Packit 5e46da
            udf_error("Can't find Anchor Volume Descriptor Pointer\n");
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        lba = input->size(input) - 1;
Packit 5e46da
        tag_id = _read_descriptor_block(input, lba, buf);
Packit 5e46da
        if (tag_id != ECMA_AnchorVolumeDescriptorPointer) {
Packit 5e46da
Packit 5e46da
            /* try last block - 256 */
Packit 5e46da
            lba -= 256;
Packit 5e46da
            tag_id = _read_descriptor_block(input, lba, buf);
Packit 5e46da
            if (tag_id != ECMA_AnchorVolumeDescriptorPointer) {
Packit 5e46da
                udf_error("Can't find Anchor Volume Descriptor Pointer\n");
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    udf_log("Found Anchor Volume Descriptor Pointer from lba %u\n", lba);
Packit 5e46da
Packit 5e46da
    decode_avdp(buf, avdp);
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Volume structure
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
/* Logical Partitions from Logical Volume Descriptor */
Packit 5e46da
struct udf_partitions {
Packit 5e46da
    uint32_t num_partition;
Packit 5e46da
    struct {
Packit 5e46da
        uint16_t number;
Packit 5e46da
        uint32_t lba;
Packit 5e46da
        uint32_t mirror_lba;
Packit 5e46da
    } p[2];
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
struct volume_descriptor_set {
Packit 5e46da
    struct partition_descriptor      pd;
Packit 5e46da
    struct primary_volume_descriptor pvd;
Packit 5e46da
    struct logical_volume_descriptor lvd;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
static int _search_vds(udfread_block_input *input, int part_number,
Packit 5e46da
                       const struct extent_ad *loc,
Packit 5e46da
                       struct volume_descriptor_set *vds)
Packit 5e46da
Packit 5e46da
{
Packit 5e46da
    struct volume_descriptor_pointer vdp;
Packit 5e46da
    uint8_t  buf[UDF_BLOCK_SIZE];
Packit 5e46da
    int      tag_id;
Packit 5e46da
    uint32_t lba;
Packit 5e46da
    uint32_t end_lba;
Packit 5e46da
    int      have_part = 0, have_lvd = 0, have_pvd = 0;
Packit 5e46da
Packit 5e46da
    memset(vds, 0, sizeof(*vds));
Packit 5e46da
Packit 5e46da
next_extent:
Packit 5e46da
    udf_trace("reading Volume Descriptor Sequence at lba %u, len %u bytes\n", loc->lba, loc->length);
Packit 5e46da
Packit 5e46da
    end_lba = loc->lba + loc->length / UDF_BLOCK_SIZE;
Packit 5e46da
Packit 5e46da
    /* parse Volume Descriptor Sequence */
Packit 5e46da
    for (lba = loc->lba; lba < end_lba; lba++) {
Packit 5e46da
Packit 5e46da
        tag_id = _read_descriptor_block(input, lba, buf);
Packit 5e46da
Packit 5e46da
        switch (tag_id) {
Packit 5e46da
Packit 5e46da
        case ECMA_VolumeDescriptorPointer:
Packit 5e46da
            decode_vdp(buf, &vdp;;
Packit 5e46da
            loc = &vdp.next_extent;
Packit 5e46da
            goto next_extent;
Packit 5e46da
Packit 5e46da
        case ECMA_PrimaryVolumeDescriptor:
Packit 5e46da
            udf_log("Primary Volume Descriptor in lba %u\n", lba);
Packit 5e46da
            decode_primary_volume(buf, &vds->pvd);
Packit 5e46da
            have_pvd = 1;
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case ECMA_LogicalVolumeDescriptor:
Packit 5e46da
            udf_log("Logical volume descriptor in lba %u\n", lba);
Packit 5e46da
            decode_logical_volume(buf, &vds->lvd);
Packit 5e46da
            have_lvd = 1;
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case ECMA_PartitionDescriptor:
Packit 5e46da
          udf_log("Partition Descriptor in lba %u\n", lba);
Packit 5e46da
          if (!have_part) {
Packit 5e46da
              decode_partition(buf, &vds->pd);
Packit 5e46da
              have_part = (part_number < 0 || part_number == vds->pd.number);
Packit 5e46da
              udf_log("  partition %u at lba %u, %u blocks\n", vds->pd.number, vds->pd.start_block, vds->pd.num_blocks);
Packit 5e46da
          }
Packit 5e46da
          break;
Packit 5e46da
Packit 5e46da
        case ECMA_TerminatingDescriptor:
Packit 5e46da
            udf_trace("Terminating Descriptor in lba %u\n", lba);
Packit 5e46da
            return (have_part && have_lvd) ? 0 : -1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (have_part && have_lvd && have_pvd) {
Packit 5e46da
            /* got everything interesting, skip rest blocks */
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return (have_part && have_lvd) ? 0 : -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _read_vds(udfread_block_input *input, int part_number,
Packit 5e46da
                     struct volume_descriptor_set *vds)
Packit 5e46da
{
Packit 5e46da
    struct anchor_volume_descriptor avdp;
Packit 5e46da
Packit 5e46da
    /* Find Anchor Volume Descriptor */
Packit 5e46da
    if (_read_avdp(input, &avdp) < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    // XXX we could read part of descriptors from main area and rest from backup if both are partially corrupted ...
Packit 5e46da
Packit 5e46da
    /* try to read Main Volume Descriptor Sequence */
Packit 5e46da
    if (!_search_vds(input, part_number, &avdp.mvds, vds)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* try to read Backup Volume Descriptor */
Packit 5e46da
    if (!_search_vds(input, part_number, &avdp.rvds, vds)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    udf_error("failed reading Volume Descriptor Sequence\n");
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _validate_logical_volume(const struct logical_volume_descriptor *lvd, struct long_ad *fsd_loc)
Packit 5e46da
{
Packit 5e46da
    if (lvd->block_size != UDF_BLOCK_SIZE) {
Packit 5e46da
        udf_error("incompatible block size %u\n", lvd->block_size);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* UDF 2.60 2.1.5.2 */
Packit 5e46da
    if (_check_domain_identifier(&lvd->domain_id, lvd_domain_id) < 0) {
Packit 5e46da
        udf_error("unknown Domain ID in Logical Volume Descriptor: %1.22s\n", lvd->domain_id.identifier);
Packit 5e46da
        return -1;
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
Packit 5e46da
        /* UDF 2.60 2.1.5.3 */
Packit 5e46da
        uint16_t rev = _get_u16(lvd->domain_id.identifier_suffix);
Packit 5e46da
        udf_log("Found UDF %x.%02x Logical Volume\n", rev >> 8, rev & 0xff);
Packit 5e46da
Packit 5e46da
        /* UDF 2.60 2.2.4.4 */
Packit 5e46da
Packit 5e46da
        /* location of File Set Descriptors */
Packit 5e46da
        decode_long_ad(lvd->contents_use, fsd_loc);
Packit 5e46da
Packit 5e46da
        udf_log("File Set Descriptor location: partition %u lba %u (len %u)\n",
Packit 5e46da
                fsd_loc->partition, fsd_loc->lba, fsd_loc->length);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _map_metadata_partition(udfread_block_input *input,
Packit 5e46da
                                   struct udf_partitions *part,
Packit 5e46da
                                   uint32_t lba, uint32_t mirror_lba,
Packit 5e46da
                                   const struct partition_descriptor *pd)
Packit 5e46da
{
Packit 5e46da
    struct file_entry *fe;
Packit 5e46da
    uint8_t       buf[UDF_BLOCK_SIZE];
Packit 5e46da
    int           tag_id;
Packit 5e46da
    unsigned int  i;
Packit 5e46da
Packit 5e46da
    /* resolve metadata partition location (it is virtual partition inside another partition) */
Packit 5e46da
    udf_trace("Reading metadata file entry: lba %u, mirror lba %u\n", lba, mirror_lba);
Packit 5e46da
Packit 5e46da
    for (i = 0; i < 2; i++) {
Packit 5e46da
Packit 5e46da
        if (i == 0) {
Packit 5e46da
            tag_id = _read_descriptor_block(input, pd->start_block + lba, buf);
Packit 5e46da
        } else {
Packit 5e46da
            tag_id = _read_descriptor_block(input, pd->start_block + mirror_lba, buf);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (tag_id != ECMA_ExtendedFileEntry) {
Packit 5e46da
            udf_error("read metadata file %u: unexpected tag %d\n", i, tag_id);
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        fe = decode_ext_file_entry(buf, UDF_BLOCK_SIZE, pd->number);
Packit 5e46da
        if (!fe) {
Packit 5e46da
            udf_error("parsing metadata file entry %u failed\n", i);
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (fe->content_inline) {
Packit 5e46da
            udf_error("invalid metadata file (content inline)\n");
Packit 5e46da
        } else if (!fe->u.ads.num_ad) {
Packit 5e46da
            udf_error("invalid metadata file (no allocation descriptors)\n");
Packit 5e46da
        } else if (fe->file_type == UDF_FT_METADATA) {
Packit 5e46da
            part->p[1].lba = pd->start_block + fe->u.ads.ad[0].lba;
Packit 5e46da
            udf_log("metadata file at lba %u\n", part->p[1].lba);
Packit 5e46da
        } else if (fe->file_type == UDF_FT_METADATA_MIRROR) {
Packit 5e46da
            part->p[1].mirror_lba = pd->start_block + fe->u.ads.ad[0].lba;
Packit 5e46da
            udf_log("metadata mirror file at lba %u\n", part->p[1].mirror_lba);
Packit 5e46da
        } else {
Packit 5e46da
            udf_error("unknown metadata file type %u\n", fe->file_type);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        free_file_entry(&fe);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!part->p[1].lba && part->p[1].mirror_lba) {
Packit 5e46da
        /* failed reading primary location, must use mirror */
Packit 5e46da
        part->p[1].lba        = part->p[1].mirror_lba;
Packit 5e46da
        part->p[1].mirror_lba = 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return part->p[1].lba ? 0 : -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _parse_udf_partition_maps(udfread_block_input *input,
Packit 5e46da
                                     struct udf_partitions *part,
Packit 5e46da
                                     const struct volume_descriptor_set *vds)
Packit 5e46da
{
Packit 5e46da
    /* parse partition maps
Packit 5e46da
     * There should be one type1 partition.
Packit 5e46da
     * There may be separate metadata partition.
Packit 5e46da
     * metadata partition is virtual partition that is mapped to metadata file.
Packit 5e46da
     */
Packit 5e46da
Packit 5e46da
    const uint8_t *map = vds->lvd.partition_map_table;
Packit 5e46da
    const uint8_t *end = map + vds->lvd.partition_map_lable_length;
Packit 5e46da
    unsigned int   i;
Packit 5e46da
    int            num_type1_partition = 0;
Packit 5e46da
Packit 5e46da
    udf_log("Partition map count: %u\n", vds->lvd.num_partition_maps);
Packit 5e46da
    if (vds->lvd.partition_map_lable_length > sizeof(vds->lvd.partition_map_table)) {
Packit 5e46da
        udf_error("partition map table too big !\n");
Packit 5e46da
        end -= vds->lvd.partition_map_lable_length - sizeof(vds->lvd.partition_map_table);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (i = 0; i < vds->lvd.num_partition_maps && map + 2 < end; i++) {
Packit 5e46da
Packit 5e46da
        /* Partition map, ECMA 167 3/10.7 */
Packit 5e46da
        uint8_t  type = _get_u8(map + 0);
Packit 5e46da
        uint8_t  len  = _get_u8(map + 1);
Packit 5e46da
        uint16_t ref;
Packit 5e46da
Packit 5e46da
        if (len < 2) {
Packit 5e46da
            udf_error("invalid partition map length %d\n", (int)len);
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        udf_trace("map %u: type %u\n", i, type);
Packit 5e46da
        if (map + len > end) {
Packit 5e46da
            udf_error("partition map table too short !\n");
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (type == 1) {
Packit 5e46da
Packit 5e46da
            /* ECMA 167 Type 1 partition map */
Packit 5e46da
Packit 5e46da
            if (len != 6) {
Packit 5e46da
                udf_error("invalid type 1 partition map length %d\n", (int)len);
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            ref = _get_u16(map + 4);
Packit 5e46da
            udf_log("partition map: %u: type 1 partition, ref %u\n", i, ref);
Packit 5e46da
Packit 5e46da
            if (num_type1_partition) {
Packit 5e46da
                udf_error("more than one type1 partitions not supported\n");
Packit 5e46da
            } else if (ref != vds->pd.number) {
Packit 5e46da
                udf_error("Logical partition %u refers to another physical partition %u (expected %u)\n", i, ref, vds->pd.number);
Packit 5e46da
            } else {
Packit 5e46da
                part->num_partition   = 1;
Packit 5e46da
                part->p[0].number     = i;
Packit 5e46da
                part->p[0].lba        = vds->pd.start_block;
Packit 5e46da
                part->p[0].mirror_lba = 0; /* no mirror for data partition */
Packit 5e46da
Packit 5e46da
                num_type1_partition++;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
        } else if (type == 2) {
Packit 5e46da
Packit 5e46da
            /* Type 2 partition map, UDF 2.60 2.2.18 */
Packit 5e46da
Packit 5e46da
            if (len != 64) {
Packit 5e46da
                udf_error("invalid type 2 partition map length %d\n", (int)len);
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            struct entity_id type_id;
Packit 5e46da
            decode_entity_id(map + 4, &type_id);
Packit 5e46da
            if (!_check_domain_identifier(&type_id, meta_domain_id)) {
Packit 5e46da
Packit 5e46da
                /* Metadata Partition, UDF 2.60 2.2.10 */
Packit 5e46da
Packit 5e46da
                uint32_t lba, mirror_lba;
Packit 5e46da
Packit 5e46da
                ref        = _get_u16(map + 38);
Packit 5e46da
                lba        = _get_u32(map + 40);
Packit 5e46da
                mirror_lba = _get_u32(map + 44);
Packit 5e46da
                if (ref != vds->pd.number) {
Packit 5e46da
                    udf_error("metadata file partition %u != %u\n", ref, vds->pd.number);
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
                if (!_map_metadata_partition(input, part, lba, mirror_lba, &vds->pd)) {
Packit 5e46da
                    part->num_partition = 2;
Packit 5e46da
                    part->p[1].number   = i;
Packit 5e46da
                    udf_log("partition map: %u: metadata partition, ref %u. lba %u, mirror %u\n", i, ref, part->p[1].lba, part->p[1].mirror_lba);
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
            } else {
Packit 5e46da
                udf_log("%u: unsupported type 2 partition\n", i);
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
        map += len;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return num_type1_partition ? 0 : -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Cached directory data
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
struct udf_file_identifier {
Packit 5e46da
    char           *filename;
Packit 5e46da
    struct long_ad  icb;
Packit 5e46da
    uint8_t         characteristic; /* CHAR_FLAG_* */
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
struct udf_dir {
Packit 5e46da
    uint32_t                     num_entries;
Packit 5e46da
    struct udf_file_identifier  *files;
Packit 5e46da
    struct udf_dir             **subdirs;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
static void _free_dir(struct udf_dir **pp)
Packit 5e46da
{
Packit 5e46da
    if (pp && *pp) {
Packit 5e46da
        struct udf_dir *p = *pp;
Packit 5e46da
        uint32_t i;
Packit 5e46da
Packit 5e46da
        if (p->subdirs) {
Packit 5e46da
            for (i = 0; i < p->num_entries; i++) {
Packit 5e46da
                _free_dir(&(p->subdirs[i]));
Packit 5e46da
            }
Packit 5e46da
            free(p->subdirs);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (p->files) {
Packit 5e46da
            for (i = 0; i < p->num_entries; i++) {
Packit 5e46da
                free(p->files[i].filename);
Packit 5e46da
            }
Packit 5e46da
            free(p->files);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        free(p);
Packit 5e46da
Packit 5e46da
        *pp = NULL;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
struct udfread {
Packit 5e46da
Packit 5e46da
    udfread_block_input *input;
Packit 5e46da
Packit 5e46da
    /* Volume partitions */
Packit 5e46da
    struct udf_partitions part;
Packit 5e46da
Packit 5e46da
    /* cached directory tree */
Packit 5e46da
    struct udf_dir *root_dir;
Packit 5e46da
Packit 5e46da
    char *volume_identifier;
Packit 5e46da
    char volume_set_identifier[128];
Packit 5e46da
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
udfread *udfread_init(void)
Packit 5e46da
{
Packit 5e46da
    /* set up logging */
Packit 5e46da
    if (getenv("UDFREAD_LOG")) {
Packit 5e46da
        enable_log = 1;
Packit 5e46da
    }
Packit 5e46da
    if (getenv("UDFREAD_TRACE")) {
Packit 5e46da
        enable_trace = 1;
Packit 5e46da
        enable_log = 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
#ifdef HAVE_UDFREAD_VERSION_H
Packit 5e46da
    udf_log("libudfread " UDFREAD_VERSION_STRING "\n");
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
    return (udfread *)calloc(1, sizeof(udfread));
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Metadata
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _partition_index(udfread *udf, uint16_t partition_number)
Packit 5e46da
{
Packit 5e46da
    if (partition_number == udf->part.p[0].number) {
Packit 5e46da
        return 0;
Packit 5e46da
    } else if (udf->part.num_partition > 1 && partition_number == udf->part.p[1].number) {
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    udf_error("unknown partition %u\n", partition_number);
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* read metadata blocks. If read fails, try from mirror (if available). */
Packit 5e46da
static int _read_metadata_blocks(udfread *udf, uint8_t *buf,
Packit 5e46da
                                const struct long_ad *loc)
Packit 5e46da
{
Packit 5e46da
    int      tag_id;
Packit 5e46da
    uint32_t lba, i, got;
Packit 5e46da
    int      part_idx;
Packit 5e46da
Packit 5e46da
    udf_trace("reading metadata from part %u lba %u\n", loc->partition, loc->lba);
Packit 5e46da
Packit 5e46da
    part_idx = _partition_index(udf, loc->partition);
Packit 5e46da
    if (part_idx < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read first block. Parse and check tag. */
Packit 5e46da
Packit 5e46da
    lba    = udf->part.p[part_idx].lba + loc->lba;
Packit 5e46da
    tag_id = _read_descriptor_block(udf->input, lba, buf);
Packit 5e46da
Packit 5e46da
    if (tag_id < 0) {
Packit 5e46da
Packit 5e46da
        /* try mirror */
Packit 5e46da
        if (udf->part.p[part_idx].mirror_lba) {
Packit 5e46da
            udf_log("read metadata from lba %u failed, trying mirror\n", lba);
Packit 5e46da
            lba    = udf->part.p[part_idx].mirror_lba + loc->lba;
Packit 5e46da
            tag_id = _read_descriptor_block(udf->input, lba, buf);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (tag_id < 0) {
Packit 5e46da
            udf_error("read metadata from lba %u failed\n", lba);
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read following blocks without tag parsing and checksum validation */
Packit 5e46da
Packit 5e46da
    for (i = 1; i <= (loc->length - 1) / UDF_BLOCK_SIZE; i++) {
Packit 5e46da
Packit 5e46da
        lba  = udf->part.p[part_idx].lba + loc->lba + i;
Packit 5e46da
        buf += UDF_BLOCK_SIZE;
Packit 5e46da
Packit 5e46da
        got = _read_blocks(udf->input, lba, buf, 1, 0);
Packit 5e46da
        if (got != 1) {
Packit 5e46da
            if (udf->part.p[part_idx].mirror_lba) {
Packit 5e46da
                udf_log("read metadata from lba %u failed, trying mirror\n", lba);
Packit 5e46da
                lba = udf->part.p[part_idx].mirror_lba + loc->lba + i;
Packit 5e46da
                got = _read_blocks(udf->input, lba, buf, 1, 0);
Packit 5e46da
            }
Packit 5e46da
            if (got != 1) {
Packit 5e46da
                udf_error("read metadata from lba %u failed\n", lba);
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return tag_id;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint8_t *_read_metadata(udfread *udf, const struct long_ad *icb, int *tag_id)
Packit 5e46da
{
Packit 5e46da
    uint32_t  num_blocks = (icb->length + UDF_BLOCK_SIZE - 1) / UDF_BLOCK_SIZE;
Packit 5e46da
    uint8_t  *buf;
Packit 5e46da
Packit 5e46da
    if (num_blocks < 1) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    buf = (uint8_t *)malloc(num_blocks * UDF_BLOCK_SIZE);
Packit 5e46da
    if (!buf) {
Packit 5e46da
        udf_error("out of memory\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    *tag_id = _read_metadata_blocks(udf, buf, icb);
Packit 5e46da
    if (*tag_id < 0) {
Packit 5e46da
        udf_log("reading icb blocks failed\n");
Packit 5e46da
        free(buf);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return buf;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static struct file_entry *_read_file_entry(udfread *udf,
Packit 5e46da
                                           const struct long_ad *icb)
Packit 5e46da
{
Packit 5e46da
    struct file_entry *fe = NULL;
Packit 5e46da
    uint8_t  *buf;
Packit 5e46da
    int       tag_id;
Packit 5e46da
Packit 5e46da
    udf_trace("file entry size %u bytes\n", icb->length);
Packit 5e46da
Packit 5e46da
    buf = _read_metadata(udf, icb, &tag_id);
Packit 5e46da
    if (!buf) {
Packit 5e46da
        udf_error("reading file entry failed\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    switch (tag_id) {
Packit 5e46da
        case ECMA_FileEntry:
Packit 5e46da
            fe = decode_file_entry(buf, UDF_BLOCK_SIZE, icb->partition);
Packit 5e46da
            break;
Packit 5e46da
        case ECMA_ExtendedFileEntry:
Packit 5e46da
            fe = decode_ext_file_entry(buf, UDF_BLOCK_SIZE, icb->partition);
Packit 5e46da
            break;
Packit 5e46da
        default:
Packit 5e46da
            udf_error("_read_file_entry: unknown tag %d\n", tag_id);
Packit 5e46da
            break;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    free(buf);
Packit 5e46da
Packit 5e46da
    /* read possible additional allocation extents */
Packit 5e46da
    if (fe && !fe->content_inline) {
Packit 5e46da
        while (fe->u.ads.num_ad > 0 &&
Packit 5e46da
               fe->u.ads.ad[fe->u.ads.num_ad - 1].extent_type == ECMA_AD_EXTENT_AD) {
Packit 5e46da
Packit 5e46da
            /* drop pointer to this extent from the end of AD list */
Packit 5e46da
            fe->u.ads.num_ad--;
Packit 5e46da
Packit 5e46da
            icb = &fe->u.ads.ad[fe->u.ads.num_ad];
Packit 5e46da
            udf_log("_read_file_entry: reading allocation extent @%u\n", icb->lba);
Packit 5e46da
Packit 5e46da
            buf = _read_metadata(udf, icb, &tag_id);
Packit 5e46da
            if (!buf) {
Packit 5e46da
                udf_error("_read_file_entry: reading allocation extent @%u failed\n", icb->lba);
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (tag_id != ECMA_AllocationExtentDescriptor) {
Packit 5e46da
                free(buf);
Packit 5e46da
                udf_error("_read_file_entry: unexpected tag %d (expected ECMA_AllocationExtentDescriptor)\n", tag_id);
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (decode_allocation_extent(&fe, buf, icb->length, icb->partition) < 0) {
Packit 5e46da
                free(buf);
Packit 5e46da
                udf_error("_read_file_entry: decode_allocation_extent() failed\n");
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            /* failure before this point will cause an error when reading the file past extent point.
Packit 5e46da
               (extent ad is left in file ad list). */
Packit 5e46da
Packit 5e46da
            free(buf);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return fe;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _parse_dir(const uint8_t *data, uint32_t length, struct udf_dir *dir)
Packit 5e46da
{
Packit 5e46da
    struct file_identifier fid;
Packit 5e46da
    const uint8_t *p   = data;
Packit 5e46da
    const uint8_t *end = data + length;
Packit 5e46da
    int            tag_id;
Packit 5e46da
Packit 5e46da
    if (length < 16) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    while (p < end - 16) {
Packit 5e46da
        size_t used;
Packit 5e46da
Packit 5e46da
        if (dir->num_entries == UINT32_MAX) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        tag_id = decode_descriptor_tag(p);
Packit 5e46da
        if (tag_id != ECMA_FileIdentifierDescriptor) {
Packit 5e46da
            udf_error("unexpected tag %d in directory file\n", tag_id);
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        dir->files = (struct udf_file_identifier *)_safe_realloc(dir->files, sizeof(dir->files[0]) * (dir->num_entries + 1));
Packit 5e46da
        if (!dir->files) {
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        used = decode_file_identifier(p, (size_t)(end - p), &fid;;
Packit 5e46da
        if (used == 0) {
Packit 5e46da
            /* not enough data. keep the entries we already have. */
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
        p += used;
Packit 5e46da
Packit 5e46da
        if (fid.characteristic & CHAR_FLAG_PARENT) {
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
        if (fid.filename_len < 1) {
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        dir->files[dir->num_entries].characteristic = fid.characteristic;
Packit 5e46da
        dir->files[dir->num_entries].icb = fid.icb;
Packit 5e46da
        dir->files[dir->num_entries].filename = _cs0_to_utf8(fid.filename, fid.filename_len);
Packit 5e46da
Packit 5e46da
        if (!dir->files[dir->num_entries].filename) {
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* Skip empty file identifiers.
Packit 5e46da
         * Not strictly compilant (?), \0 is allowed in
Packit 5e46da
         * ECMA167 file identifier.
Packit 5e46da
         */
Packit 5e46da
        if (!dir->files[dir->num_entries].filename[0]) {
Packit 5e46da
            udf_error("skipping empty file identifier\n");
Packit 5e46da
            free(dir->files[dir->num_entries].filename);
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        dir->num_entries++;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static struct udf_dir *_read_dir_file(udfread *udf, const struct long_ad *loc)
Packit 5e46da
{
Packit 5e46da
    struct udf_dir *dir = NULL;
Packit 5e46da
    uint8_t        *data;
Packit 5e46da
    int             tag_id;
Packit 5e46da
Packit 5e46da
    udf_trace("directory size %u bytes\n", loc->length);
Packit 5e46da
Packit 5e46da
    data = _read_metadata(udf, loc, &tag_id);
Packit 5e46da
    if (!data) {
Packit 5e46da
        udf_error("reading directory file failed\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    dir = (struct udf_dir *)calloc(1, sizeof(struct udf_dir));
Packit 5e46da
    if (dir) {
Packit 5e46da
        if (_parse_dir(data, loc->length, dir) < 0) {
Packit 5e46da
            _free_dir(&dir;;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    free(data);
Packit 5e46da
    return dir;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static struct udf_dir *_read_dir(udfread *udf, const struct long_ad *icb)
Packit 5e46da
{
Packit 5e46da
    struct file_entry *fe;
Packit 5e46da
    struct udf_dir    *dir = NULL;
Packit 5e46da
Packit 5e46da
    fe = _read_file_entry(udf, icb);
Packit 5e46da
    if (!fe) {
Packit 5e46da
        udf_error("error reading directory file entry\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (fe->file_type != ECMA_FT_DIR) {
Packit 5e46da
        udf_error("directory file type is not directory\n");
Packit 5e46da
        free_file_entry(&fe);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (fe->content_inline) {
Packit 5e46da
        dir = (struct udf_dir *)calloc(1, sizeof(struct udf_dir));
Packit 5e46da
        if (dir) {
Packit 5e46da
            if (_parse_dir(&fe->u.data.content[0], fe->u.data.information_length, dir) < 0) {
Packit 5e46da
                udf_error("failed parsing inline directory file\n");
Packit 5e46da
                _free_dir(&dir;;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
    } else if (fe->u.ads.num_ad == 0) {
Packit 5e46da
        udf_error("empty directory file");
Packit 5e46da
    } else {
Packit 5e46da
        if (fe->u.ads.num_ad > 1) {
Packit 5e46da
            udf_error("unsupported fragmented directory file\n");
Packit 5e46da
        }
Packit 5e46da
        dir = _read_dir_file(udf, &fe->u.ads.ad[0]);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    free_file_entry(&fe);
Packit 5e46da
    return dir;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _read_root_dir(udfread *udf, const struct long_ad *fsd_loc)
Packit 5e46da
{
Packit 5e46da
    struct file_set_descriptor fsd;
Packit 5e46da
    uint8_t             buf[UDF_BLOCK_SIZE];
Packit 5e46da
    int                 tag_id = -1;
Packit 5e46da
    struct long_ad      loc = *fsd_loc;
Packit 5e46da
Packit 5e46da
    udf_trace("reading root directory fsd from part %u lba %u\n", fsd_loc->partition, fsd_loc->lba);
Packit 5e46da
Packit 5e46da
    /* search for File Set Descriptor from the area described by fsd_loc */
Packit 5e46da
Packit 5e46da
    loc.length = UDF_BLOCK_SIZE;
Packit 5e46da
    for (; loc.lba <= fsd_loc->lba + (fsd_loc->length - 1) / UDF_BLOCK_SIZE; loc.lba++) {
Packit 5e46da
Packit 5e46da
        tag_id = _read_metadata_blocks(udf, buf, &loc;;
Packit 5e46da
        if (tag_id == ECMA_FileSetDescriptor) {
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
        if (tag_id == ECMA_TerminatingDescriptor) {
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
        udf_error("unhandled tag %d in File Set Descriptor area\n", tag_id);
Packit 5e46da
    }
Packit 5e46da
    if (tag_id != ECMA_FileSetDescriptor) {
Packit 5e46da
        udf_error("didn't find File Set Descriptor\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    decode_file_set_descriptor(buf, &fsd;;
Packit 5e46da
    udf_log("root directory in part %u lba %u\n", fsd.root_icb.partition, fsd.root_icb.lba);
Packit 5e46da
Packit 5e46da
    /* read root directory from location given in File Set Descriptor */
Packit 5e46da
Packit 5e46da
    udf->root_dir = _read_dir(udf, &fsd.root_icb);
Packit 5e46da
    if (!udf->root_dir) {
Packit 5e46da
        udf_error("error reading root directory\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static struct udf_dir *_read_subdir(udfread *udf, struct udf_dir *dir, uint32_t index)
Packit 5e46da
{
Packit 5e46da
    if (!(dir->files[index].characteristic & CHAR_FLAG_DIR)) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!dir->subdirs) {
Packit 5e46da
        struct udf_dir **subdirs = (struct udf_dir **)calloc(sizeof(struct udf_dir *), dir->num_entries);
Packit 5e46da
        if (!subdirs) {
Packit 5e46da
            udf_error("out of memory\n");
Packit 5e46da
            return NULL;
Packit 5e46da
        }
Packit 5e46da
        if (!atomic_pointer_compare_and_exchange(&dir->subdirs, NULL, subdirs)) {
Packit 5e46da
            free(subdirs);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!dir->subdirs[index]) {
Packit 5e46da
        struct udf_dir *subdir = _read_dir(udf, &dir->files[index].icb);
Packit 5e46da
        if (!subdir) {
Packit 5e46da
            return NULL;
Packit 5e46da
        }
Packit 5e46da
        if (!atomic_pointer_compare_and_exchange(&dir->subdirs[index], NULL, subdir)) {
Packit 5e46da
            _free_dir(&subdir);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return dir->subdirs[index];
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _scan_dir(const struct udf_dir *dir, const char *filename, uint32_t *index)
Packit 5e46da
{
Packit 5e46da
    uint32_t i;
Packit 5e46da
Packit 5e46da
    for (i = 0; i < dir->num_entries; i++) {
Packit 5e46da
        if (!strcmp(filename, dir->files[i].filename)) {
Packit 5e46da
            *index = i;
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    udf_log("file %s not found\n", filename);
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _find_file(udfread *udf, const char *path,
Packit 5e46da
                      const struct udf_dir **p_dir,
Packit 5e46da
                      const struct udf_file_identifier **p_fid)
Packit 5e46da
{
Packit 5e46da
    const struct udf_file_identifier *fid = NULL;
Packit 5e46da
    struct udf_dir *current_dir;
Packit 5e46da
    char *tmp_path, *save_ptr, *token;
Packit 5e46da
Packit 5e46da
    current_dir = udf->root_dir;
Packit 5e46da
    if (!current_dir) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    tmp_path = _str_dup(path);
Packit 5e46da
    if (!tmp_path) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    token = strtok_r(tmp_path, "/\\", &save_ptr);
Packit 5e46da
    if (token == NULL) {
Packit 5e46da
        udf_trace("_find_file: requested root dir\n");
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    while (token) {
Packit 5e46da
        uint32_t index;
Packit 5e46da
        if (_scan_dir(current_dir, token, &index) < 0) {
Packit 5e46da
            udf_log("_find_file: entry %s not found\n", token);
Packit 5e46da
            goto error;
Packit 5e46da
        }
Packit 5e46da
        fid = &current_dir->files[index];
Packit 5e46da
Packit 5e46da
        token = strtok_r(NULL, "/\\", &save_ptr);
Packit 5e46da
Packit 5e46da
        if (fid->characteristic & CHAR_FLAG_DIR) {
Packit 5e46da
            current_dir = _read_subdir(udf, current_dir, index);
Packit 5e46da
            if (!current_dir) {
Packit 5e46da
                goto error;
Packit 5e46da
            }
Packit 5e46da
        } else if (token) {
Packit 5e46da
            udf_log("_find_file: entry %s not found (parent is file, not directory)\n", token);
Packit 5e46da
            goto error;
Packit 5e46da
        } else {
Packit 5e46da
            // found a file, make sure we won't return directory data
Packit 5e46da
            current_dir = NULL;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p_fid) {
Packit 5e46da
        if (!fid) {
Packit 5e46da
            udf_log("no file identifier found for %s\n", path);
Packit 5e46da
            goto error;
Packit 5e46da
        }
Packit 5e46da
        *p_fid = fid;
Packit 5e46da
    }
Packit 5e46da
    if (p_dir) {
Packit 5e46da
        *p_dir = current_dir;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    free(tmp_path);
Packit 5e46da
    return 0;
Packit 5e46da
Packit 5e46da
error:
Packit 5e46da
    free(tmp_path);
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Volume access API
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
int udfread_open_input(udfread *udf, udfread_block_input *input/*, int partition*/)
Packit 5e46da
{
Packit 5e46da
    struct volume_descriptor_set vds;
Packit 5e46da
    struct long_ad fsd_location;
Packit 5e46da
Packit 5e46da
    if (!udf || !input || !input->read) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (_probe_volume(input) < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read Volume Descriptor Sequence */
Packit 5e46da
    if (_read_vds(input, 0, &vds) < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* validate logical volume structure */
Packit 5e46da
    if (_validate_logical_volume(&vds.lvd, &fsd_location) < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* Volume Identifier. CS0, UDF 2.1.1 */
Packit 5e46da
    udf->volume_identifier = _cs0_to_utf8(vds.pvd.volume_identifier, vds.pvd.volume_identifier_length);
Packit 5e46da
Packit 5e46da
    memcpy(udf->volume_set_identifier, vds.pvd.volume_set_identifier, 128);
Packit 5e46da
    udf_log("Volume Identifier: %s\n", udf->volume_identifier);
Packit 5e46da
Packit 5e46da
    /* map partitions */
Packit 5e46da
    if (_parse_udf_partition_maps(input, &udf->part, &vds) < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* Read root directory */
Packit 5e46da
    udf->input = input;
Packit 5e46da
    if (_read_root_dir(udf, &fsd_location) < 0) {
Packit 5e46da
        udf->input = NULL;
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int udfread_open(udfread *udf, const char *path)
Packit 5e46da
{
Packit 5e46da
    udfread_block_input *input;
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (!path) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    input = block_input_new(path);
Packit 5e46da
    if (!input) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = udfread_open_input(udf, input);
Packit 5e46da
    if (result < 0) {
Packit 5e46da
        if (input->close) {
Packit 5e46da
            input->close(input);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void udfread_close(udfread *udf)
Packit 5e46da
{
Packit 5e46da
    if (udf) {
Packit 5e46da
        if (udf->input) {
Packit 5e46da
            if (udf->input->close) {
Packit 5e46da
                udf->input->close(udf->input);
Packit 5e46da
            }
Packit 5e46da
            udf->input = NULL;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        _free_dir(&udf->root_dir);
Packit 5e46da
        free(udf->volume_identifier);
Packit 5e46da
        free(udf);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
const char *udfread_get_volume_id(udfread *udf)
Packit 5e46da
{
Packit 5e46da
    if (udf) {
Packit 5e46da
        return udf->volume_identifier;
Packit 5e46da
    }
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
size_t udfread_get_volume_set_id (udfread *udf, void *buffer, size_t size)
Packit 5e46da
{
Packit 5e46da
    if (udf) {
Packit 5e46da
        if (size > sizeof(udf->volume_set_identifier)) {
Packit 5e46da
            size = sizeof(udf->volume_set_identifier);
Packit 5e46da
        }
Packit 5e46da
        memcpy(buffer, udf->volume_set_identifier, size);
Packit 5e46da
        return sizeof(udf->volume_set_identifier);
Packit 5e46da
    }
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * Directory access API
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
struct udfread_dir {
Packit 5e46da
    const struct udf_dir *dir;
Packit 5e46da
    uint32_t              current_file;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
UDFDIR *udfread_opendir(udfread *udf, const char *path)
Packit 5e46da
{
Packit 5e46da
    const struct udf_dir *dir = NULL;
Packit 5e46da
    UDFDIR *result;
Packit 5e46da
Packit 5e46da
    if (!udf || !udf->input || !path) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (_find_file(udf, path, &dir, NULL) < 0) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!dir) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = (UDFDIR *)calloc(1, sizeof(UDFDIR));
Packit 5e46da
    if (result) {
Packit 5e46da
        result->dir = dir;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
struct udfread_dirent *udfread_readdir(UDFDIR *p, struct udfread_dirent *entry)
Packit 5e46da
{
Packit 5e46da
    const struct udf_file_identifier *fi;
Packit 5e46da
Packit 5e46da
    if (!p || !entry || !p->dir) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p->current_file >= p->dir->num_entries) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    fi = &p->dir->files[p->current_file];
Packit 5e46da
Packit 5e46da
    entry->d_name = fi->filename;
Packit 5e46da
Packit 5e46da
    if (fi->characteristic & CHAR_FLAG_PARENT) {
Packit 5e46da
        entry->d_type = UDF_DT_DIR;
Packit 5e46da
        entry->d_name = "..";
Packit 5e46da
    } else if (fi->characteristic & CHAR_FLAG_DIR) {
Packit 5e46da
        entry->d_type = UDF_DT_DIR;
Packit 5e46da
    } else {
Packit 5e46da
        entry->d_type = UDF_DT_REG;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->current_file++;
Packit 5e46da
Packit 5e46da
    return entry;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void udfread_rewinddir(UDFDIR *p)
Packit 5e46da
{
Packit 5e46da
    if (p) {
Packit 5e46da
        p->current_file = 0;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void udfread_closedir(UDFDIR *p)
Packit 5e46da
{
Packit 5e46da
    free(p);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * File access API
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
struct udfread_file {
Packit 5e46da
    udfread           *udf;
Packit 5e46da
    struct file_entry *fe;
Packit 5e46da
Packit 5e46da
    /* byte stream access */
Packit 5e46da
    uint64_t    pos;
Packit 5e46da
    uint8_t    *block;
Packit 5e46da
    int         block_valid;
Packit 5e46da
Packit 5e46da
    void       *block_mem;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
UDFFILE *udfread_file_open(udfread *udf, const char *path)
Packit 5e46da
{
Packit 5e46da
    const struct udf_file_identifier *fi = NULL;
Packit 5e46da
    struct file_entry *fe;
Packit 5e46da
    UDFFILE *result;
Packit 5e46da
Packit 5e46da
    if (!udf || !udf->input || !path) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (_find_file(udf, path, NULL, &fi) < 0) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (fi->characteristic & CHAR_FLAG_DIR) {
Packit 5e46da
        udf_log("error opening file %s (is directory)\n", path);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    fe = _read_file_entry(udf, &fi->icb);
Packit 5e46da
    if (!fe) {
Packit 5e46da
        udf_error("error reading file entry for %s\n", path);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result = (UDFFILE *)calloc(1, sizeof(UDFFILE));
Packit 5e46da
    if (!result) {
Packit 5e46da
        free_file_entry(&fe);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    result->udf = udf;
Packit 5e46da
    result->fe  = fe;
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t udfread_file_size(UDFFILE *p)
Packit 5e46da
{
Packit 5e46da
    if (p) {
Packit 5e46da
        return (int64_t)p->fe->length;
Packit 5e46da
    }
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void udfread_file_close(UDFFILE *p)
Packit 5e46da
{
Packit 5e46da
    if (p) {
Packit 5e46da
        free_file_entry(&p->fe);
Packit 5e46da
        free(p->block_mem);
Packit 5e46da
        free(p);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * block access
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static uint32_t _file_lba(UDFFILE *p, uint32_t file_block, uint32_t *extent_length)
Packit 5e46da
{
Packit 5e46da
    const struct file_entry *fe;
Packit 5e46da
    unsigned int i;
Packit 5e46da
    uint32_t     ad_size;
Packit 5e46da
Packit 5e46da
    fe = p->fe;
Packit 5e46da
Packit 5e46da
    for (i = 0; i < fe->u.ads.num_ad; i++) {
Packit 5e46da
        const struct long_ad *ad = &fe->u.ads.ad[0];
Packit 5e46da
        ad_size = (ad[i].length + UDF_BLOCK_SIZE - 1) / UDF_BLOCK_SIZE;
Packit 5e46da
        if (file_block < ad_size) {
Packit 5e46da
Packit 5e46da
            if (ad[i].extent_type != ECMA_AD_EXTENT_NORMAL) {
Packit 5e46da
                if (ad[i].extent_type == ECMA_AD_EXTENT_AD) {
Packit 5e46da
                    udf_error("unsupported allocation descriptor: extent type %u\n", ad[i].extent_type);
Packit 5e46da
                }
Packit 5e46da
                return 0;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (!ad[i].lba) {
Packit 5e46da
                /* empty file / no allocated space */
Packit 5e46da
                return 0;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (ad[i].partition != p->udf->part.p[0].number) {
Packit 5e46da
                udf_error("file partition %u != %u\n", ad[i].partition, p->udf->part.p[0].number);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (extent_length) {
Packit 5e46da
                *extent_length = ad_size - file_block;
Packit 5e46da
            }
Packit 5e46da
            return p->udf->part.p[0].lba + ad[i].lba + file_block;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        file_block -= ad_size;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _file_lba_exists(UDFFILE *p)
Packit 5e46da
{
Packit 5e46da
    if (!p) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p->fe->content_inline) {
Packit 5e46da
        udf_error("can't map lba for inline file\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint32_t udfread_file_lba(UDFFILE *p, uint32_t file_block)
Packit 5e46da
{
Packit 5e46da
    if (!_file_lba_exists(p)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return _file_lba(p, file_block, NULL);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint32_t udfread_read_blocks(UDFFILE *p, void *buf, uint32_t file_block, uint32_t num_blocks, int flags)
Packit 5e46da
{
Packit 5e46da
    uint32_t i;
Packit 5e46da
Packit 5e46da
    if (!num_blocks || !buf) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!_file_lba_exists(p)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (i = 0; i < num_blocks; ) {
Packit 5e46da
        uint32_t extent_length = 0;
Packit 5e46da
        uint32_t lba;
Packit 5e46da
        uint8_t *block = (uint8_t *)buf + UDF_BLOCK_SIZE * i;
Packit 5e46da
Packit 5e46da
        lba = _file_lba(p, file_block + i, &extent_length);
Packit 5e46da
        udf_trace("map block %u to lba %u\n", file_block + i, lba);
Packit 5e46da
Packit 5e46da
        if (!lba) {
Packit 5e46da
            /* unallocated/unwritten block or EOF */
Packit 5e46da
            uint32_t file_blocks = (udfread_file_size(p) + UDF_BLOCK_SIZE - 1) / UDF_BLOCK_SIZE;
Packit 5e46da
            if (file_block + i < file_blocks) {
Packit 5e46da
                udf_trace("zero-fill unallocated / unwritten block %u\n", file_block + i);
Packit 5e46da
                memset(block, 0, UDF_BLOCK_SIZE);
Packit 5e46da
                i++;
Packit 5e46da
                continue;
Packit 5e46da
            }
Packit 5e46da
            udf_error("block %u outside of file (size %u blocks)\n", file_block + i, file_blocks);
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (extent_length > num_blocks - i) {
Packit 5e46da
            extent_length = num_blocks - i;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        extent_length = _read_blocks(p->udf->input, lba, block, extent_length, flags);
Packit 5e46da
        if (extent_length < 1) {
Packit 5e46da
            break;
Packit 5e46da
        }
Packit 5e46da
        i += extent_length;
Packit 5e46da
    }
Packit 5e46da
    return i;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * byte stream
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static ssize_t _read(UDFFILE *p, void *buf, size_t bytes)
Packit 5e46da
{
Packit 5e46da
    /* start from middle of block ?
Packit 5e46da
     * maximal file size, i.e. position, is 2^32 * block size
Packit 5e46da
     */
Packit 5e46da
Packit 5e46da
    size_t pos_off = p->pos % UDF_BLOCK_SIZE;
Packit 5e46da
    uint32_t file_block = (uint32_t)(p->pos / UDF_BLOCK_SIZE);
Packit 5e46da
    if (pos_off) {
Packit 5e46da
        size_t chunk_size = UDF_BLOCK_SIZE - pos_off;
Packit 5e46da
        if (!p->block_valid) {
Packit 5e46da
            if (udfread_read_blocks(p, p->block, file_block, 1, 0) != 1) {
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
            p->block_valid = 1;
Packit 5e46da
        }
Packit 5e46da
        if (chunk_size > bytes) {
Packit 5e46da
            chunk_size = bytes;
Packit 5e46da
        }
Packit 5e46da
        memcpy(buf, p->block + pos_off, chunk_size);
Packit 5e46da
        p->pos += (uint64_t)chunk_size;
Packit 5e46da
        return (ssize_t)chunk_size;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read full block(s) ? */
Packit 5e46da
    if (bytes >= UDF_BLOCK_SIZE) {
Packit 5e46da
        uint32_t num_blocks = bytes / UDF_BLOCK_SIZE;
Packit 5e46da
        num_blocks = udfread_read_blocks(p, buf, file_block, num_blocks, 0);
Packit 5e46da
        if (num_blocks < 1) {
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
        p->pos += num_blocks * UDF_BLOCK_SIZE;
Packit 5e46da
        return num_blocks * UDF_BLOCK_SIZE;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read beginning of a block */
Packit 5e46da
    if (udfread_read_blocks(p, p->block, file_block, 1, 0) != 1) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    p->block_valid = 1;
Packit 5e46da
    memcpy(buf, p->block, bytes);
Packit 5e46da
    p->pos += bytes;
Packit 5e46da
    return (ssize_t)bytes;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static ssize_t _read_inline(UDFFILE *p, void *buf, size_t bytes)
Packit 5e46da
{
Packit 5e46da
    uint64_t information_length = p->fe->u.data.information_length;
Packit 5e46da
    size_t   pad_size = 0;
Packit 5e46da
Packit 5e46da
    if (p->pos + bytes > information_length) {
Packit 5e46da
        udf_log("read hits padding in inline file\n");
Packit 5e46da
        if (p->pos > information_length) {
Packit 5e46da
            pad_size = bytes;
Packit 5e46da
        } else {
Packit 5e46da
            pad_size = (size_t)(p->pos + bytes - information_length);
Packit 5e46da
        }
Packit 5e46da
        memset((char*)buf + bytes - pad_size, 0, pad_size);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (pad_size < bytes) {
Packit 5e46da
        memcpy(buf, &p->fe->u.data.content[p->pos], bytes - pad_size);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->pos = p->pos + bytes;
Packit 5e46da
    return (ssize_t)bytes;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
#define ALIGN(p, align) \
Packit 5e46da
  (uint8_t *)( ((uintptr_t)(p) + ((align)-1)) & ~((uintptr_t)((align)-1)))
Packit 5e46da
Packit 5e46da
ssize_t udfread_file_read(UDFFILE *p, void *buf, size_t bytes)
Packit 5e46da
{
Packit 5e46da
    uint8_t *bufpt = (uint8_t *)buf;
Packit 5e46da
Packit 5e46da
    /* sanity checks */
Packit 5e46da
    if (!p || !buf) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if ((ssize_t)bytes < 0 || (int64_t)bytes < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p->pos >= p->fe->length) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* limit range to file size */
Packit 5e46da
    if (p->pos + bytes > p->fe->length) {
Packit 5e46da
        bytes = (size_t)(p->fe->length - p->pos);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* small files may be stored inline in file entry */
Packit 5e46da
    if (p->fe->content_inline) {
Packit 5e46da
        return _read_inline(p, buf, bytes);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* allocate temp storage for input block */
Packit 5e46da
    if (!p->block) {
Packit 5e46da
        p->block_mem = malloc(2 * UDF_BLOCK_SIZE);
Packit 5e46da
        if (!p->block_mem) {
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
        p->block = ALIGN(p->block_mem, UDF_BLOCK_SIZE);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read chunks */
Packit 5e46da
    while (bytes > 0) {
Packit 5e46da
        ssize_t r = _read(p, bufpt, bytes);
Packit 5e46da
        if (r < 0) {
Packit 5e46da
            if (bufpt != buf) {
Packit 5e46da
                /* got some bytes */
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
            /* got nothing */
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
        bufpt += r;
Packit 5e46da
        bytes -= (size_t)r;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return (intptr_t)bufpt - (intptr_t)buf;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t udfread_file_tell(UDFFILE *p)
Packit 5e46da
{
Packit 5e46da
    if (p) {
Packit 5e46da
        return (int64_t)p->pos;
Packit 5e46da
    }
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int64_t udfread_file_seek(UDFFILE *p, int64_t pos, int whence)
Packit 5e46da
{
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    switch (whence) {
Packit 5e46da
        case UDF_SEEK_CUR:
Packit 5e46da
            pos = udfread_file_tell(p) + pos;
Packit 5e46da
            break;
Packit 5e46da
        case UDF_SEEK_END:
Packit 5e46da
            pos = udfread_file_size(p) + pos;
Packit 5e46da
            break;
Packit 5e46da
        case UDF_SEEK_SET:
Packit 5e46da
            break;
Packit 5e46da
        default:
Packit 5e46da
            return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (pos >= 0 && pos <= udfread_file_size(p)) {
Packit 5e46da
        p->pos = (uint64_t)pos;
Packit 5e46da
        p->block_valid = 0;
Packit 5e46da
        return udfread_file_tell(p);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}