[infiniband] Allow for the creation of multicast groups
[ipxe.git] / 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 }