Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2007 Michael Brown <mbrown (at) 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  */
     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  */
     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  */
     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  */
    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  */
    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  */
    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  */
    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  */
    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