/*
* This file is part of libudfread
* Copyright (C) 2014-2015 VLC authors and VideoLAN
*
* Authors: Petri Hintukainen <phintuka@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "ecma167.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ecma_error(...) fprintf(stderr, "ecma: " __VA_ARGS__)
/*
* Part 1: General
*/
/* fixed-length dstring, ECMA 1/7.2.12 */
static uint8_t _decode_dstring(const uint8_t *p, size_t field_length, uint8_t *str)
{
size_t length;
if (field_length < 1) {
return 0;
}
field_length--;
length = _get_u8(p + field_length);
if (length > field_length) {
length = field_length;
}
memcpy(str, p, length);
return (uint8_t)length;
}
/* Extent Descriptor (ECMA 167, 3/7.1) */
static void _decode_extent_ad(const uint8_t *p, struct extent_ad *ad)
{
ad->length = _get_u32(p + 0);
ad->lba = _get_u32(p + 4);
}
/* Entity Identifier (ECMA 167, 1/7.4) */
void decode_entity_id(const uint8_t *p, struct entity_id *eid)
{
memcpy(eid->identifier, p + 1, 23);
memcpy(eid->identifier_suffix, p + 24, 8);
}
/*
* Part 3: Volume Structure
*/
/* Descriptor Tag (ECMA 167, 3/7.2) */
enum tag_identifier decode_descriptor_tag(const uint8_t *buf)
{
uint16_t id;
uint8_t checksum = 0;
int i;
id = _get_u16(buf + 0);
/* descriptor tag is 16 bytes */
/* calculate tag checksum */
for (i = 0; i < 4; i++) {
checksum = (uint8_t)(checksum + buf[i]);
}
for (i = 5; i < 16; i++) {
checksum = (uint8_t)(checksum + buf[i]);
}
if (checksum != buf[4]) {
return ECMA_TAG_NONE;
}
return (enum tag_identifier)id;
}
/* Primary Volume Descriptor (ECMA 167, 3/10.1) */
void decode_primary_volume(const uint8_t *p, struct primary_volume_descriptor *pvd)
{
pvd->volume_identifier_length = _decode_dstring(p + 24, 32, pvd->volume_identifier);
memcpy(pvd->volume_set_identifier, p + 72, 128);
}
/* Anchor Volume Descriptor Pointer (ECMA 167 3/10.2) */
void decode_avdp(const uint8_t *p, struct anchor_volume_descriptor *avdp)
{
/* Main volume descriptor sequence extent */
_decode_extent_ad(p + 16, &avdp->mvds);
/* Reserve (backup) volume descriptor sequence extent */
_decode_extent_ad(p + 24, &avdp->rvds);
}
/* Volume Descriptor Pointer (ECMA 167 3/10.3) */
void decode_vdp(const uint8_t *p, struct volume_descriptor_pointer *vdp)
{
_decode_extent_ad(p + 20, &vdp->next_extent);
}
/* Partition Descriptor (ECMA 167 3/10.5) */
void decode_partition(const uint8_t *p, struct partition_descriptor *pd)
{
pd->number = _get_u16(p + 22);
pd->start_block = _get_u32(p + 188);
pd->num_blocks = _get_u32(p + 192);
}
/* Logical Volume Descriptor (ECMA 167 3/10.6) */
void decode_logical_volume(const uint8_t *p, struct logical_volume_descriptor *lvd)
{
size_t map_size;
lvd->block_size = _get_u32(p + 212);
decode_entity_id(p + 216, &lvd->domain_id);
memcpy(lvd->contents_use, p + 248, 16);
lvd->partition_map_lable_length = _get_u32(p + 264);
lvd->num_partition_maps = _get_u32(p + 268);
/* XXX cut long maps */
map_size = lvd->partition_map_lable_length;
if (map_size > sizeof(lvd->partition_map_table)) {
map_size = sizeof(lvd->partition_map_table);
}
/* input size is one block (2048 bytes) */
if (map_size > 2048 - 440) {
map_size = 2048 - 440;
}
memcpy(lvd->partition_map_table, p + 440, map_size);
}
/*
* Part 4: File Structure
*/
/* File Set Descriptor (ECMA 167 4/14.1) */
void decode_file_set_descriptor(const uint8_t *p, struct file_set_descriptor *fsd)
{
decode_long_ad(p + 400, &fsd->root_icb);
}
/* File Identifier (ECMA 167 4/14.4) */
size_t decode_file_identifier(const uint8_t *p, size_t size, struct file_identifier *fi)
{
size_t l_iu; /* length of implementation use field */
if (size < 38) {
ecma_error("not enough data\n");
return 0;
}
fi->characteristic = _get_u8(p + 18);
fi->filename_len = _get_u8(p + 19);
decode_long_ad(p + 20, &fi->icb);
l_iu = _get_u16(p + 36);
if (size < 38 + l_iu + fi->filename_len) {
ecma_error("not enough data\n");
return 0;
}
if (fi->filename_len) {
memcpy(fi->filename, p + 38 + l_iu, fi->filename_len);
}
fi->filename[fi->filename_len] = 0;
/* ECMA 167, 4/14.4
* padding size 4 * ip((L_FI+L_IU+38+3)/4) - (L_FI+L_IU+38)
* => padded to dwords
*/
return 4 * ((38 + (size_t)fi->filename_len + l_iu + 3) / 4);
}
/* ICB Tag (ECMA 167 4/14.6) */
struct icb_tag {
uint8_t file_type;
uint16_t strategy_type;
uint16_t flags;
};
static void _decode_icb_tag(const uint8_t *p, struct icb_tag *tag)
{
tag->strategy_type = _get_u16(p + 4);
tag->file_type = _get_u8 (p + 11);
tag->flags = _get_u16(p + 18);
}
/* Allocation Descriptors */
#define AD_LENGTH_MASK 0x3fffffff
#define AD_TYPE(length) ((length) >> 30)
/* Short Allocation Descriptor (ECMA 167, 4/14.14.1) */
static void _decode_short_ad(const uint8_t *buf, uint16_t partition, struct long_ad *ad)
{
uint32_t u32 = _get_u32(buf + 0);
ad->extent_type = AD_TYPE(u32);
ad->length = u32 & AD_LENGTH_MASK;
ad->lba = _get_u32(buf + 4);
ad->partition = partition;
}
/* Long Allocation Descriptor (ECMA 167, 4/14.14.2) */
void decode_long_ad(const uint8_t *buf, struct long_ad *ad)
{
uint32_t u32 = _get_u32(buf + 0);
ad->extent_type = AD_TYPE(u32);
ad->length = u32 & AD_LENGTH_MASK;
ad->lba = _get_u32(buf + 4);
ad->partition = _get_u16(buf + 8);
}
/* Exrtended Allocation Descriptor (ECMA 167, 4/14.14.3) */
static void _decode_extended_ad(const uint8_t *buf, struct long_ad *ad)
{
uint32_t u32 = _get_u32(buf + 0);
ad->extent_type = AD_TYPE(u32);
ad->length = u32 & AD_LENGTH_MASK;
ad->lba = _get_u32(buf + 12);
ad->partition = _get_u16(buf + 16);
}
/* File Entry */
static void _decode_file_ads(const uint8_t *p, int ad_type, uint16_t partition,
struct long_ad *ad, unsigned num_ad)
{
uint32_t i;
for (i = 0; i < num_ad; i++) {
switch (ad_type) {
case 0:
_decode_short_ad(p, partition, &ad[i]);
p += 8;
break;
case 1:
decode_long_ad(p, &ad[i]);
p += 16;
break;
case 2:
_decode_extended_ad(p, &ad[i]);
p += 20;
break;
}
}
}
static struct file_entry *_decode_file_entry(const uint8_t *p, size_t size,
uint16_t partition, uint32_t l_ad, uint32_t p_ad)
{
struct file_entry *fe;
struct icb_tag tag;
uint32_t num_ad;
int content_inline = 0;
if (p_ad + l_ad > size) {
ecma_error("not enough data in file entry\n");
return NULL;
}
_decode_icb_tag(p + 16, &tag);
if (tag.strategy_type != 4) {
/* UDF (2.): only ICB strategy types 4 and 4096 shall be recorded */
ecma_error("unsupported icb strategy type %d\n", tag.strategy_type);
return NULL;
}
switch (tag.flags & 7) {
case 0: num_ad = l_ad / 8; break;
case 1: num_ad = l_ad / 16; break;
case 2: num_ad = l_ad / 20; break;
case 3:
num_ad = 0;
content_inline = 1;
break;
default:
ecma_error("unsupported icb flags: 0x%x\n", tag.flags);
return NULL;
}
if (num_ad < 1) {
fe = (struct file_entry *)calloc(1, sizeof(struct file_entry) + l_ad);
} else {
fe = (struct file_entry *)calloc(1, sizeof(struct file_entry) + sizeof(struct long_ad) * (num_ad - 1));
}
if (!fe) {
return NULL;
}
fe->file_type = tag.file_type;
fe->length = _get_u64(p + 56);
fe->ad_type = tag.flags & 7;
if (content_inline) {
/* data of small files can be embedded in file entry */
/* copy embedded file data */
fe->content_inline = 1;
fe->u.data.information_length = l_ad;
memcpy(fe->u.data.content, p + p_ad, l_ad);
} else {
fe->u.ads.num_ad = num_ad;
_decode_file_ads(p + p_ad, fe->ad_type, partition, &fe->u.ads.ad[0], num_ad);
}
return fe;
}
int decode_allocation_extent(struct file_entry **p_fe, const uint8_t *p, size_t size, uint16_t partition)
{
struct file_entry *fe = *p_fe;
uint32_t l_ad, num_ad;
l_ad = _get_u32(p + 20);
if (size < 24 || size - 24 < l_ad) {
ecma_error("decode_allocation_extent: invalid allocation extent (l_ad)\n");
return -1;
}
switch (fe->ad_type) {
case 0: num_ad = l_ad / 8; break;
case 1: num_ad = l_ad / 16; break;
case 2: num_ad = l_ad / 20; break;
default:
return -1;
}
if (num_ad < 1) {
ecma_error("decode_allocation_extent: empty allocation extent\n");
return 0;
}
fe = (struct file_entry *)realloc(*p_fe, sizeof(struct file_entry) + sizeof(struct long_ad) * (fe->u.ads.num_ad + num_ad));
if (!fe) {
return -1;
}
*p_fe = fe;
/* decode new allocation descriptors */
_decode_file_ads(p + 24, fe->ad_type, partition, &fe->u.ads.ad[fe->u.ads.num_ad], num_ad);
fe->u.ads.num_ad += num_ad;
return 0;
}
/* File Entry (ECMA 167, 4/14.9) */
struct file_entry *decode_file_entry(const uint8_t *p, size_t size, uint16_t partition)
{
uint32_t l_ea, l_ad;
l_ea = _get_u32(p + 168);
l_ad = _get_u32(p + 172);
/* check for integer overflow */
if ((uint64_t)l_ea + (uint64_t)l_ad + (uint64_t)176 >= (uint64_t)1<<32) {
ecma_error("invalid file entry\n");
return NULL;
}
return _decode_file_entry(p, size, partition, l_ad, 176 + l_ea);
}
/* Extended File Entry (ECMA 167, 4/14.17) */
struct file_entry *decode_ext_file_entry(const uint8_t *p, size_t size, uint16_t partition)
{
uint32_t l_ea, l_ad;
l_ea = _get_u32(p + 208);
l_ad = _get_u32(p + 212);
/* check for integer overflow */
if ((uint64_t)l_ea + (uint64_t)l_ad + (uint64_t)216 >= (uint64_t)1<<32) {
ecma_error("invalid file entry\n");
return NULL;
}
return _decode_file_entry(p, size, partition, l_ad, 216 + l_ea);
}
void free_file_entry(struct file_entry **p_fe)
{
if (p_fe) {
free(*p_fe);
*p_fe = NULL;
}
}