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