xref: /freebsd/stand/efi/libefi/efihttp.c (revision bbb7a2c7c329494e0148026f8568c0da4d8db085)
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 }
782