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