VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/pdbvfs.cpp

Last change on this file was 101653, checked in by vboxsync, 7 months ago

IPRT/dbg: Fixed error handling empty DBI streams in the PDB VFS.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.1 KB
Line 
1/* $Id: pdbvfs.cpp 101653 2023-10-30 11:08:56Z vboxsync $ */
2/** @file
3 * IPRT - PDB Virtual Filesystem (read only).
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FS
42#include "internal/iprt.h"
43#include <iprt/fsvfs.h>
44
45#include <iprt/assert.h>
46#include <iprt/ctype.h>
47#include <iprt/err.h>
48#include <iprt/file.h>
49#include <iprt/log.h>
50#include <iprt/mem.h>
51#include <iprt/string.h>
52#include <iprt/vfs.h>
53#include <iprt/vfslowlevel.h>
54#include <iprt/utf16.h>
55#include <iprt/uuid.h>
56#include <iprt/formats/pdb.h>
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62/** Pointer to an ISO volume (VFS instance data). */
63typedef struct RTFSPDBVOL *PRTFSPDBVOL;
64/** Pointer to a const ISO volume (VFS instance data). */
65typedef struct RTFSPDBVOL const *PCRTFSPDBVOL;
66
67
68/**
69 * Substream info.
70 */
71typedef struct RTFSPDBSUBSTREAM
72{
73 /** The parent stream. */
74 uint32_t idxStream;
75 /** The substream index within the parent. */
76 uint8_t idxSubstream;
77 /** The offset of the start of the substream within the parent. */
78 uint32_t offSubstream;
79 /** The size of the substream. */
80 uint32_t cbSubstream;
81 /** The name of the substream (including the parent one at the start).
82 * Readonly string constant, as substreams are all hardcoded. */
83 const char *pszName;
84} RTFSPDBSUBSTREAM;
85typedef RTFSPDBSUBSTREAM *PRTFSPDBSUBSTREAM;
86typedef RTFSPDBSUBSTREAM const *PCRTFSPDBSUBSTREAM;
87
88
89/**
90 * Stream info.
91 */
92typedef struct RTFSPDBSTREAMINFO
93{
94 /** The stream name.
95 * Unnamed streams will be set to NULL. Standard streams are assigned
96 * names (C-litterals) the rest is matched up with RTFSPDBVOL::pszzNames
97 * content. */
98 const char *pszName;
99 /** Size of the stream. */
100 uint32_t cbStream;
101 /** Number of pages in the stream. */
102 uint32_t cPages;
103 /** Pointer to the page map for the stream (within RTFSPDBVOL::pbRoot). */
104 union
105 {
106 void const *pv;
107 PCRTPDB20PAGE pa20;
108 PCRTPDB70PAGE pa70;
109 } PageMap;
110 /** Number of substreams. */
111 uint8_t cSubstreams;
112 /** The index into RTFSPDBVOL::aSubstreams of the first one. */
113 uint8_t idxFirstSubstream;
114} RTFSPDBSTREAMINFO;
115typedef RTFSPDBSTREAMINFO *PRTFSPDBSTREAMINFO;
116typedef RTFSPDBSTREAMINFO const *PCRTFSPDBSTREAMINFO;
117
118
119/**
120 * Private data for a VFS file object.
121 */
122typedef struct RTFSPDBFILEOBJ
123{
124 /** Pointer to the PDB volume data. */
125 PRTFSPDBVOL pPdb;
126 /** The stream number (0 based). */
127 uint32_t idxStream;
128 /** The start offset of the substream this file object represents. This is
129 * zero for full streams. */
130 uint32_t offSubstream;
131 /** Size of the stream (or substream). */
132 uint32_t cbStream;
133 /** Number of pages in the stream (the whole stream, not just the substream). */
134 uint32_t cPages;
135 /** Pointer to the page map for the stream (within RTFSPDBVOL::pbRoot)
136 * (again the whole stream, not just the substream). */
137 union
138 {
139 void const *pv;
140 PCRTPDB20PAGE pa20;
141 PCRTPDB70PAGE pa70;
142 } PageMap;
143 /** The current file offset. */
144 uint64_t offFile;
145} RTFSPDBFILEOBJ;
146typedef RTFSPDBFILEOBJ *PRTFSPDBFILEOBJ;
147
148/**
149 * Private data for a VFS directory object.
150 */
151typedef struct RTFSPDBDIROBJ
152{
153 /** Pointer to the PDB volume data. */
154 PRTFSPDBVOL pPdb;
155 /** The next stream and substream number to return info for when reading. */
156 union
157 {
158 uint32_t idxNext;
159 struct
160 {
161 /** This is zero for the whole stream and non-zero for substreams. */
162 uint32_t idxNextSubstream : 8;
163 /** The next stream number. */
164 uint32_t idxNextStream : 24;
165 } s;
166 } u;
167} RTFSPDBDIROBJ;
168typedef RTFSPDBDIROBJ *PRTFSPDBDIROBJ;
169
170
171/**
172 * Indicates which PDB version we're accessing.
173 */
174typedef enum RTFSPDBVER
175{
176 /** Invalid zero value. */
177 RTFSPDBVER_INVALID = 0,
178 /** Accessing a v2.0 PDB. */
179 RTFSPDBVER_2,
180 /** Accessing a v7.0 PDB. */
181 RTFSPDBVER_7
182} RTFSPDBVER;
183
184
185/**
186 * A PDB volume.
187 */
188typedef struct RTFSPDBVOL
189{
190 /** Handle to itself. */
191 RTVFS hVfsSelf;
192 /** The file, partition, or whatever backing the PDB file. */
193 RTVFSFILE hVfsBacking;
194 /** The size of the backing thingy. */
195 uint64_t cbBacking;
196 /** The size of the backing thingy in sectors (cbSector). */
197 uint64_t cBackingPages;
198 /** Flags. */
199 uint32_t fFlags;
200 /** The page size (in bytes). */
201 uint32_t cbPage;
202 /** The format version. */
203 RTFSPDBVER enmVersion;
204 /** Number of streams. */
205 uint32_t cStreams;
206 /** The size of the root stream. */
207 uint32_t cbRoot;
208 /** Pointer to the root directory bytes. */
209 uint8_t *pbRoot;
210
211 /** @name PDB metadata from stream \#1.
212 * @{ */
213 /** The PDB age. */
214 uint32_t uAge;
215 /** The PDB timestamp. */
216 uint32_t uTimestamp;
217 /** The PDB UUID. */
218 RTUUID Uuid;
219 /** The VC date (see RTPDB70NAMES::uVersion and RTPDB20NAMES::uVersion). */
220 uint32_t uVcDate;
221 /** Size of the name string table. */
222 uint32_t cbNames;
223 /** Name string table. */
224 char *pszzNames;
225 /** @} */
226
227 /** The DBI header from stream \#3. */
228 RTPDBDBIHDR DbiHdr;
229
230 /** Extra per-stream info. We've do individual allocations here in case we
231 * want to add write support. */
232 PRTFSPDBSTREAMINFO *papStreamInfo;
233
234 /** Number of substreams in aSubstreams. */
235 uint8_t cSubstreams;
236 /** Substreams (only from the DBI atm.). */
237 RTFSPDBSUBSTREAM aSubstreams[8];
238} RTFSPDBVOL;
239
240
241/*********************************************************************************************************************************
242* Internal Functions *
243*********************************************************************************************************************************/
244static DECLCALLBACK(int) rtFsPdbVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir);
245
246
247
248/**
249 * Helper for methods returning file information.
250 */
251static void rtFsPdbPopulateObjInfo(PRTFSPDBVOL pPdb, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr,
252 uint32_t cbStream, uint32_t idxStream, uint8_t idxSubstream, bool fIsDir)
253{
254 RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
255 RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
256 RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
257 RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
258 pObjInfo->cbObject = cbStream == UINT32_MAX ? 0 : cbStream;
259 pObjInfo->cbAllocated = (uint64_t)RTPdbSizeToPages(cbStream, pPdb->cbPage) * pPdb->cbPage;
260 pObjInfo->Attr.fMode = RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH | RTFS_DOS_READONLY;
261 if (!fIsDir)
262 pObjInfo->Attr.fMode |= RTFS_TYPE_FILE;
263 else
264 pObjInfo->Attr.fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
265 pObjInfo->Attr.enmAdditional = enmAddAttr;
266
267 switch (enmAddAttr)
268 {
269 case RTFSOBJATTRADD_NOTHING: RT_FALL_THRU();
270 case RTFSOBJATTRADD_UNIX:
271 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
272 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
273 pObjInfo->Attr.u.Unix.cHardlinks = 1 + fIsDir;
274 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
275 pObjInfo->Attr.u.Unix.INodeId = idxStream | ((uint64_t)idxSubstream << 32);
276 pObjInfo->Attr.u.Unix.fFlags = 0;
277 pObjInfo->Attr.u.Unix.GenerationId = 0;
278 pObjInfo->Attr.u.Unix.Device = 0;
279 break;
280 case RTFSOBJATTRADD_UNIX_OWNER:
281 pObjInfo->Attr.u.UnixOwner.uid = 0;
282 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
283 break;
284 case RTFSOBJATTRADD_UNIX_GROUP:
285 pObjInfo->Attr.u.UnixGroup.gid = 0;
286 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
287 break;
288 case RTFSOBJATTRADD_EASIZE:
289 pObjInfo->Attr.u.EASize.cb = 0;
290 break;
291 default:
292 AssertFailedBreak();
293 }
294}
295
296/**
297 * Helper methods for opening a stream.
298 *
299 * This is used internally w/o any associated RTVFSFILE handle for reading
300 * stream \#1 containing PDB metadata.
301 */
302static void rtFsPdbPopulateFileObj(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb, uint32_t idxStream,
303 uint32_t cbStream, void const *pvPageMap)
304{
305 pNewFile->pPdb = pPdb;
306 pNewFile->idxStream = idxStream;
307 pNewFile->offSubstream = 0;
308 pNewFile->cbStream = cbStream;
309 pNewFile->PageMap.pv = pvPageMap;
310 pNewFile->cPages = RTPdbSizeToPages(cbStream, pPdb->cbPage);
311 pNewFile->offFile = 0;
312}
313
314
315/**
316 * Helper methods for opening a stream or substream.
317 */
318static void rtFsPdbPopulateFileObjFromInfo(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb,
319 uint32_t idxStream, PCRTFSPDBSUBSTREAM pSubstream)
320{
321 PCRTFSPDBSTREAMINFO pInfo = pPdb->papStreamInfo[idxStream];
322 rtFsPdbPopulateFileObj(pNewFile, pPdb, idxStream, pInfo->cbStream, pInfo->PageMap.pv);
323 Assert(pInfo->cPages == pNewFile->cPages);
324 if (pSubstream)
325 {
326 pNewFile->offSubstream = pSubstream->offSubstream;
327 pNewFile->cbStream = pSubstream->cbSubstream;
328 }
329}
330
331
332/**
333 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
334 */
335static DECLCALLBACK(int) rtFsPdbFile_Close(void *pvThis)
336{
337 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
338 LogFlow(("rtFsPdbFile_Close(%p/%p)\n", pThis, pThis->pPdb));
339 pThis->pPdb = NULL;
340 return VINF_SUCCESS;
341}
342
343
344/**
345 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
346 */
347static DECLCALLBACK(int) rtFsPdbFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
348{
349 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
350 rtFsPdbPopulateObjInfo(pThis->pPdb, pObjInfo, enmAddAttr, pThis->cbStream, pThis->idxStream, 0, false /*fIsDir*/);
351 return VINF_SUCCESS;
352}
353
354
355/**
356 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
357 */
358static DECLCALLBACK(int) rtFsPdbFile_Read(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
359{
360 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
361 PRTFSPDBVOL const pPdb = pThis->pPdb;
362 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
363 AssertReturn(pPdb, VERR_INTERNAL_ERROR_2);
364 RT_NOREF(fBlocking);
365
366 /* Apply default offset and switch to unsigned offset variable. */
367 uint64_t offFile;
368 if (off == -1)
369 offFile = pThis->offFile;
370 else
371 {
372 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
373 offFile = (uint64_t)off;
374 }
375
376 /*
377 * Check for EOF and figure out how much to read.
378 */
379 if (offFile >= pThis->cbStream)
380 {
381 if (pcbRead)
382 {
383 *pcbRead = 0;
384 return VINF_EOF;
385 }
386 return VERR_EOF;
387 }
388
389 int rcRet = VINF_SUCCESS;
390 size_t cbToRead = RTSgBufCalcLengthLeft(pSgBuf);
391 if ( cbToRead > pThis->cbStream
392 || offFile + cbToRead > pThis->cbStream)
393 {
394 if (!pcbRead)
395 return VERR_EOF;
396 cbToRead = (size_t)(pThis->cbStream - offFile);
397 rcRet = VINF_EOF;
398 }
399
400 /*
401 * Do it page by page, buffer segment by buffer segment, whatever is smaller.
402 */
403 uint64_t const offStart = offFile;
404 while (cbToRead > 0)
405 {
406 uint32_t iPageMap = (uint32_t)((offFile + pThis->offSubstream) / pPdb->cbPage);
407 uint32_t const offInPage = (uint32_t)((offFile + pThis->offSubstream) % pPdb->cbPage);
408 size_t cbLeftInPage = pPdb->cbPage - offInPage;
409 if (cbLeftInPage > cbToRead)
410 cbLeftInPage = cbToRead;
411 void * const pvDst = RTSgBufGetCurrentSegment(pSgBuf, cbLeftInPage, &cbLeftInPage);
412 AssertReturn(pvDst, VERR_INTERNAL_ERROR_4);
413
414 uint64_t const offPageInFile = (pPdb->enmVersion == RTFSPDBVER_2
415 ? pThis->PageMap.pa20[iPageMap] : pThis->PageMap.pa70[iPageMap])
416 * (uint64_t)pPdb->cbPage;
417 int rcRead = RTVfsFileReadAt(pPdb->hVfsBacking, offPageInFile + offInPage, pvDst, cbLeftInPage, NULL /*pcbRead*/);
418 if (RT_SUCCESS(rcRead))
419 {
420 size_t cbAssert = RTSgBufAdvance(pSgBuf, cbLeftInPage); Assert(cbAssert == cbLeftInPage); RT_NOREF(cbAssert);
421 offFile += cbLeftInPage;
422 cbToRead -= cbLeftInPage;
423 }
424 /* If we can return the number of bytes we've read, we'll advance the
425 file offset. Otherwise we won't and return immediately. */
426 else if (!pcbRead)
427 return rcRead;
428 else
429 {
430 rcRet = rcRead != VERR_EOF ? rcRead : VERR_READ_ERROR;
431 break;
432 }
433 }
434
435 /*
436 * Update the file position and stuff.
437 */
438 pThis->offFile = offFile;
439 if (pcbRead)
440 *pcbRead = offFile - offStart;
441 return rcRet;
442}
443
444
445/**
446 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
447 */
448static DECLCALLBACK(int) rtFsPdbFile_Flush(void *pvThis)
449{
450 RT_NOREF(pvThis);
451 return VINF_SUCCESS;
452}
453
454
455/**
456 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
457 */
458static DECLCALLBACK(int) rtFsPdbFile_Tell(void *pvThis, PRTFOFF poffActual)
459{
460 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
461 *poffActual = pThis->offFile;
462 return VINF_SUCCESS;
463}
464
465
466/**
467 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
468 */
469static DECLCALLBACK(int) rtFsPdbFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
470{
471 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
472 RTFOFF offNew;
473 switch (uMethod)
474 {
475 case RTFILE_SEEK_BEGIN:
476 offNew = offSeek;
477 break;
478 case RTFILE_SEEK_END:
479 offNew = (RTFOFF)pThis->cbStream + offSeek;
480 break;
481 case RTFILE_SEEK_CURRENT:
482 offNew = (RTFOFF)pThis->offFile + offSeek;
483 break;
484 default:
485 return VERR_INVALID_PARAMETER;
486 }
487 if (offNew >= 0)
488 {
489 pThis->offFile = offNew;
490 *poffActual = offNew;
491 return VINF_SUCCESS;
492 }
493 return VERR_NEGATIVE_SEEK;
494}
495
496
497/**
498 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
499 */
500static DECLCALLBACK(int) rtFsPdbFile_QuerySize(void *pvThis, uint64_t *pcbFile)
501{
502 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
503 *pcbFile = pThis->cbStream;
504 return VINF_SUCCESS;
505}
506
507
508/**
509 * PDB FS file operations.
510 */
511DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsPdbFileOps =
512{
513 { /* Stream */
514 { /* Obj */
515 RTVFSOBJOPS_VERSION,
516 RTVFSOBJTYPE_FILE,
517 "PDB File",
518 rtFsPdbFile_Close,
519 rtFsPdbFile_QueryInfo,
520 NULL,
521 RTVFSOBJOPS_VERSION
522 },
523 RTVFSIOSTREAMOPS_VERSION,
524 RTVFSIOSTREAMOPS_FEAT_NO_SG,
525 rtFsPdbFile_Read,
526 NULL /*pfnWrite*/,
527 rtFsPdbFile_Flush,
528 NULL /*pfnPollOne*/,
529 rtFsPdbFile_Tell,
530 NULL /*pfnSkip*/,
531 NULL /*pfnZeroFill*/,
532 RTVFSIOSTREAMOPS_VERSION,
533 },
534 RTVFSFILEOPS_VERSION,
535 0,
536 { /* ObjSet */
537 RTVFSOBJSETOPS_VERSION,
538 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
539 NULL /*SetMode*/,
540 NULL /*SetTimes*/,
541 NULL /*SetOwner*/,
542 RTVFSOBJSETOPS_VERSION
543 },
544 rtFsPdbFile_Seek,
545 rtFsPdbFile_QuerySize,
546 NULL /*SetSize*/,
547 NULL /*QueryMaxSize*/,
548 RTVFSFILEOPS_VERSION
549};
550
551
552
553/**
554 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
555 */
556static DECLCALLBACK(int) rtFsPdbDir_Close(void *pvThis)
557{
558 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
559 LogFlow(("rtFsPdbDir_Close(%p/%p)\n", pThis, pThis->pPdb));
560 pThis->pPdb = NULL;
561 return VINF_SUCCESS;
562}
563
564
565/**
566 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
567 */
568static DECLCALLBACK(int) rtFsPdbDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
569{
570 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
571 PRTFSPDBVOL const pPdb = pThis->pPdb;
572 rtFsPdbPopulateObjInfo(pPdb, pObjInfo, enmAddAttr, pPdb->cbRoot, 0 /* root dir is stream zero */,
573 0 /*idxSubstream*/, true /*fIsDir*/);
574 return VINF_SUCCESS;
575}
576
577
578/**
579 * Helper for looping up a substream name.
580 */
581DECLINLINE(PCRTFSPDBSUBSTREAM) rtFsPdbDirLookupSubstream(PRTFSPDBVOL pPdb, uint32_t idxStream, const char *pszName)
582{
583 uint8_t const idxFirst = idxStream != UINT32_MAX ? pPdb->papStreamInfo[idxStream]->idxFirstSubstream : 0;
584 uint8_t cLeft = idxStream != UINT32_MAX ? pPdb->papStreamInfo[idxStream]->cSubstreams : pPdb->cSubstreams;
585 while (cLeft-- > 0)
586 {
587 PCRTFSPDBSUBSTREAM pSubstream = &pPdb->aSubstreams[idxFirst + cLeft];
588 if (strcmp(pSubstream->pszName, pszName) == 0)
589 return pSubstream;
590 }
591 return NULL;
592}
593
594
595/**
596 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
597 */
598static DECLCALLBACK(int) rtFsPdbDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
599 uint32_t fFlags, PRTVFSOBJ phVfsObj)
600{
601 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
602 PRTFSPDBVOL const pPdb = pThis->pPdb;
603 int rc;
604
605 /*
606 * We cannot create or replace anything, just open stuff.
607 */
608 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
609 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
610 { /* likely */ }
611 else
612 return VERR_WRITE_PROTECT;
613
614 /*
615 * Special cases '.' and '..'
616 */
617 if ( pszEntry[0] == '.'
618 && ( pszEntry[1] == '\0'
619 || (pszEntry[1] == '.' && pszEntry[2] == '\0')))
620 {
621 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
622 {
623 RTVFSDIR hVfsDir;
624 rc = rtFsPdbVol_OpenRoot(pPdb, &hVfsDir);
625 if (RT_SUCCESS(rc))
626 {
627 *phVfsObj = RTVfsObjFromDir(hVfsDir);
628 RTVfsDirRelease(hVfsDir);
629 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
630 }
631 }
632 else
633 rc = VERR_IS_A_DIRECTORY;
634 return rc;
635 }
636
637 /*
638 * The given filename can be:
639 * - just the stream index: "1";
640 * - just the name provided it doesn't start with a digit: "pdb";
641 * - or the combination of the two: "1-pdb".
642 */
643 uint32_t idxStream;
644 PCRTFSPDBSUBSTREAM pSubstream = NULL;
645 if (RT_C_IS_DIGIT(*pszEntry))
646 {
647 char *pszNext;
648 rc = RTStrToUInt32Ex(pszEntry, &pszNext, 10, &idxStream);
649 if ( (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
650 || (*pszNext != '\0' && *pszNext != '-')
651 || idxStream >= pPdb->cStreams)
652 {
653 Log2(("rtFsPdbDir_Open: RTStrToUInt32Ex(%s,) -> %Rrc\n", pszEntry, VERR_PATH_NOT_FOUND));
654 return VERR_PATH_NOT_FOUND;
655 }
656 if ( *pszNext == '-'
657 && RTStrCmp(pszNext + 1, pPdb->papStreamInfo[idxStream]->pszName) != 0)
658 {
659 pSubstream = rtFsPdbDirLookupSubstream(pPdb, idxStream, pszNext + 1);
660 if (pSubstream)
661 Assert(pSubstream->idxStream == idxStream);
662 else
663 {
664 Log2(("rtFsPdbDir_Open: idxStream=%#x name mismatch '%s', expected '%s'\n",
665 idxStream, pszEntry, pPdb->papStreamInfo[idxStream]->pszName));
666 return VERR_PATH_NOT_FOUND;
667 }
668 }
669 }
670 else
671 {
672 for (idxStream = 0; idxStream < pPdb->cStreams; idxStream++)
673 {
674 const char * const pszStreamName = pPdb->papStreamInfo[idxStream]->pszName;
675 if (pszStreamName && strcmp(pszEntry, pszStreamName) == 0)
676 break;
677 }
678 if (idxStream >= pPdb->cStreams)
679 {
680 pSubstream = rtFsPdbDirLookupSubstream(pPdb, UINT32_MAX, pszEntry);
681 if (pSubstream)
682 idxStream = pSubstream->idxStream;
683 else
684 {
685 Log2(("rtFsPdbDir_Open: '%s' not found in name table\n", pszEntry));
686 return VERR_PATH_NOT_FOUND;
687 }
688 }
689 }
690
691 /*
692 * If opening a file, create a new file object and return it.
693 */
694 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
695 {
696 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
697 PRTFSPDBFILEOBJ pNewFile;
698 rc = RTVfsNewFile(&g_rtFsPdbFileOps, sizeof(*pNewFile), fOpen, pPdb->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
699 &hVfsFile, (void **)&pNewFile);
700 if (RT_SUCCESS(rc))
701 {
702 rtFsPdbPopulateFileObjFromInfo(pNewFile, pPdb, idxStream, pSubstream);
703
704 /* Convert it to a file object. */
705 *phVfsObj = RTVfsObjFromFile(hVfsFile);
706 RTVfsFileRelease(hVfsFile);
707 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
708
709 LogFlow(("rtFsPdbDir_Open: idxStream=%#x cbStream=%#RX64\n", idxStream, pNewFile->cbStream));
710 }
711 }
712 else
713 rc = VERR_IS_A_FILE;
714 return rc;
715}
716
717
718/**
719 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
720 */
721static DECLCALLBACK(int) rtFsPdbDir_RewindDir(void *pvThis)
722{
723 PRTFSPDBDIROBJ pThis = (PRTFSPDBDIROBJ)pvThis;
724 pThis->u.idxNext = 0;
725 return VINF_SUCCESS;
726}
727
728
729/**
730 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
731 */
732static DECLCALLBACK(int) rtFsPdbDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
733 RTFSOBJATTRADD enmAddAttr)
734{
735 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
736 PRTFSPDBVOL const pPdb = pThis->pPdb;
737 uint32_t const idxStream = pThis->u.s.idxNextStream;
738 uint32_t const idxSubstream = pThis->u.s.idxNextSubstream;
739 if (idxStream < pPdb->cStreams)
740 {
741 PCRTFSPDBSTREAMINFO const pStreamInfo = pPdb->papStreamInfo[idxStream];
742 PCRTFSPDBSUBSTREAM const pSubstreamInfo = !idxSubstream ? NULL
743 : &pPdb->aSubstreams[pStreamInfo->idxFirstSubstream + idxSubstream - 1];
744
745 /*
746 * Do names first as they may cause overflows.
747 */
748 char szStreamNo[64];
749 ssize_t const cchStreamNo = RTStrFormatU32(szStreamNo, sizeof(szStreamNo), idxStream, 10, 0, 0, 0);
750 Assert(cchStreamNo > 0);
751
752 /* Provide a more descriptive name if possible. */
753 const char *pszOtherName = !idxSubstream ? pStreamInfo->pszName : pSubstreamInfo->pszName;
754 size_t const cchOtherName = pszOtherName ? strlen(pszOtherName) : 0;
755
756 /* Do the name stuff. */
757 size_t const cchName = cchOtherName ? cchStreamNo + 1 + cchOtherName : cchStreamNo;
758 size_t const cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
759 if (*pcbDirEntry < cbNeeded)
760 {
761 Log3(("rtFsPdbDir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu\n", *pcbDirEntry, cbNeeded));
762 *pcbDirEntry = cbNeeded;
763 return VERR_BUFFER_OVERFLOW;
764 }
765 pDirEntry->cbName = (uint16_t)cchName;
766 memcpy(pDirEntry->szName, szStreamNo, cchStreamNo);
767 if (cchOtherName)
768 {
769 pDirEntry->szName[cchStreamNo] = '-';
770 memcpy(&pDirEntry->szName[cchStreamNo + 1], pszOtherName, cchOtherName);
771 }
772 pDirEntry->szName[cchName] = '\0';
773
774 if (cchOtherName && !idxSubstream)
775 {
776 Assert(cchStreamNo <= 8);
777 pDirEntry->cwcShortName = cchStreamNo;
778 szStreamNo[cchStreamNo] = '\0';
779 int rc = RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), szStreamNo);
780 AssertRC(rc);
781 }
782 else
783 {
784 pDirEntry->cwcShortName = 0;
785 pDirEntry->wszShortName[0] = '\0';
786 }
787
788 /* Provide the other info. */
789 rtFsPdbPopulateObjInfo(pPdb, &pDirEntry->Info, enmAddAttr,
790 !pSubstreamInfo ? pStreamInfo->cbStream : pSubstreamInfo->cbSubstream,
791 idxStream, idxSubstream, false /*fIsDir*/);
792
793 /* Advance the directory location and return. */
794 if (idxSubstream >= pStreamInfo->cSubstreams)
795 {
796 pThis->u.s.idxNextSubstream = 0;
797 pThis->u.s.idxNextStream = idxStream + 1;
798 }
799 else
800 pThis->u.s.idxNextSubstream = idxSubstream + 1;
801
802 return VINF_SUCCESS;
803 }
804
805 Log3(("rtFsPdbDir_ReadDir9660: idxNext=%#x: VERR_NO_MORE_FILES\n", pThis->u.idxNext));
806 return VERR_NO_MORE_FILES;
807}
808
809
810/**
811 * PDB (root) directory operations.
812 */
813static const RTVFSDIROPS g_rtFsPdbDirOps =
814{
815 { /* Obj */
816 RTVFSOBJOPS_VERSION,
817 RTVFSOBJTYPE_DIR,
818 "PDB Dir",
819 rtFsPdbDir_Close,
820 rtFsPdbDir_QueryInfo,
821 NULL,
822 RTVFSOBJOPS_VERSION
823 },
824 RTVFSDIROPS_VERSION,
825 0,
826 { /* ObjSet */
827 RTVFSOBJSETOPS_VERSION,
828 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
829 NULL /*SetMode*/,
830 NULL /*SetTimes*/,
831 NULL /*SetOwner*/,
832 RTVFSOBJSETOPS_VERSION
833 },
834 rtFsPdbDir_Open,
835 NULL /* pfnFollowAbsoluteSymlink */,
836 NULL /* pfnOpenFile */,
837 NULL /* pfnOpenDir */,
838 NULL /* pfnCreateDir */,
839 NULL /* pfnOpenSymlink */,
840 NULL /* pfnCreateSymlink */,
841 NULL /* pfnQueryEntryInfo */,
842 NULL /* pfnUnlinkEntry */,
843 NULL /* pfnRenameEntry */,
844 rtFsPdbDir_RewindDir,
845 rtFsPdbDir_ReadDir,
846 RTVFSDIROPS_VERSION,
847};
848
849
850/**
851 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
852 */
853static DECLCALLBACK(int) rtFsPdbVol_Close(void *pvThis)
854{
855 PRTFSPDBVOL pThis = (PRTFSPDBVOL)pvThis;
856 Log(("rtFsPdbVol_Close(%p)\n", pThis));
857
858 RTVfsFileRelease(pThis->hVfsBacking);
859 pThis->hVfsBacking = NIL_RTVFSFILE;
860
861 RTMemFree(pThis->pbRoot);
862 pThis->pbRoot = NULL;
863
864 RTMemFree(pThis->pszzNames);
865 pThis->pszzNames = NULL;
866
867 if (pThis->papStreamInfo)
868 {
869 uint32_t idxStream = pThis->cStreams;
870 while (idxStream-- > 0)
871 RTMemFree(pThis->papStreamInfo[idxStream]);
872 RTMemFree(pThis->papStreamInfo);
873 pThis->papStreamInfo = NULL;
874 }
875 pThis->cStreams = 0;
876
877 return VINF_SUCCESS;
878}
879
880
881/**
882 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
883 */
884static DECLCALLBACK(int) rtFsPdbVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
885{
886 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
887 return VERR_WRONG_TYPE;
888}
889
890
891/**
892 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfoEx}
893 */
894static DECLCALLBACK(int) rtFsPdbVol_QueryInfoEx(void *pvThis, RTVFSQIEX enmInfo, void *pvInfo, size_t cbInfo, size_t *pcbRet)
895{
896 PRTFSPDBVOL pThis = (PRTFSPDBVOL)pvThis;
897 LogFlow(("rtFsPdbVol_QueryInfo(%p, %d,, %#zx,)\n", pThis, enmInfo, cbInfo));
898 RT_NOREF(pThis, pvInfo, cbInfo, pcbRet);
899
900 ssize_t cchRet;
901 switch (enmInfo)
902 {
903 /* This is the same as the symbol chache subdir name: */
904 case RTVFSQIEX_VOL_LABEL:
905 if ( pThis->enmVersion == RTFSPDBVER_2
906 || RTUuidIsNull(&pThis->Uuid))
907 cchRet = RTStrPrintf2((char *)pvInfo, cbInfo, "%08X%x", pThis->uTimestamp, pThis->uAge);
908 else
909 cchRet = RTStrPrintf2((char *)pvInfo, cbInfo, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x",
910 pThis->Uuid.Gen.u32TimeLow,
911 pThis->Uuid.Gen.u16TimeMid,
912 pThis->Uuid.Gen.u16TimeHiAndVersion,
913 pThis->Uuid.Gen.u8ClockSeqHiAndReserved,
914 pThis->Uuid.Gen.u8ClockSeqLow,
915 pThis->Uuid.Gen.au8Node[0],
916 pThis->Uuid.Gen.au8Node[1],
917 pThis->Uuid.Gen.au8Node[2],
918 pThis->Uuid.Gen.au8Node[3],
919 pThis->Uuid.Gen.au8Node[4],
920 pThis->Uuid.Gen.au8Node[5],
921 pThis->uAge);
922 break;
923
924 /* This exposes the PDB and VC versions: */
925 case RTVFSQIEX_VOL_LABEL_ALT:
926 cchRet = RTStrPrintf2((char *)pvInfo, cbInfo,
927 "pdb-v%u-%u", pThis->enmVersion == RTFSPDBVER_2 ? 2 : 7, pThis->uVcDate);
928 break;
929
930 case RTVFSQIEX_VOL_SERIAL:
931 if (cbInfo == sizeof(uint64_t) || cbInfo == sizeof(uint32_t))
932 {
933 *pcbRet = cbInfo;
934 ((uint32_t *)pvInfo)[0] = pThis->uTimestamp;
935 if (cbInfo == sizeof(uint64_t))
936 ((uint32_t *)pvInfo)[1] = pThis->uAge;
937 return VINF_SUCCESS;
938 }
939 if ( pThis->enmVersion != RTFSPDBVER_2
940 && !RTUuidIsNull(&pThis->Uuid))
941 {
942 *pcbRet = sizeof(RTUUID);
943 if (cbInfo == sizeof(RTUUID))
944 {
945 *(PRTUUID)pvInfo = pThis->Uuid;
946 return VINF_SUCCESS;
947 }
948 }
949 else
950 *pcbRet = sizeof(uint64_t);
951 return cbInfo < *pcbRet ? VERR_BUFFER_OVERFLOW : VERR_BUFFER_UNDERFLOW;
952
953 default:
954 return VERR_NOT_SUPPORTED;
955 }
956
957 if (cchRet > 0)
958 {
959 *pcbRet = (size_t)cchRet;
960 return VINF_SUCCESS;
961 }
962 *pcbRet = (size_t)-cchRet;
963 return VERR_BUFFER_OVERFLOW;
964}
965
966
967/**
968 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
969 */
970static DECLCALLBACK(int) rtFsPdbVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
971{
972 PRTFSPDBVOL pThis = (PRTFSPDBVOL)pvThis;
973 PRTFSPDBDIROBJ pNewDir;
974 int rc = RTVfsNewDir(&g_rtFsPdbDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
975 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
976 if (RT_SUCCESS(rc))
977 {
978 pNewDir->pPdb = pThis;
979 pNewDir->u.idxNext = 0;
980 return VINF_SUCCESS;
981 }
982 return rc;
983}
984
985
986/**
987 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
988 */
989static DECLCALLBACK(int) rtFsPdbVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
990{
991 RT_NOREF(pvThis, off, cb, pfUsed);
992 return VERR_NOT_IMPLEMENTED;
993}
994
995
996DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsPdbVolOps =
997{
998 { /* Obj */
999 RTVFSOBJOPS_VERSION,
1000 RTVFSOBJTYPE_VFS,
1001 "PDB",
1002 rtFsPdbVol_Close,
1003 rtFsPdbVol_QueryInfo,
1004 rtFsPdbVol_QueryInfoEx,
1005 RTVFSOBJOPS_VERSION
1006 },
1007 RTVFSOPS_VERSION,
1008 0 /* fFeatures */,
1009 rtFsPdbVol_OpenRoot,
1010 rtFsPdbVol_QueryRangeState,
1011 RTVFSOPS_VERSION
1012};
1013
1014
1015/**
1016 * Internal helper for reading an entire stream into a heap block.
1017 *
1018 * Use RTMemTmpFree to free the returned memory when done.
1019 */
1020static int rtFsPdbVolReadStreamToHeap(PRTFSPDBVOL pThis, uint32_t idxStream,
1021 uint8_t **ppbStream, uint32_t *pcbStream, PRTERRINFO pErrInfo)
1022{
1023 *ppbStream = NULL;
1024 *pcbStream = 0;
1025 if (idxStream >= pThis->cStreams)
1026 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND, "idxStream=%#x not found, max %#x", idxStream, pThis->cStreams);
1027
1028 /*
1029 * Fake a handle and reuse the file read code.
1030 */
1031 RTFSPDBFILEOBJ FileObj;
1032 rtFsPdbPopulateFileObjFromInfo(&FileObj, pThis, idxStream, NULL);
1033
1034 size_t const cbDst = (size_t)FileObj.cbStream + !FileObj.cbStream;
1035 uint8_t * const pbDst = (uint8_t *)RTMemTmpAllocZ(cbDst);
1036 if (!pbDst)
1037 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
1038 "Failed to allocate memory for reading stream #%x: %#zx bytes", idxStream, cbDst);
1039
1040 RTSGSEG SgSeg = { pbDst, FileObj.cbStream };
1041 RTSGBUF SgBuf;
1042 RTSgBufInit(&SgBuf, &SgSeg, 1);
1043
1044 int rc = rtFsPdbFile_Read(&FileObj, 0, &SgBuf, true /*fBlocking*/, NULL);
1045 if (RT_SUCCESS(rc))
1046 {
1047 *ppbStream = pbDst;
1048 *pcbStream = FileObj.cbStream;
1049 }
1050 else
1051 RTMemTmpFree(pbDst);
1052 return rc;
1053}
1054
1055
1056/**
1057 * Internal helper for reading from a stream.
1058 */
1059static int rtFsPdbVolReadStreamAt(PRTFSPDBVOL pThis, uint32_t idxStream, uint32_t offStream,
1060 void *pvBuf, size_t cbToRead, size_t *pcbRead)
1061{
1062 RTFSPDBFILEOBJ FileObj;
1063 rtFsPdbPopulateFileObjFromInfo(&FileObj, pThis, idxStream, NULL);
1064
1065 RTSGSEG SgSeg = { pvBuf, cbToRead };
1066 RTSGBUF SgBuf;
1067 RTSgBufInit(&SgBuf, &SgSeg, 1);
1068
1069 return rtFsPdbFile_Read(&FileObj, offStream, &SgBuf, true /*fBlocking*/, pcbRead);
1070}
1071
1072
1073/**
1074 * Adds a substream.
1075 */
1076static void rtFsPdbVolAddSubstream(PRTFSPDBVOL pThis, uint32_t idxStream, uint8_t idxSubstream,
1077 uint32_t offSubstream, uint32_t cbSubstream, const char *pszName)
1078{
1079 /*
1080 * Sanity checking.
1081 */
1082 Assert(idxSubstream > 0); /* zero is used for the parent stream when enumerating directories. */
1083
1084 AssertReturnVoid(idxStream < pThis->cStreams);
1085 PRTFSPDBSTREAMINFO const pStreamInfo = pThis->papStreamInfo[idxStream];
1086 Assert(RTStrStartsWith(pszName, pStreamInfo->pszName));
1087 AssertReturnVoid(offSubstream < pStreamInfo->cbStream);
1088 AssertStmt(cbSubstream <= pStreamInfo->cbStream - offSubstream, cbSubstream = pStreamInfo->cbStream - offSubstream);
1089
1090 AssertReturnVoid(pThis->cSubstreams < RT_ELEMENTS(pThis->aSubstreams));
1091 PRTFSPDBSUBSTREAM const pSubstreamInfo = &pThis->aSubstreams[pThis->cSubstreams];
1092 Assert( pThis->cSubstreams == 0
1093 || pSubstreamInfo[-1].idxStream < idxStream
1094 || ( pSubstreamInfo[-1].idxStream == idxStream
1095 && pSubstreamInfo[-1].idxSubstream < idxSubstream
1096 && pSubstreamInfo[-1].offSubstream <= offSubstream));
1097
1098 /*
1099 * Add it.
1100 */
1101 pSubstreamInfo->idxStream = idxStream;
1102 pSubstreamInfo->idxSubstream = idxSubstream;
1103 pSubstreamInfo->offSubstream = offSubstream;
1104 pSubstreamInfo->cbSubstream = cbSubstream;
1105 pSubstreamInfo->pszName = pszName;
1106
1107 if (!pStreamInfo->cSubstreams)
1108 pStreamInfo->idxFirstSubstream = pThis->cSubstreams;
1109 pStreamInfo->cSubstreams++;
1110
1111 pThis->cSubstreams++;
1112}
1113
1114
1115/**
1116 * Helper for rtFsPdbVolLoadStream3 to set standard stream names.
1117 */
1118static void rtFsPdbVolSetDefaultStreamName(PRTFSPDBVOL pThis, uint32_t idxStream, const char *pszDefaultName)
1119{
1120 if (idxStream < pThis->cStreams)
1121 {
1122 /* We override entries from the string table, as the standard stream
1123 names must be assumed to work when the streams are present! */
1124 if ((uintptr_t)pThis->papStreamInfo[idxStream]->pszName - (uintptr_t)pThis->pszzNames < pThis->cbNames)
1125 Log(("rtFsPdbVolSetDefaultStreamName: Overriding string table name for stream %#x: %s -> %s\n",
1126 idxStream, pThis->papStreamInfo[idxStream]->pszName, pszDefaultName));
1127 pThis->papStreamInfo[idxStream]->pszName = pszDefaultName;
1128 }
1129}
1130
1131
1132/**
1133 * Worker for rtFsPdbVolTryInit that extracts info from the DBI stream.
1134 */
1135static int rtFsPdbVolLoadStream3(PRTFSPDBVOL pThis, PRTERRINFO pErrInfo)
1136{
1137 /* On the offchance that the DBI stream is missing... */
1138 if (3 >= pThis->cStreams)
1139 return VINF_SUCCESS;
1140
1141 /*
1142 * Read the header.
1143 */
1144 union
1145 {
1146 RTPDBDBIHDR New;
1147 RTPDBDBIHDROLD Old;
1148 uint16_t aidxStream[32];
1149 } Hdr;
1150 size_t cbRead = 0;
1151 int rc = rtFsPdbVolReadStreamAt(pThis, 3, 0, &Hdr, sizeof(Hdr.New), &cbRead);
1152 if (RT_FAILURE(rc))
1153 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error eeading the DBI header");
1154
1155 if (Hdr.New.uSignature == RTPDBDBIHDR_SIGNATURE)
1156 {
1157 Log(("rtFsPdbVolLoadStream3: New DBI header\n"));
1158 if (cbRead != sizeof(Hdr.New))
1159 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus DBI header size: cbRead=%#zx, expected %#zx",
1160 cbRead, sizeof(Hdr.New));
1161 if (Hdr.New.uVcVersion < RTPDBDBIHDR_VCVER_50)
1162 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus DBI header version: %u (%#x), expected min %u",
1163 Hdr.New.uVcVersion, Hdr.New.uVcVersion, RTPDBDBIHDR_VCVER_50);
1164 if (Hdr.New.uVcVersion < RTPDBDBIHDR_VCVER_60)
1165 Hdr.New.cbEditContinueSubstream = 0;
1166 pThis->DbiHdr = Hdr.New;
1167 }
1168 else
1169 {
1170 /* Convert old header to new to new. */
1171 if (cbRead < sizeof(Hdr.Old))
1172 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus DBI header size: cbRead=%#zx, expected %#zx",
1173 cbRead, sizeof(Hdr.Old));
1174 Log(("rtFsPdbVolLoadStream3: Old DBI header\n"));
1175 RT_ZERO(pThis->DbiHdr);
1176 pThis->DbiHdr.idxMFC = UINT16_MAX;
1177 pThis->DbiHdr.idxGlobalStream = Hdr.Old.idxGlobalStream;
1178 pThis->DbiHdr.idxPublicStream = Hdr.Old.idxPublicStream;
1179 pThis->DbiHdr.idxSymRecStream = Hdr.Old.idxSymRecStream;
1180 pThis->DbiHdr.cbModInfoSubstream = Hdr.Old.cbModInfoSubstream;
1181 pThis->DbiHdr.cbSectContribSubstream = Hdr.Old.cbSectContribSubstream;
1182 pThis->DbiHdr.cbSrcInfoSubstream = Hdr.Old.cbSrcInfoSubstream;
1183 pThis->DbiHdr.uAge = pThis->uAge;
1184 }
1185
1186 Log2((" DBI Hdr uSignature = %#x\n", pThis->DbiHdr.uSignature));
1187 Log2((" DBI Hdr uVcVersion = %#x\n", pThis->DbiHdr.uVcVersion));
1188 if (pThis->DbiHdr.uAge == pThis->uAge)
1189 Log2((" DBI Hdr uAge = %#x (same as PDB header)\n", pThis->DbiHdr.uAge));
1190 else
1191 Log2((" DBI Hdr uAge = %#x - differs from DB.uAge %#x!\n", pThis->DbiHdr.uAge, pThis->uAge));
1192 Log2((" DBI Hdr idxGlobalStream = %#x\n", pThis->DbiHdr.idxGlobalStream));
1193 if (pThis->DbiHdr.PdbDllVer.New.fNewVerFmt)
1194 {
1195 Log2((" DBI Hdr PdbDllVer.u16 = %#x - %u.%u (new)\n", pThis->DbiHdr.PdbDllVer.u16,
1196 pThis->DbiHdr.PdbDllVer.New.uMajor, pThis->DbiHdr.PdbDllVer.New.uMinor));
1197 Log2((" DBI Hdr uPdbDllBuild = %u (%#x)\n", pThis->DbiHdr.uPdbDllBuild, pThis->DbiHdr.uPdbDllBuild));
1198 Log2((" DBI Hdr uPdbDllRBuild = %u (%#x)\n", pThis->DbiHdr.uPdbDllRBuild, pThis->DbiHdr.uPdbDllRBuild));
1199 }
1200 else
1201 {
1202 Log2((" DBI Hdr PdbDllVer = %#x - %u.%u.%u (old)\n", pThis->DbiHdr.PdbDllVer.u16,
1203 pThis->DbiHdr.PdbDllVer.Old.uMajor, pThis->DbiHdr.PdbDllVer.Old.uMinor,
1204 pThis->DbiHdr.PdbDllVer.Old.uRBuild));
1205 if (pThis->DbiHdr.uPdbDllBuild)
1206 Log2((" DBI Hdr uPdbDllBuild = %u (%#x)!!\n", pThis->DbiHdr.uPdbDllBuild, pThis->DbiHdr.uPdbDllBuild));
1207 if (pThis->DbiHdr.uPdbDllRBuild)
1208 Log2((" DBI Hdr uPdbDllRBuild = %u (%#x)!!\n", pThis->DbiHdr.uPdbDllRBuild, pThis->DbiHdr.uPdbDllRBuild));
1209 }
1210 Log2((" DBI Hdr idxPublicStream = %#x\n", pThis->DbiHdr.idxPublicStream));
1211 Log2((" DBI Hdr idxSymRecStream = %#x\n", pThis->DbiHdr.idxSymRecStream));
1212 Log2((" DBI Hdr cbModInfoSubstream = %#x\n", pThis->DbiHdr.cbModInfoSubstream));
1213 Log2((" DBI Hdr cbSectContribSubstream = %#x\n", pThis->DbiHdr.cbSectContribSubstream));
1214 Log2((" DBI Hdr cbSectionMapSubstream = %#x\n", pThis->DbiHdr.cbSectionMapSubstream));
1215 Log2((" DBI Hdr cbSrcInfoSubstream = %#x\n", pThis->DbiHdr.cbSrcInfoSubstream));
1216 Log2((" DBI Hdr cbTypeServerMapSubstream = %#x\n", pThis->DbiHdr.cbTypeServerMapSubstream));
1217 Log2((" DBI Hdr idxMFC = %#x\n", pThis->DbiHdr.idxMFC));
1218 Log2((" DBI Hdr cbOptDbgHdr = %#x\n", pThis->DbiHdr.cbOptDbgHdr));
1219 Log2((" DBI Hdr cbEditContinueSubstream = %#x\n", pThis->DbiHdr.cbEditContinueSubstream));
1220 Log2((" DBI Hdr fFlags = %#x%s%s%s%s%s\n", pThis->DbiHdr.fFlags, pThis->DbiHdr.fFlags ? " -" : "",
1221 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_INCREMENTAL_LINK ? " IncrmentalLink" : "",
1222 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_PRIVATE_SYMS_STRIPPED ? " PrivateSymsStripped" : "",
1223 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_CONFLICTING_TYPES ? " ConflictingTypes" : "",
1224 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_RESERVED ? " !ReservedFlagsSet!" : ""));
1225 Log2((" DBI Hdr uMachine = %#x\n", pThis->DbiHdr.uMachine));
1226 Log2((" DBI Hdr uReserved = %#x\n", pThis->DbiHdr.uReserved));
1227
1228 /*
1229 * Apply new standard stream names.
1230 */
1231 rtFsPdbVolSetDefaultStreamName(pThis, pThis->DbiHdr.idxGlobalStream, "global-symbol-hash");
1232 rtFsPdbVolSetDefaultStreamName(pThis, pThis->DbiHdr.idxPublicStream, "public-symbol-hash");
1233 rtFsPdbVolSetDefaultStreamName(pThis, pThis->DbiHdr.idxSymRecStream, "symbol-records");
1234
1235 /*
1236 * Fill in substream info.
1237 */
1238 uint32_t offSubstream = sizeof(Hdr.New);
1239 if (pThis->DbiHdr.cbModInfoSubstream)
1240 {
1241 rtFsPdbVolAddSubstream(pThis, 3, 1, offSubstream, pThis->DbiHdr.cbModInfoSubstream, "dbi-module-info");
1242 offSubstream += pThis->DbiHdr.cbModInfoSubstream;
1243 }
1244 if (pThis->DbiHdr.cbSectContribSubstream)
1245 {
1246 rtFsPdbVolAddSubstream(pThis, 3, 2, offSubstream, pThis->DbiHdr.cbSectContribSubstream, "dbi-section-contributions");
1247 offSubstream += pThis->DbiHdr.cbSectContribSubstream;
1248 }
1249 if (pThis->DbiHdr.cbSectionMapSubstream)
1250 {
1251 rtFsPdbVolAddSubstream(pThis, 3, 3, offSubstream, pThis->DbiHdr.cbSectionMapSubstream, "dbi-section-map");
1252 offSubstream += pThis->DbiHdr.cbSectionMapSubstream;
1253 }
1254 if (pThis->DbiHdr.cbSrcInfoSubstream)
1255 {
1256 rtFsPdbVolAddSubstream(pThis, 3, 4, offSubstream, pThis->DbiHdr.cbSrcInfoSubstream, "dbi-source-info");
1257 offSubstream += pThis->DbiHdr.cbSrcInfoSubstream;
1258 }
1259 if (pThis->DbiHdr.cbTypeServerMapSubstream)
1260 {
1261 rtFsPdbVolAddSubstream(pThis, 3, 5, offSubstream, pThis->DbiHdr.cbTypeServerMapSubstream, "dbi-type-server-map");
1262 offSubstream += pThis->DbiHdr.cbTypeServerMapSubstream;
1263 }
1264 if (pThis->DbiHdr.cbEditContinueSubstream)
1265 {
1266 rtFsPdbVolAddSubstream(pThis, 3, 6, offSubstream, pThis->DbiHdr.cbEditContinueSubstream, "dbi-continue-and-edit");
1267 offSubstream += pThis->DbiHdr.cbEditContinueSubstream;
1268 }
1269 uint32_t const offOptDbgHdr = offSubstream;
1270 if (pThis->DbiHdr.cbOptDbgHdr)
1271 {
1272 rtFsPdbVolAddSubstream(pThis, 3, 7, offSubstream, pThis->DbiHdr.cbOptDbgHdr, "dbi-optional-header");
1273 offSubstream += pThis->DbiHdr.cbOptDbgHdr;
1274 }
1275 if (offSubstream < pThis->papStreamInfo[3]->cbStream)
1276 rtFsPdbVolAddSubstream(pThis, 3, 8, offSubstream, pThis->papStreamInfo[3]->cbStream - offSubstream, "dbi-unknown");
1277
1278 /*
1279 * Read the optional header if present, it identifies a bunch of standard streams.
1280 */
1281 if (pThis->DbiHdr.cbOptDbgHdr > 1)
1282 {
1283 RT_ZERO(Hdr);
1284 rc = rtFsPdbVolReadStreamAt(pThis, 3, offOptDbgHdr, &Hdr, RT_MIN(sizeof(Hdr), pThis->DbiHdr.cbOptDbgHdr), &cbRead);
1285 if (RT_FAILURE(rc))
1286 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error eeading the DBI optional header at %#x LB %#x",
1287 offOptDbgHdr, pThis->DbiHdr.cbOptDbgHdr);
1288 static const char * const s_apszIdxNames[] =
1289 {
1290 /* [RTPDBDBIOPT_IDX_FPO_MASM] = */ "image-fpo-masm-section",
1291 /* [RTPDBDBIOPT_IDX_EXCEPTION] = */ "image-exception",
1292 /* [RTPDBDBIOPT_IDX_FIXUP] = */ "image-fixup",
1293 /* [RTPDBDBIOPT_IDX_OMAP_TO_SRC] = */ "omap-to-src",
1294 /* [RTPDBDBIOPT_IDX_OMAP_FROM_SRC] = */ "omap-from-src",
1295 /* [RTPDBDBIOPT_IDX_SECTION_HEADERS] = */ "image-section-headers",
1296 /* [RTPDBDBIOPT_IDX_CLR_TOKEN_ID_MAP] = */ "clr-token-id-map",
1297 /* [RTPDBDBIOPT_IDX_XDATA] = */ "image-xdata-section",
1298 /* [RTPDBDBIOPT_IDX_PDATA] = */ "image-pdata-section",
1299 /* [RTPDBDBIOPT_IDX_FPO] = */ "image-fpo",
1300 /* [RTPDBDBIOPT_IDX_ORG_SECTION_HEADERS] = */ "image-orginal-section-headers",
1301 };
1302 uint32_t idxName = pThis->DbiHdr.cbOptDbgHdr / 2;
1303 if (idxName > RT_ELEMENTS(s_apszIdxNames))
1304 {
1305#ifdef LOG_ENABLED
1306 while (idxName-- > RT_ELEMENTS(s_apszIdxNames))
1307 Log(("Unknown DBI optional header entry %u: %#x\n", idxName, Hdr.aidxStream[idxName]));
1308#endif
1309 idxName = RT_ELEMENTS(s_apszIdxNames);
1310 }
1311 while (idxName-- > 0)
1312 rtFsPdbVolSetDefaultStreamName(pThis, Hdr.aidxStream[idxName], s_apszIdxNames[idxName]);
1313 }
1314
1315 return VINF_SUCCESS;
1316}
1317
1318
1319/**
1320 * Worker for rtFsPdbVolLoadStream1 that parses the PDB metadata stream.
1321 */
1322static int rtFsPdbVolLoadStream1Inner(PRTFSPDBVOL pThis, uint8_t const *pb, size_t cb, PRTERRINFO pErrInfo)
1323{
1324 /*
1325 * Process the header part.
1326 */
1327 if (pThis->enmVersion == RTFSPDBVER_2)
1328 {
1329 PCRTPDB20NAMES pHdr = (PCRTPDB20NAMES)pb;
1330 if (cb < sizeof(*pHdr))
1331 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
1332 "Stream #1 is smaller than expected: %#x, vs min %#zx", cb, sizeof(*pHdr));
1333 pThis->uTimestamp = pHdr->uTimestamp;
1334 pThis->uAge = pHdr->uAge;
1335 pThis->uVcDate = pHdr->uVersion;
1336 pThis->cbNames = pHdr->cbNames;
1337 pb += sizeof(*pHdr);
1338 cb -= sizeof(*pHdr);
1339 }
1340 else
1341 {
1342 PCRTPDB70NAMES pHdr = (PCRTPDB70NAMES)pb;
1343 if (cb < sizeof(*pHdr))
1344 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
1345 "Stream #1 is smaller than expected: %#x, vs min %#zx", cb, sizeof(*pHdr));
1346 pThis->uTimestamp = pHdr->uTimestamp;
1347 pThis->uAge = pHdr->uAge;
1348 pThis->uVcDate = pHdr->uVersion;
1349 pThis->Uuid = pHdr->Uuid;
1350 pThis->cbNames = pHdr->cbNames;
1351 pb += sizeof(*pHdr);
1352 cb -= sizeof(*pHdr);
1353 }
1354
1355 /*
1356 * Set default stream names that depends on the VC date.
1357 */
1358 /** @todo */
1359
1360 /*
1361 * Load the string table if present.
1362 */
1363 if (pThis->cbNames == 0)
1364 return VINF_SUCCESS;
1365 if (cb < pThis->cbNames)
1366 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1367 "Bogus string table: size given as %#x, but only %#x bytes left", pThis->cbNames, cb);
1368 pThis->pszzNames = (char *)RTMemDupEx(pb, pThis->cbNames, 2 /* two extra zero bytes */);
1369 if (!pThis->pszzNames)
1370 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY, "Failed to allocate string table: %#x + 2 bytes", pThis->cbNames);
1371
1372 pb += pThis->cbNames;
1373 cb -= pThis->cbNames;
1374
1375 /** @todo MIPS format variation may have alignment padding here. */
1376
1377 /*
1378 * What follows now is the hash table mapping string table offset to stream
1379 * numbers. This is frequently misaligned, so we take care to load the
1380 * stuff byte-by-byte on architectures which are sensive to such things.
1381 *
1382 * Structure description: https://llvm.org/docs/PDB/HashTable.html
1383 */
1384 if (cb < 4 * sizeof(uint32_t))
1385 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1386 "Bogus hash table: Min size of 16 bytes, but only %#x bytes left", cb);
1387#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
1388# define GET_U32(a_pb, a_off) *(uint32_t const *)&a_pb[(a_off)]
1389#else
1390# define GET_U32(a_pb, a_off) RT_MAKE_U32_FROM_U8(a_pb[0 + (a_off)], a_pb[1 + (a_off)], a_pb[2 + (a_off)], a_pb[3 + (a_off)])
1391#endif
1392
1393 /* Sizes (irrelevant to us as we're not reconstructing the actual hash table yet): */
1394 uint32_t const cTabSize = GET_U32(pb, 0);
1395 uint32_t const cTabCapacity = GET_U32(pb, 4);
1396 if (cTabSize > cTabCapacity)
1397 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1398 "Bogus hash table: cTabSize=%#x > cTabCapacity=%#x", cTabSize, cTabCapacity);
1399 if (cTabSize == 0)
1400 return VINF_SUCCESS;
1401 pb += 8;
1402 cb -= 8;
1403
1404 /* Present bit vector: */
1405 uint32_t const cPresentVec = RT_MAKE_U32_FROM_U8(pb[0], pb[1], pb[2], pb[3]);
1406 pb += 4;
1407 cb -= 4;
1408 if ((uint64_t)cPresentVec + 1 > cb / 4)
1409 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1410 "Bogus hash table: cPresentVec=%#x + 1 delete vec > %#x", cPresentVec, cb / 4);
1411 uint8_t const * const pbPresentBits = pb;
1412 pb += cPresentVec * 4;
1413 cb -= cPresentVec * 4;
1414
1415 /* Scan the present vector as it gives the number of key/value pairs following the deleted bit vector. */
1416 uint64_t cPresent = 0;
1417 for (uint32_t off = 0; off < cPresentVec; off += 4)
1418 {
1419 uint32_t uWord = GET_U32(pbPresentBits, off);
1420 while (uWord)
1421 {
1422 cPresent += uWord & 1;
1423 uWord >>= 1;
1424 }
1425 }
1426
1427 /* Deleted vector (irrelevant to us): */
1428 uint32_t const cDeletedVec = RT_MAKE_U32_FROM_U8(pb[0], pb[1], pb[2], pb[3]);
1429 pb += 4;
1430 cb -= 4;
1431 if ((uint64_t)cDeletedVec + cPresent > cb / 4)
1432 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1433 "Bogus hash table: cDeletedVec=%#x cPresent=%#RX64 > %#x", cDeletedVec, cPresent, cb / 4);
1434 pb += cDeletedVec * 4;
1435 cb -= cDeletedVec * 4;
1436
1437 /* What remains is cPresent pairs of string table offset and stream IDs. */
1438 Assert(cb / 4 >= cPresent);
1439 for (uint32_t i = 0, off = 0; i < cPresent; i++, off += 8)
1440 {
1441 uint32_t const offString = GET_U32(pb, off);
1442 uint32_t const idxStream = GET_U32(pb, off + 4);
1443 if (offString < pThis->cbNames)
1444 {
1445 if (idxStream < pThis->cStreams)
1446 {
1447 /* Skip leading slashes. In-string slashes are removed afterwards. */
1448 const char *pszName = &pThis->pszzNames[offString];
1449 while (*pszName == '/' || *pszName == '\\')
1450 pszName++;
1451 if (pszName[0])
1452 pThis->papStreamInfo[idxStream]->pszName = pszName;
1453 }
1454 else
1455 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1456 "Bogus hash table entry %#x: offString=%#x, max %#x (offString=%#x '%s')",
1457 i, idxStream, pThis->cStreams, offString, &pThis->pszzNames[offString]);
1458 }
1459 else
1460 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1461 "Bogus hash table entry %#x: offString=%#x, max %#x (idxStream=%#x)",
1462 i, offString, pThis->cbNames, idxStream);
1463 }
1464
1465 /*
1466 * Sanitize strings and convert any in-string slashes to underscores to
1467 * avoid VFS confusion. This has to be done after loading the hash table
1468 * so the slash skipping there works correctly should anyone do sub-string
1469 * optimizations involving slashes.
1470 */
1471 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1472 {
1473 char *pszName = (char *)pThis->papStreamInfo[idxStream]->pszName;
1474 if (pszName)
1475 {
1476 RTStrPurgeEncoding(pszName);
1477 pszName = strpbrk(pszName, "/\\");
1478 while (pszName)
1479 {
1480 *pszName = '_';
1481 pszName = strpbrk(pszName + 1, "/\\");
1482 }
1483 }
1484 }
1485
1486 return VINF_SUCCESS;
1487}
1488
1489
1490/**
1491 * Worker for rtFsPdbVolTryInit that loads the PDB metadata from stream \#1.
1492 */
1493static int rtFsPdbVolLoadStream1(PRTFSPDBVOL pThis, PRTERRINFO pErrInfo)
1494{
1495 /*
1496 * Assign default stream names based on basic PDB version.
1497 */
1498 switch (pThis->cStreams)
1499 {
1500 default:
1501 case 5:
1502 if (pThis->enmVersion == RTFSPDBVER_7) /** @todo condition? */
1503 pThis->papStreamInfo[4]->pszName = "name-map";
1504 RT_FALL_THRU();
1505 case 4:
1506 pThis->papStreamInfo[3]->pszName = "dbi";
1507 RT_FALL_THRU();
1508 case 3:
1509 pThis->papStreamInfo[2]->pszName = "tpi";
1510 RT_FALL_THRU();
1511 case 2:
1512 pThis->papStreamInfo[1]->pszName = "pdb";
1513 RT_FALL_THRU();
1514 case 1:
1515 pThis->papStreamInfo[0]->pszName = "root";
1516 break;
1517 }
1518 if (pThis->cStreams >= 2)
1519 {
1520 /*
1521 * Read the stream into temporary heap memory and process it.
1522 */
1523 uint8_t *pbStream = NULL;
1524 uint32_t cbStream = 0;
1525 int rc = rtFsPdbVolReadStreamToHeap(pThis, 1, &pbStream, &cbStream, pErrInfo);
1526 if (RT_SUCCESS(rc))
1527 {
1528 rc = rtFsPdbVolLoadStream1Inner(pThis, pbStream, cbStream, pErrInfo);
1529 RTMemTmpFree(pbStream);
1530 }
1531 return rc;
1532 }
1533 return VINF_SUCCESS;
1534}
1535
1536
1537/**
1538 * Helper for rtFsPdbVolTryInit.
1539 */
1540static int rtFsPdbVolAllocInitialStreamInfo(PRTFSPDBVOL pThis, PRTERRINFO pErrInfo)
1541{
1542 pThis->papStreamInfo = (PRTFSPDBSTREAMINFO *)RTMemAllocZ(sizeof(pThis->papStreamInfo[0]) * pThis->cStreams);
1543 if (pThis->papStreamInfo)
1544 {
1545 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1546 {
1547 pThis->papStreamInfo[idxStream] = (PRTFSPDBSTREAMINFO)RTMemAllocZ(sizeof(*pThis->papStreamInfo[0]));
1548 if (pThis->papStreamInfo[idxStream])
1549 { }
1550 else
1551 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY, "Failed to allocate RTFSPDBSTREAMINFO #%u", idxStream);
1552 }
1553 return VINF_SUCCESS;
1554 }
1555 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
1556 "Failed to allocate papStreamInfo array with %u entries", pThis->cStreams);
1557}
1558
1559
1560/**
1561 * Worker for RTFsPdbVolOpen.
1562 *
1563 * @returns IPRT status code.
1564 * @param pThis The PDB VFS instance to initialize.
1565 * @param hVfsSelf The PDB VFS handle (no reference consumed).
1566 * @param hVfsBacking The file backing the alleged PDB file system.
1567 * Reference is consumed (via rtFsPdbVol_Close).
1568 * @param fFlags Flags, RTFSPDB_F_XXX.
1569 * @param pErrInfo Where to return additional error info. Can be NULL.
1570 */
1571static int rtFsPdbVolTryInit(PRTFSPDBVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
1572{
1573 /*
1574 * First initialize the state so that rtFsPdbVol_Close won't trip up.
1575 */
1576 pThis->hVfsSelf = hVfsSelf;
1577 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsPdbVol_Close releases it. */
1578 pThis->cbBacking = 0;
1579 pThis->cBackingPages = 0;
1580 pThis->fFlags = fFlags;
1581 pThis->cbPage = 0;
1582 pThis->enmVersion = RTFSPDBVER_INVALID;
1583 pThis->uAge = 0;
1584 pThis->uTimestamp = 0;
1585 RTUuidClear(&pThis->Uuid);
1586 pThis->cbNames = 0;
1587 pThis->pszzNames = NULL;
1588 pThis->papStreamInfo = NULL;
1589 pThis->cSubstreams = 0;
1590
1591 /*
1592 * Do init stuff that may fail.
1593 */
1594 int rc = RTVfsFileQuerySize(hVfsBacking, &pThis->cbBacking);
1595 if (RT_FAILURE(rc))
1596 return rc;
1597
1598 /*
1599 * Read, validate and load the file header.
1600 */
1601 union
1602 {
1603 RTPDB70HDR Hdr70;
1604 RTPDB20HDR Hdr20;
1605 } Buf;
1606 RT_ZERO(Buf);
1607
1608 rc = RTVfsFileReadAt(hVfsBacking, 0, &Buf, sizeof(Buf), NULL);
1609 if (RT_FAILURE(rc))
1610 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to file header");
1611
1612 uint32_t cPages;
1613 uint32_t cbMinRootStream;
1614 uint64_t offRootPageMap;
1615 if (memcmp(Buf.Hdr70.szSignature, RTPDB_SIGNATURE_700, sizeof(Buf.Hdr70.szSignature)) == 0)
1616 {
1617 pThis->enmVersion = RTFSPDBVER_7;
1618 pThis->cbPage = Buf.Hdr70.cbPage;
1619 pThis->cbRoot = Buf.Hdr70.cbRoot;
1620 cPages = Buf.Hdr70.cPages;
1621 cbMinRootStream = RT_UOFFSETOF(RTPDB70ROOT, aStreams[4]) + sizeof(RTPDB70PAGE) * 4;
1622 offRootPageMap = Buf.Hdr70.iRootPages * pThis->cbPage;
1623 }
1624 else if (memcmp(Buf.Hdr20.szSignature, RTPDB_SIGNATURE_200, sizeof(Buf.Hdr20.szSignature)) == 0)
1625 {
1626 pThis->enmVersion = RTFSPDBVER_2;
1627 pThis->cbPage = Buf.Hdr20.cbPage;
1628 pThis->cbRoot = Buf.Hdr20.RootStream.cbStream;
1629 cPages = Buf.Hdr20.cPages;
1630 cbMinRootStream = RT_UOFFSETOF(RTPDB20ROOT, aStreams[4]) + sizeof(RTPDB20PAGE) * 4; /** @todo ?? */
1631 offRootPageMap = RT_UOFFSETOF(RTPDB20HDR, aiRootPageMap);
1632 }
1633 else
1634 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_INVALID_MAGIC, "Unknown file header signature: %.44Rhxs", Buf.Hdr70.szSignature);
1635
1636 if ( pThis->cbPage != _4K
1637 && pThis->cbPage != _8K
1638 && pThis->cbPage != _16K
1639 && pThis->cbPage != _32K
1640 && pThis->cbPage != _64K
1641 && pThis->cbPage != _2K
1642 && pThis->cbPage != _1K
1643 && pThis->cbPage != 512)
1644 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unsupported page size: %#x", pThis->cbPage);
1645 pThis->cBackingPages = pThis->cbBacking / pThis->cbPage;
1646 if (cPages != pThis->cBackingPages)
1647 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unexpected page count: %#x, expected %#llx (page size %#x)",
1648 cPages, pThis->cBackingPages, pThis->cbPage);
1649
1650 if (offRootPageMap > pThis->cbBacking - pThis->cbPage)
1651 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus root page map start: %#llx (file size %#llx)",
1652 offRootPageMap, pThis->cbBacking);
1653 if (pThis->cbRoot < cbMinRootStream)
1654 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Root stream is smaller than expected: %#x, expected at least %#x",
1655 pThis->cbRoot, cbMinRootStream);
1656 if (pThis->cbRoot > _64M)
1657 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Root stream is too large: %#x, max supported %#x",
1658 pThis->cbRoot, _64M);
1659 if (pThis->cbRoot > pThis->cbBacking)
1660 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Root stream is too large: %#x, file size %#llx",
1661 pThis->cbRoot, pThis->cbBacking);
1662
1663 /*
1664 * Load the root stream into memory.
1665 */
1666 uint32_t const cRootPages = (pThis->cbRoot + pThis->cbPage - 1) / pThis->cbPage;
1667 pThis->pbRoot = (uint8_t *)RTMemAllocZ(RT_ALIGN_32(pThis->cbRoot, 64));
1668 if (!pThis->pbRoot)
1669 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to allocate root stream backing: %#x bytes",
1670 RT_ALIGN_32(pThis->cbRoot, 64));
1671 /* Get the root page map. */
1672 size_t const cbPageMap = cRootPages * (pThis->enmVersion == RTFSPDBVER_2 ? sizeof(RTPDB20PAGE) : sizeof(RTPDB70PAGE));
1673 void * const pvPageMap = RTMemTmpAlloc(cbPageMap);
1674 if (!pvPageMap)
1675 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to allocate %#zx bytes for the root page map", cbPageMap);
1676
1677 rc = RTVfsFileReadAt(hVfsBacking, offRootPageMap, pvPageMap, cbPageMap, NULL);
1678 if (RT_SUCCESS(rc))
1679 {
1680 /* Validate the page map. */
1681 for (uint32_t iPageMap = 0; iPageMap < cRootPages; iPageMap++)
1682 {
1683 RTPDB70PAGE const iPageNo = pThis->enmVersion == RTFSPDBVER_2
1684 ? ((PCRTPDB20PAGE)pvPageMap)[iPageMap] : ((PCRTPDB70PAGE)pvPageMap)[iPageMap];
1685 if (iPageNo > 0 && iPageNo < pThis->cBackingPages)
1686 continue;
1687 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1688 "Root page map entry %#x is out of bounds: %#x (max %#llx)",
1689 iPageMap, iPageNo, pThis->cBackingPages);
1690 break;
1691 }
1692
1693 /* Read using regular file reader. */
1694 RTFSPDBFILEOBJ FileObj;
1695 rtFsPdbPopulateFileObj(&FileObj, pThis, 0, pThis->cbRoot, pvPageMap);
1696
1697 RTSGSEG SgSeg = { pThis->pbRoot, pThis->cbRoot };
1698 RTSGBUF SgBuf;
1699 RTSgBufInit(&SgBuf, &SgSeg, 1);
1700
1701 rc = rtFsPdbFile_Read(&FileObj, 0, &SgBuf, true /*fBlocking*/, NULL);
1702 }
1703 RTMemTmpFree(pvPageMap);
1704 if (RT_FAILURE(rc))
1705 return rc;
1706
1707 /*
1708 * Validate the root stream.
1709 */
1710 if (pThis->enmVersion == RTFSPDBVER_2)
1711 {
1712 PCRTPDB20ROOT const pRoot = (PCRTPDB20ROOT)pThis->pbRoot;
1713
1714 /* Stream count. */
1715 if ( RT_UOFFSETOF_DYN(RTPDB20ROOT, aStreams[pRoot->cStreams]) <= pThis->cbRoot
1716 && pRoot->cStreams >= 1)
1717 pThis->cStreams = pRoot->cStreams;
1718 else
1719 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1720 "Bogus root stream count: %#x (cbRoot=%#x)", pRoot->cStreams, pThis->cbRoot);
1721
1722 rc = rtFsPdbVolAllocInitialStreamInfo(pThis, pErrInfo);
1723 if (RT_FAILURE(rc))
1724 return rc;
1725
1726 /* Validate stream data and page map while populating the stream info table. */
1727 PCRTPDB20PAGE const pauPageMap = (PCRTPDB20PAGE)&pRoot->aStreams[pThis->cStreams];
1728 uint32_t const cPageMapMax = pThis->cbRoot - RT_UOFFSETOF_DYN(RTPDB20ROOT, aStreams[pThis->cStreams]);
1729 uint32_t iPageMap = 0;
1730 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1731 {
1732 uint32_t cStreamPages = RTPdbSizeToPages(pRoot->aStreams[idxStream].cbStream, pThis->cbPage);
1733 if (iPageMap + cStreamPages <= cPageMapMax)
1734 {
1735 PRTFSPDBSTREAMINFO const pInfo = pThis->papStreamInfo[idxStream];
1736 pInfo->cbStream = pRoot->aStreams[idxStream].cbStream;
1737 pInfo->cPages = cStreamPages;
1738 pInfo->PageMap.pa20 = &pauPageMap[iPageMap];
1739
1740 while (cStreamPages-- > 0)
1741 if (pauPageMap[iPageMap] == 0 || pauPageMap[iPageMap] < pThis->cBackingPages)
1742 iPageMap++;
1743 else
1744 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1745 "Bogus page map entry %#x belonging to stream %#x: %#x (max %#llx)",
1746 iPageMap, idxStream, pauPageMap[iPageMap], pThis->cBackingPages);
1747 }
1748 else
1749 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1750 "Stream %#x exceeds the page map space: cbStream=%#x iPageMap=%#x cPageMapMax=%#x",
1751 idxStream, pRoot->aStreams[idxStream].cbStream, iPageMap, cPageMapMax);
1752 }
1753 }
1754 else
1755 {
1756 PCRTPDB70ROOT const pRoot = (PCRTPDB70ROOT)pThis->pbRoot;
1757
1758 /* Stream count. */
1759 if ( RT_UOFFSETOF_DYN(RTPDB70ROOT, aStreams[pRoot->cStreams]) <= pThis->cbRoot
1760 && pRoot->cStreams >= 1)
1761 pThis->cStreams = pRoot->cStreams;
1762 else
1763 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1764 "Bogus root stream count: %#x (cbRoot=%#x)", pRoot->cStreams, pThis->cbRoot);
1765
1766 rc = rtFsPdbVolAllocInitialStreamInfo(pThis, pErrInfo);
1767 if (RT_FAILURE(rc))
1768 return rc;
1769
1770 /* Validate stream data and page map while populating the stream info table. */
1771 PCRTPDB70PAGE const pauPageMap = (PCRTPDB70PAGE)&pRoot->aStreams[pThis->cStreams];
1772 uint32_t const cPageMapMax = pThis->cbRoot - RT_UOFFSETOF_DYN(RTPDB70ROOT, aStreams[pThis->cStreams]);
1773 uint32_t iPageMap = 0;
1774 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1775 {
1776 uint32_t cStreamPages = RTPdbSizeToPages(pRoot->aStreams[idxStream].cbStream, pThis->cbPage);
1777 if (iPageMap + cStreamPages <= cPageMapMax)
1778 {
1779 PRTFSPDBSTREAMINFO const pInfo = pThis->papStreamInfo[idxStream];
1780 pInfo->cbStream = pRoot->aStreams[idxStream].cbStream;
1781 pInfo->cPages = cStreamPages;
1782 pInfo->PageMap.pa70 = &pauPageMap[iPageMap];
1783
1784 while (cStreamPages-- > 0)
1785 if (pauPageMap[iPageMap] == 0 || pauPageMap[iPageMap] < pThis->cBackingPages)
1786 iPageMap++;
1787 else
1788 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1789 "Bogus page map entry %#x belonging to stream %#x: %#x (max %#llx)",
1790 iPageMap, idxStream, pauPageMap[iPageMap], pThis->cBackingPages);
1791 }
1792 else
1793 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1794 "Stream %#x exceeds the page map space: cbStream=%#x iPageMap=%#x cPageMapMax=%#x",
1795 idxStream, pRoot->aStreams[idxStream].cbStream, iPageMap, cPageMapMax);
1796 }
1797
1798 }
1799
1800 /*
1801 * Load info from streams #1 (PDB & names) and #3 (DBI).
1802 */
1803 rc = rtFsPdbVolLoadStream1(pThis, pErrInfo);
1804 if (RT_SUCCESS(rc))
1805 rc = rtFsPdbVolLoadStream3(pThis, pErrInfo);
1806 return rc;
1807}
1808
1809
1810RTDECL(int) RTFsPdbVolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
1811{
1812 /*
1813 * Quick input validation.
1814 */
1815 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
1816 *phVfs = NIL_RTVFS;
1817 AssertReturn(!(fFlags & ~RTFSPDB_F_NO_NAMES), VERR_INVALID_FLAGS);
1818
1819 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
1820 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1821
1822 /*
1823 * Create a new PDB VFS instance and try initialize it using the given input file.
1824 */
1825 RTVFS hVfs = NIL_RTVFS;
1826 PRTFSPDBVOL pThis = NULL;
1827 int rc = RTVfsNew(&g_rtFsPdbVolOps, sizeof(RTFSPDBVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
1828 if (RT_SUCCESS(rc))
1829 {
1830 rc = rtFsPdbVolTryInit(pThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
1831 if (RT_SUCCESS(rc))
1832 *phVfs = hVfs;
1833 else
1834 RTVfsRelease(hVfs);
1835 }
1836 else
1837 RTVfsFileRelease(hVfsFileIn);
1838 return rc;
1839}
1840
1841
1842/**
1843 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1844 */
1845static DECLCALLBACK(int) rtVfsChainPdbFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1846 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1847{
1848 RT_NOREF(pProviderReg, pSpec);
1849
1850 /*
1851 * Basic checks.
1852 */
1853 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1854 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1855 if ( pElement->enmType != RTVFSOBJTYPE_VFS
1856 && pElement->enmType != RTVFSOBJTYPE_DIR)
1857 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
1858 if (pElement->cArgs > 1)
1859 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1860
1861 /*
1862 * Parse the flag if present, save in pElement->uProvider.
1863 */
1864 uint32_t fFlags = 0;
1865 if (pElement->cArgs > 0)
1866 {
1867 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
1868 {
1869 const char *psz = pElement->paArgs[iArg].psz;
1870 if (*psz)
1871 {
1872 if (!strcmp(psz, "nonames"))
1873 fFlags |= RTFSPDB_F_NO_NAMES;
1874 else
1875 {
1876 *poffError = pElement->paArgs[iArg].offSpec;
1877 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
1878 }
1879 }
1880 }
1881 }
1882
1883 pElement->uProvider = fFlags;
1884 return VINF_SUCCESS;
1885}
1886
1887
1888/**
1889 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
1890 */
1891static DECLCALLBACK(int) rtVfsChainPdbFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
1892 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
1893 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1894{
1895 RT_NOREF(pProviderReg, pSpec, poffError);
1896
1897 int rc;
1898 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
1899 if (hVfsFileIn != NIL_RTVFSFILE)
1900 {
1901 RTVFS hVfs;
1902 rc = RTFsPdbVolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
1903 RTVfsFileRelease(hVfsFileIn);
1904 if (RT_SUCCESS(rc))
1905 {
1906 *phVfsObj = RTVfsObjFromVfs(hVfs);
1907 RTVfsRelease(hVfs);
1908 if (*phVfsObj != NIL_RTVFSOBJ)
1909 return VINF_SUCCESS;
1910 rc = VERR_VFS_CHAIN_CAST_FAILED;
1911 }
1912 }
1913 else
1914 rc = VERR_VFS_CHAIN_CAST_FAILED;
1915 return rc;
1916}
1917
1918
1919/**
1920 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
1921 */
1922static DECLCALLBACK(bool) rtVfsChainPdbFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
1923 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
1924 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
1925{
1926 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
1927 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
1928 || !pReuseElement->paArgs[0].uProvider)
1929 return true;
1930 return false;
1931}
1932
1933
1934/** VFS chain element 'file'. */
1935static RTVFSCHAINELEMENTREG g_rtVfsChainPdbFsVolReg =
1936{
1937 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1938 /* fReserved = */ 0,
1939 /* pszName = */ "pdbfs",
1940 /* ListEntry = */ { NULL, NULL },
1941 /* pszHelp = */ "Open a PDB file system, requires a file object on the left side.\n",
1942 /* pfnValidate = */ rtVfsChainPdbFsVol_Validate,
1943 /* pfnInstantiate = */ rtVfsChainPdbFsVol_Instantiate,
1944 /* pfnCanReuseElement = */ rtVfsChainPdbFsVol_CanReuseElement,
1945 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1946};
1947
1948RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainPdbFsVolReg, rtVfsChainPdbFsVolReg);
1949
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use