]> git.proxmox.com Git - mirror_edk2.git/blob - UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
SignedCapsulePkg: Fix various typos
[mirror_edk2.git] / UnitTestFrameworkPkg / Library / UnitTestPersistenceLibSimpleFileSystem / UnitTestPersistenceLibSimpleFileSystem.c
1 /** @file
2 This is an instance of the Unit Test Persistence Lib that will utilize
3 the filesystem that a test application is running from to save a serialized
4 version of the internal test state in case the test needs to quit and restore.
5
6 Copyright (c) Microsoft Corporation.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 **/
9
10 #include <PiDxe.h>
11 #include <Library/UnitTestPersistenceLib.h>
12 #include <Library/BaseLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/UefiBootServicesTableLib.h>
16 #include <Library/DevicePathLib.h>
17 #include <Library/ShellLib.h>
18 #include <Protocol/LoadedImage.h>
19
20 #define CACHE_FILE_SUFFIX L"_Cache.dat"
21
22 /**
23 Generate the device path to the cache file.
24
25 @param[in] FrameworkHandle A pointer to the framework that is being persisted.
26
27 @retval !NULL A pointer to the EFI_FILE protocol instance for the filesystem.
28 @retval NULL Filesystem could not be found or an error occurred.
29
30 **/
31 STATIC
32 EFI_DEVICE_PATH_PROTOCOL*
33 GetCacheFileDevicePath (
34 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
35 )
36 {
37 EFI_STATUS Status;
38 UNIT_TEST_FRAMEWORK *Framework;
39 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
40 CHAR16 *AppPath;
41 CHAR16 *CacheFilePath;
42 CHAR16 *TestName;
43 UINTN DirectorySlashOffset;
44 UINTN CacheFilePathLength;
45 EFI_DEVICE_PATH_PROTOCOL *CacheFileDevicePath;
46
47 Framework = (UNIT_TEST_FRAMEWORK*)FrameworkHandle;
48 AppPath = NULL;
49 CacheFilePath = NULL;
50 TestName = NULL;
51 CacheFileDevicePath = NULL;
52
53 //
54 // First, we need to get some information from the loaded image.
55 //
56 Status = gBS->HandleProtocol (
57 gImageHandle,
58 &gEfiLoadedImageProtocolGuid,
59 (VOID**)&LoadedImage
60 );
61 if (EFI_ERROR (Status)) {
62 DEBUG ((DEBUG_WARN, "%a - Failed to locate DevicePath for loaded image. %r\n", __FUNCTION__, Status));
63 return NULL;
64 }
65
66 //
67 // Before we can start, change test name from ASCII to Unicode.
68 //
69 CacheFilePathLength = AsciiStrLen (Framework->ShortTitle) + 1;
70 TestName = AllocatePool (CacheFilePathLength);
71 if (!TestName) {
72 goto Exit;
73 }
74 AsciiStrToUnicodeStrS (Framework->ShortTitle, TestName, CacheFilePathLength);
75
76 //
77 // Now we should have the device path of the root device and a file path for the rest.
78 // In order to target the directory for the test application, we must process
79 // the file path a little.
80 //
81 // NOTE: This may not be necessary... Path processing functions exist...
82 // PathCleanUpDirectories (FileNameCopy);
83 // if (PathRemoveLastItem (FileNameCopy)) {
84 //
85 AppPath = ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); // NOTE: This must be freed.
86 DirectorySlashOffset = StrLen (AppPath);
87 //
88 // Make sure we didn't get any weird data.
89 //
90 if (DirectorySlashOffset == 0) {
91 DEBUG ((DEBUG_ERROR, "%a - Weird 0-length string when processing app path.\n", __FUNCTION__));
92 goto Exit;
93 }
94
95 //
96 // Now that we know we have a decent string, let's take a deeper look.
97 //
98 do {
99 if (AppPath[DirectorySlashOffset] == L'\\') {
100 break;
101 }
102 DirectorySlashOffset--;
103 } while (DirectorySlashOffset > 0);
104
105 //
106 // After that little maneuver, DirectorySlashOffset should be pointing at the last '\' in AppString.
107 // That would be the path to the parent directory that the test app is executing from.
108 // Let's check and make sure that's right.
109 //
110 if (AppPath[DirectorySlashOffset] != L'\\') {
111 DEBUG ((DEBUG_ERROR, "%a - Could not find a single directory separator in app path.\n", __FUNCTION__));
112 goto Exit;
113 }
114
115 //
116 // Now we know some things, we're ready to produce our output string, I think.
117 //
118 CacheFilePathLength = DirectorySlashOffset + 1;
119 CacheFilePathLength += StrLen (TestName);
120 CacheFilePathLength += StrLen (CACHE_FILE_SUFFIX);
121 CacheFilePathLength += 1; // Don't forget the NULL terminator.
122 CacheFilePath = AllocateZeroPool (CacheFilePathLength * sizeof (CHAR16));
123 if (!CacheFilePath) {
124 goto Exit;
125 }
126
127 //
128 // Let's produce our final path string, shall we?
129 //
130 StrnCpyS (CacheFilePath, CacheFilePathLength, AppPath, DirectorySlashOffset + 1); // Copy the path for the parent directory.
131 StrCatS (CacheFilePath, CacheFilePathLength, TestName); // Copy the base name for the test cache.
132 StrCatS (CacheFilePath, CacheFilePathLength, CACHE_FILE_SUFFIX); // Copy the file suffix.
133
134 //
135 // Finally, try to create the device path for the thing thing.
136 //
137 CacheFileDevicePath = FileDevicePath (LoadedImage->DeviceHandle, CacheFilePath);
138
139 Exit:
140 //
141 // Free allocated buffers.
142 //
143 if (AppPath != NULL) {
144 FreePool (AppPath);
145 }
146 if (CacheFilePath != NULL) {
147 FreePool (CacheFilePath);
148 }
149 if (TestName != NULL) {
150 FreePool (TestName);
151 }
152
153 return CacheFileDevicePath;
154 }
155
156 /**
157 Determines whether a persistence cache already exists for
158 the given framework.
159
160 @param[in] FrameworkHandle A pointer to the framework that is being persisted.
161
162 @retval TRUE
163 @retval FALSE Cache doesn't exist or an error occurred.
164
165 **/
166 BOOLEAN
167 EFIAPI
168 DoesCacheExist (
169 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
170 )
171 {
172 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
173 EFI_STATUS Status;
174 SHELL_FILE_HANDLE FileHandle;
175
176 //
177 // NOTE: This devpath is allocated and must be freed.
178 //
179 FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
180
181 //
182 // Check to see whether the file exists. If the file can be opened for
183 // reading, it exists. Otherwise, probably not.
184 //
185 Status = ShellOpenFileByDevicePath (
186 &FileDevicePath,
187 &FileHandle,
188 EFI_FILE_MODE_READ,
189 0
190 );
191 if (!EFI_ERROR (Status)) {
192 ShellCloseFile (&FileHandle);
193 }
194
195 if (FileDevicePath != NULL) {
196 FreePool (FileDevicePath);
197 }
198
199 DEBUG ((DEBUG_VERBOSE, "%a - Returning %d\n", __FUNCTION__, !EFI_ERROR (Status)));
200
201 return (!EFI_ERROR (Status));
202 }
203
204 /**
205 Will save the data associated with an internal Unit Test Framework
206 state in a manner that can persist a Unit Test Application quit or
207 even a system reboot.
208
209 @param[in] FrameworkHandle A pointer to the framework that is being persisted.
210 @param[in] SaveData A pointer to the buffer containing the serialized
211 framework internal state.
212
213 @retval EFI_SUCCESS Data is persisted and the test can be safely quit.
214 @retval Others Data is not persisted and test cannot be resumed upon exit.
215
216 **/
217 EFI_STATUS
218 EFIAPI
219 SaveUnitTestCache (
220 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
221 IN UNIT_TEST_SAVE_HEADER *SaveData
222 )
223 {
224 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
225 EFI_STATUS Status;
226 SHELL_FILE_HANDLE FileHandle;
227 UINTN WriteCount;
228
229 //
230 // Check the inputs for sanity.
231 //
232 if (FrameworkHandle == NULL || SaveData == NULL) {
233 return EFI_INVALID_PARAMETER;
234 }
235
236 //
237 // Determine the path for the cache file.
238 // NOTE: This devpath is allocated and must be freed.
239 //
240 FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
241
242 //
243 //First lets open the file if it exists so we can delete it...This is the work around for truncation
244 //
245 Status = ShellOpenFileByDevicePath (
246 &FileDevicePath,
247 &FileHandle,
248 (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE),
249 0
250 );
251
252 if (!EFI_ERROR (Status)) {
253 //
254 // If file handle above was opened it will be closed by the delete.
255 //
256 Status = ShellDeleteFile (&FileHandle);
257 if (EFI_ERROR (Status)) {
258 DEBUG ((DEBUG_ERROR, "%a failed to delete file %r\n", __FUNCTION__, Status));
259 }
260 }
261
262 //
263 // Now that we know the path to the file... let's open it for writing.
264 //
265 Status = ShellOpenFileByDevicePath (
266 &FileDevicePath,
267 &FileHandle,
268 (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE),
269 0
270 );
271 if (EFI_ERROR (Status)) {
272 DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __FUNCTION__, Status));
273 goto Exit;
274 }
275
276 //
277 // Write the data to the file.
278 //
279 WriteCount = SaveData->SaveStateSize;
280 DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __FUNCTION__, WriteCount));
281 Status = ShellWriteFile (
282 FileHandle,
283 &WriteCount,
284 SaveData
285 );
286
287 if (EFI_ERROR (Status) || WriteCount != SaveData->SaveStateSize) {
288 DEBUG ((DEBUG_ERROR, "%a - Writing to file failed! %r\n", __FUNCTION__, Status));
289 } else {
290 DEBUG ((DEBUG_INFO, "%a - SUCCESS!\n", __FUNCTION__));
291 }
292
293 //
294 // No matter what, we should probably close the file.
295 //
296 ShellCloseFile (&FileHandle);
297
298 Exit:
299 if (FileDevicePath != NULL) {
300 FreePool (FileDevicePath);
301 }
302
303 return Status;
304 }
305
306 /**
307 Will retrieve any cached state associated with the given framework.
308 Will allocate a buffer to hold the loaded data.
309
310 @param[in] FrameworkHandle A pointer to the framework that is being persisted.
311 @param[in] SaveData A pointer pointer that will be updated with the address
312 of the loaded data buffer.
313
314 @retval EFI_SUCCESS Data has been loaded successfully and SaveData is updated
315 with a pointer to the buffer.
316 @retval Others An error has occurred and no data has been loaded. SaveData
317 is set to NULL.
318
319 **/
320 EFI_STATUS
321 EFIAPI
322 LoadUnitTestCache (
323 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
324 OUT UNIT_TEST_SAVE_HEADER **SaveData
325 )
326 {
327 EFI_STATUS Status;
328 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
329 SHELL_FILE_HANDLE FileHandle;
330 BOOLEAN IsFileOpened;
331 UINT64 LargeFileSize;
332 UINTN FileSize;
333 UNIT_TEST_SAVE_HEADER *Buffer;
334
335 IsFileOpened = FALSE;
336 Buffer = NULL;
337
338 //
339 // Check the inputs for sanity.
340 //
341 if (FrameworkHandle == NULL || SaveData == NULL) {
342 return EFI_INVALID_PARAMETER;
343 }
344
345 //
346 // Determine the path for the cache file.
347 // NOTE: This devpath is allocated and must be freed.
348 //
349 FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
350
351 //
352 // Now that we know the path to the file... let's open it for writing.
353 //
354 Status = ShellOpenFileByDevicePath (
355 &FileDevicePath,
356 &FileHandle,
357 EFI_FILE_MODE_READ,
358 0
359 );
360 if (EFI_ERROR (Status)) {
361 DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __FUNCTION__, Status));
362 goto Exit;
363 } else {
364 IsFileOpened = TRUE;
365 }
366
367 //
368 // Now that the file is opened, we need to determine how large a buffer we need.
369 //
370 Status = ShellGetFileSize (FileHandle, &LargeFileSize);
371 if (EFI_ERROR (Status)) {
372 DEBUG ((DEBUG_ERROR, "%a - Failed to determine file size! %r\n", __FUNCTION__, Status));
373 goto Exit;
374 }
375
376 //
377 // Now that we know the size, let's allocated a buffer to hold the contents.
378 //
379 FileSize = (UINTN)LargeFileSize; // You know what... if it's too large, this lib don't care.
380 Buffer = AllocatePool (FileSize);
381 if (Buffer == NULL) {
382 DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file contents! %r\n", __FUNCTION__, Status));
383 Status = EFI_OUT_OF_RESOURCES;
384 goto Exit;
385 }
386
387 //
388 // Finally, let's read the data.
389 //
390 Status = ShellReadFile (FileHandle, &FileSize, Buffer);
391 if (EFI_ERROR (Status)) {
392 DEBUG ((DEBUG_ERROR, "%a - Failed to read the file contents! %r\n", __FUNCTION__, Status));
393 }
394
395 Exit:
396 //
397 // Free allocated buffers
398 //
399 if (FileDevicePath != NULL) {
400 FreePool (FileDevicePath);
401 }
402 if (IsFileOpened) {
403 ShellCloseFile (&FileHandle);
404 }
405
406 //
407 // If we're returning an error, make sure
408 // the state is sane.
409 if (EFI_ERROR (Status) && Buffer != NULL) {
410 FreePool (Buffer);
411 Buffer = NULL;
412 }
413
414 *SaveData = Buffer;
415 return Status;
416 }