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