]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efihttp.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / stand / efi / libefi / efihttp.c
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 <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
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
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
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
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
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
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
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
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                 path_slash = malloc(strlen(path) + 2);
580                 if (path_slash == NULL)
581                         return (ENOMEM);
582                 strcpy(path_slash, path);
583                 strcat(path_slash, "/");
584                 err = _efihttp_fs_open(path_slash, f);
585                 free(path_slash);
586         }
587         return (err);
588 }
589
590 static int
591 efihttp_fs_close(struct open_file *f __unused)
592 {
593         return (0);
594 }
595
596 static int
597 _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
598 {
599         EFI_HTTP_TOKEN token;
600         EFI_HTTP_MESSAGE message;
601         EFI_STATUS status;
602         struct devdesc *dev;
603         struct open_efihttp *oh;
604         struct file_efihttp *fh;
605         bool done;
606         int polltime;
607
608         fh = (struct file_efihttp *)f->f_fsdata;
609
610         if (fh->size > 0 && fh->offset >= fh->size) {
611                 if (resid != NULL)
612                         *resid = size;
613
614                 return 0;
615         }
616
617         dev = (struct devdesc *)f->f_devdata;
618         oh = (struct open_efihttp *)dev->d_opendata;
619         done = false;
620         status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify,
621             &done, &token.Event);
622         if (EFI_ERROR(status)) {
623                 return (efi_status_to_errno(status));
624         }
625         token.Status = EFI_NOT_READY;
626         token.Message = &message;
627         message.Data.Request = NULL;
628         message.HeaderCount = 0;
629         message.Headers = NULL;
630         message.BodyLength = size;
631         message.Body = buf;
632         status = oh->http->Response(oh->http, &token);
633         if (status == EFI_CONNECTION_FIN) {
634                 if (resid)
635                         *resid = size;
636                 return (0);
637         } else if (EFI_ERROR(status)) {
638                 BS->CloseEvent(token.Event);
639                 return (efi_status_to_errno(status));
640         }
641         polltime = 0;
642         while (!done && polltime < EFIHTTP_POLL_TIMEOUT) {
643                 status = oh->http->Poll(oh->http);
644                 if (EFI_ERROR(status))
645                                 break;
646
647                 if (!done) {
648                         delay(100 * 1000);
649                         polltime += 100;
650                 }
651         }
652         BS->CloseEvent(token.Event);
653         if (token.Status == EFI_CONNECTION_FIN) {
654                 if (resid)
655                         *resid = size;
656                 return (0);
657         } else if (EFI_ERROR(token.Status))
658                 return (efi_status_to_errno(token.Status));
659         if (resid)
660                 *resid = size - message.BodyLength;
661         fh->offset += message.BodyLength;
662         return (0);
663 }
664
665 static int
666 efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
667 {
668         size_t res;
669         int err = 0;
670
671         while (size > 0) {
672                 err = _efihttp_fs_read(f, buf, size, &res);
673                 if (err != 0 || res == size)
674                         goto end;
675                 buf += (size - res);
676                 size = res;
677         }
678 end:
679         if (resid)
680                 *resid = size;
681         return (err);
682 }
683
684 static int
685 efihttp_fs_write(struct open_file *f __unused, const void *buf __unused,
686     size_t size __unused, size_t *resid __unused)
687 {
688         return (EIO);
689 }
690
691 static off_t
692 efihttp_fs_seek(struct open_file *f, off_t offset, int where)
693 {
694         struct file_efihttp *fh;
695         char *path;
696         void *buf;
697         size_t res, res2;
698         int err;
699
700         fh = (struct file_efihttp *)f->f_fsdata;
701         if (where == SEEK_SET && fh->offset == offset)
702                 return (0);
703         if (where == SEEK_SET && fh->offset < offset) {
704                 buf = malloc(1500);
705                 if (buf == NULL)
706                         return (ENOMEM);
707                 res = offset - fh->offset;
708                 while (res > 0) {
709                         err = _efihttp_fs_read(f, buf, min(1500, res), &res2);
710                         if (err != 0) {
711                                 free(buf);
712                                 return (err);
713                         }
714                         res -= min(1500, res) - res2;
715                 }
716                 free(buf);
717                 return (0);
718         } else if (where == SEEK_SET) {
719                 path = fh->path;
720                 fh->path = NULL;
721                 efihttp_fs_close(f);
722                 err = efihttp_fs_open(path, f);
723                 free(path);
724                 if (err != 0)
725                         return (err);
726                 return efihttp_fs_seek(f, offset, where);
727         }
728         return (EIO);
729 }
730
731 static int
732 efihttp_fs_stat(struct open_file *f, struct stat *sb)
733 {
734         struct file_efihttp *fh;
735
736         fh = (struct file_efihttp *)f->f_fsdata;
737         memset(sb, 0, sizeof(*sb));
738         sb->st_nlink = 1;
739         sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG);
740         sb->st_size = fh->size;
741         return (0);
742 }
743
744 static int
745 efihttp_fs_readdir(struct open_file *f, struct dirent *d)
746 {
747         static char *dirbuf = NULL, *db2, *cursor;
748         static int dirbuf_len = 0;
749         char *end;
750         struct file_efihttp *fh;
751
752         fh = (struct file_efihttp *)f->f_fsdata;
753         if (dirbuf_len < fh->size) {
754                 db2 = realloc(dirbuf, fh->size);
755                 if (db2 == NULL) {
756                         free(dirbuf);
757                         return (ENOMEM);
758                 } else
759                         dirbuf = db2;
760
761                 dirbuf_len = fh->size;
762         }
763
764         if (fh->offset != fh->size) {
765                 efihttp_fs_seek(f, 0, SEEK_SET);
766                 efihttp_fs_read(f, dirbuf, dirbuf_len, NULL);
767                 cursor = dirbuf;
768         }
769
770         cursor = strstr(cursor, "<a href=\"");
771         if (cursor == NULL)
772                 return (ENOENT);
773         cursor += 9;
774         end = strchr(cursor, '"');
775         if (*(end - 1) == '/') {
776                 end--;
777                 d->d_type = DT_DIR;
778         } else
779                 d->d_type = DT_REG;
780         memcpy(d->d_name, cursor, end - cursor);
781         d->d_name[end - cursor] = '\0';
782
783         return (0);
784 }