TianoCore EDK2 master
Loading...
Searching...
No Matches
UnitTestPersistenceLibSimpleFileSystem.c
Go to the documentation of this file.
1
11#include <PiDxe.h>
13#include <Library/BaseLib.h>
14#include <Library/DebugLib.h>
18#include <Library/ShellLib.h>
19#include <Library/UefiLib.h>
22
23#define CACHE_FILE_SUFFIX L"_Cache.dat"
24
25CHAR16 *mCachePath = NULL;
26
37CHAR16 *
39 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
40 )
41{
42 EFI_STATUS Status;
43 UNIT_TEST_FRAMEWORK *Framework;
44 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
45 CHAR16 *AppPath;
46 CHAR16 *CacheFilePath;
47 CHAR16 *TestName;
48 UINTN DirectorySlashOffset;
49 UINTN CacheFilePathLength;
50
51 Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
52 AppPath = NULL;
53 CacheFilePath = NULL;
54 TestName = NULL;
55
56 //
57 // First, we need to get some information from the loaded image.
58 //
59 Status = gBS->HandleProtocol (
61 &gEfiLoadedImageProtocolGuid,
62 (VOID **)&LoadedImage
63 );
64 if (EFI_ERROR (Status)) {
65 DEBUG ((DEBUG_WARN, "%a - Failed to locate DevicePath for loaded image. %r\n", __func__, Status));
66 return NULL;
67 }
68
69 //
70 // Before we can start, change test name from ASCII to Unicode.
71 //
72 CacheFilePathLength = AsciiStrLen (Framework->ShortTitle) + 1;
73 TestName = AllocatePool (CacheFilePathLength * sizeof (CHAR16));
74 if (!TestName) {
75 goto Exit;
76 }
77
78 AsciiStrToUnicodeStrS (Framework->ShortTitle, TestName, CacheFilePathLength);
79
80 //
81 // Now we should have the device path of the root device and a file path for the rest.
82 // In order to target the directory for the test application, we must process
83 // the file path a little.
84 //
85 // NOTE: This may not be necessary... Path processing functions exist...
86 // PathCleanUpDirectories (FileNameCopy);
87 // if (PathRemoveLastItem (FileNameCopy)) {
88 //
89 if (mCachePath == NULL) {
90 AppPath = ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); // NOTE: This must be freed.
91 if (AppPath == NULL) {
92 goto Exit;
93 }
94
95 DirectorySlashOffset = StrLen (AppPath);
96 //
97 // Make sure we didn't get any weird data.
98 //
99 if (DirectorySlashOffset == 0) {
100 DEBUG ((DEBUG_ERROR, "%a - Weird 0-length string when processing app path.\n", __func__));
101 goto Exit;
102 }
103
104 //
105 // Now that we know we have a decent string, let's take a deeper look.
106 //
107 do {
108 if (AppPath[DirectorySlashOffset] == L'\\') {
109 break;
110 }
111
112 DirectorySlashOffset--;
113 } while (DirectorySlashOffset > 0);
114
115 //
116 // After that little maneuver, DirectorySlashOffset should be pointing at the last '\' in AppString.
117 // That would be the path to the parent directory that the test app is executing from.
118 // Let's check and make sure that's right.
119 //
120 if (AppPath[DirectorySlashOffset] != L'\\') {
121 DEBUG ((DEBUG_ERROR, "%a - Could not find a single directory separator in app path.\n", __func__));
122 goto Exit;
123 }
124 } else {
125 AppPath = FullyQualifyPath (mCachePath); // NOTE: This must be freed.
126 if (AppPath == NULL) {
127 goto Exit;
128 }
129
130 DirectorySlashOffset = StrLen (AppPath);
131
132 if (AppPath[DirectorySlashOffset - 1] != L'\\') {
133 // Set the slash if user did not specify it on the newly allocated pool
134 AppPath = ReallocatePool (
135 (DirectorySlashOffset + 1) * sizeof (CHAR16),
136 (DirectorySlashOffset + 2) * sizeof (CHAR16),
137 AppPath
138 );
139 AppPath[DirectorySlashOffset] = L'\\';
140 AppPath[DirectorySlashOffset + 1] = L'\0';
141 } else {
142 // Otherwise the user input is good enough to go, mostly
143 DirectorySlashOffset--;
144 }
145 }
146
147 //
148 // Now we know some things, we're ready to produce our output string, I think.
149 //
150 CacheFilePathLength = DirectorySlashOffset + 1;
151 CacheFilePathLength += StrLen (TestName);
152 CacheFilePathLength += StrLen (CACHE_FILE_SUFFIX);
153 CacheFilePathLength += 1; // Don't forget the NULL terminator.
154 CacheFilePath = AllocateZeroPool (CacheFilePathLength * sizeof (CHAR16));
155 if (!CacheFilePath) {
156 goto Exit;
157 }
158
159 //
160 // Let's produce our final path string, shall we?
161 //
162 StrnCpyS (CacheFilePath, CacheFilePathLength, AppPath, DirectorySlashOffset + 1); // Copy the path for the parent directory.
163 StrCatS (CacheFilePath, CacheFilePathLength, TestName); // Copy the base name for the test cache.
164 StrCatS (CacheFilePath, CacheFilePathLength, CACHE_FILE_SUFFIX); // Copy the file suffix.
165
166Exit:
167 //
168 // Free allocated buffers.
169 //
170 if (AppPath != NULL) {
171 FreePool (AppPath);
172 }
173
174 if (TestName != NULL) {
175 FreePool (TestName);
176 }
177
178 return CacheFilePath;
179}
180
191BOOLEAN
192EFIAPI
194 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
195 )
196{
197 CHAR16 *FileName;
198 EFI_STATUS Status;
199 SHELL_FILE_HANDLE FileHandle;
200
201 //
202 // NOTE: This devpath is allocated and must be freed.
203 //
204 FileName = GetCacheFileName (FrameworkHandle);
205 if (FileName == NULL) {
206 return FALSE;
207 }
208
209 //
210 // Check to see whether the file exists. If the file can be opened for
211 // reading, it exists. Otherwise, probably not.
212 //
213 Status = ShellOpenFileByName (
214 FileName,
215 &FileHandle,
216 EFI_FILE_MODE_READ,
217 0
218 );
219 if (!EFI_ERROR (Status)) {
220 ShellCloseFile (&FileHandle);
221 }
222
223 if (FileName != NULL) {
224 FreePool (FileName);
225 }
226
227 DEBUG ((DEBUG_VERBOSE, "%a - Returning %d\n", __func__, !EFI_ERROR (Status)));
228
229 return (!EFI_ERROR (Status));
230}
231
247EFIAPI
249 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
250 IN VOID *SaveData,
251 IN UINTN SaveStateSize
252 )
253{
254 CHAR16 *FileName;
255 EFI_STATUS Status;
256 SHELL_FILE_HANDLE FileHandle;
257 UINTN WriteCount;
258
259 //
260 // Check the inputs for sanity.
261 //
262 if ((FrameworkHandle == NULL) || (SaveData == NULL)) {
263 return EFI_INVALID_PARAMETER;
264 }
265
266 //
267 // Determine the path for the cache file.
268 // NOTE: This devpath is allocated and must be freed.
269 //
270 FileName = GetCacheFileName (FrameworkHandle);
271 if (FileName == NULL) {
272 return EFI_INVALID_PARAMETER;
273 }
274
275 //
276 // First lets open the file if it exists so we can delete it...This is the work around for truncation
277 //
278 Status = ShellOpenFileByName (
279 FileName,
280 &FileHandle,
281 (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE),
282 0
283 );
284
285 if (!EFI_ERROR (Status)) {
286 //
287 // If file handle above was opened it will be closed by the delete.
288 //
289 Status = ShellDeleteFile (&FileHandle);
290 if (EFI_ERROR (Status)) {
291 DEBUG ((DEBUG_ERROR, "%a failed to delete file %r\n", __func__, Status));
292 }
293 }
294
295 //
296 // Now that we know the path to the file... let's open it for writing.
297 //
298 Status = ShellOpenFileByName (
299 FileName,
300 &FileHandle,
301 (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE),
302 0
303 );
304 if (EFI_ERROR (Status)) {
305 DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __func__, Status));
306 goto Exit;
307 }
308
309 //
310 // Write the data to the file.
311 //
312 WriteCount = SaveStateSize;
313 DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __func__, WriteCount));
314 Status = ShellWriteFile (
315 FileHandle,
316 &WriteCount,
317 SaveData
318 );
319
320 if (EFI_ERROR (Status) || (WriteCount != SaveStateSize)) {
321 DEBUG ((DEBUG_ERROR, "%a - Writing to file failed! %r\n", __func__, Status));
322 } else {
323 DEBUG ((DEBUG_INFO, "%a - SUCCESS!\n", __func__));
324 }
325
326 //
327 // No matter what, we should probably close the file.
328 //
329 ShellCloseFile (&FileHandle);
330
331Exit:
332 if (FileName != NULL) {
333 FreePool (FileName);
334 }
335
336 return Status;
337}
338
355EFIAPI
357 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
358 OUT VOID **SaveData,
359 OUT UINTN *SaveStateSize
360 )
361{
362 EFI_STATUS Status;
363 CHAR16 *FileName;
364 SHELL_FILE_HANDLE FileHandle;
365 BOOLEAN IsFileOpened;
366 UINT64 LargeFileSize;
367 UINTN FileSize;
368 VOID *Buffer;
369
370 IsFileOpened = FALSE;
371 Buffer = NULL;
372
373 //
374 // Check the inputs for sanity.
375 //
376 if ((FrameworkHandle == NULL) || (SaveData == NULL)) {
377 return EFI_INVALID_PARAMETER;
378 }
379
380 //
381 // Determine the path for the cache file.
382 // NOTE: This devpath is allocated and must be freed.
383 //
384 FileName = GetCacheFileName (FrameworkHandle);
385 if (FileName == NULL) {
386 return EFI_INVALID_PARAMETER;
387 }
388
389 //
390 // Now that we know the path to the file... let's open it for writing.
391 //
392 Status = ShellOpenFileByName (
393 FileName,
394 &FileHandle,
395 EFI_FILE_MODE_READ,
396 0
397 );
398 if (EFI_ERROR (Status)) {
399 DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __func__, Status));
400 goto Exit;
401 } else {
402 IsFileOpened = TRUE;
403 }
404
405 //
406 // Now that the file is opened, we need to determine how large a buffer we need.
407 //
408 Status = ShellGetFileSize (FileHandle, &LargeFileSize);
409 if (EFI_ERROR (Status)) {
410 DEBUG ((DEBUG_ERROR, "%a - Failed to determine file size! %r\n", __func__, Status));
411 goto Exit;
412 }
413
414 //
415 // Now that we know the size, let's allocated a buffer to hold the contents.
416 //
417 FileSize = (UINTN)LargeFileSize; // You know what... if it's too large, this lib don't care.
418 *SaveStateSize = FileSize;
419 Buffer = AllocatePool (FileSize);
420 if (Buffer == NULL) {
421 DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file contents! %r\n", __func__, Status));
422 Status = EFI_OUT_OF_RESOURCES;
423 goto Exit;
424 }
425
426 //
427 // Finally, let's read the data.
428 //
429 Status = ShellReadFile (FileHandle, &FileSize, Buffer);
430 if (EFI_ERROR (Status)) {
431 DEBUG ((DEBUG_ERROR, "%a - Failed to read the file contents! %r\n", __func__, Status));
432 }
433
434Exit:
435 //
436 // Free allocated buffers
437 //
438 if (FileName != NULL) {
439 FreePool (FileName);
440 }
441
442 if (IsFileOpened) {
443 ShellCloseFile (&FileHandle);
444 }
445
446 //
447 // If we're returning an error, make sure
448 // the state is sane.
449 if (EFI_ERROR (Status) && (Buffer != NULL)) {
450 FreePool (Buffer);
451 Buffer = NULL;
452 }
453
454 *SaveData = Buffer;
455 return Status;
456}
457
468EFIAPI
470 IN EFI_HANDLE ImageHandle,
471 IN EFI_SYSTEM_TABLE *SystemTable
472 )
473{
474 UINTN Index;
475 UINTN Argc;
476 CHAR16 **Argv;
477 EFI_STATUS Status;
478 EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters;
479
480 Status = gBS->HandleProtocol (
482 &gEfiShellParametersProtocolGuid,
483 (VOID **)&ShellParameters
484 );
485 if (EFI_ERROR (Status)) {
486 ASSERT_EFI_ERROR (Status);
487 goto Done;
488 }
489
490 Argc = ShellParameters->Argc;
491 Argv = ShellParameters->Argv;
492
493 Status = EFI_SUCCESS;
494 if ((Argc > 1) && (Argv != NULL)) {
495 // This might be our cue, check for whether we need to do anything
496 for (Index = 1; Index < Argc; Index++) {
497 if (StrCmp (Argv[Index], L"--CachePath") == 0) {
498 // Need to update the potential cache path to designated path
499 if (Index < Argc - 1) {
500 mCachePath = Argv[Index + 1];
501 } else {
502 Print (L" --CachePath <Path of where to save unit test cache files, i.e. FS0:TestFolder>\n");
503 }
504 }
505 }
506 }
507
508Done:
509 return Status;
510}
UINT64 UINTN
INTN EFIAPI StrCmp(IN CONST CHAR16 *FirstString, IN CONST CHAR16 *SecondString)
Definition: String.c:109
UINTN EFIAPI AsciiStrLen(IN CONST CHAR8 *String)
Definition: String.c:641
RETURN_STATUS EFIAPI StrCatS(IN OUT CHAR16 *Destination, IN UINTN DestMax, IN CONST CHAR16 *Source)
Definition: SafeString.c:405
RETURN_STATUS EFIAPI AsciiStrToUnicodeStrS(IN CONST CHAR8 *Source, OUT CHAR16 *Destination, IN UINTN DestMax)
Definition: SafeString.c:2873
RETURN_STATUS EFIAPI StrnCpyS(OUT CHAR16 *Destination, IN UINTN DestMax, IN CONST CHAR16 *Source, IN UINTN Length)
Definition: SafeString.c:310
UINTN EFIAPI StrLen(IN CONST CHAR16 *String)
Definition: String.c:30
CHAR16 *EFIAPI ConvertDevicePathToText(IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN BOOLEAN DisplayOnly, IN BOOLEAN AllowShortcuts)
VOID *EFIAPI ReallocatePool(IN UINTN OldSize, IN UINTN NewSize, IN VOID *OldBuffer OPTIONAL)
VOID *EFIAPI AllocateZeroPool(IN UINTN AllocationSize)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
EFI_STATUS EFIAPI ShellGetFileSize(IN SHELL_FILE_HANDLE FileHandle, OUT UINT64 *Size)
CHAR16 *EFIAPI FullyQualifyPath(IN CONST CHAR16 *Path)
Definition: UefiShellLib.c:64
EFI_STATUS EFIAPI ShellDeleteFile(IN SHELL_FILE_HANDLE *FileHandle)
Definition: UefiShellLib.c:992
EFI_STATUS EFIAPI ShellOpenFileByName(IN CONST CHAR16 *FileName, OUT SHELL_FILE_HANDLE *FileHandle, IN UINT64 OpenMode, IN UINT64 Attributes)
Definition: UefiShellLib.c:720
EFI_STATUS EFIAPI ShellWriteFile(IN SHELL_FILE_HANDLE FileHandle, IN OUT UINTN *BufferSize, IN VOID *Buffer)
Definition: UefiShellLib.c:947
EFI_STATUS EFIAPI ShellCloseFile(IN SHELL_FILE_HANDLE *FileHandle)
Definition: UefiShellLib.c:969
EFI_STATUS EFIAPI ShellReadFile(IN SHELL_FILE_HANDLE FileHandle, IN OUT UINTN *ReadSize, OUT VOID *Buffer)
Definition: UefiShellLib.c:912
VOID EFIAPI Exit(IN EFI_STATUS Status)
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_HANDLE gImageHandle
EFI_BOOT_SERVICES * gBS
UINTN EFIAPI Print(IN CONST CHAR16 *Format,...)
Definition: UefiLibPrint.c:113
STATIC CHAR16 * GetCacheFileName(IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle)
EFI_STATUS EFIAPI UnitTestPersistenceLibConstructor(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
EFI_STATUS EFIAPI SaveUnitTestCache(IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle, IN VOID *SaveData, IN UINTN SaveStateSize)
EFI_STATUS EFIAPI LoadUnitTestCache(IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle, OUT VOID **SaveData, OUT UINTN *SaveStateSize)
BOOLEAN EFIAPI DoesCacheExist(IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle)
EFI_DEVICE_PATH_PROTOCOL * FilePath
Definition: LoadedImage.h:54