Skip to content

Commit

Permalink
chunk-format: parse trailing table of contents
Browse files Browse the repository at this point in the history
The new read_trailing_table_of_contents() mimics
read_table_of_contents() except that it reads the table of contents in
reverse from the end of the given hashfile. The file is given as a
memory-mapped section of memory and a size. Automatically calculate the
start of the trailing hash and read the table of contents in revers from
that position.

The errors come along from those in read_table_of_contents(). The one
exception is that the chunk_offset cannot be checked as going into the
table of contents since we do not have that length automatically. That
may have some surprising results for some narrow forms of corruption.
However, we do still limit the size to the size of the file plus the
part of the table of contents read so far. At minimum, the given sizes
can be used to limit parsing within the file itself.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
  • Loading branch information
derrickstolee committed Nov 1, 2022
1 parent 7fca0e5 commit d198fd8
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 0 deletions.
53 changes: 53 additions & 0 deletions chunk-format.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,59 @@ int read_table_of_contents(struct chunkfile *cf,
return 0;
}

int read_trailing_table_of_contents(struct chunkfile *cf,
const unsigned char *mfile,
size_t mfile_size)
{
int i;
uint32_t chunk_id;
const unsigned char *table_of_contents = mfile + mfile_size - the_hash_algo->rawsz;

while (1) {
uint64_t chunk_offset;

table_of_contents -= CHUNK_TOC_ENTRY_SIZE;

chunk_id = get_be32(table_of_contents);
chunk_offset = get_be64(table_of_contents + 4);

/* Calculate the previous chunk size, if it exists. */
if (cf->chunks_nr) {
off_t previous_offset = cf->chunks[cf->chunks_nr - 1].offset;

if (chunk_offset < previous_offset ||
chunk_offset > table_of_contents - mfile) {
error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),
previous_offset, chunk_offset);
return -1;
}

cf->chunks[cf->chunks_nr - 1].size = chunk_offset - previous_offset;
}

/* Stop at the null chunk. We only need it for the last size. */
if (!chunk_id)
break;

for (i = 0; i < cf->chunks_nr; i++) {
if (cf->chunks[i].id == chunk_id) {
error(_("duplicate chunk ID %"PRIx32" found"),
chunk_id);
return -1;
}
}

ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);

cf->chunks[cf->chunks_nr].id = chunk_id;
cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;
cf->chunks[cf->chunks_nr].offset = chunk_offset;
cf->chunks_nr++;
}

return 0;
}

static int pair_chunk_fn(const unsigned char *chunk_start,
size_t chunk_size,
void *data)
Expand Down
9 changes: 9 additions & 0 deletions chunk-format.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ int read_table_of_contents(struct chunkfile *cf,
uint64_t toc_offset,
int toc_length);

/**
* Read the given chunkfile, but read the table of contents from the
* end of the given mfile. The file is expected to be a hashfile with
* the_hash_file->rawsz bytes at the end storing the hash.
*/
int read_trailing_table_of_contents(struct chunkfile *cf,
const unsigned char *mfile,
size_t mfile_size);

#define CHUNK_NOT_FOUND (-2)

/*
Expand Down

0 comments on commit d198fd8

Please sign in to comment.