VirtualBox

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

Last change on this file since 100913 was 100913, checked in by vboxsync, 18 months ago

IPRT: Moved the PDB cache subdir string composition to the pdbvfs and expose it via RTVFSQIEX_VOL_LABEL. Extended the RTVFSQIEX_VOL_LABEL_ALT with the compiler date/version.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.0 KB
Line 
1/* $Id: pdbvfs.cpp 100913 2023-08-19 11:03:51Z 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 * Stream info.
70 */
71typedef struct RTFSPDBSTREAMINFO
72{
73 /** The stream name.
74 * Unnamed streams will be set to NULL. Standard streams are assigned
75 * names (C-litterals) the rest is matched up with RTFSPDBVOL::pszzNames
76 * content. */
77 const char *pszName;
78 /** Size of the stream. */
79 uint32_t cbStream;
80 /** Number of pages in the stream. */
81 uint32_t cPages;
82 /** Pointer to the page map for the stream (within RTFSPDBVOL::pbRoot). */
83 union
84 {
85 void const *pv;
86 PCRTPDB20PAGE pa20;
87 PCRTPDB70PAGE pa70;
88 } PageMap;
89} RTFSPDBSTREAMINFO;
90typedef RTFSPDBSTREAMINFO *PRTFSPDBSTREAMINFO;
91typedef RTFSPDBSTREAMINFO const *PCRTFSPDBSTREAMINFO;
92
93
94/**
95 * Private data for a VFS file object.
96 */
97typedef struct RTFSPDBFILEOBJ
98{
99 /** Pointer to the PDB volume data. */
100 PRTFSPDBVOL pPdb;
101 /** The stream number (0 based). */
102 uint32_t idxStream;
103 /** Size of the stream. */
104 uint32_t cbStream;
105 /** Number of pages in the stream. */
106 uint32_t cPages;
107 /** Pointer to the page map for the stream (within RTFSPDBVOL::pbRoot)(. */
108 union
109 {
110 void const *pv;
111 PCRTPDB20PAGE pa20;
112 PCRTPDB70PAGE pa70;
113 } PageMap;
114 /** The current file offset. */
115 uint64_t offFile;
116} RTFSPDBFILEOBJ;
117typedef RTFSPDBFILEOBJ *PRTFSPDBFILEOBJ;
118
119/**
120 * Private data for a VFS directory object.
121 */
122typedef struct RTFSPDBDIROBJ
123{
124 /** Pointer to the PDB volume data. */
125 PRTFSPDBVOL pPdb;
126 /** The next stream number to return info for when reading (0 based). */
127 uint32_t idxNextStream;
128} RTFSPDBDIROBJ;
129typedef RTFSPDBDIROBJ *PRTFSPDBDIROBJ;
130
131
132/**
133 * Indicates which PDB version we're accessing.
134 */
135typedef enum RTFSPDBVER
136{
137 /** Invalid zero value. */
138 RTFSPDBVER_INVALID = 0,
139 /** Accessing a v2.0 PDB. */
140 RTFSPDBVER_2,
141 /** Accessing a v7.0 PDB. */
142 RTFSPDBVER_7
143} RTFSPDBVER;
144
145
146/**
147 * A PDB volume.
148 */
149typedef struct RTFSPDBVOL
150{
151 /** Handle to itself. */
152 RTVFS hVfsSelf;
153 /** The file, partition, or whatever backing the PDB file. */
154 RTVFSFILE hVfsBacking;
155 /** The size of the backing thingy. */
156 uint64_t cbBacking;
157 /** The size of the backing thingy in sectors (cbSector). */
158 uint64_t cBackingPages;
159 /** Flags. */
160 uint32_t fFlags;
161 /** The page size (in bytes). */
162 uint32_t cbPage;
163 /** The format version. */
164 RTFSPDBVER enmVersion;
165 /** Number of streams. */
166 uint32_t cStreams;
167 /** The size of the root stream. */
168 uint32_t cbRoot;
169 /** Pointer to the root directory bytes. */
170 uint8_t *pbRoot;
171
172 /** @name PDB metadata from stream \#1.
173 * @{ */
174 /** The PDB age. */
175 uint32_t uAge;
176 /** The PDB timestamp. */
177 uint32_t uTimestamp;
178 /** The PDB UUID. */
179 RTUUID Uuid;
180 /** The VC date (see RTPDB70NAMES::uVersion and RTPDB20NAMES::uVersion). */
181 uint32_t uVcDate;
182 /** Size of the name string table. */
183 uint32_t cbNames;
184 /** Name string table. */
185 char *pszzNames;
186 /** @} */
187
188 /** Extra per-stream info. We've do individual allocations here in case we
189 * want to add write support. */
190 PRTFSPDBSTREAMINFO *papStreamInfo;
191} RTFSPDBVOL;
192
193
194/*********************************************************************************************************************************
195* Internal Functions *
196*********************************************************************************************************************************/
197static DECLCALLBACK(int) rtFsPdbVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir);
198
199
200
201/**
202 * Helper for methods returning file information.
203 */
204static void rtFsPdbPopulateObjInfo(PRTFSPDBVOL pPdb, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr,
205 uint32_t cbStream, uint32_t idxStream, bool fIsDir)
206{
207 RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
208 RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
209 RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
210 RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
211 pObjInfo->cbObject = cbStream == UINT32_MAX ? 0 : cbStream;
212 pObjInfo->cbAllocated = (uint64_t)RTPdbSizeToPages(cbStream, pPdb->cbPage) * pPdb->cbPage;
213 pObjInfo->Attr.fMode = RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH | RTFS_DOS_READONLY;
214 if (!fIsDir)
215 pObjInfo->Attr.fMode |= RTFS_TYPE_FILE;
216 else
217 pObjInfo->Attr.fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
218 pObjInfo->Attr.enmAdditional = enmAddAttr;
219
220 switch (enmAddAttr)
221 {
222 case RTFSOBJATTRADD_NOTHING: RT_FALL_THRU();
223 case RTFSOBJATTRADD_UNIX:
224 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
225 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
226 pObjInfo->Attr.u.Unix.cHardlinks = 1 + fIsDir;
227 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
228 pObjInfo->Attr.u.Unix.INodeId = idxStream;
229 pObjInfo->Attr.u.Unix.fFlags = 0;
230 pObjInfo->Attr.u.Unix.GenerationId = 0;
231 pObjInfo->Attr.u.Unix.Device = 0;
232 break;
233 case RTFSOBJATTRADD_UNIX_OWNER:
234 pObjInfo->Attr.u.UnixOwner.uid = 0;
235 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
236 break;
237 case RTFSOBJATTRADD_UNIX_GROUP:
238 pObjInfo->Attr.u.UnixGroup.gid = 0;
239 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
240 break;
241 case RTFSOBJATTRADD_EASIZE:
242 pObjInfo->Attr.u.EASize.cb = 0;
243 break;
244 default:
245 AssertFailedBreak();
246 }
247}
248
249/**
250 * Helper methods for opening a stream.
251 *
252 * This is used internally w/o any associated RTVFSFILE handle for reading
253 * stream \#1 containing PDB metadata.
254 */
255static void rtFsPdbPopulateFileObj(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb,
256 uint32_t idxStream, uint32_t cbStream, void const *pvPageMap)
257{
258 pNewFile->pPdb = pPdb;
259 pNewFile->idxStream = idxStream;
260 pNewFile->cbStream = cbStream;
261 pNewFile->PageMap.pv = pvPageMap;
262 pNewFile->cPages = RTPdbSizeToPages(cbStream, pPdb->cbPage);
263 pNewFile->offFile = 0;
264}
265
266
267/**
268 * Helper methods for opening a stream.
269 */
270static void rtFsPdbPopulateFileObjFromInfo(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb, uint32_t idxStream)
271{
272 PCRTFSPDBSTREAMINFO pInfo = pPdb->papStreamInfo[idxStream];
273 rtFsPdbPopulateFileObj(pNewFile, pPdb, idxStream, pInfo->cbStream, pInfo->PageMap.pv);
274 Assert(pInfo->cPages == pNewFile->cPages);
275}
276
277
278/**
279 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
280 */
281static DECLCALLBACK(int) rtFsPdbFile_Close(void *pvThis)
282{
283 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
284 LogFlow(("rtFsPdbFile_Close(%p/%p)\n", pThis, pThis->pPdb));
285 pThis->pPdb = NULL;
286 return VINF_SUCCESS;
287}
288
289
290/**
291 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
292 */
293static DECLCALLBACK(int) rtFsPdbFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
294{
295 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
296 rtFsPdbPopulateObjInfo(pThis->pPdb, pObjInfo, enmAddAttr, pThis->cbStream, pThis->idxStream, false /*fIsDir*/);
297 return VINF_SUCCESS;
298}
299
300
301/**
302 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
303 */
304static DECLCALLBACK(int) rtFsPdbFile_Read(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
305{
306 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
307 PRTFSPDBVOL const pPdb = pThis->pPdb;
308 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
309 AssertReturn(pPdb, VERR_INTERNAL_ERROR_2);
310 RT_NOREF(fBlocking);
311
312 /* Apply default offset and switch to unsigned offset variable. */
313 uint64_t offFile;
314 if (off == -1)
315 offFile = pThis->offFile;
316 else
317 {
318 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
319 offFile = (uint64_t)off;
320 }
321
322 /*
323 * Check for EOF and figure out how much to read.
324 */
325 if (offFile >= pThis->cbStream)
326 {
327 if (pcbRead)
328 {
329 *pcbRead = 0;
330 return VINF_EOF;
331 }
332 return VERR_EOF;
333 }
334
335 int rcRet = VINF_SUCCESS;
336 size_t cbToRead = RTSgBufCalcLengthLeft(pSgBuf);
337 if ( cbToRead > pThis->cbStream
338 || offFile + cbToRead > pThis->cbStream)
339 {
340 if (!pcbRead)
341 return VERR_EOF;
342 cbToRead = (size_t)(pThis->cbStream - offFile);
343 rcRet = VINF_EOF;
344 }
345
346 /*
347 * Do it page by page, buffer segment by buffer segment, whatever is smaller.
348 */
349 uint64_t const offStart = offFile;
350 while (cbToRead > 0)
351 {
352 uint32_t iPageMap = (uint32_t)(offFile / pPdb->cbPage);
353 uint32_t const offInPage = (uint32_t)(offFile % pPdb->cbPage);
354 size_t cbLeftInPage = pPdb->cbPage - offInPage;
355 if (cbLeftInPage > cbToRead)
356 cbLeftInPage = cbToRead;
357 void * const pvDst = RTSgBufGetCurrentSegment(pSgBuf, cbLeftInPage, &cbLeftInPage);
358 AssertReturn(pvDst, VERR_INTERNAL_ERROR_4);
359
360 uint64_t const offPageInFile = (pPdb->enmVersion == RTFSPDBVER_2
361 ? pThis->PageMap.pa20[iPageMap] : pThis->PageMap.pa70[iPageMap])
362 * (uint64_t)pPdb->cbPage;
363 int rcRead = RTVfsFileReadAt(pPdb->hVfsBacking, offPageInFile + offInPage, pvDst, cbLeftInPage, NULL /*pcbRead*/);
364 if (RT_SUCCESS(rcRead))
365 {
366 size_t cbAssert = RTSgBufAdvance(pSgBuf, cbLeftInPage); Assert(cbAssert == cbLeftInPage); RT_NOREF(cbAssert);
367 offFile += cbLeftInPage;
368 cbToRead -= cbLeftInPage;
369 }
370 /* If we can return the number of bytes we've read, we'll advance the
371 file offset. Otherwise we won't and return immediately. */
372 else if (!pcbRead)
373 return rcRead;
374 else
375 {
376 rcRet = rcRead != VERR_EOF ? rcRead : VERR_READ_ERROR;
377 break;
378 }
379 }
380
381 /*
382 * Update the file position and stuff.
383 */
384 pThis->offFile = offFile;
385 if (pcbRead)
386 *pcbRead = offFile - offStart;
387 return rcRet;
388}
389
390
391/**
392 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
393 */
394static DECLCALLBACK(int) rtFsPdbFile_Flush(void *pvThis)
395{
396 RT_NOREF(pvThis);
397 return VINF_SUCCESS;
398}
399
400
401/**
402 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
403 */
404static DECLCALLBACK(int) rtFsPdbFile_Tell(void *pvThis, PRTFOFF poffActual)
405{
406 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
407 *poffActual = pThis->offFile;
408 return VINF_SUCCESS;
409}
410
411
412/**
413 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
414 */
415static DECLCALLBACK(int) rtFsPdbFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
416{
417 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
418 RTFOFF offNew;
419 switch (uMethod)
420 {
421 case RTFILE_SEEK_BEGIN:
422 offNew = offSeek;
423 break;
424 case RTFILE_SEEK_END:
425 offNew = (RTFOFF)pThis->cbStream + offSeek;
426 break;
427 case RTFILE_SEEK_CURRENT:
428 offNew = (RTFOFF)pThis->offFile + offSeek;
429 break;
430 default:
431 return VERR_INVALID_PARAMETER;
432 }
433 if (offNew >= 0)
434 {
435 pThis->offFile = offNew;
436 *poffActual = offNew;
437 return VINF_SUCCESS;
438 }
439 return VERR_NEGATIVE_SEEK;
440}
441
442
443/**
444 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
445 */
446static DECLCALLBACK(int) rtFsPdbFile_QuerySize(void *pvThis, uint64_t *pcbFile)
447{
448 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis;
449 *pcbFile = pThis->cbStream;
450 return VINF_SUCCESS;
451}
452
453
454/**
455 * PDB FS file operations.
456 */
457DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsPdbFileOps =
458{
459 { /* Stream */
460 { /* Obj */
461 RTVFSOBJOPS_VERSION,
462 RTVFSOBJTYPE_FILE,
463 "PDB File",
464 rtFsPdbFile_Close,
465 rtFsPdbFile_QueryInfo,
466 NULL,
467 RTVFSOBJOPS_VERSION
468 },
469 RTVFSIOSTREAMOPS_VERSION,
470 RTVFSIOSTREAMOPS_FEAT_NO_SG,
471 rtFsPdbFile_Read,
472 NULL /*pfnWrite*/,
473 rtFsPdbFile_Flush,
474 NULL /*pfnPollOne*/,
475 rtFsPdbFile_Tell,
476 NULL /*pfnSkip*/,
477 NULL /*pfnZeroFill*/,
478 RTVFSIOSTREAMOPS_VERSION,
479 },
480 RTVFSFILEOPS_VERSION,
481 0,
482 { /* ObjSet */
483 RTVFSOBJSETOPS_VERSION,
484 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
485 NULL /*SetMode*/,
486 NULL /*SetTimes*/,
487 NULL /*SetOwner*/,
488 RTVFSOBJSETOPS_VERSION
489 },
490 rtFsPdbFile_Seek,
491 rtFsPdbFile_QuerySize,
492 NULL /*SetSize*/,
493 NULL /*QueryMaxSize*/,
494 RTVFSFILEOPS_VERSION
495};
496
497
498
499/**
500 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
501 */
502static DECLCALLBACK(int) rtFsPdbDir_Close(void *pvThis)
503{
504 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
505 LogFlow(("rtFsPdbDir_Close(%p/%p)\n", pThis, pThis->pPdb));
506 pThis->pPdb = NULL;
507 return VINF_SUCCESS;
508}
509
510
511/**
512 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
513 */
514static DECLCALLBACK(int) rtFsPdbDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
515{
516 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
517 PRTFSPDBVOL const pPdb = pThis->pPdb;
518 rtFsPdbPopulateObjInfo(pPdb, pObjInfo, enmAddAttr, pPdb->cbRoot, 0 /* root dir is stream zero */, true /*fIsDir*/);
519 return VINF_SUCCESS;
520}
521
522
523/**
524 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
525 */
526static DECLCALLBACK(int) rtFsPdbDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
527 uint32_t fFlags, PRTVFSOBJ phVfsObj)
528{
529 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
530 PRTFSPDBVOL const pPdb = pThis->pPdb;
531 int rc;
532
533 /*
534 * We cannot create or replace anything, just open stuff.
535 */
536 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
537 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
538 { /* likely */ }
539 else
540 return VERR_WRITE_PROTECT;
541
542 /*
543 * Special cases '.' and '..'
544 */
545 if ( pszEntry[0] == '.'
546 && ( pszEntry[1] == '\0'
547 || (pszEntry[1] == '.' && pszEntry[2] == '\0')))
548 {
549 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
550 {
551 RTVFSDIR hVfsDir;
552 rc = rtFsPdbVol_OpenRoot(pPdb, &hVfsDir);
553 if (RT_SUCCESS(rc))
554 {
555 *phVfsObj = RTVfsObjFromDir(hVfsDir);
556 RTVfsDirRelease(hVfsDir);
557 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
558 }
559 }
560 else
561 rc = VERR_IS_A_DIRECTORY;
562 return rc;
563 }
564
565 /*
566 * The given filename can be:
567 * - just the stream index: "1";
568 * - just the name provided it doesn't start with a digit: "pdb";
569 * - or the combination of the two: "1-pdb".
570 */
571 uint32_t idxStream;
572 if (RT_C_IS_DIGIT(*pszEntry))
573 {
574 char *pszNext;
575 rc = RTStrToUInt32Ex(pszEntry, &pszNext, 10, &idxStream);
576 if ( (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
577 || (*pszNext != '\0' && *pszNext != '-')
578 || idxStream >= pPdb->cStreams)
579 {
580 Log2(("rtFsPdbDir_Open: RTStrToUInt32Ex(%s,) -> %Rrc\n", pszEntry, VERR_PATH_NOT_FOUND));
581 return VERR_PATH_NOT_FOUND;
582 }
583 if ( *pszNext == '-'
584 && RTStrCmp(pszNext + 1, pPdb->papStreamInfo[idxStream]->pszName) != 0)
585 {
586 Log2(("rtFsPdbDir_Open: idxStream=%#x name mismatch '%s', expected '%s'\n",
587 idxStream, pszEntry, pPdb->papStreamInfo[idxStream]->pszName));
588 return VERR_PATH_NOT_FOUND;
589 }
590 }
591 else
592 {
593 for (idxStream = 0; idxStream < pPdb->cStreams; idxStream++)
594 {
595 const char * const pszStreamName = pPdb->papStreamInfo[idxStream]->pszName;
596 if (pszStreamName && strcmp(pszEntry, pszStreamName) == 0)
597 break;
598 }
599 if (idxStream >= pPdb->cStreams)
600 {
601 Log2(("rtFsPdbDir_Open: '%s' not found in name table\n", pszEntry));
602 return VERR_PATH_NOT_FOUND;
603 }
604 }
605
606 /*
607 * If opening a file, create a new file object and return it.
608 */
609 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
610 {
611 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
612 PRTFSPDBFILEOBJ pNewFile;
613 rc = RTVfsNewFile(&g_rtFsPdbFileOps, sizeof(*pNewFile), fOpen, pPdb->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
614 &hVfsFile, (void **)&pNewFile);
615 if (RT_SUCCESS(rc))
616 {
617 rtFsPdbPopulateFileObjFromInfo(pNewFile, pPdb, idxStream);
618
619 /* Convert it to a file object. */
620 *phVfsObj = RTVfsObjFromFile(hVfsFile);
621 RTVfsFileRelease(hVfsFile);
622 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
623
624 LogFlow(("rtFsPdbDir_Open: idxStream=%#x cbStream=%#RX64\n", idxStream, pNewFile->cbStream));
625 }
626 }
627 else
628 rc = VERR_IS_A_FILE;
629 return rc;
630}
631
632
633/**
634 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
635 */
636static DECLCALLBACK(int) rtFsPdbDir_RewindDir(void *pvThis)
637{
638 PRTFSPDBDIROBJ pThis = (PRTFSPDBDIROBJ)pvThis;
639 pThis->idxNextStream = 0;
640 return VINF_SUCCESS;
641}
642
643
644/**
645 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
646 */
647static DECLCALLBACK(int) rtFsPdbDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
648 RTFSOBJATTRADD enmAddAttr)
649{
650 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis;
651 PRTFSPDBVOL const pPdb = pThis->pPdb;
652 uint32_t const idxStream = pThis->idxNextStream;
653 if (idxStream < pPdb->cStreams)
654 {
655 /*
656 * Do names first as they may cause overflows.
657 */
658 char szStreamNo[64];
659 ssize_t const cchStreamNo = RTStrFormatU32(szStreamNo, sizeof(szStreamNo), idxStream, 10, 0, 0, 0);
660 Assert(cchStreamNo > 0);
661
662 /* Provide a more descriptive name if possible. */
663 const char *pszOtherName = pPdb->papStreamInfo[idxStream]->pszName;
664 size_t const cchOtherName = pszOtherName ? strlen(pszOtherName) : 0;
665
666 /* Do the name stuff. */
667 size_t const cchName = cchOtherName ? cchStreamNo + 1 + cchOtherName : cchStreamNo;
668 size_t const cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
669 if (*pcbDirEntry < cbNeeded)
670 {
671 Log3(("rtFsPdbDir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu\n", *pcbDirEntry, cbNeeded));
672 *pcbDirEntry = cbNeeded;
673 return VERR_BUFFER_OVERFLOW;
674 }
675 pDirEntry->cbName = (uint16_t)cchName;
676 memcpy(pDirEntry->szName, szStreamNo, cchStreamNo);
677 if (cchOtherName)
678 {
679 pDirEntry->szName[cchStreamNo] = '-';
680 memcpy(&pDirEntry->szName[cchStreamNo + 1], pszOtherName, cchOtherName);
681 }
682 pDirEntry->szName[cchName] = '\0';
683
684 if (cchOtherName)
685 {
686 Assert(cchStreamNo <= 8);
687 pDirEntry->cwcShortName = cchStreamNo;
688 szStreamNo[cchStreamNo] = '\0';
689 int rc = RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), szStreamNo);
690 AssertRC(rc);
691 }
692 else
693 {
694 pDirEntry->cwcShortName = 0;
695 pDirEntry->wszShortName[0] = '\0';
696 }
697
698 /* Provide the other info. */
699 if (pPdb->enmVersion == RTFSPDBVER_2)
700 {
701 PCRTPDB20ROOT const pRoot = (PCRTPDB20ROOT)pPdb->pbRoot;
702 rtFsPdbPopulateObjInfo(pPdb, &pDirEntry->Info, enmAddAttr,
703 pRoot->aStreams[idxStream].cbStream, idxStream, false /*fIsDir*/);
704 }
705 else
706 {
707 PCRTPDB70ROOT const pRoot = (PCRTPDB70ROOT)pPdb->pbRoot;
708 rtFsPdbPopulateObjInfo(pPdb, &pDirEntry->Info, enmAddAttr,
709 pRoot->aStreams[idxStream].cbStream, idxStream, false /*fIsDir*/);
710 }
711
712 /* Advance the directory location and return. */
713 pThis->idxNextStream = idxStream + 1;
714
715 return VINF_SUCCESS;
716 }
717
718 Log3(("rtFsPdbDir_ReadDir9660: idxNextStream=%#x: VERR_NO_MORE_FILES\n", pThis->idxNextStream));
719 return VERR_NO_MORE_FILES;
720}
721
722
723/**
724 * PDB (root) directory operations.
725 */
726static const RTVFSDIROPS g_rtFsPdbDirOps =
727{
728 { /* Obj */
729 RTVFSOBJOPS_VERSION,
730 RTVFSOBJTYPE_DIR,
731 "PDB Dir",
732 rtFsPdbDir_Close,
733 rtFsPdbDir_QueryInfo,
734 NULL,
735 RTVFSOBJOPS_VERSION
736 },
737 RTVFSDIROPS_VERSION,
738 0,
739 { /* ObjSet */
740 RTVFSOBJSETOPS_VERSION,
741 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
742 NULL /*SetMode*/,
743 NULL /*SetTimes*/,
744 NULL /*SetOwner*/,
745 RTVFSOBJSETOPS_VERSION
746 },
747 rtFsPdbDir_Open,
748 NULL /* pfnFollowAbsoluteSymlink */,
749 NULL /* pfnOpenFile */,
750 NULL /* pfnOpenDir */,
751 NULL /* pfnCreateDir */,
752 NULL /* pfnOpenSymlink */,
753 NULL /* pfnCreateSymlink */,
754 NULL /* pfnQueryEntryInfo */,
755 NULL /* pfnUnlinkEntry */,
756 NULL /* pfnRenameEntry */,
757 rtFsPdbDir_RewindDir,
758 rtFsPdbDir_ReadDir,
759 RTVFSDIROPS_VERSION,
760};
761
762
763/**
764 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
765 */
766static DECLCALLBACK(int) rtFsPdbVol_Close(void *pvThis)
767{
768 PRTFSPDBVOL pThis = (PRTFSPDBVOL)pvThis;
769 Log(("rtFsPdbVol_Close(%p)\n", pThis));
770
771 RTVfsFileRelease(pThis->hVfsBacking);
772 pThis->hVfsBacking = NIL_RTVFSFILE;
773
774 RTMemFree(pThis->pbRoot);
775 pThis->pbRoot = NULL;
776
777 return VINF_SUCCESS;
778}
779
780
781/**
782 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
783 */
784static DECLCALLBACK(int) rtFsPdbVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
785{
786 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
787 return VERR_WRONG_TYPE;
788}
789
790
791/**
792 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfoEx}
793 */
794static DECLCALLBACK(int) rtFsPdbVol_QueryInfoEx(void *pvThis, RTVFSQIEX enmInfo, void *pvInfo, size_t cbInfo, size_t *pcbRet)
795{
796 PRTFSPDBVOL pThis = (PRTFSPDBVOL)pvThis;
797 LogFlow(("rtFsPdbVol_QueryInfo(%p, %d,, %#zx,)\n", pThis, enmInfo, cbInfo));
798 RT_NOREF(pThis, pvInfo, cbInfo, pcbRet);
799
800 ssize_t cchRet;
801 switch (enmInfo)
802 {
803 /* This is the same as the symbol chache subdir name: */
804 case RTVFSQIEX_VOL_LABEL:
805 if ( pThis->enmVersion == RTFSPDBVER_2
806 || RTUuidIsNull(&pThis->Uuid))
807 cchRet = RTStrPrintf2((char *)pvInfo, cbInfo, "%08X%x", pThis->uTimestamp, pThis->uAge);
808 else
809 cchRet = RTStrPrintf2((char *)pvInfo, cbInfo, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x",
810 pThis->Uuid.Gen.u32TimeLow,
811 pThis->Uuid.Gen.u16TimeMid,
812 pThis->Uuid.Gen.u16TimeHiAndVersion,
813 pThis->Uuid.Gen.u8ClockSeqHiAndReserved,
814 pThis->Uuid.Gen.u8ClockSeqLow,
815 pThis->Uuid.Gen.au8Node[0],
816 pThis->Uuid.Gen.au8Node[1],
817 pThis->Uuid.Gen.au8Node[2],
818 pThis->Uuid.Gen.au8Node[3],
819 pThis->Uuid.Gen.au8Node[4],
820 pThis->Uuid.Gen.au8Node[5],
821 pThis->uAge);
822 break;
823
824 /* This exposes the PDB and VC versions: */
825 case RTVFSQIEX_VOL_LABEL_ALT:
826 cchRet = RTStrPrintf2((char *)pvInfo, cbInfo,
827 "pdb-v%u-%u", pThis->enmVersion == RTFSPDBVER_2 ? 2 : 7, pThis->uVcDate);
828 break;
829
830 case RTVFSQIEX_VOL_SERIAL:
831 if (cbInfo == sizeof(uint64_t) || cbInfo == sizeof(uint32_t))
832 {
833 *pcbRet = cbInfo;
834 ((uint32_t *)pvInfo)[0] = pThis->uTimestamp;
835 if (cbInfo == sizeof(uint64_t))
836 ((uint32_t *)pvInfo)[1] = pThis->uAge;
837 return VINF_SUCCESS;
838 }
839 if ( pThis->enmVersion != RTFSPDBVER_2
840 && !RTUuidIsNull(&pThis->Uuid))
841 {
842 *pcbRet = sizeof(RTUUID);
843 if (cbInfo == sizeof(RTUUID))
844 {
845 *(PRTUUID)pvInfo = pThis->Uuid;
846 return VINF_SUCCESS;
847 }
848 }
849 else
850 *pcbRet = sizeof(uint64_t);
851 return cbInfo < *pcbRet ? VERR_BUFFER_OVERFLOW : VERR_BUFFER_UNDERFLOW;
852
853 default:
854 return VERR_NOT_SUPPORTED;
855 }
856
857 if (cchRet > 0)
858 {
859 *pcbRet = (size_t)cchRet;
860 return VINF_SUCCESS;
861 }
862 *pcbRet = (size_t)-cchRet;
863 return VERR_BUFFER_OVERFLOW;
864}
865
866
867/**
868 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
869 */
870static DECLCALLBACK(int) rtFsPdbVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
871{
872 PRTFSPDBVOL pThis = (PRTFSPDBVOL)pvThis;
873 PRTFSPDBDIROBJ pNewDir;
874 int rc = RTVfsNewDir(&g_rtFsPdbDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
875 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
876 if (RT_SUCCESS(rc))
877 {
878 pNewDir->pPdb = pThis;
879 pNewDir->idxNextStream = 0;
880 return VINF_SUCCESS;
881 }
882 return rc;
883}
884
885
886/**
887 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
888 */
889static DECLCALLBACK(int) rtFsPdbVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
890{
891 RT_NOREF(pvThis, off, cb, pfUsed);
892 return VERR_NOT_IMPLEMENTED;
893}
894
895
896DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsPdbVolOps =
897{
898 { /* Obj */
899 RTVFSOBJOPS_VERSION,
900 RTVFSOBJTYPE_VFS,
901 "PDB",
902 rtFsPdbVol_Close,
903 rtFsPdbVol_QueryInfo,
904 rtFsPdbVol_QueryInfoEx,
905 RTVFSOBJOPS_VERSION
906 },
907 RTVFSOPS_VERSION,
908 0 /* fFeatures */,
909 rtFsPdbVol_OpenRoot,
910 rtFsPdbVol_QueryRangeState,
911 RTVFSOPS_VERSION
912};
913
914static int rtFsPdbVolReadStreamToHeap(PRTFSPDBVOL pThis, uint32_t idxStream,
915 uint8_t **ppbStream, uint32_t *pcbStream, PRTERRINFO pErrInfo)
916{
917 *ppbStream = NULL;
918 *pcbStream = 0;
919 if (idxStream >= pThis->cStreams)
920 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND, "idxStream=%#x not found, max %#x", idxStream, pThis->cStreams);
921
922 /*
923 * Fake a handle and reuse the file read code.
924 */
925 RTFSPDBFILEOBJ FileObj;
926 rtFsPdbPopulateFileObjFromInfo(&FileObj, pThis, idxStream);
927
928 size_t const cbDst = (size_t)FileObj.cbStream + !FileObj.cbStream;
929 uint8_t * const pbDst = (uint8_t *)RTMemTmpAllocZ(cbDst);
930 if (!pbDst)
931 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
932 "Failed to allocate memory for reading stream #%x: %#zx bytes", idxStream, cbDst);
933
934 RTSGSEG SgSeg = { pbDst, FileObj.cbStream };
935 RTSGBUF SgBuf;
936 RTSgBufInit(&SgBuf, &SgSeg, 1);
937
938 int rc = rtFsPdbFile_Read(&FileObj, 0, &SgBuf, true /*fBlocking*/, NULL);
939 if (RT_SUCCESS(rc))
940 {
941 *ppbStream = pbDst;
942 *pcbStream = FileObj.cbStream;
943 }
944 else
945 RTMemTmpFree(pbDst);
946 return rc;
947}
948
949
950/**
951 * Worker for rtFsPdbVolLoadStream1 that parses the PDB metadata stream.
952 */
953static int rtFsPdbVolLoadStream1Inner(PRTFSPDBVOL pThis, uint8_t const *pb, size_t cb, PRTERRINFO pErrInfo)
954{
955 /*
956 * Process the header part.
957 */
958 if (pThis->enmVersion == RTFSPDBVER_2)
959 {
960 PCRTPDB20NAMES pHdr = (PCRTPDB20NAMES)pb;
961 if (cb < sizeof(*pHdr))
962 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
963 "Stream #1 is smaller than expected: %#x, vs min %#zx", cb, sizeof(*pHdr));
964 pThis->uTimestamp = pHdr->uTimestamp;
965 pThis->uAge = pHdr->uAge;
966 pThis->uVcDate = pHdr->uVersion;
967 pThis->cbNames = pHdr->cbNames;
968 pb += sizeof(*pHdr);
969 cb -= sizeof(*pHdr);
970 }
971 else
972 {
973 PCRTPDB70NAMES pHdr = (PCRTPDB70NAMES)pb;
974 if (cb < sizeof(*pHdr))
975 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
976 "Stream #1 is smaller than expected: %#x, vs min %#zx", cb, sizeof(*pHdr));
977 pThis->uTimestamp = pHdr->uTimestamp;
978 pThis->uAge = pHdr->uAge;
979 pThis->uVcDate = pHdr->uVersion;
980 pThis->Uuid = pHdr->Uuid;
981 pThis->cbNames = pHdr->cbNames;
982 pb += sizeof(*pHdr);
983 cb -= sizeof(*pHdr);
984 }
985
986 /*
987 * Set default stream names that depends on the VC date.
988 */
989 /** @todo */
990
991 /*
992 * Load the string table if present.
993 */
994 if (pThis->cbNames == 0)
995 return VINF_SUCCESS;
996 if (cb < pThis->cbNames)
997 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
998 "Bogus string table: size given as %#x, but only %#x bytes left", pThis->cbNames, cb);
999 pThis->pszzNames = (char *)RTMemDupEx(pb, pThis->cbNames, 2 /* two extra zero bytes */);
1000 if (!pThis->pszzNames)
1001 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY, "Failed to allocate string table: %#x + 2 bytes", pThis->cbNames);
1002
1003 pb += pThis->cbNames;
1004 cb -= pThis->cbNames;
1005
1006 /** @todo MIPS format variation may have alignment padding here. */
1007
1008 /*
1009 * What follows now is the hash table mapping string table offset to stream
1010 * numbers. This is frequently misaligned, so we take care to load the
1011 * stuff byte-by-byte on architectures which are sensive to such things.
1012 *
1013 * Structure description: https://llvm.org/docs/PDB/HashTable.html
1014 */
1015 if (cb < 4 * sizeof(uint32_t))
1016 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1017 "Bogus hash table: Min size of 16 bytes, but only %#x bytes left", cb);
1018#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
1019# define GET_U32(a_pb, a_off) *(uint32_t const *)&a_pb[(a_off)]
1020#else
1021# 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)])
1022#endif
1023
1024 /* Sizes (irrelevant to us as we're not reconstructing the actual hash table yet): */
1025 uint32_t const cTabSize = GET_U32(pb, 0);
1026 uint32_t const cTabCapacity = GET_U32(pb, 4);
1027 if (cTabSize > cTabCapacity)
1028 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1029 "Bogus hash table: cTabSize=%#x > cTabCapacity=%#x", cTabSize, cTabCapacity);
1030 if (cTabSize == 0)
1031 return VINF_SUCCESS;
1032 pb += 8;
1033 cb -= 8;
1034
1035 /* Present bit vector: */
1036 uint32_t const cPresentVec = RT_MAKE_U32_FROM_U8(pb[0], pb[1], pb[2], pb[3]);
1037 pb += 4;
1038 cb -= 4;
1039 if ((uint64_t)cPresentVec + 1 > cb / 4)
1040 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1041 "Bogus hash table: cPresentVec=%#x + 1 delete vec > %#x", cPresentVec, cb / 4);
1042 uint8_t const * const pbPresentBits = pb;
1043 pb += cPresentVec * 4;
1044 cb -= cPresentVec * 4;
1045
1046 /* Scan the present vector as it gives the number of key/value pairs following the deleted bit vector. */
1047 uint64_t cPresent = 0;
1048 for (uint32_t off = 0; off < cPresentVec; off += 4)
1049 {
1050 uint32_t uWord = GET_U32(pbPresentBits, off);
1051 while (uWord)
1052 {
1053 cPresent += uWord & 1;
1054 uWord >>= 1;
1055 }
1056 }
1057
1058 /* Deleted vector (irrelevant to us): */
1059 uint32_t const cDeletedVec = RT_MAKE_U32_FROM_U8(pb[0], pb[1], pb[2], pb[3]);
1060 pb += 4;
1061 cb -= 4;
1062 if ((uint64_t)cDeletedVec + cPresent > cb / 4)
1063 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1064 "Bogus hash table: cDeletedVec=%#x cPresent=%#RX64 > %#x", cDeletedVec, cPresent, cb / 4);
1065 pb += cDeletedVec * 4;
1066 cb -= cDeletedVec * 4;
1067
1068 /* What remains is cPresent pairs of string table offset and stream IDs. */
1069 Assert(cb / 4 >= cPresent);
1070 for (uint32_t i = 0, off = 0; i < cPresent; i++, off += 8)
1071 {
1072 uint32_t const offString = GET_U32(pb, off);
1073 uint32_t const idxStream = GET_U32(pb, off + 4);
1074 if (offString < pThis->cbNames)
1075 {
1076 if (idxStream < pThis->cStreams)
1077 {
1078 /* Skip leading slashes. In-string slashes are removed afterwards. */
1079 const char *pszName = &pThis->pszzNames[offString];
1080 while (*pszName == '/' || *pszName == '\\')
1081 pszName++;
1082 if (pszName[0])
1083 pThis->papStreamInfo[idxStream]->pszName = pszName;
1084 }
1085 else
1086 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1087 "Bogus hash table entry %#x: offString=%#x, max %#x (offString=%#x '%s')",
1088 i, idxStream, pThis->cStreams, offString, &pThis->pszzNames[offString]);
1089 }
1090 else
1091 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
1092 "Bogus hash table entry %#x: offString=%#x, max %#x (idxStream=%#x)",
1093 i, offString, pThis->cbNames, idxStream);
1094 }
1095
1096 /*
1097 * Sanitize strings and convert any in-string slashes to underscores to
1098 * avoid VFS confusion. This has to be done after loading the hash table
1099 * so the slash skipping there works correctly should anyone do sub-string
1100 * optimizations involving slashes.
1101 */
1102 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1103 {
1104 char *pszName = (char *)pThis->papStreamInfo[idxStream]->pszName;
1105 if (pszName)
1106 {
1107 RTStrPurgeEncoding(pszName);
1108 pszName = strpbrk(pszName, "/\\");
1109 while (pszName)
1110 {
1111 *pszName = '_';
1112 pszName = strpbrk(pszName + 1, "/\\");
1113 }
1114 }
1115 }
1116
1117 return VINF_SUCCESS;
1118}
1119
1120
1121/**
1122 * Worker for rtFsPdbVolTryInit that loads the PDB metadata from stream \#1.
1123 */
1124static int rtFsPdbVolLoadStream1(PRTFSPDBVOL pThis, PRTERRINFO pErrInfo)
1125{
1126 /*
1127 * Assign default stream names based on basic PDB version.
1128 */
1129 switch (pThis->cStreams)
1130 {
1131 default:
1132 case 5:
1133 if (pThis->enmVersion == RTFSPDBVER_7) /** @todo condition? */
1134 pThis->papStreamInfo[3]->pszName = "name-map";
1135 RT_FALL_THRU();
1136 case 4:
1137 pThis->papStreamInfo[3]->pszName = "dbi"; /** @todo conditional? */
1138 RT_FALL_THRU();
1139 case 3:
1140 pThis->papStreamInfo[2]->pszName = "tpi";
1141 RT_FALL_THRU();
1142 case 2:
1143 pThis->papStreamInfo[1]->pszName = "pdb";
1144 RT_FALL_THRU();
1145 case 1:
1146 pThis->papStreamInfo[0]->pszName = "root";
1147 break;
1148 }
1149 if (pThis->cStreams >= 2)
1150 {
1151 /*
1152 * Read the stream into temporary heap memory and process it.
1153 */
1154 uint8_t *pbStream = NULL;
1155 uint32_t cbStream = 0;
1156 int rc = rtFsPdbVolReadStreamToHeap(pThis, 1, &pbStream, &cbStream, pErrInfo);
1157 if (RT_SUCCESS(rc))
1158 {
1159 rc = rtFsPdbVolLoadStream1Inner(pThis, pbStream, cbStream, pErrInfo);
1160 RTMemTmpFree(pbStream);
1161 }
1162 return rc;
1163 }
1164 return VINF_SUCCESS;
1165}
1166
1167
1168/**
1169 * Helper for rtFsPdbVolTryInit.
1170 */
1171static int rtFsPdbVolAllocInitialStreamInfo(PRTFSPDBVOL pThis, PRTERRINFO pErrInfo)
1172{
1173 pThis->papStreamInfo = (PRTFSPDBSTREAMINFO *)RTMemAllocZ(sizeof(pThis->papStreamInfo[0]) * pThis->cStreams);
1174 if (pThis->papStreamInfo)
1175 {
1176 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1177 {
1178 pThis->papStreamInfo[idxStream] = (PRTFSPDBSTREAMINFO)RTMemAllocZ(sizeof(*pThis->papStreamInfo[0]));
1179 if (pThis->papStreamInfo[idxStream])
1180 { }
1181 else
1182 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY, "Failed to allocate RTFSPDBSTREAMINFO #%u", idxStream);
1183 }
1184 return VINF_SUCCESS;
1185 }
1186 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY,
1187 "Failed to allocate papStreamInfo array with %u entries", pThis->cStreams);
1188}
1189
1190
1191/**
1192 * Worker for RTFsPdbVolOpen.
1193 *
1194 * @returns IPRT status code.
1195 * @param pThis The PDB VFS instance to initialize.
1196 * @param hVfsSelf The PDB VFS handle (no reference consumed).
1197 * @param hVfsBacking The file backing the alleged PDB file system.
1198 * Reference is consumed (via rtFsPdbVol_Close).
1199 * @param fFlags Flags, RTFSPDB_F_XXX.
1200 * @param pErrInfo Where to return additional error info. Can be NULL.
1201 */
1202static int rtFsPdbVolTryInit(PRTFSPDBVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
1203{
1204 /*
1205 * First initialize the state so that rtFsPdbVol_Close won't trip up.
1206 */
1207 pThis->hVfsSelf = hVfsSelf;
1208 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsPdbVol_Close releases it. */
1209 pThis->cbBacking = 0;
1210 pThis->cBackingPages = 0;
1211 pThis->fFlags = fFlags;
1212 pThis->cbPage = 0;
1213 pThis->enmVersion = RTFSPDBVER_INVALID;
1214 pThis->uAge = 0;
1215 pThis->uTimestamp = 0;
1216 RTUuidClear(&pThis->Uuid);
1217 pThis->cbNames = 0;
1218 pThis->pszzNames = NULL;
1219 pThis->papStreamInfo = NULL;
1220
1221 /*
1222 * Do init stuff that may fail.
1223 */
1224 int rc = RTVfsFileQuerySize(hVfsBacking, &pThis->cbBacking);
1225 if (RT_FAILURE(rc))
1226 return rc;
1227
1228 /*
1229 * Read, validate and load the file header.
1230 */
1231 union
1232 {
1233 RTPDB70HDR Hdr70;
1234 RTPDB20HDR Hdr20;
1235 } Buf;
1236 RT_ZERO(Buf);
1237
1238 rc = RTVfsFileReadAt(hVfsBacking, 0, &Buf, sizeof(Buf), NULL);
1239 if (RT_FAILURE(rc))
1240 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to file header");
1241
1242 uint32_t cPages;
1243 uint32_t cbMinRootStream;
1244 uint64_t offRootPageMap;
1245 if (memcmp(Buf.Hdr70.szSignature, RTPDB_SIGNATURE_700, sizeof(Buf.Hdr70.szSignature)) == 0)
1246 {
1247 pThis->enmVersion = RTFSPDBVER_7;
1248 pThis->cbPage = Buf.Hdr70.cbPage;
1249 pThis->cbRoot = Buf.Hdr70.cbRoot;
1250 cPages = Buf.Hdr70.cPages;
1251 cbMinRootStream = RT_UOFFSETOF(RTPDB70ROOT, aStreams[4]) + sizeof(RTPDB70PAGE) * 4;
1252 offRootPageMap = Buf.Hdr70.iRootPages * pThis->cbPage;
1253 }
1254 else if (memcmp(Buf.Hdr20.szSignature, RTPDB_SIGNATURE_200, sizeof(Buf.Hdr20.szSignature)) == 0)
1255 {
1256 pThis->enmVersion = RTFSPDBVER_2;
1257 pThis->cbPage = Buf.Hdr20.cbPage;
1258 pThis->cbRoot = Buf.Hdr20.RootStream.cbStream;
1259 cPages = Buf.Hdr20.cPages;
1260 cbMinRootStream = RT_UOFFSETOF(RTPDB20ROOT, aStreams[4]) + sizeof(RTPDB20PAGE) * 4; /** @todo ?? */
1261 offRootPageMap = RT_UOFFSETOF(RTPDB20HDR, aiRootPageMap);
1262 }
1263 else
1264 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unknown file header signature: %.44Rhxs", Buf.Hdr70.szSignature);
1265
1266 if ( pThis->cbPage != _4K
1267 && pThis->cbPage != _8K
1268 && pThis->cbPage != _16K
1269 && pThis->cbPage != _32K
1270 && pThis->cbPage != _64K
1271 && pThis->cbPage != _2K
1272 && pThis->cbPage != _1K
1273 && pThis->cbPage != 512)
1274 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unsupported page size: %#x", pThis->cbPage);
1275 pThis->cBackingPages = pThis->cbBacking / pThis->cbPage;
1276 if (cPages != pThis->cBackingPages)
1277 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unexpected page count: %#x, expected %#llx (page size %#x)",
1278 cPages, pThis->cBackingPages, pThis->cbPage);
1279
1280 if (offRootPageMap > pThis->cbBacking - pThis->cbPage)
1281 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus root page map start: %#llx (file size %#llx)",
1282 offRootPageMap, pThis->cbBacking);
1283 if (pThis->cbRoot < cbMinRootStream)
1284 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Root stream is smaller than expected: %#x, expected at least %#x",
1285 pThis->cbRoot, cbMinRootStream);
1286 if (pThis->cbRoot > _64M)
1287 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Root stream is too large: %#x, max supported %#x",
1288 pThis->cbRoot, _64M);
1289 if (pThis->cbRoot > pThis->cbBacking)
1290 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Root stream is too large: %#x, file size %#llx",
1291 pThis->cbRoot, pThis->cbBacking);
1292
1293 /*
1294 * Load the root stream into memory.
1295 */
1296 uint32_t const cRootPages = (pThis->cbRoot + pThis->cbPage - 1) / pThis->cbPage;
1297 pThis->pbRoot = (uint8_t *)RTMemAllocZ(RT_ALIGN_32(pThis->cbRoot, 64));
1298 if (!pThis->pbRoot)
1299 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to allocate root stream backing: %#x bytes",
1300 RT_ALIGN_32(pThis->cbRoot, 64));
1301 /* Get the root page map. */
1302 size_t const cbPageMap = cRootPages * (pThis->enmVersion == RTFSPDBVER_2 ? sizeof(RTPDB20PAGE) : sizeof(RTPDB70PAGE));
1303 void * const pvPageMap = RTMemTmpAlloc(cbPageMap);
1304 if (!pvPageMap)
1305 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to allocate %#zx bytes for the root page map", cbPageMap);
1306
1307 rc = RTVfsFileReadAt(hVfsBacking, offRootPageMap, pvPageMap, cbPageMap, NULL);
1308 if (RT_SUCCESS(rc))
1309 {
1310 /* Validate the page map. */
1311 for (uint32_t iPageMap = 0; iPageMap < cRootPages; iPageMap++)
1312 {
1313 RTPDB70PAGE const iPageNo = pThis->enmVersion == RTFSPDBVER_2
1314 ? ((PCRTPDB20PAGE)pvPageMap)[iPageMap] : ((PCRTPDB70PAGE)pvPageMap)[iPageMap];
1315 if (iPageNo > 0 && iPageNo < pThis->cBackingPages)
1316 continue;
1317 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1318 "Root page map entry %#x is out of bounds: %#x (max %#llx)",
1319 iPageMap, iPageNo, pThis->cBackingPages);
1320 break;
1321 }
1322
1323 /* Read using regular file reader. */
1324 RTFSPDBFILEOBJ FileObj;
1325 rtFsPdbPopulateFileObj(&FileObj, pThis, 0, pThis->cbRoot, pvPageMap);
1326
1327 RTSGSEG SgSeg = { pThis->pbRoot, pThis->cbRoot };
1328 RTSGBUF SgBuf;
1329 RTSgBufInit(&SgBuf, &SgSeg, 1);
1330
1331 rc = rtFsPdbFile_Read(&FileObj, 0, &SgBuf, true /*fBlocking*/, NULL);
1332 }
1333 RTMemTmpFree(pvPageMap);
1334 if (RT_FAILURE(rc))
1335 return rc;
1336
1337 /*
1338 * Validate the root stream.
1339 */
1340 if (pThis->enmVersion == RTFSPDBVER_2)
1341 {
1342 PCRTPDB20ROOT const pRoot = (PCRTPDB20ROOT)pThis->pbRoot;
1343
1344 /* Stream count. */
1345 if ( RT_UOFFSETOF_DYN(RTPDB20ROOT, aStreams[pRoot->cStreams]) <= pThis->cbRoot
1346 && pRoot->cStreams >= 1)
1347 pThis->cStreams = pRoot->cStreams;
1348 else
1349 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1350 "Bogus root stream count: %#x (cbRoot=%#x)", pRoot->cStreams, pThis->cbRoot);
1351
1352 rc = rtFsPdbVolAllocInitialStreamInfo(pThis, pErrInfo);
1353 if (RT_FAILURE(rc))
1354 return rc;
1355
1356 /* Validate stream data and page map while populating the stream info table. */
1357 PCRTPDB20PAGE const pauPageMap = (PCRTPDB20PAGE)&pRoot->aStreams[pThis->cStreams];
1358 uint32_t const cPageMapMax = pThis->cbRoot - RT_UOFFSETOF_DYN(RTPDB20ROOT, aStreams[pThis->cStreams]);
1359 uint32_t iPageMap = 0;
1360 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1361 {
1362 uint32_t cStreamPages = RTPdbSizeToPages(pRoot->aStreams[idxStream].cbStream, pThis->cbPage);
1363 if (iPageMap + cStreamPages <= cPageMapMax)
1364 {
1365 PRTFSPDBSTREAMINFO const pInfo = pThis->papStreamInfo[idxStream];
1366 pInfo->cbStream = pRoot->aStreams[idxStream].cbStream;
1367 pInfo->cPages = cStreamPages;
1368 pInfo->PageMap.pa20 = &pauPageMap[iPageMap];
1369
1370 while (cStreamPages-- > 0)
1371 if (pauPageMap[iPageMap] == 0 || pauPageMap[iPageMap] < pThis->cBackingPages)
1372 iPageMap++;
1373 else
1374 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1375 "Bogus page map entry %#x belonging to stream %#x: %#x (max %#llx)",
1376 iPageMap, idxStream, pauPageMap[iPageMap], pThis->cBackingPages);
1377 }
1378 else
1379 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1380 "Stream %#x exceeds the page map space: cbStream=%#x iPageMap=%#x cPageMapMax=%#x",
1381 idxStream, pRoot->aStreams[idxStream].cbStream, iPageMap, cPageMapMax);
1382 }
1383 }
1384 else
1385 {
1386 PCRTPDB70ROOT const pRoot = (PCRTPDB70ROOT)pThis->pbRoot;
1387
1388 /* Stream count. */
1389 if ( RT_UOFFSETOF_DYN(RTPDB70ROOT, aStreams[pRoot->cStreams]) <= pThis->cbRoot
1390 && pRoot->cStreams >= 1)
1391 pThis->cStreams = pRoot->cStreams;
1392 else
1393 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1394 "Bogus root stream count: %#x (cbRoot=%#x)", pRoot->cStreams, pThis->cbRoot);
1395
1396 rc = rtFsPdbVolAllocInitialStreamInfo(pThis, pErrInfo);
1397 if (RT_FAILURE(rc))
1398 return rc;
1399
1400 /* Validate stream data and page map while populating the stream info table. */
1401 PCRTPDB70PAGE const pauPageMap = (PCRTPDB70PAGE)&pRoot->aStreams[pThis->cStreams];
1402 uint32_t const cPageMapMax = pThis->cbRoot - RT_UOFFSETOF_DYN(RTPDB70ROOT, aStreams[pThis->cStreams]);
1403 uint32_t iPageMap = 0;
1404 for (uint32_t idxStream = 0; idxStream < pThis->cStreams; idxStream++)
1405 {
1406 uint32_t cStreamPages = RTPdbSizeToPages(pRoot->aStreams[idxStream].cbStream, pThis->cbPage);
1407 if (iPageMap + cStreamPages <= cPageMapMax)
1408 {
1409 PRTFSPDBSTREAMINFO const pInfo = pThis->papStreamInfo[idxStream];
1410 pInfo->cbStream = pRoot->aStreams[idxStream].cbStream;
1411 pInfo->cPages = cStreamPages;
1412 pInfo->PageMap.pa70 = &pauPageMap[iPageMap];
1413
1414 while (cStreamPages-- > 0)
1415 if (pauPageMap[iPageMap] == 0 || pauPageMap[iPageMap] < pThis->cBackingPages)
1416 iPageMap++;
1417 else
1418 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1419 "Bogus page map entry %#x belonging to stream %#x: %#x (max %#llx)",
1420 iPageMap, idxStream, pauPageMap[iPageMap], pThis->cBackingPages);
1421 }
1422 else
1423 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE,
1424 "Stream %#x exceeds the page map space: cbStream=%#x iPageMap=%#x cPageMapMax=%#x",
1425 idxStream, pRoot->aStreams[idxStream].cbStream, iPageMap, cPageMapMax);
1426 }
1427
1428 }
1429
1430 /*
1431 * Load stream #1 if there.
1432 */
1433 return rtFsPdbVolLoadStream1(pThis, pErrInfo);
1434}
1435
1436
1437/**
1438 * Opens an PDB file system volume.
1439 *
1440 * @returns IPRT status code.
1441 * @param hVfsFileIn The file or device backing the volume.
1442 * @param fFlags 0 or RTFSPDB_F_NO_NAMES
1443 * @param phVfs Where to return the virtual file system handle.
1444 * @param pErrInfo Where to return additional error information.
1445 */
1446RTDECL(int) RTFsPdbVolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
1447{
1448 /*
1449 * Quick input validation.
1450 */
1451 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
1452 *phVfs = NIL_RTVFS;
1453 AssertReturn(!(fFlags & ~RTFSPDB_F_NO_NAMES), VERR_INVALID_FLAGS);
1454
1455 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
1456 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1457
1458 /*
1459 * Create a new PDB VFS instance and try initialize it using the given input file.
1460 */
1461 RTVFS hVfs = NIL_RTVFS;
1462 PRTFSPDBVOL pThis = NULL;
1463 int rc = RTVfsNew(&g_rtFsPdbVolOps, sizeof(RTFSPDBVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
1464 if (RT_SUCCESS(rc))
1465 {
1466 rc = rtFsPdbVolTryInit(pThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
1467 if (RT_SUCCESS(rc))
1468 *phVfs = hVfs;
1469 else
1470 RTVfsRelease(hVfs);
1471 }
1472 else
1473 RTVfsFileRelease(hVfsFileIn);
1474 return rc;
1475}
1476
1477
1478/**
1479 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1480 */
1481static DECLCALLBACK(int) rtVfsChainPdbFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1482 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1483{
1484 RT_NOREF(pProviderReg, pSpec);
1485
1486 /*
1487 * Basic checks.
1488 */
1489 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1490 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1491 if ( pElement->enmType != RTVFSOBJTYPE_VFS
1492 && pElement->enmType != RTVFSOBJTYPE_DIR)
1493 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
1494 if (pElement->cArgs > 1)
1495 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1496
1497 /*
1498 * Parse the flag if present, save in pElement->uProvider.
1499 */
1500 uint32_t fFlags = 0;
1501 if (pElement->cArgs > 0)
1502 {
1503 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
1504 {
1505 const char *psz = pElement->paArgs[iArg].psz;
1506 if (*psz)
1507 {
1508 if (!strcmp(psz, "nonames"))
1509 fFlags |= RTFSPDB_F_NO_NAMES;
1510 else
1511 {
1512 *poffError = pElement->paArgs[iArg].offSpec;
1513 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
1514 }
1515 }
1516 }
1517 }
1518
1519 pElement->uProvider = fFlags;
1520 return VINF_SUCCESS;
1521}
1522
1523
1524/**
1525 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
1526 */
1527static DECLCALLBACK(int) rtVfsChainPdbFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
1528 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
1529 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1530{
1531 RT_NOREF(pProviderReg, pSpec, poffError);
1532
1533 int rc;
1534 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
1535 if (hVfsFileIn != NIL_RTVFSFILE)
1536 {
1537 RTVFS hVfs;
1538 rc = RTFsPdbVolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
1539 RTVfsFileRelease(hVfsFileIn);
1540 if (RT_SUCCESS(rc))
1541 {
1542 *phVfsObj = RTVfsObjFromVfs(hVfs);
1543 RTVfsRelease(hVfs);
1544 if (*phVfsObj != NIL_RTVFSOBJ)
1545 return VINF_SUCCESS;
1546 rc = VERR_VFS_CHAIN_CAST_FAILED;
1547 }
1548 }
1549 else
1550 rc = VERR_VFS_CHAIN_CAST_FAILED;
1551 return rc;
1552}
1553
1554
1555/**
1556 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
1557 */
1558static DECLCALLBACK(bool) rtVfsChainPdbFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
1559 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
1560 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
1561{
1562 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
1563 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
1564 || !pReuseElement->paArgs[0].uProvider)
1565 return true;
1566 return false;
1567}
1568
1569
1570/** VFS chain element 'file'. */
1571static RTVFSCHAINELEMENTREG g_rtVfsChainPdbFsVolReg =
1572{
1573 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1574 /* fReserved = */ 0,
1575 /* pszName = */ "pdbfs",
1576 /* ListEntry = */ { NULL, NULL },
1577 /* pszHelp = */ "Open a PDB file system, requires a file object on the left side.\n",
1578 /* pfnValidate = */ rtVfsChainPdbFsVol_Validate,
1579 /* pfnInstantiate = */ rtVfsChainPdbFsVol_Instantiate,
1580 /* pfnCanReuseElement = */ rtVfsChainPdbFsVol_CanReuseElement,
1581 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1582};
1583
1584RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainPdbFsVolReg, rtVfsChainPdbFsVolReg);
1585
Note: See TracBrowser for help on using the repository browser.

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