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