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