VirtualBox

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

Last change on this file since 90733 was 90343, checked in by vboxsync, 4 years ago

Runtime/efivarstorevfs.cpp: Fixes, the variable header is aligned to a 32bit boundary, bugref:9580

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