Applied a modified version of holger's regparm patches.
[people/adir/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 <stdarg.h>
21 #include <errno.h>
22 #include <gpxe/xfer.h>
23 #include <gpxe/open.h>
24 #include <gpxe/job.h>
25 #include <gpxe/uaccess.h>
26 #include <gpxe/umalloc.h>
27 #include <gpxe/image.h>
28 #include <gpxe/downloader.h>
29
30 /** @file
31  *
32  * Image downloader
33  *
34  */
35
36 /** A downloader */
37 struct downloader {
38         /** Reference count for this object */
39         struct refcnt refcnt;
40
41         /** Job control interface */
42         struct job_interface job;
43         /** Data transfer interface */
44         struct xfer_interface xfer;
45
46         /** Image to contain downloaded file */
47         struct image *image;
48         /** Current position within image buffer */
49         size_t pos;
50         /** Image registration routine */
51         int ( * register_image ) ( struct image *image );
52 };
53
54 /**
55  * Free downloader object
56  *
57  * @v refcnt            Downloader reference counter
58  */
59 static void downloader_free ( struct refcnt *refcnt ) {
60         struct downloader *downloader =
61                 container_of ( refcnt, struct downloader, refcnt );
62
63         image_put ( downloader->image );
64         free ( downloader );
65 }
66
67 /**
68  * Terminate download
69  *
70  * @v downloader        Downloader
71  * @v rc                Reason for termination
72  */
73 static void downloader_finished ( struct downloader *downloader, int rc ) {
74
75         /* Block further incoming messages */
76         job_nullify ( &downloader->job );
77         xfer_nullify ( &downloader->xfer );
78
79         /* Free resources and close interfaces */
80         xfer_close ( &downloader->xfer, rc );
81         job_done ( &downloader->job, rc );
82 }
83
84 /**
85  * Ensure that download buffer is large enough for the specified size
86  *
87  * @v downloader        Downloader
88  * @v len               Required minimum size
89  * @ret rc              Return status code
90  */
91 static int downloader_ensure_size ( struct downloader *downloader,
92                                     size_t len ) {
93         userptr_t new_buffer;
94
95         /* If buffer is already large enough, do nothing */
96         if ( len <= downloader->image->len )
97                 return 0;
98
99         /* Extend buffer */
100         new_buffer = urealloc ( downloader->image->data, len );
101         if ( ! new_buffer ) {
102                 DBGC ( downloader, "Downloader %p could not extend buffer to "
103                        "%zd bytes\n", downloader, len );
104                 return -ENOBUFS;
105         }
106         downloader->image->data = new_buffer;
107         downloader->image->len = len;
108
109         return 0;
110 }
111
112 /****************************************************************************
113  *
114  * Job control interface
115  *
116  */
117
118 /**
119  * Handle kill() event received via job control interface
120  *
121  * @v job               Downloader job control interface
122  */
123 static void downloader_job_kill ( struct job_interface *job ) {
124         struct downloader *downloader = 
125                 container_of ( job, struct downloader, job );
126
127         /* Terminate download */
128         downloader_finished ( downloader, -ECANCELED );
129 }
130
131 /** Downloader job control interface operations */
132 static struct job_interface_operations downloader_job_operations = {
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         .seek           = downloader_xfer_seek,
231         .window         = unlimited_xfer_window,
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 image             Image to fill with downloaded file
247  * @v register_image    Image registration routine
248  * @v type              Location type to pass to xfer_open()
249  * @v ...               Remaining arguments to pass to xfer_open()
250  * @ret rc              Return status code
251  *
252  * Instantiates a downloader object to download the specified URI into
253  * the specified image object.  If the download is successful, the
254  * image registration routine @c register_image() will be called.
255  */
256 int create_downloader ( struct job_interface *job, struct image *image,
257                         int ( * register_image ) ( struct image *image ),
258                         int type, ... ) {
259         struct downloader *downloader;
260         va_list args;
261         int rc;
262
263         /* Allocate and initialise structure */
264         downloader = zalloc ( sizeof ( *downloader ) );
265         if ( ! downloader )
266                 return -ENOMEM;
267         downloader->refcnt.free = downloader_free;
268         job_init ( &downloader->job, &downloader_job_operations,
269                    &downloader->refcnt );
270         xfer_init ( &downloader->xfer, &downloader_xfer_operations,
271                     &downloader->refcnt );
272         downloader->image = image_get ( image );
273         downloader->register_image = register_image;
274         va_start ( args, type );
275
276         /* Instantiate child objects and attach to our interfaces */
277         if ( ( rc = xfer_vopen ( &downloader->xfer, type, args ) ) != 0 )
278                 goto err;
279
280         /* Attach parent interface, mortalise self, and return */
281         job_plug_plug ( &downloader->job, job );
282         ref_put ( &downloader->refcnt );
283         va_end ( args );
284         return 0;
285
286  err:
287         downloader_finished ( downloader, rc );
288         ref_put ( &downloader->refcnt );
289         va_end ( args );
290         return rc;
291 }