]> git.proxmox.com Git - efi-boot-shim.git/blob - lib/simple_file.c
Clean up warnings.
[efi-boot-shim.git] / lib / simple_file.c
1 /*
2 * Copyright 2012 <James.Bottomley@HansenPartnership.com>
3 *
4 * see COPYING file
5 */
6
7 #include <efi.h>
8 #include <efilib.h>
9
10 #include <console.h>
11 #include <simple_file.h>
12 #include <efiauthenticated.h>
13 #include <execute.h> /* for generate_path() */
14
15 static EFI_GUID IMAGE_PROTOCOL = LOADED_IMAGE_PROTOCOL;
16 static EFI_GUID SIMPLE_FS_PROTOCOL = SIMPLE_FILE_SYSTEM_PROTOCOL;
17 static EFI_GUID FILE_INFO = EFI_FILE_INFO_ID;
18 static EFI_GUID FS_INFO = EFI_FILE_SYSTEM_INFO_ID;
19
20 EFI_STATUS
21 simple_file_open_by_handle(EFI_HANDLE device, CHAR16 *name, EFI_FILE **file, UINT64 mode)
22 {
23 EFI_STATUS efi_status;
24 EFI_FILE_IO_INTERFACE *drive;
25 EFI_FILE *root;
26
27 efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
28 &SIMPLE_FS_PROTOCOL, (void **)&drive);
29
30 if (efi_status != EFI_SUCCESS) {
31 Print(L"Unable to find simple file protocol (%d)\n", efi_status);
32 goto error;
33 }
34
35 efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
36
37 if (efi_status != EFI_SUCCESS) {
38 Print(L"Failed to open drive volume (%d)\n", efi_status);
39 goto error;
40 }
41
42 efi_status = uefi_call_wrapper(root->Open, 5, root, file, name,
43 mode, 0);
44
45 error:
46 return efi_status;
47 }
48
49 EFI_STATUS
50 simple_file_open(EFI_HANDLE image, CHAR16 *name, EFI_FILE **file, UINT64 mode)
51 {
52 EFI_STATUS efi_status;
53 EFI_HANDLE device;
54 EFI_LOADED_IMAGE *li;
55 EFI_DEVICE_PATH *loadpath = NULL;
56 CHAR16 *PathName = NULL;
57
58 efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image,
59 &IMAGE_PROTOCOL, (void **)&li);
60
61 if (efi_status != EFI_SUCCESS)
62 return simple_file_open_by_handle(image, name, file, mode);
63
64 efi_status = generate_path(name, li, &loadpath, &PathName);
65
66 if (efi_status != EFI_SUCCESS) {
67 Print(L"Unable to generate load path for %s\n", name);
68 return efi_status;
69 }
70
71 device = li->DeviceHandle;
72
73 efi_status = simple_file_open_by_handle(device, PathName, file, mode);
74
75 FreePool(PathName);
76 FreePool(loadpath);
77
78 return efi_status;
79 }
80
81 EFI_STATUS
82 simple_dir_read_all_by_handle(EFI_HANDLE image, EFI_FILE *file, CHAR16* name, EFI_FILE_INFO **entries,
83 int *count)
84 {
85 EFI_STATUS status;
86 char buf[4096];
87 UINTN size = sizeof(buf);
88 EFI_FILE_INFO *fi = (void *)buf;
89
90 status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO,
91 &size, fi);
92 if (status != EFI_SUCCESS) {
93 Print(L"Failed to get file info\n");
94 goto out;
95 }
96 if ((fi->Attribute & EFI_FILE_DIRECTORY) == 0) {
97 Print(L"Not a directory %s\n", name);
98 status = EFI_INVALID_PARAMETER;
99 goto out;
100 }
101 size = 0;
102 *count = 0;
103 for (;;) {
104 UINTN len = sizeof(buf);
105 status = uefi_call_wrapper(file->Read, 3, file, &len, buf);
106 if (status != EFI_SUCCESS || len == 0)
107 break;
108 (*count)++;
109 size += len;
110 }
111 uefi_call_wrapper(file->SetPosition, 2, file, 0);
112
113 char *ptr = AllocatePool(size);
114 *entries = (EFI_FILE_INFO *)ptr;
115 if (!*entries)
116 return EFI_OUT_OF_RESOURCES;
117 int i;
118 for (i = 0; i < *count; i++) {
119 UINTN len = size;
120 uefi_call_wrapper(file->Read, 3, file, &len, ptr);
121 ptr += len;
122 size -= len;
123 }
124 status = EFI_SUCCESS;
125 out:
126 simple_file_close(file);
127 if (status != EFI_SUCCESS && *entries) {
128 FreePool(*entries);
129 *entries = NULL;
130 }
131 return status;
132 }
133
134 EFI_STATUS
135 simple_dir_read_all(EFI_HANDLE image, CHAR16 *name, EFI_FILE_INFO **entries,
136 int *count)
137 {
138 EFI_FILE *file;
139 EFI_STATUS status;
140
141 status = simple_file_open(image, name, &file, EFI_FILE_MODE_READ);
142 if (status != EFI_SUCCESS) {
143 Print(L"failed to open file %s: %d\n", name, status);
144 return status;
145 }
146
147 return simple_dir_read_all_by_handle(image, file, name, entries, count);
148 }
149
150 EFI_STATUS
151 simple_file_read_all(EFI_FILE *file, UINTN *size, void **buffer)
152 {
153 EFI_STATUS efi_status;
154 EFI_FILE_INFO *fi;
155 char buf[1024];
156
157 *size = sizeof(buf);
158 fi = (void *)buf;
159
160
161 efi_status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO,
162 size, fi);
163 if (efi_status != EFI_SUCCESS) {
164 Print(L"Failed to get file info\n");
165 return efi_status;
166 }
167
168 *size = fi->FileSize;
169
170 *buffer = AllocatePool(*size);
171 if (!*buffer) {
172 Print(L"Failed to allocate buffer of size %d\n", *size);
173 return EFI_OUT_OF_RESOURCES;
174 }
175 efi_status = uefi_call_wrapper(file->Read, 3, file, size, *buffer);
176
177 return efi_status;
178 }
179
180
181 EFI_STATUS
182 simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer)
183 {
184 EFI_STATUS efi_status;
185
186 efi_status = uefi_call_wrapper(file->Write, 3, file, &size, buffer);
187
188 return efi_status;
189 }
190
191 void
192 simple_file_close(EFI_FILE *file)
193 {
194 uefi_call_wrapper(file->Close, 1, file);
195 }
196
197 EFI_STATUS
198 simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h)
199 {
200 UINTN count, i;
201 EFI_HANDLE *vol_handles = NULL;
202 EFI_STATUS status;
203 CHAR16 **entries;
204 int val;
205
206 uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol,
207 &SIMPLE_FS_PROTOCOL, NULL, &count, &vol_handles);
208
209 if (!count || !vol_handles)
210 return EFI_NOT_FOUND;
211
212 entries = AllocatePool(sizeof(CHAR16 *) * (count+1));
213 if (!entries)
214 return EFI_OUT_OF_RESOURCES;
215
216 for (i = 0; i < count; i++) {
217 char buf[4096];
218 UINTN size = sizeof(buf);
219 EFI_FILE_SYSTEM_INFO *fi = (void *)buf;
220 EFI_FILE *root;
221 CHAR16 *name;
222 EFI_FILE_IO_INTERFACE *drive;
223
224 status = uefi_call_wrapper(BS->HandleProtocol, 3,
225 vol_handles[i],
226 &SIMPLE_FS_PROTOCOL,
227 (void **)&drive);
228 if (status != EFI_SUCCESS || !drive)
229 continue;
230
231 status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
232 if (status != EFI_SUCCESS)
233 continue;
234
235 status = uefi_call_wrapper(root->GetInfo, 4, root, &FS_INFO,
236 &size, fi);
237 if (status != EFI_SUCCESS)
238 continue;
239
240 name = fi->VolumeLabel;
241
242 if (!name || StrLen(name) == 0 || StrCmp(name, L" ") == 0)
243 name = DevicePathToStr(DevicePathFromHandle(vol_handles[i]));
244
245 entries[i] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16));
246 if (!entries[i])
247 break;
248 StrCpy(entries[i], name);
249 }
250 entries[i] = NULL;
251
252 val = console_select(title, entries, 0);
253
254 if (val >= 0) {
255 *selected = AllocatePool((StrLen(entries[val]) + 1) * sizeof(CHAR16));
256 if (*selected) {
257 StrCpy(*selected , entries[val]);
258 }
259 *h = vol_handles[val];
260 } else {
261 *selected = NULL;
262 *h = 0;
263 }
264
265 for (i = 0; i < count; i++) {
266 if (entries[i])
267 FreePool(entries[i]);
268 }
269 FreePool(entries);
270 FreePool(vol_handles);
271
272
273 return EFI_SUCCESS;
274 }
275
276 EFI_STATUS
277 simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter,
278 CHAR16 ***result, int *count, EFI_FILE_INFO **entries)
279 {
280 EFI_STATUS status;
281 int tot, offs = StrLen(filter), i, c, filtercount = 1;
282 EFI_FILE_INFO *next;
283 void *ptr;
284 CHAR16 *newfilter = AllocatePool((StrLen(filter) + 1) * sizeof(CHAR16)),
285 **filterarr;
286
287 if (!newfilter)
288 return EFI_OUT_OF_RESOURCES;
289
290 /* just in case efi ever stops writeable strings */
291 StrCpy(newfilter, filter);
292
293 for (i = 0; i < offs; i++) {
294 if (filter[i] == '|')
295 filtercount++;
296 }
297 filterarr = AllocatePool(filtercount * sizeof(void *));
298 if (!filterarr)
299 return EFI_OUT_OF_RESOURCES;
300 c = 0;
301 filterarr[c++] = newfilter;
302 for (i = 0; i < offs; i++) {
303 if (filter[i] == '|') {
304 newfilter[i] = '\0';
305 filterarr[c++] = &newfilter[i+1];
306 }
307 }
308
309 *count = 0;
310
311 status = simple_dir_read_all(image, name, entries, &tot);
312
313 if (status != EFI_SUCCESS)
314 goto out;
315 ptr = next = *entries;
316
317 for (i = 0; i < tot; i++) {
318 int len = StrLen(next->FileName);
319
320 for (c = 0; c < filtercount; c++) {
321 offs = StrLen(filterarr[c]);
322
323 if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0
324 || (next->Attribute & EFI_FILE_DIRECTORY)) {
325 (*count)++;
326 break;
327 }
328 }
329 ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16);
330 next = ptr;
331 }
332 if (*count)
333 *result = AllocatePool(((*count) + 1) * sizeof(void *));
334 else
335 *result = AllocatePool(2 * sizeof(void *));
336
337 *count = 0;
338 ptr = next = *entries;
339
340 for (i = 0; i < tot; i++) {
341 int len = StrLen(next->FileName);
342
343 if (StrCmp(next->FileName, L".") == 0)
344 /* ignore . directory */
345 goto next;
346
347 if (next->Attribute & EFI_FILE_DIRECTORY) {
348 (*result)[(*count)] = PoolPrint(L"%s/", next->FileName);
349 if (!(*result)[(*count)]) {
350 Print(L"Failed to allocate buffer");
351 return EFI_OUT_OF_RESOURCES;
352 }
353 (*count)++;
354 goto next;
355 }
356
357 for (c = 0; c < filtercount; c++) {
358 offs = StrLen(filterarr[c]);
359
360 if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0) {
361 (*result)[(*count)] = StrDuplicate(next->FileName);
362 if (!(*result)[(*count)]) {
363 Print(L"Failed to allocate buffer");
364 return EFI_OUT_OF_RESOURCES;
365 }
366 (*count)++;
367 } else {
368 continue;
369 }
370 break;
371 }
372
373 next:
374 if (StrCmp(next->FileName, L"..") == 0) {
375 /* place .. directory first */
376 CHAR16 *tmp = (*result)[(*count) - 1];
377
378 (*result)[(*count) - 1] = (*result)[0];
379 (*result)[0] = tmp;
380 }
381
382 ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16);
383 next = ptr;
384 }
385 if (*count == 0) {
386 /* no entries at all ... can happen because top level dir has no . or .. */
387 (*result)[(*count)++] = L"./";
388 }
389 (*result)[*count] = NULL;
390 status = EFI_SUCCESS;
391
392 out:
393 if (status != EFI_SUCCESS) {
394 if (*entries)
395 FreePool(*entries);
396 *entries = NULL;
397 if (*result)
398 FreePool(*result);
399 *result = NULL;
400 }
401 return status;
402 }
403
404 static void
405 free_entries(CHAR16 **entries, int count)
406 {
407 int i;
408
409 for (i = 0; i<count; i++)
410 FreePool(entries[i]);
411 }
412
413 void
414 simple_file_selector(EFI_HANDLE *im, CHAR16 **title, CHAR16 *name,
415 CHAR16 *filter, CHAR16 **result)
416 {
417 EFI_STATUS status;
418 CHAR16 **entries;
419 EFI_FILE_INFO *dmp;
420 int count, select, len;
421 CHAR16 *newname, *selected;
422
423 *result = NULL;
424 if (!name)
425 name = L"\\";
426 if (!filter)
427 filter = L"";
428 if (!*im) {
429 EFI_HANDLE h;
430 CHAR16 *volname;
431
432 simple_volume_selector(title, &volname, &h);
433 if (!volname)
434 return;
435 FreePool(volname);
436 *im = h;
437 }
438
439 newname = AllocatePool((StrLen(name) + 1)*sizeof(CHAR16));
440 if (!newname)
441 return;
442
443 StrCpy(newname, name);
444 name = newname;
445
446 redo:
447 status = simple_dir_filter(*im, name, filter, &entries, &count, &dmp);
448
449 if (status != EFI_SUCCESS)
450 goto out_free_name;
451
452 select = console_select(title, entries, 0);
453 if (select < 0)
454 /* ESC key */
455 goto out_free;
456 selected = entries[select];
457 /* note that memory used by selected is valid until dmp is freed */
458 len = StrLen(selected);
459 if (selected[len - 1] == '/') {
460 CHAR16 *newname;
461
462 /* stay where we are */
463 if (StrCmp(selected, L"./") == 0) {
464 free_entries(entries, count);
465 FreePool(entries);
466 entries = NULL;
467 FreePool(dmp);
468 goto redo;
469 } else if (StrCmp(selected, L"../") == 0) {
470 int i;
471
472 i = StrLen(name) - 1;
473
474
475 for (i = StrLen(name); i > 0; --i) {
476 if (name[i] == '\\')
477 break;
478 }
479 if (i == 0)
480 i = 1;
481
482 if (StrCmp(name, L"\\") != 0
483 && StrCmp(&name[i], L"..") != 0) {
484 name[i] = '\0';
485 free_entries(entries, count);
486 FreePool(entries);
487 entries = NULL;
488 FreePool(dmp);
489 goto redo;
490 }
491 }
492 newname = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16));
493 if (!newname)
494 goto out_free;
495 StrCpy(newname, name);
496
497 if (name[StrLen(name) - 1] != '\\')
498 StrCat(newname, L"\\");
499 StrCat(newname, selected);
500 /* remove trailing / */
501 newname[StrLen(newname) - 1] = '\0';
502
503 free_entries(entries, count);
504 FreePool(entries);
505 entries = NULL;
506 FreePool(dmp);
507 FreePool(name);
508 name = newname;
509
510 goto redo;
511 }
512 *result = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16));
513 if (*result) {
514 StrCpy(*result, name);
515 if (name[StrLen(name) - 1] != '\\')
516 StrCat(*result, L"\\");
517 StrCat(*result, selected);
518 }
519
520 out_free:
521 FreePool(dmp);
522 if (entries) {
523 free_entries(entries, count);
524 FreePool(entries);
525 }
526 out_free_name:
527 FreePool(name);
528 }