blob: d6d428b53a9d064bfe9ff1a9a160589ae158a468 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "base/algorithm.h"
#include "base/dcdir.h"
#include "base/dcdir_structs.h"
#include "base/xalloc.h"
#include "drivers/flash/flash.h"
int dcdir_open_root(DcDir *dcdir, StorageOps *storage, uint32_t anchor_offset)
{
DcDirAnchor anchor;
if (storage_read(storage, &anchor, anchor_offset, sizeof(anchor)))
return 1;
if (memcmp(anchor.signature, DcDirAnchorSignature,
DcDirAnchorSignatureSize)) {
printf("DcDir anchor signature mismatch.\n");
return 1;
}
if (anchor.major_version != 1) {
printf("Incompatible DcDir version %d.\n",
anchor.major_version);
return 1;
}
if (anchor.anchor_offset != anchor_offset) {
printf("DcDir anchor offset mismatch. "
"Expected %#x, found %#x.\n",
anchor_offset, anchor.anchor_offset);
return 1;
}
dcdir->offset = anchor_offset + sizeof(DcDirAnchor);
dcdir->base = anchor.root_base;
return 0;
}
static DcDirPointer *dcdir_find_in_dir(DcDir *dir, StorageOps *storage,
const char *name)
{
DcDirDirectoryHeader header;
if (storage_read(storage, &header, dir->offset, sizeof(header)))
return NULL;
if (memcmp(header.signature, DcDirDirectorySignature,
DcDirDirectorySignatureSize)) {
printf("DcDir directory signature mismatch.\n");
return NULL;
}
int size = ((header.size[0] << 0) +
(header.size[1] << 8) +
(header.size[2] << 16) + 1) * 8;
size -= sizeof(header);
if (size < 0) {
printf("Malformed DcDir directory found when looking up %s.\n",
name);
return NULL;
}
void *pointers = xmalloc(size);
if (storage_read(storage, pointers, dir->offset + sizeof(header),
size)) {
free(pointers);
return NULL;
}
while (size > 0) {
uint64_t *ptr_name = pointers;
pointers = (uint8_t *)pointers + sizeof(*ptr_name);
DcDirPointer *gen_ptr = pointers;
int ptr_size = (gen_ptr->size + 1) * 8;
size_t name_len = MIN(strlen(name), sizeof(*ptr_name));
if (!memcmp(name, ptr_name, name_len)) {
void *buf = xmalloc(ptr_size);
memcpy(buf, gen_ptr, ptr_size);
free(pointers);
return buf;
}
pointers = (uint8_t *)pointers + ptr_size;
size -= ptr_size;
}
free(pointers);
return NULL;
}
int dcdir_open_dir_raw(DcDir *dcdir, DcDirRegion *raw,
StorageOps *storage, DcDir *parent_dir,
const char *name)
{
DcDirPointer *ptr_ptr = dcdir_find_in_dir(parent_dir, storage, name);
if (!ptr_ptr)
return 1;
int directory = ptr_ptr->type & 0x1;
int type = ptr_ptr->type >> 1;
if (!directory) {
free(ptr_ptr);
printf("DcDir region is not a directory.\n");
return 1;
}
uint32_t parent_offset = parent_dir->offset - parent_dir->base;
switch (type) {
case DcDirOffset24Length24:
{
DcDirPointerOffset24Length24 *ptr1 =
(DcDirPointerOffset24Length24 *)ptr_ptr;
dcdir->offset = parent_offset +
(ptr1->offset[0] << 0) +
(ptr1->offset[1] << 8) +
(ptr1->offset[2] << 16);
dcdir->base = 0;
raw->size =
(ptr1->length[0] << 0) +
(ptr1->length[1] << 8) +
(ptr1->length[2] << 16) + 1;
}
break;
case DcDirBase32Offset32Length32:
{
DcDirPointerBase32Offset32Length32 *ptr2 =
(DcDirPointerBase32Offset32Length32 *)ptr_ptr;
dcdir->offset = parent_offset + ptr2->offset + ptr2->base;
dcdir->base = ptr2->base;
raw->size = ptr2->length + 1;
}
break;
default:
free(ptr_ptr);
printf("Unrecognized dcdir pointer type.");
return 1;
}
raw->offset = dcdir->offset;
free(ptr_ptr);
return 0;
}
int dcdir_open_dir(DcDir *dcdir, StorageOps *storage, DcDir *parent_dir,
const char *name)
{
DcDirRegion raw;
return dcdir_open_dir_raw(dcdir, &raw, storage, parent_dir, name);
}
int dcdir_open_region(DcDirRegion *region, StorageOps *storage,
DcDir *parent_dir, const char *name)
{
DcDirPointer *ptr_ptr = dcdir_find_in_dir(parent_dir, storage, name);
if (!ptr_ptr)
return 1;
int directory = ptr_ptr->type & 0x1;
int type = ptr_ptr->type >> 1;
if (directory) {
free(ptr_ptr);
printf("DcDir region is a directory.\n");
return 1;
}
uint32_t parent_offset = parent_dir->offset - parent_dir->base;
switch (type) {
case 0x1:
{
DcDirPointerOffset24Length24 *ptr1 =
(DcDirPointerOffset24Length24 *)ptr_ptr;
region->offset = parent_offset +
(ptr1->offset[0] << 0) +
(ptr1->offset[1] << 8) +
(ptr1->offset[2] << 16);
region->size =
(ptr1->length[0] << 0) +
(ptr1->length[1] << 8) +
(ptr1->length[2] << 16) + 1;
}
break;
case 0x2:
{
DcDirPointerBase32Offset32Length32 *ptr2 =
(DcDirPointerBase32Offset32Length32 *)ptr_ptr;
region->offset = parent_offset + ptr2->offset;
region->size = ptr2->length + 1;
}
break;
default:
free(ptr_ptr);
printf("Unrecognized dcdir pointer type.");
return 1;
}
free(ptr_ptr);
return 0;
}