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