diff options
Diffstat (limited to 'src/interface/efi/efi_local.c')
-rw-r--r-- | src/interface/efi/efi_local.c | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index ec6d93a1..b2881424 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -247,6 +247,62 @@ static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device, } /** + * Open root filesystem of specified volume by index + * + * @v local Local file + * @v index Volume index + * @ret rc Return status code + */ +static int efi_local_open_volume_index ( struct efi_local *local, + UINTN index ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; + EFI_FILE_PROTOCOL *root; + EFI_HANDLE *handles; + EFI_HANDLE device; + UINTN num_handles; + EFI_STATUS efirc; + int rc; + + /* Locate all filesystem handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol, + NULL, &num_handles, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not enumerate handles: " + "%s\n", local, strerror ( rc ) ); + return rc; + } + + /* Find matching handle */ + if ( index >= num_handles ) { + rc = -ENOENT; + } else { + /* Get this device handle */ + device = handles[index]; + + /* Open root directory */ + if ( ( rc = efi_local_open_root ( local, device, &root ) ) == 0 && root ) { + local->root = root; + rc = 0; + } else { + rc = -EACCES; + } + } + + /* Free handles, if applicable */ + bs->FreePool ( handles ); + + /* Fail if we found no matching handle */ + if ( ! local->root ) { + DBGC ( local, "LOCAL %p found no matching device with index %u\n", + local, (unsigned int)index ); + } + + return rc; +} + +/** * Open root filesystem of specified volume * * @v local Local file @@ -581,19 +637,45 @@ static struct process_descriptor efi_local_process_desc = */ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { struct efi_local *local; + int vol; + int rc = 0; /* Allocate and initialise structure */ local = zalloc ( sizeof ( *local ) ); if ( ! local ) return -ENOMEM; ref_init ( &local->refcnt, efi_local_free ); - intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); - process_init_stopped ( &local->process, &efi_local_process_desc, - &local->refcnt ); local->uri = uri_get ( uri ); local->volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL ); local->path = ( uri->opaque ? uri->opaque : uri->path ); + if ( local->path && local->volume && strcmp ( local->volume, "*" ) == 0 ) { + /* Open on any volume */ + vol = 0; + while ( ( rc = efi_local_open_volume_index ( local, vol++ ) ) != -ENOENT ) { + if ( rc != 0 ) + continue; + /* Open specified path */ + if ( ( rc = efi_local_open_path ( local ) ) != 0 ) { + local->root->Close ( local->root ); + local->root = NULL; + continue; + } + /* Success */ + break; + } + if ( rc != 0 ) { + DBGC ( local, "LOCAL %p could not find %s on any partition\n", + local, local->path ); + ref_put ( &local->refcnt ); + return -ENOENT; + } + } + + intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); + process_init_stopped ( &local->process, &efi_local_process_desc, + &local->refcnt ); + /* Start download process */ process_add ( &local->process ); |