Prefix all the open()-family routines with xfer_, to disambiguate them
[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 start() event received via job control interface
119  *
120  * @v job               Downloader job control interface
121  */
122 static void downloader_job_start ( struct job_interface *job ) {
123         struct downloader *downloader = 
124                 container_of ( job, struct downloader, job );
125
126         /* Start data transfer */
127         xfer_request_all ( &downloader->xfer );
128 }
129
130 /**
131  * Handle kill() event received via job control interface
132  *
133  * @v job               Downloader job control interface
134  */
135 static void downloader_job_kill ( struct job_interface *job ) {
136         struct downloader *downloader = 
137                 container_of ( job, struct downloader, job );
138
139         /* Terminate download */
140         downloader_finished ( downloader, -ECANCELED );
141 }
142
143 /** Downloader job control interface operations */
144 static struct job_interface_operations downloader_job_operations = {
145         .start          = downloader_job_start,
146         .done           = ignore_job_done,
147         .kill           = downloader_job_kill,
148         .progress       = ignore_job_progress,
149 };
150
151 /****************************************************************************
152  *
153  * Data transfer interface
154  *
155  */
156
157 /**
158  * Handle seek() event received via data transfer interface
159  *
160  * @v xfer              Downloader data transfer interface
161  * @v pos               New position
162  * @ret rc              Return status code
163  */
164 static int downloader_xfer_seek ( struct xfer_interface *xfer, off_t offset,
165                                   int whence ) {
166         struct downloader *downloader =
167                 container_of ( xfer, struct downloader, xfer );
168         off_t new_pos;
169         int rc;
170
171         /* Calculate new buffer position */
172         switch ( whence ) {
173         case SEEK_SET:
174                 new_pos = offset;
175                 break;
176         case SEEK_CUR:
177                 new_pos = ( downloader->pos + offset );
178                 break;
179         default:
180                 assert ( 0 );
181                 return -EINVAL;
182         }
183
184         /* Ensure that we have enough buffer space for this buffer position */
185         if ( ( rc = downloader_ensure_size ( downloader, new_pos ) ) != 0 )
186                 return rc;
187         downloader->pos = new_pos;
188
189         return 0;
190 }
191
192 /**
193  * Handle deliver_raw() event received via data transfer interface
194  *
195  * @v xfer              Downloader data transfer interface
196  * @v data              Data buffer
197  * @v len               Length of data buffer
198  * @ret rc              Return status code
199  */
200 static int downloader_xfer_deliver_raw ( struct xfer_interface *xfer,
201                                          const void *data, size_t len ) {
202         struct downloader *downloader =
203                 container_of ( xfer, struct downloader, xfer );
204         size_t max;
205         int rc;
206
207         /* Ensure that we have enough buffer space for this data */
208         max = ( downloader->pos + len );
209         if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
210                 return rc;
211
212         /* Copy data to buffer */
213         copy_to_user ( downloader->image->data, downloader->pos, data, len );
214
215         /* Update current buffer position */
216         downloader->pos += len;
217
218         return 0;
219 }
220
221 /**
222  * Handle close() event received via data transfer interface
223  *
224  * @v xfer              Downloader data transfer interface
225  * @v rc                Reason for close
226  */
227 static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) {
228         struct downloader *downloader =
229                 container_of ( xfer, struct downloader, xfer );
230
231         /* Register image if download was successful */
232         if ( rc == 0 )
233                 rc = downloader->register_image ( downloader->image );
234
235         /* Terminate download */
236         downloader_finished ( downloader, rc );
237 }
238
239 /** Downloader data transfer interface operations */
240 static struct xfer_interface_operations downloader_xfer_operations = {
241         .close          = downloader_xfer_close,
242         .vredirect      = xfer_vopen,
243         .request        = ignore_xfer_request,
244         .seek           = downloader_xfer_seek,
245         .deliver_iob    = xfer_deliver_as_raw,
246         .deliver_raw    = downloader_xfer_deliver_raw,
247 };
248
249 /****************************************************************************
250  *
251  * Instantiator
252  *
253  */
254
255 /**
256  * Instantiate a downloader
257  *
258  * @v job               Job control interface
259  * @v uri_string        URI string
260  * @v image             Image to fill with downloaded file
261  * @v register_image    Image registration routine
262  * @ret rc              Return status code
263  *
264  * Instantiates a downloader object to download the specified URI into
265  * the specified image object.  If the download is successful, the
266  * image registration routine @c register_image() will be called.
267  */
268 int create_downloader ( struct job_interface *job, const char *uri_string,
269                         struct image *image,
270                         int ( * register_image ) ( struct image *image ) ) {
271         struct downloader *downloader;
272         int rc;
273
274         /* Allocate and initialise structure */
275         downloader = malloc ( sizeof ( *downloader ) );
276         if ( ! downloader )
277                 return -ENOMEM;
278         memset ( downloader, 0, sizeof ( *downloader ) );
279         downloader->refcnt.free = downloader_free;
280         job_init ( &downloader->job, &downloader_job_operations,
281                    &downloader->refcnt );
282         xfer_init ( &downloader->xfer, &downloader_xfer_operations,
283                     &downloader->refcnt );
284         downloader->image = image_get ( image );
285         downloader->register_image = register_image;
286
287         /* Instantiate child objects and attach to our interfaces */
288         if ( ( rc = xfer_open ( &downloader->xfer, LOCATION_URI,
289                                 uri_string ) ) != 0 )
290                 goto err;
291
292         /* Attach parent interface, mortalise self, and return */
293         job_plug_plug ( &downloader->job, job );
294         ref_put ( &downloader->refcnt );
295         return 0;
296
297  err:
298         downloader_finished ( downloader, rc );
299         ref_put ( &downloader->refcnt );
300         return rc;
301 }