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