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**/
33EFI_STATUS
34GetNvVarsFile (
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**/
97VOID
98NvVarsFileReadCheckup (
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**/
135EFI_STATUS
136FileHandleEmpty (
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 occurred.
188
189**/
190VOID*
191FileHandleReadToNewBuffer (
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**/
226EFI_STATUS
227ReadNvVarsFile (
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**/
283STATIC
284VOID
285SetNvVarsVariable (
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**/
320EFI_STATUS
321LoadNvVarsFromFs (
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 relevant 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
380STATIC
381RETURN_STATUS
382EFIAPI
383IterateVariablesCallbackAddAllNvVariables (
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**/
423EFI_STATUS
424SaveNvVarsToFs (
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