UnitTestPersistenceLibSimpleFileSystem.c 13 KB

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