]> code.delx.au - refind/blob - filesystems/fsw_efi.c
3ec649455e9a040738c3c7475d8fac31c71f4d85
[refind] / filesystems / fsw_efi.c
1 /**
2 * \file fsw_efi.c
3 * EFI host environment code.
4 */
5
6 /*-
7 * Copyright (c) 2006 Christoph Pfisterer
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are
11 * met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the
19 * distribution.
20 *
21 * * Neither the name of Christoph Pfisterer nor the names of the
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37 /*
38 * Changes by Roderick Smith are licensed under the preceding terms.
39 */
40
41 #include "fsw_efi.h"
42 #include "fsw_core.h"
43 #ifdef __MAKEWITH_GNUEFI
44 #include "edk2/DriverBinding.h"
45 #include "edk2/ComponentName.h"
46 #endif
47 #include "../include/refit_call_wrapper.h"
48
49 #define DEBUG_LEVEL 0
50
51 #ifndef FSTYPE
52 /** The file system type name to use. */
53 #define FSTYPE ext2
54 #endif
55
56 #ifdef __MAKEWITH_GNUEFI
57
58 #define EFI_DISK_IO_PROTOCOL_GUID \
59 { \
60 0xce345171, 0xba0b, 0x11d2, {0x8e, 0x4f, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
61 }
62
63 #define EFI_BLOCK_IO_PROTOCOL_GUID \
64 { \
65 0x964e5b21, 0x6459, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
66 }
67
68 EFI_GUID gEfiDriverBindingProtocolGuid = EFI_DRIVER_BINDING_PROTOCOL_GUID;
69 EFI_GUID gEfiComponentNameProtocolGuid = EFI_COMPONENT_NAME_PROTOCOL_GUID;
70 EFI_GUID gEfiDiskIoProtocolGuid = EFI_DISK_IO_PROTOCOL_GUID;
71 EFI_GUID gEfiBlockIoProtocolGuid = EFI_BLOCK_IO_PROTOCOL_GUID;
72 EFI_GUID gEfiFileInfoGuid = EFI_FILE_INFO_ID;
73 EFI_GUID gEfiFileSystemInfoGuid = EFI_FILE_SYSTEM_INFO_ID;
74 EFI_GUID gEfiFileSystemVolumeLabelInfoIdGuid = EFI_FILE_SYSTEM_VOLUME_LABEL_INFO_ID;
75 #define gEfiSimpleFileSystemProtocolGuid FileSystemProtocol
76 #endif
77
78 /** Helper macro for stringification. */
79 #define FSW_EFI_STRINGIFY(x) #x
80 /** Expands to the EFI driver name given the file system type name. */
81 #define FSW_EFI_DRIVER_NAME(t) L"rEFInd 0.10.3 " FSW_EFI_STRINGIFY(t) L" File System Driver"
82
83 // function prototypes
84
85 EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
86 IN EFI_HANDLE ControllerHandle,
87 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
88 EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
89 IN EFI_HANDLE ControllerHandle,
90 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
91 EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
92 IN EFI_HANDLE ControllerHandle,
93 IN UINTN NumberOfChildren,
94 IN EFI_HANDLE *ChildHandleBuffer);
95
96 EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
97 IN CHAR8 *Language,
98 OUT CHAR16 **DriverName);
99 EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
100 IN EFI_HANDLE ControllerHandle,
101 IN EFI_HANDLE ChildHandle OPTIONAL,
102 IN CHAR8 *Language,
103 OUT CHAR16 **ControllerName);
104
105 void EFIAPI fsw_efi_change_blocksize(struct fsw_volume *vol,
106 fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
107 fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize);
108 fsw_status_t EFIAPI fsw_efi_read_block(struct fsw_volume *vol, fsw_u64 phys_bno, void *buffer);
109
110 EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume);
111
112 EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
113 OUT EFI_FILE **Root);
114 EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
115 OUT EFI_FILE **NewFileHandle);
116
117 EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
118 IN OUT UINTN *BufferSize,
119 OUT VOID *Buffer);
120 EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
121 OUT UINT64 *Position);
122 EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
123 IN UINT64 Position);
124
125 EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
126 OUT EFI_FILE **NewHandle,
127 IN CHAR16 *FileName,
128 IN UINT64 OpenMode,
129 IN UINT64 Attributes);
130 EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
131 IN OUT UINTN *BufferSize,
132 OUT VOID *Buffer);
133 EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
134 IN UINT64 Position);
135
136 EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
137 IN EFI_GUID *InformationType,
138 IN OUT UINTN *BufferSize,
139 OUT VOID *Buffer);
140 EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
141 IN struct fsw_dnode *dno,
142 IN OUT UINTN *BufferSize,
143 OUT VOID *Buffer);
144
145 /**
146 * Structure for holding disk cache data.
147 */
148
149 #define CACHE_SIZE 131072 /* 128KiB */
150 struct cache_data {
151 fsw_u8 *Cache;
152 fsw_u64 CacheStart;
153 BOOLEAN CacheValid;
154 FSW_VOLUME_DATA *Volume; // NOTE: Do not deallocate; copied here to ID volume
155 };
156
157 #define NUM_CACHES 2 /* Don't increase without modifying fsw_efi_read_block() */
158 static struct cache_data Caches[NUM_CACHES];
159 static int LastRead = -1;
160
161 /**
162 * Interface structure for the EFI Driver Binding protocol.
163 */
164
165 EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = {
166 fsw_efi_DriverBinding_Supported,
167 fsw_efi_DriverBinding_Start,
168 fsw_efi_DriverBinding_Stop,
169 0x10,
170 NULL,
171 NULL
172 };
173
174 /**
175 * Interface structure for the EFI Component Name protocol.
176 */
177
178 EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = {
179 fsw_efi_ComponentName_GetDriverName,
180 fsw_efi_ComponentName_GetControllerName,
181 (CHAR8*) "eng"
182 };
183
184 /**
185 * Dispatch table for our FSW host driver.
186 */
187
188 struct fsw_host_table fsw_efi_host_table = {
189 FSW_STRING_TYPE_UTF16,
190
191 fsw_efi_change_blocksize,
192 fsw_efi_read_block
193 };
194
195 extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE);
196
197
198 static VOID EFIAPI fsw_efi_clear_cache(VOID) {
199 int i;
200
201 // clear the cache
202 for (i = 0; i < NUM_CACHES; i++) {
203 if (Caches[i].Cache != NULL) {
204 FreePool(Caches[i].Cache);
205 Caches[i].Cache = NULL;
206 } // if
207 Caches[i].CacheStart = 0;
208 Caches[i].CacheValid = FALSE;
209 Caches[i].Volume = NULL;
210 }
211 LastRead = -1;
212 } // VOID EFIAPI fsw_efi_clear_cache();
213
214 /**
215 * Image entry point. Installs the Driver Binding and Component Name protocols
216 * on the image's handle. Actually mounting a file system is initiated through
217 * the Driver Binding protocol at the firmware's request.
218 */
219 EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle,
220 IN EFI_SYSTEM_TABLE *SystemTable)
221 {
222 EFI_STATUS Status;
223
224 #ifndef __MAKEWITH_TIANO
225 // Not available in EDK2 toolkit
226 InitializeLib(ImageHandle, SystemTable);
227 #endif
228
229 // complete Driver Binding protocol instance
230 fsw_efi_DriverBinding_table.ImageHandle = ImageHandle;
231 fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle;
232 // install Driver Binding protocol
233 Status = refit_call4_wrapper(BS->InstallProtocolInterface, &fsw_efi_DriverBinding_table.DriverBindingHandle,
234 &gEfiDriverBindingProtocolGuid,
235 EFI_NATIVE_INTERFACE,
236 &fsw_efi_DriverBinding_table);
237 if (EFI_ERROR (Status)) {
238 return Status;
239 }
240
241 // install Component Name protocol
242 Status = refit_call4_wrapper(BS->InstallProtocolInterface, &fsw_efi_DriverBinding_table.DriverBindingHandle,
243 &gEfiComponentNameProtocolGuid,
244 EFI_NATIVE_INTERFACE,
245 &fsw_efi_ComponentName_table);
246 if (EFI_ERROR (Status)) {
247 return Status;
248 }
249
250 // OverrideFunctions();
251 // Msg = NULL;
252 // msgCursor = NULL;
253 // Status = BS->LocateProtocol(&gMsgLogProtocolGuid, NULL, (VOID **) &Msg);
254 // if (!EFI_ERROR(Status) && (Msg != NULL)) {
255 // msgCursor = Msg->Cursor;
256 // BootLog("MsgLog installed into VBoxFs\n");
257 // }
258
259 return EFI_SUCCESS;
260 }
261
262 #ifdef __MAKEWITH_GNUEFI
263 EFI_DRIVER_ENTRY_POINT(fsw_efi_main)
264 #endif
265
266 /**
267 * Driver Binding EFI protocol, Supported function. This function is called by EFI
268 * to test if this driver can handle a certain device. Our implementation only checks
269 * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols)
270 * and implicitly checks if the disk is already in use by another driver.
271 */
272
273 EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
274 IN EFI_HANDLE ControllerHandle,
275 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
276 {
277 EFI_STATUS Status;
278 EFI_DISK_IO *DiskIo;
279
280 // we check for both DiskIO and BlockIO protocols
281
282 // first, open DiskIO
283 Status = refit_call6_wrapper(BS->OpenProtocol, ControllerHandle,
284 &gEfiDiskIoProtocolGuid,
285 (VOID **) &DiskIo,
286 This->DriverBindingHandle,
287 ControllerHandle,
288 EFI_OPEN_PROTOCOL_BY_DRIVER);
289 if (EFI_ERROR(Status))
290 return Status;
291
292 // we were just checking, close it again
293 refit_call4_wrapper(BS->CloseProtocol, ControllerHandle,
294 &gEfiDiskIoProtocolGuid,
295 This->DriverBindingHandle,
296 ControllerHandle);
297
298 // next, check BlockIO without actually opening it
299 Status = refit_call6_wrapper(BS->OpenProtocol, ControllerHandle,
300 &gEfiBlockIoProtocolGuid,
301 NULL,
302 This->DriverBindingHandle,
303 ControllerHandle,
304 EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
305 return Status;
306 }
307
308 /**
309 * Driver Binding EFI protocol, Start function. This function is called by EFI
310 * to start driving the given device. It is still possible at this point to
311 * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver
312 * cannot find the superblock signature (or equivalent) that it expects.
313 *
314 * This function allocates memory for a per-volume structure, opens the
315 * required protocols (just Disk I/O in our case, Block I/O is only looked
316 * at to get the MediaId field), and lets the FSW core mount the file system.
317 * If successful, an EFI Simple File System protocol is exported on the
318 * device handle.
319 */
320
321 EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
322 IN EFI_HANDLE ControllerHandle,
323 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
324 {
325 EFI_STATUS Status;
326 EFI_BLOCK_IO *BlockIo;
327 EFI_DISK_IO *DiskIo;
328 FSW_VOLUME_DATA *Volume;
329
330 #if DEBUG_LEVEL
331 Print(L"fsw_efi_DriverBinding_Start\n");
332 #endif
333
334 // open consumed protocols
335 Status = refit_call6_wrapper(BS->OpenProtocol, ControllerHandle,
336 &gEfiBlockIoProtocolGuid,
337 (VOID **) &BlockIo,
338 This->DriverBindingHandle,
339 ControllerHandle,
340 EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId
341 if (EFI_ERROR(Status)) {
342 // Print(L"Fsw ERROR: OpenProtocol(BlockIo) returned %x\n", Status);
343 return Status;
344 }
345
346 Status = refit_call6_wrapper(BS->OpenProtocol, ControllerHandle,
347 &gEfiDiskIoProtocolGuid,
348 (VOID **) &DiskIo,
349 This->DriverBindingHandle,
350 ControllerHandle,
351 EFI_OPEN_PROTOCOL_BY_DRIVER);
352 if (EFI_ERROR(Status)) {
353 Print(L"Fsw ERROR: OpenProtocol(DiskIo) returned %x\n", Status);
354 return Status;
355 }
356
357 // allocate volume structure
358 Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA));
359 Volume->Signature = FSW_VOLUME_DATA_SIGNATURE;
360 Volume->Handle = ControllerHandle;
361 Volume->DiskIo = DiskIo;
362 Volume->MediaId = BlockIo->Media->MediaId;
363 Volume->LastIOStatus = EFI_SUCCESS;
364
365 // mount the filesystem
366 Status = fsw_efi_map_status(fsw_mount(Volume, &fsw_efi_host_table,
367 &FSW_FSTYPE_TABLE_NAME(FSTYPE), &Volume->vol),
368 Volume);
369
370 if (!EFI_ERROR(Status)) {
371 // register the SimpleFileSystem protocol
372 Volume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION;
373 Volume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume;
374 Status = refit_call4_wrapper(BS->InstallMultipleProtocolInterfaces, &ControllerHandle,
375 &gEfiSimpleFileSystemProtocolGuid,
376 &Volume->FileSystem,
377 NULL);
378 if (EFI_ERROR(Status)) {
379 // Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status);
380 }
381 }
382
383 // on errors, close the opened protocols
384 if (EFI_ERROR(Status)) {
385 if (Volume->vol != NULL)
386 fsw_unmount(Volume->vol);
387 FreePool(Volume);
388
389 refit_call4_wrapper(BS->CloseProtocol, ControllerHandle,
390 &gEfiDiskIoProtocolGuid,
391 This->DriverBindingHandle,
392 ControllerHandle);
393 }
394 return Status;
395 }
396
397 /**
398 * Driver Binding EFI protocol, Stop function. This function is called by EFI
399 * to stop the driver on the given device. This translates to an unmount
400 * call for the FSW core.
401 *
402 * We assume that all file handles on the volume have been closed before
403 * the driver is stopped. At least with the EFI shell, that is actually the
404 * case; it closes all file handles between commands.
405 */
406
407 EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
408 IN EFI_HANDLE ControllerHandle,
409 IN UINTN NumberOfChildren,
410 IN EFI_HANDLE *ChildHandleBuffer)
411 {
412 EFI_STATUS Status;
413 EFI_FILE_IO_INTERFACE *FileSystem;
414 FSW_VOLUME_DATA *Volume;
415
416 #if DEBUG_LEVEL
417 Print(L"fsw_efi_DriverBinding_Stop\n");
418 #endif
419
420 // get the installed SimpleFileSystem interface
421 Status = refit_call6_wrapper(BS->OpenProtocol, ControllerHandle,
422 &gEfiSimpleFileSystemProtocolGuid,
423 (VOID **) &FileSystem,
424 This->DriverBindingHandle,
425 ControllerHandle,
426 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
427 if (EFI_ERROR(Status))
428 return EFI_UNSUPPORTED;
429
430 // get private data structure
431 Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem);
432
433 // uninstall Simple File System protocol
434 Status = refit_call4_wrapper(BS->UninstallMultipleProtocolInterfaces, ControllerHandle,
435 &gEfiSimpleFileSystemProtocolGuid, &Volume->FileSystem,
436 NULL);
437 if (EFI_ERROR(Status)) {
438 // Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status);
439 return Status;
440 }
441 #if DEBUG_LEVEL
442 Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n");
443 #endif
444
445 // release private data structure
446 if (Volume->vol != NULL)
447 fsw_unmount(Volume->vol);
448 FreePool(Volume);
449
450 // close the consumed protocols
451 Status = refit_call4_wrapper(BS->CloseProtocol, ControllerHandle,
452 &gEfiDiskIoProtocolGuid,
453 This->DriverBindingHandle,
454 ControllerHandle);
455
456 // clear the cache
457 fsw_efi_clear_cache();
458
459 return Status;
460 }
461
462 /**
463 * Component Name EFI protocol, GetDriverName function. Used by the EFI
464 * environment to inquire the name of this driver. The name returned is
465 * based on the file system type actually used in compilation.
466 */
467
468 EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
469 IN CHAR8 *Language,
470 OUT CHAR16 **DriverName)
471 {
472 if (Language == NULL || DriverName == NULL)
473 return EFI_INVALID_PARAMETER;
474
475 if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) {
476 *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE);
477 return EFI_SUCCESS;
478 }
479 return EFI_UNSUPPORTED;
480 }
481
482 /**
483 * Component Name EFI protocol, GetControllerName function. Not implemented
484 * because this is not a "bus" driver in the sense of the EFI Driver Model.
485 */
486
487 EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
488 IN EFI_HANDLE ControllerHandle,
489 IN EFI_HANDLE ChildHandle OPTIONAL,
490 IN CHAR8 *Language,
491 OUT CHAR16 **ControllerName)
492 {
493 return EFI_UNSUPPORTED;
494 }
495
496 /**
497 * FSW interface function for block size changes. This function is called by the FSW core
498 * when the file system driver changes the block sizes for the volume.
499 */
500
501 void EFIAPI fsw_efi_change_blocksize(struct fsw_volume *vol,
502 fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
503 fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize)
504 {
505 // nothing to do
506 }
507
508 /**
509 * FSW interface function to read data blocks. This function is called by the FSW core
510 * to read a block of data from the device. The buffer is allocated by the core code.
511 * Two caches are maintained, so as to improve performance on some systems. (VirtualBox
512 * is particularly susceptible to performance problems with an uncached driver -- the
513 * ext2 driver can take 200 seconds to load a Linux kernel under VirtualBox, whereas
514 * the time is more like 3 seconds with a cache!) Two independent caches are maintained
515 * because the ext2fs driver tends to alternate between accessing two parts of the
516 * disk.
517 */
518
519 fsw_status_t EFIAPI fsw_efi_read_block(struct fsw_volume *vol, fsw_u64 phys_bno, void *buffer) {
520 int i, ReadCache = -1;
521 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data;
522 EFI_STATUS Status = EFI_SUCCESS;
523 BOOLEAN ReadOneBlock = FALSE;
524 UINT64 StartRead = (UINT64) phys_bno * (UINT64) vol->phys_blocksize;
525
526 if (buffer == NULL)
527 return (fsw_status_t) EFI_BAD_BUFFER_SIZE;
528
529 // Initialize static data structures, if necessary....
530 if (LastRead < 0) {
531 fsw_efi_clear_cache();
532 } // if
533
534 // Look for a cache hit on the current query....
535 i = 0;
536 do {
537 if ((Caches[i].Volume == Volume) &&
538 (Caches[i].CacheValid == TRUE) &&
539 (StartRead >= Caches[i].CacheStart) &&
540 ((StartRead + vol->phys_blocksize) <= (Caches[i].CacheStart + CACHE_SIZE))) {
541 ReadCache = i;
542 }
543 i++;
544 } while ((i < NUM_CACHES) && (ReadCache < 0));
545
546 // No cache hit found; load new cache and pass it on....
547 if (ReadCache < 0) {
548 if (LastRead == -1)
549 LastRead = 1;
550 ReadCache = 1 - LastRead; // NOTE: If NUM_CACHES > 2, this must become more complex
551 Caches[ReadCache].CacheValid = FALSE;
552 if (Caches[ReadCache].Cache == NULL)
553 Caches[ReadCache].Cache = AllocatePool(CACHE_SIZE);
554 if (Caches[ReadCache].Cache != NULL) {
555 // TODO: Below call hangs on my 32-bit Mac Mini when compiled with GNU-EFI.
556 // The same binary is fine under VirtualBox, and the same call is fine when
557 // compiled with Tianocore. Further clue: Omitting "Status =" avoids the
558 // hang but produces a failure to mount the filesystem, even when the same
559 // change is made to later similar call. Calling Volume->DiskIo->ReadDisk()
560 // directly (without refit_call5_wrapper()) changes nothing. Placing Print()
561 // statements at the start and end of the function, and before and after the
562 // ReadDisk() call, suggests that when it fails, the program is executing
563 // code starting mid-function, so there seems to be something messed up in
564 // the way the function is being called. FIGURE THIS OUT!
565 Status = refit_call5_wrapper(Volume->DiskIo->ReadDisk, Volume->DiskIo, Volume->MediaId,
566 StartRead, (UINTN) CACHE_SIZE, (VOID*) Caches[ReadCache].Cache);
567 if (!EFI_ERROR(Status)) {
568 Caches[ReadCache].CacheStart = StartRead;
569 Caches[ReadCache].CacheValid = TRUE;
570 Caches[ReadCache].Volume = Volume;
571 LastRead = ReadCache;
572 } else {
573 ReadOneBlock = TRUE;
574 }
575 } else {
576 ReadOneBlock = TRUE;
577 } // if cache memory allocated
578 } // if (ReadCache < 0)
579
580 if (Caches[ReadCache].Cache != NULL && Caches[ReadCache].CacheValid == TRUE && vol->phys_blocksize > 0) {
581 CopyMem(buffer, &Caches[ReadCache].Cache[StartRead - Caches[ReadCache].CacheStart], vol->phys_blocksize);
582 } else {
583 ReadOneBlock = TRUE;
584 }
585
586 if (ReadOneBlock) { // Something's failed, so try a simple disk read of one block....
587 Status = refit_call5_wrapper(Volume->DiskIo->ReadDisk, Volume->DiskIo, Volume->MediaId,
588 phys_bno * vol->phys_blocksize,
589 (UINTN) vol->phys_blocksize,
590 (VOID*) buffer);
591 }
592 Volume->LastIOStatus = Status;
593
594 return Status;
595 } // fsw_status_t *fsw_efi_read_block()
596
597 /**
598 * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced
599 * by fsw_efi_read_block, so we map it back to the EFI status code remembered from
600 * the last I/O operation.
601 */
602
603 EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume)
604 {
605 switch (fsw_status) {
606 case FSW_SUCCESS:
607 return EFI_SUCCESS;
608 case FSW_OUT_OF_MEMORY:
609 return EFI_VOLUME_CORRUPTED;
610 case FSW_IO_ERROR:
611 return Volume->LastIOStatus;
612 case FSW_UNSUPPORTED:
613 return EFI_UNSUPPORTED;
614 case FSW_NOT_FOUND:
615 return EFI_NOT_FOUND;
616 case FSW_VOLUME_CORRUPTED:
617 return EFI_VOLUME_CORRUPTED;
618 default:
619 return EFI_DEVICE_ERROR;
620 }
621 }
622
623 /**
624 * File System EFI protocol, OpenVolume function. Creates a file handle for
625 * the root directory and returns it. Note that this function may be called
626 * multiple times and returns a new file handle each time. Each returned
627 * handle is closed by the client using it.
628 */
629
630 EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
631 OUT EFI_FILE **Root)
632 {
633 EFI_STATUS Status;
634 FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This);
635
636 #if DEBUG_LEVEL
637 Print(L"fsw_efi_FileSystem_OpenVolume\n");
638 #endif
639
640 fsw_efi_clear_cache();
641 Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root);
642
643 return Status;
644 }
645
646 /**
647 * File Handle EFI protocol, Open function. Dispatches the call
648 * based on the kind of file handle.
649 */
650
651 EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This,
652 OUT EFI_FILE **NewHandle,
653 IN CHAR16 *FileName,
654 IN UINT64 OpenMode,
655 IN UINT64 Attributes)
656 {
657 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
658
659 if (File->Type == FSW_EFI_FILE_TYPE_DIR)
660 return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes);
661 // not supported for regular files
662 return EFI_UNSUPPORTED;
663 }
664
665 /**
666 * File Handle EFI protocol, Close function. Closes the FSW shandle
667 * and frees the memory used for the structure.
668 */
669
670 EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This)
671 {
672 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
673
674 #if DEBUG_LEVEL
675 Print(L"fsw_efi_FileHandle_Close\n");
676 #endif
677
678 fsw_shandle_close(&File->shand);
679 FreePool(File);
680
681 return EFI_SUCCESS;
682 }
683
684 /**
685 * File Handle EFI protocol, Delete function. Calls through to Close
686 * and returns a warning because this driver is read-only.
687 */
688
689 EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This)
690 {
691 EFI_STATUS Status;
692
693 Status = refit_call1_wrapper(This->Close, This);
694 if (Status == EFI_SUCCESS) {
695 // this driver is read-only
696 Status = EFI_WARN_DELETE_FAILURE;
697 }
698
699 return Status;
700 }
701
702 /**
703 * File Handle EFI protocol, Read function. Dispatches the call
704 * based on the kind of file handle.
705 */
706
707 EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This,
708 IN OUT UINTN *BufferSize,
709 OUT VOID *Buffer)
710 {
711 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
712
713 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
714 return fsw_efi_file_read(File, BufferSize, Buffer);
715 else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
716 return fsw_efi_dir_read(File, BufferSize, Buffer);
717 return EFI_UNSUPPORTED;
718 }
719
720 /**
721 * File Handle EFI protocol, Write function. Returns unsupported status
722 * because this driver is read-only.
723 */
724
725 EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This,
726 IN OUT UINTN *BufferSize,
727 IN VOID *Buffer)
728 {
729 // this driver is read-only
730 return EFI_WRITE_PROTECTED;
731 }
732
733 /**
734 * File Handle EFI protocol, GetPosition function. Dispatches the call
735 * based on the kind of file handle.
736 */
737
738 EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This,
739 OUT UINT64 *Position)
740 {
741 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
742
743 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
744 return fsw_efi_file_getpos(File, Position);
745 // not defined for directories
746 return EFI_UNSUPPORTED;
747 }
748
749 /**
750 * File Handle EFI protocol, SetPosition function. Dispatches the call
751 * based on the kind of file handle.
752 */
753
754 EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This,
755 IN UINT64 Position)
756 {
757 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
758
759 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
760 return fsw_efi_file_setpos(File, Position);
761 else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
762 return fsw_efi_dir_setpos(File, Position);
763 return EFI_UNSUPPORTED;
764 }
765
766 /**
767 * File Handle EFI protocol, GetInfo function. Dispatches to the common
768 * function implementing this.
769 */
770
771 EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This,
772 IN EFI_GUID *InformationType,
773 IN OUT UINTN *BufferSize,
774 OUT VOID *Buffer)
775 {
776 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
777
778 return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer);
779 }
780
781 /**
782 * File Handle EFI protocol, SetInfo function. Returns unsupported status
783 * because this driver is read-only.
784 */
785
786 EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This,
787 IN EFI_GUID *InformationType,
788 IN UINTN BufferSize,
789 IN VOID *Buffer)
790 {
791 // this driver is read-only
792 return EFI_WRITE_PROTECTED;
793 }
794
795 /**
796 * File Handle EFI protocol, Flush function. Returns unsupported status
797 * because this driver is read-only.
798 */
799
800 EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This)
801 {
802 // this driver is read-only
803 return EFI_WRITE_PROTECTED;
804 }
805
806 /**
807 * Set up a file handle for a dnode. This function allocates a data structure
808 * for a file handle, opens a FSW shandle and populates the EFI_FILE structure
809 * with the interface functions.
810 */
811
812 EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
813 OUT EFI_FILE **NewFileHandle)
814 {
815 EFI_STATUS Status;
816 FSW_FILE_DATA *File;
817
818 // make sure the dnode has complete info
819 Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data);
820 if (EFI_ERROR(Status))
821 return Status;
822
823 // check type
824 if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR)
825 return EFI_UNSUPPORTED;
826
827 // allocate file structure
828 File = AllocateZeroPool(sizeof(FSW_FILE_DATA));
829 File->Signature = FSW_FILE_DATA_SIGNATURE;
830 if (dno->type == FSW_DNODE_TYPE_FILE)
831 File->Type = FSW_EFI_FILE_TYPE_FILE;
832 else if (dno->type == FSW_DNODE_TYPE_DIR)
833 File->Type = FSW_EFI_FILE_TYPE_DIR;
834
835 // open shandle
836 Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand),
837 (FSW_VOLUME_DATA *)dno->vol->host_data);
838 if (EFI_ERROR(Status)) {
839 FreePool(File);
840 return Status;
841 }
842
843 // populate the file handle
844 File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION;
845 File->FileHandle.Open = fsw_efi_FileHandle_Open;
846 File->FileHandle.Close = fsw_efi_FileHandle_Close;
847 File->FileHandle.Delete = fsw_efi_FileHandle_Delete;
848 File->FileHandle.Read = fsw_efi_FileHandle_Read;
849 File->FileHandle.Write = fsw_efi_FileHandle_Write;
850 File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition;
851 File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition;
852 File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo;
853 File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo;
854 File->FileHandle.Flush = fsw_efi_FileHandle_Flush;
855
856 *NewFileHandle = &File->FileHandle;
857 return EFI_SUCCESS;
858 }
859
860 /**
861 * Data read function for regular files. Calls through to fsw_shandle_read.
862 */
863
864 EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
865 IN OUT UINTN *BufferSize,
866 OUT VOID *Buffer)
867 {
868 EFI_STATUS Status;
869 fsw_u32 buffer_size;
870
871 #if DEBUG_LEVEL
872 Print(L"fsw_efi_file_read %d bytes\n", *BufferSize);
873 #endif
874
875 buffer_size = (fsw_u32)*BufferSize;
876 Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer),
877 (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data);
878 *BufferSize = buffer_size;
879
880 return Status;
881 }
882
883 /**
884 * Get file position for regular files.
885 */
886
887 EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
888 OUT UINT64 *Position)
889 {
890 *Position = File->shand.pos;
891 return EFI_SUCCESS;
892 }
893
894 /**
895 * Set file position for regular files. EFI specifies the all-ones value
896 * to be a special value for the end of the file.
897 */
898
899 EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File, IN UINT64 Position)
900 {
901 if (Position == 0xFFFFFFFFFFFFFFFFULL)
902 File->shand.pos = File->shand.dnode->size;
903 else
904 File->shand.pos = Position;
905 return EFI_SUCCESS;
906 }
907
908 /**
909 * Open function used to open new file handles relative to a directory.
910 * In EFI, the "open file" function is implemented by directory file handles
911 * and is passed a relative or volume-absolute path to the file or directory
912 * to open. We use fsw_dnode_lookup_path to find the node plus an additional
913 * call to fsw_dnode_resolve because EFI has no concept of symbolic links.
914 */
915
916 EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
917 OUT EFI_FILE **NewHandle,
918 IN CHAR16 *FileName,
919 IN UINT64 OpenMode,
920 IN UINT64 Attributes)
921 {
922 EFI_STATUS Status;
923 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
924 struct fsw_dnode *dno;
925 struct fsw_dnode *target_dno;
926 struct fsw_string lookup_path;
927
928 #if DEBUG_LEVEL
929 Print(L"fsw_efi_dir_open: '%s'\n", FileName);
930 #endif
931
932 if (OpenMode != EFI_FILE_MODE_READ)
933 return EFI_WRITE_PROTECTED;
934
935 lookup_path.type = FSW_STRING_TYPE_UTF16;
936 lookup_path.len = (int)StrLen(FileName);
937 lookup_path.size = lookup_path.len * sizeof(fsw_u16);
938 lookup_path.data = FileName;
939
940 // resolve the path (symlinks along the way are automatically resolved)
941 Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno), Volume);
942 if (EFI_ERROR(Status))
943 return Status;
944
945 // if the final node is a symlink, also resolve it
946 Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), Volume);
947 fsw_dnode_release(dno);
948 if (EFI_ERROR(Status))
949 return Status;
950 dno = target_dno;
951
952 // make a new EFI handle for the target dnode
953 Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle);
954 fsw_dnode_release(dno);
955 return Status;
956 }
957
958 /**
959 * Read function for directories. A file handle read on a directory retrieves
960 * the next directory entry.
961 */
962
963 EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
964 IN OUT UINTN *BufferSize,
965 OUT VOID *Buffer)
966 {
967 EFI_STATUS Status;
968 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
969 struct fsw_dnode *dno;
970
971 #if DEBUG_LEVEL
972 Print(L"fsw_efi_dir_read...\n");
973 #endif
974
975 // read the next entry
976 Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno), Volume);
977 if (Status == EFI_NOT_FOUND) {
978 // end of directory
979 *BufferSize = 0;
980 #if DEBUG_LEVEL
981 Print(L"...no more entries\n");
982 #endif
983 return EFI_SUCCESS;
984 }
985 if (EFI_ERROR(Status))
986 return Status;
987
988 // get info into buffer
989 Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer);
990 fsw_dnode_release(dno);
991 return Status;
992 }
993
994 /**
995 * Set file position for directories. The only allowed set position operation
996 * for directories is to rewind the directory completely by setting the
997 * position to zero.
998 */
999
1000 EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File, IN UINT64 Position)
1001 {
1002 if (Position == 0) {
1003 File->shand.pos = 0;
1004 return EFI_SUCCESS;
1005 } else {
1006 // directories can only rewind to the start
1007 return EFI_UNSUPPORTED;
1008 }
1009 }
1010
1011 /**
1012 * Get file or volume information. This function implements the GetInfo call
1013 * for all file handles. Control is dispatched according to the type of information
1014 * requested by the caller.
1015 */
1016
1017 EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
1018 IN EFI_GUID *InformationType,
1019 IN OUT UINTN *BufferSize,
1020 OUT VOID *Buffer)
1021 {
1022 EFI_STATUS Status;
1023 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
1024 EFI_FILE_SYSTEM_INFO *FSInfo;
1025 UINTN RequiredSize;
1026 struct fsw_volume_stat vsb;
1027
1028
1029 if (CompareGuid(InformationType, &gEfiFileInfoGuid)) {
1030 #if DEBUG_LEVEL
1031 Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n");
1032 #endif
1033
1034 Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer);
1035
1036 } else if (CompareGuid(InformationType, &gEfiFileSystemInfoGuid)) {
1037 #if DEBUG_LEVEL
1038 Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n");
1039 #endif
1040
1041 // check buffer size
1042 RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label);
1043 if (*BufferSize < RequiredSize) {
1044 *BufferSize = RequiredSize;
1045 return EFI_BUFFER_TOO_SMALL;
1046 }
1047
1048 // fill structure
1049 FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
1050 FSInfo->Size = RequiredSize;
1051 FSInfo->ReadOnly = TRUE;
1052 FSInfo->BlockSize = Volume->vol->log_blocksize;
1053 fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label);
1054
1055 // get the missing info from the fs driver
1056 ZeroMem(&vsb, sizeof(struct fsw_volume_stat));
1057 Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume);
1058 if (EFI_ERROR(Status))
1059 return Status;
1060 FSInfo->VolumeSize = vsb.total_bytes;
1061 FSInfo->FreeSpace = vsb.free_bytes;
1062
1063 // prepare for return
1064 *BufferSize = RequiredSize;
1065 Status = EFI_SUCCESS;
1066
1067 } else if (CompareGuid(InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
1068 #if DEBUG_LEVEL
1069 Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n");
1070 #endif
1071
1072 // check buffer size
1073 RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label);
1074 if (*BufferSize < RequiredSize) {
1075 *BufferSize = RequiredSize;
1076 return EFI_BUFFER_TOO_SMALL;
1077 }
1078
1079 // copy volume label
1080 fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label);
1081
1082 // prepare for return
1083 *BufferSize = RequiredSize;
1084 Status = EFI_SUCCESS;
1085
1086 } else {
1087 Status = EFI_UNSUPPORTED;
1088 }
1089
1090 return Status;
1091 }
1092
1093 /**
1094 * Time mapping callback for the fsw_dnode_stat call. This function converts
1095 * a Posix style timestamp into an EFI_TIME structure and writes it to the
1096 * appropriate member of the EFI_FILE_INFO structure that we're filling.
1097 */
1098
1099 void fsw_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time)
1100 {
1101 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1102
1103 if (which == FSW_DNODE_STAT_CTIME)
1104 fsw_efi_decode_time(&FileInfo->CreateTime, posix_time);
1105 else if (which == FSW_DNODE_STAT_MTIME)
1106 fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time);
1107 else if (which == FSW_DNODE_STAT_ATIME)
1108 fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time);
1109 }
1110
1111 /**
1112 * Mode mapping callback for the fsw_dnode_stat call. This function looks at
1113 * the Posix mode passed by the file system driver and makes appropriate
1114 * adjustments to the EFI_FILE_INFO structure that we're filling.
1115 */
1116
1117 void fsw_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode)
1118 {
1119 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1120
1121 if ((posix_mode & S_IWUSR) == 0)
1122 FileInfo->Attribute |= EFI_FILE_READ_ONLY;
1123 }
1124
1125 void fsw_store_attr_efi(struct fsw_dnode_stat *sb, fsw_u16 attr)
1126 {
1127 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1128
1129 FileInfo->Attribute |= attr;
1130 }
1131
1132 /**
1133 * Common function to fill an EFI_FILE_INFO with information about a dnode.
1134 */
1135
1136 EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
1137 IN struct fsw_dnode *dno,
1138 IN OUT UINTN *BufferSize,
1139 OUT VOID *Buffer)
1140 {
1141 EFI_STATUS Status;
1142 EFI_FILE_INFO *FileInfo;
1143 UINTN RequiredSize;
1144 struct fsw_dnode_stat sb;
1145
1146 // make sure the dnode has complete info
1147 Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
1148 if (EFI_ERROR(Status))
1149 return Status;
1150
1151 // TODO: check/assert that the dno's name is in UTF16
1152
1153 // check buffer size
1154 RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name);
1155 if (*BufferSize < RequiredSize) {
1156 // TODO: wind back the directory in this case
1157
1158 #if DEBUG_LEVEL
1159 Print(L"...BUFFER TOO SMALL\n");
1160 #endif
1161 *BufferSize = RequiredSize;
1162 return EFI_BUFFER_TOO_SMALL;
1163 }
1164
1165 // fill structure
1166 ZeroMem(Buffer, RequiredSize);
1167 FileInfo = (EFI_FILE_INFO *)Buffer;
1168 FileInfo->Size = RequiredSize;
1169 FileInfo->FileSize = dno->size;
1170 FileInfo->Attribute = 0;
1171 if (dno->type == FSW_DNODE_TYPE_DIR)
1172 FileInfo->Attribute |= EFI_FILE_DIRECTORY;
1173 fsw_efi_strcpy(FileInfo->FileName, &dno->name);
1174
1175 // get the missing info from the fs driver
1176 ZeroMem(&sb, sizeof(struct fsw_dnode_stat));
1177 sb.host_data = FileInfo;
1178 Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume);
1179 if (EFI_ERROR(Status))
1180 return Status;
1181 FileInfo->PhysicalSize = sb.used_bytes;
1182
1183 // prepare for return
1184 *BufferSize = RequiredSize;
1185 #if DEBUG_LEVEL
1186 Print(L"...returning '%s'\n", FileInfo->FileName);
1187 #endif
1188 return EFI_SUCCESS;
1189 }
1190
1191 // EOF