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