Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

downloader.c 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. FILE_LICENCE ( GPL2_OR_LATER );
  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. /** @file
  30. *
  31. * Image downloader
  32. *
  33. */
  34. /** A downloader */
  35. struct downloader {
  36. /** Reference count for this object */
  37. struct refcnt refcnt;
  38. /** Job control interface */
  39. struct job_interface job;
  40. /** Data transfer interface */
  41. struct xfer_interface xfer;
  42. /** Image to contain downloaded file */
  43. struct image *image;
  44. /** Current position within image buffer */
  45. size_t pos;
  46. /** Image registration routine */
  47. int ( * register_image ) ( struct image *image );
  48. };
  49. /**
  50. * Free downloader object
  51. *
  52. * @v refcnt Downloader reference counter
  53. */
  54. static void downloader_free ( struct refcnt *refcnt ) {
  55. struct downloader *downloader =
  56. container_of ( refcnt, struct downloader, refcnt );
  57. image_put ( downloader->image );
  58. free ( downloader );
  59. }
  60. /**
  61. * Terminate download
  62. *
  63. * @v downloader Downloader
  64. * @v rc Reason for termination
  65. */
  66. static void downloader_finished ( struct downloader *downloader, int rc ) {
  67. /* Block further incoming messages */
  68. job_nullify ( &downloader->job );
  69. xfer_nullify ( &downloader->xfer );
  70. /* Free resources and close interfaces */
  71. xfer_close ( &downloader->xfer, rc );
  72. job_done ( &downloader->job, rc );
  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. /* If buffer is already large enough, do nothing */
  85. if ( len <= downloader->image->len )
  86. return 0;
  87. DBGC ( downloader, "Downloader %p extending to %zd bytes\n",
  88. downloader, len );
  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. return 0;
  99. }
  100. /****************************************************************************
  101. *
  102. * Job control interface
  103. *
  104. */
  105. /**
  106. * Handle kill() event received via job control interface
  107. *
  108. * @v job Downloader job control interface
  109. */
  110. static void downloader_job_kill ( struct job_interface *job ) {
  111. struct downloader *downloader =
  112. container_of ( job, struct downloader, job );
  113. /* Terminate download */
  114. downloader_finished ( downloader, -ECANCELED );
  115. }
  116. /**
  117. * Report progress of download job
  118. *
  119. * @v job Downloader job control interface
  120. * @v progress Progress report to fill in
  121. */
  122. static void downloader_job_progress ( struct job_interface *job,
  123. struct job_progress *progress ) {
  124. struct downloader *downloader =
  125. container_of ( job, struct downloader, job );
  126. /* This is not entirely accurate, since downloaded data may
  127. * arrive out of order (e.g. with multicast protocols), but
  128. * it's a reasonable first approximation.
  129. */
  130. progress->completed = downloader->pos;
  131. progress->total = downloader->image->len;
  132. }
  133. /** Downloader job control interface operations */
  134. static struct job_interface_operations downloader_job_operations = {
  135. .done = ignore_job_done,
  136. .kill = downloader_job_kill,
  137. .progress = downloader_job_progress,
  138. };
  139. /****************************************************************************
  140. *
  141. * Data transfer interface
  142. *
  143. */
  144. /**
  145. * Handle deliver_raw() event received via data transfer interface
  146. *
  147. * @v xfer Downloader data transfer interface
  148. * @v iobuf Datagram I/O buffer
  149. * @v meta Data transfer metadata
  150. * @ret rc Return status code
  151. */
  152. static int downloader_xfer_deliver_iob ( struct xfer_interface *xfer,
  153. struct io_buffer *iobuf,
  154. struct xfer_metadata *meta ) {
  155. struct downloader *downloader =
  156. container_of ( xfer, struct downloader, xfer );
  157. size_t len;
  158. size_t max;
  159. int rc;
  160. /* Calculate new buffer position */
  161. if ( meta->whence != SEEK_CUR )
  162. downloader->pos = 0;
  163. downloader->pos += meta->offset;
  164. /* Ensure that we have enough buffer space for this data */
  165. len = iob_len ( iobuf );
  166. max = ( downloader->pos + len );
  167. if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
  168. goto done;
  169. /* Copy data to buffer */
  170. copy_to_user ( downloader->image->data, downloader->pos,
  171. iobuf->data, len );
  172. /* Update current buffer position */
  173. downloader->pos += len;
  174. done:
  175. free_iob ( iobuf );
  176. return rc;
  177. }
  178. /**
  179. * Handle close() event received via data transfer interface
  180. *
  181. * @v xfer Downloader data transfer interface
  182. * @v rc Reason for close
  183. */
  184. static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) {
  185. struct downloader *downloader =
  186. container_of ( xfer, struct downloader, xfer );
  187. /* Register image if download was successful */
  188. if ( rc == 0 )
  189. rc = downloader->register_image ( downloader->image );
  190. /* Terminate download */
  191. downloader_finished ( downloader, rc );
  192. }
  193. /** Downloader data transfer interface operations */
  194. static struct xfer_interface_operations downloader_xfer_operations = {
  195. .close = downloader_xfer_close,
  196. .vredirect = xfer_vreopen,
  197. .window = unlimited_xfer_window,
  198. .alloc_iob = default_xfer_alloc_iob,
  199. .deliver_iob = downloader_xfer_deliver_iob,
  200. .deliver_raw = xfer_deliver_as_iob,
  201. };
  202. /****************************************************************************
  203. *
  204. * Instantiator
  205. *
  206. */
  207. /**
  208. * Instantiate a downloader
  209. *
  210. * @v job Job control interface
  211. * @v image Image to fill with downloaded file
  212. * @v register_image Image registration routine
  213. * @v type Location type to pass to xfer_open()
  214. * @v ... Remaining arguments to pass to xfer_open()
  215. * @ret rc Return status code
  216. *
  217. * Instantiates a downloader object to download the specified URI into
  218. * the specified image object. If the download is successful, the
  219. * image registration routine @c register_image() will be called.
  220. */
  221. int create_downloader ( struct job_interface *job, struct image *image,
  222. int ( * register_image ) ( struct image *image ),
  223. int type, ... ) {
  224. struct downloader *downloader;
  225. va_list args;
  226. int rc;
  227. /* Allocate and initialise structure */
  228. downloader = zalloc ( sizeof ( *downloader ) );
  229. if ( ! downloader )
  230. return -ENOMEM;
  231. downloader->refcnt.free = downloader_free;
  232. job_init ( &downloader->job, &downloader_job_operations,
  233. &downloader->refcnt );
  234. xfer_init ( &downloader->xfer, &downloader_xfer_operations,
  235. &downloader->refcnt );
  236. downloader->image = image_get ( image );
  237. downloader->register_image = register_image;
  238. va_start ( args, type );
  239. /* Instantiate child objects and attach to our interfaces */
  240. if ( ( rc = xfer_vopen ( &downloader->xfer, type, args ) ) != 0 )
  241. goto err;
  242. /* Attach parent interface, mortalise self, and return */
  243. job_plug_plug ( &downloader->job, job );
  244. ref_put ( &downloader->refcnt );
  245. va_end ( args );
  246. return 0;
  247. err:
  248. downloader_finished ( downloader, rc );
  249. ref_put ( &downloader->refcnt );
  250. va_end ( args );
  251. return rc;
  252. }