1 /*
2 * Copyright (C) 2016 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <string.h>
27 #include <strings.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <ipxe/refcnt.h>
32 #include <ipxe/malloc.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/open.h>
35 #include <ipxe/uri.h>
36 #include <ipxe/iobuf.h>
37 #include <ipxe/process.h>
38 #include <ipxe/efi/efi.h>
39 #include <ipxe/efi/efi_strings.h>
40 #include <ipxe/efi/efi_utils.h>
41 #include <ipxe/efi/Protocol/SimpleFileSystem.h>
42 #include <ipxe/efi/Guid/FileInfo.h>
43 #include <ipxe/efi/Guid/FileSystemInfo.h>
44
45 /** @file
46 *
47 * EFI local file access
48 *
49 */
50
51 /** Download blocksize */
52 #define EFI_LOCAL_BLKSIZE 4096
53
54 /** An EFI local file */
55 struct efi_local {
56 /** Reference count */
57 struct refcnt refcnt;
58 /** Data transfer interface */
59 struct interface xfer;
60 /** Download process */
61 struct process process;
62
63 /** EFI root directory */
64 EFI_FILE_PROTOCOL *root;
65 /** EFI file */
66 EFI_FILE_PROTOCOL *file;
67 /** Length of file */
68 size_t len;
69 };
70
71 /**
72 * Close local file
73 *
74 * @v local Local file
75 * @v rc Reason for close
76 */
efi_local_close(struct efi_local * local,int rc)77 static void efi_local_close ( struct efi_local *local, int rc ) {
78
79 /* Stop process */
80 process_del ( &local->process );
81
82 /* Shut down data transfer interface */
83 intf_shutdown ( &local->xfer, rc );
84
85 /* Close EFI file */
86 if ( local->file ) {
87 local->file->Close ( local->file );
88 local->file = NULL;
89 }
90
91 /* Close EFI root directory */
92 if ( local->root ) {
93 local->root->Close ( local->root );
94 local->root = NULL;
95 }
96 }
97
98 /**
99 * Local file process
100 *
101 * @v local Local file
102 */
efi_local_step(struct efi_local * local)103 static void efi_local_step ( struct efi_local *local ) {
104 EFI_FILE_PROTOCOL *file = local->file;
105 struct io_buffer *iobuf = NULL;
106 size_t remaining;
107 size_t frag_len;
108 UINTN size;
109 EFI_STATUS efirc;
110 int rc;
111
112 /* Wait until data transfer interface is ready */
113 if ( ! xfer_window ( &local->xfer ) )
114 return;
115
116 /* Presize receive buffer */
117 remaining = local->len;
118 xfer_seek ( &local->xfer, remaining );
119 xfer_seek ( &local->xfer, 0 );
120
121 /* Get file contents */
122 while ( remaining ) {
123
124 /* Calculate length for this fragment */
125 frag_len = remaining;
126 if ( frag_len > EFI_LOCAL_BLKSIZE )
127 frag_len = EFI_LOCAL_BLKSIZE;
128
129 /* Allocate I/O buffer */
130 iobuf = xfer_alloc_iob ( &local->xfer, frag_len );
131 if ( ! iobuf ) {
132 rc = -ENOMEM;
133 goto err;
134 }
135
136 /* Read block */
137 size = frag_len;
138 if ( ( efirc = file->Read ( file, &size, iobuf->data ) ) != 0 ){
139 rc = -EEFI ( efirc );
140 DBGC ( local, "LOCAL %p could not read from file: %s\n",
141 local, strerror ( rc ) );
142 goto err;
143 }
144 assert ( size <= frag_len );
145 iob_put ( iobuf, size );
146
147 /* Deliver data */
148 if ( ( rc = xfer_deliver_iob ( &local->xfer,
149 iob_disown ( iobuf ) ) ) != 0 ) {
150 DBGC ( local, "LOCAL %p could not deliver data: %s\n",
151 local, strerror ( rc ) );
152 goto err;
153 }
154
155 /* Move to next block */
156 remaining -= frag_len;
157 }
158
159 /* Close download */
160 efi_local_close ( local, 0 );
161
162 return;
163
164 err:
165 free_iob ( iobuf );
166 efi_local_close ( local, rc );
167 }
168
169 /** Data transfer interface operations */
170 static struct interface_operation efi_local_operations[] = {
171 INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ),
172 INTF_OP ( intf_close, struct efi_local *, efi_local_close ),
173 };
174
175 /** Data transfer interface descriptor */
176 static struct interface_descriptor efi_local_xfer_desc =
177 INTF_DESC ( struct efi_local, xfer, efi_local_operations );
178
179 /** Process descriptor */
180 static struct process_descriptor efi_local_process_desc =
181 PROC_DESC_ONCE ( struct efi_local, process, efi_local_step );
182
183 /**
184 * Check for matching volume name
185 *
186 * @v local Local file
187 * @v device Device handle
188 * @v root Root filesystem handle
189 * @v volume Volume name
190 * @ret rc Return status code
191 */
efi_local_check_volume_name(struct efi_local * local,EFI_HANDLE device,EFI_FILE_PROTOCOL * root,const char * volume)192 static int efi_local_check_volume_name ( struct efi_local *local,
193 EFI_HANDLE device,
194 EFI_FILE_PROTOCOL *root,
195 const char *volume ) {
196 EFI_FILE_SYSTEM_INFO *info;
197 UINTN size;
198 char *label;
199 EFI_STATUS efirc;
200 int rc;
201
202 /* Get length of file system information */
203 size = 0;
204 root->GetInfo ( root, &efi_file_system_info_id, &size, NULL );
205
206 /* Allocate file system information */
207 info = malloc ( size );
208 if ( ! info ) {
209 rc = -ENOMEM;
210 goto err_alloc_info;
211 }
212
213 /* Get file system information */
214 if ( ( efirc = root->GetInfo ( root, &efi_file_system_info_id, &size,
215 info ) ) != 0 ) {
216 rc = -EEFI ( efirc );
217 DBGC ( local, "LOCAL %p could not get file system info on %s: "
218 "%s\n", local, efi_handle_name ( device ),
219 strerror ( rc ) );
220 goto err_get_info;
221 }
222 DBGC2 ( local, "LOCAL %p found %s with label \"%ls\"\n",
223 local, efi_handle_name ( device ), info->VolumeLabel );
224
225 /* Construct volume label for comparison */
226 if ( asprintf ( &label, "%ls", info->VolumeLabel ) < 0 ) {
227 rc = -ENOMEM;
228 goto err_alloc_label;
229 }
230
231 /* Compare volume label */
232 if ( strcasecmp ( volume, label ) != 0 ) {
233 rc = -ENOENT;
234 goto err_compare;
235 }
236
237 /* Success */
238 rc = 0;
239
240 err_compare:
241 free ( label );
242 err_alloc_label:
243 err_get_info:
244 free ( info );
245 err_alloc_info:
246 return rc;
247 }
248
249 /**
250 * Open root filesystem
251 *
252 * @v local Local file
253 * @v device Device handle
254 * @v root Root filesystem handle to fill in
255 * @ret rc Return status code
256 */
efi_local_open_root(struct efi_local * local,EFI_HANDLE device,EFI_FILE_PROTOCOL ** root)257 static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device,
258 EFI_FILE_PROTOCOL **root ) {
259 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
260 union {
261 void *interface;
262 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
263 } u;
264 EFI_STATUS efirc;
265 int rc;
266
267 /* Open file system protocol */
268 if ( ( efirc = bs->OpenProtocol ( device,
269 &efi_simple_file_system_protocol_guid,
270 &u.interface, efi_image_handle,
271 device,
272 EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
273 rc = -EEFI ( efirc );
274 DBGC ( local, "LOCAL %p could not open filesystem on %s: %s\n",
275 local, efi_handle_name ( device ), strerror ( rc ) );
276 goto err_filesystem;
277 }
278
279 /* Open root directory */
280 if ( ( efirc = u.fs->OpenVolume ( u.fs, root ) ) != 0 ) {
281 rc = -EEFI ( efirc );
282 DBGC ( local, "LOCAL %p could not open volume on %s: %s\n",
283 local, efi_handle_name ( device ), strerror ( rc ) );
284 goto err_volume;
285 }
286
287 /* Success */
288 rc = 0;
289
290 err_volume:
291 bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid,
292 efi_image_handle, device );
293 err_filesystem:
294 return rc;
295 }
296
297 /**
298 * Open root filesystem of specified volume
299 *
300 * @v local Local file
301 * @v volume Volume name, or NULL to use loaded image's device
302 * @ret rc Return status code
303 */
efi_local_open_volume(struct efi_local * local,const char * volume)304 static int efi_local_open_volume ( struct efi_local *local,
305 const char *volume ) {
306 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
307 EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
308 int ( * check ) ( struct efi_local *local, EFI_HANDLE device,
309 EFI_FILE_PROTOCOL *root, const char *volume );
310 EFI_DEVICE_PATH_PROTOCOL *path;
311 EFI_FILE_PROTOCOL *root;
312 EFI_HANDLE *handles;
313 EFI_HANDLE device;
314 UINTN num_handles;
315 UINTN i;
316 EFI_STATUS efirc;
317 int rc;
318
319 /* Identify candidate handles */
320 if ( volume ) {
321 /* Locate all filesystem handles */
322 if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol,
323 NULL, &num_handles,
324 &handles ) ) != 0 ) {
325 rc = -EEFI ( efirc );
326 DBGC ( local, "LOCAL %p could not enumerate handles: "
327 "%s\n", local, strerror ( rc ) );
328 return rc;
329 }
330 check = efi_local_check_volume_name;
331 } else {
332 /* Locate filesystem from which we were loaded */
333 path = efi_loaded_image_path;
334 if ( ( efirc = bs->LocateDevicePath ( protocol, &path,
335 &device ) ) != 0 ) {
336 rc = -EEFI ( efirc );
337 DBGC ( local, "LOCAL %p could not locate file system "
338 "on %s: %s\n", local,
339 efi_devpath_text ( efi_loaded_image_path ),
340 strerror ( rc ) );
341 return rc;
342 }
343 handles = &device;
344 num_handles = 1;
345 check = NULL;
346 }
347
348 /* Find matching handle */
349 for ( i = 0 ; i < num_handles ; i++ ) {
350
351 /* Get this device handle */
352 device = handles[i];
353
354 /* Open root directory */
355 if ( ( rc = efi_local_open_root ( local, device, &root ) ) != 0)
356 continue;
357
358 /* Check volume name, if applicable */
359 if ( ( check == NULL ) ||
360 ( ( rc = check ( local, device, root, volume ) ) == 0 ) ) {
361 DBGC ( local, "LOCAL %p using %s",
362 local, efi_handle_name ( device ) );
363 if ( volume )
364 DBGC ( local, " with label \"%s\"", volume );
365 DBGC ( local, "\n" );
366 local->root = root;
367 break;
368 }
369
370 /* Close root directory */
371 root->Close ( root );
372 }
373
374 /* Free handles, if applicable */
375 if ( volume )
376 bs->FreePool ( handles );
377
378 /* Fail if we found no matching handle */
379 if ( ! local->root ) {
380 DBGC ( local, "LOCAL %p found no matching handle\n", local );
381 return -ENOENT;
382 }
383
384 return 0;
385 }
386
387 /**
388 * Open fully-resolved path
389 *
390 * @v local Local file
391 * @v resolved Resolved path
392 * @ret rc Return status code
393 */
efi_local_open_resolved(struct efi_local * local,const char * resolved)394 static int efi_local_open_resolved ( struct efi_local *local,
395 const char *resolved ) {
396 size_t name_len = strlen ( resolved );
397 CHAR16 name[ name_len + 1 /* wNUL */ ];
398 EFI_FILE_PROTOCOL *file;
399 EFI_STATUS efirc;
400 int rc;
401
402 /* Construct filename */
403 efi_snprintf ( name, ( name_len + 1 /* wNUL */ ), "%s", resolved );
404
405 /* Open file */
406 if ( ( efirc = local->root->Open ( local->root, &file, name,
407 EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
408 rc = -EEFI ( efirc );
409 DBGC ( local, "LOCAL %p could not open \"%s\": %s\n",
410 local, resolved, strerror ( rc ) );
411 return rc;
412 }
413 local->file = file;
414
415 return 0;
416 }
417
418 /**
419 * Open specified path
420 *
421 * @v local Local file
422 * @v path Path to file
423 * @ret rc Return status code
424 */
efi_local_open_path(struct efi_local * local,const char * path)425 static int efi_local_open_path ( struct efi_local *local, const char *path ) {
426 FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath,
427 FILEPATH_DEVICE_PATH, Header);
428 size_t fp_len = ( fp ? efi_devpath_len ( &fp->Header ) : 0 );
429 char base[ fp_len / 2 /* Cannot exceed this length */ ];
430 size_t remaining = sizeof ( base );
431 size_t len;
432 char *resolved;
433 char *tmp;
434 int rc;
435
436 /* Construct base path to our own image, if possible */
437 memset ( base, 0, sizeof ( base ) );
438 tmp = base;
439 while ( fp && ( fp->Header.Type != END_DEVICE_PATH_TYPE ) ) {
440 len = snprintf ( tmp, remaining, "%ls", fp->PathName );
441 assert ( len < remaining );
442 tmp += len;
443 remaining -= len;
444 fp = ( ( ( void * ) fp ) + ( ( fp->Header.Length[1] << 8 ) |
445 fp->Header.Length[0] ) );
446 }
447 DBGC2 ( local, "LOCAL %p base path \"%s\"\n",
448 local, base );
449
450 /* Convert to sane path separators */
451 for ( tmp = base ; *tmp ; tmp++ ) {
452 if ( *tmp == '\\' )
453 *tmp = '/';
454 }
455
456 /* Resolve path */
457 resolved = resolve_path ( base, path );
458 if ( ! resolved ) {
459 rc = -ENOMEM;
460 goto err_resolve;
461 }
462
463 /* Convert to insane path separators */
464 for ( tmp = resolved ; *tmp ; tmp++ ) {
465 if ( *tmp == '/' )
466 *tmp = '\\';
467 }
468 DBGC ( local, "LOCAL %p using \"%s\"\n",
469 local, resolved );
470
471 /* Open resolved path */
472 if ( ( rc = efi_local_open_resolved ( local, resolved ) ) != 0 )
473 goto err_open;
474
475 err_open:
476 free ( resolved );
477 err_resolve:
478 return rc;
479 }
480
481 /**
482 * Get file length
483 *
484 * @v local Local file
485 * @ret rc Return status code
486 */
efi_local_len(struct efi_local * local)487 static int efi_local_len ( struct efi_local *local ) {
488 EFI_FILE_PROTOCOL *file = local->file;
489 EFI_FILE_INFO *info;
490 EFI_STATUS efirc;
491 UINTN size;
492 int rc;
493
494 /* Get size of file information */
495 size = 0;
496 file->GetInfo ( file, &efi_file_info_id, &size, NULL );
497
498 /* Allocate file information */
499 info = malloc ( size );
500 if ( ! info ) {
501 rc = -ENOMEM;
502 goto err_alloc;
503 }
504
505 /* Get file information */
506 if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size,
507 info ) ) != 0 ) {
508 rc = -EEFI ( efirc );
509 DBGC ( local, "LOCAL %p could not get file info: %s\n",
510 local, strerror ( rc ) );
511 goto err_info;
512 }
513
514 /* Record file length */
515 local->len = info->FileSize;
516
517 /* Success */
518 rc = 0;
519
520 err_info:
521 free ( info );
522 err_alloc:
523 return rc;
524 }
525
526 /**
527 * Open local file
528 *
529 * @v xfer Data transfer interface
530 * @v uri Request URI
531 * @ret rc Return status code
532 */
efi_local_open(struct interface * xfer,struct uri * uri)533 static int efi_local_open ( struct interface *xfer, struct uri *uri ) {
534 struct efi_local *local;
535 const char *volume;
536 const char *path;
537 int rc;
538
539 /* Parse URI */
540 volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL );
541 path = ( uri->opaque ? uri->opaque : uri->path );
542
543 /* Allocate and initialise structure */
544 local = zalloc ( sizeof ( *local ) );
545 if ( ! local ) {
546 rc = -ENOMEM;
547 goto err_alloc;
548 }
549 ref_init ( &local->refcnt, NULL );
550 intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt );
551 process_init ( &local->process, &efi_local_process_desc,
552 &local->refcnt );
553
554 /* Open specified volume */
555 if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 )
556 goto err_open_root;
557
558 /* Open specified path */
559 if ( ( rc = efi_local_open_path ( local, path ) ) != 0 )
560 goto err_open_file;
561
562 /* Get length of file */
563 if ( ( rc = efi_local_len ( local ) ) != 0 )
564 goto err_len;
565
566 /* Attach to parent interface, mortalise self, and return */
567 intf_plug_plug ( &local->xfer, xfer );
568 ref_put ( &local->refcnt );
569 return 0;
570
571 err_len:
572 err_open_file:
573 err_open_root:
574 efi_local_close ( local, 0 );
575 ref_put ( &local->refcnt );
576 err_alloc:
577 return rc;
578 }
579
580 /** EFI local file URI opener */
581 struct uri_opener efi_local_uri_opener __uri_opener = {
582 .scheme = "file",
583 .open = efi_local_open,
584 };
585