These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / pccrc.c
1 /*
2  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <errno.h>
27 #include <assert.h>
28 #include <ipxe/uaccess.h>
29 #include <ipxe/sha256.h>
30 #include <ipxe/sha512.h>
31 #include <ipxe/hmac.h>
32 #include <ipxe/base16.h>
33 #include <ipxe/pccrc.h>
34
35 /** @file
36  *
37  * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC]
38  *
39  */
40
41 /******************************************************************************
42  *
43  * Utility functions
44  *
45  ******************************************************************************
46  */
47
48 /**
49  * Transcribe hash value (for debugging)
50  *
51  * @v info              Content information
52  * @v hash              Hash value
53  * @ret string          Hash value string
54  */
55 static inline const char *
56 peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) {
57         static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ];
58         size_t digestsize = info->digestsize;
59
60         /* Sanity check */
61         assert ( info != NULL );
62         assert ( digestsize != 0 );
63         assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) );
64
65         /* Transcribe hash value */
66         base16_encode ( hash, digestsize, buf, sizeof ( buf ) );
67         return buf;
68 }
69
70 /**
71  * Get raw data
72  *
73  * @v info              Content information
74  * @v data              Data buffer
75  * @v offset            Starting offset
76  * @v len               Length
77  * @ret rc              Return status code
78  */
79 static int peerdist_info_get ( const struct peerdist_info *info, void *data,
80                                size_t offset, size_t len ) {
81
82         /* Sanity check */
83         if ( ( offset > info->raw.len ) ||
84              ( len > ( info->raw.len - offset ) ) ) {
85                 DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n",
86                        info, offset, ( offset + len ), info->raw.len );
87                 return -ERANGE;
88         }
89
90         /* Copy data */
91         copy_from_user ( data, info->raw.data, offset, len );
92
93         return 0;
94 }
95
96 /**
97  * Populate segment hashes
98  *
99  * @v segment           Content information segment to fill in
100  * @v hash              Segment hash of data
101  * @v secret            Segment secret
102  */
103 static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment,
104                                          const void *hash, const void *secret ){
105         const struct peerdist_info *info = segment->info;
106         struct digest_algorithm *digest = info->digest;
107         uint8_t ctx[digest->ctxsize];
108         size_t digestsize = info->digestsize;
109         size_t secretsize = digestsize;
110         static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC;
111
112         /* Sanity check */
113         assert ( digestsize <= sizeof ( segment->hash ) );
114         assert ( digestsize <= sizeof ( segment->secret ) );
115         assert ( digestsize <= sizeof ( segment->id ) );
116
117         /* Get segment hash of data */
118         memcpy ( segment->hash, hash, digestsize );
119
120         /* Get segment secret */
121         memcpy ( segment->secret, secret, digestsize );
122
123         /* Calculate segment identifier */
124         hmac_init ( digest, ctx, segment->secret, &secretsize );
125         assert ( secretsize == digestsize );
126         hmac_update ( digest, ctx, segment->hash, digestsize );
127         hmac_update ( digest, ctx, magic, sizeof ( magic ) );
128         hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id );
129         assert ( secretsize == digestsize );
130 }
131
132 /******************************************************************************
133  *
134  * Content Information version 1
135  *
136  ******************************************************************************
137  */
138
139 /**
140  * Get number of blocks within a block description
141  *
142  * @v info              Content information
143  * @v offset            Block description offset
144  * @ret blocks          Number of blocks, or negative error
145  */
146 static int peerdist_info_v1_blocks ( const struct peerdist_info *info,
147                                      size_t offset ) {
148         struct peerdist_info_v1_block raw;
149         unsigned int blocks;
150         int rc;
151
152         /* Get block description header */
153         if ( ( rc = peerdist_info_get ( info, &raw, offset,
154                                         sizeof ( raw ) ) ) != 0 )
155                 return rc;
156
157         /* Calculate number of blocks */
158         blocks = le32_to_cpu ( raw.blocks );
159
160         return blocks;
161 }
162
163 /**
164  * Locate block description
165  *
166  * @v info              Content information
167  * @v index             Segment index
168  * @ret offset          Block description offset, or negative error
169  */
170 static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info,
171                                                unsigned int index ) {
172         size_t digestsize = info->digestsize;
173         unsigned int i;
174         size_t offset;
175         int blocks;
176         int rc;
177
178         /* Sanity check */
179         assert ( index < info->segments );
180
181         /* Calculate offset of first block description */
182         offset = ( sizeof ( struct peerdist_info_v1 ) +
183                    ( info->segments *
184                      sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) );
185
186         /* Iterate over block descriptions until we find this segment */
187         for ( i = 0 ; i < index ; i++ ) {
188
189                 /* Get number of blocks */
190                 blocks = peerdist_info_v1_blocks ( info, offset );
191                 if ( blocks < 0 ) {
192                         rc = blocks;
193                         DBGC ( info, "PCCRC %p segment %d could not get number "
194                                "of blocks: %s\n", info, i, strerror ( rc ) );
195                         return rc;
196                 }
197
198                 /* Move to next block description */
199                 offset += sizeof ( peerdist_info_v1_block_t ( digestsize,
200                                                               blocks ) );
201         }
202
203         return offset;
204 }
205
206 /**
207  * Populate content information
208  *
209  * @v info              Content information to fill in
210  * @ret rc              Return status code
211  */
212 static int peerdist_info_v1 ( struct peerdist_info *info ) {
213         struct peerdist_info_v1 raw;
214         struct peerdist_info_segment first;
215         struct peerdist_info_segment last;
216         size_t first_skip;
217         size_t last_skip;
218         size_t last_read;
219         int rc;
220
221         /* Get raw header */
222         if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
223                 DBGC ( info, "PCCRC %p could not get V1 content information: "
224                        "%s\n", info, strerror ( rc ) );
225                 return rc;
226         }
227         assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) );
228
229         /* Determine hash algorithm */
230         switch ( raw.hash ) {
231         case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) :
232                 info->digest = &sha256_algorithm;
233                 break;
234         case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) :
235                 info->digest = &sha384_algorithm;
236                 break;
237         case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) :
238                 info->digest = &sha512_algorithm;
239                 break;
240         default:
241                 DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n",
242                        info, le32_to_cpu ( raw.hash ) );
243                 return -ENOTSUP;
244         }
245         info->digestsize = info->digest->digestsize;
246         assert ( info->digest != NULL );
247         DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
248                 info, info->digest->name, ( info->digestsize * 8 ) );
249
250         /* Calculate number of segments */
251         info->segments = le32_to_cpu ( raw.segments );
252
253         /* Get first segment */
254         if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 )
255                 return rc;
256
257         /* Calculate range start offset */
258         info->range.start = first.range.start;
259
260         /* Calculate trimmed range start offset */
261         first_skip = le32_to_cpu ( raw.first );
262         info->trim.start = ( first.range.start + first_skip );
263
264         /* Get last segment */
265         if ( ( rc = peerdist_info_segment ( info, &last,
266                                             ( info->segments - 1 ) ) ) != 0 )
267                 return rc;
268
269         /* Calculate range end offset */
270         info->range.end = last.range.end;
271
272         /* Calculate trimmed range end offset */
273         if ( raw.last ) {
274                 /* Explicit length to include from last segment is given */
275                 last_read = le32_to_cpu ( raw.last );
276                 last_skip = ( last.index ? 0 : first_skip );
277                 info->trim.end = ( last.range.start + last_skip + last_read );
278         } else {
279                 /* No explicit length given: range extends to end of segment */
280                 info->trim.end = last.range.end;
281         }
282
283         return 0;
284 }
285
286 /**
287  * Populate content information segment
288  *
289  * @v segment           Content information segment to fill in
290  * @ret rc              Return status code
291  */
292 static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) {
293         const struct peerdist_info *info = segment->info;
294         size_t digestsize = info->digestsize;
295         peerdist_info_v1_segment_t ( digestsize ) raw;
296         ssize_t raw_offset;
297         int blocks;
298         int rc;
299
300         /* Sanity checks */
301         assert ( segment->index < info->segments );
302
303         /* Get raw description */
304         raw_offset = ( sizeof ( struct peerdist_info_v1 ) +
305                        ( segment->index * sizeof ( raw ) ) );
306         if ( ( rc = peerdist_info_get ( info, &raw, raw_offset,
307                                         sizeof ( raw ) ) ) != 0 ) {
308                 DBGC ( info, "PCCRC %p segment %d could not get segment "
309                        "description: %s\n", info, segment->index,
310                        strerror ( rc ) );
311                 return rc;
312         }
313
314         /* Calculate start offset of this segment */
315         segment->range.start = le64_to_cpu ( raw.segment.offset );
316
317         /* Calculate end offset of this segment */
318         segment->range.end = ( segment->range.start +
319                                le32_to_cpu ( raw.segment.len ) );
320
321         /* Calculate block size of this segment */
322         segment->blksize = le32_to_cpu ( raw.segment.blksize );
323
324         /* Locate block description for this segment */
325         raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
326         if ( raw_offset < 0 ) {
327                 rc = raw_offset;
328                 return rc;
329         }
330
331         /* Get number of blocks */
332         blocks = peerdist_info_v1_blocks ( info, raw_offset );
333         if ( blocks < 0 ) {
334                 rc = blocks;
335                 DBGC ( info, "PCCRC %p segment %d could not get number of "
336                        "blocks: %s\n", info, segment->index, strerror ( rc ) );
337                 return rc;
338         }
339         segment->blocks = blocks;
340
341         /* Calculate segment hashes */
342         peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
343
344         return 0;
345 }
346
347 /**
348  * Populate content information block
349  *
350  * @v block             Content information block to fill in
351  * @ret rc              Return status code
352  */
353 static int peerdist_info_v1_block ( struct peerdist_info_block *block ) {
354         const struct peerdist_info_segment *segment = block->segment;
355         const struct peerdist_info *info = segment->info;
356         size_t digestsize = info->digestsize;
357         peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw;
358         ssize_t raw_offset;
359         int rc;
360
361         /* Sanity checks */
362         assert ( block->index < segment->blocks );
363
364         /* Calculate start offset of this block */
365         block->range.start = ( segment->range.start +
366                                ( block->index * segment->blksize ) );
367
368         /* Calculate end offset of this block */
369         block->range.end = ( block->range.start + segment->blksize );
370         if ( block->range.end > segment->range.end )
371                 block->range.end = segment->range.end;
372
373         /* Locate block description */
374         raw_offset = peerdist_info_v1_block_offset ( info, segment->index );
375         if ( raw_offset < 0 ) {
376                 rc = raw_offset;
377                 return rc;
378         }
379
380         /* Get block hash */
381         raw_offset += offsetof ( typeof ( raw ), hash[block->index] );
382         if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset,
383                                         digestsize ) ) != 0 ) {
384                 DBGC ( info, "PCCRC %p segment %d block %d could not get "
385                        "hash: %s\n", info, segment->index, block->index,
386                        strerror ( rc ) );
387                 return rc;
388         }
389
390         return 0;
391 }
392
393 /** Content information version 1 operations */
394 static struct peerdist_info_operations peerdist_info_v1_operations = {
395         .info = peerdist_info_v1,
396         .segment = peerdist_info_v1_segment,
397         .block = peerdist_info_v1_block,
398 };
399
400 /******************************************************************************
401  *
402  * Content Information version 2
403  *
404  ******************************************************************************
405  */
406
407 /** A segment cursor */
408 struct peerdist_info_v2_cursor {
409         /** Raw data offset */
410         size_t offset;
411         /** Number of segments remaining within this chunk */
412         unsigned int remaining;
413         /** Accumulated segment length */
414         size_t len;
415 };
416
417 /**
418  * Initialise segment cursor
419  *
420  * @v cursor            Segment cursor
421  */
422 static inline void
423 peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) {
424
425         /* Initialise cursor */
426         cursor->offset = ( sizeof ( struct peerdist_info_v2 ) +
427                            sizeof ( struct peerdist_info_v2_chunk ) );
428         cursor->remaining = 0;
429         cursor->len = 0;
430 }
431
432 /**
433  * Update segment cursor to next segment description
434  *
435  * @v info              Content information
436  * @v offset            Current offset
437  * @v remaining         Number of segments remaining within this chunk
438  * @ret rc              Return status code
439  */
440 static int
441 peerdist_info_v2_cursor_next ( const struct peerdist_info *info,
442                                struct peerdist_info_v2_cursor *cursor ) {
443         size_t digestsize = info->digestsize;
444         peerdist_info_v2_segment_t ( digestsize ) raw;
445         struct peerdist_info_v2_chunk chunk;
446         int rc;
447
448         /* Get chunk description if applicable */
449         if ( ! cursor->remaining ) {
450
451                 /* Get chunk description */
452                 if ( ( rc = peerdist_info_get ( info, &chunk,
453                                                 ( cursor->offset -
454                                                   sizeof ( chunk ) ),
455                                                 sizeof ( chunk ) ) ) != 0 )
456                         return rc;
457
458                 /* Update number of segments remaining */
459                 cursor->remaining = ( be32_to_cpu ( chunk.len ) /
460                                       sizeof ( raw ) );
461         }
462
463         /* Get segment description header */
464         if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset,
465                                         sizeof ( raw.segment ) ) ) != 0 )
466                 return rc;
467
468         /* Update cursor */
469         cursor->offset += sizeof ( raw );
470         cursor->remaining--;
471         if ( ! cursor->remaining )
472                 cursor->offset += sizeof ( chunk );
473         cursor->len += be32_to_cpu ( raw.segment.len );
474
475         return 0;
476 }
477
478 /**
479  * Get number of segments and total length
480  *
481  * @v info              Content information
482  * @v len               Length to fill in
483  * @ret rc              Number of segments, or negative error
484  */
485 static int peerdist_info_v2_segments ( const struct peerdist_info *info,
486                                        size_t *len ) {
487         struct peerdist_info_v2_cursor cursor;
488         unsigned int segments;
489         int rc;
490
491         /* Iterate over all segments */
492         for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ;
493               cursor.offset < info->raw.len ; segments++ ) {
494
495                 /* Update segment cursor */
496                 if ( ( rc = peerdist_info_v2_cursor_next ( info,
497                                                            &cursor ) ) != 0 ) {
498                         DBGC ( info, "PCCRC %p segment %d could not update "
499                                "segment cursor: %s\n",
500                                info, segments, strerror ( rc ) );
501                         return rc;
502                 }
503         }
504
505         /* Record accumulated length */
506         *len = cursor.len;
507
508         return segments;
509 }
510
511 /**
512  * Populate content information
513  *
514  * @v info              Content information to fill in
515  * @ret rc              Return status code
516  */
517 static int peerdist_info_v2 ( struct peerdist_info *info ) {
518         struct peerdist_info_v2 raw;
519         size_t len = 0;
520         int segments;
521         int rc;
522
523         /* Get raw header */
524         if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){
525                 DBGC ( info, "PCCRC %p could not get V2 content information: "
526                        "%s\n", info, strerror ( rc ) );
527                 return rc;
528         }
529         assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) );
530
531         /* Determine hash algorithm */
532         switch ( raw.hash ) {
533         case PEERDIST_INFO_V2_HASH_SHA512_TRUNC :
534                 info->digest = &sha512_algorithm;
535                 info->digestsize = ( 256 / 8 );
536                 break;
537         default:
538                 DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n",
539                        info, raw.hash );
540                 return -ENOTSUP;
541         }
542         assert ( info->digest != NULL );
543         DBGC2 ( info, "PCCRC %p using %s[%zd]\n",
544                 info, info->digest->name, ( info->digestsize * 8 ) );
545
546         /* Calculate number of segments and total length */
547         segments = peerdist_info_v2_segments ( info, &len );
548         if ( segments < 0 ) {
549                 rc = segments;
550                 DBGC ( info, "PCCRC %p could not get segment count and length: "
551                        "%s\n", info, strerror ( rc ) );
552                 return rc;
553         }
554         info->segments = segments;
555
556         /* Calculate range start offset */
557         info->range.start = be64_to_cpu ( raw.offset );
558
559         /* Calculate trimmed range start offset */
560         info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) );
561
562         /* Calculate range end offset */
563         info->range.end = ( info->range.start + len );
564
565         /* Calculate trimmed range end offset */
566         info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) :
567                            info->range.end );
568
569         return 0;
570 }
571
572 /**
573  * Populate content information segment
574  *
575  * @v segment           Content information segment to fill in
576  * @ret rc              Return status code
577  */
578 static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) {
579         const struct peerdist_info *info = segment->info;
580         size_t digestsize = info->digestsize;
581         peerdist_info_v2_segment_t ( digestsize ) raw;
582         struct peerdist_info_v2_cursor cursor;
583         unsigned int index;
584         size_t len;
585         int rc;
586
587         /* Sanity checks */
588         assert ( segment->index < info->segments );
589
590         /* Iterate over all segments before the target segment */
591         for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ;
592               index < segment->index ; index++ ) {
593
594                 /* Update segment cursor */
595                 if ( ( rc = peerdist_info_v2_cursor_next ( info,
596                                                            &cursor ) ) != 0 ) {
597                         DBGC ( info, "PCCRC %p segment %d could not update "
598                                "segment cursor: %s\n",
599                                info, index, strerror ( rc ) );
600                         return rc;
601                 }
602         }
603
604         /* Get raw description */
605         if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset,
606                                         sizeof ( raw ) ) ) != 0 ) {
607                 DBGC ( info, "PCCRC %p segment %d could not get segment "
608                        "description: %s\n",
609                        info, segment->index, strerror ( rc ) );
610                 return rc;
611         }
612
613         /* Calculate start offset of this segment */
614         segment->range.start = ( info->range.start + cursor.len );
615
616         /* Calculate end offset of this segment */
617         len = be32_to_cpu ( raw.segment.len );
618         segment->range.end = ( segment->range.start + len );
619
620         /* Model as a segment containing a single block */
621         segment->blocks = 1;
622         segment->blksize = len;
623
624         /* Calculate segment hashes */
625         peerdist_info_segment_hash ( segment, raw.hash, raw.secret );
626
627         return 0;
628 }
629
630 /**
631  * Populate content information block
632  *
633  * @v block             Content information block to fill in
634  * @ret rc              Return status code
635  */
636 static int peerdist_info_v2_block ( struct peerdist_info_block *block ) {
637         const struct peerdist_info_segment *segment = block->segment;
638         const struct peerdist_info *info = segment->info;
639         size_t digestsize = info->digestsize;
640
641         /* Sanity checks */
642         assert ( block->index < segment->blocks );
643
644         /* Model as a block covering the whole segment */
645         memcpy ( &block->range, &segment->range, sizeof ( block->range ) );
646         memcpy ( block->hash, segment->hash, digestsize );
647
648         return 0;
649 }
650
651 /** Content information version 2 operations */
652 static struct peerdist_info_operations peerdist_info_v2_operations = {
653         .block = peerdist_info_v2_block,
654         .segment = peerdist_info_v2_segment,
655         .info = peerdist_info_v2,
656 };
657
658 /******************************************************************************
659  *
660  * Content Information
661  *
662  ******************************************************************************
663  */
664
665 /**
666  * Populate content information
667  *
668  * @v data              Raw data
669  * @v len               Length of raw data
670  * @v info              Content information to fill in
671  * @ret rc              Return status code
672  */
673 int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) {
674         union peerdist_info_version version;
675         int rc;
676
677         /* Initialise structure */
678         memset ( info, 0, sizeof ( *info ) );
679         info->raw.data = data;
680         info->raw.len = len;
681
682         /* Get version */
683         if ( ( rc = peerdist_info_get ( info, &version, 0,
684                                         sizeof ( version ) ) ) != 0 ) {
685                 DBGC ( info, "PCCRC %p could not get version: %s\n",
686                        info, strerror ( rc ) );
687                 return rc;
688         }
689         DBGC2 ( info, "PCCRC %p version %d.%d\n",
690                 info, version.major, version.minor );
691
692         /* Determine version */
693         switch ( version.raw ) {
694         case cpu_to_le16 ( PEERDIST_INFO_V1 ) :
695                 info->op = &peerdist_info_v1_operations;
696                 break;
697         case cpu_to_le16 ( PEERDIST_INFO_V2 ) :
698                 info->op = &peerdist_info_v2_operations;
699                 break;
700         default:
701                 DBGC ( info, "PCCRC %p unsupported version %d.%d\n",
702                        info, version.major, version.minor );
703                 return -ENOTSUP;
704         }
705         assert ( info->op != NULL );
706         assert ( info->op->info != NULL );
707
708         /* Populate content information */
709         if ( ( rc = info->op->info ( info ) ) != 0 )
710                 return rc;
711
712         DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with "
713                 "%d segments\n", info, info->range.start, info->range.end,
714                 info->trim.start, info->trim.end, info->segments );
715         return 0;
716 }
717
718 /**
719  * Populate content information segment
720  *
721  * @v info              Content information
722  * @v segment           Content information segment to fill in
723  * @v index             Segment index
724  * @ret rc              Return status code
725  */
726 int peerdist_info_segment ( const struct peerdist_info *info,
727                             struct peerdist_info_segment *segment,
728                             unsigned int index ) {
729         int rc;
730
731         /* Sanity checks */
732         assert ( info != NULL );
733         assert ( info->op != NULL );
734         assert ( info->op->segment != NULL );
735         if ( index >= info->segments ) {
736                 DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n",
737                        info, index, info->segments );
738                 return -ERANGE;
739         }
740
741         /* Initialise structure */
742         memset ( segment, 0, sizeof ( *segment ) );
743         segment->info = info;
744         segment->index = index;
745
746         /* Populate content information segment */
747         if ( ( rc = info->op->segment ( segment ) ) != 0 )
748                 return rc;
749
750         DBGC2 ( info, "PCCRC %p segment %d range [%08zx,%08zx) with %d "
751                 "blocks\n", info, segment->index, segment->range.start,
752                 segment->range.end, segment->blocks );
753         DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index,
754                 peerdist_info_hash_ntoa ( info, segment->hash ) );
755         DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index,
756                 peerdist_info_hash_ntoa ( info, segment->secret ) );
757         DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index,
758                 peerdist_info_hash_ntoa ( info, segment->id ) );
759         return 0;
760 }
761
762 /**
763  * Populate content information block
764  *
765  * @v segment           Content information segment
766  * @v block             Content information block to fill in
767  * @v index             Block index
768  * @ret rc              Return status code
769  */
770 int peerdist_info_block ( const struct peerdist_info_segment *segment,
771                           struct peerdist_info_block *block,
772                           unsigned int index ) {
773         const struct peerdist_info *info = segment->info;
774         size_t start;
775         size_t end;
776         int rc;
777
778         /* Sanity checks */
779         assert ( segment != NULL );
780         assert ( info != NULL );
781         assert ( info->op != NULL );
782         assert ( info->op->block != NULL );
783         if ( index >= segment->blocks ) {
784                 DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of "
785                        "range\n", info, segment->index, index, segment->blocks);
786                 return -ERANGE;
787         }
788
789         /* Initialise structure */
790         memset ( block, 0, sizeof ( *block ) );
791         block->segment = segment;
792         block->index = index;
793
794         /* Populate content information block */
795         if ( ( rc = info->op->block ( block ) ) != 0 )
796                 return rc;
797
798         /* Calculate trimmed range */
799         start = block->range.start;
800         if ( start < info->trim.start )
801                 start = info->trim.start;
802         end = block->range.end;
803         if ( end > info->trim.end )
804                 end = info->trim.end;
805         if ( end < start )
806                 end = start;
807         block->trim.start = start;
808         block->trim.end = end;
809
810         DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n",
811                 info, segment->index, block->index,
812                 peerdist_info_hash_ntoa ( info, block->hash ) );
813         DBGC2 ( info, "PCCRC %p segment %d block %d range [%08zx,%08zx) covers "
814                 "[%08zx,%08zx)\n", info, segment->index, block->index,
815                 block->range.start, block->range.end, block->trim.start,
816                 block->trim.end );
817         return 0;
818 }