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