• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 FILE_LICENCE ( GPL2_OR_LATER );
20 
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <errno.h>
24 #include <gpxe/xfer.h>
25 #include <gpxe/open.h>
26 #include <gpxe/job.h>
27 #include <gpxe/uaccess.h>
28 #include <gpxe/umalloc.h>
29 #include <gpxe/image.h>
30 #include <gpxe/downloader.h>
31 
32 /** @file
33  *
34  * Image downloader
35  *
36  */
37 
38 /** A downloader */
39 struct downloader {
40 	/** Reference count for this object */
41 	struct refcnt refcnt;
42 
43 	/** Job control interface */
44 	struct job_interface job;
45 	/** Data transfer interface */
46 	struct xfer_interface xfer;
47 
48 	/** Image to contain downloaded file */
49 	struct image *image;
50 	/** Current position within image buffer */
51 	size_t pos;
52 	/** Image registration routine */
53 	int ( * register_image ) ( struct image *image );
54 };
55 
56 /**
57  * Free downloader object
58  *
59  * @v refcnt		Downloader reference counter
60  */
downloader_free(struct refcnt * refcnt)61 static void downloader_free ( struct refcnt *refcnt ) {
62 	struct downloader *downloader =
63 		container_of ( refcnt, struct downloader, refcnt );
64 
65 	image_put ( downloader->image );
66 	free ( downloader );
67 }
68 
69 /**
70  * Terminate download
71  *
72  * @v downloader	Downloader
73  * @v rc		Reason for termination
74  */
downloader_finished(struct downloader * downloader,int rc)75 static void downloader_finished ( struct downloader *downloader, int rc ) {
76 
77 	/* Block further incoming messages */
78 	job_nullify ( &downloader->job );
79 	xfer_nullify ( &downloader->xfer );
80 
81 	/* Free resources and close interfaces */
82 	xfer_close ( &downloader->xfer, rc );
83 	job_done ( &downloader->job, rc );
84 }
85 
86 /**
87  * Ensure that download buffer is large enough for the specified size
88  *
89  * @v downloader	Downloader
90  * @v len		Required minimum size
91  * @ret rc		Return status code
92  */
downloader_ensure_size(struct downloader * downloader,size_t len)93 static int downloader_ensure_size ( struct downloader *downloader,
94 				    size_t len ) {
95 	userptr_t new_buffer;
96 
97 	/* If buffer is already large enough, do nothing */
98 	if ( len <= downloader->image->len )
99 		return 0;
100 
101 	DBGC ( downloader, "Downloader %p extending to %zd bytes\n",
102 	       downloader, len );
103 
104 	/* Extend buffer */
105 	new_buffer = urealloc ( downloader->image->data, len );
106 	if ( ! new_buffer ) {
107 		DBGC ( downloader, "Downloader %p could not extend buffer to "
108 		       "%zd bytes\n", downloader, len );
109 		return -ENOBUFS;
110 	}
111 	downloader->image->data = new_buffer;
112 	downloader->image->len = len;
113 
114 	return 0;
115 }
116 
117 /****************************************************************************
118  *
119  * Job control interface
120  *
121  */
122 
123 /**
124  * Handle kill() event received via job control interface
125  *
126  * @v job		Downloader job control interface
127  */
downloader_job_kill(struct job_interface * job)128 static void downloader_job_kill ( struct job_interface *job ) {
129 	struct downloader *downloader =
130 		container_of ( job, struct downloader, job );
131 
132 	/* Terminate download */
133 	downloader_finished ( downloader, -ECANCELED );
134 }
135 
136 /**
137  * Report progress of download job
138  *
139  * @v job		Downloader job control interface
140  * @v progress		Progress report to fill in
141  */
downloader_job_progress(struct job_interface * job,struct job_progress * progress)142 static void downloader_job_progress ( struct job_interface *job,
143 				      struct job_progress *progress ) {
144 	struct downloader *downloader =
145 		container_of ( job, struct downloader, job );
146 
147 	/* This is not entirely accurate, since downloaded data may
148 	 * arrive out of order (e.g. with multicast protocols), but
149 	 * it's a reasonable first approximation.
150 	 */
151 	progress->completed = downloader->pos;
152 	progress->total = downloader->image->len;
153 }
154 
155 /** Downloader job control interface operations */
156 static struct job_interface_operations downloader_job_operations = {
157 	.done		= ignore_job_done,
158 	.kill		= downloader_job_kill,
159 	.progress	= downloader_job_progress,
160 };
161 
162 /****************************************************************************
163  *
164  * Data transfer interface
165  *
166  */
167 
168 /**
169  * Handle deliver_raw() event received via data transfer interface
170  *
171  * @v xfer		Downloader data transfer interface
172  * @v iobuf		Datagram I/O buffer
173  * @v meta		Data transfer metadata
174  * @ret rc		Return status code
175  */
downloader_xfer_deliver_iob(struct xfer_interface * xfer,struct io_buffer * iobuf,struct xfer_metadata * meta)176 static int downloader_xfer_deliver_iob ( struct xfer_interface *xfer,
177 					 struct io_buffer *iobuf,
178 					 struct xfer_metadata *meta ) {
179 	struct downloader *downloader =
180 		container_of ( xfer, struct downloader, xfer );
181 	size_t len;
182 	size_t max;
183 	int rc;
184 
185 	/* Calculate new buffer position */
186 	if ( meta->whence != SEEK_CUR )
187 		downloader->pos = 0;
188 	downloader->pos += meta->offset;
189 
190 	/* Ensure that we have enough buffer space for this data */
191 	len = iob_len ( iobuf );
192 	max = ( downloader->pos + len );
193 	if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
194 		goto done;
195 
196 	/* Copy data to buffer */
197 	copy_to_user ( downloader->image->data, downloader->pos,
198 		       iobuf->data, len );
199 
200 	/* Update current buffer position */
201 	downloader->pos += len;
202 
203  done:
204 	free_iob ( iobuf );
205 	return rc;
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  */
downloader_xfer_close(struct xfer_interface * xfer,int rc)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_vreopen,
230 	.window		= unlimited_xfer_window,
231 	.alloc_iob	= default_xfer_alloc_iob,
232 	.deliver_iob	= downloader_xfer_deliver_iob,
233 	.deliver_raw	= xfer_deliver_as_iob,
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  */
create_downloader(struct job_interface * job,struct image * image,int (* register_image)(struct image * image),int type,...)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 }
292