|
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 = ¤t_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 |
}
|