HelenOS sources
This source file includes following definitions.
- udf_long_ad_to_pos
- udf_volume_recongnition_structure_test
- udf_volume_recongnition
- udf_prepare_tag
- udf_get_anchor_volume_descriptor_by_ssize
- udf_get_anchor_volume_descriptor
- udf_check_prevailing_pvd
- udf_check_prevailing_lvd
- udf_check_prevailing_pd
- udf_read_virtual_partition
- udf_find_partition
- udf_fill_volume_info
- udf_read_volume_descriptor_sequence
#include <byteorder.h>
#include <block.h>
#include <libfs.h>
#include <errno.h>
#include <stdlib.h>
#include <str.h>
#include <mem.h>
#include <inttypes.h>
#include <io/log.h>
#include "udf.h"
#include "udf_volume.h"
#include "udf_osta.h"
#include "udf_cksum.h"
#include "udf_file.h"
fs_index_t udf_long_ad_to_pos(udf_instance_t *instance, udf_long_ad_t *long_ad)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "Long_Ad to Pos: "
"partition_num=%" PRIu16 ", partition_block=%" PRIu32,
FLE16(long_ad->location.partition_num),
FLE32(long_ad->location.lblock_num));
return instance->partitions[
FLE16(long_ad->location.partition_num)].start +
FLE32(long_ad->location.lblock_num);
}
static errno_t udf_volume_recongnition_structure_test(service_id_t service_id,
aoff64_t addr, udf_vrs_descriptor_t *vd)
{
return block_read_bytes_direct(service_id, addr,
sizeof(udf_vrs_descriptor_t), vd);
}
errno_t udf_volume_recongnition(service_id_t service_id)
{
aoff64_t addr = VRS_ADDR;
bool nsr_found = false;
udf_vrs_descriptor_t *vd = malloc(sizeof(udf_vrs_descriptor_t));
if (!vd)
return ENOMEM;
errno_t rc = udf_volume_recongnition_structure_test(service_id, addr, vd);
if (rc != EOK) {
free(vd);
return rc;
}
for (size_t i = 0; i < VRS_DEPTH; i++) {
addr += sizeof(udf_vrs_descriptor_t);
rc = udf_volume_recongnition_structure_test(service_id, addr, vd);
if (rc != EOK) {
free(vd);
return rc;
}
if ((str_lcmp(VRS_NSR2, (char *) vd->identifier, VRS_ID_LEN) == 0) ||
(str_lcmp(VRS_NSR3, (char *) vd->identifier, VRS_ID_LEN) == 0)) {
nsr_found = true;
log_msg(LOG_DEFAULT, LVL_DEBUG, "VRS: NSR found");
continue;
}
if (str_lcmp(VRS_END, (char *) vd->identifier, VRS_ID_LEN) == 0) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "VRS: end found");
break;
}
}
free(vd);
if (nsr_found)
return EOK;
else
return EINVAL;
}
static void udf_prepare_tag(udf_descriptor_tag_t *tag)
{
GET_LE16(tag->id);
GET_LE16(tag->version);
GET_LE16(tag->serial);
GET_LE16(tag->descriptor_crc);
GET_LE16(tag->descriptor_crc_length);
GET_LE32(tag->location);
}
static errno_t udf_get_anchor_volume_descriptor_by_ssize(service_id_t service_id,
udf_anchor_volume_descriptor_t *avd, uint32_t sector_size)
{
errno_t rc = block_read_bytes_direct(service_id,
UDF_AVDP_SECTOR * sector_size,
sizeof(udf_anchor_volume_descriptor_t), avd);
if (rc != EOK)
return rc;
if (avd->tag.checksum != udf_tag_checksum((uint8_t *) &avd->tag))
return EINVAL;
udf_prepare_tag(&avd->tag);
if (avd->tag.id != UDF_TAG_AVDP)
return EINVAL;
GET_LE32(avd->main_extent.length);
GET_LE32(avd->main_extent.location);
GET_LE32(avd->reserve_extent.length);
GET_LE32(avd->reserve_extent.location);
return EOK;
}
errno_t udf_get_anchor_volume_descriptor(service_id_t service_id,
udf_anchor_volume_descriptor_t *avd)
{
uint32_t default_sector_size[] = { 512, 1024, 2048, 4096, 8192, 0 };
udf_instance_t *instance;
errno_t rc = fs_instance_get(service_id, (void **) &instance);
if (rc != EOK)
return rc;
if (instance->sector_size) {
return udf_get_anchor_volume_descriptor_by_ssize(service_id, avd,
instance->sector_size);
} else {
size_t i = 0;
while (default_sector_size[i] != 0) {
rc = udf_get_anchor_volume_descriptor_by_ssize(service_id, avd,
default_sector_size[i]);
if (rc == EOK) {
instance->sector_size = default_sector_size[i];
return EOK;
}
i++;
}
}
return EINVAL;
}
static bool udf_check_prevailing_pvd(udf_primary_volume_descriptor_t *pvd,
size_t cnt, udf_primary_volume_descriptor_t *desc)
{
for (size_t i = 0; i < cnt; i++) {
if ((memcmp((uint8_t *) pvd[i].volume_id,
(uint8_t *) desc->volume_id, 32) == 0) &&
(memcmp((uint8_t *) pvd[i].volume_set_id,
(uint8_t *) desc->volume_set_id, 128) == 0) &&
(memcmp((uint8_t *) &pvd[i].descriptor_charset,
(uint8_t *) &desc->descriptor_charset, 64) == 0) &&
(FLE32(desc->sequence_number) > FLE32(pvd[i].sequence_number))) {
memcpy(&pvd[i], desc, sizeof(udf_primary_volume_descriptor_t));
return true;
}
}
return false;
}
static bool udf_check_prevailing_lvd(udf_logical_volume_descriptor_t *lvd,
size_t cnt, udf_logical_volume_descriptor_t *desc)
{
for (size_t i = 0; i < cnt; i++) {
if ((memcmp((uint8_t *) lvd[i].logical_volume_id,
(uint8_t *) desc->logical_volume_id, 128) == 0) &&
(memcmp((uint8_t *) &lvd[i].charset,
(uint8_t *) &desc->charset, 64) == 0) &&
(FLE32(desc->sequence_number) > FLE32(lvd[i].sequence_number))) {
memcpy(&lvd[i], desc, sizeof(udf_logical_volume_descriptor_t));
return true;
}
}
return false;
}
static bool udf_check_prevailing_pd(udf_partition_descriptor_t *pd, size_t cnt,
udf_partition_descriptor_t *desc)
{
for (size_t i = 0; i < cnt; i++) {
if ((FLE16(pd[i].number) == FLE16(desc->number)) &&
(FLE32(desc->sequence_number) > FLE32(pd[i].sequence_number))) {
memcpy(&pd[i], desc, sizeof(udf_partition_descriptor_t));
return true;
}
}
return false;
}
static errno_t udf_read_virtual_partition(udf_instance_t *instance, uint32_t pos,
uint32_t id)
{
block_t *block = NULL;
errno_t rc = block_get(&block, instance->service_id, pos,
BLOCK_FLAGS_NONE);
if (rc != EOK)
return rc;
udf_descriptor_tag_t *desc = (udf_descriptor_tag_t *) (block->data);
if (desc->checksum != udf_tag_checksum((uint8_t *) desc)) {
block_put(block);
return EINVAL;
}
switch (FLE16(desc->id)) {
case UDF_FILE_ENTRY:
log_msg(LOG_DEFAULT, LVL_DEBUG, "ICB: File entry descriptor found");
udf_file_entry_descriptor_t *fed =
(udf_file_entry_descriptor_t *) block->data;
uint32_t start_alloc = FLE32(fed->ea_length) + UDF_FE_OFFSET;
udf_short_ad_t *short_d =
(udf_short_ad_t *) ((uint8_t *) fed + start_alloc);
instance->partitions[id].start = FLE32(short_d->position);
instance->partitions[id].length = FLE32(short_d->length);
break;
case UDF_EFILE_ENTRY:
log_msg(LOG_DEFAULT, LVL_DEBUG, "ICB: Extended file entry descriptor found");
udf_extended_file_entry_descriptor_t *efed =
(udf_extended_file_entry_descriptor_t *) block->data;
start_alloc = FLE32(efed->ea_length) + UDF_EFE_OFFSET;
short_d = (udf_short_ad_t *) ((uint8_t *) efed + start_alloc);
instance->partitions[id].start = FLE32(short_d->position);
instance->partitions[id].length = FLE32(short_d->length);
break;
}
return block_put(block);
}
static size_t udf_find_partition(udf_partition_descriptor_t *pd, size_t pd_cnt,
size_t id)
{
for (size_t i = 0; i < pd_cnt; i++) {
if (FLE16(pd[i].number) == id)
return i;
}
return (size_t) -1;
}
static errno_t udf_fill_volume_info(udf_logical_volume_descriptor_t *lvd,
size_t lvd_cnt, udf_partition_descriptor_t *pd, size_t pd_cnt,
udf_instance_t *instance)
{
instance->volumes = calloc(lvd_cnt, sizeof(udf_lvolume_t));
if (instance->volumes == NULL)
return ENOMEM;
instance->partitions = calloc(pd_cnt, sizeof(udf_partition_t));
if (instance->partitions == NULL) {
free(instance->volumes);
return ENOMEM;
}
instance->partition_cnt = pd_cnt;
size_t vir_pd_cnt = 0;
for (size_t i = 0; i < lvd_cnt; i++) {
instance->volumes[i].partitions =
calloc(FLE32(lvd[i].number_of_partitions_maps),
sizeof(udf_partition_t *));
if (instance->volumes[i].partitions == NULL) {
return ENOMEM;
}
instance->volumes[i].partition_cnt = 0;
instance->volumes[i].logical_block_size =
FLE32(lvd[i].logical_block_size);
uint8_t *idx = lvd[i].partition_map;
for (size_t j = 0; j < FLE32(lvd[i].number_of_partitions_maps);
j++) {
udf_type1_partition_map_t *pm1 =
(udf_type1_partition_map_t *) idx;
if (pm1->partition_map_type == 1) {
size_t pd_num = udf_find_partition(pd, pd_cnt,
FLE16(pm1->partition_number));
if (pd_num == (size_t) -1) {
return ENOENT;
}
instance->partitions[j].access_type =
FLE32(pd[pd_num].access_type);
instance->partitions[j].length =
FLE32(pd[pd_num].length);
instance->partitions[j].number =
FLE16(pm1->partition_number);
instance->partitions[j].start =
FLE32(pd[pd_num].starting_location);
instance->volumes[i].partitions[
instance->volumes[i].partition_cnt] =
&instance->partitions[j];
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume[%" PRIun "]: partition [type %u] "
"found and filled", i, pm1->partition_map_type);
instance->volumes[i].partition_cnt++;
idx += pm1->partition_map_length;
continue;
}
udf_type2_partition_map_t *pm2 =
(udf_type2_partition_map_t *) idx;
if (pm2->partition_map_type == 2) {
udf_metadata_partition_map_t *metadata =
(udf_metadata_partition_map_t *) idx;
log_msg(LOG_DEFAULT, LVL_DEBUG, "Metadata file location=%u",
FLE32(metadata->metadata_fileloc));
vir_pd_cnt++;
instance->partitions = realloc(instance->partitions,
(pd_cnt + vir_pd_cnt) * sizeof(udf_partition_t));
if (instance->partitions == NULL) {
return ENOMEM;
}
instance->partition_cnt++;
size_t pd_num = udf_find_partition(pd, pd_cnt,
FLE16(metadata->partition_number));
if (pd_num == (size_t) -1) {
return ENOENT;
}
instance->partitions[j].number =
FLE16(metadata->partition_number);
errno_t rc = udf_read_virtual_partition(instance,
FLE32(metadata->metadata_fileloc) +
FLE32(pd[pd_num].starting_location), j);
if (rc != EOK) {
return rc;
}
instance->partitions[j].start +=
FLE32(pd[pd_num].starting_location);
instance->volumes[i].partitions[
instance->volumes[i].partition_cnt] =
&instance->partitions[j];
log_msg(LOG_DEFAULT, LVL_DEBUG, "Virtual partition: num=%d, start=%d",
instance->partitions[j].number,
instance->partitions[j].start);
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume[%" PRIun "]: partition [type %u] "
"found and filled", i, pm2->partition_map_type);
instance->volumes[i].partition_cnt++;
idx += metadata->partition_map_length;
continue;
}
udf_general_type_t *pm = (udf_general_type_t *) idx;
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume[%" PRIun "]: partition [type %u] "
"found and skipped", i, pm->partition_map_type);
idx += pm->partition_map_length;
}
}
return EOK;
}
errno_t udf_read_volume_descriptor_sequence(service_id_t service_id,
udf_extent_t addr)
{
udf_instance_t *instance;
errno_t rc = fs_instance_get(service_id, (void **) &instance);
if (rc != EOK)
return rc;
aoff64_t pos = addr.location;
aoff64_t end = pos + (addr.length / instance->sector_size) - 1;
if (pos == end)
return EINVAL;
size_t max_descriptors = ALL_UP(addr.length, instance->sector_size);
udf_primary_volume_descriptor_t *pvd = calloc(max_descriptors,
sizeof(udf_primary_volume_descriptor_t));
if (pvd == NULL)
return ENOMEM;
udf_logical_volume_descriptor_t *lvd = calloc(max_descriptors,
instance->sector_size);
if (lvd == NULL) {
free(pvd);
return ENOMEM;
}
udf_partition_descriptor_t *pd = calloc(max_descriptors,
sizeof(udf_partition_descriptor_t));
if (pd == NULL) {
free(pvd);
free(lvd);
return ENOMEM;
}
size_t pvd_cnt = 0;
size_t lvd_cnt = 0;
size_t pd_cnt = 0;
while (pos <= end) {
block_t *block = NULL;
rc = block_get(&block, service_id, pos, BLOCK_FLAGS_NONE);
if (rc != EOK) {
free(pvd);
free(lvd);
free(pd);
return rc;
}
udf_volume_descriptor_t *vol =
(udf_volume_descriptor_t *) block->data;
switch (FLE16(vol->common.tag.id)) {
case UDF_TAG_PVD:
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume: Primary volume descriptor found");
if (!udf_check_prevailing_pvd(pvd, pvd_cnt, &vol->volume)) {
memcpy(&pvd[pvd_cnt], &vol->volume,
sizeof(udf_primary_volume_descriptor_t));
pvd_cnt++;
}
pos++;
break;
case UDF_TAG_VDP:
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume: Volume descriptor pointer found");
pos++;
break;
case UDF_TAG_IUVD:
log_msg(LOG_DEFAULT, LVL_DEBUG,
"Volume: Implementation use volume descriptor found");
pos++;
break;
case UDF_TAG_PD:
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume: Partition descriptor found");
log_msg(LOG_DEFAULT, LVL_DEBUG, "Partition number: %u, contents: '%.6s', "
"access type: %" PRIu32, FLE16(vol->partition.number),
vol->partition.contents.id, FLE32(vol->partition.access_type));
log_msg(LOG_DEFAULT, LVL_DEBUG, "Partition start: %" PRIu32 " (sector), "
"size: %" PRIu32 " (sectors)",
FLE32(vol->partition.starting_location),
FLE32(vol->partition.length));
if (!udf_check_prevailing_pd(pd, pd_cnt, &vol->partition)) {
memcpy(&pd[pd_cnt], &vol->partition,
sizeof(udf_partition_descriptor_t));
pd_cnt++;
}
udf_partition_header_descriptor_t *phd =
(udf_partition_header_descriptor_t *) vol->partition.contents_use;
if (FLE32(phd->unallocated_space_table.length)) {
log_msg(LOG_DEFAULT, LVL_DEBUG,
"space table: length=%" PRIu32 ", pos=%" PRIu32,
FLE32(phd->unallocated_space_table.length),
FLE32(phd->unallocated_space_table.position));
instance->space_type = SPACE_TABLE;
instance->uaspace_start =
FLE32(vol->partition.starting_location) +
FLE32(phd->unallocated_space_table.position);
instance->uaspace_length =
FLE32(phd->unallocated_space_table.length);
}
if (FLE32(phd->unallocated_space_bitmap.length)) {
log_msg(LOG_DEFAULT, LVL_DEBUG,
"space bitmap: length=%" PRIu32 ", pos=%" PRIu32,
FLE32(phd->unallocated_space_bitmap.length),
FLE32(phd->unallocated_space_bitmap.position));
instance->space_type = SPACE_BITMAP;
instance->uaspace_start =
FLE32(vol->partition.starting_location) +
FLE32(phd->unallocated_space_bitmap.position);
instance->uaspace_length =
FLE32(phd->unallocated_space_bitmap.length);
}
pos++;
break;
case UDF_TAG_LVD:
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume: Logical volume descriptor found");
aoff64_t sct =
ALL_UP((sizeof(udf_logical_volume_descriptor_t) +
FLE32(vol->logical.map_table_length)),
sizeof(udf_common_descriptor_t));
pos += sct;
char tmp[130];
udf_to_unix_name(tmp, 129,
(char *) vol->logical.logical_volume_id, 128,
&vol->logical.charset);
log_msg(LOG_DEFAULT, LVL_DEBUG, "Logical Volume ID: '%s', "
"logical block size: %" PRIu32 " (bytes)", tmp,
FLE32(vol->logical.logical_block_size));
log_msg(LOG_DEFAULT, LVL_DEBUG, "Map table size: %" PRIu32 " (bytes), "
"number of partition maps: %" PRIu32,
FLE32(vol->logical.map_table_length),
FLE32(vol->logical.number_of_partitions_maps));
if (!udf_check_prevailing_lvd(lvd, lvd_cnt, &vol->logical)) {
memcpy(&lvd[lvd_cnt], &vol->logical,
sizeof(udf_logical_volume_descriptor_t) +
FLE32(vol->logical.map_table_length));
lvd_cnt++;
}
break;
case UDF_TAG_USD:
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume: Unallocated space descriptor found");
sct = ALL_UP((sizeof(udf_unallocated_space_descriptor_t) +
FLE32(vol->unallocated.allocation_descriptors_num) *
sizeof(udf_extent_t)), sizeof(udf_common_descriptor_t));
instance->uaspace_start = pos;
instance->uaspace_length = sct;
instance->uasd = (udf_unallocated_space_descriptor_t *)
malloc(sct * instance->sector_size);
if (instance->uasd == NULL) {
return ENOMEM;
}
memcpy(instance->uasd, block->data, instance->sector_size);
pos += sct;
break;
case UDF_TAG_LVID:
log_msg(LOG_DEFAULT, LVL_DEBUG,
"Volume: Logical volume integrity descriptor found");
pos++;
break;
case UDF_TAG_TD:
log_msg(LOG_DEFAULT, LVL_DEBUG, "Volume: Terminating descriptor found");
pos = end + 1;
break;
default:
pos++;
}
rc = block_put(block);
if (rc != EOK) {
free(pvd);
free(lvd);
free(pd);
return rc;
}
}
udf_fill_volume_info(lvd, lvd_cnt, pd, pd_cnt, instance);
for (size_t i = 0; i < lvd_cnt; i++) {
pos = udf_long_ad_to_pos(instance,
(udf_long_ad_t *) &lvd[i].logical_volume_conents_use);
block_t *block = NULL;
rc = block_get(&block, instance->service_id, pos,
BLOCK_FLAGS_NONE);
if (rc != EOK) {
return rc;
}
udf_descriptor_tag_t *desc = block->data;
log_msg(LOG_DEFAULT, LVL_DEBUG, "First tag ID=%" PRIu16, desc->id);
if (desc->checksum != udf_tag_checksum((uint8_t *) desc)) {
return EINVAL;
}
udf_prepare_tag(desc);
udf_fileset_descriptor_t *fd = block->data;
memcpy((uint8_t *) &instance->charset,
(uint8_t *) &fd->fileset_charset, sizeof(fd->fileset_charset));
instance->volumes[i].root_dir = udf_long_ad_to_pos(instance,
&fd->root_dir_icb);
}
free(pvd);
free(lvd);
free(pd);
return EOK;
}
HelenOS homepage, sources at GitHub