~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/net/pccrc.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}