Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/efi/libefi/efihttp.c
101190 views
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
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
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
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
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
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
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
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
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
595
efihttp_fs_close(struct open_file *f __unused)
596
{
597
return (0);
598
}
599
600
static int
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
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
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
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
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
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
798