| /* | |
| * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. | |
| * | |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA | |
| * 02110-1301, USA. | |
| * | |
| * You can also choose to distribute this program under the terms of | |
| * the Unmodified Binary Distribution Licence (as given in the file | |
| * COPYING.UBDL), provided that you have satisfied its requirements. | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #include <errno.h> | |
| #include <assert.h> | |
| #include <ipxe/sha256.h> | |
| #include <ipxe/sha512.h> | |
| #include <ipxe/hmac.h> | |
| #include <ipxe/base16.h> | |
| #include <ipxe/pccrc.h> | |
| /** @file | |
| * | |
| * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] | |
| * | |
| */ | |
| /****************************************************************************** | |
| * | |
| * Utility functions | |
| * | |
| ****************************************************************************** | |
| */ | |
| /** | |
| * Transcribe hash value (for debugging) | |
| * | |
| * @v info Content information | |
| * @v hash Hash value | |
| * @ret string Hash value string | |
| */ | |
| static inline const char * | |
| peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) { | |
| static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ]; | |
| size_t digestsize = info->digestsize; | |
| /* Sanity check */ | |
| assert ( info != NULL ); | |
| assert ( digestsize != 0 ); | |
| assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) ); | |
| /* Transcribe hash value */ | |
| base16_encode ( hash, digestsize, buf, sizeof ( buf ) ); | |
| return buf; | |
| } | |
| /** | |
| * Get raw data | |
| * | |
| * @v info Content information | |
| * @v data Data buffer | |
| * @v offset Starting offset | |
| * @v len Length | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_get ( const struct peerdist_info *info, void *data, | |
| size_t offset, size_t len ) { | |
| /* Sanity check */ | |
| if ( ( offset > info->raw.len ) || | |
| ( len > ( info->raw.len - offset ) ) ) { | |
| DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n", | |
| info, offset, ( offset + len ), info->raw.len ); | |
| return -ERANGE; | |
| } | |
| /* Copy data */ | |
| memcpy ( data, ( info->raw.data + offset ), len ); | |
| return 0; | |
| } | |
| /** | |
| * Populate segment hashes | |
| * | |
| * @v segment Content information segment to fill in | |
| * @v hash Segment hash of data | |
| * @v secret Segment secret | |
| */ | |
| static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, | |
| const void *hash, const void *secret ){ | |
| const struct peerdist_info *info = segment->info; | |
| struct digest_algorithm *digest = info->digest; | |
| uint8_t ctx[ hmac_ctxsize ( digest ) ]; | |
| size_t digestsize = info->digestsize; | |
| static const uint8_t magic[] = PEERDIST_SEGMENT_ID_MAGIC; | |
| /* Sanity check */ | |
| assert ( digestsize <= sizeof ( segment->hash ) ); | |
| assert ( digestsize <= sizeof ( segment->secret ) ); | |
| assert ( digestsize <= sizeof ( segment->id ) ); | |
| /* Get segment hash of data */ | |
| memcpy ( segment->hash, hash, digestsize ); | |
| /* Get segment secret */ | |
| memcpy ( segment->secret, secret, digestsize ); | |
| /* Calculate segment identifier */ | |
| hmac_init ( digest, ctx, segment->secret, digestsize ); | |
| hmac_update ( digest, ctx, segment->hash, digestsize ); | |
| hmac_update ( digest, ctx, magic, sizeof ( magic ) ); | |
| hmac_final ( digest, ctx, segment->id ); | |
| } | |
| /****************************************************************************** | |
| * | |
| * Content Information version 1 | |
| * | |
| ****************************************************************************** | |
| */ | |
| /** | |
| * Get number of blocks within a block description | |
| * | |
| * @v info Content information | |
| * @v offset Block description offset | |
| * @ret blocks Number of blocks, or negative error | |
| */ | |
| static int peerdist_info_v1_blocks ( const struct peerdist_info *info, | |
| size_t offset ) { | |
| struct peerdist_info_v1_block raw; | |
| unsigned int blocks; | |
| int rc; | |
| /* Get block description header */ | |
| if ( ( rc = peerdist_info_get ( info, &raw, offset, | |
| sizeof ( raw ) ) ) != 0 ) | |
| return rc; | |
| /* Calculate number of blocks */ | |
| blocks = le32_to_cpu ( raw.blocks ); | |
| return blocks; | |
| } | |
| /** | |
| * Locate block description | |
| * | |
| * @v info Content information | |
| * @v index Segment index | |
| * @ret offset Block description offset, or negative error | |
| */ | |
| static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info, | |
| unsigned int index ) { | |
| size_t digestsize = info->digestsize; | |
| unsigned int i; | |
| size_t offset; | |
| int blocks; | |
| int rc; | |
| /* Sanity check */ | |
| assert ( index < info->segments ); | |
| /* Calculate offset of first block description */ | |
| offset = ( sizeof ( struct peerdist_info_v1 ) + | |
| ( info->segments * | |
| sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) ); | |
| /* Iterate over block descriptions until we find this segment */ | |
| for ( i = 0 ; i < index ; i++ ) { | |
| /* Get number of blocks */ | |
| blocks = peerdist_info_v1_blocks ( info, offset ); | |
| if ( blocks < 0 ) { | |
| rc = blocks; | |
| DBGC ( info, "PCCRC %p segment %d could not get number " | |
| "of blocks: %s\n", info, i, strerror ( rc ) ); | |
| return rc; | |
| } | |
| /* Move to next block description */ | |
| offset += sizeof ( peerdist_info_v1_block_t ( digestsize, | |
| blocks ) ); | |
| } | |
| return offset; | |
| } | |
| /** | |
| * Populate content information | |
| * | |
| * @v info Content information to fill in | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_v1 ( struct peerdist_info *info ) { | |
| struct peerdist_info_v1 raw; | |
| struct peerdist_info_segment first; | |
| struct peerdist_info_segment last; | |
| size_t first_skip; | |
| size_t last_skip; | |
| size_t last_read; | |
| int rc; | |
| /* Get raw header */ | |
| if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ | |
| DBGC ( info, "PCCRC %p could not get V1 content information: " | |
| "%s\n", info, strerror ( rc ) ); | |
| return rc; | |
| } | |
| assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) ); | |
| /* Determine hash algorithm */ | |
| switch ( raw.hash ) { | |
| case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) : | |
| info->digest = &sha256_algorithm; | |
| break; | |
| case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) : | |
| info->digest = &sha384_algorithm; | |
| break; | |
| case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) : | |
| info->digest = &sha512_algorithm; | |
| break; | |
| default: | |
| DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n", | |
| info, le32_to_cpu ( raw.hash ) ); | |
| return -ENOTSUP; | |
| } | |
| info->digestsize = info->digest->digestsize; | |
| assert ( info->digest != NULL ); | |
| DBGC2 ( info, "PCCRC %p using %s[%zd]\n", | |
| info, info->digest->name, ( info->digestsize * 8 ) ); | |
| /* Calculate number of segments */ | |
| info->segments = le32_to_cpu ( raw.segments ); | |
| /* Get first segment */ | |
| if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 ) | |
| return rc; | |
| /* Calculate range start offset */ | |
| info->range.start = first.range.start; | |
| /* Calculate trimmed range start offset */ | |
| first_skip = le32_to_cpu ( raw.first ); | |
| info->trim.start = ( first.range.start + first_skip ); | |
| /* Get last segment */ | |
| if ( ( rc = peerdist_info_segment ( info, &last, | |
| ( info->segments - 1 ) ) ) != 0 ) | |
| return rc; | |
| /* Calculate range end offset */ | |
| info->range.end = last.range.end; | |
| /* Calculate trimmed range end offset */ | |
| if ( raw.last ) { | |
| /* Explicit length to include from last segment is given */ | |
| last_read = le32_to_cpu ( raw.last ); | |
| last_skip = ( last.index ? 0 : first_skip ); | |
| info->trim.end = ( last.range.start + last_skip + last_read ); | |
| } else { | |
| /* No explicit length given: range extends to end of segment */ | |
| info->trim.end = last.range.end; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Populate content information segment | |
| * | |
| * @v segment Content information segment to fill in | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) { | |
| const struct peerdist_info *info = segment->info; | |
| size_t digestsize = info->digestsize; | |
| peerdist_info_v1_segment_t ( digestsize ) raw; | |
| ssize_t raw_offset; | |
| int blocks; | |
| int rc; | |
| /* Sanity checks */ | |
| assert ( segment->index < info->segments ); | |
| /* Get raw description */ | |
| raw_offset = ( sizeof ( struct peerdist_info_v1 ) + | |
| ( segment->index * sizeof ( raw ) ) ); | |
| if ( ( rc = peerdist_info_get ( info, &raw, raw_offset, | |
| sizeof ( raw ) ) ) != 0 ) { | |
| DBGC ( info, "PCCRC %p segment %d could not get segment " | |
| "description: %s\n", info, segment->index, | |
| strerror ( rc ) ); | |
| return rc; | |
| } | |
| /* Calculate start offset of this segment */ | |
| segment->range.start = le64_to_cpu ( raw.segment.offset ); | |
| /* Calculate end offset of this segment */ | |
| segment->range.end = ( segment->range.start + | |
| le32_to_cpu ( raw.segment.len ) ); | |
| /* Calculate block size of this segment */ | |
| segment->blksize = le32_to_cpu ( raw.segment.blksize ); | |
| /* Locate block description for this segment */ | |
| raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); | |
| if ( raw_offset < 0 ) { | |
| rc = raw_offset; | |
| return rc; | |
| } | |
| /* Get number of blocks */ | |
| blocks = peerdist_info_v1_blocks ( info, raw_offset ); | |
| if ( blocks < 0 ) { | |
| rc = blocks; | |
| DBGC ( info, "PCCRC %p segment %d could not get number of " | |
| "blocks: %s\n", info, segment->index, strerror ( rc ) ); | |
| return rc; | |
| } | |
| segment->blocks = blocks; | |
| /* Calculate segment hashes */ | |
| peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); | |
| return 0; | |
| } | |
| /** | |
| * Populate content information block | |
| * | |
| * @v block Content information block to fill in | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_v1_block ( struct peerdist_info_block *block ) { | |
| const struct peerdist_info_segment *segment = block->segment; | |
| const struct peerdist_info *info = segment->info; | |
| size_t digestsize = info->digestsize; | |
| peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw; | |
| ssize_t raw_offset; | |
| int rc; | |
| /* Sanity checks */ | |
| assert ( block->index < segment->blocks ); | |
| /* Calculate start offset of this block */ | |
| block->range.start = ( segment->range.start + | |
| ( block->index * segment->blksize ) ); | |
| /* Calculate end offset of this block */ | |
| block->range.end = ( block->range.start + segment->blksize ); | |
| if ( block->range.end > segment->range.end ) | |
| block->range.end = segment->range.end; | |
| /* Locate block description */ | |
| raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); | |
| if ( raw_offset < 0 ) { | |
| rc = raw_offset; | |
| return rc; | |
| } | |
| /* Get block hash */ | |
| raw_offset += offsetof ( typeof ( raw ), hash[block->index] ); | |
| if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset, | |
| digestsize ) ) != 0 ) { | |
| DBGC ( info, "PCCRC %p segment %d block %d could not get " | |
| "hash: %s\n", info, segment->index, block->index, | |
| strerror ( rc ) ); | |
| return rc; | |
| } | |
| return 0; | |
| } | |
| /** Content information version 1 operations */ | |
| static struct peerdist_info_operations peerdist_info_v1_operations = { | |
| .info = peerdist_info_v1, | |
| .segment = peerdist_info_v1_segment, | |
| .block = peerdist_info_v1_block, | |
| }; | |
| /****************************************************************************** | |
| * | |
| * Content Information version 2 | |
| * | |
| ****************************************************************************** | |
| */ | |
| /** A segment cursor */ | |
| struct peerdist_info_v2_cursor { | |
| /** Raw data offset */ | |
| size_t offset; | |
| /** Number of segments remaining within this chunk */ | |
| unsigned int remaining; | |
| /** Accumulated segment length */ | |
| size_t len; | |
| }; | |
| /** | |
| * Initialise segment cursor | |
| * | |
| * @v cursor Segment cursor | |
| */ | |
| static inline void | |
| peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) { | |
| /* Initialise cursor */ | |
| cursor->offset = ( sizeof ( struct peerdist_info_v2 ) + | |
| sizeof ( struct peerdist_info_v2_chunk ) ); | |
| cursor->remaining = 0; | |
| cursor->len = 0; | |
| } | |
| /** | |
| * Update segment cursor to next segment description | |
| * | |
| * @v info Content information | |
| * @v offset Current offset | |
| * @v remaining Number of segments remaining within this chunk | |
| * @ret rc Return status code | |
| */ | |
| static int | |
| peerdist_info_v2_cursor_next ( const struct peerdist_info *info, | |
| struct peerdist_info_v2_cursor *cursor ) { | |
| size_t digestsize = info->digestsize; | |
| peerdist_info_v2_segment_t ( digestsize ) raw; | |
| struct peerdist_info_v2_chunk chunk; | |
| int rc; | |
| /* Get chunk description if applicable */ | |
| if ( ! cursor->remaining ) { | |
| /* Get chunk description */ | |
| if ( ( rc = peerdist_info_get ( info, &chunk, | |
| ( cursor->offset - | |
| sizeof ( chunk ) ), | |
| sizeof ( chunk ) ) ) != 0 ) | |
| return rc; | |
| /* Update number of segments remaining */ | |
| cursor->remaining = ( be32_to_cpu ( chunk.len ) / | |
| sizeof ( raw ) ); | |
| } | |
| /* Get segment description header */ | |
| if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset, | |
| sizeof ( raw.segment ) ) ) != 0 ) | |
| return rc; | |
| /* Update cursor */ | |
| cursor->offset += sizeof ( raw ); | |
| cursor->remaining--; | |
| if ( ! cursor->remaining ) | |
| cursor->offset += sizeof ( chunk ); | |
| cursor->len += be32_to_cpu ( raw.segment.len ); | |
| return 0; | |
| } | |
| /** | |
| * Get number of segments and total length | |
| * | |
| * @v info Content information | |
| * @v len Length to fill in | |
| * @ret rc Number of segments, or negative error | |
| */ | |
| static int peerdist_info_v2_segments ( const struct peerdist_info *info, | |
| size_t *len ) { | |
| struct peerdist_info_v2_cursor cursor; | |
| unsigned int segments; | |
| int rc; | |
| /* Iterate over all segments */ | |
| for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ; | |
| cursor.offset < info->raw.len ; segments++ ) { | |
| /* Update segment cursor */ | |
| if ( ( rc = peerdist_info_v2_cursor_next ( info, | |
| &cursor ) ) != 0 ) { | |
| DBGC ( info, "PCCRC %p segment %d could not update " | |
| "segment cursor: %s\n", | |
| info, segments, strerror ( rc ) ); | |
| return rc; | |
| } | |
| } | |
| /* Record accumulated length */ | |
| *len = cursor.len; | |
| return segments; | |
| } | |
| /** | |
| * Populate content information | |
| * | |
| * @v info Content information to fill in | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_v2 ( struct peerdist_info *info ) { | |
| struct peerdist_info_v2 raw; | |
| size_t len = 0; | |
| int segments; | |
| int rc; | |
| /* Get raw header */ | |
| if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ | |
| DBGC ( info, "PCCRC %p could not get V2 content information: " | |
| "%s\n", info, strerror ( rc ) ); | |
| return rc; | |
| } | |
| assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) ); | |
| /* Determine hash algorithm */ | |
| switch ( raw.hash ) { | |
| case PEERDIST_INFO_V2_HASH_SHA512_TRUNC : | |
| info->digest = &sha512_algorithm; | |
| info->digestsize = ( 256 / 8 ); | |
| break; | |
| default: | |
| DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n", | |
| info, raw.hash ); | |
| return -ENOTSUP; | |
| } | |
| assert ( info->digest != NULL ); | |
| DBGC2 ( info, "PCCRC %p using %s[%zd]\n", | |
| info, info->digest->name, ( info->digestsize * 8 ) ); | |
| /* Calculate number of segments and total length */ | |
| segments = peerdist_info_v2_segments ( info, &len ); | |
| if ( segments < 0 ) { | |
| rc = segments; | |
| DBGC ( info, "PCCRC %p could not get segment count and length: " | |
| "%s\n", info, strerror ( rc ) ); | |
| return rc; | |
| } | |
| info->segments = segments; | |
| /* Calculate range start offset */ | |
| info->range.start = be64_to_cpu ( raw.offset ); | |
| /* Calculate trimmed range start offset */ | |
| info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) ); | |
| /* Calculate range end offset */ | |
| info->range.end = ( info->range.start + len ); | |
| /* Calculate trimmed range end offset */ | |
| info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) : | |
| info->range.end ); | |
| return 0; | |
| } | |
| /** | |
| * Populate content information segment | |
| * | |
| * @v segment Content information segment to fill in | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) { | |
| const struct peerdist_info *info = segment->info; | |
| size_t digestsize = info->digestsize; | |
| peerdist_info_v2_segment_t ( digestsize ) raw; | |
| struct peerdist_info_v2_cursor cursor; | |
| unsigned int index; | |
| size_t len; | |
| int rc; | |
| /* Sanity checks */ | |
| assert ( segment->index < info->segments ); | |
| /* Iterate over all segments before the target segment */ | |
| for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ; | |
| index < segment->index ; index++ ) { | |
| /* Update segment cursor */ | |
| if ( ( rc = peerdist_info_v2_cursor_next ( info, | |
| &cursor ) ) != 0 ) { | |
| DBGC ( info, "PCCRC %p segment %d could not update " | |
| "segment cursor: %s\n", | |
| info, index, strerror ( rc ) ); | |
| return rc; | |
| } | |
| } | |
| /* Get raw description */ | |
| if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset, | |
| sizeof ( raw ) ) ) != 0 ) { | |
| DBGC ( info, "PCCRC %p segment %d could not get segment " | |
| "description: %s\n", | |
| info, segment->index, strerror ( rc ) ); | |
| return rc; | |
| } | |
| /* Calculate start offset of this segment */ | |
| segment->range.start = ( info->range.start + cursor.len ); | |
| /* Calculate end offset of this segment */ | |
| len = be32_to_cpu ( raw.segment.len ); | |
| segment->range.end = ( segment->range.start + len ); | |
| /* Model as a segment containing a single block */ | |
| segment->blocks = 1; | |
| segment->blksize = len; | |
| /* Calculate segment hashes */ | |
| peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); | |
| return 0; | |
| } | |
| /** | |
| * Populate content information block | |
| * | |
| * @v block Content information block to fill in | |
| * @ret rc Return status code | |
| */ | |
| static int peerdist_info_v2_block ( struct peerdist_info_block *block ) { | |
| const struct peerdist_info_segment *segment = block->segment; | |
| const struct peerdist_info *info = segment->info; | |
| size_t digestsize = info->digestsize; | |
| /* Sanity checks */ | |
| assert ( block->index < segment->blocks ); | |
| /* Model as a block covering the whole segment */ | |
| memcpy ( &block->range, &segment->range, sizeof ( block->range ) ); | |
| memcpy ( block->hash, segment->hash, digestsize ); | |
| return 0; | |
| } | |
| /** Content information version 2 operations */ | |
| static struct peerdist_info_operations peerdist_info_v2_operations = { | |
| .block = peerdist_info_v2_block, | |
| .segment = peerdist_info_v2_segment, | |
| .info = peerdist_info_v2, | |
| }; | |
| /****************************************************************************** | |
| * | |
| * Content Information | |
| * | |
| ****************************************************************************** | |
| */ | |
| /** | |
| * Populate content information | |
| * | |
| * @v data Raw data | |
| * @v len Length of raw data | |
| * @v info Content information to fill in | |
| * @ret rc Return status code | |
| */ | |
| int peerdist_info ( const void *data, size_t len, | |
| struct peerdist_info *info ) { | |
| union peerdist_info_version version; | |
| int rc; | |
| /* Initialise structure */ | |
| memset ( info, 0, sizeof ( *info ) ); | |
| info->raw.data = data; | |
| info->raw.len = len; | |
| /* Get version */ | |
| if ( ( rc = peerdist_info_get ( info, &version, 0, | |
| sizeof ( version ) ) ) != 0 ) { | |
| DBGC ( info, "PCCRC %p could not get version: %s\n", | |
| info, strerror ( rc ) ); | |
| return rc; | |
| } | |
| DBGC2 ( info, "PCCRC %p version %d.%d\n", | |
| info, version.major, version.minor ); | |
| /* Determine version */ | |
| switch ( version.raw ) { | |
| case cpu_to_le16 ( PEERDIST_INFO_V1 ) : | |
| info->op = &peerdist_info_v1_operations; | |
| break; | |
| case cpu_to_le16 ( PEERDIST_INFO_V2 ) : | |
| info->op = &peerdist_info_v2_operations; | |
| break; | |
| default: | |
| DBGC ( info, "PCCRC %p unsupported version %d.%d\n", | |
| info, version.major, version.minor ); | |
| return -ENOTSUP; | |
| } | |
| assert ( info->op != NULL ); | |
| assert ( info->op->info != NULL ); | |
| /* Populate content information */ | |
| if ( ( rc = info->op->info ( info ) ) != 0 ) | |
| return rc; | |
| DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with " | |
| "%d segments\n", info, info->range.start, info->range.end, | |
| info->trim.start, info->trim.end, info->segments ); | |
| return 0; | |
| } | |
| /** | |
| * Populate content information segment | |
| * | |
| * @v info Content information | |
| * @v segment Content information segment to fill in | |
| * @v index Segment index | |
| * @ret rc Return status code | |
| */ | |
| int peerdist_info_segment ( const struct peerdist_info *info, | |
| struct peerdist_info_segment *segment, | |
| unsigned int index ) { | |
| int rc; | |
| /* Sanity checks */ | |
| assert ( info != NULL ); | |
| assert ( info->op != NULL ); | |
| assert ( info->op->segment != NULL ); | |
| if ( index >= info->segments ) { | |
| DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n", | |
| info, index, info->segments ); | |
| return -ERANGE; | |
| } | |
| /* Initialise structure */ | |
| memset ( segment, 0, sizeof ( *segment ) ); | |
| segment->info = info; | |
| segment->index = index; | |
| /* Populate content information segment */ | |
| if ( ( rc = info->op->segment ( segment ) ) != 0 ) | |
| return rc; | |
| DBGC2 ( info, "PCCRC %p segment %d range [%08zx,%08zx) with %d " | |
| "blocks\n", info, segment->index, segment->range.start, | |
| segment->range.end, segment->blocks ); | |
| DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index, | |
| peerdist_info_hash_ntoa ( info, segment->hash ) ); | |
| DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index, | |
| peerdist_info_hash_ntoa ( info, segment->secret ) ); | |
| DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index, | |
| peerdist_info_hash_ntoa ( info, segment->id ) ); | |
| return 0; | |
| } | |
| /** | |
| * Populate content information block | |
| * | |
| * @v segment Content information segment | |
| * @v block Content information block to fill in | |
| * @v index Block index | |
| * @ret rc Return status code | |
| */ | |
| int peerdist_info_block ( const struct peerdist_info_segment *segment, | |
| struct peerdist_info_block *block, | |
| unsigned int index ) { | |
| const struct peerdist_info *info = segment->info; | |
| size_t start; | |
| size_t end; | |
| int rc; | |
| /* Sanity checks */ | |
| assert ( segment != NULL ); | |
| assert ( info != NULL ); | |
| assert ( info->op != NULL ); | |
| assert ( info->op->block != NULL ); | |
| if ( index >= segment->blocks ) { | |
| DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of " | |
| "range\n", info, segment->index, index, segment->blocks); | |
| return -ERANGE; | |
| } | |
| /* Initialise structure */ | |
| memset ( block, 0, sizeof ( *block ) ); | |
| block->segment = segment; | |
| block->index = index; | |
| /* Populate content information block */ | |
| if ( ( rc = info->op->block ( block ) ) != 0 ) | |
| return rc; | |
| /* Calculate trimmed range */ | |
| start = block->range.start; | |
| if ( start < info->trim.start ) | |
| start = info->trim.start; | |
| end = block->range.end; | |
| if ( end > info->trim.end ) | |
| end = info->trim.end; | |
| if ( end < start ) | |
| end = start; | |
| block->trim.start = start; | |
| block->trim.end = end; | |
| DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n", | |
| info, segment->index, block->index, | |
| peerdist_info_hash_ntoa ( info, block->hash ) ); | |
| DBGC2 ( info, "PCCRC %p segment %d block %d range [%08zx,%08zx) covers " | |
| "[%08zx,%08zx)\n", info, segment->index, block->index, | |
| block->range.start, block->range.end, block->trim.start, | |
| block->trim.end ); | |
| return 0; | |
| } |