These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / peerblk.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 <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <ipxe/http.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/xfer.h>
32 #include <ipxe/uri.h>
33 #include <ipxe/timer.h>
34 #include <ipxe/profile.h>
35 #include <ipxe/fault.h>
36 #include <ipxe/pccrr.h>
37 #include <ipxe/peerblk.h>
38
39 /** @file
40  *
41  * Peer Content Caching and Retrieval (PeerDist) protocol block downloads
42  *
43  */
44
45 /** PeerDist decryption chunksize
46  *
47  * This is a policy decision.
48  */
49 #define PEERBLK_DECRYPT_CHUNKSIZE 2048
50
51 /** PeerDist raw block download attempt initial progress timeout
52  *
53  * This is a policy decision.
54  */
55 #define PEERBLK_RAW_OPEN_TIMEOUT ( 10 * TICKS_PER_SEC )
56
57 /** PeerDist raw block download attempt ongoing progress timeout
58  *
59  * This is a policy decision.
60  */
61 #define PEERBLK_RAW_RX_TIMEOUT ( 15 * TICKS_PER_SEC )
62
63 /** PeerDist retrieval protocol block download attempt initial progress timeout
64  *
65  * This is a policy decision.
66  */
67 #define PEERBLK_RETRIEVAL_OPEN_TIMEOUT ( 3 * TICKS_PER_SEC )
68
69 /** PeerDist retrieval protocol block download attempt ongoing progress timeout
70  *
71  * This is a policy decision.
72  */
73 #define PEERBLK_RETRIEVAL_RX_TIMEOUT ( 5 * TICKS_PER_SEC )
74
75 /** PeerDist maximum number of full download attempt cycles
76  *
77  * This is the maximum number of times that we will try a full cycle
78  * of download attempts (i.e. a retrieval protocol download attempt
79  * from each discovered peer plus a raw download attempt from the
80  * origin server).
81  *
82  * This is a policy decision.
83  */
84 #define PEERBLK_MAX_ATTEMPT_CYCLES 4
85
86 /** PeerDist block download profiler */
87 static struct profiler peerblk_download_profiler __profiler =
88         { .name = "peerblk.download" };
89
90 /** PeerDist block download attempt success profiler */
91 static struct profiler peerblk_attempt_success_profiler __profiler =
92         { .name = "peerblk.attempt.success" };
93
94 /** PeerDist block download attempt failure profiler */
95 static struct profiler peerblk_attempt_failure_profiler __profiler =
96         { .name = "peerblk.attempt.failure" };
97
98 /** PeerDist block download attempt timeout profiler */
99 static struct profiler peerblk_attempt_timeout_profiler __profiler =
100         { .name = "peerblk.attempt.timeout" };
101
102 /** PeerDist block download discovery success profiler */
103 static struct profiler peerblk_discovery_success_profiler __profiler =
104         { .name = "peerblk.discovery.success" };
105
106 /** PeerDist block download discovery timeout profiler */
107 static struct profiler peerblk_discovery_timeout_profiler __profiler =
108         { .name = "peerblk.discovery.timeout" };
109
110 /**
111  * Get profiling timestamp
112  *
113  * @ret timestamp       Timestamp
114  */
115 static inline __attribute__ (( always_inline )) unsigned long
116 peerblk_timestamp ( void ) {
117
118         if ( PROFILING ) {
119                 return currticks();
120         } else {
121                 return 0;
122         }
123 }
124
125 /**
126  * Free PeerDist block download
127  *
128  * @v refcnt            Reference count
129  */
130 static void peerblk_free ( struct refcnt *refcnt ) {
131         struct peerdist_block *peerblk =
132                 container_of ( refcnt, struct peerdist_block, refcnt );
133
134         uri_put ( peerblk->uri );
135         free ( peerblk->cipherctx );
136         free ( peerblk );
137 }
138
139 /**
140  * Reset PeerDist block download attempt
141  *
142  * @v peerblk           PeerDist block download
143  * @v rc                Reason for reset
144  */
145 static void peerblk_reset ( struct peerdist_block *peerblk, int rc ) {
146
147         /* Stop decryption process */
148         process_del ( &peerblk->process );
149
150         /* Stop timer */
151         stop_timer ( &peerblk->timer );
152
153         /* Abort any current download attempt */
154         intf_restart ( &peerblk->raw, rc );
155         intf_restart ( &peerblk->retrieval, rc );
156
157         /* Empty received data buffer */
158         xferbuf_free ( &peerblk->buffer );
159         peerblk->pos = 0;
160
161         /* Reset digest and free cipher context */
162         digest_init ( peerblk->digest, peerblk->digestctx );
163         free ( peerblk->cipherctx );
164         peerblk->cipherctx = NULL;
165         peerblk->cipher = NULL;
166
167         /* Reset trim thresholds */
168         peerblk->start = ( peerblk->trim.start - peerblk->range.start );
169         peerblk->end = ( peerblk->trim.end - peerblk->range.start );
170         assert ( peerblk->start <= peerblk->end );
171 }
172
173 /**
174  * Close PeerDist block download
175  *
176  * @v peerblk           PeerDist block download
177  * @v rc                Reason for close
178  */
179 static void peerblk_close ( struct peerdist_block *peerblk, int rc ) {
180         unsigned long now = peerblk_timestamp();
181
182         /* Profile overall block download */
183         profile_custom ( &peerblk_download_profiler,
184                          ( now - peerblk->started ) );
185
186         /* Reset download attempt */
187         peerblk_reset ( peerblk, rc );
188
189         /* Close discovery */
190         peerdisc_close ( &peerblk->discovery );
191
192         /* Shut down all interfaces */
193         intf_shutdown ( &peerblk->retrieval, rc );
194         intf_shutdown ( &peerblk->raw, rc );
195         intf_shutdown ( &peerblk->xfer, rc );
196 }
197
198 /**
199  * Calculate offset within overall download
200  *
201  * @v peerblk           PeerDist block download
202  * @v pos               Position within incoming data stream
203  * @ret offset          Offset within overall download
204  */
205 static inline __attribute__ (( always_inline )) size_t
206 peerblk_offset ( struct peerdist_block *peerblk, size_t pos ) {
207
208         return ( ( pos - peerblk->start ) + peerblk->offset );
209 }
210
211 /**
212  * Deliver download attempt data block
213  *
214  * @v peerblk           PeerDist block download
215  * @v iobuf             I/O buffer
216  * @v meta              Original data transfer metadata
217  * @v pos               Position within incoming data stream
218  * @ret rc              Return status code
219  */
220 static int peerblk_deliver ( struct peerdist_block *peerblk,
221                              struct io_buffer *iobuf,
222                              struct xfer_metadata *meta, size_t pos ) {
223         struct xfer_metadata xfer_meta;
224         size_t len = iob_len ( iobuf );
225         size_t start = pos;
226         size_t end = ( pos + len );
227         int rc;
228
229         /* Discard zero-length packets and packets which lie entirely
230          * outside the trimmed range.
231          */
232         if ( ( start >= peerblk->end ) || ( end <= peerblk->start ) ||
233              ( len == 0 ) ) {
234                 free_iob ( iobuf );
235                 return 0;
236         }
237
238         /* Truncate data to within trimmed range */
239         if ( start < peerblk->start ) {
240                 iob_pull ( iobuf, ( peerblk->start - start ) );
241                 start = peerblk->start;
242         }
243         if ( end > peerblk->end ) {
244                 iob_unput ( iobuf, ( end - peerblk->end ) );
245                 end = peerblk->end;
246         }
247
248         /* Construct metadata */
249         memcpy ( &xfer_meta, meta, sizeof ( xfer_meta ) );
250         xfer_meta.flags |= XFER_FL_ABS_OFFSET;
251         xfer_meta.offset = peerblk_offset ( peerblk, start );
252
253         /* Deliver data */
254         if ( ( rc = xfer_deliver ( &peerblk->xfer, iob_disown ( iobuf ),
255                                    &xfer_meta ) ) != 0 ) {
256                 DBGC ( peerblk, "PEERBLK %p %d.%d could not deliver data: %s\n",
257                        peerblk, peerblk->segment, peerblk->block,
258                        strerror ( rc ) );
259                 return rc;
260         }
261
262         return 0;
263 }
264
265 /**
266  * Finish PeerDist block download attempt
267  *
268  * @v peerblk           PeerDist block download
269  * @v rc                Reason for close
270  */
271 static void peerblk_done ( struct peerdist_block *peerblk, int rc ) {
272         struct digest_algorithm *digest = peerblk->digest;
273         uint8_t hash[digest->digestsize];
274         unsigned long now = peerblk_timestamp();
275
276         /* Check for errors on completion */
277         if ( rc != 0 ) {
278                 DBGC ( peerblk, "PEERBLK %p %d.%d attempt failed: %s\n",
279                        peerblk, peerblk->segment, peerblk->block,
280                        strerror ( rc ) );
281                 goto err;
282         }
283
284         /* Check digest */
285         digest_final ( digest, peerblk->digestctx, hash );
286         if ( memcmp ( hash, peerblk->hash, peerblk->digestsize ) != 0 ) {
287                 DBGC ( peerblk, "PEERBLK %p %d.%d digest mismatch:\n",
288                        peerblk, peerblk->segment, peerblk->block );
289                 DBGC_HDA ( peerblk, 0, hash, peerblk->digestsize );
290                 DBGC_HDA ( peerblk, 0, peerblk->hash, peerblk->digestsize );
291                 rc = -EIO;
292                 goto err;
293         }
294
295         /* Profile successful attempt */
296         profile_custom ( &peerblk_attempt_success_profiler,
297                          ( now - peerblk->attempted ) );
298
299         /* Close download */
300         peerblk_close ( peerblk, 0 );
301         return;
302
303  err:
304         /* Record failure reason and schedule a retry attempt */
305         profile_custom ( &peerblk_attempt_failure_profiler,
306                          ( now - peerblk->attempted ) );
307         peerblk_reset ( peerblk, rc );
308         peerblk->rc = rc;
309         start_timer_nodelay ( &peerblk->timer );
310 }
311
312 /******************************************************************************
313  *
314  * Raw block download attempts (using an HTTP range request)
315  *
316  ******************************************************************************
317  */
318
319 /**
320  * Open PeerDist raw block download attempt
321  *
322  * @v peerblk           PeerDist block download
323  * @ret rc              Return status code
324  */
325 static int peerblk_raw_open ( struct peerdist_block *peerblk ) {
326         struct http_request_range range;
327         int rc;
328
329         DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting raw range request\n",
330                 peerblk, peerblk->segment, peerblk->block );
331
332         /* Construct HTTP range */
333         memset ( &range, 0, sizeof ( range ) );
334         range.start = peerblk->range.start;
335         range.len = ( peerblk->range.end - peerblk->range.start );
336
337         /* Initiate range request to retrieve block */
338         if ( ( rc = http_open ( &peerblk->raw, &http_get, peerblk->uri,
339                                 &range, NULL ) ) != 0 ) {
340                 DBGC ( peerblk, "PEERBLK %p %d.%d could not create range "
341                        "request: %s\n", peerblk, peerblk->segment,
342                        peerblk->block, strerror ( rc ) );
343                 return rc;
344         }
345
346         /* Annul HTTP connection (for testing) if applicable.  Do not
347          * report as an immediate error, in order to test our ability
348          * to recover from a totally unresponsive HTTP server.
349          */
350         if ( inject_fault ( PEERBLK_ANNUL_RATE ) )
351                 intf_restart ( &peerblk->raw, 0 );
352
353         return 0;
354 }
355
356 /**
357  * Receive PeerDist raw data
358  *
359  * @v peerblk           PeerDist block download
360  * @v iobuf             I/O buffer
361  * @v meta              Data transfer metadata
362  * @ret rc              Return status code
363  */
364 static int peerblk_raw_rx ( struct peerdist_block *peerblk,
365                             struct io_buffer *iobuf,
366                             struct xfer_metadata *meta ) {
367         size_t len = iob_len ( iobuf );
368         size_t pos = peerblk->pos;
369         size_t mid = ( ( peerblk->range.end - peerblk->range.start ) / 2 );
370         int rc;
371
372         /* Corrupt received data (for testing) if applicable */
373         inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len );
374
375         /* Fail if data is delivered out of order, since the streaming
376          * digest requires strict ordering.
377          */
378         if ( ( rc = xfer_check_order ( meta, &peerblk->pos, len ) ) != 0 )
379                 goto err;
380
381         /* Add data to digest */
382         digest_update ( peerblk->digest, peerblk->digestctx, iobuf->data, len );
383
384         /* Deliver data */
385         if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta,
386                                       pos ) ) != 0 )
387                 goto err;
388
389         /* Extend download attempt timer */
390         start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_RX_TIMEOUT );
391
392         /* Stall download attempt (for testing) if applicable */
393         if ( ( pos < mid ) && ( ( pos + len ) >= mid ) &&
394              ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) {
395                 intf_restart ( &peerblk->raw, rc );
396         }
397
398         return 0;
399
400  err:
401         free_iob ( iobuf );
402         peerblk_done ( peerblk, rc );
403         return rc;
404 }
405
406 /**
407  * Close PeerDist raw block download attempt
408  *
409  * @v peerblk           PeerDist block download
410  * @v rc                Reason for close
411  */
412 static void peerblk_raw_close ( struct peerdist_block *peerblk, int rc ) {
413
414         /* Restart interface */
415         intf_restart ( &peerblk->raw, rc );
416
417         /* Fail immediately if we have an error */
418         if ( rc != 0 )
419                 goto done;
420
421         /* Abort download attempt (for testing) if applicable */
422         if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
423                 goto done;
424
425  done:
426         /* Complete download attempt */
427         peerblk_done ( peerblk, rc );
428 }
429
430 /******************************************************************************
431  *
432  * Retrieval protocol block download attempts (using HTTP POST)
433  *
434  ******************************************************************************
435  */
436
437 /**
438  * Construct PeerDist retrieval protocol URI
439  *
440  * @v location          Peer location
441  * @ret uri             Retrieval URI, or NULL on error
442  */
443 static struct uri * peerblk_retrieval_uri ( const char *location ) {
444         char uri_string[ 7 /* "http://" */ + strlen ( location ) +
445                          sizeof ( PEERDIST_MAGIC_PATH /* includes NUL */ ) ];
446
447         /* Construct URI string */
448         snprintf ( uri_string, sizeof ( uri_string ),
449                    ( "http://%s" PEERDIST_MAGIC_PATH ), location );
450
451         /* Parse URI string */
452         return parse_uri ( uri_string );
453 }
454
455 /**
456  * Open PeerDist retrieval protocol block download attempt
457  *
458  * @v peerblk           PeerDist block download
459  * @v location          Peer location
460  * @ret rc              Return status code
461  */
462 static int peerblk_retrieval_open ( struct peerdist_block *peerblk,
463                                     const char *location ) {
464         size_t digestsize = peerblk->digestsize;
465         peerdist_msg_getblks_t ( digestsize, 1, 0 ) req;
466         peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *rsp;
467         struct http_request_content content;
468         struct uri *uri;
469         int rc;
470
471         DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting retrieval from %s\n",
472                 peerblk, peerblk->segment, peerblk->block, location );
473
474         /* Construct block fetch request */
475         memset ( &req, 0, sizeof ( req ) );
476         req.getblks.hdr.version.raw = htonl ( PEERDIST_MSG_GETBLKS_VERSION );
477         req.getblks.hdr.type = htonl ( PEERDIST_MSG_GETBLKS_TYPE );
478         req.getblks.hdr.len = htonl ( sizeof ( req ) );
479         req.getblks.hdr.algorithm = htonl ( PEERDIST_MSG_AES_128_CBC );
480         req.segment.segment.digestsize = htonl ( digestsize );
481         memcpy ( req.segment.id, peerblk->id, digestsize );
482         req.ranges.ranges.count = htonl ( 1 );
483         req.ranges.range[0].first = htonl ( peerblk->block );
484         req.ranges.range[0].count = htonl ( 1 );
485
486         /* Construct POST request content */
487         memset ( &content, 0, sizeof ( content ) );
488         content.data = &req;
489         content.len = sizeof ( req );
490
491         /* Construct URI */
492         if ( ( uri = peerblk_retrieval_uri ( location ) ) == NULL ) {
493                 rc = -ENOMEM;
494                 goto err_uri;
495         }
496
497         /* Update trim thresholds */
498         peerblk->start += offsetof ( typeof ( *rsp ), msg.vrf );
499         peerblk->end += offsetof ( typeof ( *rsp ), msg.vrf );
500
501         /* Initiate HTTP POST to retrieve block */
502         if ( ( rc = http_open ( &peerblk->retrieval, &http_post, uri,
503                                 NULL, &content ) ) != 0 ) {
504                 DBGC ( peerblk, "PEERBLK %p %d.%d could not create retrieval "
505                        "request: %s\n", peerblk, peerblk->segment,
506                        peerblk->block, strerror ( rc ) );
507                 goto err_open;
508         }
509
510         /* Annul HTTP connection (for testing) if applicable.  Do not
511          * report as an immediate error, in order to test our ability
512          * to recover from a totally unresponsive HTTP server.
513          */
514         if ( inject_fault ( PEERBLK_ANNUL_RATE ) )
515                 intf_restart ( &peerblk->retrieval, 0 );
516
517  err_open:
518         uri_put ( uri );
519  err_uri:
520         return rc;
521 }
522
523 /**
524  * Receive PeerDist retrieval protocol data
525  *
526  * @v peerblk           PeerDist block download
527  * @v iobuf             I/O buffer
528  * @v meta              Data transfer metadata
529  * @ret rc              Return status code
530  */
531 static int peerblk_retrieval_rx ( struct peerdist_block *peerblk,
532                                   struct io_buffer *iobuf,
533                                   struct xfer_metadata *meta ) {
534         size_t len = iob_len ( iobuf );
535         size_t start;
536         size_t end;
537         size_t before;
538         size_t after;
539         size_t cut;
540         int rc;
541
542         /* Some genius at Microsoft thought it would be a great idea
543          * to place the AES-CBC initialisation vector *after* the
544          * encrypted data, thereby making it logically impossible to
545          * decrypt each packet as it arrives.
546          *
547          * To work around this mindless stupidity, we deliver the
548          * ciphertext as-is and later use xfer_buffer() to obtain
549          * access to the underlying data transfer buffer in order to
550          * perform the decryption.
551          *
552          * There will be some data both before and after the bytes
553          * corresponding to the trimmed plaintext: a MSG_BLK
554          * header/footer, some block padding for the AES-CBC cipher,
555          * and a possibly large quantity of unwanted ciphertext which
556          * is excluded from the trimmed content range.  We store this
557          * data in a local data transfer buffer.  If the amount of
558          * data to be stored is too large, we will fail allocation and
559          * so eventually fall back to using a range request (which
560          * does not require this kind of temporary storage
561          * allocation).
562          */
563
564         /* Corrupt received data (for testing) if applicable */
565         inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len );
566
567         /* Calculate start and end positions of this buffer */
568         start = peerblk->pos;
569         if ( meta->flags & XFER_FL_ABS_OFFSET )
570                 start = 0;
571         start += meta->offset;
572         end = ( start + len );
573
574         /* Buffer any data before the trimmed content */
575         if ( ( start < peerblk->start ) && ( len > 0 ) ) {
576
577                 /* Calculate length of data before the trimmed content */
578                 before = ( peerblk->start - start );
579                 if ( before > len )
580                         before = len;
581
582                 /* Buffer data before the trimmed content */
583                 if ( ( rc = xferbuf_write ( &peerblk->buffer, start,
584                                             iobuf->data, before ) ) != 0 ) {
585                         DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer "
586                                "data: %s\n", peerblk, peerblk->segment,
587                                peerblk->block, strerror ( rc ) );
588                         goto err;
589                 }
590         }
591
592         /* Buffer any data after the trimmed content */
593         if ( ( end > peerblk->end ) && ( len > 0 ) ) {
594
595                 /* Calculate length of data after the trimmed content */
596                 after = ( end - peerblk->end );
597                 if ( after > len )
598                         after = len;
599
600                 /* Buffer data after the trimmed content */
601                 cut = ( peerblk->end - peerblk->start );
602                 if ( ( rc = xferbuf_write ( &peerblk->buffer,
603                                             ( end - after - cut ),
604                                             ( iobuf->data + len - after ),
605                                             after ) ) != 0 ) {
606                         DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer "
607                                "data: %s\n", peerblk, peerblk->segment,
608                                peerblk->block, strerror ( rc ) );
609                         goto err;
610                 }
611         }
612
613         /* Deliver any remaining data */
614         if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta,
615                                       start ) ) != 0 )
616                 goto err;
617
618         /* Update position */
619         peerblk->pos = end;
620
621         /* Extend download attempt timer */
622         start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_RX_TIMEOUT );
623
624         /* Stall download attempt (for testing) if applicable */
625         if ( ( start < peerblk->end ) && ( end >= peerblk->end ) &&
626              ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) {
627                 intf_restart ( &peerblk->retrieval, rc );
628         }
629
630         return 0;
631
632  err:
633         free_iob ( iobuf );
634         peerblk_done ( peerblk, rc );
635         return rc;
636 }
637
638 /**
639  * Parse retrieval protocol message header
640  *
641  * @v peerblk           PeerDist block download
642  * @ret rc              Return status code
643  */
644 static int peerblk_parse_header ( struct peerdist_block *peerblk ) {
645         struct {
646                 struct peerdist_msg_transport_header hdr;
647                 struct peerdist_msg_header msg;
648         } __attribute__ (( packed )) *msg = peerblk->buffer.data;
649         struct cipher_algorithm *cipher;
650         size_t len = peerblk->buffer.len;
651         size_t keylen = 0;
652         int rc;
653
654         /* Check message length */
655         if ( len < sizeof ( *msg ) ) {
656                 DBGC ( peerblk, "PEERBLK %p %d.%d message too short for header "
657                        "(%zd bytes)\n", peerblk, peerblk->segment,
658                        peerblk->block, len );
659                 return -ERANGE;
660         }
661
662         /* Check message type */
663         if ( msg->msg.type != htonl ( PEERDIST_MSG_BLK_TYPE ) ) {
664                 DBGC ( peerblk, "PEERBLK %p %d.%d unexpected message type "
665                        "%#08x\n", peerblk, peerblk->segment, peerblk->block,
666                        ntohl ( msg->msg.type ) );
667                 return -EPROTO;
668         }
669
670         /* Determine cipher algorithm and key length */
671         cipher = &aes_cbc_algorithm;
672         switch ( msg->msg.algorithm ) {
673         case htonl ( PEERDIST_MSG_PLAINTEXT ) :
674                 cipher = NULL;
675                 break;
676         case htonl ( PEERDIST_MSG_AES_128_CBC ) :
677                 keylen = ( 128 / 8 );
678                 break;
679         case htonl ( PEERDIST_MSG_AES_192_CBC ) :
680                 keylen = ( 192 / 8 );
681                 break;
682         case htonl ( PEERDIST_MSG_AES_256_CBC ) :
683                 keylen = ( 256 / 8 );
684                 break;
685         default:
686                 DBGC ( peerblk, "PEERBLK %p %d.%d unrecognised algorithm "
687                        "%#08x\n", peerblk, peerblk->segment, peerblk->block,
688                        ntohl ( msg->msg.algorithm ) );
689                 return -ENOTSUP;
690         }
691         DBGC2 ( peerblk, "PEERBLK %p %d.%d using %s with %zd-bit key\n",
692                 peerblk, peerblk->segment, peerblk->block,
693                 ( cipher ? cipher->name : "plaintext" ), ( 8 * keylen ) );
694
695         /* Sanity check key length against maximum secret length */
696         if ( keylen > peerblk->digestsize ) {
697                 DBGC ( peerblk, "PEERBLK %p %d.%d %zd-byte secret too short "
698                        "for %zd-bit key\n", peerblk, peerblk->segment,
699                        peerblk->block, peerblk->digestsize, ( 8 * keylen ) );
700                 return -EPROTO;
701         }
702
703         /* Allocate cipher context.  Freeing the cipher context (on
704          * error or otherwise) is handled by peerblk_reset().
705          */
706         peerblk->cipher = cipher;
707         assert ( peerblk->cipherctx == NULL );
708         peerblk->cipherctx = malloc ( cipher->ctxsize );
709         if ( ! peerblk->cipherctx )
710                 return -ENOMEM;
711
712         /* Initialise cipher */
713         if ( ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret,
714                                     keylen ) ) != 0 ) {
715                 DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n",
716                        peerblk, peerblk->segment, peerblk->block,
717                        strerror ( rc ) );
718                 return rc;
719         }
720
721         return 0;
722 }
723
724 /**
725  * Parse retrieval protocol message segment and block details
726  *
727  * @v peerblk           PeerDist block download
728  * @v buf_len           Length of buffered data to fill in
729  * @ret rc              Return status code
730  */
731 static int peerblk_parse_block ( struct peerdist_block *peerblk,
732                                  size_t *buf_len ) {
733         size_t digestsize = peerblk->digestsize;
734         peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data;
735         size_t len = peerblk->buffer.len;
736         size_t data_len;
737         size_t total;
738
739         /* Check message length */
740         if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) {
741                 DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
742                        "zero-length data (%zd bytes)\n", peerblk,
743                        peerblk->segment, peerblk->block, len );
744                 return -ERANGE;
745         }
746
747         /* Check digest size */
748         if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) {
749                 DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n",
750                        peerblk, peerblk->segment, peerblk->block,
751                        ntohl ( msg->msg.segment.segment.digestsize ) );
752                 return -EPROTO;
753         }
754
755         /* Check segment ID */
756         if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) {
757                 DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n",
758                        peerblk, peerblk->segment, peerblk->block );
759                 return -EPROTO;
760         }
761
762         /* Check block ID */
763         if ( ntohl ( msg->msg.index ) != peerblk->block ) {
764                 DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n",
765                        peerblk, peerblk->segment, peerblk->block,
766                        ntohl ( msg->msg.index ) );
767                 return -EPROTO;
768         }
769
770         /* Check for missing blocks */
771         data_len = be32_to_cpu ( msg->msg.block.block.len );
772         if ( ! data_len ) {
773                 DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n",
774                        peerblk, peerblk->segment, peerblk->block );
775                 return -ENOENT;
776         }
777
778         /* Check for underlength blocks */
779         if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) {
780                 DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd "
781                        "bytes)\n", peerblk, peerblk->segment, peerblk->block,
782                        data_len );
783                 return -ERANGE;
784         }
785
786         /* Calculate buffered data length (i.e. excluding data which
787          * was delivered to the final data transfer buffer).
788          */
789         *buf_len = ( data_len - ( peerblk->end - peerblk->start ) );
790
791         /* Describe data before the trimmed content */
792         peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer;
793         peerblk->decrypt[PEERBLK_BEFORE].offset =
794                 offsetof ( typeof ( *msg ), msg.block.data );
795         peerblk->decrypt[PEERBLK_BEFORE].len =
796                 ( peerblk->start -
797                   offsetof ( typeof ( *msg ), msg.block.data ) );
798         total = peerblk->decrypt[PEERBLK_BEFORE].len;
799
800         /* Describe data within the trimmed content */
801         peerblk->decrypt[PEERBLK_DURING].offset =
802                 peerblk_offset ( peerblk, peerblk->start );
803         peerblk->decrypt[PEERBLK_DURING].len =
804                 ( peerblk->end - peerblk->start );
805         total += peerblk->decrypt[PEERBLK_DURING].len;
806
807         /* Describe data after the trimmed content */
808         peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer;
809         peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start;
810         peerblk->decrypt[PEERBLK_AFTER].len =
811                 ( offsetof ( typeof ( *msg ), msg.block.data )
812                   + *buf_len - peerblk->start );
813         total += peerblk->decrypt[PEERBLK_AFTER].len;
814
815         /* Sanity check */
816         assert ( total == be32_to_cpu ( msg->msg.block.block.len ) );
817
818         /* Initialise cipher and digest lengths */
819         peerblk->cipher_remaining = total;
820         peerblk->digest_remaining =
821                 ( peerblk->range.end - peerblk->range.start );
822         assert ( peerblk->cipher_remaining >= peerblk->digest_remaining );
823
824         return 0;
825 }
826
827 /**
828  * Parse retrieval protocol message useless details
829  *
830  * @v peerblk           PeerDist block download
831  * @v buf_len           Length of buffered data
832  * @v vrf_len           Length of uselessness to fill in
833  * @ret rc              Return status code
834  */
835 static int peerblk_parse_useless ( struct peerdist_block *peerblk,
836                                    size_t buf_len, size_t *vrf_len ) {
837         size_t digestsize = peerblk->digestsize;
838         peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg =
839                 peerblk->buffer.data;
840         size_t len = peerblk->buffer.len;
841
842         /* Check message length */
843         if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) {
844                 DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
845                        "zero-length uselessness (%zd bytes)\n", peerblk,
846                        peerblk->segment, peerblk->block, len );
847                 return -ERANGE;
848         }
849
850         /* Extract length of uselessness */
851         *vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len );
852
853         return 0;
854 }
855
856 /**
857  * Parse retrieval protocol message initialisation vector details
858  *
859  * @v peerblk           PeerDist block download
860  * @v buf_len           Length of buffered data
861  * @v vrf_len           Length of uselessness
862  * @ret rc              Return status code
863  */
864 static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len,
865                               size_t vrf_len ) {
866         size_t digestsize = peerblk->digestsize;
867         size_t blksize = peerblk->cipher->blocksize;
868         peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg =
869                 peerblk->buffer.data;
870         size_t len = peerblk->buffer.len;
871
872         /* Check message length */
873         if ( len < sizeof ( *msg ) ) {
874                 DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
875                        "initialisation vector (%zd bytes)\n", peerblk,
876                        peerblk->segment, peerblk->block, len );
877                 return -ERANGE;
878         }
879
880         /* Check initialisation vector size */
881         if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) {
882                 DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n",
883                        peerblk, peerblk->segment, peerblk->block,
884                        ntohl ( msg->msg.iv.iv.blksize ) );
885                 return -EPROTO;
886         }
887
888         /* Set initialisation vector */
889         cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data );
890
891         return 0;
892 }
893
894 /**
895  * Read from decryption buffers
896  *
897  * @v peerblk           PeerDist block download
898  * @v data              Data buffer
899  * @v len               Length to read
900  * @ret rc              Return status code
901  */
902 static int peerblk_decrypt_read ( struct peerdist_block *peerblk,
903                                   void *data, size_t len ) {
904         struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
905         size_t frag_len;
906         int rc;
907
908         /* Read from each decryption buffer in turn */
909         for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
910
911                 /* Calculate length to use from this buffer */
912                 frag_len = decrypt->len;
913                 if ( frag_len > len )
914                         frag_len = len;
915                 if ( ! frag_len )
916                         continue;
917
918                 /* Read from this buffer */
919                 if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset,
920                                            data, frag_len ) ) != 0 )
921                         return rc;
922         }
923
924         return 0;
925 }
926
927 /**
928  * Write to decryption buffers and update offsets and lengths
929  *
930  * @v peerblk           PeerDist block download
931  * @v data              Data buffer
932  * @v len               Length to read
933  * @ret rc              Return status code
934  */
935 static int peerblk_decrypt_write ( struct peerdist_block *peerblk,
936                                    const void *data, size_t len ) {
937         struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
938         size_t frag_len;
939         int rc;
940
941         /* Write to each decryption buffer in turn */
942         for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
943
944                 /* Calculate length to use from this buffer */
945                 frag_len = decrypt->len;
946                 if ( frag_len > len )
947                         frag_len = len;
948                 if ( ! frag_len )
949                         continue;
950
951                 /* Write to this buffer */
952                 if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset,
953                                             data, frag_len ) ) != 0 )
954                         return rc;
955
956                 /* Update offset and length */
957                 decrypt->offset += frag_len;
958                 decrypt->len -= frag_len;
959         }
960
961         return 0;
962 }
963
964 /**
965  * Decrypt one chunk of PeerDist retrieval protocol data
966  *
967  * @v peerblk           PeerDist block download
968  */
969 static void peerblk_decrypt ( struct peerdist_block *peerblk ) {
970         struct cipher_algorithm *cipher = peerblk->cipher;
971         struct digest_algorithm *digest = peerblk->digest;
972         struct xfer_buffer *xferbuf;
973         size_t cipher_len;
974         size_t digest_len;
975         void *data;
976         int rc;
977
978         /* Sanity check */
979         assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 );
980
981         /* Get the underlying data transfer buffer */
982         xferbuf = xfer_buffer ( &peerblk->xfer );
983         if ( ! xferbuf ) {
984                 DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data "
985                        "transfer buffer\n", peerblk, peerblk->segment,
986                        peerblk->block );
987                 rc = -ENOTSUP;
988                 goto err_xfer_buffer;
989         }
990         peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf;
991
992         /* Calculate cipher and digest lengths */
993         cipher_len = PEERBLK_DECRYPT_CHUNKSIZE;
994         if ( cipher_len > peerblk->cipher_remaining )
995                 cipher_len = peerblk->cipher_remaining;
996         digest_len = cipher_len;
997         if ( digest_len > peerblk->digest_remaining )
998                 digest_len = peerblk->digest_remaining;
999         assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 );
1000
1001         /* Allocate temporary data buffer */
1002         data = malloc ( cipher_len );
1003         if ( ! data ) {
1004                 rc = -ENOMEM;
1005                 goto err_alloc_data;
1006         }
1007
1008         /* Read ciphertext */
1009         if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){
1010                 DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: "
1011                        "%s\n", peerblk, peerblk->segment, peerblk->block,
1012                        strerror ( rc ) );
1013                 goto err_read;
1014         }
1015
1016         /* Decrypt data */
1017         cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len );
1018
1019         /* Add data to digest */
1020         digest_update ( digest, peerblk->digestctx, data, digest_len );
1021
1022         /* Write plaintext */
1023         if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){
1024                 DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: "
1025                        "%s\n", peerblk, peerblk->segment, peerblk->block,
1026                        strerror ( rc ) );
1027                 goto err_write;
1028         }
1029
1030         /* Consume input */
1031         peerblk->cipher_remaining -= cipher_len;
1032         peerblk->digest_remaining -= digest_len;
1033
1034         /* Free temporary data buffer */
1035         free ( data );
1036
1037         /* Continue processing until all input is consumed */
1038         if ( peerblk->cipher_remaining )
1039                 return;
1040
1041         /* Complete download attempt */
1042         peerblk_done ( peerblk, 0 );
1043         return;
1044
1045  err_write:
1046  err_read:
1047         free ( data );
1048  err_alloc_data:
1049  err_xfer_buffer:
1050         peerblk_done ( peerblk, rc );
1051 }
1052
1053 /**
1054  * Close PeerDist retrieval protocol block download attempt
1055  *
1056  * @v peerblk           PeerDist block download
1057  * @v rc                Reason for close
1058  */
1059 static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) {
1060         size_t buf_len;
1061         size_t vrf_len;
1062
1063         /* Restart interface */
1064         intf_restart ( &peerblk->retrieval, rc );
1065
1066         /* Fail immediately if we have an error */
1067         if ( rc != 0 )
1068                 goto done;
1069
1070         /* Abort download attempt (for testing) if applicable */
1071         if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
1072                 goto done;
1073
1074         /* Parse message header */
1075         if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 )
1076                 goto done;
1077
1078         /* Parse message segment and block details */
1079         if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 )
1080                 goto done;
1081
1082         /* If the block was plaintext, then there is nothing more to do */
1083         if ( ! peerblk->cipher )
1084                 goto done;
1085
1086         /* Parse message useless details */
1087         if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0)
1088                 goto done;
1089
1090         /* Parse message initialisation vector details */
1091         if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 )
1092                 goto done;
1093
1094         /* Fail if decryption length is not aligned to the cipher block size */
1095         if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) {
1096                 DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n",
1097                        peerblk, peerblk->segment, peerblk->block,
1098                        peerblk->cipher_remaining );
1099                 rc = -EPROTO;
1100                 goto done;
1101         }
1102
1103         /* Stop the download attempt timer: there is no point in
1104          * timing out while decrypting.
1105          */
1106         stop_timer ( &peerblk->timer );
1107
1108         /* Start decryption process */
1109         process_add ( &peerblk->process );
1110         return;
1111
1112  done:
1113         /* Complete download attempt */
1114         peerblk_done ( peerblk, rc );
1115 }
1116
1117 /******************************************************************************
1118  *
1119  * Retry policy
1120  *
1121  ******************************************************************************
1122  */
1123
1124 /**
1125  * Handle PeerDist retry timer expiry
1126  *
1127  * @v timer             Retry timer
1128  * @v over              Failure indicator
1129  */
1130 static void peerblk_expired ( struct retry_timer *timer, int over __unused ) {
1131         struct peerdist_block *peerblk =
1132                 container_of ( timer, struct peerdist_block, timer );
1133         struct peerdisc_segment *segment = peerblk->discovery.segment;
1134         struct peerdisc_peer *head;
1135         unsigned long now = peerblk_timestamp();
1136         const char *location;
1137         int rc;
1138
1139         /* Profile discovery timeout, if applicable */
1140         if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) {
1141                 profile_custom ( &peerblk_discovery_timeout_profiler,
1142                                  ( now - peerblk->started ) );
1143                 DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after "
1144                        "%ld ticks\n", peerblk, peerblk->segment,
1145                        peerblk->block, timer->timeout );
1146         }
1147
1148         /* Profile download timeout, if applicable */
1149         if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) {
1150                 profile_custom ( &peerblk_attempt_timeout_profiler,
1151                                  ( now - peerblk->attempted ) );
1152                 DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n",
1153                        peerblk, peerblk->segment, peerblk->block,
1154                        timer->timeout );
1155         }
1156
1157         /* Abort any current download attempt */
1158         peerblk_reset ( peerblk, -ETIMEDOUT );
1159
1160         /* Record attempt start time */
1161         peerblk->attempted = now;
1162
1163         /* If we have exceeded our maximum number of attempt cycles
1164          * (each cycle comprising a retrieval protocol download from
1165          * each peer in the list followed by a raw download from the
1166          * origin server), then abort the overall download.
1167          */
1168         head = list_entry ( &segment->peers, struct peerdisc_peer, list );
1169         if ( ( peerblk->peer == head ) &&
1170              ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) {
1171                 rc = peerblk->rc;
1172                 assert ( rc != 0 );
1173                 goto err;
1174         }
1175
1176         /* If we have not yet made any download attempts, then move to
1177          * the start of the peer list.
1178          */
1179         if ( peerblk->peer == NULL )
1180                 peerblk->peer = head;
1181
1182         /* Attempt retrieval protocol download from next usable peer */
1183         list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) {
1184
1185                 /* Attempt retrieval protocol download from this peer */
1186                 location = peerblk->peer->location;
1187                 if ( ( rc = peerblk_retrieval_open ( peerblk,
1188                                                      location ) ) != 0 ) {
1189                         /* Non-fatal: continue to try next peer */
1190                         continue;
1191                 }
1192
1193                 /* Start download attempt timer */
1194                 peerblk->rc = -ETIMEDOUT;
1195                 start_timer_fixed ( &peerblk->timer,
1196                                     PEERBLK_RETRIEVAL_OPEN_TIMEOUT );
1197                 return;
1198         }
1199
1200         /* Attempt raw download */
1201         if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 )
1202                 goto err;
1203
1204         /* Start download attempt timer */
1205         peerblk->rc = -ETIMEDOUT;
1206         start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT );
1207         return;
1208
1209  err:
1210         peerblk_close ( peerblk, rc );
1211 }
1212
1213 /**
1214  * Handle PeerDist peer discovery
1215  *
1216  * @v discovery         PeerDist discovery client
1217  */
1218 static void peerblk_discovered ( struct peerdisc_client *discovery ) {
1219         struct peerdist_block *peerblk =
1220                 container_of ( discovery, struct peerdist_block, discovery );
1221         unsigned long now = peerblk_timestamp();
1222
1223         /* Do nothing unless we are still waiting for the initial
1224          * discovery timeout.
1225          */
1226         if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) )
1227                 return;
1228
1229         /* Schedule an immediate retry */
1230         start_timer_nodelay ( &peerblk->timer );
1231
1232         /* Profile discovery success */
1233         profile_custom ( &peerblk_discovery_success_profiler,
1234                          ( now - peerblk->started ) );
1235 }
1236
1237 /******************************************************************************
1238  *
1239  * Opener
1240  *
1241  ******************************************************************************
1242  */
1243
1244 /** PeerDist block download data transfer interface operations */
1245 static struct interface_operation peerblk_xfer_operations[] = {
1246         INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ),
1247 };
1248
1249 /** PeerDist block download data transfer interface descriptor */
1250 static struct interface_descriptor peerblk_xfer_desc =
1251         INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations );
1252
1253 /** PeerDist block download raw data interface operations */
1254 static struct interface_operation peerblk_raw_operations[] = {
1255         INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ),
1256         INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ),
1257 };
1258
1259 /** PeerDist block download raw data interface descriptor */
1260 static struct interface_descriptor peerblk_raw_desc =
1261         INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations );
1262
1263 /** PeerDist block download retrieval protocol interface operations */
1264 static struct interface_operation peerblk_retrieval_operations[] = {
1265         INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ),
1266         INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close),
1267 };
1268
1269 /** PeerDist block download retrieval protocol interface descriptor */
1270 static struct interface_descriptor peerblk_retrieval_desc =
1271         INTF_DESC ( struct peerdist_block, retrieval,
1272                     peerblk_retrieval_operations );
1273
1274 /** PeerDist block download decryption process descriptor */
1275 static struct process_descriptor peerblk_process_desc =
1276         PROC_DESC ( struct peerdist_block, process, peerblk_decrypt );
1277
1278 /** PeerDist block download discovery operations */
1279 static struct peerdisc_client_operations peerblk_discovery_operations = {
1280         .discovered = peerblk_discovered,
1281 };
1282
1283 /**
1284  * Open PeerDist block download
1285  *
1286  * @v xfer              Data transfer interface
1287  * @v uri               Original URI
1288  * @v info              Content information block
1289  * @ret rc              Return status code
1290  */
1291 int peerblk_open ( struct interface *xfer, struct uri *uri,
1292                    struct peerdist_info_block *block ) {
1293         const struct peerdist_info_segment *segment = block->segment;
1294         const struct peerdist_info *info = segment->info;
1295         struct digest_algorithm *digest = info->digest;
1296         struct peerdist_block *peerblk;
1297         unsigned long timeout;
1298         size_t digestsize;
1299         int rc;
1300
1301         /* Allocate and initialise structure */
1302         peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize );
1303         if ( ! peerblk ) {
1304                 rc = -ENOMEM;
1305                 goto err_alloc;
1306         }
1307         ref_init ( &peerblk->refcnt, peerblk_free );
1308         intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt );
1309         intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt );
1310         intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc,
1311                     &peerblk->refcnt );
1312         peerblk->uri = uri_get ( uri );
1313         memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) );
1314         memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) );
1315         peerblk->offset = ( block->trim.start - info->trim.start );
1316         peerblk->digest = info->digest;
1317         peerblk->digestsize = digestsize = info->digestsize;
1318         peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) );
1319         peerblk->segment = segment->index;
1320         memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) );
1321         memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) );
1322         peerblk->block = block->index;
1323         memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) );
1324         xferbuf_malloc_init ( &peerblk->buffer );
1325         process_init_stopped ( &peerblk->process, &peerblk_process_desc,
1326                                &peerblk->refcnt );
1327         peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations );
1328         timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt );
1329         DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..."
1330                 "%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment,
1331                 peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2],
1332                 peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ],
1333                 peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ],
1334                 peerblk->range.start, peerblk->range.end );
1335         if ( ( peerblk->trim.start != peerblk->range.start ) ||
1336              ( peerblk->trim.end != peerblk->range.end ) ) {
1337                 DBGC2 ( peerblk, " covers [%08zx,%08zx)",
1338                         peerblk->trim.start, peerblk->trim.end );
1339         }
1340         DBGC2 ( peerblk, "\n" );
1341
1342         /* Open discovery */
1343         if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id,
1344                                     peerblk->digestsize ) ) != 0 )
1345                 goto err_open_discovery;
1346
1347         /* Schedule a retry attempt either immediately (if we already
1348          * have some peers) or after the discovery timeout.
1349          */
1350         timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ?
1351                     ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 );
1352         start_timer_fixed ( &peerblk->timer, timeout );
1353
1354         /* Record start time */
1355         peerblk->started = peerblk_timestamp();
1356
1357         /* Attach to parent interface, mortalise self, and return */
1358         intf_plug_plug ( xfer, &peerblk->xfer );
1359         ref_put ( &peerblk->refcnt );
1360         return 0;
1361
1362  err_open_discovery:
1363         peerblk_close ( peerblk, rc );
1364  err_alloc:
1365         return rc;
1366 }