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
notify(EFI_EVENT event __unused,void * context)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
setup_ipv4_config2(EFI_HANDLE handle,MAC_ADDR_DEVICE_PATH * mac,IPv4_DEVICE_PATH * ipv4,DNS_DEVICE_PATH * dns)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
efihttp_dev_init(void)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
efihttp_dev_strategy(void * devdata __unused,int rw __unused,daddr_t blk __unused,size_t size __unused,char * buf __unused,size_t * rsize __unused)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
efihttp_dev_open(struct open_file * f,...)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
efihttp_dev_close(struct open_file * f)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
_efihttp_fs_open(const char * path,struct open_file * f)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
efihttp_fs_open(const char * path,struct open_file * f)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 if (f->f_dev != &efihttp_dev)
569 return (EINVAL);
570 /*
571 * If any path fails to open, try with a trailing slash in
572 * case it's a directory.
573 */
574 err = _efihttp_fs_open(path, f);
575 if (err != 0) {
576 /*
577 * Work around a bug in the EFI HTTP implementation which
578 * causes a crash if the http instance isn't torn down
579 * between requests.
580 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917
581 */
582 efihttp_dev_close(f);
583 efihttp_dev_open(f);
584 path_slash = malloc(strlen(path) + 2);
585 if (path_slash == NULL)
586 return (ENOMEM);
587 strcpy(path_slash, path);
588 strcat(path_slash, "/");
589 err = _efihttp_fs_open(path_slash, f);
590 free(path_slash);
591 }
592 return (err);
593 }
594
595 static int
efihttp_fs_close(struct open_file * f __unused)596 efihttp_fs_close(struct open_file *f __unused)
597 {
598 return (0);
599 }
600
601 static int
_efihttp_fs_read(struct open_file * f,void * buf,size_t size,size_t * resid)602 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
603 {
604 EFI_HTTP_TOKEN token;
605 EFI_HTTP_MESSAGE message;
606 EFI_STATUS status;
607 struct devdesc *dev;
608 struct open_efihttp *oh;
609 struct file_efihttp *fh;
610 bool done;
611 int polltime;
612
613 fh = (struct file_efihttp *)f->f_fsdata;
614
615 if (fh->size > 0 && fh->offset >= fh->size) {
616 if (resid != NULL)
617 *resid = size;
618
619 return 0;
620 }
621
622 dev = (struct devdesc *)f->f_devdata;
623 oh = (struct open_efihttp *)dev->d_opendata;
624 done = false;
625 status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
626 &done, &token.Event);
627 if (EFI_ERROR(status)) {
628 return (efi_status_to_errno(status));
629 }
630 token.Status = EFI_NOT_READY;
631 token.Message = &message;
632 message.Data.Request = NULL;
633 message.HeaderCount = 0;
634 message.Headers = NULL;
635 message.BodyLength = size;
636 message.Body = buf;
637 status = oh->http->Response(oh->http, &token);
638 if (status == EFI_CONNECTION_FIN) {
639 if (resid)
640 *resid = size;
641 return (0);
642 } else if (EFI_ERROR(status)) {
643 BS->CloseEvent(token.Event);
644 return (efi_status_to_errno(status));
645 }
646 polltime = 0;
647 while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
648 status = oh->http->Poll(oh->http);
649 if (EFI_ERROR(status))
650 break;
651
652 if (!done) {
653 delay(100 * 1000);
654 polltime += 100;
655 }
656 }
657 BS->CloseEvent(token.Event);
658 if (token.Status == EFI_CONNECTION_FIN) {
659 if (resid)
660 *resid = size;
661 return (0);
662 } else if (EFI_ERROR(token.Status))
663 return (efi_status_to_errno(token.Status));
664 if (resid)
665 *resid = size - message.BodyLength;
666 fh->offset += message.BodyLength;
667 return (0);
668 }
669
670 static int
efihttp_fs_read(struct open_file * f,void * buf,size_t size,size_t * resid)671 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
672 {
673 size_t res;
674 int err = 0;
675
676 while (size > 0) {
677 err = _efihttp_fs_read(f, buf, size, &res);
678 if (err != 0 || res == size)
679 goto end;
680 buf += (size - res);
681 size = res;
682 }
683 end:
684 if (resid)
685 *resid = size;
686 return (err);
687 }
688
689 static int
efihttp_fs_write(struct open_file * f __unused,const void * buf __unused,size_t size __unused,size_t * resid __unused)690 efihttp_fs_write(struct open_file *f __unused, const void *buf __unused,
691 size_t size __unused, size_t *resid __unused)
692 {
693 return (EIO);
694 }
695
696 static off_t
efihttp_fs_seek(struct open_file * f,off_t offset,int where)697 efihttp_fs_seek(struct open_file *f, off_t offset, int where)
698 {
699 struct file_efihttp *fh;
700 char *path;
701 void *buf;
702 size_t res, res2;
703 int err;
704
705 fh = (struct file_efihttp *)f->f_fsdata;
706 if (where == SEEK_SET && fh->offset == offset)
707 return (0);
708 if (where == SEEK_SET && fh->offset < offset) {
709 buf = malloc(1500);
710 if (buf == NULL)
711 return (ENOMEM);
712 res = offset - fh->offset;
713 while (res > 0) {
714 err = _efihttp_fs_read(f, buf, min(1500, res), &res2);
715 if (err != 0) {
716 free(buf);
717 return (err);
718 }
719 res -= min(1500, res) - res2;
720 }
721 free(buf);
722 return (0);
723 } else if (where == SEEK_SET) {
724 path = fh->path;
725 fh->path = NULL;
726 efihttp_fs_close(f);
727 /*
728 * Work around a bug in the EFI HTTP implementation which
729 * causes a crash if the http instance isn't torn down
730 * between requests.
731 * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917
732 */
733 efihttp_dev_close(f);
734 efihttp_dev_open(f);
735 err = efihttp_fs_open(path, f);
736 free(path);
737 if (err != 0)
738 return (err);
739 return efihttp_fs_seek(f, offset, where);
740 }
741 return (EIO);
742 }
743
744 static int
efihttp_fs_stat(struct open_file * f,struct stat * sb)745 efihttp_fs_stat(struct open_file *f, struct stat *sb)
746 {
747 struct file_efihttp *fh;
748
749 fh = (struct file_efihttp *)f->f_fsdata;
750 memset(sb, 0, sizeof(*sb));
751 sb->st_nlink = 1;
752 sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG);
753 sb->st_size = fh->size;
754 return (0);
755 }
756
757 static int
efihttp_fs_readdir(struct open_file * f,struct dirent * d)758 efihttp_fs_readdir(struct open_file *f, struct dirent *d)
759 {
760 static char *dirbuf = NULL, *db2, *cursor;
761 static int dirbuf_len = 0;
762 char *end;
763 struct file_efihttp *fh;
764
765 fh = (struct file_efihttp *)f->f_fsdata;
766 if (dirbuf_len < fh->size) {
767 db2 = realloc(dirbuf, fh->size);
768 if (db2 == NULL) {
769 free(dirbuf);
770 return (ENOMEM);
771 } else
772 dirbuf = db2;
773
774 dirbuf_len = fh->size;
775 }
776
777 if (fh->offset != fh->size) {
778 efihttp_fs_seek(f, 0, SEEK_SET);
779 efihttp_fs_read(f, dirbuf, dirbuf_len, NULL);
780 cursor = dirbuf;
781 }
782
783 cursor = strstr(cursor, "<a href=\"");
784 if (cursor == NULL)
785 return (ENOENT);
786 cursor += 9;
787 end = strchr(cursor, '"');
788 if (*(end - 1) == '/') {
789 end--;
790 d->d_type = DT_DIR;
791 } else
792 d->d_type = DT_REG;
793 memcpy(d->d_name, cursor, end - cursor);
794 d->d_name[end - cursor] = '\0';
795
796 return (0);
797 }
798