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, 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", 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, int rw, daddr_t blk, size_t size, char *buf, 221 size_t *rsize) 222 { 223 return (EIO); 224 } 225 226 static int 227 efihttp_dev_open(struct open_file *f, ...) 228 { 229 EFI_HTTP_CONFIG_DATA config; 230 EFI_HTTPv4_ACCESS_POINT config_access; 231 DNS_DEVICE_PATH *dns; 232 EFI_DEVICE_PATH *devpath, *imgpath; 233 EFI_SERVICE_BINDING_PROTOCOL *sb; 234 IPv4_DEVICE_PATH *ipv4; 235 MAC_ADDR_DEVICE_PATH *mac; 236 URI_DEVICE_PATH *uri; 237 struct devdesc *dev; 238 struct open_efihttp *oh; 239 char *c; 240 EFI_HANDLE handle; 241 EFI_STATUS status; 242 int err, len; 243 244 if (!efihttp_init_done) 245 return (ENXIO); 246 247 imgpath = efi_lookup_image_devpath(IH); 248 if (imgpath == NULL) 249 return (ENXIO); 250 devpath = imgpath; 251 status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle); 252 if (EFI_ERROR(status)) 253 return (efi_status_to_errno(status)); 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, 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 int 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 = "Host"; 472 headers[0].FieldValue = hostp; 473 headers[1].FieldName = "Connection"; 474 headers[1].FieldValue = "close"; 475 headers[2].FieldName = "Accept"; 476 headers[2].FieldValue = "*/*"; 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(message.Headers[i].FieldName, 546 "Content-Length") == 0) 547 fh->size = strtoul(message.Headers[i].FieldValue, NULL, 548 10); 549 else if (strcasecmp(message.Headers[i].FieldName, 550 "Content-type") == 0) { 551 if (strncmp(message.Headers[i].FieldValue, "text/html", 552 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 path_slash = malloc(strlen(path) + 2); 575 if (path_slash == NULL) 576 return (ENOMEM); 577 strcpy(path_slash, path); 578 strcat(path_slash, "/"); 579 err = _efihttp_fs_open(path_slash, f); 580 free(path_slash); 581 } 582 return (err); 583 } 584 585 static int 586 efihttp_fs_close(struct open_file *f) 587 { 588 return (0); 589 } 590 591 static int 592 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 593 { 594 EFI_HTTP_TOKEN token; 595 EFI_HTTP_MESSAGE message; 596 EFI_STATUS status; 597 struct devdesc *dev; 598 struct open_efihttp *oh; 599 struct file_efihttp *fh; 600 bool done; 601 int polltime; 602 603 fh = (struct file_efihttp *)f->f_fsdata; 604 605 if (fh->size > 0 && fh->offset >= fh->size) { 606 if (resid != NULL) 607 *resid = size; 608 609 return 0; 610 } 611 612 dev = (struct devdesc *)f->f_devdata; 613 oh = (struct open_efihttp *)dev->d_opendata; 614 done = false; 615 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, 616 &done, &token.Event); 617 if (EFI_ERROR(status)) { 618 return (efi_status_to_errno(status)); 619 } 620 token.Status = EFI_NOT_READY; 621 token.Message = &message; 622 message.Data.Request = NULL; 623 message.HeaderCount = 0; 624 message.Headers = NULL; 625 message.BodyLength = size; 626 message.Body = buf; 627 status = oh->http->Response(oh->http, &token); 628 if (status == EFI_CONNECTION_FIN) { 629 if (resid) 630 *resid = size; 631 return (0); 632 } else if (EFI_ERROR(status)) { 633 BS->CloseEvent(token.Event); 634 return (efi_status_to_errno(status)); 635 } 636 polltime = 0; 637 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { 638 status = oh->http->Poll(oh->http); 639 if (EFI_ERROR(status)) 640 break; 641 642 if (!done) { 643 delay(100 * 1000); 644 polltime += 100; 645 } 646 } 647 BS->CloseEvent(token.Event); 648 if (token.Status == EFI_CONNECTION_FIN) { 649 if (resid) 650 *resid = size; 651 return (0); 652 } else if (EFI_ERROR(token.Status)) 653 return (efi_status_to_errno(token.Status)); 654 if (resid) 655 *resid = size - message.BodyLength; 656 fh->offset += message.BodyLength; 657 return (0); 658 } 659 660 static int 661 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 662 { 663 size_t res; 664 int err; 665 666 while (size > 0) { 667 err = _efihttp_fs_read(f, buf, size, &res); 668 if (err != 0 || res == size) 669 goto end; 670 buf += (size - res); 671 size = res; 672 } 673 end: 674 if (resid) 675 *resid = size; 676 return (err); 677 } 678 679 static int 680 efihttp_fs_write(struct open_file *f, const void *buf, size_t size, size_t *resid) 681 { 682 return (EIO); 683 } 684 685 static off_t 686 efihttp_fs_seek(struct open_file *f, off_t offset, int where) 687 { 688 struct file_efihttp *fh; 689 char *path; 690 void *buf; 691 size_t res, res2; 692 int err; 693 694 fh = (struct file_efihttp *)f->f_fsdata; 695 if (where == SEEK_SET && fh->offset == offset) 696 return (0); 697 if (where == SEEK_SET && fh->offset < offset) { 698 buf = malloc(1500); 699 res = offset - fh->offset; 700 while (res > 0) { 701 err = _efihttp_fs_read(f, buf, min(1500, res), &res2); 702 if (err != 0) { 703 free(buf); 704 return (err); 705 } 706 res -= min(1500, res) - res2; 707 } 708 free(buf); 709 return (0); 710 } else if (where == SEEK_SET) { 711 path = fh->path; 712 fh->path = NULL; 713 efihttp_fs_close(f); 714 err = efihttp_fs_open(path, f); 715 free(path); 716 if (err != 0) 717 return (err); 718 return efihttp_fs_seek(f, offset, where); 719 } 720 return (EIO); 721 } 722 723 static int 724 efihttp_fs_stat(struct open_file *f, struct stat *sb) 725 { 726 struct file_efihttp *fh; 727 728 fh = (struct file_efihttp *)f->f_fsdata; 729 memset(sb, 0, sizeof(*sb)); 730 sb->st_nlink = 1; 731 sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG); 732 sb->st_size = fh->size; 733 return (0); 734 } 735 736 static int 737 efihttp_fs_readdir(struct open_file *f, struct dirent *d) 738 { 739 static char *dirbuf = NULL, *db2, *cursor; 740 static int dirbuf_len = 0; 741 char *end; 742 struct file_efihttp *fh; 743 744 fh = (struct file_efihttp *)f->f_fsdata; 745 if (dirbuf_len < fh->size) { 746 db2 = realloc(dirbuf, fh->size); 747 if (db2 == NULL) { 748 free(dirbuf); 749 return (ENOMEM); 750 } else 751 dirbuf = db2; 752 753 dirbuf_len = fh->size; 754 } 755 756 if (fh->offset != fh->size) { 757 efihttp_fs_seek(f, 0, SEEK_SET); 758 efihttp_fs_read(f, dirbuf, dirbuf_len, NULL); 759 cursor = dirbuf; 760 } 761 762 cursor = strstr(cursor, "<a href=\""); 763 if (cursor == NULL) 764 return (ENOENT); 765 cursor += 9; 766 end = strchr(cursor, '"'); 767 if (*(end - 1) == '/') { 768 end--; 769 d->d_type = DT_DIR; 770 } else 771 d->d_type = DT_REG; 772 memcpy(d->d_name, cursor, end - cursor); 773 d->d_name[end - cursor] = '\0'; 774 775 return (0); 776 } 777