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