iPXE
pccrc.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <errno.h>
00027 #include <assert.h>
00028 #include <ipxe/uaccess.h>
00029 #include <ipxe/sha256.h>
00030 #include <ipxe/sha512.h>
00031 #include <ipxe/hmac.h>
00032 #include <ipxe/base16.h>
00033 #include <ipxe/pccrc.h>
00034 
00035 /** @file
00036  *
00037  * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
00038  *
00039  */
00040 
00041 /******************************************************************************
00042  *
00043  * Utility functions
00044  *
00045  ******************************************************************************
00046  */
00047 
00048 /**
00049  * Transcribe hash value (for debugging)
00050  *
00051  * @v info              Content information
00052  * @v hash              Hash value
00053  * @ret string          Hash value string
00054  */
00055 static inline const char *
00056 peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) {
00057         static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ];
00058         size_t digestsize = info->digestsize;
00059 
00060         /* Sanity check */
00061         assert ( info != NULL );
00062         assert ( digestsize != 0 );
00063         assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) );
00064 
00065         /* Transcribe hash value */
00066         base16_encode ( hash, digestsize, buf, sizeof ( buf ) );
00067         return buf;
00068 }
00069 
00070 /**
00071  * Get raw data
00072  *
00073  * @v info              Content information
00074  * @v data              Data buffer
00075  * @v offset            Starting offset
00076  * @v len               Length
00077  * @ret rc              Return status code
00078  */
00079 static int peerdist_info_get ( const struct peerdist_info *info, void *data,
00080                                size_t offset, size_t len ) {
00081 
00082         /* Sanity check */
00083         if ( ( offset > info->raw.len ) ||
00084              ( len > ( info->raw.len - offset ) ) ) {
00085                 DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n",
00086                        info, offset, ( offset + len ), info->raw.len );
00087                 return -ERANGE;
00088         }
00089 
00090         /* Copy data */
00091         copy_from_user ( data, info->raw.data, offset, len );
00092 
00093         return 0;
00094 }
00095 
00096 /**
00097  * Populate segment hashes
00098  *
00099  * @v segment           Content information segment to fill in
00100  * @v hash              Segment hash of data
00101  * @v secret            Segment secret
00102  */
00103 static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment,
00104                                          const void *hash, const void *secret ){
00105         const struct peerdist_info *info = segment->info;
00106         struct digest_algorithm *digest = info->digest;
00107         uint8_t ctx[digest->ctxsize];
00108         size_t digestsize = info->digestsize;
00109         size_t secretsize = digestsize;
00110         static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC;
00111 
00112         /* Sanity check */
00113         assert ( digestsize <= sizeof ( segment->hash ) );
00114         assert ( digestsize <= sizeof ( segment->secret ) );
00115         assert ( digestsize <= sizeof ( segment->id ) );
00116 
00117         /* Get segment hash of data */
00118         memcpy ( segment->hash, hash, digestsize );
00119 
00120         /* Get segment secret */
00121         memcpy ( segment->secret, secret, digestsize );
00122 
00123         /* Calculate segment identifier */
00124         hmac_init ( digest, ctx, segment->secret, &secretsize );
00125         assert ( secretsize == digestsize );
00126         hmac_update ( digest, ctx, segment->hash, digestsize );
00127         hmac_update ( digest, ctx, magic, sizeof ( magic ) );
00128         hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id );
00129         assert ( secretsize == digestsize );
00130 }
00131 
00132 /******************************************************************************
00133  *
00134  * Content Information version 1
00135  *
00136  ******************************************************************************
00137  */
00138 
00139 /**
00140  * Get number of blocks within a block description
00141  *
00142  * @v info              Content information
00143  * @v offset            Block description offset
00144  * @ret blocks          Number of blocks, or negative error
00145  */
00146 static int peerdist_info_v1_blocks ( const struct peerdist_info *info,
00147                                      size_t offset ) {
00148         struct peerdist_info_v1_block raw;
00149         unsigned int blocks;
00150         int rc;
00151 
00152         /* Get block description header */
00153         if ( ( rc = peerdist_info_get ( info, &raw, offset,
00154                                         sizeof ( raw ) ) ) != 0 )
00155                 return rc;
00156 
00157         /* Calculate number of blocks */
00158         blocks = le32_to_cpu ( raw.blocks );
00159 
00160         return blocks;
00161 }
00162 
00163 /**
00164  * Locate block description
00165  *
00166  * @v info              Content information
00167  * @v index             Segment index
00168  * @ret offset          Block description offset, or negative error
00169  */
00170 static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info,
00171                                                unsigned int index ) {
00172         size_t digestsize = info->digestsize;
00173         unsigned int i;
00174         size_t offset;
00175         int blocks;
00176         int rc;
00177 
00178         /* Sanity check */
00179         assert ( index < info->segments );
00180 
00181         /* Calculate offset of first block description */
00182         offset = ( sizeof ( struct peerdist_info_v1 ) +
00183                    ( info->segments *
00184                      sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) );
00185 
00186         /* Iterate over block descriptions until we find this segment */
00187         for ( i = 0 ; i < index ; i++ ) {
00188 
00189                 /* Get number of blocks */
00190                 blocks = peerdist_info_v1_blocks ( info, offset );
00191                 if ( blocks < 0 ) {
00192                         rc = blocks;
00193                         DBGC ( info, "PCCRC %p segment %d could not get number "
00194                                "of blocks: %s\n", info, i, strerror ( rc ) );
00195                         return rc;
00196                 }
00197 
00198                 /* Move to next block description */
00199                 offset += sizeof ( peerdist_info_v1_block_t ( digestsize,
00200                                                               blocks ) );
00201         }
00202 
00203         return offset;
00204 }
00205 
00206 /**
00207  * Populate content information
00208  *
00209  * @v info              Content information to fill in
00210  * @ret rc              Return status code
00211  */
00212 static int peerdist_info_v1 ( struct peerdist_info *info ) {
00213         struct peerdist_info_v1 raw;
00214         struct peerdist_info_segment first;
00215         struct peerdist_info_segment last;
00216         size_t first_skip;
00217         size_t last_skip;
00218         size_t last_read;
00219         int rc;
00220 
00221         /* Get raw header */
00222         if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
00223                 DBGC ( info, "PCCRC %p could not get V1 content information: "
00224                        "%s\n", info, strerror ( rc ) );
00225                 return rc;
00226         }
00227         assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) );
00228 
00229         /* Determine hash algorithm */
00230         switch ( raw.hash ) {
00231         case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) :
00232                 info->digest = &sha256_algorithm;
00233                 break;
00234         case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) :
00235                 info->digest = &sha384_algorithm;
00236                 break;
00237         case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) :
00238                 info->digest = &sha512_algorithm;
00239                 break;
00240         default:
00241                 DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n",
00242                        info, le32_to_cpu ( raw.hash ) );
00243                 return -ENOTSUP;
00244         }
00245         info->digestsize = info->digest->digestsize;
00246         assert ( info->digest != NULL );
00247         DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
00248                 info, info->digest->name, ( info->digestsize * 8 ) );
00249 
00250         /* Calculate number of segments */
00251         info->segments = le32_to_cpu ( raw.segments );
00252 
00253         /* Get first segment */
00254         if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 )
00255                 return rc;
00256 
00257         /* Calculate range start offset */
00258         info->range.start = first.range.start;
00259 
00260         /* Calculate trimmed range start offset */
00261         first_skip = le32_to_cpu ( raw.first );
00262         info->trim.start = ( first.range.start + first_skip );
00263 
00264         /* Get last segment */
00265         if ( ( rc = peerdist_info_segment ( info, &last,
00266                                             ( info->segments - 1 ) ) ) != 0 )
00267                 return rc;
00268 
00269         /* Calculate range end offset */
00270         info->range.end = last.range.end;
00271 
00272         /* Calculate trimmed range end offset */
00273         if ( raw.last ) {
00274                 /* Explicit length to include from last segment is given */
00275                 last_read = le32_to_cpu ( raw.last );
00276                 last_skip = ( last.index ? 0 : first_skip );
00277                 info->trim.end = ( last.range.start + last_skip + last_read );
00278         } else {
00279                 /* No explicit length given: range extends to end of segment */
00280                 info->trim.end = last.range.end;
00281         }
00282 
00283         return 0;
00284 }
00285 
00286 /**
00287  * Populate content information segment
00288  *
00289  * @v segment           Content information segment to fill in
00290  * @ret rc              Return status code
00291  */
00292 static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) {
00293         const struct peerdist_info *info = segment->info;
00294         size_t digestsize = info->digestsize;
00295         peerdist_info_v1_segment_t ( digestsize ) raw;
00296         ssize_t raw_offset;
00297         int blocks;
00298         int rc;
00299 
00300         /* Sanity checks */
00301         assert ( segment->index < info->segments );
00302 
00303         /* Get raw description */
00304         raw_offset = ( sizeof ( struct peerdist_info_v1 ) +
00305                        ( segment->index * sizeof ( raw ) ) );
00306         if ( ( rc = peerdist_info_get ( info, &raw, raw_offset,
00307                                         sizeof ( raw ) ) ) != 0 ) {
00308                 DBGC ( info, "PCCRC %p segment %d could not get segment "
00309                        "description: %s\n", info, segment->index,
00310                        strerror ( rc ) );
00311                 return rc;
00312         }
00313 
00314         /* Calculate start offset of this segment */
00315         segment->range.start = le64_to_cpu ( raw.segment.offset );
00316 
00317         /* Calculate end offset of this segment */
00318         segment->range.end = ( segment->range.start +
00319                                le32_to_cpu ( raw.segment.len ) );
00320 
00321         /* Calculate block size of this segment */
00322         segment->blksize = le32_to_cpu ( raw.segment.blksize );
00323 
00324         /* Locate block description for this segment */
00325         raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
00326         if ( raw_offset < 0 ) {
00327                 rc = raw_offset;
00328                 return rc;
00329         }
00330 
00331         /* Get number of blocks */
00332         blocks = peerdist_info_v1_blocks ( info, raw_offset );
00333         if ( blocks < 0 ) {
00334                 rc = blocks;
00335                 DBGC ( info, "PCCRC %p segment %d could not get number of "
00336                        "blocks: %s\n", info, segment->index, strerror ( rc ) );
00337                 return rc;
00338         }
00339         segment->blocks = blocks;
00340 
00341         /* Calculate segment hashes */
00342         peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
00343 
00344         return 0;
00345 }
00346 
00347 /**
00348  * Populate content information block
00349  *
00350  * @v block             Content information block to fill in
00351  * @ret rc              Return status code
00352  */
00353 static int peerdist_info_v1_block ( struct peerdist_info_block *block ) {
00354         const struct peerdist_info_segment *segment = block->segment;
00355         const struct peerdist_info *info = segment->info;
00356         size_t digestsize = info->digestsize;
00357         peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw;
00358         ssize_t raw_offset;
00359         int rc;
00360 
00361         /* Sanity checks */
00362         assert ( block->index < segment->blocks );
00363 
00364         /* Calculate start offset of this block */
00365         block->range.start = ( segment->range.start +
00366                                ( block->index * segment->blksize ) );
00367 
00368         /* Calculate end offset of this block */
00369         block->range.end = ( block->range.start + segment->blksize );
00370         if ( block->range.end > segment->range.end )
00371                 block->range.end = segment->range.end;
00372 
00373         /* Locate block description */
00374         raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
00375         if ( raw_offset < 0 ) {
00376                 rc = raw_offset;
00377                 return rc;
00378         }
00379 
00380         /* Get block hash */
00381         raw_offset += offsetof ( typeof ( raw ), hash[block->index] );
00382         if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset,
00383                                         digestsize ) ) != 0 ) {
00384                 DBGC ( info, "PCCRC %p segment %d block %d could not get "
00385                        "hash: %s\n", info, segment->index, block->index,
00386                        strerror ( rc ) );
00387                 return rc;
00388         }
00389 
00390         return 0;
00391 }
00392 
00393 /** Content information version 1 operations */
00394 static struct peerdist_info_operations peerdist_info_v1_operations = {
00395         .info = peerdist_info_v1,
00396         .segment = peerdist_info_v1_segment,
00397         .block = peerdist_info_v1_block,
00398 };
00399 
00400 /******************************************************************************
00401  *
00402  * Content Information version 2
00403  *
00404  ******************************************************************************
00405  */
00406 
00407 /** A segment cursor */
00408 struct peerdist_info_v2_cursor {
00409         /** Raw data offset */
00410         size_t offset;
00411         /** Number of segments remaining within this chunk */
00412         unsigned int remaining;
00413         /** Accumulated segment length */
00414         size_t len;
00415 };
00416 
00417 /**
00418  * Initialise segment cursor
00419  *
00420  * @v cursor            Segment cursor
00421  */
00422 static inline void
00423 peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) {
00424 
00425         /* Initialise cursor */
00426         cursor->offset = ( sizeof ( struct peerdist_info_v2 ) +
00427                            sizeof ( struct peerdist_info_v2_chunk ) );
00428         cursor->remaining = 0;
00429         cursor->len = 0;
00430 }
00431 
00432 /**
00433  * Update segment cursor to next segment description
00434  *
00435  * @v info              Content information
00436  * @v offset            Current offset
00437  * @v remaining         Number of segments remaining within this chunk
00438  * @ret rc              Return status code
00439  */
00440 static int
00441 peerdist_info_v2_cursor_next ( const struct peerdist_info *info,
00442                                struct peerdist_info_v2_cursor *cursor ) {
00443         size_t digestsize = info->digestsize;
00444         peerdist_info_v2_segment_t ( digestsize ) raw;
00445         struct peerdist_info_v2_chunk chunk;
00446         int rc;
00447 
00448         /* Get chunk description if applicable */
00449         if ( ! cursor->remaining ) {
00450 
00451                 /* Get chunk description */
00452                 if ( ( rc = peerdist_info_get ( info, &chunk,
00453                                                 ( cursor->offset -
00454                                                   sizeof ( chunk ) ),
00455                                                 sizeof ( chunk ) ) ) != 0 )
00456                         return rc;
00457 
00458                 /* Update number of segments remaining */
00459                 cursor->remaining = ( be32_to_cpu ( chunk.len ) /
00460                                       sizeof ( raw ) );
00461         }
00462 
00463         /* Get segment description header */
00464         if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset,
00465                                         sizeof ( raw.segment ) ) ) != 0 )
00466                 return rc;
00467 
00468         /* Update cursor */
00469         cursor->offset += sizeof ( raw );
00470         cursor->remaining--;
00471         if ( ! cursor->remaining )
00472                 cursor->offset += sizeof ( chunk );
00473         cursor->len += be32_to_cpu ( raw.segment.len );
00474 
00475         return 0;
00476 }
00477 
00478 /**
00479  * Get number of segments and total length
00480  *
00481  * @v info              Content information
00482  * @v len               Length to fill in
00483  * @ret rc              Number of segments, or negative error
00484  */
00485 static int peerdist_info_v2_segments ( const struct peerdist_info *info,
00486                                        size_t *len ) {
00487         struct peerdist_info_v2_cursor cursor;
00488         unsigned int segments;
00489         int rc;
00490 
00491         /* Iterate over all segments */
00492         for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ;
00493               cursor.offset < info->raw.len ; segments++ ) {
00494 
00495                 /* Update segment cursor */
00496                 if ( ( rc = peerdist_info_v2_cursor_next ( info,
00497                                                            &cursor ) ) != 0 ) {
00498                         DBGC ( info, "PCCRC %p segment %d could not update "
00499                                "segment cursor: %s\n",
00500                                info, segments, strerror ( rc ) );
00501                         return rc;
00502                 }
00503         }
00504 
00505         /* Record accumulated length */
00506         *len = cursor.len;
00507 
00508         return segments;
00509 }
00510 
00511 /**
00512  * Populate content information
00513  *
00514  * @v info              Content information to fill in
00515  * @ret rc              Return status code
00516  */
00517 static int peerdist_info_v2 ( struct peerdist_info *info ) {
00518         struct peerdist_info_v2 raw;
00519         size_t len = 0;
00520         int segments;
00521         int rc;
00522 
00523         /* Get raw header */
00524         if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
00525                 DBGC ( info, "PCCRC %p could not get V2 content information: "
00526                        "%s\n", info, strerror ( rc ) );
00527                 return rc;
00528         }
00529         assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) );
00530 
00531         /* Determine hash algorithm */
00532         switch ( raw.hash ) {
00533         case PEERDIST_INFO_V2_HASH_SHA512_TRUNC :
00534                 info->digest = &sha512_algorithm;
00535                 info->digestsize = ( 256 / 8 );
00536                 break;
00537         default:
00538                 DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n",
00539                        info, raw.hash );
00540                 return -ENOTSUP;
00541         }
00542         assert ( info->digest != NULL );
00543         DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
00544                 info, info->digest->name, ( info->digestsize * 8 ) );
00545 
00546         /* Calculate number of segments and total length */
00547         segments = peerdist_info_v2_segments ( info, &len );
00548         if ( segments < 0 ) {
00549                 rc = segments;
00550                 DBGC ( info, "PCCRC %p could not get segment count and length: "
00551                        "%s\n", info, strerror ( rc ) );
00552                 return rc;
00553         }
00554         info->segments = segments;
00555 
00556         /* Calculate range start offset */
00557         info->range.start = be64_to_cpu ( raw.offset );
00558 
00559         /* Calculate trimmed range start offset */
00560         info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) );
00561 
00562         /* Calculate range end offset */
00563         info->range.end = ( info->range.start + len );
00564 
00565         /* Calculate trimmed range end offset */
00566         info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) :
00567                            info->range.end );
00568 
00569         return 0;
00570 }
00571 
00572 /**
00573  * Populate content information segment
00574  *
00575  * @v segment           Content information segment to fill in
00576  * @ret rc              Return status code
00577  */
00578 static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) {
00579         const struct peerdist_info *info = segment->info;
00580         size_t digestsize = info->digestsize;
00581         peerdist_info_v2_segment_t ( digestsize ) raw;
00582         struct peerdist_info_v2_cursor cursor;
00583         unsigned int index;
00584         size_t len;
00585         int rc;
00586 
00587         /* Sanity checks */
00588         assert ( segment->index < info->segments );
00589 
00590         /* Iterate over all segments before the target segment */
00591         for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ;
00592               index < segment->index ; index++ ) {
00593 
00594                 /* Update segment cursor */
00595                 if ( ( rc = peerdist_info_v2_cursor_next ( info,
00596                                                            &cursor ) ) != 0 ) {
00597                         DBGC ( info, "PCCRC %p segment %d could not update "
00598                                "segment cursor: %s\n",
00599                                info, index, strerror ( rc ) );
00600                         return rc;
00601                 }
00602         }
00603 
00604         /* Get raw description */
00605         if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset,
00606                                         sizeof ( raw ) ) ) != 0 ) {
00607                 DBGC ( info, "PCCRC %p segment %d could not get segment "
00608                        "description: %s\n",
00609                        info, segment->index, strerror ( rc ) );
00610                 return rc;
00611         }
00612 
00613         /* Calculate start offset of this segment */
00614         segment->range.start = ( info->range.start + cursor.len );
00615 
00616         /* Calculate end offset of this segment */
00617         len = be32_to_cpu ( raw.segment.len );
00618         segment->range.end = ( segment->range.start + len );
00619 
00620         /* Model as a segment containing a single block */
00621         segment->blocks = 1;
00622         segment->blksize = len;
00623 
00624         /* Calculate segment hashes */
00625         peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
00626 
00627         return 0;
00628 }
00629 
00630 /**
00631  * Populate content information block
00632  *
00633  * @v block             Content information block to fill in
00634  * @ret rc              Return status code
00635  */
00636 static int peerdist_info_v2_block ( struct peerdist_info_block *block ) {
00637         const struct peerdist_info_segment *segment = block->segment;
00638         const struct peerdist_info *info = segment->info;
00639         size_t digestsize = info->digestsize;
00640 
00641         /* Sanity checks */
00642         assert ( block->index < segment->blocks );
00643 
00644         /* Model as a block covering the whole segment */
00645         memcpy ( &block->range, &segment->range, sizeof ( block->range ) );
00646         memcpy ( block->hash, segment->hash, digestsize );
00647 
00648         return 0;
00649 }
00650 
00651 /** Content information version 2 operations */
00652 static struct peerdist_info_operations peerdist_info_v2_operations = {
00653         .block = peerdist_info_v2_block,
00654         .segment = peerdist_info_v2_segment,
00655         .info = peerdist_info_v2,
00656 };
00657 
00658 /******************************************************************************
00659  *
00660  * Content Information
00661  *
00662  ******************************************************************************
00663  */
00664 
00665 /**
00666  * Populate content information
00667  *
00668  * @v data              Raw data
00669  * @v len               Length of raw data
00670  * @v info              Content information to fill in
00671  * @ret rc              Return status code
00672  */
00673 int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) {
00674         union peerdist_info_version version;
00675         int rc;
00676 
00677         /* Initialise structure */
00678         memset ( info, 0, sizeof ( *info ) );
00679         info->raw.data = data;
00680         info->raw.len = len;
00681 
00682         /* Get version */
00683         if ( ( rc = peerdist_info_get ( info, &version, 0,
00684                                         sizeof ( version ) ) ) != 0 ) {
00685                 DBGC ( info, "PCCRC %p could not get version: %s\n",
00686                        info, strerror ( rc ) );
00687                 return rc;
00688         }
00689         DBGC2 ( info, "PCCRC %p version %d.%d\n",
00690                 info, version.major, version.minor );
00691 
00692         /* Determine version */
00693         switch ( version.raw ) {
00694         case cpu_to_le16 ( PEERDIST_INFO_V1 ) :
00695                 info->op = &peerdist_info_v1_operations;
00696                 break;
00697         case cpu_to_le16 ( PEERDIST_INFO_V2 ) :
00698                 info->op = &peerdist_info_v2_operations;
00699                 break;
00700         default:
00701                 DBGC ( info, "PCCRC %p unsupported version %d.%d\n",
00702                        info, version.major, version.minor );
00703                 return -ENOTSUP;
00704         }
00705         assert ( info->op != NULL );
00706         assert ( info->op->info != NULL );
00707 
00708         /* Populate content information */
00709         if ( ( rc = info->op->info ( info ) ) != 0 )
00710                 return rc;
00711 
00712         DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with "
00713                 "%d segments\n", info, info->range.start, info->range.end,
00714                 info->trim.start, info->trim.end, info->segments );
00715         return 0;
00716 }
00717 
00718 /**
00719  * Populate content information segment
00720  *
00721  * @v info              Content information
00722  * @v segment           Content information segment to fill in
00723  * @v index             Segment index
00724  * @ret rc              Return status code
00725  */
00726 int peerdist_info_segment ( const struct peerdist_info *info,
00727                             struct peerdist_info_segment *segment,
00728                             unsigned int index ) {
00729         int rc;
00730 
00731         /* Sanity checks */
00732         assert ( info != NULL );
00733         assert ( info->op != NULL );
00734         assert ( info->op->segment != NULL );
00735         if ( index >= info->segments ) {
00736                 DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n",
00737                        info, index, info->segments );
00738                 return -ERANGE;
00739         }
00740 
00741         /* Initialise structure */
00742         memset ( segment, 0, sizeof ( *segment ) );
00743         segment->info = info;
00744         segment->index = index;
00745 
00746         /* Populate content information segment */
00747         if ( ( rc = info->op->segment ( segment ) ) != 0 )
00748                 return rc;
00749 
00750         DBGC2 ( info, "PCCRC %p segment %d range [%08zx,%08zx) with %d "
00751                 "blocks\n", info, segment->index, segment->range.start,
00752                 segment->range.end, segment->blocks );
00753         DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index,
00754                 peerdist_info_hash_ntoa ( info, segment->hash ) );
00755         DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index,
00756                 peerdist_info_hash_ntoa ( info, segment->secret ) );
00757         DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index,
00758                 peerdist_info_hash_ntoa ( info, segment->id ) );
00759         return 0;
00760 }
00761 
00762 /**
00763  * Populate content information block
00764  *
00765  * @v segment           Content information segment
00766  * @v block             Content information block to fill in
00767  * @v index             Block index
00768  * @ret rc              Return status code
00769  */
00770 int peerdist_info_block ( const struct peerdist_info_segment *segment,
00771                           struct peerdist_info_block *block,
00772                           unsigned int index ) {
00773         const struct peerdist_info *info = segment->info;
00774         size_t start;
00775         size_t end;
00776         int rc;
00777 
00778         /* Sanity checks */
00779         assert ( segment != NULL );
00780         assert ( info != NULL );
00781         assert ( info->op != NULL );
00782         assert ( info->op->block != NULL );
00783         if ( index >= segment->blocks ) {
00784                 DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of "
00785                        "range\n", info, segment->index, index, segment->blocks);
00786                 return -ERANGE;
00787         }
00788 
00789         /* Initialise structure */
00790         memset ( block, 0, sizeof ( *block ) );
00791         block->segment = segment;
00792         block->index = index;
00793 
00794         /* Populate content information block */
00795         if ( ( rc = info->op->block ( block ) ) != 0 )
00796                 return rc;
00797 
00798         /* Calculate trimmed range */
00799         start = block->range.start;
00800         if ( start < info->trim.start )
00801                 start = info->trim.start;
00802         end = block->range.end;
00803         if ( end > info->trim.end )
00804                 end = info->trim.end;
00805         if ( end < start )
00806                 end = start;
00807         block->trim.start = start;
00808         block->trim.end = end;
00809 
00810         DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n",
00811                 info, segment->index, block->index,
00812                 peerdist_info_hash_ntoa ( info, block->hash ) );
00813         DBGC2 ( info, "PCCRC %p segment %d block %d range [%08zx,%08zx) covers "
00814                 "[%08zx,%08zx)\n", info, segment->index, block->index,
00815                 block->range.start, block->range.end, block->trim.start,
00816                 block->trim.end );
00817         return 0;
00818 }