Modify data-xfer semantics: it is no longer necessary to call one of
[people/dverkamp/gpxe.git] / src / core / downloader.c
1 /*
2  * Copyright (C) 2007 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <gpxe/xfer.h>
22 #include <gpxe/open.h>
23 #include <gpxe/job.h>
24 #include <gpxe/uaccess.h>
25 #include <gpxe/umalloc.h>
26 #include <gpxe/image.h>
27 #include <gpxe/downloader.h>
28
29 /** @file
30  *
31  * Image downloader
32  *
33  */
34
35 /** A downloader */
36 struct downloader {
37         /** Reference count for this object */
38         struct refcnt refcnt;
39
40         /** Job control interface */
41         struct job_interface job;
42         /** Data transfer interface */
43         struct xfer_interface xfer;
44
45         /** Image to contain downloaded file */
46         struct image *image;
47         /** Current position within image buffer */
48         size_t pos;
49         /** Image registration routine */
50         int ( * register_image ) ( struct image *image );
51 };
52
53 /**
54  * Free downloader object
55  *
56  * @v refcnt            Downloader reference counter
57  */
58 static void downloader_free ( struct refcnt *refcnt ) {
59         struct downloader *downloader =
60                 container_of ( refcnt, struct downloader, refcnt );
61
62         image_put ( downloader->image );
63         free ( downloader );
64 }
65
66 /**
67  * Terminate download
68  *
69  * @v downloader        Downloader
70  * @v rc                Reason for termination
71  */
72 static void downloader_finished ( struct downloader *downloader, int rc ) {
73
74         /* Block further incoming messages */
75         job_nullify ( &downloader->job );
76         xfer_nullify ( &downloader->xfer );
77
78         /* Free resources and close interfaces */
79         xfer_close ( &downloader->xfer, rc );
80         job_done ( &downloader->job, rc );
81 }
82
83 /**
84  * Ensure that download buffer is large enough for the specified size
85  *
86  * @v downloader        Downloader
87  * @v len               Required minimum size
88  * @ret rc              Return status code
89  */
90 static int downloader_ensure_size ( struct downloader *downloader,
91                                     size_t len ) {
92         userptr_t new_buffer;
93
94         /* If buffer is already large enough, do nothing */
95         if ( len <= downloader->image->len )
96                 return 0;
97
98         /* Extend buffer */
99         new_buffer = urealloc ( downloader->image->data, len );
100         if ( ! new_buffer ) {
101                 DBGC ( downloader, "Downloader %p could not extend buffer to "
102                        "%zd bytes\n", downloader, len );
103                 return -ENOBUFS;
104         }
105         downloader->image->data = new_buffer;
106         downloader->image->len = len;
107
108         return 0;
109 }
110
111 /****************************************************************************
112  *
113  * Job control interface
114  *
115  */
116
117 /**
118  * Handle kill() event received via job control interface
119  *
120  * @v job               Downloader job control interface
121  */
122 static void downloader_job_kill ( struct job_interface *job ) {
123         struct downloader *downloader = 
124                 container_of ( job, struct downloader, job );
125
126         /* Terminate download */
127         downloader_finished ( downloader, -ECANCELED );
128 }
129
130 /** Downloader job control interface operations */
131 static struct job_interface_operations downloader_job_operations = {
132         .start          = ignore_job_start,
133         .done           = ignore_job_done,
134         .kill           = downloader_job_kill,
135         .progress       = ignore_job_progress,
136 };
137
138 /****************************************************************************
139  *
140  * Data transfer interface
141  *
142  */
143
144 /**
145  * Handle seek() event received via data transfer interface
146  *
147  * @v xfer              Downloader data transfer interface
148  * @v pos               New position
149  * @ret rc              Return status code
150  */
151 static int downloader_xfer_seek ( struct xfer_interface *xfer, off_t offset,
152                                   int whence ) {
153         struct downloader *downloader =
154                 container_of ( xfer, struct downloader, xfer );
155         off_t new_pos;
156         int rc;
157
158         /* Calculate new buffer position */
159         switch ( whence ) {
160         case SEEK_SET:
161                 new_pos = offset;
162                 break;
163         case SEEK_CUR:
164                 new_pos = ( downloader->pos + offset );
165                 break;
166         default:
167                 assert ( 0 );
168                 return -EINVAL;
169         }
170
171         /* Ensure that we have enough buffer space for this buffer position */
172         if ( ( rc = downloader_ensure_size ( downloader, new_pos ) ) != 0 )
173                 return rc;
174         downloader->pos = new_pos;
175
176         return 0;
177 }
178
179 /**
180  * Handle deliver_raw() event received via data transfer interface
181  *
182  * @v xfer              Downloader data transfer interface
183  * @v data              Data buffer
184  * @v len               Length of data buffer
185  * @ret rc              Return status code
186  */
187 static int downloader_xfer_deliver_raw ( struct xfer_interface *xfer,
188                                          const void *data, size_t len ) {
189         struct downloader *downloader =
190                 container_of ( xfer, struct downloader, xfer );
191         size_t max;
192         int rc;
193
194         /* Ensure that we have enough buffer space for this data */
195         max = ( downloader->pos + len );
196         if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
197                 return rc;
198
199         /* Copy data to buffer */
200         copy_to_user ( downloader->image->data, downloader->pos, data, len );
201
202         /* Update current buffer position */
203         downloader->pos += len;
204
205         return 0;
206 }
207
208 /**
209  * Handle close() event received via data transfer interface
210  *
211  * @v xfer              Downloader data transfer interface
212  * @v rc                Reason for close
213  */
214 static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) {
215         struct downloader *downloader =
216                 container_of ( xfer, struct downloader, xfer );
217
218         /* Register image if download was successful */
219         if ( rc == 0 )
220                 rc = downloader->register_image ( downloader->image );
221
222         /* Terminate download */
223         downloader_finished ( downloader, rc );
224 }
225
226 /** Downloader data transfer interface operations */
227 static struct xfer_interface_operations downloader_xfer_operations = {
228         .close          = downloader_xfer_close,
229         .vredirect      = xfer_vopen,
230         .request        = ignore_xfer_request,
231         .seek           = downloader_xfer_seek,
232         .deliver_iob    = xfer_deliver_as_raw,
233         .deliver_raw    = downloader_xfer_deliver_raw,
234 };
235
236 /****************************************************************************
237  *
238  * Instantiator
239  *
240  */
241
242 /**
243  * Instantiate a downloader
244  *
245  * @v job               Job control interface
246  * @v uri_string        URI string
247  * @v image             Image to fill with downloaded file
248  * @v register_image    Image registration routine
249  * @ret rc              Return status code
250  *
251  * Instantiates a downloader object to download the specified URI into
252  * the specified image object.  If the download is successful, the
253  * image registration routine @c register_image() will be called.
254  */
255 int create_downloader ( struct job_interface *job, const char *uri_string,
256                         struct image *image,
257                         int ( * register_image ) ( struct image *image ) ) {
258         struct downloader *downloader;
259         int rc;
260
261         /* Allocate and initialise structure */
262         downloader = malloc ( sizeof ( *downloader ) );
263         if ( ! downloader )
264                 return -ENOMEM;
265         memset ( downloader, 0, sizeof ( *downloader ) );
266         downloader->refcnt.free = downloader_free;
267         job_init ( &downloader->job, &downloader_job_operations,
268                    &downloader->refcnt );
269         xfer_init ( &downloader->xfer, &downloader_xfer_operations,
270                     &downloader->refcnt );
271         downloader->image = image_get ( image );
272         downloader->register_image = register_image;
273
274         /* Instantiate child objects and attach to our interfaces */
275         if ( ( rc = xfer_open ( &downloader->xfer, LOCATION_URI,
276                                 uri_string ) ) != 0 )
277                 goto err;
278
279         /* Attach parent interface, mortalise self, and return */
280         job_plug_plug ( &downloader->job, job );
281         ref_put ( &downloader->refcnt );
282         return 0;
283
284  err:
285         downloader_finished ( downloader, rc );
286         ref_put ( &downloader->refcnt );
287         return rc;
288 }