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