937e4abe196ddc9ccecd20628bd398c8c1e82b01
[mirror_edk2.git] / OvmfPkg / Library / NvVarsFileLib / FsAccess.c
1 /** @file
2 File System Access for NvVarsFileLib
3
4 Copyright (c) 2004 - 2013, 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
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
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
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*
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
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 %d bytes from NV Variables file\n",
259 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
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
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=%d)\n",
373 Size
374 ));
375
376 return Status;
377 }
378
379
380 STATIC
381 RETURN_STATUS
382 EFIAPI
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
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 Status = SerializeVariablesNewInstance (&SerializedVariables);
436 if (EFI_ERROR (Status)) {
437 return Status;
438 }
439
440 Status = SerializeVariablesIterateSystemVariables (
441 IterateVariablesCallbackAddAllNvVariables,
442 (VOID*) SerializedVariables
443 );
444 if (EFI_ERROR (Status)) {
445 return Status;
446 }
447
448 VariableData = NULL;
449 VariableDataSize = 0;
450 Status = SerializeVariablesToBuffer (
451 SerializedVariables,
452 NULL,
453 &VariableDataSize
454 );
455 if (Status == RETURN_BUFFER_TOO_SMALL) {
456 VariableData = AllocatePool (VariableDataSize);
457 if (VariableData == NULL) {
458 Status = EFI_OUT_OF_RESOURCES;
459 } else {
460 Status = SerializeVariablesToBuffer (
461 SerializedVariables,
462 VariableData,
463 &VariableDataSize
464 );
465 }
466 }
467
468 SerializeVariablesFreeInstance (SerializedVariables);
469
470 if (EFI_ERROR (Status)) {
471 return Status;
472 }
473
474 //
475 // Open the NvVars file for writing.
476 //
477 Status = GetNvVarsFile (FsHandle, FALSE, &File);
478 if (EFI_ERROR (Status)) {
479 DEBUG ((EFI_D_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n"));
480 return Status;
481 }
482
483 //
484 // Empty the starting file contents.
485 //
486 Status = FileHandleEmpty (File);
487 if (EFI_ERROR (Status)) {
488 FileHandleClose (File);
489 return Status;
490 }
491
492 WriteSize = VariableDataSize;
493 Status = FileHandleWrite (File, &WriteSize, VariableData);
494 if (EFI_ERROR (Status)) {
495 return Status;
496 }
497
498 FileHandleClose (File);
499
500 if (!EFI_ERROR (Status)) {
501 //
502 // Write a variable to indicate we've already loaded the
503 // variable data. If it is found, we skip the loading on
504 // subsequent attempts.
505 //
506 SetNvVarsVariable();
507
508 DEBUG ((EFI_D_INFO, "Saved NV Variables to NvVars file\n"));
509 }
510
511 return Status;
512
513 }
514
515