VirtualBox

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

最後變更 在這個檔案從91500是 91500,由 vboxsync 提交於 3 年 前

Runtime/efivarstorevfs: bugref:9580 - newer clang is upset that
computing the size of the block map for the checksum looks
suspiciously like incorrect computation of the number of array
elements. Pacify it using the workaround it suggests.

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

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