Fix VS2013 build failure.
[mirror_edk2.git] / OvmfPkg / Library / NvVarsFileLib / FsAccess.c
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
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 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