1 /** @file
2   File System Access for NvVarsFileLib
3 
4   Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "NvVarsFileLib.h"
10 
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/DebugLib.h>
13 #include <Library/MemoryAllocationLib.h>
14 
15 
16 /**
17   Open the NvVars file for reading or writing
18 
19   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
20   @param[in]  ReadingFile - TRUE: open the file for reading.  FALSE: writing
21   @param[out] NvVarsFile - If EFI_SUCCESS is returned, then this is updated
22                            with the opened NvVars file.
23 
24   @return     EFI_SUCCESS if the file was opened
25 
26 **/
27 EFI_STATUS
GetNvVarsFile(IN EFI_HANDLE FsHandle,IN BOOLEAN ReadingFile,OUT EFI_FILE_HANDLE * NvVarsFile)28 GetNvVarsFile (
29   IN  EFI_HANDLE            FsHandle,
30   IN  BOOLEAN               ReadingFile,
31   OUT EFI_FILE_HANDLE       *NvVarsFile
32   )
33 {
34   EFI_STATUS                            Status;
35   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL       *Fs;
36   EFI_FILE_HANDLE                       Root;
37 
38   //
39   // Get the FileSystem protocol on that handle
40   //
41   Status = gBS->HandleProtocol (
42                   FsHandle,
43                   &gEfiSimpleFileSystemProtocolGuid,
44                   (VOID **)&Fs
45                   );
46   if (EFI_ERROR (Status)) {
47     return Status;
48   }
49 
50   //
51   // Get the volume (the root directory)
52   //
53   Status = Fs->OpenVolume (Fs, &Root);
54   if (EFI_ERROR (Status)) {
55     return Status;
56   }
57 
58   //
59   // Attempt to open the NvVars file in the root directory
60   //
61   Status = Root->Open (
62                    Root,
63                    NvVarsFile,
64                    L"NvVars",
65                    ReadingFile ?
66                      EFI_FILE_MODE_READ :
67                      (
68                        EFI_FILE_MODE_CREATE |
69                        EFI_FILE_MODE_READ |
70                        EFI_FILE_MODE_WRITE
71                      ),
72                    0
73                    );
74 
75   return Status;
76 }
77 
78 
79 /**
80   Open the NvVars file for reading or writing
81 
82   @param[in]  File - The file to inspect
83   @param[out] Exists - Returns whether the file exists
84   @param[out] Size - Returns the size of the file
85                      (0 if the file does not exist)
86 
87 **/
88 VOID
NvVarsFileReadCheckup(IN EFI_FILE_HANDLE File,OUT BOOLEAN * Exists,OUT UINTN * Size)89 NvVarsFileReadCheckup (
90   IN  EFI_FILE_HANDLE        File,
91   OUT BOOLEAN                *Exists,
92   OUT UINTN                  *Size
93   )
94 {
95   EFI_FILE_INFO               *FileInfo;
96 
97   *Exists = FALSE;
98   *Size = 0;
99 
100   FileInfo = FileHandleGetInfo (File);
101   if (FileInfo == NULL) {
102     return;
103   }
104 
105   if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
106     FreePool (FileInfo);
107     return;
108   }
109 
110   *Exists = TRUE;
111   *Size = (UINTN) FileInfo->FileSize;
112 
113   FreePool (FileInfo);
114 }
115 
116 
117 /**
118   Open the NvVars file for reading or writing
119 
120   @param[in]  File - The file to inspect
121   @param[out] Exists - Returns whether the file exists
122   @param[out] Size - Returns the size of the file
123                      (0 if the file does not exist)
124 
125 **/
126 EFI_STATUS
FileHandleEmpty(IN EFI_FILE_HANDLE File)127 FileHandleEmpty (
128   IN  EFI_FILE_HANDLE        File
129   )
130 {
131   EFI_STATUS                  Status;
132   EFI_FILE_INFO               *FileInfo;
133 
134   //
135   // Retrieve the FileInfo structure
136   //
137   FileInfo = FileHandleGetInfo (File);
138   if (FileInfo == NULL) {
139     return EFI_INVALID_PARAMETER;
140   }
141 
142   //
143   // If the path is a directory, then return an error
144   //
145   if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
146     FreePool (FileInfo);
147     return EFI_INVALID_PARAMETER;
148   }
149 
150   //
151   // If the file size is already 0, then it is empty, so
152   // we can return success.
153   //
154   if (FileInfo->FileSize == 0) {
155     FreePool (FileInfo);
156     return EFI_SUCCESS;
157   }
158 
159   //
160   // Set the file size to 0.
161   //
162   FileInfo->FileSize = 0;
163   Status = FileHandleSetInfo (File, FileInfo);
164 
165   FreePool (FileInfo);
166 
167   return Status;
168 }
169 
170 
171 /**
172   Reads a file to a newly allocated buffer
173 
174   @param[in]  File - The file to read
175   @param[in]  ReadSize - The size of data to read from the file
176 
177   @return     Pointer to buffer allocated to hold the file
178               contents.  NULL if an error occurred.
179 
180 **/
181 VOID*
FileHandleReadToNewBuffer(IN EFI_FILE_HANDLE FileHandle,IN UINTN ReadSize)182 FileHandleReadToNewBuffer (
183   IN EFI_FILE_HANDLE            FileHandle,
184   IN UINTN                      ReadSize
185   )
186 {
187   EFI_STATUS                  Status;
188   UINTN                       ActualReadSize;
189   VOID                        *FileContents;
190 
191   ActualReadSize = ReadSize;
192   FileContents = AllocatePool (ReadSize);
193   if (FileContents != NULL) {
194     Status = FileHandleRead (
195                FileHandle,
196                &ReadSize,
197                FileContents
198                );
199     if (EFI_ERROR (Status) || (ActualReadSize != ReadSize)) {
200       FreePool (FileContents);
201       return NULL;
202     }
203   }
204 
205   return FileContents;
206 }
207 
208 
209 /**
210   Reads the contents of the NvVars file on the file system
211 
212   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
213 
214   @return     EFI_STATUS based on the success or failure of the file read
215 
216 **/
217 EFI_STATUS
ReadNvVarsFile(IN EFI_HANDLE FsHandle)218 ReadNvVarsFile (
219   IN  EFI_HANDLE            FsHandle
220   )
221 {
222   EFI_STATUS                  Status;
223   EFI_FILE_HANDLE             File;
224   UINTN                       FileSize;
225   BOOLEAN                     FileExists;
226   VOID                        *FileContents;
227   EFI_HANDLE                  SerializedVariables;
228 
229   Status = GetNvVarsFile (FsHandle, TRUE, &File);
230   if (EFI_ERROR (Status)) {
231     DEBUG ((DEBUG_INFO, "FsAccess.c: Could not open NV Variables file on this file system\n"));
232     return Status;
233   }
234 
235   NvVarsFileReadCheckup (File, &FileExists, &FileSize);
236   if (FileSize == 0) {
237     FileHandleClose (File);
238     return EFI_UNSUPPORTED;
239   }
240 
241   FileContents = FileHandleReadToNewBuffer (File, FileSize);
242   if (FileContents == NULL) {
243     FileHandleClose (File);
244     return EFI_UNSUPPORTED;
245   }
246 
247   DEBUG ((
248     DEBUG_INFO,
249     "FsAccess.c: Read %Lu bytes from NV Variables file\n",
250     (UINT64)FileSize
251     ));
252 
253   Status = SerializeVariablesNewInstanceFromBuffer (
254              &SerializedVariables,
255              FileContents,
256              FileSize
257              );
258   if (!RETURN_ERROR (Status)) {
259     Status = SerializeVariablesSetSerializedVariables (SerializedVariables);
260   }
261 
262   FreePool (FileContents);
263   FileHandleClose (File);
264 
265   return Status;
266 }
267 
268 
269 /**
270   Writes a variable to indicate that the NV variables
271   have been loaded from the file system.
272 
273 **/
274 STATIC
275 VOID
SetNvVarsVariable(VOID)276 SetNvVarsVariable (
277   VOID
278   )
279 {
280   BOOLEAN                        VarData;
281   UINTN                          Size;
282 
283   //
284   // Write a variable to indicate we've already loaded the
285   // variable data.  If it is found, we skip the loading on
286   // subsequent attempts.
287   //
288   Size = sizeof (VarData);
289   VarData = TRUE;
290   gRT->SetVariable (
291          L"NvVars",
292          &gEfiSimpleFileSystemProtocolGuid,
293          EFI_VARIABLE_NON_VOLATILE |
294            EFI_VARIABLE_BOOTSERVICE_ACCESS |
295            EFI_VARIABLE_RUNTIME_ACCESS,
296          Size,
297          (VOID*) &VarData
298          );
299 }
300 
301 
302 /**
303   Loads the non-volatile variables from the NvVars file on the
304   given file system.
305 
306   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
307 
308   @return     EFI_STATUS based on the success or failure of load operation
309 
310 **/
311 EFI_STATUS
LoadNvVarsFromFs(EFI_HANDLE FsHandle)312 LoadNvVarsFromFs (
313   EFI_HANDLE                            FsHandle
314   )
315 {
316   EFI_STATUS                     Status;
317   BOOLEAN                        VarData;
318   UINTN                          Size;
319 
320   DEBUG ((DEBUG_INFO, "FsAccess.c: LoadNvVarsFromFs\n"));
321 
322   //
323   // We write a variable to indicate we've already loaded the
324   // variable data.  If it is found, we skip the loading.
325   //
326   // This is relevant if the non-volatile variable have been
327   // able to survive a reboot operation.  In that case, we don't
328   // want to re-load the file as it would overwrite newer changes
329   // made to the variables.
330   //
331   Size = sizeof (VarData);
332   VarData = TRUE;
333   Status = gRT->GetVariable (
334                   L"NvVars",
335                   &gEfiSimpleFileSystemProtocolGuid,
336                   NULL,
337                   &Size,
338                   (VOID*) &VarData
339                   );
340   if (Status == EFI_SUCCESS) {
341     DEBUG ((DEBUG_INFO, "NV Variables were already loaded\n"));
342     return EFI_ALREADY_STARTED;
343   }
344 
345   //
346   // Attempt to restore the variables from the NvVars file.
347   //
348   Status = ReadNvVarsFile (FsHandle);
349   if (EFI_ERROR (Status)) {
350     DEBUG ((DEBUG_INFO, "Error while restoring NV variable data\n"));
351     return Status;
352   }
353 
354   //
355   // Write a variable to indicate we've already loaded the
356   // variable data.  If it is found, we skip the loading on
357   // subsequent attempts.
358   //
359   SetNvVarsVariable();
360 
361   DEBUG ((
362     DEBUG_INFO,
363     "FsAccess.c: Read NV Variables file (size=%Lu)\n",
364     (UINT64)Size
365     ));
366 
367   return Status;
368 }
369 
370 
371 STATIC
372 RETURN_STATUS
373 EFIAPI
IterateVariablesCallbackAddAllNvVariables(IN VOID * Context,IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID * Data)374 IterateVariablesCallbackAddAllNvVariables (
375   IN  VOID                         *Context,
376   IN  CHAR16                       *VariableName,
377   IN  EFI_GUID                     *VendorGuid,
378   IN  UINT32                       Attributes,
379   IN  UINTN                        DataSize,
380   IN  VOID                         *Data
381   )
382 {
383   EFI_HANDLE  Instance;
384 
385   Instance = (EFI_HANDLE) Context;
386 
387   //
388   // Only save non-volatile variables
389   //
390   if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
391     return RETURN_SUCCESS;
392   }
393 
394   return SerializeVariablesAddVariable (
395            Instance,
396            VariableName,
397            VendorGuid,
398            Attributes,
399            DataSize,
400            Data
401            );
402 }
403 
404 
405 /**
406   Saves the non-volatile variables into the NvVars file on the
407   given file system.
408 
409   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
410 
411   @return     EFI_STATUS based on the success or failure of load operation
412 
413 **/
414 EFI_STATUS
SaveNvVarsToFs(EFI_HANDLE FsHandle)415 SaveNvVarsToFs (
416   EFI_HANDLE                            FsHandle
417   )
418 {
419   EFI_STATUS                  Status;
420   EFI_FILE_HANDLE             File;
421   UINTN                       WriteSize;
422   UINTN                       VariableDataSize;
423   VOID                        *VariableData;
424   EFI_HANDLE                  SerializedVariables;
425 
426   SerializedVariables = NULL;
427 
428   Status = SerializeVariablesNewInstance (&SerializedVariables);
429   if (EFI_ERROR (Status)) {
430     return Status;
431   }
432 
433   Status = SerializeVariablesIterateSystemVariables (
434              IterateVariablesCallbackAddAllNvVariables,
435              (VOID*) SerializedVariables
436              );
437   if (EFI_ERROR (Status)) {
438     return Status;
439   }
440 
441   VariableData = NULL;
442   VariableDataSize = 0;
443   Status = SerializeVariablesToBuffer (
444              SerializedVariables,
445              NULL,
446              &VariableDataSize
447              );
448   if (Status == RETURN_BUFFER_TOO_SMALL) {
449     VariableData = AllocatePool (VariableDataSize);
450     if (VariableData == NULL) {
451       Status = EFI_OUT_OF_RESOURCES;
452     } else {
453       Status = SerializeVariablesToBuffer (
454                  SerializedVariables,
455                  VariableData,
456                  &VariableDataSize
457                  );
458     }
459   }
460 
461   SerializeVariablesFreeInstance (SerializedVariables);
462 
463   if (EFI_ERROR (Status)) {
464     return Status;
465   }
466 
467   //
468   // Open the NvVars file for writing.
469   //
470   Status = GetNvVarsFile (FsHandle, FALSE, &File);
471   if (EFI_ERROR (Status)) {
472     DEBUG ((DEBUG_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n"));
473     return Status;
474   }
475 
476   //
477   // Empty the starting file contents.
478   //
479   Status = FileHandleEmpty (File);
480   if (EFI_ERROR (Status)) {
481     FileHandleClose (File);
482     return Status;
483   }
484 
485   WriteSize = VariableDataSize;
486   Status = FileHandleWrite (File, &WriteSize, VariableData);
487   if (EFI_ERROR (Status)) {
488     return Status;
489   }
490 
491   FileHandleClose (File);
492 
493   if (!EFI_ERROR (Status)) {
494     //
495     // Write a variable to indicate we've already loaded the
496     // variable data.  If it is found, we skip the loading on
497     // subsequent attempts.
498     //
499     SetNvVarsVariable();
500 
501     DEBUG ((DEBUG_INFO, "Saved NV Variables to NvVars file\n"));
502   }
503 
504   return Status;
505 
506 }
507 
508 
509