VirtualBox

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

Last change on this file since 93138 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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