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