VirtualBox

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

Last change on this file since 90283 was 90133, checked in by vboxsync, 4 years ago

Runtime/efivarstorevfs.cpp: Some basic variable creation and deletion code (incomplete), bugref:9580

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

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