VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/efi/efivarstorevfs.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.1 KB
Line 
1/* $Id: efivarstorevfs.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - Expose a EFI variable store as a Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2021-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FS
42#include <iprt/efi.h>
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/crc.h>
47#include <iprt/file.h>
48#include <iprt/err.h>
49#include <iprt/log.h>
50#include <iprt/mem.h>
51#include <iprt/string.h>
52#include <iprt/uuid.h>
53#include <iprt/utf16.h>
54#include <iprt/vfs.h>
55#include <iprt/vfslowlevel.h>
56#include <iprt/formats/efi-fv.h>
57#include <iprt/formats/efi-varstore.h>
58
59
60/*********************************************************************************************************************************
61* Defined Constants And Macros *
62*********************************************************************************************************************************/
63
64
65/*********************************************************************************************************************************
66* Structures and Typedefs *
67*********************************************************************************************************************************/
68/** Pointer to the varstore filesystem data. */
69typedef struct RTEFIVARSTORE *PRTEFIVARSTORE;
70
71
72/**
73 * EFI variable entry.
74 */
75typedef struct RTEFIVAR
76{
77 /** Pointer to the owning variable store. */
78 PRTEFIVARSTORE pVarStore;
79 /** Offset of the variable data located in the backing image - 0 if not written yet. */
80 uint64_t offVarData;
81 /** Pointer to the in memory data, NULL if not yet read. */
82 void *pvData;
83 /** Monotonic counter value. */
84 uint64_t cMonotonic;
85 /** Size of the variable data in bytes. */
86 uint32_t cbData;
87 /** Index of the assoicated public key. */
88 uint32_t idPubKey;
89 /** Attributes for the variable. */
90 uint32_t fAttr;
91 /** Flag whether the variable was deleted. */
92 bool fDeleted;
93 /** Name of the variable. */
94 char *pszName;
95 /** The raw EFI timestamp as read from the header. */
96 EFI_TIME EfiTimestamp;
97 /** The creation/update time. */
98 RTTIMESPEC Time;
99 /** The vendor UUID of the variable. */
100 RTUUID Uuid;
101} RTEFIVAR;
102/** Pointer to an EFI variable. */
103typedef RTEFIVAR *PRTEFIVAR;
104
105
106/**
107 * EFI GUID entry.
108 */
109typedef struct RTEFIGUID
110{
111 /** The UUID representation of the GUID. */
112 RTUUID Uuid;
113 /** Pointer to the array of indices into RTEFIVARSTORE::paVars. */
114 uint32_t *paidxVars;
115 /** Number of valid indices in the array. */
116 uint32_t cVars;
117 /** Maximum number of indices the array can hold. */
118 uint32_t cVarsMax;
119} RTEFIGUID;
120/** Pointer to an EFI variable. */
121typedef RTEFIGUID *PRTEFIGUID;
122
123
124/**
125 * EFI variable store filesystem volume.
126 */
127typedef struct RTEFIVARSTORE
128{
129 /** Handle to itself. */
130 RTVFS hVfsSelf;
131 /** The file, partition, or whatever backing the volume has. */
132 RTVFSFILE hVfsBacking;
133 /** The size of the backing thingy. */
134 uint64_t cbBacking;
135
136 /** RTVFSMNT_F_XXX. */
137 uint32_t fMntFlags;
138 /** RTEFIVARSTOREVFS_F_XXX (currently none defined). */
139 uint32_t fVarStoreFlags;
140
141 /** Size of the variable store (minus the header). */
142 uint64_t cbVarStore;
143 /** Start offset into the backing image where the variable data starts. */
144 uint64_t offStoreData;
145 /** Flag whether the variable store uses authenticated variables. */
146 bool fAuth;
147 /** Number of bytes occupied by existing variables. */
148 uint64_t cbVarData;
149
150 /** Pointer to the array of variables sorted by start offset. */
151 PRTEFIVAR paVars;
152 /** Number of valid variables in the array. */
153 uint32_t cVars;
154 /** Maximum number of variables the array can hold. */
155 uint32_t cVarsMax;
156
157 /** Pointer to the array of vendor GUIDS. */
158 PRTEFIGUID paGuids;
159 /** Number of valid GUIDS in the array. */
160 uint32_t cGuids;
161 /** Maximum number of GUIDS the array can hold. */
162 uint32_t cGuidsMax;
163
164} RTEFIVARSTORE;
165
166
167/**
168 * Variable store directory type.
169 */
170typedef enum RTEFIVARSTOREDIRTYPE
171{
172 /** Invalid directory type. */
173 RTEFIVARSTOREDIRTYPE_INVALID = 0,
174 /** Root directory type. */
175 RTEFIVARSTOREDIRTYPE_ROOT,
176 /** 'by-name' directory. */
177 RTEFIVARSTOREDIRTYPE_BY_NAME,
178 /** 'by-uuid' directory. */
179 RTEFIVARSTOREDIRTYPE_BY_GUID,
180 /** 'raw' directory. */
181 RTEFIVARSTOREDIRTYPE_RAW,
182 /** Specific 'by-uuid/{...}' directory. */
183 RTEFIVARSTOREDIRTYPE_GUID,
184 /** Specific 'raw/{...}' directory. */
185 RTEFIVARSTOREDIRTYPE_RAW_ENTRY,
186 /** 32bit blowup hack. */
187 RTEFIVARSTOREDIRTYPE_32BIT_HACK = 0x7fffffff
188} RTEFIVARSTOREDIRTYPE;
189
190
191/**
192 * EFI variable store directory entry.
193 */
194typedef struct RTEFIVARSTOREDIRENTRY
195{
196 /** Name of the directory if constant. */
197 const char *pszName;
198 /** Size of the name. */
199 size_t cbName;
200 /** Entry type. */
201 RTEFIVARSTOREDIRTYPE enmType;
202 /** Parent entry type. */
203 RTEFIVARSTOREDIRTYPE enmParentType;
204} RTEFIVARSTOREDIRENTRY;
205/** Pointer to a EFI variable store directory entry. */
206typedef RTEFIVARSTOREDIRENTRY *PRTEFIVARSTOREDIRENTRY;
207/** Pointer to a const EFI variable store directory entry. */
208typedef const RTEFIVARSTOREDIRENTRY *PCRTEFIVARSTOREDIRENTRY;
209
210
211/**
212 * Variable store directory.
213 */
214typedef struct RTEFIVARSTOREDIR
215{
216 /* Flag whether we reached the end of directory entries. */
217 bool fNoMoreFiles;
218 /** The index of the next item to read. */
219 uint32_t idxNext;
220 /** Directory entry. */
221 PCRTEFIVARSTOREDIRENTRY pEntry;
222 /** The variable store associated with this directory. */
223 PRTEFIVARSTORE pVarStore;
224 /** Time when the directory was created. */
225 RTTIMESPEC Time;
226 /** Pointer to the GUID entry, only valid for RTEFIVARSTOREDIRTYPE_GUID. */
227 PRTEFIGUID pGuid;
228 /** The variable ID, only valid for RTEFIVARSTOREDIRTYPE_RAW_ENTRY. */
229 uint32_t idVar;
230} RTEFIVARSTOREDIR;
231/** Pointer to an Variable store directory. */
232typedef RTEFIVARSTOREDIR *PRTEFIVARSTOREDIR;
233
234
235/**
236 * File type.
237 */
238typedef enum RTEFIVARSTOREFILETYPE
239{
240 /** Invalid type, do not use. */
241 RTEFIVARSTOREFILETYPE_INVALID = 0,
242 /** File accesses the data portion of the variable. */
243 RTEFIVARSTOREFILETYPE_DATA,
244 /** File accesses the attributes of the variable. */
245 RTEFIVARSTOREFILETYPE_ATTR,
246 /** File accesses the UUID of the variable. */
247 RTEFIVARSTOREFILETYPE_UUID,
248 /** File accesses the public key index of the variable. */
249 RTEFIVARSTOREFILETYPE_PUBKEY,
250 /** File accesses the raw EFI Time of the variable. */
251 RTEFIVARSTOREFILETYPE_TIME,
252 /** The monotonic counter (deprecated). */
253 RTEFIVARSTOREFILETYPE_MONOTONIC,
254 /** 32bit hack. */
255 RTEFIVARSTOREFILETYPE_32BIT_HACK = 0x7fffffff
256} RTEFIVARSTOREFILETYPE;
257
258
259/**
260 * Raw file type entry.
261 */
262typedef struct RTEFIVARSTOREFILERAWENTRY
263{
264 /** Name of the entry. */
265 const char *pszName;
266 /** The associated file type. */
267 RTEFIVARSTOREFILETYPE enmType;
268 /** File size of the object, 0 if dynamic. */
269 size_t cbObject;
270 /** Offset of the item in the variable header. */
271 uint32_t offObject;
272} RTEFIVARSTOREFILERAWENTRY;
273/** Pointer to a raw file type entry. */
274typedef RTEFIVARSTOREFILERAWENTRY *PRTEFIVARSTOREFILERAWENTRY;
275/** Pointer to a const file type entry. */
276typedef const RTEFIVARSTOREFILERAWENTRY *PCRTEFIVARSTOREFILERAWENTRY;
277
278
279/**
280 * Open file instance.
281 */
282typedef struct RTEFIVARFILE
283{
284 /** The file type. */
285 PCRTEFIVARSTOREFILERAWENTRY pEntry;
286 /** Variable store this file belongs to. */
287 PRTEFIVARSTORE pVarStore;
288 /** The underlying variable structure. */
289 PRTEFIVAR pVar;
290 /** Current offset into the file for I/O. */
291 RTFOFF offFile;
292} RTEFIVARFILE;
293/** Pointer to an open file instance. */
294typedef RTEFIVARFILE *PRTEFIVARFILE;
295
296
297/**
298 * Directories.
299 */
300static const RTEFIVARSTOREDIRENTRY g_aDirs[] =
301{
302 { NULL, 0, RTEFIVARSTOREDIRTYPE_ROOT, RTEFIVARSTOREDIRTYPE_ROOT },
303 { RT_STR_TUPLE("by-name"), RTEFIVARSTOREDIRTYPE_BY_NAME, RTEFIVARSTOREDIRTYPE_ROOT },
304 { RT_STR_TUPLE("by-uuid"), RTEFIVARSTOREDIRTYPE_BY_GUID, RTEFIVARSTOREDIRTYPE_ROOT },
305 { RT_STR_TUPLE("raw"), RTEFIVARSTOREDIRTYPE_RAW, RTEFIVARSTOREDIRTYPE_ROOT },
306 { NULL, 0, RTEFIVARSTOREDIRTYPE_GUID, RTEFIVARSTOREDIRTYPE_BY_GUID },
307 { NULL, 0, RTEFIVARSTOREDIRTYPE_RAW_ENTRY, RTEFIVARSTOREDIRTYPE_RAW },
308};
309
310
311/**
312 * Raw files for accessing specific items in the variable header.
313 */
314static const RTEFIVARSTOREFILERAWENTRY g_aRawFiles[] =
315{
316 { "attr", RTEFIVARSTOREFILETYPE_ATTR, sizeof(uint32_t), RT_UOFFSETOF(RTEFIVAR, fAttr) },
317 { "data", RTEFIVARSTOREFILETYPE_DATA, 0, 0 },
318 { "uuid", RTEFIVARSTOREFILETYPE_UUID, sizeof(RTUUID), RT_UOFFSETOF(RTEFIVAR, Uuid) },
319 { "pubkey", RTEFIVARSTOREFILETYPE_PUBKEY, sizeof(uint32_t), RT_UOFFSETOF(RTEFIVAR, idPubKey) },
320 { "time", RTEFIVARSTOREFILETYPE_TIME, sizeof(EFI_TIME), RT_UOFFSETOF(RTEFIVAR, EfiTimestamp) },
321 { "monotonic", RTEFIVARSTOREFILETYPE_MONOTONIC, sizeof(uint64_t), RT_UOFFSETOF(RTEFIVAR, cMonotonic) }
322};
323
324#define RTEFIVARSTORE_FILE_ENTRY_DATA 1
325
326
327/*********************************************************************************************************************************
328* Internal Functions *
329*********************************************************************************************************************************/
330static int rtEfiVarStore_NewDirByType(PRTEFIVARSTORE pThis, RTEFIVARSTOREDIRTYPE enmDirType,
331 PRTEFIGUID pGuid, uint32_t idVar, PRTVFSOBJ phVfsObj);
332
333
334#ifdef LOG_ENABLED
335/**
336 * Logs a firmware volume header.
337 *
338 * @returns nothing.
339 * @param pFvHdr The firmware volume header.
340 */
341static void rtEfiVarStoreFvHdr_Log(PCEFI_FIRMWARE_VOLUME_HEADER pFvHdr)
342{
343 if (LogIs2Enabled())
344 {
345 Log2(("EfiVarStore: Volume Header:\n"));
346 Log2(("EfiVarStore: abZeroVec %#.*Rhxs\n", sizeof(pFvHdr->abZeroVec), &pFvHdr->abZeroVec[0]));
347 Log2(("EfiVarStore: GuidFilesystem %#.*Rhxs\n", sizeof(pFvHdr->GuidFilesystem), &pFvHdr->GuidFilesystem));
348 Log2(("EfiVarStore: cbFv %#RX64\n", RT_LE2H_U64(pFvHdr->cbFv)));
349 Log2(("EfiVarStore: u32Signature %#RX32\n", RT_LE2H_U32(pFvHdr->u32Signature)));
350 Log2(("EfiVarStore: fAttr %#RX32\n", RT_LE2H_U32(pFvHdr->fAttr)));
351 Log2(("EfiVarStore: cbFvHdr %#RX16\n", RT_LE2H_U16(pFvHdr->cbFvHdr)));
352 Log2(("EfiVarStore: u16Chksum %#RX16\n", RT_LE2H_U16(pFvHdr->u16Chksum)));
353 Log2(("EfiVarStore: offExtHdr %#RX16\n", RT_LE2H_U16(pFvHdr->offExtHdr)));
354 Log2(("EfiVarStore: bRsvd %#RX8\n", pFvHdr->bRsvd));
355 Log2(("EfiVarStore: bRevision %#RX8\n", pFvHdr->bRevision));
356 }
357}
358
359
360/**
361 * Logs a variable store header.
362 *
363 * @returns nothing.
364 * @param pStoreHdr The variable store header.
365 */
366static void rtEfiVarStoreHdr_Log(PCEFI_VARSTORE_HEADER pStoreHdr)
367{
368 if (LogIs2Enabled())
369 {
370 Log2(("EfiVarStore: Variable Store Header:\n"));
371 Log2(("EfiVarStore: GuidVarStore %#.*Rhxs\n", sizeof(pStoreHdr->GuidVarStore), &pStoreHdr->GuidVarStore));
372 Log2(("EfiVarStore: cbVarStore %#RX32\n", RT_LE2H_U32(pStoreHdr->cbVarStore)));
373 Log2(("EfiVarStore: bFmt %#RX8\n", pStoreHdr->bFmt));
374 Log2(("EfiVarStore: bState %#RX8\n", pStoreHdr->bState));
375 }
376}
377
378
379/**
380 * Logs a authenticated variable header.
381 *
382 * @returns nothing.
383 * @param pVarHdr The authenticated variable header.
384 * @param offVar Offset of the authenticated variable header.
385 */
386static void rtEfiVarStoreAuthVarHdr_Log(PCEFI_AUTH_VAR_HEADER pVarHdr, uint64_t offVar)
387{
388 if (LogIs2Enabled())
389 {
390 Log2(("EfiVarStore: Authenticated Variable Header at offset %#RU64:\n", offVar));
391 Log2(("EfiVarStore: u16StartId %#RX16\n", RT_LE2H_U16(pVarHdr->u16StartId)));
392 Log2(("EfiVarStore: bState %#RX8\n", pVarHdr->bState));
393 Log2(("EfiVarStore: bRsvd %#RX8\n", pVarHdr->bRsvd));
394 Log2(("EfiVarStore: fAttr %#RX32\n", RT_LE2H_U32(pVarHdr->fAttr)));
395 Log2(("EfiVarStore: cMonotonic %#RX64\n", RT_LE2H_U64(pVarHdr->cMonotonic)));
396 Log2(("EfiVarStore: Timestamp.u16Year %#RX16\n", RT_LE2H_U16(pVarHdr->Timestamp.u16Year)));
397 Log2(("EfiVarStore: Timestamp.u8Month %#RX8\n", pVarHdr->Timestamp.u8Month));
398 Log2(("EfiVarStore: Timestamp.u8Day %#RX8\n", pVarHdr->Timestamp.u8Day));
399 Log2(("EfiVarStore: Timestamp.u8Hour %#RX8\n", pVarHdr->Timestamp.u8Hour));
400 Log2(("EfiVarStore: Timestamp.u8Minute %#RX8\n", pVarHdr->Timestamp.u8Minute));
401 Log2(("EfiVarStore: Timestamp.u8Second %#RX8\n", pVarHdr->Timestamp.u8Second));
402 Log2(("EfiVarStore: Timestamp.bPad0 %#RX8\n", pVarHdr->Timestamp.bPad0));
403 Log2(("EfiVarStore: Timestamp.u32Nanosecond %#RX32\n", RT_LE2H_U32(pVarHdr->Timestamp.u32Nanosecond)));
404 Log2(("EfiVarStore: Timestamp.iTimezone %#RI16\n", RT_LE2H_S16(pVarHdr->Timestamp.iTimezone)));
405 Log2(("EfiVarStore: Timestamp.u8Daylight %#RX8\n", pVarHdr->Timestamp.u8Daylight));
406 Log2(("EfiVarStore: Timestamp.bPad1 %#RX8\n", pVarHdr->Timestamp.bPad1));
407 Log2(("EfiVarStore: idPubKey %#RX32\n", RT_LE2H_U32(pVarHdr->idPubKey)));
408 Log2(("EfiVarStore: cbName %#RX32\n", RT_LE2H_U32(pVarHdr->cbName)));
409 Log2(("EfiVarStore: cbData %#RX32\n", RT_LE2H_U32(pVarHdr->cbData)));
410 Log2(("EfiVarStore: GuidVendor %#.*Rhxs\n", sizeof(pVarHdr->GuidVendor), &pVarHdr->GuidVendor));
411 }
412}
413#endif
414
415
416/**
417 * Worker for rtEfiVarStoreFile_QueryInfo() and rtEfiVarStoreDir_QueryInfo().
418 *
419 * @returns IPRT status code.
420 * @param cbObject Size of the object in bytes.
421 * @param fIsDir Flag whether the object is a directory or file.
422 * @param pTime The time to use.
423 * @param pObjInfo The FS object information structure to fill in.
424 * @param enmAddAttr What to fill in.
425 */
426static int rtEfiVarStore_QueryInfo(uint64_t cbObject, bool fIsDir, PCRTTIMESPEC pTime, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
427{
428 pObjInfo->cbObject = cbObject;
429 pObjInfo->cbAllocated = cbObject;
430 pObjInfo->AccessTime = *pTime;
431 pObjInfo->ModificationTime = *pTime;
432 pObjInfo->ChangeTime = *pTime;
433 pObjInfo->BirthTime = *pTime;
434 pObjInfo->Attr.fMode = fIsDir
435 ? RTFS_TYPE_DIRECTORY | RTFS_UNIX_ALL_ACCESS_PERMS
436 : RTFS_TYPE_FILE | RTFS_UNIX_IWOTH | RTFS_UNIX_IROTH
437 | RTFS_UNIX_IWGRP | RTFS_UNIX_IRGRP
438 | RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR;
439 pObjInfo->Attr.enmAdditional = enmAddAttr;
440
441 switch (enmAddAttr)
442 {
443 case RTFSOBJATTRADD_NOTHING: RT_FALL_THRU();
444 case RTFSOBJATTRADD_UNIX:
445 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
446 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
447 pObjInfo->Attr.u.Unix.cHardlinks = 1;
448 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
449 pObjInfo->Attr.u.Unix.INodeId = 0;
450 pObjInfo->Attr.u.Unix.fFlags = 0;
451 pObjInfo->Attr.u.Unix.GenerationId = 0;
452 pObjInfo->Attr.u.Unix.Device = 0;
453 break;
454 case RTFSOBJATTRADD_UNIX_OWNER:
455 pObjInfo->Attr.u.UnixOwner.uid = 0;
456 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
457 break;
458 case RTFSOBJATTRADD_UNIX_GROUP:
459 pObjInfo->Attr.u.UnixGroup.gid = 0;
460 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
461 break;
462 case RTFSOBJATTRADD_EASIZE:
463 pObjInfo->Attr.u.EASize.cb = 0;
464 break;
465 default:
466 return VERR_INVALID_PARAMETER;
467 }
468 return VINF_SUCCESS;
469}
470
471
472/**
473 * Tries to find and return the GUID entry for the given UUID.
474 *
475 * @returns Pointer to the GUID entry or NULL if not found.
476 * @param pThis The EFI variable store instance.
477 * @param pUuid The UUID to look for.
478 */
479static PRTEFIGUID rtEfiVarStore_GetGuid(PRTEFIVARSTORE pThis, PCRTUUID pUuid)
480{
481 for (uint32_t i = 0; i < pThis->cGuids; i++)
482 if (!RTUuidCompare(&pThis->paGuids[i].Uuid, pUuid))
483 return &pThis->paGuids[i];
484
485 return NULL;
486}
487
488
489/**
490 * Adds the given UUID to the array of known GUIDs.
491 *
492 * @returns Pointer to the GUID entry or NULL if out of memory.
493 * @param pThis The EFI variable store instance.
494 * @param pUuid The UUID to add.
495 */
496static PRTEFIGUID rtEfiVarStore_AddGuid(PRTEFIVARSTORE pThis, PCRTUUID pUuid)
497{
498 if (pThis->cGuids == pThis->cGuidsMax)
499 {
500 /* Grow the array. */
501 uint32_t cGuidsMaxNew = pThis->cGuidsMax + 10;
502 PRTEFIGUID paGuidsNew = (PRTEFIGUID)RTMemRealloc(pThis->paGuids, cGuidsMaxNew * sizeof(RTEFIGUID));
503 if (!paGuidsNew)
504 return NULL;
505
506 pThis->paGuids = paGuidsNew;
507 pThis->cGuidsMax = cGuidsMaxNew;
508 }
509
510 PRTEFIGUID pGuid = &pThis->paGuids[pThis->cGuids++];
511 pGuid->Uuid = *pUuid;
512 pGuid->paidxVars = NULL;
513 pGuid->cVars = 0;
514 pGuid->cVarsMax = 0;
515 return pGuid;
516}
517
518
519/**
520 * Adds the given variable to the GUID array.
521 *
522 * @returns IPRT status code.
523 * @param pThis The EFI variable store instance.
524 * @param pUuid The UUID of the variable.
525 * @param idVar The variable index into the array.
526 */
527static int rtEfiVarStore_AddVarByGuid(PRTEFIVARSTORE pThis, PCRTUUID pUuid, uint32_t idVar)
528{
529 PRTEFIGUID pGuid = rtEfiVarStore_GetGuid(pThis, pUuid);
530 if (!pGuid)
531 pGuid = rtEfiVarStore_AddGuid(pThis, pUuid);
532
533 if ( pGuid
534 && pGuid->cVars == pGuid->cVarsMax)
535 {
536 /* Grow the array. */
537 uint32_t cVarsMaxNew = pGuid->cVarsMax + 10;
538 uint32_t *paidxVarsNew = (uint32_t *)RTMemRealloc(pGuid->paidxVars, cVarsMaxNew * sizeof(uint32_t));
539 if (!paidxVarsNew)
540 return VERR_NO_MEMORY;
541
542 pGuid->paidxVars = paidxVarsNew;
543 pGuid->cVarsMax = cVarsMaxNew;
544 }
545
546 int rc = VINF_SUCCESS;
547 if (pGuid)
548 pGuid->paidxVars[pGuid->cVars++] = idVar;
549 else
550 rc = VERR_NO_MEMORY;
551
552 return rc;
553}
554
555
556/**
557 * Reads variable data from the given memory area.
558 *
559 * @returns IPRT status code.
560 * @param pThis The EFI variable file instance.
561 * @param pvData Pointer to the start of the data.
562 * @param cbData Size of the variable data in bytes.
563 * @param off Where to start reading relative from the data start offset.
564 * @param pSgBuf Where to store the read data.
565 * @param pcbRead Where to return the number of bytes read, optional.
566 */
567static int rtEfiVarStoreFile_ReadMem(PRTEFIVARFILE pThis, const void *pvData, size_t cbData,
568 RTFOFF off, PCRTSGBUF pSgBuf, size_t *pcbRead)
569{
570 int rc = VINF_SUCCESS;
571 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
572 size_t cbThisRead = RT_MIN(cbData - off, cbRead);
573 const uint8_t *pbData = (const uint8_t *)pvData;
574 if (!pcbRead)
575 {
576 if (cbThisRead == cbRead)
577 memcpy(pSgBuf->paSegs[0].pvSeg, &pbData[off], cbThisRead);
578 else
579 rc = VERR_EOF;
580
581 if (RT_SUCCESS(rc))
582 pThis->offFile = off + cbThisRead;
583 Log6(("rtEfiVarStoreFile_ReadMem: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
584 }
585 else
586 {
587 if ((uint64_t)off >= cbData)
588 {
589 *pcbRead = 0;
590 rc = VINF_EOF;
591 }
592 else
593 {
594 memcpy(pSgBuf->paSegs[0].pvSeg, &pbData[off], cbThisRead);
595 /* Return VINF_EOF if beyond end-of-file. */
596 if (cbThisRead < cbRead)
597 rc = VINF_EOF;
598 pThis->offFile = off + cbThisRead;
599 *pcbRead = cbThisRead;
600 }
601 Log6(("rtEfiVarStoreFile_ReadMem: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
602 }
603
604 return rc;
605}
606
607
608/**
609 * Writes variable data from the given memory area.
610 *
611 * @returns IPRT status code.
612 * @param pThis The EFI variable file instance.
613 * @param pvData Pointer to the start of the data.
614 * @param cbData Size of the variable data in bytes.
615 * @param off Where to start writing relative from the data start offset.
616 * @param pSgBuf The data to write.
617 * @param pcbWritten Where to return the number of bytes written, optional.
618 */
619static int rtEfiVarStoreFile_WriteMem(PRTEFIVARFILE pThis, void *pvData, size_t cbData,
620 RTFOFF off, PCRTSGBUF pSgBuf, size_t *pcbWritten)
621{
622 int rc = VINF_SUCCESS;
623 size_t cbWrite = pSgBuf->paSegs[0].cbSeg;
624 size_t cbThisWrite = RT_MIN(cbData - off, cbWrite);
625 uint8_t *pbData = (uint8_t *)pvData;
626 if (!pcbWritten)
627 {
628 if (cbThisWrite == cbWrite)
629 memcpy(&pbData[off], pSgBuf->paSegs[0].pvSeg, cbThisWrite);
630 else
631 rc = VERR_EOF;
632
633 if (RT_SUCCESS(rc))
634 pThis->offFile = off + cbThisWrite;
635 Log6(("rtEfiVarStoreFile_WriteMem: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
636 }
637 else
638 {
639 if ((uint64_t)off >= cbData)
640 {
641 *pcbWritten = 0;
642 rc = VINF_EOF;
643 }
644 else
645 {
646 memcpy(&pbData[off], pSgBuf->paSegs[0].pvSeg, cbThisWrite);
647 /* Return VINF_EOF if beyond end-of-file. */
648 if (cbThisWrite < cbWrite)
649 rc = VINF_EOF;
650 pThis->offFile = off + cbThisWrite;
651 *pcbWritten = cbThisWrite;
652 }
653 Log6(("rtEfiVarStoreFile_WriteMem: off=%#RX64 cbSeg=%#x -> %Rrc *pcbWritten=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbWritten));
654 }
655
656 return rc;
657}
658
659
660/**
661 * Reads variable data from the given range.
662 *
663 * @returns IPRT status code.
664 * @param pThis The EFI variable file instance.
665 * @param offData Where the data starts in the backing storage.
666 * @param cbData Size of the variable data in bytes.
667 * @param off Where to start reading relative from the data start offset.
668 * @param pSgBuf Where to store the read data.
669 * @param pcbRead Where to return the number of bytes read, optional.
670 */
671static int rtEfiVarStoreFile_ReadFile(PRTEFIVARFILE pThis, uint64_t offData, size_t cbData,
672 RTFOFF off, PCRTSGBUF pSgBuf, size_t *pcbRead)
673{
674 int rc;
675 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
676 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
677 size_t cbThisRead = RT_MIN(cbData - off, cbRead);
678 uint64_t offStart = offData + off;
679 if (!pcbRead)
680 {
681 if (cbThisRead == cbRead)
682 rc = RTVfsFileReadAt(pVarStore->hVfsBacking, offStart, pSgBuf->paSegs[0].pvSeg, cbThisRead, NULL);
683 else
684 rc = VERR_EOF;
685
686 if (RT_SUCCESS(rc))
687 pThis->offFile = off + cbThisRead;
688 Log6(("rtFsEfiVarStore_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
689 }
690 else
691 {
692 if ((uint64_t)off >= cbData)
693 {
694 *pcbRead = 0;
695 rc = VINF_EOF;
696 }
697 else
698 {
699 rc = RTVfsFileReadAt(pVarStore->hVfsBacking, offStart, pSgBuf->paSegs[0].pvSeg, cbThisRead, NULL);
700 if (RT_SUCCESS(rc))
701 {
702 /* Return VINF_EOF if beyond end-of-file. */
703 if (cbThisRead < cbRead)
704 rc = VINF_EOF;
705 pThis->offFile = off + cbThisRead;
706 *pcbRead = cbThisRead;
707 }
708 else
709 *pcbRead = 0;
710 }
711 Log6(("rtFsEfiVarStore_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
712 }
713
714 return rc;
715}
716
717
718/**
719 * Ensures that the variable data is available before any modification.
720 *
721 * @returns IPRT status code.
722 * @param pVar The variable instance.
723 */
724static int rtEfiVarStore_VarReadData(PRTEFIVAR pVar)
725{
726 if (RT_LIKELY( !pVar->offVarData
727 || !pVar->cbData))
728 return VINF_SUCCESS;
729
730 Assert(!pVar->pvData);
731 pVar->pvData = RTMemAlloc(pVar->cbData);
732 if (RT_UNLIKELY(!pVar->pvData))
733 return VERR_NO_MEMORY;
734
735 PRTEFIVARSTORE pVarStore = pVar->pVarStore;
736 int rc = RTVfsFileReadAt(pVarStore->hVfsBacking, pVar->offVarData, pVar->pvData, pVar->cbData, NULL);
737 if (RT_SUCCESS(rc))
738 pVar->offVarData = 0; /* Marks the variable data as in memory. */
739 else
740 {
741 RTMemFree(pVar->pvData);
742 pVar->pvData = NULL;
743 }
744
745 return rc;
746}
747
748
749/**
750 * Ensures that the given variable has the given data size.
751 *
752 * @returns IPRT status code.
753 * @retval VERR_DISK_FULL if the new size would exceed the variable storage size.
754 * @param pVar The variable instance.
755 * @param cbData New number of bytes of data for the variable.
756 */
757static int rtEfiVarStore_VarEnsureDataSz(PRTEFIVAR pVar, size_t cbData)
758{
759 PRTEFIVARSTORE pVarStore = pVar->pVarStore;
760
761 if (pVar->cbData == cbData)
762 return VINF_SUCCESS;
763
764 if ((uint32_t)cbData != cbData)
765 return VERR_FILE_TOO_BIG;
766
767 int rc = VINF_SUCCESS;
768 if (cbData < pVar->cbData)
769 {
770 /* Shrink. */
771 void *pvNew = RTMemRealloc(pVar->pvData, cbData);
772 if (pvNew)
773 {
774 pVar->pvData = pvNew;
775 pVarStore->cbVarData -= pVar->cbData - cbData;
776 pVar->cbData = (uint32_t)cbData;
777 }
778 else
779 rc = VERR_NO_MEMORY;
780 }
781 else if (cbData > pVar->cbData)
782 {
783 /* Grow. */
784 if (pVarStore->cbVarStore - pVarStore->cbVarData >= cbData - pVar->cbData)
785 {
786 void *pvNew = RTMemRealloc(pVar->pvData, cbData);
787 if (pvNew)
788 {
789 pVar->pvData = pvNew;
790 pVarStore->cbVarData += cbData - pVar->cbData;
791 pVar->cbData = (uint32_t)cbData;
792 }
793 else
794 rc = VERR_NO_MEMORY;
795 }
796 else
797 rc = VERR_DISK_FULL;
798 }
799
800 return rc;
801}
802
803
804/**
805 * Flush the variable store to the backing storage. This will remove any
806 * deleted variables in the backing storage.
807 *
808 * @returns IPRT status code.
809 * @param pThis The EFI variable store instance.
810 */
811static int rtEfiVarStore_Flush(PRTEFIVARSTORE pThis)
812{
813 int rc = VINF_SUCCESS;
814 uint64_t offCur = pThis->offStoreData;
815
816 for (uint32_t i = 0; i < pThis->cVars && RT_SUCCESS(rc); i++)
817 {
818 PRTUTF16 pwszName = NULL;
819 size_t cwcLen = 0;
820 PRTEFIVAR pVar = &pThis->paVars[i];
821
822 if (!pVar->fDeleted)
823 {
824 rc = RTStrToUtf16Ex(pVar->pszName, RTSTR_MAX, &pwszName, 0, &cwcLen);
825 if (RT_SUCCESS(rc))
826 {
827 cwcLen++; /* Include the terminator. */
828
829 /* Read in the data of the variable if it exists. */
830 rc = rtEfiVarStore_VarReadData(pVar);
831 if (RT_SUCCESS(rc))
832 {
833 /* Write out the variable. */
834 EFI_AUTH_VAR_HEADER VarHdr;
835 size_t cbName = cwcLen * sizeof(RTUTF16);
836
837 VarHdr.u16StartId = RT_H2LE_U16(EFI_AUTH_VAR_HEADER_START);
838 VarHdr.bState = EFI_AUTH_VAR_HEADER_STATE_ADDED;
839 VarHdr.bRsvd = 0;
840 VarHdr.fAttr = RT_H2LE_U32(pVar->fAttr);
841 VarHdr.cMonotonic = RT_H2LE_U64(pVar->cMonotonic);
842 VarHdr.idPubKey = RT_H2LE_U32(pVar->idPubKey);
843 VarHdr.cbName = RT_H2LE_U32((uint32_t)cbName);
844 VarHdr.cbData = RT_H2LE_U32(pVar->cbData);
845 RTEfiGuidFromUuid(&VarHdr.GuidVendor, &pVar->Uuid);
846 memcpy(&VarHdr.Timestamp, &pVar->EfiTimestamp, sizeof(pVar->EfiTimestamp));
847
848 rc = RTVfsFileWriteAt(pThis->hVfsBacking, offCur, &VarHdr, sizeof(VarHdr), NULL);
849 if (RT_SUCCESS(rc))
850 rc = RTVfsFileWriteAt(pThis->hVfsBacking, offCur + sizeof(VarHdr), pwszName, cbName, NULL);
851 if (RT_SUCCESS(rc))
852 rc = RTVfsFileWriteAt(pThis->hVfsBacking, offCur + sizeof(VarHdr) + cbName, pVar->pvData, pVar->cbData, NULL);
853 if (RT_SUCCESS(rc))
854 {
855 offCur += sizeof(VarHdr) + cbName + pVar->cbData;
856 uint64_t offCurAligned = RT_ALIGN_64(offCur, sizeof(uint32_t));
857 if (offCurAligned > offCur)
858 {
859 /* Should be at most 3 bytes to align the next variable to a 32bit boundary. */
860 Assert(offCurAligned - offCur <= 3);
861 uint8_t abFill[3] = { 0xff };
862 rc = RTVfsFileWriteAt(pThis->hVfsBacking, offCur, &abFill[0], offCurAligned - offCur, NULL);
863 }
864
865 offCur = offCurAligned;
866 }
867 }
868
869 RTUtf16Free(pwszName);
870 }
871 }
872 }
873
874 if (RT_SUCCESS(rc))
875 {
876 /* Fill the remainder with 0xff as it would be the case for a real NAND flash device. */
877 uint8_t abFF[512];
878 memset(&abFF[0], 0xff, sizeof(abFF));
879
880 uint64_t offStart = offCur;
881 uint64_t offEnd = pThis->offStoreData + pThis->cbVarStore;
882 while ( offStart < offEnd
883 && RT_SUCCESS(rc))
884 {
885 size_t cbThisWrite = RT_MIN(sizeof(abFF), offEnd - offStart);
886 rc = RTVfsFileWriteAt(pThis->hVfsBacking, offStart, &abFF[0], cbThisWrite, NULL);
887 offStart += cbThisWrite;
888 }
889 }
890
891 return rc;
892}
893
894
895/**
896 * Tries to find a variable with the given name.
897 *
898 * @returns Pointer to the variable if found or NULL otherwise.
899 * @param pThis The variable store instance.
900 * @param pszName Name of the variable to look for.
901 * @param pidVar Where to store the index of the variable, optional.
902 */
903static PRTEFIVAR rtEfiVarStore_VarGet(PRTEFIVARSTORE pThis, const char *pszName, uint32_t *pidVar)
904{
905 for (uint32_t i = 0; i < pThis->cVars; i++)
906 if ( !pThis->paVars[i].fDeleted
907 && !strcmp(pszName, pThis->paVars[i].pszName))
908 {
909 if (pidVar)
910 *pidVar = i;
911 return &pThis->paVars[i];
912 }
913
914 return NULL;
915}
916
917
918/**
919 * Maybe grows the array of variables to hold more entries.
920 *
921 * @returns IPRT status code.
922 * @param pThis The variable store instance.
923 */
924static int rtEfiVarStore_VarMaybeGrowEntries(PRTEFIVARSTORE pThis)
925{
926 if (pThis->cVars == pThis->cVarsMax)
927 {
928 /* Grow the variable array. */
929 uint32_t cVarsMaxNew = pThis->cVarsMax + 10;
930 PRTEFIVAR paVarsNew = (PRTEFIVAR)RTMemRealloc(pThis->paVars, cVarsMaxNew * sizeof(RTEFIVAR));
931 if (!paVarsNew)
932 return VERR_NO_MEMORY;
933
934 pThis->paVars = paVarsNew;
935 pThis->cVarsMax = cVarsMaxNew;
936 }
937
938 return VINF_SUCCESS;
939}
940
941
942/**
943 * Add a variable with the given name.
944 *
945 * @returns Pointer to the entry or NULL if out of memory.
946 * @param pThis The variable store instance.
947 * @param pszName Name of the variable to add.
948 * @param pUuid The UUID of the variable owner.
949 * @param pidVar Where to store the variable index on success, optional
950 */
951static PRTEFIVAR rtEfiVarStore_VarAdd(PRTEFIVARSTORE pThis, const char *pszName, PCRTUUID pUuid, uint32_t *pidVar)
952{
953 Assert(!rtEfiVarStore_VarGet(pThis, pszName, NULL));
954
955 int rc = rtEfiVarStore_VarMaybeGrowEntries(pThis);
956 if (RT_SUCCESS(rc))
957 {
958 PRTEFIVAR pVar = &pThis->paVars[pThis->cVars];
959 RT_ZERO(*pVar);
960
961 pVar->pszName = RTStrDup(pszName);
962 if (pVar->pszName)
963 {
964 pVar->pVarStore = pThis;
965 pVar->offVarData = 0;
966 pVar->fDeleted = false;
967 pVar->Uuid = *pUuid;
968 RTTimeNow(&pVar->Time);
969
970 rc = rtEfiVarStore_AddVarByGuid(pThis, pUuid, pThis->cVars);
971 AssertRC(rc); /** @todo */
972
973 if (pidVar)
974 *pidVar = pThis->cVars;
975 pThis->cVars++;
976 return pVar;
977 }
978 }
979
980 return NULL;
981}
982
983
984/**
985 * Delete the given variable.
986 *
987 * @returns IPRT status code.
988 * @param pThis The variable store instance.
989 * @param pVar The variable.
990 */
991static int rtEfiVarStore_VarDel(PRTEFIVARSTORE pThis, PRTEFIVAR pVar)
992{
993 pVar->fDeleted = true;
994 if (pVar->pvData)
995 RTMemFree(pVar->pvData);
996 pVar->pvData = NULL;
997 pThis->cbVarData -= sizeof(EFI_AUTH_VAR_HEADER) + pVar->cbData;
998 /** @todo Delete from GUID entry. */
999 return VINF_SUCCESS;
1000}
1001
1002
1003/**
1004 * Delete the variable with the given index.
1005 *
1006 * @returns IPRT status code.
1007 * @param pThis The variable store instance.
1008 * @param idVar The variable index.
1009 */
1010static int rtEfiVarStore_VarDelById(PRTEFIVARSTORE pThis, uint32_t idVar)
1011{
1012 return rtEfiVarStore_VarDel(pThis, &pThis->paVars[idVar]);
1013}
1014
1015
1016/**
1017 * Delete the variable with the given name.
1018 *
1019 * @returns IPRT status code.
1020 * @param pThis The variable store instance.
1021 * @param pszName Name of the variable to delete.
1022 */
1023static int rtEfiVarStore_VarDelByName(PRTEFIVARSTORE pThis, const char *pszName)
1024{
1025 PRTEFIVAR pVar = rtEfiVarStore_VarGet(pThis, pszName, NULL);
1026 if (pVar)
1027 return rtEfiVarStore_VarDel(pThis, pVar);
1028
1029 return VERR_FILE_NOT_FOUND;
1030}
1031
1032
1033/*
1034 *
1035 * File operations.
1036 * File operations.
1037 * File operations.
1038 *
1039 */
1040
1041/**
1042 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1043 */
1044static DECLCALLBACK(int) rtEfiVarStoreFile_Close(void *pvThis)
1045{
1046 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1047 LogFlow(("rtEfiVarStoreFile_Close(%p/%p)\n", pThis, pThis->pVar));
1048 RT_NOREF(pThis);
1049 return VINF_SUCCESS;
1050}
1051
1052
1053/**
1054 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1055 */
1056static DECLCALLBACK(int) rtEfiVarStoreFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1057{
1058 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1059 uint64_t cbObject = pThis->pEntry->cbObject > 0
1060 ? pThis->pEntry->cbObject
1061 : pThis->pVar->cbData;
1062 return rtEfiVarStore_QueryInfo(cbObject, false /*fIsDir*/, &pThis->pVar->Time, pObjInfo, enmAddAttr);
1063}
1064
1065
1066/**
1067 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1068 */
1069static DECLCALLBACK(int) rtEfiVarStoreFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1070{
1071 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1072 PRTEFIVAR pVar = pThis->pVar;
1073 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1074 RT_NOREF(fBlocking);
1075
1076 if (off == -1)
1077 off = pThis->offFile;
1078 else
1079 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1080
1081 int rc;
1082 if (pThis->pEntry->cbObject)
1083 rc = rtEfiVarStoreFile_ReadMem(pThis, (const uint8_t *)pVar + pThis->pEntry->offObject, pThis->pEntry->cbObject, off, pSgBuf, pcbRead);
1084 else
1085 {
1086 /* Data section. */
1087 if (!pVar->offVarData)
1088 rc = rtEfiVarStoreFile_ReadMem(pThis, pVar->pvData, pVar->cbData, off, pSgBuf, pcbRead);
1089 else
1090 rc = rtEfiVarStoreFile_ReadFile(pThis, pVar->offVarData, pVar->cbData, off, pSgBuf, pcbRead);
1091 }
1092
1093 return rc;
1094}
1095
1096
1097/**
1098 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1099 */
1100static DECLCALLBACK(int) rtEfiVarStoreFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1101{
1102 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1103 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
1104 PRTEFIVAR pVar = pThis->pVar;
1105 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1106 RT_NOREF(fBlocking);
1107
1108 if (pVarStore->fMntFlags & RTVFSMNT_F_READ_ONLY)
1109 return VERR_WRITE_PROTECT;
1110
1111 if (off == -1)
1112 off = pThis->offFile;
1113 else
1114 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1115
1116 int rc;
1117 if (pThis->pEntry->cbObject) /* These can't grow. */
1118 rc = rtEfiVarStoreFile_WriteMem(pThis, (uint8_t *)pVar + pThis->pEntry->offObject, pThis->pEntry->cbObject,
1119 off, pSgBuf, pcbWritten);
1120 else
1121 {
1122 /* Writing data section. */
1123 rc = rtEfiVarStore_VarReadData(pVar);
1124 if (RT_SUCCESS(rc))
1125 {
1126 if (off + pSgBuf->paSegs[0].cbSeg > pVar->cbData)
1127 rc = rtEfiVarStore_VarEnsureDataSz(pVar, off + pSgBuf->paSegs[0].cbSeg);
1128 if (RT_SUCCESS(rc))
1129 rc = rtEfiVarStoreFile_WriteMem(pThis, pVar->pvData, pVar->cbData, off, pSgBuf, pcbWritten);
1130 }
1131 }
1132
1133 return rc;
1134}
1135
1136
1137/**
1138 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1139 */
1140static DECLCALLBACK(int) rtEfiVarStoreFile_Flush(void *pvThis)
1141{
1142 RT_NOREF(pvThis);
1143 return VINF_SUCCESS;
1144}
1145
1146
1147/**
1148 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1149 */
1150static DECLCALLBACK(int) rtEfiVarStoreFile_Tell(void *pvThis, PRTFOFF poffActual)
1151{
1152 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1153 *poffActual = pThis->offFile;
1154 return VINF_SUCCESS;
1155}
1156
1157
1158/**
1159 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1160 */
1161static DECLCALLBACK(int) rtEfiVarStoreFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1162{
1163 RT_NOREF(pvThis, fMode, fMask);
1164 return VERR_WRITE_PROTECT;
1165}
1166
1167
1168/**
1169 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1170 */
1171static DECLCALLBACK(int) rtEfiVarStoreFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1172 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1173{
1174 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1175 return VERR_WRITE_PROTECT;
1176}
1177
1178
1179/**
1180 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1181 */
1182static DECLCALLBACK(int) rtEfiVarStoreFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1183{
1184 RT_NOREF(pvThis, uid, gid);
1185 return VERR_WRITE_PROTECT;
1186}
1187
1188
1189/**
1190 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1191 */
1192static DECLCALLBACK(int) rtEfiVarStoreFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1193{
1194 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1195 RTFOFF offNew;
1196 switch (uMethod)
1197 {
1198 case RTFILE_SEEK_BEGIN:
1199 offNew = offSeek;
1200 break;
1201 case RTFILE_SEEK_END:
1202 offNew = pThis->pVar->cbData + offSeek;
1203 break;
1204 case RTFILE_SEEK_CURRENT:
1205 offNew = (RTFOFF)pThis->offFile + offSeek;
1206 break;
1207 default:
1208 return VERR_INVALID_PARAMETER;
1209 }
1210 if (offNew >= 0)
1211 {
1212 pThis->offFile = offNew;
1213 *poffActual = offNew;
1214 return VINF_SUCCESS;
1215 }
1216 return VERR_NEGATIVE_SEEK;
1217}
1218
1219
1220/**
1221 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1222 */
1223static DECLCALLBACK(int) rtEfiVarStoreFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1224{
1225 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1226 if (pThis->pEntry->cbObject)
1227 *pcbFile = pThis->pEntry->cbObject;
1228 else
1229 *pcbFile = (uint64_t)pThis->pVar->cbData;
1230 return VINF_SUCCESS;
1231}
1232
1233
1234/**
1235 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
1236 */
1237static DECLCALLBACK(int) rtEfiVarStoreFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
1238{
1239 PRTEFIVARFILE pThis = (PRTEFIVARFILE)pvThis;
1240 PRTEFIVAR pVar = pThis->pVar;
1241 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
1242
1243 RT_NOREF(fFlags);
1244
1245 if (pVarStore->fMntFlags & RTVFSMNT_F_READ_ONLY)
1246 return VERR_WRITE_PROTECT;
1247
1248 int rc = rtEfiVarStore_VarReadData(pVar);
1249 if (RT_SUCCESS(rc))
1250 rc = rtEfiVarStore_VarEnsureDataSz(pVar, cbFile);
1251
1252 return rc;
1253}
1254
1255
1256/**
1257 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
1258 */
1259static DECLCALLBACK(int) rtEfiVarStoreFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
1260{
1261 RT_NOREF(pvThis);
1262 *pcbMax = UINT32_MAX;
1263 return VINF_SUCCESS;
1264}
1265
1266
1267/**
1268 * EFI variable store file operations.
1269 */
1270static const RTVFSFILEOPS g_rtEfiVarStoreFileOps =
1271{
1272 { /* Stream */
1273 { /* Obj */
1274 RTVFSOBJOPS_VERSION,
1275 RTVFSOBJTYPE_FILE,
1276 "EfiVarStore File",
1277 rtEfiVarStoreFile_Close,
1278 rtEfiVarStoreFile_QueryInfo,
1279 NULL,
1280 RTVFSOBJOPS_VERSION
1281 },
1282 RTVFSIOSTREAMOPS_VERSION,
1283 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1284 rtEfiVarStoreFile_Read,
1285 rtEfiVarStoreFile_Write,
1286 rtEfiVarStoreFile_Flush,
1287 NULL /*PollOne*/,
1288 rtEfiVarStoreFile_Tell,
1289 NULL /*pfnSkip*/,
1290 NULL /*pfnZeroFill*/,
1291 RTVFSIOSTREAMOPS_VERSION,
1292 },
1293 RTVFSFILEOPS_VERSION,
1294 0,
1295 { /* ObjSet */
1296 RTVFSOBJSETOPS_VERSION,
1297 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1298 rtEfiVarStoreFile_SetMode,
1299 rtEfiVarStoreFile_SetTimes,
1300 rtEfiVarStoreFile_SetOwner,
1301 RTVFSOBJSETOPS_VERSION
1302 },
1303 rtEfiVarStoreFile_Seek,
1304 rtEfiVarStoreFile_QuerySize,
1305 rtEfiVarStoreFile_SetSize,
1306 rtEfiVarStoreFile_QueryMaxSize,
1307 RTVFSFILEOPS_VERSION
1308};
1309
1310
1311/**
1312 * Creates a new VFS file from the given regular file inode.
1313 *
1314 * @returns IPRT status code.
1315 * @param pThis The ext volume instance.
1316 * @param fOpen Open flags passed.
1317 * @param pVar The variable this file accesses.
1318 * @param pEntry File type entry.
1319 * @param phVfsFile Where to store the VFS file handle on success.
1320 * @param pErrInfo Where to record additional error information on error, optional.
1321 */
1322static int rtEfiVarStore_NewFile(PRTEFIVARSTORE pThis, uint64_t fOpen, PRTEFIVAR pVar,
1323 PCRTEFIVARSTOREFILERAWENTRY pEntry, PRTVFSOBJ phVfsObj)
1324{
1325 RTVFSFILE hVfsFile;
1326 PRTEFIVARFILE pNewFile;
1327 int rc = RTVfsNewFile(&g_rtEfiVarStoreFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
1328 &hVfsFile, (void **)&pNewFile);
1329 if (RT_SUCCESS(rc))
1330 {
1331 pNewFile->pEntry = pEntry;
1332 pNewFile->pVarStore = pThis;
1333 pNewFile->pVar = pVar;
1334 pNewFile->offFile = 0;
1335
1336 *phVfsObj = RTVfsObjFromFile(hVfsFile);
1337 RTVfsFileRelease(hVfsFile);
1338 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
1339 }
1340
1341 return rc;
1342}
1343
1344
1345
1346/*
1347 *
1348 * Directory instance methods
1349 * Directory instance methods
1350 * Directory instance methods
1351 *
1352 */
1353
1354/**
1355 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1356 */
1357static DECLCALLBACK(int) rtEfiVarStoreDir_Close(void *pvThis)
1358{
1359 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1360 LogFlowFunc(("pThis=%p\n", pThis));
1361 pThis->pVarStore = NULL;
1362 return VINF_SUCCESS;
1363}
1364
1365
1366/**
1367 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1368 */
1369static DECLCALLBACK(int) rtEfiVarStoreDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1370{
1371 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1372 LogFlowFunc(("\n"));
1373 return rtEfiVarStore_QueryInfo(1, true /*fIsDir*/, &pThis->Time, pObjInfo, enmAddAttr);
1374}
1375
1376
1377/**
1378 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1379 */
1380static DECLCALLBACK(int) rtEfiVarStoreDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1381{
1382 LogFlowFunc(("\n"));
1383 RT_NOREF(pvThis, fMode, fMask);
1384 return VERR_WRITE_PROTECT;
1385}
1386
1387
1388/**
1389 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1390 */
1391static DECLCALLBACK(int) rtEfiVarStoreDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1392 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1393{
1394 LogFlowFunc(("\n"));
1395 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1396 return VERR_WRITE_PROTECT;
1397}
1398
1399
1400/**
1401 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1402 */
1403static DECLCALLBACK(int) rtEfiVarStoreDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1404{
1405 LogFlowFunc(("\n"));
1406 RT_NOREF(pvThis, uid, gid);
1407 return VERR_WRITE_PROTECT;
1408}
1409
1410
1411/**
1412 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
1413 */
1414static DECLCALLBACK(int) rtEfiVarStoreDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
1415 uint32_t fFlags, PRTVFSOBJ phVfsObj)
1416{
1417 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
1418 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1419 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
1420 int rc = VINF_SUCCESS;
1421
1422 /*
1423 * Special cases '.' and '.'
1424 */
1425 if (pszEntry[0] == '.')
1426 {
1427 RTEFIVARSTOREDIRTYPE enmDirTypeNew = RTEFIVARSTOREDIRTYPE_INVALID;
1428 if (pszEntry[1] == '\0')
1429 enmDirTypeNew = pThis->pEntry->enmType;
1430 else if (pszEntry[1] == '.' && pszEntry[2] == '\0')
1431 enmDirTypeNew = pThis->pEntry->enmParentType;
1432
1433 if (enmDirTypeNew != RTEFIVARSTOREDIRTYPE_INVALID)
1434 {
1435 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
1436 {
1437 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
1438 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
1439 rc = rtEfiVarStore_NewDirByType(pVarStore, enmDirTypeNew, NULL /*pGuid*/, 0 /*idVar*/, phVfsObj);
1440 else
1441 rc = VERR_ACCESS_DENIED;
1442 }
1443 else
1444 rc = VERR_IS_A_DIRECTORY;
1445 return rc;
1446 }
1447 }
1448
1449 /*
1450 * We can create or replace in certain directories.
1451 */
1452 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
1453 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
1454 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
1455 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
1456 { /* likely */ }
1457 else
1458 return VERR_WRITE_PROTECT;
1459
1460 switch (pThis->pEntry->enmType)
1461 {
1462 case RTEFIVARSTOREDIRTYPE_ROOT:
1463 {
1464 if (!strcmp(pszEntry, "by-name"))
1465 return rtEfiVarStore_NewDirByType(pVarStore, RTEFIVARSTOREDIRTYPE_BY_NAME,
1466 NULL /*pGuid*/, 0 /*idVar*/, phVfsObj);
1467 else if (!strcmp(pszEntry, "by-uuid"))
1468 return rtEfiVarStore_NewDirByType(pVarStore, RTEFIVARSTOREDIRTYPE_BY_GUID,
1469 NULL /*pGuid*/, 0 /*idVar*/, phVfsObj);
1470 else if (!strcmp(pszEntry, "raw"))
1471 return rtEfiVarStore_NewDirByType(pVarStore, RTEFIVARSTOREDIRTYPE_RAW,
1472 NULL /*pGuid*/, 0 /*idVar*/, phVfsObj);
1473 else
1474 rc = VERR_FILE_NOT_FOUND;
1475 break;
1476 }
1477 case RTEFIVARSTOREDIRTYPE_GUID: /** @todo This looks through all variables, not only the ones with the GUID. */
1478 case RTEFIVARSTOREDIRTYPE_BY_NAME:
1479 case RTEFIVARSTOREDIRTYPE_RAW:
1480 {
1481 /* Look for the name. */
1482 uint32_t idVar = 0;
1483 PRTEFIVAR pVar = rtEfiVarStore_VarGet(pVarStore, pszEntry, &idVar);
1484 if ( !pVar
1485 && ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
1486 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
1487 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE))
1488 {
1489 if (pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_GUID)
1490 pVar = rtEfiVarStore_VarAdd(pVarStore, pszEntry, &pThis->pGuid->Uuid, &idVar);
1491 else
1492 {
1493 RTUUID UuidNull;
1494 RTUuidClear(&UuidNull);
1495 pVar = rtEfiVarStore_VarAdd(pVarStore, pszEntry, &UuidNull, &idVar);
1496 }
1497
1498 if (!pVar)
1499 {
1500 rc = VERR_NO_MEMORY;
1501 break;
1502 }
1503 }
1504
1505 if (pVar)
1506 {
1507 if (pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_RAW)
1508 return rtEfiVarStore_NewDirByType(pVarStore, RTEFIVARSTOREDIRTYPE_RAW_ENTRY,
1509 NULL /*pGuid*/, idVar, phVfsObj);
1510 else
1511 return rtEfiVarStore_NewFile(pVarStore, fOpen, pVar,
1512 &g_aRawFiles[RTEFIVARSTORE_FILE_ENTRY_DATA], phVfsObj);
1513 }
1514
1515 rc = VERR_FILE_NOT_FOUND;
1516 break;
1517 }
1518 case RTEFIVARSTOREDIRTYPE_BY_GUID:
1519 {
1520 /* Look for the name. */
1521 for (uint32_t i = 0; i < pVarStore->cGuids; i++)
1522 {
1523 PRTEFIGUID pGuid = &pVarStore->paGuids[i];
1524 char szUuid[RTUUID_STR_LENGTH];
1525 rc = RTUuidToStr(&pGuid->Uuid, szUuid, sizeof(szUuid));
1526 AssertRC(rc);
1527
1528 if (!strcmp(pszEntry, szUuid))
1529 return rtEfiVarStore_NewDirByType(pVarStore, RTEFIVARSTOREDIRTYPE_GUID,
1530 pGuid, 0 /*idVar*/, phVfsObj);
1531 }
1532
1533 rc = VERR_FILE_NOT_FOUND;
1534 break;
1535 }
1536 case RTEFIVARSTOREDIRTYPE_RAW_ENTRY:
1537 {
1538 /* Look for the name. */
1539 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRawFiles); i++)
1540 if (!strcmp(pszEntry, g_aRawFiles[i].pszName))
1541 return rtEfiVarStore_NewFile(pVarStore, fOpen, &pVarStore->paVars[pThis->idVar],
1542 &g_aRawFiles[i], phVfsObj);
1543
1544 rc = VERR_FILE_NOT_FOUND;
1545 break;
1546 }
1547 case RTEFIVARSTOREDIRTYPE_INVALID:
1548 default:
1549 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
1550 }
1551
1552 LogFlow(("rtEfiVarStoreDir_Open(%s): returns %Rrc\n", pszEntry, rc));
1553 return rc;
1554}
1555
1556
1557/**
1558 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
1559 */
1560static DECLCALLBACK(int) rtEfiVarStoreDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
1561{
1562 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1563 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
1564 LogFlowFunc(("\n"));
1565
1566 RT_NOREF(fMode, phVfsDir);
1567
1568 if (pVarStore->fMntFlags & RTVFSMNT_F_READ_ONLY)
1569 return VERR_WRITE_PROTECT;
1570
1571 /* We support creating directories only for GUIDs and RAW variable entries. */
1572 int rc = VINF_SUCCESS;
1573 if (pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_BY_GUID)
1574 {
1575 RTUUID Uuid;
1576 rc = RTUuidFromStr(&Uuid, pszSubDir);
1577 if (RT_FAILURE(rc))
1578 return VERR_NOT_SUPPORTED;
1579
1580 PRTEFIGUID pGuid = rtEfiVarStore_GetGuid(pVarStore, &Uuid);
1581 if (pGuid)
1582 return VERR_ALREADY_EXISTS;
1583
1584 pGuid = rtEfiVarStore_AddGuid(pVarStore, &Uuid);
1585 if (!pGuid)
1586 return VERR_NO_MEMORY;
1587 }
1588 else if (pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_RAW)
1589 {
1590 PRTEFIVAR pVar = rtEfiVarStore_VarGet(pVarStore, pszSubDir, NULL /*pidVar*/);
1591 if (!pVar)
1592 {
1593 if (sizeof(EFI_AUTH_VAR_HEADER) < pVarStore->cbVarStore - pVarStore->cbVarData)
1594 {
1595 uint32_t idVar = 0;
1596 RTUUID UuidNull;
1597 RTUuidClear(&UuidNull);
1598
1599 pVar = rtEfiVarStore_VarAdd(pVarStore, pszSubDir, &UuidNull, &idVar);
1600 if (pVar)
1601 pVarStore->cbVarData += sizeof(EFI_AUTH_VAR_HEADER);
1602 else
1603 rc = VERR_NO_MEMORY;
1604 }
1605 else
1606 rc = VERR_DISK_FULL;
1607 }
1608 else
1609 rc = VERR_ALREADY_EXISTS;
1610 }
1611 else
1612 rc = VERR_NOT_SUPPORTED;
1613
1614 return rc;
1615}
1616
1617
1618/**
1619 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
1620 */
1621static DECLCALLBACK(int) rtEfiVarStoreDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
1622{
1623 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
1624 LogFlowFunc(("\n"));
1625 return VERR_NOT_SUPPORTED;
1626}
1627
1628
1629/**
1630 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
1631 */
1632static DECLCALLBACK(int) rtEfiVarStoreDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
1633 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
1634{
1635 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
1636 LogFlowFunc(("\n"));
1637 return VERR_WRITE_PROTECT;
1638}
1639
1640
1641/**
1642 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
1643 */
1644static DECLCALLBACK(int) rtEfiVarStoreDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
1645{
1646 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1647 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
1648 LogFlowFunc(("\n"));
1649
1650 RT_NOREF(fType);
1651
1652 if (pVarStore->fMntFlags & RTVFSMNT_F_READ_ONLY)
1653 return VERR_WRITE_PROTECT;
1654
1655 if ( pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_RAW
1656 || pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_BY_NAME
1657 || pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_GUID)
1658 return rtEfiVarStore_VarDelByName(pVarStore, pszEntry);
1659 else if (pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_BY_GUID)
1660 {
1661 /* Look for the name. */
1662 for (uint32_t i = 0; i < pVarStore->cGuids; i++)
1663 {
1664 PRTEFIGUID pGuid = &pVarStore->paGuids[i];
1665 char szUuid[RTUUID_STR_LENGTH];
1666 int rc = RTUuidToStr(&pGuid->Uuid, szUuid, sizeof(szUuid));
1667 AssertRC(rc); RT_NOREF(rc);
1668
1669 if (!strcmp(pszEntry, szUuid))
1670 {
1671 for (uint32_t iVar = 0; iVar < pGuid->cVars; iVar++)
1672 rtEfiVarStore_VarDelById(pVarStore, pGuid->paidxVars[iVar]);
1673
1674 if (pGuid->paidxVars)
1675 RTMemFree(pGuid->paidxVars);
1676 pGuid->paidxVars = NULL;
1677 pGuid->cVars = 0;
1678 pGuid->cVarsMax = 0;
1679 RTUuidClear(&pGuid->Uuid);
1680 return VINF_SUCCESS;
1681 }
1682 }
1683
1684 return VERR_FILE_NOT_FOUND;
1685 }
1686
1687 return VERR_NOT_SUPPORTED;
1688}
1689
1690
1691/**
1692 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
1693 */
1694static DECLCALLBACK(int) rtEfiVarStoreDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
1695{
1696 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
1697 LogFlowFunc(("\n"));
1698 return VERR_WRITE_PROTECT;
1699}
1700
1701
1702/**
1703 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
1704 */
1705static DECLCALLBACK(int) rtEfiVarStoreDir_RewindDir(void *pvThis)
1706{
1707 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1708 LogFlowFunc(("\n"));
1709
1710 pThis->idxNext = 0;
1711 return VINF_SUCCESS;
1712}
1713
1714
1715/**
1716 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1717 */
1718static DECLCALLBACK(int) rtEfiVarStoreDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1719 RTFSOBJATTRADD enmAddAttr)
1720{
1721 PRTEFIVARSTOREDIR pThis = (PRTEFIVARSTOREDIR)pvThis;
1722 PRTEFIVARSTORE pVarStore = pThis->pVarStore;
1723 LogFlowFunc(("\n"));
1724
1725 if (pThis->fNoMoreFiles)
1726 return VERR_NO_MORE_FILES;
1727
1728 int rc = VINF_SUCCESS;
1729 char aszUuid[RTUUID_STR_LENGTH];
1730 const char *pszName = NULL;
1731 size_t cbName = 0;
1732 uint64_t cbObject = 0;
1733 bool fIsDir = false;
1734 bool fNoMoreFiles = false;
1735 RTTIMESPEC Time;
1736 PCRTTIMESPEC pTimeSpec = &Time;
1737 RTTimeNow(&Time);
1738
1739 switch (pThis->pEntry->enmType)
1740 {
1741 case RTEFIVARSTOREDIRTYPE_ROOT:
1742 {
1743 if (pThis->idxNext == 0)
1744 {
1745 pszName = "by-name";
1746 cbName = sizeof("by-name");
1747 cbObject = 1;
1748 fIsDir = true;
1749 }
1750 else if (pThis->idxNext == 1)
1751 {
1752 pszName = "by-uuid";
1753 cbName = sizeof("by-uuid");
1754 cbObject = 1;
1755 fIsDir = true;
1756 }
1757 else if (pThis->idxNext == 2)
1758 {
1759 pszName = "raw";
1760 cbName = sizeof("raw");
1761 cbObject = 1;
1762 fIsDir = true;
1763 fNoMoreFiles = true;
1764 }
1765 break;
1766 }
1767 case RTEFIVARSTOREDIRTYPE_BY_NAME:
1768 case RTEFIVARSTOREDIRTYPE_RAW:
1769 {
1770 PRTEFIVAR pVar = &pVarStore->paVars[pThis->idxNext];
1771 if (pThis->idxNext + 1 == pVarStore->cVars)
1772 fNoMoreFiles = true;
1773 pszName = pVar->pszName;
1774 cbName = strlen(pszName) + 1;
1775 cbObject = pVar->cbData;
1776 pTimeSpec = &pVar->Time;
1777 if (pThis->pEntry->enmType == RTEFIVARSTOREDIRTYPE_RAW)
1778 fIsDir = true;
1779 break;
1780 }
1781 case RTEFIVARSTOREDIRTYPE_BY_GUID:
1782 {
1783 PRTEFIGUID pGuid = &pVarStore->paGuids[pThis->idxNext];
1784 if (pThis->idxNext + 1 == pVarStore->cGuids)
1785 fNoMoreFiles = true;
1786 pszName = &aszUuid[0];
1787 cbName = sizeof(aszUuid);
1788 cbObject = 1;
1789 rc = RTUuidToStr(&pGuid->Uuid, &aszUuid[0], cbName);
1790 AssertRC(rc);
1791 break;
1792 }
1793 case RTEFIVARSTOREDIRTYPE_GUID:
1794 {
1795 PRTEFIGUID pGuid = pThis->pGuid;
1796 uint32_t idVar = pGuid->paidxVars[pThis->idxNext];
1797 PRTEFIVAR pVar = &pVarStore->paVars[idVar];
1798 if (pThis->idxNext + 1 == pGuid->cVars)
1799 fNoMoreFiles = true;
1800 pszName = pVar->pszName;
1801 cbName = strlen(pszName) + 1;
1802 cbObject = pVar->cbData;
1803 pTimeSpec = &pVar->Time;
1804 break;
1805 }
1806 case RTEFIVARSTOREDIRTYPE_RAW_ENTRY:
1807 {
1808 PCRTEFIVARSTOREFILERAWENTRY pEntry = &g_aRawFiles[pThis->idxNext];
1809 PRTEFIVAR pVar = &pVarStore->paVars[pThis->idVar];
1810
1811 if (pThis->idxNext + 1 == RT_ELEMENTS(g_aRawFiles))
1812 fNoMoreFiles = true;
1813 pszName = pEntry->pszName;
1814 cbName = strlen(pszName) + 1;
1815 cbObject = pEntry->cbObject;
1816 if (!cbObject)
1817 cbObject = pVar->cbData;
1818 pTimeSpec = &pVar->Time;
1819 break;
1820 }
1821 case RTEFIVARSTOREDIRTYPE_INVALID:
1822 default:
1823 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
1824 }
1825
1826 if (cbName <= 255)
1827 {
1828 size_t const cbDirEntry = *pcbDirEntry;
1829
1830 *pcbDirEntry = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cbName + 2]);
1831 if (*pcbDirEntry <= cbDirEntry)
1832 {
1833 memcpy(&pDirEntry->szName[0], pszName, cbName);
1834 pDirEntry->szName[cbName] = '\0';
1835 pDirEntry->cbName = (uint16_t)cbName;
1836 rc = rtEfiVarStore_QueryInfo(cbObject, fIsDir, &Time, &pDirEntry->Info, enmAddAttr);
1837 if (RT_SUCCESS(rc))
1838 {
1839 pThis->fNoMoreFiles = fNoMoreFiles;
1840 pThis->idxNext++;
1841 return VINF_SUCCESS;
1842 }
1843 }
1844 else
1845 rc = VERR_BUFFER_OVERFLOW;
1846 }
1847 else
1848 rc = VERR_FILENAME_TOO_LONG;
1849 return rc;
1850}
1851
1852
1853/**
1854 * EFI variable store directory operations.
1855 */
1856static const RTVFSDIROPS g_rtEfiVarStoreDirOps =
1857{
1858 { /* Obj */
1859 RTVFSOBJOPS_VERSION,
1860 RTVFSOBJTYPE_DIR,
1861 "EfiVarStore Dir",
1862 rtEfiVarStoreDir_Close,
1863 rtEfiVarStoreDir_QueryInfo,
1864 NULL,
1865 RTVFSOBJOPS_VERSION
1866 },
1867 RTVFSDIROPS_VERSION,
1868 0,
1869 { /* ObjSet */
1870 RTVFSOBJSETOPS_VERSION,
1871 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
1872 rtEfiVarStoreDir_SetMode,
1873 rtEfiVarStoreDir_SetTimes,
1874 rtEfiVarStoreDir_SetOwner,
1875 RTVFSOBJSETOPS_VERSION
1876 },
1877 rtEfiVarStoreDir_Open,
1878 NULL /* pfnFollowAbsoluteSymlink */,
1879 NULL /* pfnOpenFile */,
1880 NULL /* pfnOpenDir */,
1881 rtEfiVarStoreDir_CreateDir,
1882 rtEfiVarStoreDir_OpenSymlink,
1883 rtEfiVarStoreDir_CreateSymlink,
1884 NULL /* pfnQueryEntryInfo */,
1885 rtEfiVarStoreDir_UnlinkEntry,
1886 rtEfiVarStoreDir_RenameEntry,
1887 rtEfiVarStoreDir_RewindDir,
1888 rtEfiVarStoreDir_ReadDir,
1889 RTVFSDIROPS_VERSION,
1890};
1891
1892
1893static int rtEfiVarStore_NewDirByType(PRTEFIVARSTORE pThis, RTEFIVARSTOREDIRTYPE enmDirType,
1894 PRTEFIGUID pGuid, uint32_t idVar, PRTVFSOBJ phVfsObj)
1895{
1896 RTVFSDIR hVfsDir;
1897 PRTEFIVARSTOREDIR pDir;
1898 int rc = RTVfsNewDir(&g_rtEfiVarStoreDirOps, sizeof(*pDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
1899 &hVfsDir, (void **)&pDir);
1900 if (RT_SUCCESS(rc))
1901 {
1902 PCRTEFIVARSTOREDIRENTRY pEntry = NULL;
1903
1904 for (uint32_t i = 0; i < RT_ELEMENTS(g_aDirs); i++)
1905 if (g_aDirs[i].enmType == enmDirType)
1906 {
1907 pEntry = &g_aDirs[i];
1908 break;
1909 }
1910
1911 AssertPtr(pEntry);
1912 pDir->idxNext = 0;
1913 pDir->pEntry = pEntry;
1914 pDir->pVarStore = pThis;
1915 pDir->pGuid = pGuid;
1916 pDir->idVar = idVar;
1917 RTTimeNow(&pDir->Time);
1918
1919 *phVfsObj = RTVfsObjFromDir(hVfsDir);
1920 RTVfsDirRelease(hVfsDir);
1921 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
1922 }
1923
1924 return rc;
1925}
1926
1927
1928/*
1929 *
1930 * Volume level code.
1931 * Volume level code.
1932 * Volume level code.
1933 *
1934 */
1935
1936/**
1937 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1938 */
1939static DECLCALLBACK(int) rtEfiVarStore_Close(void *pvThis)
1940{
1941 PRTEFIVARSTORE pThis = (PRTEFIVARSTORE)pvThis;
1942
1943 /* Write the variable store if in read/write mode. */
1944 if (!(pThis->fMntFlags & RTVFSMNT_F_READ_ONLY))
1945 {
1946 int rc = rtEfiVarStore_Flush(pThis);
1947 if (RT_FAILURE(rc))
1948 return rc;
1949 }
1950
1951 /*
1952 * Backing file and handles.
1953 */
1954 RTVfsFileRelease(pThis->hVfsBacking);
1955 pThis->hVfsBacking = NIL_RTVFSFILE;
1956 pThis->hVfsSelf = NIL_RTVFS;
1957 if (pThis->paVars)
1958 {
1959 for (uint32_t i = 0; i < pThis->cVars; i++)
1960 {
1961 RTStrFree(pThis->paVars[i].pszName);
1962 if (pThis->paVars[i].pvData)
1963 RTMemFree(pThis->paVars[i].pvData);
1964 }
1965
1966 RTMemFree(pThis->paVars);
1967 pThis->paVars = NULL;
1968 pThis->cVars = 0;
1969 pThis->cVarsMax = 0;
1970 }
1971
1972 if (pThis->paGuids)
1973 {
1974 for (uint32_t i = 0; i < pThis->cGuids; i++)
1975 {
1976 PRTEFIGUID pGuid = &pThis->paGuids[i];
1977
1978 if (pGuid->paidxVars)
1979 {
1980 RTMemFree(pGuid->paidxVars);
1981 pGuid->paidxVars = NULL;
1982 }
1983 }
1984
1985 RTMemFree(pThis->paGuids);
1986 pThis->paGuids = NULL;
1987 }
1988
1989 return VINF_SUCCESS;
1990}
1991
1992
1993/**
1994 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1995 */
1996static DECLCALLBACK(int) rtEfiVarStore_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1997{
1998 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1999 return VERR_WRONG_TYPE;
2000}
2001
2002
2003/**
2004 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
2005 */
2006static DECLCALLBACK(int) rtEfiVarStore_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
2007{
2008 PRTEFIVARSTORE pThis = (PRTEFIVARSTORE)pvThis;
2009 RTVFSOBJ hVfsObj;
2010 int rc = rtEfiVarStore_NewDirByType(pThis, RTEFIVARSTOREDIRTYPE_ROOT,
2011 NULL /*pGuid*/, 0 /*idVar*/, &hVfsObj);
2012 if (RT_SUCCESS(rc))
2013 {
2014 *phVfsDir = RTVfsObjToDir(hVfsObj);
2015 RTVfsObjRelease(hVfsObj);
2016 }
2017
2018 LogFlowFunc(("returns %Rrc\n", rc));
2019 return rc;
2020}
2021
2022
2023DECL_HIDDEN_CONST(const RTVFSOPS) g_rtEfiVarStoreOps =
2024{
2025 /* .Obj = */
2026 {
2027 /* .uVersion = */ RTVFSOBJOPS_VERSION,
2028 /* .enmType = */ RTVFSOBJTYPE_VFS,
2029 /* .pszName = */ "EfiVarStore",
2030 /* .pfnClose = */ rtEfiVarStore_Close,
2031 /* .pfnQueryInfo = */ rtEfiVarStore_QueryInfo,
2032 /* .pfnQueryInfoEx = */ NULL,
2033 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
2034 },
2035 /* .uVersion = */ RTVFSOPS_VERSION,
2036 /* .fFeatures = */ 0,
2037 /* .pfnOpenRoot = */ rtEfiVarStore_OpenRoot,
2038 /* .pfnQueryRangeState = */ NULL,
2039 /* .uEndMarker = */ RTVFSOPS_VERSION
2040};
2041
2042
2043/**
2044 * Validates the given firmware header.
2045 *
2046 * @returns true if the given header is considered valid, flse otherwise.
2047 * @param pThis The EFI variable store instance.
2048 * @param pFvHdr The firmware volume header to validate.
2049 * @param poffData The offset into the backing where the data area begins.
2050 * @param pErrInfo Where to return additional error info.
2051 */
2052static int rtEfiVarStoreFvHdr_Validate(PRTEFIVARSTORE pThis, PCEFI_FIRMWARE_VOLUME_HEADER pFvHdr, uint64_t *poffData,
2053 PRTERRINFO pErrInfo)
2054{
2055#ifdef LOG_ENABLED
2056 rtEfiVarStoreFvHdr_Log(pFvHdr);
2057#endif
2058
2059 EFI_GUID GuidNvData = EFI_VARSTORE_FILESYSTEM_GUID;
2060 if (memcmp(&pFvHdr->GuidFilesystem, &GuidNvData, sizeof(GuidNvData)))
2061 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Filesystem GUID doesn't indicate a variable store");
2062 if (RT_LE2H_U64(pFvHdr->cbFv) > pThis->cbBacking)
2063 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Firmware volume length exceeds size of backing storage (truncated file?)");
2064 /* Signature was already verfied by caller. */
2065 /** @todo Check attributes. */
2066 if (pFvHdr->bRsvd != 0)
2067 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Reserved field of header is not 0");
2068 if (pFvHdr->bRevision != EFI_FIRMWARE_VOLUME_HEADER_REVISION)
2069 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unexpected revision of the firmware volume header");
2070 if (RT_LE2H_U16(pFvHdr->offExtHdr) != 0)
2071 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Firmware volume header contains unsupported extended headers");
2072
2073 /* Start calculating the checksum of the main header. */
2074 uint16_t u16Chksum = 0;
2075 const uint16_t *pu16 = (const uint16_t *)pFvHdr;
2076 while (pu16 < (const uint16_t *)pFvHdr + (sizeof(*pFvHdr) / sizeof(uint16_t)))
2077 u16Chksum += RT_LE2H_U16(*pu16++);
2078
2079 /* Read in the block map and verify it as well. */
2080 uint64_t cbFvVol = 0;
2081 uint64_t cbFvHdr = sizeof(*pFvHdr);
2082 uint64_t offBlockMap = sizeof(*pFvHdr);
2083 for (;;)
2084 {
2085 EFI_FW_BLOCK_MAP BlockMap;
2086 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offBlockMap, &BlockMap, sizeof(BlockMap), NULL);
2087 if (RT_FAILURE(rc))
2088 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Reading block map entry from %#RX64 failed", offBlockMap);
2089
2090 cbFvHdr += sizeof(BlockMap);
2091 offBlockMap += sizeof(BlockMap);
2092
2093 /* A zero entry denotes the end. */
2094 if ( !RT_LE2H_U32(BlockMap.cBlocks)
2095 && !RT_LE2H_U32(BlockMap.cbBlock))
2096 break;
2097
2098 cbFvVol += RT_LE2H_U32(BlockMap.cBlocks) * RT_LE2H_U32(BlockMap.cbBlock);
2099
2100 pu16 = (const uint16_t *)&BlockMap;
2101 while (pu16 < (const uint16_t *)&BlockMap + (sizeof(BlockMap) / sizeof(uint16_t)))
2102 u16Chksum += RT_LE2H_U16(*pu16++);
2103 }
2104
2105 *poffData = offBlockMap;
2106
2107 if (u16Chksum)
2108 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Firmware volume header has incorrect checksum");
2109 if (RT_LE2H_U64(pFvHdr->cbFvHdr) != cbFvHdr)
2110 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unexpected firmware volume header size");
2111
2112 return VINF_SUCCESS;
2113}
2114
2115
2116/**
2117 * Validates the given variable store header.
2118 *
2119 * @returns true if the given header is considered valid, false otherwise.
2120 * @param pThis The EFI variable store instance.
2121 * @param pHdr The variable store header to validate.
2122 * @param pfAuth Where to store whether the variable store uses authenticated variables or not.
2123 * @param pErrInfo Where to return additional error info.
2124 */
2125static int rtEfiVarStoreHdr_Validate(PRTEFIVARSTORE pThis, PCEFI_VARSTORE_HEADER pHdr, bool *pfAuth, PRTERRINFO pErrInfo)
2126{
2127#ifdef LOG_ENABLED
2128 rtEfiVarStoreHdr_Log(pHdr);
2129#endif
2130
2131 EFI_GUID GuidVarStoreAuth = EFI_VARSTORE_HEADER_GUID_AUTHENTICATED_VARIABLE;
2132 EFI_GUID GuidVarStore = EFI_VARSTORE_HEADER_GUID_VARIABLE;
2133 if (!memcmp(&pHdr->GuidVarStore, &GuidVarStoreAuth, sizeof(GuidVarStoreAuth)))
2134 *pfAuth = true;
2135 else if (!memcmp(&pHdr->GuidVarStore, &GuidVarStore, sizeof(GuidVarStore)))
2136 *pfAuth = false;
2137 else
2138 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Variable store GUID doesn't indicate a variable store");
2139 if (RT_LE2H_U32(pHdr->cbVarStore) >= pThis->cbBacking)
2140 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Variable store length exceeds size of backing storage (truncated file?)");
2141 if (pHdr->bFmt != EFI_VARSTORE_HEADER_FMT_FORMATTED)
2142 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Variable store is not formatted");
2143 if (pHdr->bState != EFI_VARSTORE_HEADER_STATE_HEALTHY)
2144 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Variable store is not healthy");
2145
2146 return VINF_SUCCESS;
2147}
2148
2149
2150/**
2151 * Validates the given authenticate variable header.
2152 *
2153 * @returns true if the given header is considered valid, false otherwise.
2154 * @param pThis The EFI variable store instance.
2155 * @param pVarHdr The variable header to validate.
2156 * @param offVar Offset of the authenticated variable header.
2157 * @param pErrInfo Where to return additional error info.
2158 */
2159static int rtEfiVarStoreAuthVar_Validate(PRTEFIVARSTORE pThis, PCEFI_AUTH_VAR_HEADER pVarHdr, uint64_t offVar, PRTERRINFO pErrInfo)
2160{
2161#ifdef LOG_ENABLED
2162 rtEfiVarStoreAuthVarHdr_Log(pVarHdr, offVar);
2163#endif
2164
2165 uint32_t cbName = RT_LE2H_U32(pVarHdr->cbName);
2166 uint32_t cbData = RT_LE2H_U32(pVarHdr->cbData);
2167 uint64_t cbVarMax = pThis->cbBacking - offVar - sizeof(*pVarHdr);
2168 if ( cbVarMax <= cbName
2169 || cbVarMax - cbName <= cbData)
2170 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Variable exceeds remaining space in store (cbName=%u cbData=%u cbVarMax=%llu)",
2171 cbName, cbData, cbVarMax);
2172
2173 return VINF_SUCCESS;
2174}
2175
2176
2177/**
2178 * Loads the authenticated variable at the given offset.
2179 *
2180 * @returns IPRT status code.
2181 * @retval VERR_EOF if the end of the store was reached.
2182 * @param pThis The EFI variable store instance.
2183 * @param offVar Offset of the variable to load.
2184 * @param poffVarEnd Where to store the offset pointing to the end of the variable.
2185 * @param fIgnoreDelVars Flag whether to ignore deleted variables.
2186 * @param pErrInfo Where to return additional error info.
2187 */
2188static int rtEfiVarStoreLoadAuthVar(PRTEFIVARSTORE pThis, uint64_t offVar, uint64_t *poffVarEnd,
2189 bool fIgnoreDelVars, PRTERRINFO pErrInfo)
2190{
2191 EFI_AUTH_VAR_HEADER VarHdr;
2192 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offVar, &VarHdr, sizeof(VarHdr), NULL);
2193 if (RT_FAILURE(rc))
2194 return rc;
2195
2196 rc = rtEfiVarStoreAuthVar_Validate(pThis, &VarHdr, offVar, pErrInfo);
2197 if (RT_FAILURE(rc))
2198 return rc;
2199
2200 if (poffVarEnd)
2201 *poffVarEnd = offVar + sizeof(VarHdr) + RT_LE2H_U32(VarHdr.cbData) + RT_LE2H_U32(VarHdr.cbName);
2202
2203 /* Only add complete variables or deleted variables when requested. */
2204 if ( ( fIgnoreDelVars
2205 && VarHdr.bState != EFI_AUTH_VAR_HEADER_STATE_ADDED)
2206 || VarHdr.bState == EFI_AUTH_VAR_HEADER_STATE_HDR_VALID_ONLY)
2207 return VINF_SUCCESS;
2208
2209 pThis->cbVarData += sizeof(VarHdr) + RT_LE2H_U32(VarHdr.cbData) + RT_LE2H_U32(VarHdr.cbName);
2210
2211 RTUTF16 awchName[128]; RT_ZERO(awchName);
2212 if (RT_LE2H_U32(VarHdr.cbName) > sizeof(awchName) - sizeof(RTUTF16))
2213 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Variable name is too long (%llu vs. %llu)\n",
2214 RT_LE2H_U32(VarHdr.cbName), sizeof(awchName));
2215
2216 rc = RTVfsFileReadAt(pThis->hVfsBacking, offVar + sizeof(VarHdr), &awchName[0], RT_LE2H_U32(VarHdr.cbName), NULL);
2217 if (RT_FAILURE(rc))
2218 return rc;
2219
2220 Log2(("Variable name '%ls'\n", &awchName[0]));
2221 rc = rtEfiVarStore_VarMaybeGrowEntries(pThis);
2222 if (RT_FAILURE(rc))
2223 return rc;
2224
2225 PRTEFIVAR pVar = &pThis->paVars[pThis->cVars++];
2226 pVar->pVarStore = pThis;
2227 if (RT_LE2H_U32(VarHdr.cbData))
2228 pVar->offVarData = offVar + sizeof(VarHdr) + RT_LE2H_U32(VarHdr.cbName);
2229 else
2230 pVar->offVarData = 0;
2231 pVar->fAttr = RT_LE2H_U32(VarHdr.fAttr);
2232 pVar->cMonotonic = RT_LE2H_U64(VarHdr.cMonotonic);
2233 pVar->idPubKey = RT_LE2H_U32(VarHdr.idPubKey);
2234 pVar->cbData = RT_LE2H_U32(VarHdr.cbData);
2235 pVar->pvData = NULL;
2236 pVar->fDeleted = false;
2237 memcpy(&pVar->EfiTimestamp, &VarHdr.Timestamp, sizeof(VarHdr.Timestamp));
2238
2239 if (VarHdr.Timestamp.u8Month)
2240 RTEfiTimeToTimeSpec(&pVar->Time, &VarHdr.Timestamp);
2241 else
2242 RTTimeNow(&pVar->Time);
2243
2244 RTEfiGuidToUuid(&pVar->Uuid, &VarHdr.GuidVendor);
2245
2246 rc = RTUtf16ToUtf8(&awchName[0], &pVar->pszName);
2247 if (RT_FAILURE(rc))
2248 pThis->cVars--;
2249
2250 rc = rtEfiVarStore_AddVarByGuid(pThis, &pVar->Uuid, pThis->cVars - 1);
2251
2252 return rc;
2253}
2254
2255
2256/**
2257 * Looks for the next variable starting at the given offset.
2258 *
2259 * @returns IPRT status code.
2260 * @retval VERR_EOF if the end of the store was reached.
2261 * @param pThis The EFI variable store instance.
2262 * @param offStart Where in the image to start looking.
2263 * @param poffVar Where to store the start of the next variable if found.
2264 */
2265static int rtEfiVarStoreFindVar(PRTEFIVARSTORE pThis, uint64_t offStart, uint64_t *poffVar)
2266{
2267 /* Try to find the ID indicating a variable start by loading data in chunks. */
2268 uint64_t offEnd = pThis->offStoreData + pThis->cbVarStore;
2269 while (offStart < offEnd)
2270 {
2271 uint16_t au16Tmp[_1K / sizeof(uint16_t)];
2272 size_t cbThisRead = RT_MIN(sizeof(au16Tmp), offEnd - offStart);
2273 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offStart, &au16Tmp[0], sizeof(au16Tmp), NULL);
2274 if (RT_FAILURE(rc))
2275 return rc;
2276
2277 for (uint32_t i = 0; i < RT_ELEMENTS(au16Tmp); i++)
2278 if (RT_LE2H_U16(au16Tmp[i]) == EFI_AUTH_VAR_HEADER_START)
2279 {
2280 *poffVar = offStart + i * sizeof(uint16_t);
2281 return VINF_SUCCESS;
2282 }
2283
2284 offStart += cbThisRead;
2285 }
2286
2287 return VERR_EOF;
2288}
2289
2290
2291/**
2292 * Loads and parses the superblock of the filesystem.
2293 *
2294 * @returns IPRT status code.
2295 * @param pThis The EFI variable store instance.
2296 * @param pErrInfo Where to return additional error info.
2297 */
2298static int rtEfiVarStoreLoad(PRTEFIVARSTORE pThis, PRTERRINFO pErrInfo)
2299{
2300 int rc = VINF_SUCCESS;
2301 EFI_FIRMWARE_VOLUME_HEADER FvHdr;
2302 rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, &FvHdr, sizeof(FvHdr), NULL);
2303 if (RT_FAILURE(rc))
2304 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading firmware volume header");
2305
2306 /* Validate the signature. */
2307 if (RT_LE2H_U32(FvHdr.u32Signature) != EFI_FIRMWARE_VOLUME_HEADER_SIGNATURE)
2308 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not a EFI variable store - Signature mismatch: %RX32", RT_LE2H_U16(FvHdr.u32Signature));
2309
2310 uint64_t offData = 0;
2311 rc = rtEfiVarStoreFvHdr_Validate(pThis, &FvHdr, &offData, pErrInfo);
2312 if (RT_FAILURE(rc))
2313 return rc;
2314
2315 EFI_VARSTORE_HEADER StoreHdr;
2316 rc = RTVfsFileReadAt(pThis->hVfsBacking, offData, &StoreHdr, sizeof(StoreHdr), NULL);
2317 if (RT_FAILURE(rc))
2318 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading variable store header");
2319
2320 rc = rtEfiVarStoreHdr_Validate(pThis, &StoreHdr, &pThis->fAuth, pErrInfo);
2321 if (RT_FAILURE(rc))
2322 return rc;
2323
2324 pThis->offStoreData = offData + sizeof(StoreHdr);
2325 pThis->cbVarStore = RT_LE2H_U32(StoreHdr.cbVarStore) - sizeof(StoreHdr);
2326
2327 /* Go over variables and set up the pointers. */
2328 offData = pThis->offStoreData;
2329 for (;;)
2330 {
2331 uint64_t offVar = 0;
2332
2333 rc = rtEfiVarStoreFindVar(pThis, offData, &offVar);
2334 if (RT_FAILURE(rc))
2335 break;
2336
2337 rc = rtEfiVarStoreLoadAuthVar(pThis, offVar, &offData, true /* fIgnoreDelVars*/, pErrInfo);
2338 if (RT_FAILURE(rc))
2339 break;
2340
2341 /* Align to 16bit boundary. */
2342 offData = RT_ALIGN_64(offData, 2);
2343 }
2344
2345 if (rc == VERR_EOF) /* Reached end of variable store. */
2346 rc = VINF_SUCCESS;
2347
2348 return rc;
2349}
2350
2351
2352/**
2353 * Fills the given range with 0xff to match what a real NAND flash device would return for
2354 * unwritten storage.
2355 *
2356 * @returns IPRT status code.
2357 * @param hVfsFile The VFS file handle to write to.
2358 * @param offStart The start offset to fill.
2359 * @param offEnd Offset to fill up to (exclusive).
2360 */
2361static int rtEfiVarStoreFillWithFF(RTVFSFILE hVfsFile, uint64_t offStart, uint64_t offEnd)
2362{
2363 int rc = VINF_SUCCESS;
2364 uint8_t abFF[512];
2365 memset(&abFF[0], 0xff, sizeof(abFF));
2366
2367 while ( offStart < offEnd
2368 && RT_SUCCESS(rc))
2369 {
2370 size_t cbThisWrite = RT_MIN(sizeof(abFF), offEnd - offStart);
2371 rc = RTVfsFileWriteAt(hVfsFile, offStart, &abFF[0], cbThisWrite, NULL);
2372 offStart += cbThisWrite;
2373 }
2374
2375 return rc;
2376}
2377
2378
2379RTDECL(int) RTEfiVarStoreOpenAsVfs(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fVarStoreFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2380{
2381 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2382 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
2383 AssertReturn(!fVarStoreFlags, VERR_INVALID_FLAGS);
2384
2385 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2386 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2387
2388 /*
2389 * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
2390 */
2391 RTVFS hVfs;
2392 PRTEFIVARSTORE pThis;
2393 int rc = RTVfsNew(&g_rtEfiVarStoreOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
2394 if (RT_SUCCESS(rc))
2395 {
2396 pThis->hVfsBacking = hVfsFileIn;
2397 pThis->hVfsSelf = hVfs;
2398 pThis->fMntFlags = fMntFlags;
2399 pThis->fVarStoreFlags = fVarStoreFlags;
2400
2401 rc = RTVfsFileQuerySize(pThis->hVfsBacking, &pThis->cbBacking);
2402 if (RT_SUCCESS(rc))
2403 {
2404 rc = rtEfiVarStoreLoad(pThis, pErrInfo);
2405 if (RT_SUCCESS(rc))
2406 {
2407 *phVfs = hVfs;
2408 return VINF_SUCCESS;
2409 }
2410 }
2411
2412 RTVfsRelease(hVfs);
2413 *phVfs = NIL_RTVFS;
2414 }
2415 else
2416 RTVfsFileRelease(hVfsFileIn);
2417
2418 return rc;
2419}
2420
2421
2422RTDECL(int) RTEfiVarStoreCreate(RTVFSFILE hVfsFile, uint64_t offStore, uint64_t cbStore, uint32_t fFlags, uint32_t cbBlock,
2423 PRTERRINFO pErrInfo)
2424{
2425 RT_NOREF(pErrInfo);
2426
2427 /*
2428 * Validate input.
2429 */
2430 if (!cbBlock)
2431 cbBlock = 4096;
2432 else
2433 AssertMsgReturn(cbBlock <= 8192 && RT_IS_POWER_OF_TWO(cbBlock),
2434 ("cbBlock=%#x\n", cbBlock), VERR_INVALID_PARAMETER);
2435 AssertReturn(!(fFlags & ~RTEFIVARSTORE_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
2436
2437 if (!cbStore)
2438 {
2439 uint64_t cbFile;
2440 int rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
2441 AssertRCReturn(rc, rc);
2442 AssertMsgReturn(cbFile > offStore, ("cbFile=%#RX64 offStore=%#RX64\n", cbFile, offStore), VERR_INVALID_PARAMETER);
2443 cbStore = cbFile - offStore;
2444 }
2445
2446 uint32_t cbFtw = 0;
2447 uint32_t offFtw = 0;
2448 uint32_t cbVarStore = cbStore;
2449 uint32_t cbNvEventLog = 0;
2450 uint32_t offNvEventLog = 0;
2451 if (!(fFlags & RTEFIVARSTORE_CREATE_F_NO_FTW_WORKING_SPACE))
2452 {
2453 /* Split the available space in half for the fault tolerant working area. */
2454 /** @todo Don't fully understand how these values come together right now but
2455 * we want to create NVRAM files matching the default OVMF_VARS.fd for now, see
2456 * https://github.com/tianocore/edk2/commit/b24fca05751f8222acf264853709012e0ab7bf49
2457 * for the layout.
2458 * Probably have toadd more arguments to control the different parameters.
2459 */
2460 cbNvEventLog = _4K;
2461 cbVarStore = cbStore / 2 - cbNvEventLog - _4K;
2462 cbFtw = cbVarStore + _4K;
2463 offNvEventLog = cbVarStore;
2464 offFtw = offNvEventLog + cbNvEventLog;
2465 }
2466
2467 uint32_t const cBlocks = (uint32_t)(cbStore / cbBlock);
2468
2469 EFI_GUID GuidVarStore = EFI_VARSTORE_FILESYSTEM_GUID;
2470 EFI_GUID GuidVarAuth = EFI_VARSTORE_HEADER_GUID_AUTHENTICATED_VARIABLE;
2471 EFI_FIRMWARE_VOLUME_HEADER FvHdr; RT_ZERO(FvHdr);
2472 EFI_FW_BLOCK_MAP aBlockMap[2]; RT_ZERO(aBlockMap);
2473 EFI_VARSTORE_HEADER VarStoreHdr; RT_ZERO(VarStoreHdr);
2474
2475 /* Firmware volume header. */
2476 memcpy(&FvHdr.GuidFilesystem, &GuidVarStore, sizeof(GuidVarStore));
2477 FvHdr.cbFv = RT_H2LE_U64(cbStore);
2478 FvHdr.u32Signature = RT_H2LE_U32(EFI_FIRMWARE_VOLUME_HEADER_SIGNATURE);
2479 FvHdr.fAttr = RT_H2LE_U32(0x4feff); /** @todo */
2480 FvHdr.cbFvHdr = RT_H2LE_U16(sizeof(FvHdr) + sizeof(aBlockMap));
2481 FvHdr.bRevision = EFI_FIRMWARE_VOLUME_HEADER_REVISION;
2482
2483 /* Start calculating the checksum of the main header. */
2484 uint16_t u16Chksum = 0;
2485 const uint16_t *pu16 = (const uint16_t *)&FvHdr;
2486 while (pu16 < (const uint16_t *)&FvHdr + (sizeof(FvHdr) / sizeof(uint16_t)))
2487 u16Chksum += RT_LE2H_U16(*pu16++);
2488
2489 /* Block map, the second entry remains 0 as it serves the delimiter. */
2490 aBlockMap[0].cbBlock = RT_H2LE_U32(cbBlock);
2491 aBlockMap[0].cBlocks = RT_H2LE_U32(cBlocks);
2492
2493 pu16 = (const uint16_t *)&aBlockMap[0];
2494 while (pu16 < (const uint16_t *)&aBlockMap[0] + (sizeof(aBlockMap) / (sizeof(uint16_t))))
2495 u16Chksum += RT_LE2H_U16(*pu16++);
2496
2497 FvHdr.u16Chksum = RT_H2LE_U16(UINT16_MAX - u16Chksum + 1);
2498
2499 /* Variable store header. */
2500 memcpy(&VarStoreHdr.GuidVarStore, &GuidVarAuth, sizeof(GuidVarAuth));
2501 VarStoreHdr.cbVarStore = RT_H2LE_U32(cbVarStore - sizeof(FvHdr) - sizeof(aBlockMap));
2502 VarStoreHdr.bFmt = EFI_VARSTORE_HEADER_FMT_FORMATTED;
2503 VarStoreHdr.bState = EFI_VARSTORE_HEADER_STATE_HEALTHY;
2504
2505 /* Write everything. */
2506 int rc = RTVfsFileWriteAt(hVfsFile, offStore, &FvHdr, sizeof(FvHdr), NULL);
2507 if (RT_SUCCESS(rc))
2508 rc = RTVfsFileWriteAt(hVfsFile, offStore + sizeof(FvHdr), &aBlockMap[0], sizeof(aBlockMap), NULL);
2509 if (RT_SUCCESS(rc))
2510 rc = RTVfsFileWriteAt(hVfsFile, offStore + sizeof(FvHdr) + sizeof(aBlockMap), &VarStoreHdr, sizeof(VarStoreHdr), NULL);
2511 if (RT_SUCCESS(rc))
2512 {
2513 /* Fill the remainder with 0xff as it would be the case for a real NAND flash device. */
2514 uint64_t offStart = offStore + sizeof(FvHdr) + sizeof(aBlockMap) + sizeof(VarStoreHdr);
2515 uint64_t offEnd = offStore + cbVarStore;
2516
2517 rc = rtEfiVarStoreFillWithFF(hVfsFile, offStart, offEnd);
2518 }
2519
2520 if ( RT_SUCCESS(rc)
2521 && !(fFlags & RTEFIVARSTORE_CREATE_F_NO_FTW_WORKING_SPACE))
2522 {
2523 EFI_GUID GuidFtwArea = EFI_WORKING_BLOCK_SIGNATURE_GUID;
2524 EFI_FTW_BLOCK_HEADER FtwHdr; RT_ZERO(FtwHdr);
2525
2526 memcpy(&FtwHdr.GuidSignature, &GuidFtwArea, sizeof(GuidFtwArea));
2527 FtwHdr.fWorkingBlockValid = RT_H2LE_U32(0xfffffffe); /** @todo */
2528 FtwHdr.cbWriteQueue = RT_H2LE_U64(0xfe0ULL); /* This comes from the default OVMF variable volume. */
2529 FtwHdr.u32Chksum = RTCrc32(&FtwHdr, sizeof(FtwHdr));
2530
2531 /* The area starts with the event log which defaults to 0xff. */
2532 rc = rtEfiVarStoreFillWithFF(hVfsFile, offNvEventLog, offNvEventLog + cbNvEventLog);
2533 if (RT_SUCCESS(rc))
2534 {
2535 /* Write the FTW header. */
2536 rc = RTVfsFileWriteAt(hVfsFile, offFtw, &FtwHdr, sizeof(FtwHdr), NULL);
2537 if (RT_SUCCESS(rc))
2538 rc = rtEfiVarStoreFillWithFF(hVfsFile, offFtw + sizeof(FtwHdr), offFtw + cbFtw);
2539 }
2540 }
2541
2542 return rc;
2543}
2544
2545
2546/**
2547 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2548 */
2549static DECLCALLBACK(int) rtVfsChainEfiVarStore_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2550 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2551{
2552 RT_NOREF(pProviderReg);
2553
2554 /*
2555 * Basic checks.
2556 */
2557 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2558 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2559 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2560 && pElement->enmType != RTVFSOBJTYPE_DIR)
2561 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2562 if (pElement->cArgs > 1)
2563 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2564
2565 /*
2566 * Parse the flag if present, save in pElement->uProvider.
2567 */
2568 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
2569 if (pElement->cArgs > 0)
2570 {
2571 const char *psz = pElement->paArgs[0].psz;
2572 if (*psz)
2573 {
2574 if (!strcmp(psz, "ro"))
2575 fReadOnly = true;
2576 else if (!strcmp(psz, "rw"))
2577 fReadOnly = false;
2578 else
2579 {
2580 *poffError = pElement->paArgs[0].offSpec;
2581 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
2582 }
2583 }
2584 }
2585
2586 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
2587 return VINF_SUCCESS;
2588}
2589
2590
2591/**
2592 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2593 */
2594static DECLCALLBACK(int) rtVfsChainEfiVarStore_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2595 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2596 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2597{
2598 RT_NOREF(pProviderReg, pSpec, poffError);
2599
2600 int rc;
2601 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2602 if (hVfsFileIn != NIL_RTVFSFILE)
2603 {
2604 RTVFS hVfs;
2605 rc = RTEfiVarStoreOpenAsVfs(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2606 RTVfsFileRelease(hVfsFileIn);
2607 if (RT_SUCCESS(rc))
2608 {
2609 *phVfsObj = RTVfsObjFromVfs(hVfs);
2610 RTVfsRelease(hVfs);
2611 if (*phVfsObj != NIL_RTVFSOBJ)
2612 return VINF_SUCCESS;
2613 rc = VERR_VFS_CHAIN_CAST_FAILED;
2614 }
2615 }
2616 else
2617 rc = VERR_VFS_CHAIN_CAST_FAILED;
2618 return rc;
2619}
2620
2621
2622/**
2623 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2624 */
2625static DECLCALLBACK(bool) rtVfsChainEfiVarStore_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2626 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2627 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2628{
2629 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2630 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2631 || !pReuseElement->paArgs[0].uProvider)
2632 return true;
2633 return false;
2634}
2635
2636
2637/** VFS chain element 'efivarstore'. */
2638static RTVFSCHAINELEMENTREG g_rtVfsChainEfiVarStoreReg =
2639{
2640 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2641 /* fReserved = */ 0,
2642 /* pszName = */ "efivarstore",
2643 /* ListEntry = */ { NULL, NULL },
2644 /* pszHelp = */ "Open a EFI variable store, requires a file object on the left side.\n"
2645 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2646 /* pfnValidate = */ rtVfsChainEfiVarStore_Validate,
2647 /* pfnInstantiate = */ rtVfsChainEfiVarStore_Instantiate,
2648 /* pfnCanReuseElement = */ rtVfsChainEfiVarStore_CanReuseElement,
2649 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2650};
2651
2652RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainEfiVarStoreReg, rtVfsChainEfiVarStoreReg);
2653
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette