1da4961c7SRebecca Cran /*- 2da4961c7SRebecca Cran * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3da4961c7SRebecca Cran * 4da4961c7SRebecca Cran * Copyright (c) 2019 Intel Corporation 5da4961c7SRebecca Cran * 6da4961c7SRebecca Cran * Redistribution and use in source and binary forms, with or without 7da4961c7SRebecca Cran * modification, are permitted provided that the following conditions 8da4961c7SRebecca Cran * are met: 9da4961c7SRebecca Cran * 1. Redistributions of source code must retain the above copyright 10da4961c7SRebecca Cran * notice, this list of conditions and the following disclaimer. 11da4961c7SRebecca Cran * 2. Redistributions in binary form must reproduce the above copyright 12da4961c7SRebecca Cran * notice, this list of conditions and the following disclaimer in the 13da4961c7SRebecca Cran * documentation and/or other materials provided with the distribution. 14da4961c7SRebecca Cran * 15da4961c7SRebecca Cran * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16da4961c7SRebecca Cran * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17da4961c7SRebecca Cran * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18da4961c7SRebecca Cran * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19da4961c7SRebecca Cran * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20da4961c7SRebecca Cran * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21da4961c7SRebecca Cran * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22da4961c7SRebecca Cran * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23da4961c7SRebecca Cran * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24da4961c7SRebecca Cran * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25da4961c7SRebecca Cran * SUCH DAMAGE. 26da4961c7SRebecca Cran * 27da4961c7SRebecca Cran * $FreeBSD$ 28da4961c7SRebecca Cran */ 29da4961c7SRebecca Cran 30da4961c7SRebecca Cran #include <sys/cdefs.h> 31da4961c7SRebecca Cran __FBSDID("$FreeBSD$"); 32da4961c7SRebecca Cran 33da4961c7SRebecca Cran #include <sys/types.h> 34da4961c7SRebecca Cran 35da4961c7SRebecca Cran #include <netinet/in.h> 36da4961c7SRebecca Cran #include <netinet/in_systm.h> 37da4961c7SRebecca Cran 38da4961c7SRebecca Cran #include <stand.h> 3928e002cdSKyle Evans #include <bootstrap.h> 40da4961c7SRebecca Cran #include <net.h> 41da4961c7SRebecca Cran 42da4961c7SRebecca Cran #include <efi.h> 43da4961c7SRebecca Cran #include <efilib.h> 44da4961c7SRebecca Cran #include <efiprot.h> 45da4961c7SRebecca Cran #include <Protocol/Http.h> 46da4961c7SRebecca Cran #include <Protocol/Ip4Config2.h> 47da4961c7SRebecca Cran #include <Protocol/ServiceBinding.h> 48da4961c7SRebecca Cran 49da4961c7SRebecca Cran /* Poll timeout in milliseconds */ 50da4961c7SRebecca Cran static const int EFIHTTP_POLL_TIMEOUT = 300000; 51da4961c7SRebecca Cran 52da4961c7SRebecca Cran static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID; 53da4961c7SRebecca Cran static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; 54da4961c7SRebecca Cran static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; 55da4961c7SRebecca Cran 561ee03da2SRebecca Cran static bool efihttp_init_done = false; 571ee03da2SRebecca Cran 58da4961c7SRebecca Cran static int efihttp_dev_init(void); 59da4961c7SRebecca Cran static int efihttp_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, 60da4961c7SRebecca Cran char *buf, size_t *rsize); 61da4961c7SRebecca Cran static int efihttp_dev_open(struct open_file *f, ...); 62da4961c7SRebecca Cran static int efihttp_dev_close(struct open_file *f); 63da4961c7SRebecca Cran 64da4961c7SRebecca Cran static int efihttp_fs_open(const char *path, struct open_file *f); 65da4961c7SRebecca Cran static int efihttp_fs_close(struct open_file *f); 66da4961c7SRebecca Cran static int efihttp_fs_read(struct open_file *f, void *buf, size_t size, 67da4961c7SRebecca Cran size_t *resid); 68da4961c7SRebecca Cran static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size, 69da4961c7SRebecca Cran size_t *resid); 70da4961c7SRebecca Cran static off_t efihttp_fs_seek(struct open_file *f, off_t offset, int where); 71da4961c7SRebecca Cran static int efihttp_fs_stat(struct open_file *f, struct stat *sb); 72da4961c7SRebecca Cran static int efihttp_fs_readdir(struct open_file *f, struct dirent *d); 73da4961c7SRebecca Cran 74da4961c7SRebecca Cran struct open_efihttp { 75da4961c7SRebecca Cran EFI_HTTP_PROTOCOL *http; 76da4961c7SRebecca Cran EFI_HANDLE http_handle; 77da4961c7SRebecca Cran EFI_HANDLE dev_handle; 78da4961c7SRebecca Cran char *uri_base; 79da4961c7SRebecca Cran }; 80da4961c7SRebecca Cran 81da4961c7SRebecca Cran struct file_efihttp { 82da4961c7SRebecca Cran ssize_t size; 83da4961c7SRebecca Cran off_t offset; 84da4961c7SRebecca Cran char *path; 85da4961c7SRebecca Cran bool is_dir; 86da4961c7SRebecca Cran }; 87da4961c7SRebecca Cran 88da4961c7SRebecca Cran struct devsw efihttp_dev = { 89da4961c7SRebecca Cran .dv_name = "http", 90da4961c7SRebecca Cran .dv_type = DEVT_NET, 91da4961c7SRebecca Cran .dv_init = efihttp_dev_init, 92da4961c7SRebecca Cran .dv_strategy = efihttp_dev_strategy, 93da4961c7SRebecca Cran .dv_open = efihttp_dev_open, 94da4961c7SRebecca Cran .dv_close = efihttp_dev_close, 95da4961c7SRebecca Cran .dv_ioctl = noioctl, 96da4961c7SRebecca Cran .dv_print = NULL, 97*e98f952cSWarner Losh .dv_cleanup = nullsys, 98da4961c7SRebecca Cran }; 99da4961c7SRebecca Cran 100da4961c7SRebecca Cran struct fs_ops efihttp_fsops = { 101da4961c7SRebecca Cran .fs_name = "efihttp", 102da4961c7SRebecca Cran .fo_open = efihttp_fs_open, 103da4961c7SRebecca Cran .fo_close = efihttp_fs_close, 104da4961c7SRebecca Cran .fo_read = efihttp_fs_read, 105da4961c7SRebecca Cran .fo_write = efihttp_fs_write, 106da4961c7SRebecca Cran .fo_seek = efihttp_fs_seek, 107da4961c7SRebecca Cran .fo_stat = efihttp_fs_stat, 108da4961c7SRebecca Cran .fo_readdir = efihttp_fs_readdir, 109da4961c7SRebecca Cran }; 110da4961c7SRebecca Cran 111da4961c7SRebecca Cran static void EFIAPI 1128136db28SToomas Soome notify(EFI_EVENT event __unused, void *context) 113da4961c7SRebecca Cran { 114da4961c7SRebecca Cran bool *b; 115da4961c7SRebecca Cran 116da4961c7SRebecca Cran b = (bool *)context; 117da4961c7SRebecca Cran *b = true; 118da4961c7SRebecca Cran } 119da4961c7SRebecca Cran 120da4961c7SRebecca Cran static int 121da4961c7SRebecca Cran setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac, 122da4961c7SRebecca Cran IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns) 123da4961c7SRebecca Cran { 124da4961c7SRebecca Cran EFI_IP4_CONFIG2_PROTOCOL *ip4config2; 125da4961c7SRebecca Cran EFI_STATUS status; 126da4961c7SRebecca Cran 127da4961c7SRebecca Cran status = BS->OpenProtocol(handle, &ip4config2_guid, 128da4961c7SRebecca Cran (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 129da4961c7SRebecca Cran if (EFI_ERROR(status)) 130da4961c7SRebecca Cran return (efi_status_to_errno(status)); 13121b2840eSToomas Soome if (ipv4 != NULL) { 13221b2840eSToomas Soome if (mac != NULL) { 133da4961c7SRebecca Cran setenv("boot.netif.hwaddr", 134da4961c7SRebecca Cran ether_sprintf((u_char *)mac->MacAddress.Addr), 1); 13521b2840eSToomas Soome } 136da4961c7SRebecca Cran setenv("boot.netif.ip", 137da4961c7SRebecca Cran inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1); 138da4961c7SRebecca Cran setenv("boot.netif.netmask", 139da4961c7SRebecca Cran intoa(*(n_long *)ipv4->SubnetMask.Addr), 1); 140da4961c7SRebecca Cran setenv("boot.netif.gateway", 141da4961c7SRebecca Cran inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr), 142da4961c7SRebecca Cran 1); 143da4961c7SRebecca Cran status = ip4config2->SetData(ip4config2, 144da4961c7SRebecca Cran Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), 145da4961c7SRebecca Cran &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic }); 146da4961c7SRebecca Cran if (EFI_ERROR(status)) 147da4961c7SRebecca Cran return (efi_status_to_errno(status)); 148da4961c7SRebecca Cran 149da4961c7SRebecca Cran status = ip4config2->SetData(ip4config2, 150da4961c7SRebecca Cran Ip4Config2DataTypeManualAddress, 151da4961c7SRebecca Cran sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS), 152da4961c7SRebecca Cran &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) { 153da4961c7SRebecca Cran .Address = ipv4->LocalIpAddress, 154da4961c7SRebecca Cran .SubnetMask = ipv4->SubnetMask }); 155da4961c7SRebecca Cran if (EFI_ERROR(status)) 156da4961c7SRebecca Cran return (efi_status_to_errno(status)); 157da4961c7SRebecca Cran 158da4961c7SRebecca Cran if (ipv4->GatewayIpAddress.Addr[0] != 0) { 159da4961c7SRebecca Cran status = ip4config2->SetData(ip4config2, 160da4961c7SRebecca Cran Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS), 161da4961c7SRebecca Cran &ipv4->GatewayIpAddress); 162da4961c7SRebecca Cran if (EFI_ERROR(status)) 163da4961c7SRebecca Cran return (efi_status_to_errno(status)); 164da4961c7SRebecca Cran } 165da4961c7SRebecca Cran 16621b2840eSToomas Soome if (dns != NULL) { 167da4961c7SRebecca Cran status = ip4config2->SetData(ip4config2, 168da4961c7SRebecca Cran Ip4Config2DataTypeDnsServer, 169da4961c7SRebecca Cran sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp); 170da4961c7SRebecca Cran if (EFI_ERROR(status)) 171da4961c7SRebecca Cran return (efi_status_to_errno(status)); 172da4961c7SRebecca Cran } 173da4961c7SRebecca Cran } else { 174da4961c7SRebecca Cran status = ip4config2->SetData(ip4config2, 175da4961c7SRebecca Cran Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), 176da4961c7SRebecca Cran &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp }); 177da4961c7SRebecca Cran if (EFI_ERROR(status)) 178da4961c7SRebecca Cran return (efi_status_to_errno(status)); 179da4961c7SRebecca Cran } 180da4961c7SRebecca Cran 181da4961c7SRebecca Cran return (0); 182da4961c7SRebecca Cran } 183da4961c7SRebecca Cran 184da4961c7SRebecca Cran static int 185da4961c7SRebecca Cran efihttp_dev_init(void) 186da4961c7SRebecca Cran { 187da4961c7SRebecca Cran EFI_DEVICE_PATH *imgpath, *devpath; 188da4961c7SRebecca Cran URI_DEVICE_PATH *uri; 189da4961c7SRebecca Cran EFI_HANDLE handle; 190da4961c7SRebecca Cran EFI_STATUS status; 191da4961c7SRebecca Cran int err; 192da4961c7SRebecca Cran bool found_http; 193da4961c7SRebecca Cran 194da4961c7SRebecca Cran imgpath = efi_lookup_image_devpath(IH); 195da4961c7SRebecca Cran if (imgpath == NULL) 196da4961c7SRebecca Cran return (ENXIO); 197da4961c7SRebecca Cran devpath = imgpath; 198da4961c7SRebecca Cran found_http = false; 199da4961c7SRebecca Cran for (; !IsDevicePathEnd(devpath); 200da4961c7SRebecca Cran devpath = NextDevicePathNode(devpath)) { 201da4961c7SRebecca Cran if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH || 202da4961c7SRebecca Cran DevicePathSubType(devpath) != MSG_URI_DP) 203da4961c7SRebecca Cran continue; 204da4961c7SRebecca Cran uri = (URI_DEVICE_PATH *)devpath; 2055bca29b8SToomas Soome if (strncmp("http", (const char *)uri->Uri, 4) == 0) 206da4961c7SRebecca Cran found_http = true; 207da4961c7SRebecca Cran } 208da4961c7SRebecca Cran if (!found_http) 209da4961c7SRebecca Cran return (ENXIO); 210da4961c7SRebecca Cran 211da4961c7SRebecca Cran status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle); 212da4961c7SRebecca Cran if (EFI_ERROR(status)) 213da4961c7SRebecca Cran return (efi_status_to_errno(status)); 214da4961c7SRebecca Cran 215da4961c7SRebecca Cran err = efi_register_handles(&efihttp_dev, &handle, NULL, 1); 2161ee03da2SRebecca Cran if (!err) 2171ee03da2SRebecca Cran efihttp_init_done = true; 2181ee03da2SRebecca Cran 219da4961c7SRebecca Cran return (err); 220da4961c7SRebecca Cran } 221da4961c7SRebecca Cran 222da4961c7SRebecca Cran static int 2238136db28SToomas Soome efihttp_dev_strategy(void *devdata __unused, int rw __unused, 2248136db28SToomas Soome daddr_t blk __unused, size_t size __unused, char *buf __unused, 2258136db28SToomas Soome size_t *rsize __unused) 226da4961c7SRebecca Cran { 227da4961c7SRebecca Cran return (EIO); 228da4961c7SRebecca Cran } 229da4961c7SRebecca Cran 230da4961c7SRebecca Cran static int 231da4961c7SRebecca Cran efihttp_dev_open(struct open_file *f, ...) 232da4961c7SRebecca Cran { 233da4961c7SRebecca Cran EFI_HTTP_CONFIG_DATA config; 234da4961c7SRebecca Cran EFI_HTTPv4_ACCESS_POINT config_access; 235da4961c7SRebecca Cran DNS_DEVICE_PATH *dns; 236da4961c7SRebecca Cran EFI_DEVICE_PATH *devpath, *imgpath; 237da4961c7SRebecca Cran EFI_SERVICE_BINDING_PROTOCOL *sb; 238da4961c7SRebecca Cran IPv4_DEVICE_PATH *ipv4; 239da4961c7SRebecca Cran MAC_ADDR_DEVICE_PATH *mac; 240da4961c7SRebecca Cran URI_DEVICE_PATH *uri; 241da4961c7SRebecca Cran struct devdesc *dev; 242da4961c7SRebecca Cran struct open_efihttp *oh; 243da4961c7SRebecca Cran char *c; 244da4961c7SRebecca Cran EFI_HANDLE handle; 245da4961c7SRebecca Cran EFI_STATUS status; 246da4961c7SRebecca Cran int err, len; 247da4961c7SRebecca Cran 2481ee03da2SRebecca Cran if (!efihttp_init_done) 2491ee03da2SRebecca Cran return (ENXIO); 2501ee03da2SRebecca Cran 251da4961c7SRebecca Cran imgpath = efi_lookup_image_devpath(IH); 252da4961c7SRebecca Cran if (imgpath == NULL) 253da4961c7SRebecca Cran return (ENXIO); 254da4961c7SRebecca Cran devpath = imgpath; 255da4961c7SRebecca Cran status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle); 256da4961c7SRebecca Cran if (EFI_ERROR(status)) 257da4961c7SRebecca Cran return (efi_status_to_errno(status)); 25821b2840eSToomas Soome mac = NULL; 259da4961c7SRebecca Cran ipv4 = NULL; 260da4961c7SRebecca Cran dns = NULL; 261da4961c7SRebecca Cran uri = NULL; 262da4961c7SRebecca Cran for (; !IsDevicePathEnd(imgpath); 263da4961c7SRebecca Cran imgpath = NextDevicePathNode(imgpath)) { 264da4961c7SRebecca Cran if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH) 265da4961c7SRebecca Cran continue; 266da4961c7SRebecca Cran switch (DevicePathSubType(imgpath)) { 267da4961c7SRebecca Cran case MSG_MAC_ADDR_DP: 268da4961c7SRebecca Cran mac = (MAC_ADDR_DEVICE_PATH *)imgpath; 269da4961c7SRebecca Cran break; 270da4961c7SRebecca Cran case MSG_IPv4_DP: 271da4961c7SRebecca Cran ipv4 = (IPv4_DEVICE_PATH *)imgpath; 272da4961c7SRebecca Cran break; 273da4961c7SRebecca Cran case MSG_DNS_DP: 274da4961c7SRebecca Cran dns = (DNS_DEVICE_PATH *)imgpath; 275da4961c7SRebecca Cran break; 276da4961c7SRebecca Cran case MSG_URI_DP: 277da4961c7SRebecca Cran uri = (URI_DEVICE_PATH *)imgpath; 278da4961c7SRebecca Cran break; 279da4961c7SRebecca Cran default: 280da4961c7SRebecca Cran break; 281da4961c7SRebecca Cran } 282da4961c7SRebecca Cran } 283da4961c7SRebecca Cran 284da4961c7SRebecca Cran if (uri == NULL) 285da4961c7SRebecca Cran return (ENXIO); 286da4961c7SRebecca Cran 287da4961c7SRebecca Cran err = setup_ipv4_config2(handle, mac, ipv4, dns); 288da4961c7SRebecca Cran if (err) 289da4961c7SRebecca Cran return (err); 290da4961c7SRebecca Cran 291da4961c7SRebecca Cran oh = calloc(1, sizeof(struct open_efihttp)); 292da4961c7SRebecca Cran if (!oh) 293da4961c7SRebecca Cran return (ENOMEM); 294da4961c7SRebecca Cran oh->dev_handle = handle; 295da4961c7SRebecca Cran dev = (struct devdesc *)f->f_devdata; 296da4961c7SRebecca Cran dev->d_opendata = oh; 297da4961c7SRebecca Cran 298da4961c7SRebecca Cran status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL, 299da4961c7SRebecca Cran EFI_OPEN_PROTOCOL_GET_PROTOCOL); 300da4961c7SRebecca Cran if (EFI_ERROR(status)) { 301da4961c7SRebecca Cran err = efi_status_to_errno(status); 302da4961c7SRebecca Cran goto end; 303da4961c7SRebecca Cran } 304da4961c7SRebecca Cran 305da4961c7SRebecca Cran status = sb->CreateChild(sb, &oh->http_handle); 306da4961c7SRebecca Cran if (EFI_ERROR(status)) { 307da4961c7SRebecca Cran err = efi_status_to_errno(status); 308da4961c7SRebecca Cran goto end; 309da4961c7SRebecca Cran } 310da4961c7SRebecca Cran 311da4961c7SRebecca Cran status = BS->OpenProtocol(oh->http_handle, &http_guid, 312da4961c7SRebecca Cran (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 313da4961c7SRebecca Cran if (EFI_ERROR(status)) { 314da4961c7SRebecca Cran sb->DestroyChild(sb, oh->http_handle); 315da4961c7SRebecca Cran err = efi_status_to_errno(status); 316da4961c7SRebecca Cran goto end; 317da4961c7SRebecca Cran } 318da4961c7SRebecca Cran 319da4961c7SRebecca Cran config.HttpVersion = HttpVersion11; 320da4961c7SRebecca Cran config.TimeOutMillisec = 0; 321da4961c7SRebecca Cran config.LocalAddressIsIPv6 = FALSE; 322da4961c7SRebecca Cran config.AccessPoint.IPv4Node = &config_access; 323da4961c7SRebecca Cran config_access.UseDefaultAddress = TRUE; 324da4961c7SRebecca Cran config_access.LocalPort = 0; 325da4961c7SRebecca Cran status = oh->http->Configure(oh->http, &config); 326da4961c7SRebecca Cran if (EFI_ERROR(status)) { 327da4961c7SRebecca Cran sb->DestroyChild(sb, oh->http_handle); 328da4961c7SRebecca Cran err = efi_status_to_errno(status); 329da4961c7SRebecca Cran goto end; 330da4961c7SRebecca Cran } 331da4961c7SRebecca Cran 332da4961c7SRebecca Cran /* 333da4961c7SRebecca Cran * Here we make attempt to construct a "base" URI by stripping 334da4961c7SRebecca Cran * the last two path components from the loaded URI under the 335da4961c7SRebecca Cran * assumption that it is something like: 336da4961c7SRebecca Cran * 337da4961c7SRebecca Cran * http://127.0.0.1/foo/boot/loader.efi 338da4961c7SRebecca Cran * 339da4961c7SRebecca Cran * hoping to arriving at: 340da4961c7SRebecca Cran * 341da4961c7SRebecca Cran * http://127.0.0.1/foo/ 342da4961c7SRebecca Cran */ 343da4961c7SRebecca Cran len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH); 344da4961c7SRebecca Cran oh->uri_base = malloc(len + 1); 345da4961c7SRebecca Cran if (oh->uri_base == NULL) { 346da4961c7SRebecca Cran err = ENOMEM; 347da4961c7SRebecca Cran goto end; 348da4961c7SRebecca Cran } 3495bca29b8SToomas Soome strncpy(oh->uri_base, (const char *)uri->Uri, len); 350da4961c7SRebecca Cran oh->uri_base[len] = '\0'; 351da4961c7SRebecca Cran c = strrchr(oh->uri_base, '/'); 352da4961c7SRebecca Cran if (c != NULL) 353da4961c7SRebecca Cran *c = '\0'; 354da4961c7SRebecca Cran c = strrchr(oh->uri_base, '/'); 355da4961c7SRebecca Cran if (c != NULL && *(c + 1) != '\0') 356da4961c7SRebecca Cran *(c + 1) = '\0'; 357da4961c7SRebecca Cran 358da4961c7SRebecca Cran err = 0; 359da4961c7SRebecca Cran end: 360da4961c7SRebecca Cran if (err != 0) { 361da4961c7SRebecca Cran free(dev->d_opendata); 362da4961c7SRebecca Cran dev->d_opendata = NULL; 363da4961c7SRebecca Cran } 364da4961c7SRebecca Cran return (err); 365da4961c7SRebecca Cran } 366da4961c7SRebecca Cran 367da4961c7SRebecca Cran static int 368da4961c7SRebecca Cran efihttp_dev_close(struct open_file *f) 369da4961c7SRebecca Cran { 370da4961c7SRebecca Cran EFI_SERVICE_BINDING_PROTOCOL *sb; 371da4961c7SRebecca Cran struct devdesc *dev; 372da4961c7SRebecca Cran struct open_efihttp *oh; 373da4961c7SRebecca Cran EFI_STATUS status; 374da4961c7SRebecca Cran 375da4961c7SRebecca Cran dev = (struct devdesc *)f->f_devdata; 376da4961c7SRebecca Cran oh = (struct open_efihttp *)dev->d_opendata; 377da4961c7SRebecca Cran status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb, 378da4961c7SRebecca Cran IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 379da4961c7SRebecca Cran if (EFI_ERROR(status)) 380da4961c7SRebecca Cran return (efi_status_to_errno(status)); 381da4961c7SRebecca Cran sb->DestroyChild(sb, oh->http_handle); 382da4961c7SRebecca Cran free(oh->uri_base); 383da4961c7SRebecca Cran free(oh); 384da4961c7SRebecca Cran dev->d_opendata = NULL; 385da4961c7SRebecca Cran return (0); 386da4961c7SRebecca Cran } 387da4961c7SRebecca Cran 388da4961c7SRebecca Cran static int 389da4961c7SRebecca Cran _efihttp_fs_open(const char *path, struct open_file *f) 390da4961c7SRebecca Cran { 391da4961c7SRebecca Cran EFI_HTTP_CONFIG_DATA config; 392da4961c7SRebecca Cran EFI_HTTPv4_ACCESS_POINT config_access; 393da4961c7SRebecca Cran EFI_HTTP_TOKEN token; 394da4961c7SRebecca Cran EFI_HTTP_MESSAGE message; 395da4961c7SRebecca Cran EFI_HTTP_REQUEST_DATA request; 396da4961c7SRebecca Cran EFI_HTTP_RESPONSE_DATA response; 397da4961c7SRebecca Cran EFI_HTTP_HEADER headers[3]; 398da4961c7SRebecca Cran char *host, *hostp; 399da4961c7SRebecca Cran char *c; 400da4961c7SRebecca Cran struct devdesc *dev; 401da4961c7SRebecca Cran struct open_efihttp *oh; 402da4961c7SRebecca Cran struct file_efihttp *fh; 403da4961c7SRebecca Cran EFI_STATUS status; 404f7ae06cbSToomas Soome UINTN i; 405da4961c7SRebecca Cran int polltime; 406da4961c7SRebecca Cran bool done; 407da4961c7SRebecca Cran 408da4961c7SRebecca Cran dev = (struct devdesc *)f->f_devdata; 409da4961c7SRebecca Cran oh = (struct open_efihttp *)dev->d_opendata; 410da4961c7SRebecca Cran fh = calloc(1, sizeof(struct file_efihttp)); 411da4961c7SRebecca Cran if (fh == NULL) 412da4961c7SRebecca Cran return (ENOMEM); 413da4961c7SRebecca Cran f->f_fsdata = fh; 414da4961c7SRebecca Cran fh->path = strdup(path); 415da4961c7SRebecca Cran 416da4961c7SRebecca Cran /* 417da4961c7SRebecca Cran * Reset the HTTP state. 418da4961c7SRebecca Cran * 419da4961c7SRebecca Cran * EDK II's persistent HTTP connection handling is graceless, 420da4961c7SRebecca Cran * assuming that all connections are persistent regardless of 421da4961c7SRebecca Cran * any Connection: header or HTTP version reported by the 422da4961c7SRebecca Cran * server, and failing to send requests when a more sane 423da4961c7SRebecca Cran * implementation would seem to be just reestablishing the 424da4961c7SRebecca Cran * closed connection. 425da4961c7SRebecca Cran * 426da4961c7SRebecca Cran * In the hopes of having some robustness, we indicate to the 427da4961c7SRebecca Cran * server that we will close the connection by using a 428da4961c7SRebecca Cran * Connection: close header. And then here we manually 429da4961c7SRebecca Cran * unconfigure and reconfigure the http instance to force the 430da4961c7SRebecca Cran * connection closed. 431da4961c7SRebecca Cran */ 432da4961c7SRebecca Cran memset(&config, 0, sizeof(config)); 433da4961c7SRebecca Cran memset(&config_access, 0, sizeof(config_access)); 434da4961c7SRebecca Cran config.AccessPoint.IPv4Node = &config_access; 435da4961c7SRebecca Cran status = oh->http->GetModeData(oh->http, &config); 436da4961c7SRebecca Cran if (EFI_ERROR(status)) 437da4961c7SRebecca Cran return (efi_status_to_errno(status)); 438da4961c7SRebecca Cran status = oh->http->Configure(oh->http, NULL); 439da4961c7SRebecca Cran if (EFI_ERROR(status)) 440da4961c7SRebecca Cran return (efi_status_to_errno(status)); 441da4961c7SRebecca Cran status = oh->http->Configure(oh->http, &config); 442da4961c7SRebecca Cran if (EFI_ERROR(status)) 443da4961c7SRebecca Cran return (efi_status_to_errno(status)); 444da4961c7SRebecca Cran 445da4961c7SRebecca Cran /* Send the read request */ 446da4961c7SRebecca Cran done = false; 447da4961c7SRebecca Cran status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 448da4961c7SRebecca Cran &done, &token.Event); 449da4961c7SRebecca Cran if (EFI_ERROR(status)) 450da4961c7SRebecca Cran return (efi_status_to_errno(status)); 451da4961c7SRebecca Cran 452da4961c7SRebecca Cran /* extract the host portion of the URL */ 453da4961c7SRebecca Cran host = strdup(oh->uri_base); 454da4961c7SRebecca Cran if (host == NULL) 455da4961c7SRebecca Cran return (ENOMEM); 456da4961c7SRebecca Cran hostp = host; 457da4961c7SRebecca Cran /* Remove the protocol scheme */ 458da4961c7SRebecca Cran c = strchr(host, '/'); 459da4961c7SRebecca Cran if (c != NULL && *(c + 1) == '/') 460da4961c7SRebecca Cran hostp = (c + 2); 461da4961c7SRebecca Cran 462da4961c7SRebecca Cran /* Remove any path information */ 463da4961c7SRebecca Cran c = strchr(hostp, '/'); 464da4961c7SRebecca Cran if (c != NULL) 465da4961c7SRebecca Cran *c = '\0'; 466da4961c7SRebecca Cran 467da4961c7SRebecca Cran token.Status = EFI_NOT_READY; 468da4961c7SRebecca Cran token.Message = &message; 469da4961c7SRebecca Cran message.Data.Request = &request; 470da4961c7SRebecca Cran message.HeaderCount = 3; 471da4961c7SRebecca Cran message.Headers = headers; 472da4961c7SRebecca Cran message.BodyLength = 0; 473da4961c7SRebecca Cran message.Body = NULL; 474da4961c7SRebecca Cran request.Method = HttpMethodGet; 475da4961c7SRebecca Cran request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2); 4765bca29b8SToomas Soome headers[0].FieldName = (CHAR8 *)"Host"; 4775bca29b8SToomas Soome headers[0].FieldValue = (CHAR8 *)hostp; 4785bca29b8SToomas Soome headers[1].FieldName = (CHAR8 *)"Connection"; 4795bca29b8SToomas Soome headers[1].FieldValue = (CHAR8 *)"close"; 4805bca29b8SToomas Soome headers[2].FieldName = (CHAR8 *)"Accept"; 4815bca29b8SToomas Soome headers[2].FieldValue = (CHAR8 *)"*/*"; 482da4961c7SRebecca Cran cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base)); 483da4961c7SRebecca Cran cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path)); 484da4961c7SRebecca Cran status = oh->http->Request(oh->http, &token); 485da4961c7SRebecca Cran free(request.Url); 486da4961c7SRebecca Cran free(host); 487da4961c7SRebecca Cran if (EFI_ERROR(status)) { 488da4961c7SRebecca Cran BS->CloseEvent(token.Event); 489da4961c7SRebecca Cran return (efi_status_to_errno(status)); 490da4961c7SRebecca Cran } 491da4961c7SRebecca Cran 492da4961c7SRebecca Cran polltime = 0; 493da4961c7SRebecca Cran while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 494da4961c7SRebecca Cran status = oh->http->Poll(oh->http); 495da4961c7SRebecca Cran if (EFI_ERROR(status)) 496da4961c7SRebecca Cran break; 497da4961c7SRebecca Cran 498da4961c7SRebecca Cran if (!done) { 499da4961c7SRebecca Cran delay(100 * 1000); 500da4961c7SRebecca Cran polltime += 100; 501da4961c7SRebecca Cran } 502da4961c7SRebecca Cran } 503da4961c7SRebecca Cran BS->CloseEvent(token.Event); 504da4961c7SRebecca Cran if (EFI_ERROR(token.Status)) 505da4961c7SRebecca Cran return (efi_status_to_errno(token.Status)); 506da4961c7SRebecca Cran 507da4961c7SRebecca Cran /* Wait for the read response */ 508da4961c7SRebecca Cran done = false; 509da4961c7SRebecca Cran status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 510da4961c7SRebecca Cran &done, &token.Event); 511da4961c7SRebecca Cran if (EFI_ERROR(status)) 512da4961c7SRebecca Cran return (efi_status_to_errno(status)); 513da4961c7SRebecca Cran token.Status = EFI_NOT_READY; 514da4961c7SRebecca Cran token.Message = &message; 515da4961c7SRebecca Cran message.Data.Response = &response; 516da4961c7SRebecca Cran message.HeaderCount = 0; 517da4961c7SRebecca Cran message.Headers = NULL; 518da4961c7SRebecca Cran message.BodyLength = 0; 519da4961c7SRebecca Cran message.Body = NULL; 520da4961c7SRebecca Cran response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS; 521da4961c7SRebecca Cran status = oh->http->Response(oh->http, &token); 522da4961c7SRebecca Cran if (EFI_ERROR(status)) { 523da4961c7SRebecca Cran BS->CloseEvent(token.Event); 524da4961c7SRebecca Cran return (efi_status_to_errno(status)); 525da4961c7SRebecca Cran } 526da4961c7SRebecca Cran 527da4961c7SRebecca Cran polltime = 0; 528da4961c7SRebecca Cran while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 529da4961c7SRebecca Cran status = oh->http->Poll(oh->http); 530da4961c7SRebecca Cran if (EFI_ERROR(status)) 531da4961c7SRebecca Cran break; 532da4961c7SRebecca Cran 533da4961c7SRebecca Cran if (!done) { 534da4961c7SRebecca Cran delay(100 * 1000); 535da4961c7SRebecca Cran polltime += 100; 536da4961c7SRebecca Cran } 537da4961c7SRebecca Cran } 538da4961c7SRebecca Cran BS->CloseEvent(token.Event); 539da4961c7SRebecca Cran if (EFI_ERROR(token.Status)) { 540da4961c7SRebecca Cran BS->FreePool(message.Headers); 541da4961c7SRebecca Cran return (efi_status_to_errno(token.Status)); 542da4961c7SRebecca Cran } 543da4961c7SRebecca Cran if (response.StatusCode != HTTP_STATUS_200_OK) { 544da4961c7SRebecca Cran BS->FreePool(message.Headers); 545da4961c7SRebecca Cran return (EIO); 546da4961c7SRebecca Cran } 547da4961c7SRebecca Cran fh->size = 0; 548da4961c7SRebecca Cran fh->is_dir = false; 549da4961c7SRebecca Cran for (i = 0; i < message.HeaderCount; i++) { 5505bca29b8SToomas Soome if (strcasecmp((const char *)message.Headers[i].FieldName, 551da4961c7SRebecca Cran "Content-Length") == 0) 5525bca29b8SToomas Soome fh->size = strtoul((const char *) 5535bca29b8SToomas Soome message.Headers[i].FieldValue, NULL, 10); 5545bca29b8SToomas Soome else if (strcasecmp((const char *)message.Headers[i].FieldName, 555da4961c7SRebecca Cran "Content-type") == 0) { 5565bca29b8SToomas Soome if (strncmp((const char *)message.Headers[i].FieldValue, 5575bca29b8SToomas Soome "text/html", 9) == 0) 558da4961c7SRebecca Cran fh->is_dir = true; 559da4961c7SRebecca Cran } 560da4961c7SRebecca Cran } 561da4961c7SRebecca Cran 562da4961c7SRebecca Cran return (0); 563da4961c7SRebecca Cran } 564da4961c7SRebecca Cran 565da4961c7SRebecca Cran static int 566da4961c7SRebecca Cran efihttp_fs_open(const char *path, struct open_file *f) 567da4961c7SRebecca Cran { 568da4961c7SRebecca Cran char *path_slash; 569da4961c7SRebecca Cran int err; 570da4961c7SRebecca Cran 5711ee03da2SRebecca Cran if (!efihttp_init_done) 5721ee03da2SRebecca Cran return (ENXIO); 573da4961c7SRebecca Cran /* 574da4961c7SRebecca Cran * If any path fails to open, try with a trailing slash in 575da4961c7SRebecca Cran * case it's a directory. 576da4961c7SRebecca Cran */ 577da4961c7SRebecca Cran err = _efihttp_fs_open(path, f); 578da4961c7SRebecca Cran if (err != 0) { 579d455cd5aSD Scott Phillips /* 580d455cd5aSD Scott Phillips * Work around a bug in the EFI HTTP implementation which 581d455cd5aSD Scott Phillips * causes a crash if the http instance isn't torn down 582d455cd5aSD Scott Phillips * between requests. 583d455cd5aSD Scott Phillips * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917 584d455cd5aSD Scott Phillips */ 585d455cd5aSD Scott Phillips efihttp_dev_close(f); 586d455cd5aSD Scott Phillips efihttp_dev_open(f); 587da4961c7SRebecca Cran path_slash = malloc(strlen(path) + 2); 588da4961c7SRebecca Cran if (path_slash == NULL) 589da4961c7SRebecca Cran return (ENOMEM); 590da4961c7SRebecca Cran strcpy(path_slash, path); 591da4961c7SRebecca Cran strcat(path_slash, "/"); 592da4961c7SRebecca Cran err = _efihttp_fs_open(path_slash, f); 593da4961c7SRebecca Cran free(path_slash); 594da4961c7SRebecca Cran } 595da4961c7SRebecca Cran return (err); 596da4961c7SRebecca Cran } 597da4961c7SRebecca Cran 598da4961c7SRebecca Cran static int 5998136db28SToomas Soome efihttp_fs_close(struct open_file *f __unused) 600da4961c7SRebecca Cran { 601da4961c7SRebecca Cran return (0); 602da4961c7SRebecca Cran } 603da4961c7SRebecca Cran 604da4961c7SRebecca Cran static int 605da4961c7SRebecca Cran _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 606da4961c7SRebecca Cran { 607da4961c7SRebecca Cran EFI_HTTP_TOKEN token; 608da4961c7SRebecca Cran EFI_HTTP_MESSAGE message; 609da4961c7SRebecca Cran EFI_STATUS status; 610da4961c7SRebecca Cran struct devdesc *dev; 611da4961c7SRebecca Cran struct open_efihttp *oh; 612da4961c7SRebecca Cran struct file_efihttp *fh; 613da4961c7SRebecca Cran bool done; 614da4961c7SRebecca Cran int polltime; 615da4961c7SRebecca Cran 616da4961c7SRebecca Cran fh = (struct file_efihttp *)f->f_fsdata; 617da4961c7SRebecca Cran 618da4961c7SRebecca Cran if (fh->size > 0 && fh->offset >= fh->size) { 619da4961c7SRebecca Cran if (resid != NULL) 620da4961c7SRebecca Cran *resid = size; 621da4961c7SRebecca Cran 622da4961c7SRebecca Cran return 0; 623da4961c7SRebecca Cran } 624da4961c7SRebecca Cran 625da4961c7SRebecca Cran dev = (struct devdesc *)f->f_devdata; 626da4961c7SRebecca Cran oh = (struct open_efihttp *)dev->d_opendata; 627da4961c7SRebecca Cran done = false; 628da4961c7SRebecca Cran status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 629da4961c7SRebecca Cran &done, &token.Event); 630da4961c7SRebecca Cran if (EFI_ERROR(status)) { 631da4961c7SRebecca Cran return (efi_status_to_errno(status)); 632da4961c7SRebecca Cran } 633da4961c7SRebecca Cran token.Status = EFI_NOT_READY; 634da4961c7SRebecca Cran token.Message = &message; 635da4961c7SRebecca Cran message.Data.Request = NULL; 636da4961c7SRebecca Cran message.HeaderCount = 0; 637da4961c7SRebecca Cran message.Headers = NULL; 638da4961c7SRebecca Cran message.BodyLength = size; 639da4961c7SRebecca Cran message.Body = buf; 640da4961c7SRebecca Cran status = oh->http->Response(oh->http, &token); 641da4961c7SRebecca Cran if (status == EFI_CONNECTION_FIN) { 642da4961c7SRebecca Cran if (resid) 643da4961c7SRebecca Cran *resid = size; 644da4961c7SRebecca Cran return (0); 645da4961c7SRebecca Cran } else if (EFI_ERROR(status)) { 646da4961c7SRebecca Cran BS->CloseEvent(token.Event); 647da4961c7SRebecca Cran return (efi_status_to_errno(status)); 648da4961c7SRebecca Cran } 649da4961c7SRebecca Cran polltime = 0; 650da4961c7SRebecca Cran while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 651da4961c7SRebecca Cran status = oh->http->Poll(oh->http); 652da4961c7SRebecca Cran if (EFI_ERROR(status)) 653da4961c7SRebecca Cran break; 654da4961c7SRebecca Cran 655da4961c7SRebecca Cran if (!done) { 656da4961c7SRebecca Cran delay(100 * 1000); 657da4961c7SRebecca Cran polltime += 100; 658da4961c7SRebecca Cran } 659da4961c7SRebecca Cran } 660da4961c7SRebecca Cran BS->CloseEvent(token.Event); 661da4961c7SRebecca Cran if (token.Status == EFI_CONNECTION_FIN) { 662da4961c7SRebecca Cran if (resid) 663da4961c7SRebecca Cran *resid = size; 664da4961c7SRebecca Cran return (0); 665da4961c7SRebecca Cran } else if (EFI_ERROR(token.Status)) 666da4961c7SRebecca Cran return (efi_status_to_errno(token.Status)); 667da4961c7SRebecca Cran if (resid) 668da4961c7SRebecca Cran *resid = size - message.BodyLength; 669da4961c7SRebecca Cran fh->offset += message.BodyLength; 670da4961c7SRebecca Cran return (0); 671da4961c7SRebecca Cran } 672da4961c7SRebecca Cran 673da4961c7SRebecca Cran static int 674da4961c7SRebecca Cran efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 675da4961c7SRebecca Cran { 676da4961c7SRebecca Cran size_t res; 67721b2840eSToomas Soome int err = 0; 678da4961c7SRebecca Cran 679da4961c7SRebecca Cran while (size > 0) { 680da4961c7SRebecca Cran err = _efihttp_fs_read(f, buf, size, &res); 681da4961c7SRebecca Cran if (err != 0 || res == size) 682da4961c7SRebecca Cran goto end; 683da4961c7SRebecca Cran buf += (size - res); 684da4961c7SRebecca Cran size = res; 685da4961c7SRebecca Cran } 686da4961c7SRebecca Cran end: 687da4961c7SRebecca Cran if (resid) 688da4961c7SRebecca Cran *resid = size; 689da4961c7SRebecca Cran return (err); 690da4961c7SRebecca Cran } 691da4961c7SRebecca Cran 692da4961c7SRebecca Cran static int 6938136db28SToomas Soome efihttp_fs_write(struct open_file *f __unused, const void *buf __unused, 6948136db28SToomas Soome size_t size __unused, size_t *resid __unused) 695da4961c7SRebecca Cran { 696da4961c7SRebecca Cran return (EIO); 697da4961c7SRebecca Cran } 698da4961c7SRebecca Cran 699da4961c7SRebecca Cran static off_t 700da4961c7SRebecca Cran efihttp_fs_seek(struct open_file *f, off_t offset, int where) 701da4961c7SRebecca Cran { 702da4961c7SRebecca Cran struct file_efihttp *fh; 703da4961c7SRebecca Cran char *path; 704da4961c7SRebecca Cran void *buf; 705da4961c7SRebecca Cran size_t res, res2; 706da4961c7SRebecca Cran int err; 707da4961c7SRebecca Cran 708da4961c7SRebecca Cran fh = (struct file_efihttp *)f->f_fsdata; 709da4961c7SRebecca Cran if (where == SEEK_SET && fh->offset == offset) 710da4961c7SRebecca Cran return (0); 711da4961c7SRebecca Cran if (where == SEEK_SET && fh->offset < offset) { 712da4961c7SRebecca Cran buf = malloc(1500); 7138abc11f6SToomas Soome if (buf == NULL) 7148abc11f6SToomas Soome return (ENOMEM); 715da4961c7SRebecca Cran res = offset - fh->offset; 716da4961c7SRebecca Cran while (res > 0) { 717da4961c7SRebecca Cran err = _efihttp_fs_read(f, buf, min(1500, res), &res2); 718da4961c7SRebecca Cran if (err != 0) { 719da4961c7SRebecca Cran free(buf); 720da4961c7SRebecca Cran return (err); 721da4961c7SRebecca Cran } 722da4961c7SRebecca Cran res -= min(1500, res) - res2; 723da4961c7SRebecca Cran } 724da4961c7SRebecca Cran free(buf); 725da4961c7SRebecca Cran return (0); 726da4961c7SRebecca Cran } else if (where == SEEK_SET) { 727da4961c7SRebecca Cran path = fh->path; 728da4961c7SRebecca Cran fh->path = NULL; 729da4961c7SRebecca Cran efihttp_fs_close(f); 730d455cd5aSD Scott Phillips /* 731d455cd5aSD Scott Phillips * Work around a bug in the EFI HTTP implementation which 732d455cd5aSD Scott Phillips * causes a crash if the http instance isn't torn down 733d455cd5aSD Scott Phillips * between requests. 734d455cd5aSD Scott Phillips * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917 735d455cd5aSD Scott Phillips */ 736d455cd5aSD Scott Phillips efihttp_dev_close(f); 737d455cd5aSD Scott Phillips efihttp_dev_open(f); 738da4961c7SRebecca Cran err = efihttp_fs_open(path, f); 739da4961c7SRebecca Cran free(path); 740da4961c7SRebecca Cran if (err != 0) 741da4961c7SRebecca Cran return (err); 742da4961c7SRebecca Cran return efihttp_fs_seek(f, offset, where); 743da4961c7SRebecca Cran } 744da4961c7SRebecca Cran return (EIO); 745da4961c7SRebecca Cran } 746da4961c7SRebecca Cran 747da4961c7SRebecca Cran static int 748da4961c7SRebecca Cran efihttp_fs_stat(struct open_file *f, struct stat *sb) 749da4961c7SRebecca Cran { 750da4961c7SRebecca Cran struct file_efihttp *fh; 751da4961c7SRebecca Cran 752da4961c7SRebecca Cran fh = (struct file_efihttp *)f->f_fsdata; 753da4961c7SRebecca Cran memset(sb, 0, sizeof(*sb)); 754da4961c7SRebecca Cran sb->st_nlink = 1; 755da4961c7SRebecca Cran sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG); 756da4961c7SRebecca Cran sb->st_size = fh->size; 757da4961c7SRebecca Cran return (0); 758da4961c7SRebecca Cran } 759da4961c7SRebecca Cran 760da4961c7SRebecca Cran static int 761da4961c7SRebecca Cran efihttp_fs_readdir(struct open_file *f, struct dirent *d) 762da4961c7SRebecca Cran { 763da4961c7SRebecca Cran static char *dirbuf = NULL, *db2, *cursor; 764da4961c7SRebecca Cran static int dirbuf_len = 0; 765da4961c7SRebecca Cran char *end; 766da4961c7SRebecca Cran struct file_efihttp *fh; 767da4961c7SRebecca Cran 768da4961c7SRebecca Cran fh = (struct file_efihttp *)f->f_fsdata; 769da4961c7SRebecca Cran if (dirbuf_len < fh->size) { 770da4961c7SRebecca Cran db2 = realloc(dirbuf, fh->size); 771da4961c7SRebecca Cran if (db2 == NULL) { 772da4961c7SRebecca Cran free(dirbuf); 773da4961c7SRebecca Cran return (ENOMEM); 774da4961c7SRebecca Cran } else 775da4961c7SRebecca Cran dirbuf = db2; 776da4961c7SRebecca Cran 777da4961c7SRebecca Cran dirbuf_len = fh->size; 778da4961c7SRebecca Cran } 779da4961c7SRebecca Cran 780da4961c7SRebecca Cran if (fh->offset != fh->size) { 781da4961c7SRebecca Cran efihttp_fs_seek(f, 0, SEEK_SET); 782da4961c7SRebecca Cran efihttp_fs_read(f, dirbuf, dirbuf_len, NULL); 783da4961c7SRebecca Cran cursor = dirbuf; 784da4961c7SRebecca Cran } 785da4961c7SRebecca Cran 786da4961c7SRebecca Cran cursor = strstr(cursor, "<a href=\""); 787da4961c7SRebecca Cran if (cursor == NULL) 788da4961c7SRebecca Cran return (ENOENT); 789da4961c7SRebecca Cran cursor += 9; 790da4961c7SRebecca Cran end = strchr(cursor, '"'); 791da4961c7SRebecca Cran if (*(end - 1) == '/') { 792da4961c7SRebecca Cran end--; 793da4961c7SRebecca Cran d->d_type = DT_DIR; 794da4961c7SRebecca Cran } else 795da4961c7SRebecca Cran d->d_type = DT_REG; 796da4961c7SRebecca Cran memcpy(d->d_name, cursor, end - cursor); 797da4961c7SRebecca Cran d->d_name[end - cursor] = '\0'; 798da4961c7SRebecca Cran 799da4961c7SRebecca Cran return (0); 800da4961c7SRebecca Cran } 801