You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pccrc.c 23KB

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