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