VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/ntfsvfs.cpp@ 69925

Last change on this file since 69925 was 69925, checked in by vboxsync, 7 years ago

iprt/ntfsvfs.cpp: Made opening and traversing directories work.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 197.0 KB
Line 
1/* $Id: ntfsvfs.cpp 69925 2017-12-05 00:06:59Z vboxsync $ */
2/** @file
3 * IPRT - NTFS Virtual Filesystem, currently only for reading allocation bitmap.
4 */
5
6/*
7 * Copyright (C) 2012-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include <iprt/fsvfs.h>
33
34#include <iprt/asm.h>
35#include <iprt/avl.h>
36#include <iprt/assert.h>
37#include <iprt/ctype.h>
38#include <iprt/file.h>
39#include <iprt/log.h>
40#include <iprt/mem.h>
41#include <iprt/string.h>
42#include <iprt/vfs.h>
43#include <iprt/vfslowlevel.h>
44#include <iprt/utf16.h>
45#include <iprt/formats/ntfs.h>
46
47#include <internal/fs.h> /* For RTFSMODE_SYMLINK_REPARSE_TAG. */
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53/** The maximum bitmap size to try cache in its entirity (in bytes). */
54#define RTFSNTFS_MAX_WHOLE_BITMAP_CACHE _64K
55/** The maximum node cache size (in bytes). */
56#if ARCH_BITS >= 64
57# define RTFSNTFS_MAX_NODE_CACHE_SIZE _1M
58#else
59# define RTFSNTFS_MAX_NODE_CACHE_SIZE _256K
60#endif
61
62/** Makes a combined NTFS version value.
63 * @see RTFSNTFSVOL::uNtfsVersion */
64#define RTFSNTFS_MAKE_VERSION(a_uMajor, a_uMinor) RT_MAKE_U16(a_uMinor, a_uMajor)
65
66
67/*********************************************************************************************************************************
68* Structures and Typedefs *
69*********************************************************************************************************************************/
70/** Pointer to the instance data for a NTFS volume. */
71typedef struct RTFSNTFSVOL *PRTFSNTFSVOL;
72/** Pointer to a NTFS MFT record. */
73typedef struct RTFSNTFSMFTREC *PRTFSNTFSMFTREC;
74/** Poitner to a NTFS core object record. */
75typedef struct RTFSNTFSCORE *PRTFSNTFSCORE;
76/** Pointer to an index node. */
77typedef struct RTFSNTFSIDXNODE *PRTFSNTFSIDXNODE;
78/** Pointer to a shared NTFS directory object. */
79typedef struct RTFSNTFSDIRSHRD *PRTFSNTFSDIRSHRD;
80/** Pointer to a shared NTFS file object. */
81typedef struct RTFSNTFSFILESHRD *PRTFSNTFSFILESHRD;
82
83
84/**
85 * NTFS disk allocation extent (internal representation).
86 */
87typedef struct RTFSNTFSEXTENT
88{
89 /** The disk or partition byte offset.
90 * This is set to UINT64_MAX for parts of sparse files that aren't recorded. */
91 uint64_t off;
92 /** The size of the extent in bytes. */
93 uint64_t cbExtent;
94} RTFSNTFSEXTENT;
95/** Pointer to an NTFS 9660 extent. */
96typedef RTFSNTFSEXTENT *PRTFSNTFSEXTENT;
97/** Pointer to a const NTFS 9660 extent. */
98typedef RTFSNTFSEXTENT const *PCRTFSNTFSEXTENT;
99
100/**
101 * An array of zero or more extents.
102 */
103typedef struct RTFSNTFSEXTENTS
104{
105 /** Number of bytes covered by the extents. */
106 uint64_t cbData;
107 /** Number of allocation extents. */
108 uint32_t cExtents;
109 /** Array of allocation extents. */
110 PRTFSNTFSEXTENT paExtents;
111} RTFSNTFSEXTENTS;
112/** Pointer to an extent array. */
113typedef RTFSNTFSEXTENTS *PRTFSNTFSEXTENTS;
114/** Pointer to a const extent array. */
115typedef RTFSNTFSEXTENTS const *PCRTFSNTFSEXTENTS;
116
117
118/**
119 * NTFS MFT record.
120 *
121 * These are kept in a tree to , so
122 */
123typedef struct RTFSNTFSMFTREC
124{
125 /** MFT record number (index) as key. */
126 AVLU64NODECORE TreeNode;
127 /** Pointer to the next MFT record if chained. Holds a reference. */
128 PRTFSNTFSMFTREC pNext;
129 union
130 {
131 /** Generic record pointer. RTFSNTFSVOL::cbMftRecord in size. */
132 uint8_t *pbRec;
133 /** Pointer to the file record. */
134 PNTFSRECFILE pFileRec;
135 } RT_UNION_NM(u);
136 /** Pointer to the core object with the parsed data.
137 * This is a weak reference. Non-base MFT record all point to the base one. */
138 PRTFSNTFSCORE pCore;
139 /** Reference counter. */
140 uint32_t volatile cRefs;
141 /** Set if this is a base MFT record. */
142 bool fIsBase;
143} RTFSNTFSMFTREC;
144
145
146/** Pointer to a attribute subrecord structure. */
147typedef struct RTFSNTFSATTRSUBREC *PRTFSNTFSATTRSUBREC;
148
149/**
150 * An attribute subrecord.
151 *
152 * This is for covering non-resident attributes that have had their allocation
153 * list split.
154 */
155typedef struct RTFSNTFSATTRSUBREC
156{
157 /** Pointer to the next one. */
158 PRTFSNTFSATTRSUBREC pNext;
159 /** Pointer to the attribute header.
160 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */
161 PNTFSATTRIBHDR pAttrHdr;
162 /** Disk space allocation if non-resident. */
163 RTFSNTFSEXTENTS Extents;
164} RTFSNTFSATTRSUBREC;
165
166/**
167 * An attribute.
168 */
169typedef struct RTFSNTFSATTR
170{
171 /** List entry (head RTFSNTFSCORE::AttribHead). */
172 RTLISTNODE ListEntry;
173 /** Pointer to the core object this attribute belongs to. */
174 PRTFSNTFSCORE pCore;
175 /** Pointer to the attribute header.
176 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */
177 PNTFSATTRIBHDR pAttrHdr;
178 /** The offset of the attribute header in the MFT record.
179 * This is needed to validate header relative offsets. */
180 uint32_t offAttrHdrInMftRec;
181 /** Number of resident bytes available (can be smaller than cbValue).
182 * Set to zero for non-resident attributes. */
183 uint32_t cbResident;
184 /** The (uncompressed) attribute size. */
185 uint64_t cbValue;
186 /** Disk space allocation if non-resident. */
187 RTFSNTFSEXTENTS Extents;
188 /** Pointer to any subrecords containing further allocation extents. */
189 PRTFSNTFSATTRSUBREC pSubRecHead;
190 /** Pointer to the VFS object for this attribute.
191 * This is a weak reference since it's the VFS object that is referencing us. */
192 union
193 {
194 /** Pointer to a shared directory (NTFS_AT_DIRECTORY). */
195 PRTFSNTFSDIRSHRD pSharedDir;
196 /** Pointer to a shared file (NTFS_AT_DATA). */
197 PRTFSNTFSFILESHRD pSharedFile;
198 } uObj;
199} RTFSNTFSATTR;
200/** Pointer to a attribute structure. */
201typedef RTFSNTFSATTR *PRTFSNTFSATTR;
202
203
204/**
205 * NTFS file system object, shared part.
206 */
207typedef struct RTFSNTFSCORE
208{
209 /** Reference counter. */
210 uint32_t volatile cRefs;
211 /** Pointer to the volume. */
212 PRTFSNTFSVOL pVol;
213 /** Pointer to the head of the MFT record chain for this object.
214 * Holds a reference. */
215 PRTFSNTFSMFTREC pMftRec;
216 /** List of attributes (RTFSNTFSATTR). */
217 RTLISTANCHOR AttribHead;
218} RTFSNTFSCORE;
219
220
221/**
222 * Node lookup information for facilitating binary searching of node.
223 */
224typedef struct RTFSNTFSIDXNODEINFO
225{
226 /** The index header. */
227 PCNTFSINDEXHDR pIndexHdr;
228 /** Number of entries. */
229 uint32_t cEntries;
230 /** Set if internal node. */
231 bool fInternal;
232 /** Array with pointers to the entries. */
233 PCNTFSIDXENTRYHDR *papEntries;
234 /** Pointer to the index node this info is for, NULL if root node.
235 * This is for reducing the enumeration stack entry size. */
236 PRTFSNTFSIDXNODE pNode;
237 /** Pointer to the NTFS volume instace. */
238 PRTFSNTFSVOL pVol;
239} RTFSNTFSIDXNODEINFO;
240/** Pointer to index node lookup info. */
241typedef RTFSNTFSIDXNODEINFO *PRTFSNTFSIDXNODEINFO;
242/** Pointer to const index node lookup info. */
243typedef RTFSNTFSIDXNODEINFO const *PCRTFSNTFSIDXNODEINFO;
244
245/**
246 * Index node, cached.
247 *
248 * These are cached to avoid reading, validating and parsing things each time a
249 * subnode is accessed.
250 */
251typedef struct RTFSNTFSIDXNODE
252{
253 /** Entry in RTFSNTFSVOL::IdxNodeCahceRoot, key is disk byte offset. */
254 AVLU64NODECORE TreeNode;
255 /** List entry on the unused list. Gets removed from it when cRefs is
256 * increase to one, and added when it reaches zero. */
257 RTLISTNODE UnusedListEntry;
258 /** Reference counter. */
259 uint32_t volatile cRefs;
260 /** The estimated memory cost of this node. */
261 uint32_t cbCost;
262 /** Pointer to the node data. */
263 PNTFSATINDEXALLOC pNode;
264 /** Node info. */
265 RTFSNTFSIDXNODEINFO NodeInfo;
266} RTFSNTFSIDXNODE;
267
268/**
269 * Common index root structure.
270 */
271typedef struct RTFSNTFSIDXROOTINFO
272{
273 /** Pointer to the index root attribute value. */
274 PCNTFSATINDEXROOT pRoot;
275 /** Pointer to the index allocation attribute, if present.
276 * This and the bitmap may be absent if the whole directory fits into the
277 * root index. */
278 PRTFSNTFSATTR pAlloc;
279 /** End of the node addresses range (exclusive). */
280 uint64_t uEndNodeAddresses;
281 /** Node address misalignement mask. */
282 uint32_t fNodeAddressMisalign;
283 /** The byte shift count for node addresses. */
284 uint8_t cNodeAddressByteShift;
285 /** Node info for the root. */
286 RTFSNTFSIDXNODEINFO NodeInfo;
287 /** Pointer to the index root attribute. We reference the core thru this and
288 * use it to zero RTFSNTFSATTR::uObj::pSharedDir on destruction. */
289 PRTFSNTFSATTR pRootAttr;
290} RTFSNTFSIDXROOTINFO;
291/** Pointer to an index root structure. */
292typedef RTFSNTFSIDXROOTINFO *PRTFSNTFSIDXROOTINFO;
293/** Pointer to a const index root structure. */
294typedef RTFSNTFSIDXROOTINFO const *PCRTFSNTFSIDXROOTINFO;
295
296/**
297 * Pointer to a shared NTFS directory object.
298 */
299typedef struct RTFSNTFSDIRSHRD
300{
301 /** Reference counter. */
302 uint32_t volatile cRefs;
303 /** Index root information. */
304 RTFSNTFSIDXROOTINFO RootInfo;
305} RTFSNTFSDIRSHRD;
306
307/**
308 * Index stack entry for index enumeration.
309 */
310typedef struct RTFSNTFSIDXSTACKENTRY
311{
312 /** The next entry to process in this stack entry. */
313 uint32_t iNext;
314 /** Pointer to the node info for this entry. */
315 PRTFSNTFSIDXNODEINFO pNodeInfo;
316} RTFSNTFSIDXSTACKENTRY;
317/** Pointer to an index enumeration stack entry. */
318typedef RTFSNTFSIDXSTACKENTRY *PRTFSNTFSIDXSTACKENTRY;
319
320
321/**
322 * Open directory instance.
323 */
324typedef struct RTFSNTFSDIR
325{
326 /** Pointer to the shared directory instance (referenced). */
327 PRTFSNTFSDIRSHRD pShared;
328 /** Set if we've reached the end of the directory enumeration. */
329 bool fNoMoreFiles;
330 /** The enumeration stack size. */
331 uint32_t cEnumStackEntries;
332 /** The allocated enumeration stack depth. */
333 uint32_t cEnumStackMaxDepth;
334 /** The numeration stack. Allocated as needed. */
335 PRTFSNTFSIDXSTACKENTRY paEnumStack;
336} RTFSNTFSDIR;
337/** Pointer to an open directory instance. */
338typedef RTFSNTFSDIR *PRTFSNTFSDIR;
339
340
341/**
342 * Instance data for an NTFS volume.
343 */
344typedef struct RTFSNTFSVOL
345{
346 /** Handle to itself. */
347 RTVFS hVfsSelf;
348 /** The file, partition, or whatever backing the NTFS volume. */
349 RTVFSFILE hVfsBacking;
350 /** The size of the backing thingy. */
351 uint64_t cbBacking;
352 /** The formatted size of the volume. */
353 uint64_t cbVolume;
354 /** cbVolume expressed as a cluster count. */
355 uint64_t cClusters;
356
357 /** RTVFSMNT_F_XXX. */
358 uint32_t fMntFlags;
359 /** RTFSNTVFS_F_XXX (currently none defined). */
360 uint32_t fNtfsFlags;
361
362 /** The (logical) sector size. */
363 uint32_t cbSector;
364
365 /** The (logical) cluster size. */
366 uint32_t cbCluster;
367 /** Max cluster count value that won't overflow a signed 64-bit when
368 * converted to bytes. Inclusive. */
369 uint64_t iMaxVirtualCluster;
370 /** The shift count for converting between bytes and clusters. */
371 uint8_t cClusterShift;
372
373 /** Explicit padding. */
374 uint8_t abReserved[3];
375 /** The NTFS version of the volume (RTFSNTFS_MAKE_VERSION). */
376 uint16_t uNtfsVersion;
377 /** The NTFS_VOLUME_F_XXX. */
378 uint16_t fVolumeFlags;
379
380 /** The logical cluster number of the MFT. */
381 uint64_t uLcnMft;
382 /** The logical cluster number of the mirror MFT. */
383 uint64_t uLcnMftMirror;
384
385 /** The MFT record size. */
386 uint32_t cbMftRecord;
387 /** The default index (B-tree) node size. */
388 uint32_t cbDefaultIndexNode;
389
390 /** The volume serial number. */
391 uint64_t uSerialNo;
392
393 /** The '$Mft' data attribute. */
394 PRTFSNTFSATTR pMftData;
395
396 /** @name Allocation bitmap and cache.
397 * @{ */
398 /** The '$Bitmap' data attribute. */
399 PRTFSNTFSATTR pMftBitmap;
400 /** The first cluster currently loaded into the bitmap cache . */
401 uint64_t iFirstBitmapCluster;
402 /** The number of clusters currently loaded into the bitmap cache */
403 uint32_t cBitmapClusters;
404 /** The size of the pvBitmap allocation. */
405 uint32_t cbBitmapAlloc;
406 /** Allocation bitmap cache buffer. */
407 void *pvBitmap;
408 /** @} */
409
410 /** Root of the MFT record tree (RTFSNTFSMFTREC). */
411 AVLU64TREE MftRoot;
412
413 /** @name Directory/index related.
414 * @{ */
415 /** Tree of index nodes, index by disk byte offset. (RTFSNTFSIDXNODE) */
416 AVLU64TREE IdxNodeCacheRoot;
417 /** List of currently unreferenced index nodes. (RTFSNTFSIDXNODE)
418 * Most recently used nodes are found at the end of the list. Nodes are added
419 * when their reference counter reaches zero. They are removed when it
420 * increases to one again.
421 *
422 * The nodes are still in the index node cache tree (IdxNodeCacheRoot), but
423 * we'll trim this from the end when we reach a certain size. */
424 RTLISTANCHOR IdxNodeUnusedHead;
425 /** Number of unreferenced index nodes. */
426 uint32_t cUnusedIdxNodes;
427 /** Number of cached index nodes. */
428 uint32_t cIdxNodes;
429 /** Total index node memory cost. */
430 size_t cbIdxNodes;
431 /** The root directory. */
432 PRTFSNTFSDIRSHRD pRootDir;
433 /** Lower to uppercase conversion table for this filesystem.
434 * This always has 64K valid entries. */
435 PRTUTF16 pawcUpcase;
436 /** @} */
437
438} RTFSNTFSVOL;
439
440
441
442/*********************************************************************************************************************************
443* Internal Functions *
444*********************************************************************************************************************************/
445static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis);
446static uint32_t rtFsNtfsCore_Retain(PRTFSNTFSCORE pThis);
447#ifdef LOG_ENABLED
448static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot);
449#endif
450static int rtFsNtfsVol_NewDirFromShared(PRTFSNTFSVOL pThis, PRTFSNTFSDIRSHRD pSharedDir, PRTVFSDIR phVfsDir);
451static uint32_t rtFsNtfsDirShrd_Retain(PRTFSNTFSDIRSHRD pThis);
452static uint32_t rtFsNtfsIdxNode_Release(PRTFSNTFSIDXNODE pNode);
453static uint32_t rtFsNtfsIdxNode_Retain(PRTFSNTFSIDXNODE pNode);
454
455
456
457/**
458 * Checks if a bit is set in an NTFS bitmap (little endian).
459 *
460 * @returns true if set, false if not.
461 * @param pvBitmap The bitmap buffer.
462 * @param iBit The bit.
463 */
464DECLINLINE(bool) rtFsNtfsBitmap_IsSet(void *pvBitmap, uint32_t iBit)
465{
466#if 0 //def RT_LITTLE_ENDIAN
467 return ASMBitTest(pvBitmap, iBit);
468#else
469 uint8_t b = ((uint8_t const *)pvBitmap)[iBit >> 3];
470 return RT_BOOL(b & (1 << (iBit & 7)));
471#endif
472}
473
474
475
476static PRTFSNTFSMFTREC rtFsNtfsVol_NewMftRec(PRTFSNTFSVOL pVol, uint64_t idMft)
477{
478 PRTFSNTFSMFTREC pRec = (PRTFSNTFSMFTREC)RTMemAllocZ(sizeof(*pRec));
479 if (pRec)
480 {
481 pRec->pbRec = (uint8_t *)RTMemAllocZ(pVol->cbMftRecord);
482 if (pRec->pbRec)
483 {
484 pRec->TreeNode.Key = idMft;
485 pRec->pNext = NULL;
486 pRec->cRefs = 1;
487 if (RTAvlU64Insert(&pVol->MftRoot, &pRec->TreeNode))
488 return pRec;
489 RTMemFree(pRec);
490 }
491 }
492 return NULL;
493}
494
495
496static uint32_t rtFsNtfsMftRec_Destroy(PRTFSNTFSMFTREC pThis, PRTFSNTFSVOL pVol)
497{
498 RTMemFree(pThis->pbRec);
499 pThis->pbRec = NULL;
500
501 PAVLU64NODECORE pRemoved = RTAvlU64Remove(&pVol->MftRoot, pThis->TreeNode.Key);
502 Assert(pRemoved == &pThis->TreeNode); NOREF(pRemoved);
503
504 RTMemFree(pThis);
505
506 return 0;
507}
508
509
510static uint32_t rtFsNtfsMftRec_Retain(PRTFSNTFSMFTREC pThis)
511{
512 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
513 Assert(cRefs < 64);
514 return cRefs;
515}
516
517static uint32_t rtFsNtfsMftRec_Release(PRTFSNTFSMFTREC pThis, PRTFSNTFSVOL pVol)
518{
519 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
520 Assert(cRefs < 64);
521 if (cRefs != 0)
522 return cRefs;
523 return rtFsNtfsMftRec_Destroy(pThis, pVol);
524}
525
526
527#ifdef LOG_ENABLED
528/**
529 * Logs the MFT record
530 *
531 * @param pRec The MFT record to log.
532 * @param cbMftRecord MFT record size (from RTFSNTFSVOL).
533 */
534static void rtfsNtfsMftRec_Log(PRTFSNTFSMFTREC pRec, uint32_t cbMftRecord)
535{
536 if (LogIs2Enabled())
537 {
538 PCNTFSRECFILE pFileRec = pRec->pFileRec;
539 Log2(("NTFS: MFT #%#RX64\n", pRec->TreeNode.Key));
540 if (pFileRec->Hdr.uMagic == NTFSREC_MAGIC_FILE)
541 {
542 size_t const cbRec = cbMftRecord;
543 uint8_t const * const pbRec = pRec->pbRec;
544
545 Log2(("NTFS: FILE record: \n"));
546 Log2(("NTFS: UpdateSeqArray %#x L %#x\n", RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray), RT_LE2H_U16(pFileRec->Hdr.cUpdateSeqEntries) ));
547 Log2(("NTFS: uLsn %#RX64\n", RT_LE2H_U64(pFileRec->uLsn)));
548 Log2(("NTFS: uRecReuseSeqNo %#RX16\n", RT_LE2H_U16(pFileRec->uRecReuseSeqNo)));
549 Log2(("NTFS: cLinks %#RX16\n", RT_LE2H_U16(pFileRec->cLinks)));
550 Log2(("NTFS: offFirstAttrib %#RX16\n", RT_LE2H_U16(pFileRec->offFirstAttrib)));
551 Log2(("NTFS: fFlags %#RX16%s%s\n", RT_LE2H_U16(pFileRec->fFlags),
552 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_IN_USE ? " in-use" : "",
553 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_DIRECTORY ? " directory" : ""));
554 Log2(("NTFS: cbRecUsed %#RX32\n", RT_LE2H_U32(pFileRec->cbRecUsed)));
555 Log2(("NTFS: BaseMftRec %#RX64, sqn %#x\n",
556 NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec), NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec)));
557 Log2(("NTFS: idNextAttrib %#RX16\n", RT_LE2H_U16(pFileRec->idNextAttrib)));
558 if ( RT_LE2H_U16(pFileRec->offFirstAttrib) >= sizeof(*pFileRec)
559 && ( RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray) >= sizeof(*pFileRec)
560 || pFileRec->Hdr.offUpdateSeqArray == 0))
561 {
562 Log2(("NTFS: uPaddingOrUsa %#RX16\n", pFileRec->uPaddingOrUsa));
563 Log2(("NTFS: idxMftSelf %#RX32\n", RT_LE2H_U32(pFileRec->idxMftSelf)));
564 }
565
566 uint32_t offRec = pFileRec->offFirstAttrib;
567 size_t cbRecUsed = RT_MIN(cbRec, pFileRec->cbRecUsed);
568 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
569 {
570 PCNTFSATTRIBHDR pHdr = (PCNTFSATTRIBHDR)&pbRec[offRec];
571 uint32_t const cbAttrib = RT_LE2H_U32(pHdr->cbAttrib);
572 Log2(("NTFS: @%#05x: Attrib record: %#x LB %#x, instance #%#x, fFlags=%#RX16, %s\n", offRec,
573 RT_LE2H_U32(pHdr->uAttrType), cbAttrib, RT_LE2H_U16(pHdr->idAttrib), RT_LE2H_U16(pHdr->fFlags),
574 pHdr->fNonResident == 0 ? "resident" : pHdr->fNonResident == 1 ? "non-resident" : "bad-resident-flag"));
575 if (pHdr->offName && pHdr->cwcName)
576 {
577 if (offRec + RT_LE2H_U16(pHdr->offName) + pHdr->cwcName * sizeof(RTUTF16) <= cbRec)
578 Log2(("NTFS: Name %.*ls\n", pHdr->cwcName,&pbRec[offRec + RT_LE2H_U16(pHdr->offName)]));
579 else
580 Log2(("NTFS: Name <!out of bounds!> %#x L %#x\n", RT_LE2H_U16(pHdr->offName), pHdr->cwcName));
581 }
582 switch (pHdr->uAttrType)
583 {
584 case NTFS_AT_UNUSED: Log2(("NTFS: Type: UNUSED\n")); break;
585 case NTFS_AT_STANDARD_INFORMATION: Log2(("NTFS: Type: STANDARD_INFORMATION\n")); break;
586 case NTFS_AT_ATTRIBUTE_LIST: Log2(("NTFS: Type: ATTRIBUTE_LIST\n")); break;
587 case NTFS_AT_FILENAME: Log2(("NTFS: Type: FILENAME\n")); break;
588 case NTFS_AT_OBJECT_ID: Log2(("NTFS: Type: OBJECT_ID\n")); break;
589 case NTFS_AT_SECURITY_DESCRIPTOR: Log2(("NTFS: Type: SECURITY_DESCRIPTOR\n")); break;
590 case NTFS_AT_VOLUME_NAME: Log2(("NTFS: Type: VOLUME_NAME\n")); break;
591 case NTFS_AT_VOLUME_INFORMATION: Log2(("NTFS: Type: VOLUME_INFORMATION\n")); break;
592 case NTFS_AT_DATA: Log2(("NTFS: Type: DATA\n")); break;
593 case NTFS_AT_INDEX_ROOT: Log2(("NTFS: Type: INDEX_ROOT\n")); break;
594 case NTFS_AT_INDEX_ALLOCATION: Log2(("NTFS: Type: INDEX_ALLOCATION\n")); break;
595 case NTFS_AT_BITMAP: Log2(("NTFS: Type: BITMAP\n")); break;
596 case NTFS_AT_REPARSE_POINT: Log2(("NTFS: Type: REPARSE_POINT\n")); break;
597 case NTFS_AT_EA_INFORMATION: Log2(("NTFS: Type: EA_INFORMATION\n")); break;
598 case NTFS_AT_EA: Log2(("NTFS: Type: EA\n")); break;
599 case NTFS_AT_PROPERTY_SET: Log2(("NTFS: Type: PROPERTY_SET\n")); break;
600 case NTFS_AT_LOGGED_UTILITY_STREAM: Log2(("NTFS: Type: LOGGED_UTILITY_STREAM\n")); break;
601 default:
602 if (RT_LE2H_U32(pHdr->uAttrType) >= RT_LE2H_U32_C(NTFS_AT_FIRST_USER_DEFINED))
603 Log2(("NTFS: Type: unknown user defined - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
604 else
605 Log2(("NTFS: Type: unknown - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
606 break;
607 }
608
609 size_t const cbMaxAttrib = cbRec - offRec;
610 if (!pHdr->fNonResident)
611 {
612 uint16_t const offValue = RT_LE2H_U16(pHdr->u.Res.offValue);
613 uint32_t const cbValue = RT_LE2H_U32(pHdr->u.Res.cbValue);
614 Log2(("NTFS: Value: %#x LB %#x, fFlags=%#x bReserved=%#x\n",
615 offValue, cbValue, pHdr->u.Res.fFlags, pHdr->u.Res.bReserved));
616 if ( offValue < cbMaxAttrib
617 && cbValue < cbMaxAttrib
618 && offValue + cbValue <= cbMaxAttrib)
619 {
620 uint8_t const *pbValue = &pbRec[offRec + offValue];
621 RTTIMESPEC Spec;
622 char sz[80];
623 switch (pHdr->uAttrType)
624 {
625 case NTFS_AT_STANDARD_INFORMATION:
626 {
627 PCNTFSATSTDINFO pInfo = (PCNTFSATSTDINFO)pbValue;
628 if (cbValue >= NTFSATSTDINFO_SIZE_NTFS_V12)
629 {
630 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
631 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
632 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
633 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
634 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
635 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
636 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
637 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
638 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
639 Log2(("NTFS: cMaxFileVersions %#RX32\n", RT_LE2H_U32(pInfo->cMaxFileVersions) ));
640 Log2(("NTFS: uFileVersion %#RX32\n", RT_LE2H_U32(pInfo->uFileVersion) ));
641 }
642 else
643 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATSTDINFO!\n",
644 cbValue, NTFSATSTDINFO_SIZE_NTFS_V12));
645 if (cbValue >= sizeof(*pInfo))
646 {
647 Log2(("NTFS: idClass %#RX32\n", RT_LE2H_U32(pInfo->idClass) ));
648 Log2(("NTFS: idOwner %#RX32\n", RT_LE2H_U32(pInfo->idOwner) ));
649 Log2(("NTFS: idSecurity %#RX32\n", RT_LE2H_U32(pInfo->idSecurity) ));
650 Log2(("NTFS: cbQuotaChared %#RX64\n", RT_LE2H_U64(pInfo->cbQuotaChared) ));
651 Log2(("NTFS: idxUpdateSequence %#RX64\n", RT_LE2H_U64(pInfo->idxUpdateSequence) ));
652 }
653 if (cbValue > sizeof(*pInfo))
654 Log2(("NTFS: Undefined data: %.*Rhxs\n", cbValue - sizeof(*pInfo), &pbValue[sizeof(*pInfo)]));
655 break;
656 }
657
658 case NTFS_AT_ATTRIBUTE_LIST:
659 {
660 uint32_t iEntry = 0;
661 uint32_t offEntry = 0;
662 while (offEntry + NTFSATLISTENTRY_SIZE_MINIMAL < cbValue)
663 {
664 PCNTFSATLISTENTRY pInfo = (PCNTFSATLISTENTRY)&pbValue[offEntry];
665 Log2(("NTFS: attr[%u]: %#x in %#RX64 (sqn %#x), instance %#x, VNC=%#RX64-, name %#x L %#x\n",
666 iEntry, RT_LE2H_U32(pInfo->uAttrType), NTFSMFTREF_GET_IDX(&pInfo->InMftRec),
667 NTFSMFTREF_GET_SEQ(&pInfo->InMftRec), RT_LE2H_U16(pInfo->idAttrib),
668 RT_LE2H_U64(pInfo->iVcnFirst), pInfo->offName, pInfo->cwcName));
669 if ( pInfo->cwcName > 0
670 && pInfo->offName < pInfo->cbEntry)
671 Log2(("NTFS: name '%.*ls'\n", pInfo->cwcName, (uint8_t *)pInfo + pInfo->offName));
672
673 /* next */
674 if (pInfo->cbEntry < NTFSATLISTENTRY_SIZE_MINIMAL)
675 {
676 Log2(("NTFS: cbEntry is too small! cbEntry=%#x, min %#x\n",
677 pInfo->cbEntry, NTFSATLISTENTRY_SIZE_MINIMAL));
678 break;
679 }
680 iEntry++;
681 offEntry += RT_ALIGN_32(pInfo->cbEntry, 8);
682 }
683 break;
684 }
685
686 case NTFS_AT_FILENAME:
687 {
688 PCNTFSATFILENAME pInfo = (PCNTFSATFILENAME)pbValue;
689 if (cbValue >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
690 {
691 Log2(("NTFS: ParentDirMftRec %#RX64, sqn %#x\n",
692 NTFSMFTREF_GET_IDX(&pInfo->ParentDirMftRec), NTFSMFTREF_GET_SEQ(&pInfo->ParentDirMftRec) ));
693 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
694 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
695 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
696 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
697 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
698 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
699 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
700 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
701 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
702 RT_LE2H_U64(pInfo->cbAllocated), RT_LE2H_U64(pInfo->cbAllocated)));
703 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
704 RT_LE2H_U64(pInfo->cbData), RT_LE2H_U64(pInfo->cbData)));
705 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
706 if (RT_LE2H_U32(pInfo->fFileAttribs) & NTFS_FA_REPARSE_POINT)
707 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pInfo->u.uReparseTag) ));
708 else
709 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pInfo->u.cbPackedEas) ));
710 Log2(("NTFS: cwcFilename %#x\n", pInfo->cwcFilename));
711 Log2(("NTFS: fFilenameType %#x\n", pInfo->fFilenameType));
712 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pInfo->cwcFilename]) <= cbValue)
713 Log2(("NTFS: wszFilename '%.*ls'\n", pInfo->cwcFilename, pInfo->wszFilename ));
714 else
715 Log2(("NTFS: Error! Truncated filename!!\n"));
716 }
717 else
718 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATFILENAME!\n",
719 cbValue, RT_OFFSETOF(NTFSATFILENAME, wszFilename) ));
720 break;
721 }
722
723 //case NTFS_AT_OBJECT_ID:
724 //case NTFS_AT_SECURITY_DESCRIPTOR:
725 //case NTFS_AT_VOLUME_NAME:
726 //case NTFS_AT_VOLUME_INFORMATION:
727 //case NTFS_AT_DATA:
728
729 case NTFS_AT_INDEX_ROOT:
730 rtFsNtfsVol_LogIndexRoot((PCNTFSATINDEXROOT)pbValue, cbValue);
731 break;
732
733 //case NTFS_AT_INDEX_ALLOCATION:
734 //case NTFS_AT_BITMAP:
735 //case NTFS_AT_REPARSE_POINT:
736 //case NTFS_AT_EA_INFORMATION:
737 //case NTFS_AT_EA:
738 //case NTFS_AT_PROPERTY_SET:
739 //case NTFS_AT_LOGGED_UTILITY_STREAM:
740
741 default:
742 if (cbValue <= 24)
743 Log2(("NTFS: %.*Rhxs\n", cbValue, pbValue));
744 else
745 Log2(("%.*Rhxd\n", cbValue, pbValue));
746 break;
747 }
748
749 }
750 else
751 Log2(("NTFS: !Value is out of bounds!\n"));
752 }
753 else if (RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED) <= cbMaxAttrib)
754 {
755 Log2(("NTFS: VNC range %#RX64 .. %#RX64 (%#RX64 clusters)\n",
756 RT_LE2H_U64(pHdr->u.NonRes.iVcnFirst), RT_LE2H_U64(pHdr->u.NonRes.iVcnLast),
757 RT_LE2H_U64(pHdr->u.NonRes.iVcnLast) - RT_LE2H_U64(pHdr->u.NonRes.iVcnFirst) + 1));
758 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
759 RT_LE2H_U64(pHdr->u.NonRes.cbAllocated), RT_LE2H_U64(pHdr->u.NonRes.cbAllocated)));
760 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
761 RT_LE2H_U64(pHdr->u.NonRes.cbData), RT_LE2H_U64(pHdr->u.NonRes.cbData)));
762 Log2(("NTFS: cbInitialized %#RX64 (%Rhcb)\n",
763 RT_LE2H_U64(pHdr->u.NonRes.cbInitialized), RT_LE2H_U64(pHdr->u.NonRes.cbInitialized)));
764 uint16_t const offMappingPairs = RT_LE2H_U16(pHdr->u.NonRes.offMappingPairs);
765 Log2(("NTFS: offMappingPairs %#RX16\n", offMappingPairs));
766 if ( pHdr->u.NonRes.abReserved[0] || pHdr->u.NonRes.abReserved[1]
767 || pHdr->u.NonRes.abReserved[2] || pHdr->u.NonRes.abReserved[3] || pHdr->u.NonRes.abReserved[4] )
768 Log2(("NTFS: abReserved %.7Rhxs\n", pHdr->u.NonRes.abReserved));
769 if (pHdr->u.NonRes.uCompressionUnit != 0)
770 Log2(("NTFS: Compression unit 2^%u clusters\n", pHdr->u.NonRes.uCompressionUnit));
771
772 if ( cbMaxAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
773 && cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
774 && ( offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
775 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED))
776 Log2(("NTFS: cbCompressed %#RX64 (%Rhcb)\n",
777 RT_LE2H_U64(pHdr->u.NonRes.cbCompressed), RT_LE2H_U64(pHdr->u.NonRes.cbCompressed)));
778 else if ( pHdr->u.NonRes.uCompressionUnit != 0
779 && pHdr->u.NonRes.uCompressionUnit != 64
780 && pHdr->u.NonRes.iVcnFirst == 0)
781 Log2(("NTFS: !Error! Compressed attrib fields are out of bound!\n"));
782
783 if ( offMappingPairs < cbAttrib
784 && offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
785 {
786 uint8_t const *pbPairs = &pbRec[offRec + offMappingPairs];
787 uint32_t const cbMaxPairs = cbAttrib - offMappingPairs;
788 int64_t iVnc = pHdr->u.NonRes.iVcnFirst;
789 if (cbMaxPairs < 48)
790 Log2(("NTFS: Mapping Pairs: cbMaxPairs=%#x %.*Rhxs\n", cbMaxPairs, cbMaxPairs, pbPairs));
791 else
792 Log2(("NTFS: Mapping Pairs: cbMaxPairs=%#x\n%.*Rhxd\n", cbMaxPairs, cbMaxPairs, pbPairs));
793 if (!iVnc && !*pbPairs)
794 Log2(("NTFS: [0]: Empty\n"));
795 else
796 {
797 if (iVnc != 0)
798 Log2(("NTFS: [00/0x000]: VCN=%#012RX64 L %#012RX64 - not mapped\n", 0, iVnc));
799 int64_t iLnc = 0;
800 uint32_t iPair = 0;
801 uint32_t offPairs = 0;
802 while (offPairs < cbMaxPairs)
803 {
804 /* First byte: 4-bit length of each of the pair values */
805 uint8_t const bLengths = pbPairs[offPairs];
806 if (!bLengths)
807 break;
808 uint8_t const cbRun = (bLengths & 0x0f) + (bLengths >> 4);
809 if (offPairs + cbRun > cbMaxPairs)
810 {
811 Log2(("NTFS: [%02d/%#05x]: run overrun! cbRun=%#x bLengths=%#x offPairs=%#x cbMaxPairs=%#x\n",
812 iPair, offPairs, cbRun, bLengths, offPairs, cbMaxPairs));
813 break;
814 }
815 //Log2(("NTFS: @%#05x: %.*Rhxs\n", offPairs, cbRun + 1, &pbPairs[offPairs]));
816
817 /* Value 1: Number of (virtual) clusters in this run. */
818 int64_t cClustersInRun;
819 uint8_t cbNum = (bLengths & 0xf);
820 if (cbNum)
821 {
822 uint8_t const *pbNum = &pbPairs[offPairs + cbNum]; /* last byte */
823 cClustersInRun = (int8_t)*pbNum--;
824 while (cbNum-- > 1)
825 cClustersInRun = (cClustersInRun << 8) + *pbNum--;
826 }
827 else
828 cClustersInRun = -1;
829
830 /* Value 2: The logical cluster delta to get to the first cluster in the run. */
831 cbNum = bLengths >> 4;
832 if (cbNum)
833 {
834 uint8_t const *pbNum = &pbPairs[offPairs + cbNum + (bLengths & 0xf)]; /* last byte */
835 int64_t cLcnDelta = (int8_t)*pbNum--;
836 while (cbNum-- > 1)
837 cLcnDelta = (cLcnDelta << 8) + *pbNum--;
838 iLnc += cLcnDelta;
839 Log2(("NTFS: [%02d/%#05x]: VNC=%#012RX64 L %#012RX64 => LNC=%#012RX64\n",
840 iPair, offPairs, iVnc, cClustersInRun, iLnc));
841 }
842 else
843 Log2(("NTFS: [%02d/%#05x]: VNC=%#012RX64 L %#012RX64 => HOLE\n",
844 iPair, offPairs, iVnc, cClustersInRun));
845
846 /* Advance. */
847 iVnc += cClustersInRun;
848 offPairs += 1 + cbRun;
849 iPair++;
850 }
851 }
852 }
853 else if ( cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
854 && cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
855 {
856 Log2(("NTFS: Warning! Odd non-resident attribute size: %#x!\n", cbAttrib));
857 if (cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
858 Log2(("NTFS: @%05x: %.*Rhxs!\n", offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
859 cbAttrib - NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
860 &pbRec[offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED]));
861 }
862 }
863 else
864 Log2(("NTFS: !Attrib header is out of bound!\n"));
865
866 /* Advance. */
867 offRec += RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_RESIDENT);
868 }
869
870 /* Anything left? */
871 if (offRec < cbRecUsed)
872 Log2(("NTFS: @%#05x: Tail: %.*Rhxs\n", offRec, cbRecUsed - offRec, &pbRec[offRec]));
873 }
874 else
875 Log2(("NTFS: Unknown record type: %.4Rhxs\n", pFileRec));
876 }
877}
878#endif /* LOG_ENABLED */
879
880
881static int rtFsNtfsAttr_ParseExtents(PRTFSNTFSATTR pAttrib, PRTFSNTFSEXTENTS pExtents, uint8_t cClusterShift, int64_t iVcnFirst,
882 uint64_t cbVolume, PRTERRINFO pErrInfo, uint64_t idxMft, uint32_t offAttrib)
883{
884 PCNTFSATTRIBHDR pAttrHdr = pAttrib->pAttrHdr;
885 Assert(pAttrHdr->fNonResident);
886 Assert(pExtents->cExtents == 0);
887 Assert(pExtents->paExtents == NULL);
888
889 /** @todo Not entirely sure how to best detect empty mapping pair program.
890 * Not sure if this is a real problem as zero length stuff can be
891 * resident. */
892 uint16_t const offMappingPairs = RT_LE2H_U16(pAttrHdr->u.NonRes.offMappingPairs);
893 uint32_t const cbAttrib = RT_LE2H_U32(pAttrHdr->cbAttrib);
894 if ( offMappingPairs != cbAttrib
895 && offMappingPairs != 0)
896 {
897 if (pAttrHdr->u.NonRes.iVcnFirst < iVcnFirst)
898 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
899 "Bad MFT record %#RX64: Attribute (@%#x) has a lower starting VNC than expected: %#RX64, %#RX64",
900 idxMft, offAttrib, pAttrHdr->u.NonRes.iVcnFirst, iVcnFirst);
901
902 if ( offMappingPairs >= cbAttrib
903 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
904 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
905 "Bad MFT record %#RX64: Mapping pair program for attribute (@%#x) is out of bounds: %#x, cbAttrib=%#x",
906 idxMft, offAttrib, offMappingPairs, cbAttrib);
907
908 /*
909 * Count the pairs.
910 */
911 uint8_t const * const pbPairs = (const uint8_t *)pAttrHdr + pAttrHdr->u.NonRes.offMappingPairs;
912 uint32_t const cbPairs = cbAttrib - offMappingPairs;
913 uint32_t offPairs = 0;
914 uint32_t cPairs = 0;
915 while (offPairs < cbPairs)
916 {
917 uint8_t const bLengths = pbPairs[offPairs];
918 if (bLengths)
919 {
920 uint8_t const cbRunField = bLengths & 0x0f;
921 uint8_t const cbLcnField = bLengths >> 4;
922 if ( cbRunField > 0
923 && cbRunField <= 8)
924 {
925 if (cbLcnField <= 8)
926 {
927 cPairs++;
928
929 /* Advance and check for overflow/end. */
930 offPairs += 1 + cbRunField + cbLcnField;
931 if (offPairs <= cbAttrib)
932 continue;
933 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
934 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x) is out of bounds",
935 idxMft, cPairs - 1, offAttrib);
936 }
937 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
938 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x): cbLcnField is out of bound: %u",
939 idxMft, cPairs - 1, offAttrib, cbLcnField);
940
941 }
942 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
943 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x): cbRunField is out of bound: %u",
944 idxMft, cPairs - 1, offAttrib, cbRunField);
945 }
946 break;
947 }
948
949 /*
950 * Allocate an the extent table for them.
951 */
952 uint32_t const cExtents = cPairs + (pAttrHdr->u.NonRes.iVcnFirst != iVcnFirst);
953 if (cExtents)
954 {
955 PRTFSNTFSEXTENT paExtents = (PRTFSNTFSEXTENT)RTMemAllocZ(sizeof(paExtents[0]) * cExtents);
956 AssertReturn(paExtents, VERR_NO_MEMORY);
957
958 /*
959 * Fill the table.
960 */
961 uint32_t iExtent = 0;
962
963 /* A sparse hole between this and the previous extent table? */
964 if (pAttrHdr->u.NonRes.iVcnFirst != iVcnFirst)
965 {
966 paExtents[iExtent].off = UINT64_MAX;
967 paExtents[iExtent].cbExtent = (pAttrHdr->u.NonRes.iVcnFirst - iVcnFirst) << cClusterShift;
968 Log3((" paExtent[%#04x]: %#018RX64 LB %#010RX64\n", iExtent, paExtents[iExtent].off, paExtents[iExtent].cbExtent));
969 iExtent++;
970 }
971
972 /* Run the program again, now with values and without verbose error checking. */
973 uint64_t cMaxClustersInRun = (INT64_MAX >> cClusterShift) - pAttrHdr->u.NonRes.iVcnFirst;
974 uint64_t cbData = 0;
975 int64_t iLcn = 0;
976 int rc = VINF_SUCCESS;
977 offPairs = 0;
978 for (; iExtent < cExtents; iExtent++)
979 {
980 uint8_t const bLengths = pbPairs[offPairs++];
981 uint8_t const cbRunField = bLengths & 0x0f;
982 uint8_t const cbLcnField = bLengths >> 4;
983 AssertBreakStmt((unsigned)cbRunField - 1U <= 7U, rc = VERR_VFS_BOGUS_FORMAT);
984 AssertBreakStmt((unsigned)cbLcnField <= 8U, rc = VERR_VFS_BOGUS_FORMAT);
985
986 AssertBreakStmt(!(pbPairs[offPairs + cbRunField - 1] & 0x80),
987 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
988 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): Negative runlength value",
989 idxMft, iExtent, offAttrib));
990 uint64_t cClustersInRun = 0;
991 switch (cbRunField)
992 {
993 case 8: cClustersInRun |= (uint64_t)pbPairs[offPairs + 7] << 56; RT_FALL_THRU();
994 case 7: cClustersInRun |= (uint64_t)pbPairs[offPairs + 6] << 48; RT_FALL_THRU();
995 case 6: cClustersInRun |= (uint64_t)pbPairs[offPairs + 5] << 40; RT_FALL_THRU();
996 case 5: cClustersInRun |= (uint64_t)pbPairs[offPairs + 4] << 32; RT_FALL_THRU();
997 case 4: cClustersInRun |= (uint32_t)pbPairs[offPairs + 3] << 24; RT_FALL_THRU();
998 case 3: cClustersInRun |= (uint32_t)pbPairs[offPairs + 2] << 16; RT_FALL_THRU();
999 case 2: cClustersInRun |= (uint16_t)pbPairs[offPairs + 1] << 8; RT_FALL_THRU();
1000 case 1: cClustersInRun |= (uint16_t)pbPairs[offPairs + 0] << 0; RT_FALL_THRU();
1001 }
1002 offPairs += cbRunField;
1003 AssertBreakStmt(cClustersInRun <= cMaxClustersInRun,
1004 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1005 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): too many clusters %#RX64, max %#RX64",
1006 idxMft, iExtent, offAttrib, cClustersInRun, cMaxClustersInRun));
1007 cMaxClustersInRun -= cClustersInRun;
1008 paExtents[iExtent].cbExtent = cClustersInRun << cClusterShift;
1009 cbData += cClustersInRun << cClusterShift;
1010
1011 if (cbLcnField)
1012 {
1013 unsigned offVncDelta = cbLcnField;
1014 int64_t cLncDelta = (int8_t)pbPairs[--offVncDelta + offPairs];
1015 while (offVncDelta-- > 0)
1016 cLncDelta = (cLncDelta << 8) | pbPairs[offVncDelta + offPairs];
1017 offPairs += cbLcnField;
1018
1019 iLcn += cLncDelta;
1020 if (iLcn >= 0)
1021 {
1022 paExtents[iExtent].off = (uint64_t)iLcn << cClusterShift;
1023 AssertBreakStmt((paExtents[iExtent].off >> cClusterShift) == (uint64_t)iLcn,
1024 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1025 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): iLcn %RX64 overflows when shifted by %u",
1026 idxMft, iExtent, offAttrib, iLcn, cClusterShift));
1027 AssertBreakStmt( paExtents[iExtent].off < cbVolume
1028 || paExtents[iExtent].cbExtent < cbVolume
1029 || paExtents[iExtent].off + paExtents[iExtent].cbExtent <= cbVolume,
1030 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1031 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x) outside volume: %#RX64 LB %#RX64, cbVolume=%#RX64",
1032 idxMft, iExtent, offAttrib, paExtents[iExtent].off,
1033 paExtents[iExtent].cbExtent, cbVolume));
1034 }
1035 else
1036 paExtents[iExtent].off = UINT64_MAX;
1037 }
1038 else
1039 paExtents[iExtent].off = UINT64_MAX;
1040 Log3((" paExtent[%#04x]: %#018RX64 LB %#010RX64\n", iExtent, paExtents[iExtent].off, paExtents[iExtent].cbExtent));
1041 }
1042
1043 /* Commit if everything went fine? */
1044 if (RT_SUCCESS(rc))
1045 {
1046 pExtents->cbData = cbData;
1047 pExtents->cExtents = cExtents;
1048 pExtents->paExtents = paExtents;
1049 }
1050 else
1051 {
1052 RTMemFree(paExtents);
1053 return rc;
1054 }
1055 }
1056 }
1057 return VINF_SUCCESS;
1058}
1059
1060
1061/**
1062 * Parses the given MTF record and all related records, putting the result in
1063 * pRec->pCore (with one reference for the caller).
1064 *
1065 * ASSUMES caller will release pRec->pCore on failure.
1066 *
1067 * @returns IPRT status code.
1068 * @param pThis The volume.
1069 * @param pRec The MFT record to parse.
1070 * @param pErrInfo Where to return additional error information. Optional.
1071 */
1072static int rtFsNtfsVol_ParseMft(PRTFSNTFSVOL pThis, PRTFSNTFSMFTREC pRec, PRTERRINFO pErrInfo)
1073{
1074 AssertReturn(!pRec->pCore, VERR_INTERNAL_ERROR_4);
1075
1076 /*
1077 * Check that it is a file record and that its base MFT record number is zero.
1078 * Caller should do the base record resolving.
1079 */
1080 PNTFSRECFILE pFileRec = pRec->pFileRec;
1081 if (pFileRec->Hdr.uMagic != NTFSREC_MAGIC_FILE)
1082 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1083 "Bad MFT record %#RX64: Not a FILE entry (%.4Rhxs)",
1084 pRec->TreeNode.Key, &pFileRec->Hdr);
1085 if (pFileRec->BaseMftRec.u64 != 0)
1086 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1087 "Bad MFT record %#RX64: Not a base record (%#RX64, sqn %#x)",
1088 pRec->TreeNode.Key, NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec),
1089 NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec) );
1090
1091 /*
1092 * Create a core node (1 reference, returned even on error).
1093 */
1094 PRTFSNTFSCORE pCore = (PRTFSNTFSCORE)RTMemAllocZ(sizeof(*pCore));
1095 AssertReturn(pCore, VERR_NO_MEMORY);
1096
1097 pCore->cRefs = 1;
1098 pCore->pVol = pThis;
1099 RTListInit(&pCore->AttribHead);
1100 pCore->pMftRec = pRec;
1101 rtFsNtfsMftRec_Retain(pRec);
1102 pRec->pCore = pCore;
1103
1104 /*
1105 * Parse attributes.
1106 * We process any attribute list afterwards, skipping attributes in this MFT record.
1107 */
1108 PRTFSNTFSATTR pAttrList = NULL;
1109 uint8_t * const pbRec = pRec->pbRec;
1110 uint32_t offRec = pFileRec->offFirstAttrib;
1111 uint32_t const cbRecUsed = RT_MIN(pThis->cbMftRecord, pFileRec->cbRecUsed);
1112 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
1113 {
1114 PNTFSATTRIBHDR pAttrHdr = (PNTFSATTRIBHDR)&pbRec[offRec];
1115
1116 /*
1117 * Validate the attribute data.
1118 */
1119 uint32_t const cbAttrib = RT_LE2H_U32(pAttrHdr->cbAttrib);
1120 uint32_t const cbMin = !pAttrHdr->fNonResident ? NTFSATTRIBHDR_SIZE_RESIDENT
1121 : pAttrHdr->u.NonRes.uCompressionUnit == 0 ? NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED
1122 : NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED;
1123 if (cbAttrib < cbMin)
1124 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1125 "Bad MFT record %#RX64: Attribute (@%#x) is too small (%#x, cbMin=%#x)",
1126 pRec->TreeNode.Key, offRec, cbAttrib, cbMin);
1127 if (offRec + cbAttrib > cbRecUsed)
1128 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1129 "Bad MFT record %#RX64: Attribute (@%#x) is too long (%#x, cbRecUsed=%#x)",
1130 pRec->TreeNode.Key, offRec, cbAttrib, cbRecUsed);
1131 if (cbAttrib & 0x7)
1132 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1133 "Bad MFT record %#RX64: Attribute (@%#x) size is misaligned: %#x",
1134 pRec->TreeNode.Key, offRec, cbAttrib);
1135 if (pAttrHdr->fNonResident)
1136 {
1137 int64_t const iVcnFirst = RT_LE2H_U64(pAttrHdr->u.NonRes.iVcnFirst);
1138 int64_t const iVcnLast = RT_LE2H_U64(pAttrHdr->u.NonRes.iVcnLast);
1139 if (iVcnFirst > iVcnLast)
1140 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1141 "Bad MFT record %#RX64: Attribute (@%#x): iVcnFirst (%#RX64) is higher than iVcnLast (%#RX64)",
1142 pRec->TreeNode.Key, offRec, iVcnFirst, iVcnLast);
1143 if (iVcnFirst < 0)
1144 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1145 "Bad MFT record %#RX64: Attribute (@%#x): iVcnFirst (%#RX64) is negative",
1146 pRec->TreeNode.Key, offRec, iVcnFirst);
1147 if ((uint64_t)iVcnLast > pThis->iMaxVirtualCluster)
1148 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1149 "Bad MFT record %#RX64: Attribute (@%#x): iVcnLast (%#RX64) is too high, max %RX64 (shift %#x)",
1150 pRec->TreeNode.Key, offRec, iVcnLast, pThis->cClusterShift, pThis->iMaxVirtualCluster);
1151 uint16_t const offMappingPairs = RT_LE2H_U16(pAttrHdr->u.NonRes.offMappingPairs);
1152 if ( (offMappingPairs != 0 && offMappingPairs < cbMin)
1153 || offMappingPairs > cbAttrib)
1154 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1155 "Bad MFT record %#RX64: Attribute (@%#x): offMappingPairs (%#x) is out of bounds (cbAttrib=%#x, cbMin=%#x)",
1156 pRec->TreeNode.Key, offRec, offMappingPairs, cbAttrib, cbMin);
1157 if (pAttrHdr->u.NonRes.uCompressionUnit > 16)
1158 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1159 "Bad MFT record %#RX64: Attribute (@%#x): uCompressionUnit (%#x) is too high",
1160 pRec->TreeNode.Key, offRec, pAttrHdr->u.NonRes.uCompressionUnit);
1161
1162 int64_t const cbAllocated = RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated);
1163 if (cbAllocated < 0)
1164 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1165 "Bad MFT record %#RX64: Attribute (@%#x): cbAllocated (%#RX64) is negative",
1166 pRec->TreeNode.Key, offRec, cbAllocated);
1167 if ((uint64_t)cbAllocated & (pThis->cbCluster - 1))
1168 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1169 "Bad MFT record %#RX64: Attribute (@%#x): cbAllocated (%#RX64) isn't cluster aligned (cbCluster=%#x)",
1170 pRec->TreeNode.Key, offRec, cbAllocated, pThis->cbCluster);
1171
1172 int64_t const cbData = RT_LE2H_U64(pAttrHdr->u.NonRes.cbData);
1173 if (cbData < 0)
1174 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1175 "Bad MFT record %#RX64: Attribute (@%#x): cbData (%#RX64) is negative",
1176 pRec->TreeNode.Key, offRec, cbData);
1177
1178 int64_t const cbInitialized = RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized);
1179 if (cbInitialized < 0)
1180 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1181 "Bad MFT record %#RX64: Attribute (@%#x): cbInitialized (%#RX64) is negative",
1182 pRec->TreeNode.Key, offRec, cbInitialized);
1183 if (cbMin >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED)
1184 {
1185 int64_t const cbCompressed = RT_LE2H_U64(pAttrHdr->u.NonRes.cbCompressed);
1186 if (cbAllocated < 0)
1187 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1188 "Bad MFT record %#RX64: Attribute (@%#x): cbCompressed (%#RX64) is negative",
1189 pRec->TreeNode.Key, offRec, cbCompressed);
1190 }
1191 }
1192 else
1193 {
1194 uint16_t const offValue = RT_LE2H_U32(pAttrHdr->u.Res.offValue);
1195 if ( offValue > cbAttrib
1196 || offValue < NTFSATTRIBHDR_SIZE_RESIDENT)
1197 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1198 "Bad MFT record %#RX64: Attribute (@%#x): offValue (%#RX16) is out of bounds (cbAttrib=%#RX32, cbValue=%#RX32)",
1199 pRec->TreeNode.Key, offRec, offValue, cbAttrib, RT_LE2H_U32(pAttrHdr->u.Res.cbValue));
1200 if ((pAttrHdr->fFlags & NTFS_AF_COMPR_FMT_MASK) != NTFS_AF_COMPR_FMT_NONE)
1201 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1202 "Bad MFT record %#RX64: Attribute (@%#x): fFlags (%#RX16) indicate compression of a resident attribute",
1203 pRec->TreeNode.Key, offRec, RT_LE2H_U16(pAttrHdr->fFlags));
1204 }
1205
1206 if (pAttrHdr->cwcName != 0)
1207 {
1208 uint16_t offName = RT_LE2H_U16(pAttrHdr->offName);
1209 if ( offName < cbMin
1210 || offName >= cbAttrib)
1211 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1212 "Bad MFT record %#RX64: Attribute (@%#x): offName (%#RX16) is out of bounds (cbAttrib=%#RX32, cbMin=%#RX32)",
1213 pRec->TreeNode.Key, offRec, offName, cbAttrib, cbMin);
1214 if (offName + pAttrHdr->cwcName * sizeof(RTUTF16) > cbAttrib)
1215 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1216 "Bad MFT record %#RX64: Attribute (@%#x): offName (%#RX16) + cwcName (%#x) is out of bounds (cbAttrib=%#RX32)",
1217 pRec->TreeNode.Key, offRec, offName, pAttrHdr->cwcName, cbAttrib);
1218 }
1219
1220 /*
1221 * Allocate and initialize a new attribute.
1222 */
1223 PRTFSNTFSATTR pAttrib = (PRTFSNTFSATTR)RTMemAllocZ(sizeof(*pAttrib));
1224 AssertReturn(pAttrib, VERR_NO_MEMORY);
1225 pAttrib->pAttrHdr = pAttrHdr;
1226 pAttrib->offAttrHdrInMftRec = offRec;
1227 pAttrib->pCore = pCore;
1228 //pAttrib->cbResident = 0;
1229 //pAttrib->cbValue = 0;
1230 //pAttrib->Extents.cExtents = 0;
1231 //pAttrib->Extents.paExtents = NULL;
1232 //pAttrib->pSubRecHead = NULL;
1233 if (pAttrHdr->fNonResident)
1234 {
1235 pAttrib->cbValue = RT_LE2H_U64(pAttrHdr->u.NonRes.cbData);
1236 int rc = rtFsNtfsAttr_ParseExtents(pAttrib, &pAttrib->Extents, pThis->cClusterShift, 0 /*iVncFirst*/,
1237 pThis->cbVolume, pErrInfo, pRec->TreeNode.Key, offRec);
1238 if (RT_FAILURE(rc))
1239 {
1240 RTMemFree(pAttrib);
1241 return rc;
1242 }
1243 }
1244 else
1245 {
1246 pAttrib->cbValue = RT_LE2H_U32(pAttrHdr->u.Res.cbValue);
1247 if ( (uint32_t)pAttrib->cbValue > 0
1248 && RT_LE2H_U16(pAttrHdr->u.Res.offValue) < cbAttrib)
1249 {
1250 pAttrib->cbResident = cbAttrib - RT_LE2H_U16(pAttrHdr->u.Res.offValue);
1251 if (pAttrib->cbResident > (uint32_t)pAttrib->cbValue)
1252 pAttrib->cbResident = (uint32_t)pAttrib->cbValue;
1253 }
1254 }
1255
1256 RTListAppend(&pCore->AttribHead, &pAttrib->ListEntry);
1257
1258 if (pAttrHdr->uAttrType == NTFS_AT_ATTRIBUTE_LIST)
1259 pAttrList = pAttrib;
1260
1261 /* Advance. */
1262 offRec += cbAttrib;
1263 }
1264
1265 /*
1266 * Process any attribute list.
1267 */
1268 if (pAttrList)
1269 {
1270 /** @todo */
1271 }
1272
1273 return VINF_SUCCESS;
1274}
1275
1276
1277/**
1278 * Translates a attribute value offset to a disk offset.
1279 *
1280 * @returns Disk offset, UINT64_MAX if not translatable for some reason.
1281 * @param pAttr The
1282 * @param off The offset to translate.
1283 * @param pcbValid Where to return the run length at the return offset.
1284 * Optional.
1285 */
1286static uint64_t rtFsNtfsAttr_OffsetToDisk(PRTFSNTFSATTR pAttr, uint64_t off, uint64_t *pcbValid)
1287{
1288 /*
1289 * Searching the extend list is a tad complicated since it starts in one
1290 * structure and continues in a different one. But whatever.
1291 */
1292 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1293 PRTFSNTFSATTRSUBREC pCurSub = NULL;
1294 for (;;)
1295 {
1296 if (off < pTable->cbData)
1297 {
1298 uint32_t iExtent = 0;
1299 while ( iExtent < pTable->cExtents
1300 && off >= pTable->paExtents[iExtent].cbExtent)
1301 {
1302 off -= pTable->paExtents[iExtent].cbExtent;
1303 iExtent++;
1304 }
1305 AssertReturn(iExtent < pTable->cExtents, UINT64_MAX);
1306 if (pcbValid)
1307 *pcbValid = pTable->paExtents[iExtent].cbExtent - off;
1308 return pTable->paExtents[iExtent].off != UINT64_MAX ? pTable->paExtents[iExtent].off + off : UINT64_MAX;
1309 }
1310
1311 /* Next table. */
1312 off -= pTable->cbData;
1313 if (!pCurSub)
1314 pCurSub = pAttr->pSubRecHead;
1315 else
1316 pCurSub = pCurSub->pNext;
1317 if (!pCurSub)
1318 {
1319 if (pcbValid)
1320 *pcbValid = 0;
1321 return UINT64_MAX;
1322 }
1323 pTable = &pCurSub->Extents;
1324 }
1325 /* not reached */
1326}
1327
1328
1329static int rtFsNtfsAttr_Read(PRTFSNTFSATTR pAttr, uint64_t off, void *pvBuf, size_t cbToRead)
1330{
1331 PRTFSNTFSVOL pVol = pAttr->pCore->pVol;
1332 int rc;
1333 if (!pAttr->pAttrHdr->fNonResident)
1334 {
1335 /*
1336 * The attribute is resident.
1337 */
1338 uint32_t cbAttrib = RT_LE2H_U32(pAttr->pAttrHdr->cbAttrib);
1339 uint32_t cbValue = RT_LE2H_U32(pAttr->pAttrHdr->u.Res.cbValue);
1340 uint16_t offValue = RT_LE2H_U16(pAttr->pAttrHdr->u.Res.offValue);
1341 if ( off < cbValue
1342 && cbToRead <= cbValue
1343 && off + cbToRead <= cbValue)
1344 {
1345 if (offValue <= cbAttrib)
1346 {
1347 cbAttrib -= offValue;
1348 if (off < cbAttrib)
1349 {
1350 /** @todo check if its possible to have cbValue larger than the attribute and
1351 * reading those extra bytes as zero. */
1352 if ( pAttr->offAttrHdrInMftRec + offValue + cbAttrib <= pVol->cbMftRecord
1353 && cbAttrib <= pVol->cbMftRecord)
1354 {
1355 size_t cbToCopy = cbAttrib - off;
1356 if (cbToCopy > cbToRead)
1357 cbToCopy = cbToRead;
1358 memcpy(pvBuf, (uint8_t *)pAttr->pAttrHdr + offValue, cbToCopy);
1359 pvBuf = (uint8_t *)pvBuf + cbToCopy;
1360 cbToRead -= cbToCopy;
1361 rc = VINF_SUCCESS;
1362 }
1363 else
1364 {
1365 rc = VERR_VFS_BOGUS_OFFSET;
1366 Log(("rtFsNtfsAttr_Read: bad resident attribute!\n"));
1367 }
1368 }
1369 else
1370 rc = VINF_SUCCESS;
1371 }
1372 else
1373 rc = VERR_VFS_BOGUS_FORMAT;
1374 }
1375 else
1376 rc = VERR_EOF;
1377 }
1378 else if (pAttr->pAttrHdr->u.NonRes.uCompressionUnit == 0)
1379 {
1380 /*
1381 * Uncompressed non-resident attribute.
1382 */
1383 uint64_t const cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated);
1384 if ( off >= cbAllocated
1385 || cbToRead > cbAllocated
1386 || off + cbToRead > cbAllocated)
1387 rc = VERR_EOF;
1388 else
1389 {
1390 rc = VINF_SUCCESS;
1391
1392 uint64_t const cbInitialized = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbInitialized);
1393 if ( off < cbInitialized
1394 && cbToRead > 0)
1395 {
1396 /*
1397 * Locate the first extent. This is a tad complicated.
1398 *
1399 * We move off along as we traverse the extent tables, so that it is relative
1400 * to the start of the current extent.
1401 */
1402 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1403 uint32_t iExtent = 0;
1404 PRTFSNTFSATTRSUBREC pCurSub = NULL;
1405 for (;;)
1406 {
1407 if (off < pTable->cbData)
1408 {
1409 while ( iExtent < pTable->cExtents
1410 && off >= pTable->paExtents[iExtent].cbExtent)
1411 {
1412 off -= pTable->paExtents[iExtent].cbExtent;
1413 iExtent++;
1414 }
1415 AssertReturn(iExtent < pTable->cExtents, VERR_INTERNAL_ERROR_2);
1416 break;
1417 }
1418
1419 /* Next table. */
1420 off -= pTable->cbData;
1421 if (!pCurSub)
1422 pCurSub = pAttr->pSubRecHead;
1423 else
1424 pCurSub = pCurSub->pNext;
1425 if (!pCurSub)
1426 {
1427 iExtent = UINT32_MAX;
1428 break;
1429 }
1430 pTable = &pCurSub->Extents;
1431 iExtent = 0;
1432 }
1433
1434 /*
1435 * The read loop.
1436 */
1437 while (iExtent != UINT32_MAX)
1438 {
1439 uint64_t cbMaxRead = pTable->paExtents[iExtent].cbExtent;
1440 Assert(off < cbMaxRead);
1441 cbMaxRead -= off;
1442 size_t const cbThisRead = cbMaxRead >= cbToRead ? cbToRead : (size_t)cbMaxRead;
1443 if (pTable->paExtents[iExtent].off == UINT64_MAX)
1444 RT_BZERO(pvBuf, cbThisRead);
1445 else
1446 {
1447 rc = RTVfsFileReadAt(pVol->hVfsBacking, pTable->paExtents[iExtent].off + off, pvBuf, cbThisRead, NULL);
1448 Log4(("NTFS: Volume read: @%#RX64 LB %#zx -> %Rrc\n", pTable->paExtents[iExtent].off + off, cbThisRead, rc));
1449 if (RT_FAILURE(rc))
1450 break;
1451 }
1452 pvBuf = (uint8_t *)pvBuf + cbThisRead;
1453 cbToRead -= cbThisRead;
1454 if (!cbToRead)
1455 break;
1456
1457 /*
1458 * Advance to the next extent.
1459 */
1460 iExtent++;
1461 if (iExtent >= pTable->cExtents)
1462 {
1463 pCurSub = pCurSub ? pCurSub->pNext : pAttr->pSubRecHead;
1464 if (!pCurSub)
1465 break;
1466 pTable = &pCurSub->Extents;
1467 iExtent = 0;
1468 }
1469 }
1470 }
1471 }
1472 }
1473 else
1474 {
1475 LogRel(("rtFsNtfsAttr_Read: Compressed files are not supported\n"));
1476 rc = VERR_NOT_SUPPORTED;
1477 }
1478
1479 /*
1480 * Anything else beyond the end of what's stored/initialized?
1481 */
1482 if ( cbToRead > 0
1483 && RT_SUCCESS(rc))
1484 {
1485 RT_BZERO(pvBuf, cbToRead);
1486 }
1487
1488 return rc;
1489}
1490
1491
1492/**
1493 *
1494 * @returns
1495 * @param pRecHdr .
1496 * @param cbRec .
1497 * @param fRelaxedUsa .
1498 * @param pErrInfo .
1499 *
1500 * @see https://msdn.microsoft.com/en-us/library/bb470212%28v=vs.85%29.aspx
1501 */
1502static int rtFsNtfsRec_DoMultiSectorFixups(PNTFSRECHDR pRecHdr, uint32_t cbRec, bool fRelaxedUsa, PRTERRINFO pErrInfo)
1503{
1504 /*
1505 * Do sanity checking.
1506 */
1507 uint16_t offUpdateSeqArray = RT_LE2H_U16(pRecHdr->offUpdateSeqArray);
1508 uint16_t cUpdateSeqEntries = RT_LE2H_U16(pRecHdr->cUpdateSeqEntries);
1509 if ( !(cbRec & (NTFS_MULTI_SECTOR_STRIDE - 1))
1510 && !(offUpdateSeqArray & 1) /* two byte aligned */
1511 && cUpdateSeqEntries == 1 + cbRec / NTFS_MULTI_SECTOR_STRIDE
1512 && offUpdateSeqArray + (uint32_t)cUpdateSeqEntries * 2U < NTFS_MULTI_SECTOR_STRIDE - 2U)
1513 {
1514 uint16_t const *pauUsa = (uint16_t const *)((uint8_t *)pRecHdr + offUpdateSeqArray);
1515
1516 /*
1517 * The first update seqence array entry is the value stored at
1518 * the fixup locations at the end of the blocks. We read this
1519 * and check each of the blocks.
1520 */
1521 uint16_t const uCheck = *pauUsa++;
1522 cUpdateSeqEntries--;
1523 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1524 {
1525 uint16_t const *puBlockCheck = (uint16_t const *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1526 if (*puBlockCheck == uCheck)
1527 { /* likely */ }
1528 else if (!fRelaxedUsa)
1529 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1530 "Multisector transfer error: block #%u ends with %#x instead of %#x (fixup: %#x)",
1531 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) );
1532 else
1533 {
1534 Log(("NTFS: Multisector transfer warning: block #%u ends with %#x instead of %#x (fixup: %#x)\n",
1535 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) ));
1536 return VINF_SUCCESS;
1537 }
1538 }
1539
1540 /*
1541 * Apply the fixups.
1542 * Note! We advanced pauUsa above, so it's now at the fixup values.
1543 */
1544 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1545 {
1546 uint16_t *puFixup = (uint16_t *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1547 *puFixup = pauUsa[iBlock];
1548 }
1549 return VINF_SUCCESS;
1550 }
1551 if (fRelaxedUsa)
1552 {
1553 Log(("NTFS: Ignoring bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x\n",
1554 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries ));
1555 return VINF_SUCCESS;
1556 }
1557 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1558 "Bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x",
1559 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries);
1560}
1561
1562
1563/**
1564 * Allocate and parse an MFT record, returning a core object structure.
1565 *
1566 * @returns IPRT status code.
1567 * @param pThis The NTFS volume instance.
1568 * @param idxMft The index of the MTF record.
1569 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1570 * checks doesn't work or not present.
1571 * @param ppCore Where to return the core object structure.
1572 * @param pErrInfo Where to return error details. Optional.
1573 */
1574static int rtFsNtfsVol_NewCoreForMftIdx(PRTFSNTFSVOL pThis, uint64_t idxMft, bool fRelaxedUsa,
1575 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1576{
1577 *ppCore = NULL;
1578 Assert(pThis->pMftData);
1579 Assert(RTAvlU64Get(&pThis->MftRoot, idxMft) == NULL);
1580
1581 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, idxMft);
1582 AssertReturn(pRec, VERR_NO_MEMORY);
1583
1584 uint64_t offRec = idxMft * pThis->cbMftRecord;
1585 int rc = rtFsNtfsAttr_Read(pThis->pMftData, offRec, pRec->pbRec, pThis->cbMftRecord);
1586 if (RT_SUCCESS(rc))
1587 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, fRelaxedUsa, pErrInfo);
1588 if (RT_SUCCESS(rc))
1589 {
1590#ifdef LOG_ENABLED
1591 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
1592#endif
1593 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
1594 if (RT_SUCCESS(rc))
1595 {
1596 rtFsNtfsMftRec_Release(pRec, pThis);
1597 *ppCore = pRec->pCore;
1598 return VINF_SUCCESS;
1599 }
1600 rtFsNtfsCore_Release(pRec->pCore);
1601 rtFsNtfsMftRec_Release(pRec, pThis);
1602 }
1603 return rc;
1604}
1605
1606
1607/**
1608 * Queries the core object struct for the given MFT record reference.
1609 *
1610 * Does caching.
1611 *
1612 * @returns IPRT status code.
1613 * @param pThis The NTFS volume instance.
1614 * @param pMftRef The MFT reference to get the corresponding core
1615 * for.
1616 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1617 * checks doesn't work or not present.
1618 * @param ppCore Where to return the referenced core object
1619 * structure.
1620 * @param pErrInfo Where to return error details. Optional.
1621 */
1622static int rtFsNtfsVol_QueryCoreForMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pMftRef , bool fRelaxedUsa,
1623 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1624{
1625 *ppCore = NULL;
1626 Assert(pThis->pMftData);
1627
1628 int rc;
1629 PRTFSNTFSMFTREC pMftRec = (PRTFSNTFSMFTREC)RTAvlU64Get(&pThis->MftRoot, NTFSMFTREF_GET_IDX(pMftRef));
1630 if (pMftRec)
1631 {
1632 /*
1633 * Cache hit. Check that the resure sequence number matches.
1634 * To be slightly paranoid, also check that it's a base MFT record and that it has been parsed already.
1635 */
1636 if (RT_LE2H_U16(pMftRec->pFileRec->uRecReuseSeqNo) == NTFSMFTREF_GET_SEQ(pMftRef))
1637 {
1638 if ( NTFSMFTREF_IS_ZERO(&pMftRec->pFileRec->BaseMftRec)
1639 && pMftRec->pCore)
1640 {
1641 rtFsNtfsCore_Retain(pMftRec->pCore);
1642 *ppCore = pMftRec->pCore;
1643 rc = VINF_SUCCESS;
1644 }
1645 else
1646 AssertLogRelMsgFailedStmt(("pCore=%p; BaseMftRec=%#RX64 sqn %#x\n", pMftRec->pCore,
1647 NTFSMFTREF_GET_IDX(&pMftRec->pFileRec->BaseMftRec),
1648 NTFSMFTREF_GET_SEQ(&pMftRec->pFileRec->BaseMftRec)),
1649 rc = VERR_INTERNAL_ERROR_3 );
1650 }
1651 else
1652 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1653 "Stale parent directory MFT reference: %#RX64 sqn %#x - current sqn %#x",
1654 NTFSMFTREF_GET_IDX(pMftRef), NTFSMFTREF_GET_SEQ(pMftRef),
1655 RT_LE2H_U16(pMftRec->pFileRec->uRecReuseSeqNo) );
1656 }
1657 else
1658 {
1659 /*
1660 * Load new and check that the reuse sequence number match.
1661 */
1662 rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFSMFTREF_GET_IDX(pMftRef), fRelaxedUsa, ppCore, pErrInfo);
1663 if (RT_SUCCESS(rc))
1664 {
1665 PRTFSNTFSCORE pCore = *ppCore;
1666 if (RT_LE2H_U16(pCore->pMftRec->pFileRec->uRecReuseSeqNo) == NTFSMFTREF_GET_SEQ(pMftRef))
1667 rc = VINF_SUCCESS;
1668 else
1669 {
1670 rtFsNtfsCore_Release(pCore);
1671 *ppCore = NULL;
1672 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1673 "Stale parent directory MFT reference: %#RX64 sqn %#x - current sqn %#x",
1674 NTFSMFTREF_GET_IDX(pMftRef), NTFSMFTREF_GET_SEQ(pMftRef),
1675 RT_LE2H_U16(pCore->pMftRec->pFileRec->uRecReuseSeqNo) );
1676 }
1677 }
1678 }
1679 return rc;
1680}
1681
1682
1683/**
1684 * Destroys a core structure.
1685 *
1686 * @returns 0
1687 * @param pThis The core structure.
1688 */
1689static uint32_t rtFsNtfsCore_Destroy(PRTFSNTFSCORE pThis)
1690{
1691 /*
1692 * Free attributes.
1693 */
1694 PRTFSNTFSATTR pCurAttr;
1695 PRTFSNTFSATTR pNextAttr;
1696 RTListForEachSafe(&pThis->AttribHead, pCurAttr, pNextAttr, RTFSNTFSATTR, ListEntry)
1697 {
1698 PRTFSNTFSATTRSUBREC pSub = pCurAttr->pSubRecHead;
1699 while (pSub)
1700 {
1701 pCurAttr->pSubRecHead = pSub->pNext;
1702 RTMemFree(pSub->Extents.paExtents);
1703 pSub->Extents.paExtents = NULL;
1704 pSub->pAttrHdr = NULL;
1705 pSub->pNext = NULL;
1706 RTMemFree(pSub);
1707
1708 pSub = pCurAttr->pSubRecHead;
1709 }
1710
1711 pCurAttr->pCore = NULL;
1712 pCurAttr->pAttrHdr = NULL;
1713 RTMemFree(pCurAttr->Extents.paExtents);
1714 pCurAttr->Extents.paExtents = NULL;
1715 }
1716
1717 /*
1718 * Release the MFT chain.
1719 */
1720 PRTFSNTFSMFTREC pMftRec = pThis->pMftRec;
1721 while (pMftRec)
1722 {
1723 pThis->pMftRec = pMftRec->pNext;
1724 Assert(pMftRec->pCore == pThis);
1725 pMftRec->pNext = NULL;
1726 pMftRec->pCore = NULL;
1727 rtFsNtfsMftRec_Release(pMftRec, pThis->pVol);
1728
1729 pMftRec = pThis->pMftRec;
1730 }
1731
1732 RTMemFree(pThis);
1733
1734 return 0;
1735}
1736
1737
1738/**
1739 * Releases a refernece to a core structure, maybe destroying it.
1740 *
1741 * @returns New reference count.
1742 * @param pThis The core structure.
1743 */
1744static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis)
1745{
1746 if (pThis)
1747 {
1748 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1749 Assert(cRefs < 128);
1750 if (cRefs != 0)
1751 return cRefs;
1752 return rtFsNtfsCore_Destroy(pThis);
1753 }
1754 return 0;
1755}
1756
1757
1758/**
1759 * Retains a refernece to a core structure.
1760 *
1761 * @returns New reference count.
1762 * @param pThis The core structure.
1763 */
1764static uint32_t rtFsNtfsCore_Retain(PRTFSNTFSCORE pThis)
1765{
1766 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1767 Assert(cRefs < 128);
1768 return cRefs;
1769}
1770
1771
1772/**
1773 * Finds an unnamed attribute.
1774 *
1775 * @returns Pointer to the attribute structure if found, NULL if not.
1776 * @param pThis The core object structure to search.
1777 * @param uAttrType The attribute type to find.
1778 */
1779static PRTFSNTFSATTR rtFsNtfsCore_FindUnnamedAttribute(PRTFSNTFSCORE pThis, uint32_t uAttrType)
1780{
1781 PRTFSNTFSATTR pCurAttr;
1782 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
1783 {
1784 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
1785 if ( pAttrHdr->uAttrType == uAttrType
1786 && pAttrHdr->cwcName == 0)
1787 return pCurAttr;
1788 }
1789 return NULL;
1790}
1791
1792
1793/**
1794 * Finds a named attribute, case insensitive ASCII variant.
1795 *
1796 * @returns Pointer to the attribute structure if found, NULL if not.
1797 * @param pThis The core object structure to search.
1798 * @param uAttrType The attribute type to find.
1799 * @param pszAttrib The attribute name, predefined 7-bit ASCII name.
1800 * @param cchAttrib The length of the attribute.
1801 */
1802static PRTFSNTFSATTR rtFsNtfsCore_FindNamedAttributeAscii(PRTFSNTFSCORE pThis, uint32_t uAttrType,
1803 const char *pszAttrib, size_t cchAttrib)
1804{
1805 Assert(cchAttrib > 0);
1806 PRTFSNTFSATTR pCurAttr;
1807 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
1808 {
1809 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
1810 if ( pAttrHdr->uAttrType == uAttrType
1811 && pAttrHdr->cwcName == cchAttrib
1812 && RTUtf16NICmpAscii(NTFSATTRIBHDR_GET_NAME(pAttrHdr), pszAttrib, cchAttrib) == 0)
1813 return pCurAttr;
1814 }
1815 return NULL;
1816}
1817
1818
1819/**
1820 * Worker for various QueryInfo methods.
1821 *
1822 * @returns IPRT status code.
1823 * @param pThis The core object structure to return info for.
1824 * @param pAttr The attribute that's being presented. Take the
1825 * allocation and timestamp info from it, if
1826 * non-resident.
1827 * @param pObjInfo Where to return object info.
1828 * @param enmAddAttr What additional info to return.
1829 */
1830static int rtFsNtfsCore_QueryInfo(PRTFSNTFSCORE pThis, PRTFSNTFSATTR pAttr, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1831{
1832 /*
1833 * Wipe the structure and fill in common dummy value.
1834 */
1835 RT_ZERO(*pObjInfo);
1836 switch (enmAddAttr)
1837 {
1838 case RTFSOBJATTRADD_UNIX:
1839 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
1840 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
1841 pObjInfo->Attr.u.Unix.cHardlinks = 1;
1842 //pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
1843 pObjInfo->Attr.u.Unix.INodeId = pThis->pMftRec->TreeNode.Key;
1844 //pObjInfo->Attr.u.Unix.fFlags = 0;
1845 //pObjInfo->Attr.u.Unix.GenerationId = 0;
1846 //pObjInfo->Attr.u.Unix.Device = 0;
1847 break;
1848
1849 case RTFSOBJATTRADD_UNIX_OWNER:
1850 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
1851 break;
1852
1853 case RTFSOBJATTRADD_UNIX_GROUP:
1854 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
1855 break;
1856
1857 default:
1858 break;
1859 }
1860
1861 /*
1862 * Look for the standard information attribute and use that as basis.
1863 */
1864 uint32_t fFileAttrs;
1865 PRTFSNTFSATTR pStdInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pThis, NTFS_AT_STANDARD_INFORMATION);
1866 if ( pStdInfoAttr
1867 && pStdInfoAttr->cbResident >= sizeof(NTFSATSTDINFO) )
1868 {
1869 Assert(!pStdInfoAttr->pAttrHdr->fNonResident);
1870 PCNTFSATSTDINFO pStdInfo = (PCNTFSATSTDINFO)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pStdInfoAttr->pAttrHdr);
1871 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, RT_LE2H_U64(pStdInfo->iCreationTime));
1872 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, RT_LE2H_U64(pStdInfo->iLastDataModTime));
1873 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, RT_LE2H_U64(pStdInfo->iLastMftModTime));
1874 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, RT_LE2H_U64(pStdInfo->iLastAccessTime));
1875 if (enmAddAttr == RTFSOBJATTRADD_UNIX)
1876 {
1877 pObjInfo->Attr.u.Unix.uid = pStdInfo->idOwner;
1878 pObjInfo->Attr.u.Unix.GenerationId = pStdInfo->uFileVersion;
1879 }
1880 else if (enmAddAttr == RTFSOBJATTRADD_UNIX_OWNER)
1881 pObjInfo->Attr.u.UnixOwner.uid = pStdInfo->idOwner;
1882 fFileAttrs = pStdInfo->fFileAttribs;
1883 }
1884 else
1885 {
1886 /** @todo check out the filename record? */
1887 switch (pAttr->pAttrHdr->uAttrType)
1888 {
1889 default:
1890 AssertFailed();
1891 case NTFS_AT_DATA:
1892 fFileAttrs = NTFS_FA_NORMAL;
1893 break;
1894
1895 case NTFS_AT_INDEX_ROOT:
1896 case NTFS_AT_INDEX_ALLOCATION:
1897 fFileAttrs = NTFS_FA_DIRECTORY;
1898 break;
1899 }
1900 }
1901
1902 /*
1903 * Take the allocation info from the destilled attribute data.
1904 */
1905 pObjInfo->cbObject = pAttr->cbValue;
1906 pObjInfo->cbAllocated = pAttr->Extents.cbData;
1907 if ( pAttr->pAttrHdr->fNonResident
1908 && (int64_t)pObjInfo->cbAllocated < (int64_t)RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated))
1909 pObjInfo->cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated);
1910
1911
1912 /*
1913 * See if we can find a filename record before we try convert the file attributes to mode.
1914 */
1915 PCNTFSATFILENAME pFilename = NULL;
1916 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pThis, NTFS_AT_FILENAME);
1917 if ( pFilenameAttr
1918 && pFilenameAttr->cbResident >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename) )
1919 {
1920 Assert(!pFilenameAttr->pAttrHdr->fNonResident);
1921 pFilename = (PCNTFSATFILENAME)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pFilenameAttr->pAttrHdr);
1922 if (pStdInfoAttr)
1923 fFileAttrs |= pFilename->fFileAttribs;
1924 else
1925 fFileAttrs = pFilename->fFileAttribs;
1926 }
1927
1928 /*
1929 * This attribute conversion code is a slightly modified version of rtFsModeFromDos.
1930 */
1931 pObjInfo->Attr.fMode = (fFileAttrs << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT;
1932 if (fFileAttrs & NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT)
1933 pObjInfo->Attr.fMode |= RTFS_DOS_DIRECTORY;
1934
1935 /* everything is readable. */
1936 pObjInfo->Attr.fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
1937 if (pObjInfo->Attr.fMode & RTFS_DOS_DIRECTORY)
1938 /* directories are executable. */
1939 pObjInfo->Attr.fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
1940 else
1941 {
1942 pObjInfo->Attr.fMode |= RTFS_TYPE_FILE;
1943 if ( pFilename
1944 && pFilename->cwcFilename >= 4
1945 && RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) <= pFilenameAttr->cbResident)
1946 {
1947 PCRTUTF16 pwcExt = &pFilename->wszFilename[pFilename->cwcFilename - 4];
1948 if ( *pwcExt++ == '.')
1949 {
1950 /* check for executable extension. */
1951 if ( (unsigned)pwcExt[0] < 0x7fU
1952 && (unsigned)pwcExt[1] < 0x7fU
1953 && (unsigned)pwcExt[2] < 0x7fU)
1954 {
1955 char szExt[4];
1956 szExt[0] = RT_C_TO_LOWER(pwcExt[0]);
1957 szExt[1] = RT_C_TO_LOWER(pwcExt[1]);
1958 szExt[2] = RT_C_TO_LOWER(pwcExt[2]);
1959 szExt[3] = '\0';
1960 if ( !memcmp(szExt, "exe", 4)
1961 || !memcmp(szExt, "bat", 4)
1962 || !memcmp(szExt, "com", 4)
1963 || !memcmp(szExt, "cmd", 4)
1964 || !memcmp(szExt, "btm", 4)
1965 )
1966 pObjInfo->Attr.fMode |= RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
1967 }
1968 }
1969 }
1970 }
1971
1972 /* Is it really a symbolic link? */
1973 if ( (pObjInfo->Attr.fMode & RTFS_DOS_NT_REPARSE_POINT)
1974 && pFilename
1975 && pFilename->u.uReparseTag == RTFSMODE_SYMLINK_REPARSE_TAG)
1976 pObjInfo->Attr.fMode = (pObjInfo->Attr.fMode & ~RTFS_TYPE_MASK) | RTFS_TYPE_SYMLINK;
1977
1978 /* writable? */
1979 if (!(pObjInfo->Attr.fMode & RTFS_DOS_READONLY))
1980 pObjInfo->Attr.fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
1981
1982 return VINF_SUCCESS;
1983}
1984
1985
1986/*
1987 *
1988 * NTFS directory code.
1989 * NTFS directory code.
1990 * NTFS directory code.
1991 *
1992 */
1993
1994#ifdef LOG_ENABLED
1995
1996/**
1997 * Logs an index header and all the entries.
1998 *
1999 * @param pIdxHdr The index header.
2000 * @param cbIndex The number of valid bytes starting with the header.
2001 * @param offIndex The offset of the index header into the parent
2002 * structure.
2003 * @param pszPrefix The log prefix.
2004 * @param uIdxType The index type.
2005 */
2006static void rtFsNtfsVol_LogIndexHdrAndEntries(PCNTFSINDEXHDR pIdxHdr, uint32_t cbIndex, uint32_t offIndex,
2007 const char *pszPrefix, uint32_t uIdxType)
2008{
2009 if (!LogIs2Enabled())
2010 return;
2011
2012 /*
2013 * Do the header.
2014 */
2015 if (cbIndex <= sizeof(*pIdxHdr))
2016 {
2017 Log2(("NTFS: %s: Error! Not enough space for the index header! cbIndex=%#x, index head needs %#x\n",
2018 pszPrefix, cbIndex, sizeof(*pIdxHdr)));
2019 return;
2020 }
2021
2022 Log2(("NTFS: %s: offFirstEntry %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->offFirstEntry),
2023 RT_LE2H_U32(pIdxHdr->offFirstEntry) >= cbIndex ? " !out-of-bounds!" : ""));
2024 Log2(("NTFS: %s: cbUsed %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbUsed),
2025 RT_LE2H_U32(pIdxHdr->cbUsed) > cbIndex ? " !out-of-bounds!" : ""));
2026 Log2(("NTFS: %s: cbAllocated %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbAllocated),
2027 RT_LE2H_U32(pIdxHdr->cbAllocated) > cbIndex ? " !out-of-bounds!" : ""));
2028 Log2(("NTFS: %s: fFlags %#x (%s%s)\n", pszPrefix, pIdxHdr->fFlags,
2029 pIdxHdr->fFlags & NTFSINDEXHDR_F_INTERNAL ? "internal" : "leaf",
2030 pIdxHdr->fFlags & ~NTFSINDEXHDR_F_INTERNAL ? " !!unknown-flags!!" : ""));
2031 if (pIdxHdr->abReserved[0]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[0]));
2032 if (pIdxHdr->abReserved[1]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[1]));
2033 if (pIdxHdr->abReserved[2]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[2]));
2034
2035 /*
2036 * The entries.
2037 */
2038 bool fSeenEnd = false;
2039 uint32_t iEntry = 0;
2040 uint32_t offCurEntry = RT_LE2H_U32(pIdxHdr->offFirstEntry);
2041 while (offCurEntry < cbIndex)
2042 {
2043 if (offCurEntry + sizeof(NTFSIDXENTRYHDR) > cbIndex)
2044 {
2045 Log2(("NTFS: Entry[%#04x]: Out of bounds: %#x LB %#x, max %#x\n",
2046 iEntry, offCurEntry, sizeof(NTFSIDXENTRYHDR), cbIndex));
2047 break;
2048 }
2049 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIdxHdr + offCurEntry);
2050 Log2(("NTFS: [%#04x]: @%#05x/@%#05x cbEntry=%#x cbKey=%#x fFlags=%#x (%s%s%s)\n",
2051 iEntry, offCurEntry, offCurEntry + offIndex, RT_LE2H_U16(pEntryHdr->cbEntry), RT_LE2H_U16(pEntryHdr->cbKey),
2052 RT_LE2H_U16(pEntryHdr->fFlags),
2053 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? "internal" : "leaf",
2054 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END ? " end" : "",
2055 pEntryHdr->fFlags & ~(NTFSIDXENTRYHDR_F_INTERNAL | NTFSIDXENTRYHDR_F_END) ? " !unknown!" : ""));
2056 if (uIdxType == NTFSATINDEXROOT_TYPE_DIR)
2057 Log2(("NTFS: FileMftRec %#RX64 sqn %#x\n",
2058 NTFSMFTREF_GET_IDX(&pEntryHdr->u.FileMftRec), NTFSMFTREF_GET_SEQ(&pEntryHdr->u.FileMftRec) ));
2059 else
2060 Log2(("NTFS: offData=%#x cbData=%#x uReserved=%#x\n",
2061 RT_LE2H_U16(pEntryHdr->u.View.offData), RT_LE2H_U16(pEntryHdr->u.View.cbData),
2062 RT_LE2H_U32(pEntryHdr->u.View.uReserved) ));
2063 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2064 Log2(("NTFS: Subnode=%#RX64\n", RT_LE2H_U64(NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr)) ));
2065
2066 if ( RT_LE2H_U16(pEntryHdr->cbKey) >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename)
2067 && uIdxType == NTFSATINDEXROOT_TYPE_DIR)
2068 {
2069 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2070 RTTIMESPEC Spec;
2071 char sz[80];
2072 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iCreationTime),
2073 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iCreationTime)), sz, sizeof(sz)) ));
2074 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastDataModTime),
2075 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastDataModTime)), sz, sizeof(sz)) ));
2076 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastMftModTime),
2077 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastMftModTime)), sz, sizeof(sz)) ));
2078 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastAccessTime),
2079 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastAccessTime)), sz, sizeof(sz)) ));
2080 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
2081 RT_LE2H_U64(pFilename->cbAllocated), RT_LE2H_U64(pFilename->cbAllocated)));
2082 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
2083 RT_LE2H_U64(pFilename->cbData), RT_LE2H_U64(pFilename->cbData)));
2084 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pFilename->fFileAttribs) ));
2085 if (RT_LE2H_U32(pFilename->fFileAttribs) & NTFS_FA_REPARSE_POINT)
2086 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pFilename->u.uReparseTag) ));
2087 else
2088 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pFilename->u.cbPackedEas) ));
2089 Log2(("NTFS: cwcFilename %#x\n", pFilename->cwcFilename));
2090 Log2(("NTFS: fFilenameType %#x\n", pFilename->fFilenameType));
2091 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) <= RT_LE2H_U16(pEntryHdr->cbKey))
2092 Log2(("NTFS: wszFilename '%.*ls'\n", pFilename->cwcFilename, pFilename->wszFilename ));
2093 else
2094 Log2(("NTFS: Error! Truncated filename!!\n"));
2095 }
2096
2097
2098 /* next */
2099 iEntry++;
2100 offCurEntry += RT_LE2H_U16(pEntryHdr->cbEntry);
2101 fSeenEnd = RT_BOOL(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END);
2102 if (fSeenEnd || RT_LE2H_U16(pEntryHdr->cbEntry) < sizeof(*pEntryHdr))
2103 break;
2104 }
2105 if (!fSeenEnd)
2106 Log2(("NTFS: %s: Warning! Missing NTFSIDXENTRYHDR_F_END node!\n", pszPrefix));
2107}
2108
2109# if 0 /* unused */
2110static void rtFsNtfsVol_LogIndexNode(PCNTFSATINDEXALLOC pIdxNode, uint32_t cbIdxNode, uint32_t uType)
2111{
2112 if (!LogIs2Enabled())
2113 return;
2114 if (cbIdxNode < sizeof(*pIdxNode))
2115 Log2(("NTFS: Index Node: Error! Too small! cbIdxNode=%#x, index node needs %#x\n", cbIdxNode, sizeof(*pIdxNode)));
2116 else
2117 {
2118 Log2(("NTFS: Index Node: uMagic %#x\n", RT_LE2H_U32(pIdxNode->RecHdr.uMagic)));
2119 Log2(("NTFS: Index Node: UpdateSeqArray %#x L %#x\n",
2120 RT_LE2H_U16(pIdxNode->RecHdr.offUpdateSeqArray), RT_LE2H_U16(pIdxNode->RecHdr.cUpdateSeqEntries) ));
2121 Log2(("NTFS: Index Node: uLsn %#RX64\n", RT_LE2H_U64(pIdxNode->uLsn) ));
2122 Log2(("NTFS: Index Node: iSelfAddress %#RX64\n", RT_LE2H_U64(pIdxNode->iSelfAddress) ));
2123 if (pIdxNode->RecHdr.uMagic == NTFSREC_MAGIC_INDEX_ALLOC)
2124 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxNode->Hdr, cbIdxNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2125 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "Index Node Hdr", uType);
2126 else
2127 Log2(("NTFS: Index Node: !Error! Invalid magic!\n"));
2128 }
2129}
2130# endif
2131
2132/**
2133 * Logs a index root structure and what follows (index header + entries).
2134 *
2135 * @param pIdxRoot The index root.
2136 * @param cbIdxRoot Number of valid bytes starting with @a pIdxRoot.
2137 */
2138static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot)
2139{
2140 if (!LogIs2Enabled())
2141 return;
2142 if (cbIdxRoot < sizeof(*pIdxRoot))
2143 Log2(("NTFS: Index Root: Error! Too small! cbIndex=%#x, index head needs %#x\n", cbIdxRoot, sizeof(*pIdxRoot)));
2144 else
2145 {
2146 Log2(("NTFS: Index Root: cbIdxRoot %#x\n", cbIdxRoot));
2147 Log2(("NTFS: Index Root: uType %#x %s\n", RT_LE2H_U32(pIdxRoot->uType),
2148 pIdxRoot->uType == NTFSATINDEXROOT_TYPE_VIEW ? "view"
2149 : pIdxRoot->uType == NTFSATINDEXROOT_TYPE_DIR ? "directory" : "!unknown!"));
2150 Log2(("NTFS: Index Root: uCollationRules %#x %s\n", RT_LE2H_U32(pIdxRoot->uCollationRules),
2151 pIdxRoot->uCollationRules == NTFS_COLLATION_BINARY ? "binary"
2152 : pIdxRoot->uCollationRules == NTFS_COLLATION_FILENAME ? "filename"
2153 : pIdxRoot->uCollationRules == NTFS_COLLATION_UNICODE_STRING ? "unicode-string"
2154 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32 ? "uint32"
2155 : pIdxRoot->uCollationRules == NTFS_COLLATION_SID ? "sid"
2156 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_PAIR ? "uint32-pair"
2157 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_SEQ ? "uint32-sequence" : "!unknown!"));
2158 Log2(("NTFS: Index Root: cbIndexNode %#x\n", RT_LE2H_U32(pIdxRoot->cbIndexNode) ));
2159 Log2(("NTFS: Index Root: cAddressesPerIndexNode %#x => cbNodeAddressingUnit=%#x\n",
2160 pIdxRoot->cAddressesPerIndexNode, RT_LE2H_U32(pIdxRoot->cbIndexNode) / RT_MAX(1, pIdxRoot->cAddressesPerIndexNode) ));
2161 if (pIdxRoot->abReserved[0]) Log2(("NTFS: Index Root: abReserved[0] %#x\n", pIdxRoot->abReserved[0]));
2162 if (pIdxRoot->abReserved[1]) Log2(("NTFS: Index Root: abReserved[1] %#x\n", pIdxRoot->abReserved[1]));
2163 if (pIdxRoot->abReserved[2]) Log2(("NTFS: Index Root: abReserved[2] %#x\n", pIdxRoot->abReserved[2]));
2164
2165 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxRoot->Hdr, cbIdxRoot - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr),
2166 RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), "Index Root Hdr", pIdxRoot->uType);
2167 }
2168}
2169
2170#endif /* LOG_ENABLED */
2171
2172
2173/**
2174 * Validates an index header.
2175 *
2176 * @returns IPRT status code.
2177 * @param pRootInfo Pointer to the index root info.
2178 * @param pNodeInfo Pointer to the node info structure to load.
2179 * @param pIndexHdr Pointer to the index header.
2180 * @param cbIndex Size of the index.
2181 * @param pErrInfo Where to return extra error info.
2182 * @param pszWhat Error prefix.
2183 */
2184static int rtFsNtfsVol_LoadIndexNodeInfo(PCRTFSNTFSIDXROOTINFO pRootInfo, PRTFSNTFSIDXNODEINFO pNodeInfo, PCNTFSINDEXHDR pIndexHdr,
2185 uint32_t cbIndex, PRTERRINFO pErrInfo, const char *pszWhat)
2186{
2187 uint32_t const cbMinIndex = sizeof(*pIndexHdr) + sizeof(NTFSIDXENTRYHDR);
2188 if (cbIndex < cbMinIndex)
2189 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2190 "%s: Not enough room for the index header and one entry header! cbIndex=%#x (cbMinIndex=%#x)",
2191 pszWhat, cbIndex, cbMinIndex);
2192 uint32_t const cbAllocated = RT_LE2H_U32(pIndexHdr->cbAllocated);
2193 if ( cbAllocated > cbIndex
2194 || cbAllocated < cbMinIndex
2195 || (cbAllocated & 7) )
2196 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2197 "%s: Bogus index allocation size: %#x (min %#x, max %#x, 8 byte aligned)",
2198 pszWhat, cbAllocated, cbMinIndex, cbIndex);
2199 uint32_t const cbUsed = RT_LE2H_U32(pIndexHdr->cbUsed);
2200 if ( cbUsed > cbAllocated
2201 || cbUsed < cbMinIndex
2202 || (cbUsed & 7) )
2203 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2204 "%s: Bogus index used size: %#x (min %#x, max %#x, 8 byte aligned)",
2205 pszWhat, cbUsed, cbMinIndex, cbAllocated);
2206 uint32_t const offFirstEntry = RT_LE2H_U32(pIndexHdr->offFirstEntry);
2207 if ( offFirstEntry < sizeof(*pIndexHdr)
2208 || offFirstEntry >= cbUsed - sizeof(NTFSIDXENTRYHDR)
2209 || (offFirstEntry & 7) )
2210 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2211 "%s: Bogus first entry offset: %#x (min %#x, max %#x, 8 byte aligned)",
2212 pszWhat, offFirstEntry, sizeof(*pIndexHdr), cbUsed - sizeof(NTFSIDXENTRYHDR));
2213
2214 /*
2215 * The index entries.
2216 */
2217 uint32_t const uType = pRootInfo->pRoot->uType;
2218 uint32_t offEntry = offFirstEntry;
2219 uint32_t iEntry = 0;
2220 for (;;)
2221 {
2222 if (offEntry + sizeof(NTFSIDXENTRYHDR) > cbUsed)
2223 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2224 "%s: Entry #%u is out of bound: offset %#x (cbUsed=%#x)",
2225 pszWhat, iEntry, offEntry, cbUsed);
2226 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIndexHdr + offEntry);
2227 uint16_t const cbEntry = RT_LE2H_U16(pEntryHdr->cbEntry);
2228 uint32_t const cbSubnodeAddr = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? sizeof(int64_t) : 0);
2229 uint32_t const cbMinEntry = sizeof(*pEntryHdr) + cbSubnodeAddr;
2230 if ( cbEntry < cbMinEntry
2231 || offEntry + cbEntry > cbUsed
2232 || (cbEntry & 7) )
2233 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2234 "%s: Entry #%u has a bogus size: %#x (min %#x, max %#x, 8 byte aligned)",
2235 pszWhat, iEntry, cbEntry, cbMinEntry, cbUsed - offEntry);
2236
2237 uint32_t const cbMaxKey = cbEntry - sizeof(*pEntryHdr) - cbSubnodeAddr;
2238 uint32_t const cbMinKey = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END) ? 0
2239 : uType == NTFSATINDEXROOT_TYPE_DIR ? RT_UOFFSETOF(NTFSATFILENAME, wszFilename) : 0;
2240 uint16_t const cbKey = RT_LE2H_U16(pEntryHdr->cbKey);
2241 if ( cbKey < cbMinKey
2242 || cbKey > cbMaxKey)
2243 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2244 "%s: Entry #%u has a bogus key size: %#x (min %#x, max %#x)",
2245 pszWhat, iEntry, cbKey, cbMinKey, cbMaxKey);
2246 if ( !(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
2247 && uType == NTFSATINDEXROOT_TYPE_DIR)
2248 {
2249 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2250 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) > cbKey)
2251 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2252 "%s: Entry #%u filename is out of bounds: cwcFilename=%#x -> %#x key, max %#x",
2253 pszWhat, iEntry, pFilename->cwcFilename,
2254 RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]), cbKey);
2255 }
2256
2257 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2258 {
2259 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr);
2260 if ( (uint64_t)iSubnode >= pRootInfo->uEndNodeAddresses
2261 || (iSubnode & pRootInfo->fNodeAddressMisalign) )
2262 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2263 "%s: Entry #%u has bogus subnode address: %#RX64 (max %#RX64, misalign %#x)",
2264 pszWhat, iEntry, iSubnode, pRootInfo->uEndNodeAddresses,
2265 pRootInfo->fNodeAddressMisalign);
2266 }
2267
2268 /* Advance. */
2269 offEntry += cbEntry;
2270 iEntry++;
2271 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
2272 break;
2273 }
2274
2275 /*
2276 * Popuplate the node info structure.
2277 */
2278 pNodeInfo->pIndexHdr = pIndexHdr;
2279 pNodeInfo->fInternal = RT_BOOL(pIndexHdr->fFlags & NTFSINDEXHDR_F_INTERNAL);
2280 if (pNodeInfo != &pRootInfo->NodeInfo)
2281 pNodeInfo->pVol = pRootInfo->NodeInfo.pVol;
2282 pNodeInfo->cEntries = iEntry;
2283 pNodeInfo->papEntries = (PCNTFSIDXENTRYHDR *)RTMemAlloc(iEntry * sizeof(pNodeInfo->papEntries[0]));
2284 if (pNodeInfo->papEntries)
2285 {
2286 PCNTFSIDXENTRYHDR pEntryHdr = NTFSINDEXHDR_GET_FIRST_ENTRY(pIndexHdr);
2287 for (iEntry = 0; iEntry < pNodeInfo->cEntries; iEntry++)
2288 {
2289 pNodeInfo->papEntries[iEntry] = pEntryHdr;
2290 pEntryHdr = NTFSIDXENTRYHDR_GET_NEXT(pEntryHdr);
2291 }
2292 return VINF_SUCCESS;
2293 }
2294 return VERR_NO_MEMORY;
2295}
2296
2297
2298/**
2299 * Creates a shared directory structure given a MFT core.
2300 *
2301 * @returns IPRT status code.
2302 * @param pThis The NTFS volume instance.
2303 * @param pCore The MFT core structure that's allegedly a directory.
2304 * (No reference consumed of course.)
2305 * @param ppSharedDir Where to return the pointer to the new shared directory
2306 * structure on success. (Referenced.)
2307 * @param pErrInfo Where to return additions error info. Optional.
2308 * @param pszWhat Context prefix for error reporting and logging.
2309 */
2310static int rtFsNtfsVol_NewSharedDirFromCore(PRTFSNTFSVOL pThis, PRTFSNTFSCORE pCore, PRTFSNTFSDIRSHRD *ppSharedDir,
2311 PRTERRINFO pErrInfo, const char *pszWhat)
2312{
2313 *ppSharedDir = NULL;
2314
2315 /*
2316 * Look for the index root and validate it.
2317 */
2318 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
2319 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2320 if (!pRootAttr)
2321 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: Found no INDEX_ROOT attribute named $I30", pszWhat);
2322 if (pRootAttr->pAttrHdr->fNonResident)
2323 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is is not resident", pszWhat);
2324 if (pRootAttr->cbResident < sizeof(NTFSATINDEXROOT))
2325 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is too small: %#x, min %#x ",
2326 pszWhat, pRootAttr->cbResident, sizeof(pRootAttr->cbResident));
2327
2328 PCNTFSATINDEXROOT pIdxRoot = (PCNTFSATINDEXROOT)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pRootAttr->pAttrHdr);
2329#ifdef LOG_ENABLED
2330 rtFsNtfsVol_LogIndexRoot(pIdxRoot, pRootAttr->cbResident);
2331#endif
2332 if (pIdxRoot->uType != NTFSATINDEXROOT_TYPE_DIR)
2333 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2334 "%s: Wrong INDEX_ROOT type for a directory: %#x, expected %#x",
2335 pszWhat, RT_LE2H_U32(pIdxRoot->uType), RT_LE2H_U32_C(NTFSATINDEXROOT_TYPE_DIR));
2336 if (pIdxRoot->uCollationRules != NTFS_COLLATION_FILENAME)
2337 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2338 "%s: Wrong collation rules for a directory: %#x, expected %#x",
2339 pszWhat, RT_LE2H_U32(pIdxRoot->uCollationRules), RT_LE2H_U32_C(NTFS_COLLATION_FILENAME));
2340 uint32_t cbIndexNode = RT_LE2H_U32(pIdxRoot->cbIndexNode);
2341 if (cbIndexNode < 512 || cbIndexNode > _64K || !RT_IS_POWER_OF_TWO(cbIndexNode))
2342 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2343 "%s: Bogus index node size: %#x (expected power of two between 512 and 64KB)",
2344 pszWhat, cbIndexNode);
2345 unsigned const cNodeAddressShift = cbIndexNode >= pThis->cbCluster ? pThis->cClusterShift : 9;
2346 if (((uint32_t)pIdxRoot->cAddressesPerIndexNode << cNodeAddressShift) != cbIndexNode)
2347 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2348 "%s: Bogus addresses per index node value: %#x (cbIndexNode=%#x cNodeAddressShift=%#x)",
2349 pszWhat, pIdxRoot->cAddressesPerIndexNode, cbIndexNode, cNodeAddressShift);
2350 AssertReturn(pRootAttr->uObj.pSharedDir == NULL, VERR_INTERNAL_ERROR_3);
2351
2352 /*
2353 * Check for the node data stream and related allocation bitmap.
2354 */
2355 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
2356 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2357 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
2358 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2359 if (pIndexAlloc && !pIndexBitmap)
2360 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2361 "%s: INDEX_ALLOCATION attribute without BITMAP", pszWhat);
2362 if (!pIndexAlloc && pIndexBitmap)
2363 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2364 "%s: BITMAP attribute without INDEX_ALLOCATION", pszWhat);
2365 uint64_t uNodeAddressEnd = 0;
2366 if (pIndexAlloc)
2367 {
2368 if (!pIndexAlloc->pAttrHdr->fNonResident)
2369 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ALLOCATION is resident", pszWhat);
2370 if (pIndexAlloc->cbValue & (cbIndexNode - 1))
2371 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2372 "%s: INDEX_ALLOCATION size isn't aligned on node boundrary: %#RX64, cbIndexNode=%#x",
2373 pszWhat, pIndexAlloc->cbValue, cbIndexNode);
2374 uint64_t const cNodes = pIndexAlloc->cbValue / cbIndexNode;
2375 if (pIndexBitmap->cbValue != (RT_ALIGN_64(cNodes, 64) >> 3))
2376 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2377 "%s: BITMAP size does not match INDEX_ALLOCATION: %#RX64, expected %#RX64 (cbIndexNode=%#x, cNodes=%#RX64)",
2378 pszWhat, pIndexBitmap->cbValue, RT_ALIGN_64(cNodes, 64) >> 3, cbIndexNode, cNodes);
2379 uNodeAddressEnd = cNodes * pIdxRoot->cAddressesPerIndexNode;
2380 }
2381
2382 /*
2383 * Create a directory instance.
2384 */
2385 PRTFSNTFSDIRSHRD pNewDir = (PRTFSNTFSDIRSHRD)RTMemAllocZ(sizeof(*pNewDir));
2386 if (!pNewDir)
2387 return VERR_NO_MEMORY;
2388
2389 pNewDir->cRefs = 1;
2390 rtFsNtfsCore_Retain(pCore);
2391 pNewDir->RootInfo.pRootAttr = pRootAttr;
2392 pNewDir->RootInfo.pRoot = pIdxRoot;
2393 pNewDir->RootInfo.pAlloc = pIndexAlloc;
2394 pNewDir->RootInfo.uEndNodeAddresses = uNodeAddressEnd;
2395 pNewDir->RootInfo.cNodeAddressByteShift = cNodeAddressShift;
2396 pNewDir->RootInfo.fNodeAddressMisalign = pIdxRoot->cAddressesPerIndexNode - 1;
2397 pNewDir->RootInfo.NodeInfo.pVol = pThis;
2398
2399 /*
2400 * Finally validate the index header and entries.
2401 */
2402 int rc = rtFsNtfsVol_LoadIndexNodeInfo(&pNewDir->RootInfo, &pNewDir->RootInfo.NodeInfo, &pIdxRoot->Hdr,
2403 pRootAttr->cbResident - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), pErrInfo, pszWhat);
2404 if (RT_SUCCESS(rc))
2405 {
2406 *ppSharedDir = pNewDir;
2407 pRootAttr->uObj.pSharedDir = pNewDir;
2408 return VINF_SUCCESS;
2409 }
2410 RTMemFree(pNewDir);
2411 rtFsNtfsCore_Release(pCore);
2412 return rc;
2413}
2414
2415
2416/**
2417 * Gets a shared directory structure given an MFT record reference, creating a
2418 * new one if necessary.
2419 *
2420 * @returns IPRT status code.
2421 * @param pThis The NTFS volume instance.
2422 * @param pDirMftRef The MFT record reference to follow.
2423 * @param ppSharedDir Where to return the shared directory structure
2424 * (referenced).
2425 * @param pErrInfo Where to return error details. Optional.
2426 * @param pszWhat Error/log prefix.
2427 */
2428static int rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pDirMftRef,
2429 PRTFSNTFSDIRSHRD *ppSharedDir, PRTERRINFO pErrInfo, const char *pszWhat)
2430{
2431 /*
2432 * Get the core structure for the MFT record and check that it's a directory we've got.
2433 */
2434 PRTFSNTFSCORE pCore;
2435 int rc = rtFsNtfsVol_QueryCoreForMftRef(pThis, pDirMftRef, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2436 if (RT_SUCCESS(rc))
2437 {
2438 if (pCore->pMftRec->pFileRec->fFlags & NTFSRECFILE_F_DIRECTORY)
2439 {
2440 /*
2441 * Locate the $I30 root index attribute as we associate the
2442 * pointer to the shared directory pointer with it.
2443 */
2444 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
2445 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2446 if (pRootAttr)
2447 {
2448 if (!pRootAttr->uObj.pSharedDir)
2449 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, ppSharedDir, pErrInfo, pszWhat);
2450 else
2451 {
2452 Assert(pRootAttr->uObj.pSharedDir->RootInfo.pRootAttr->pCore == pCore);
2453 rtFsNtfsDirShrd_Retain(pRootAttr->uObj.pSharedDir);
2454 *ppSharedDir = pRootAttr->uObj.pSharedDir;
2455 }
2456 }
2457 else
2458 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY,
2459 "%s: Found INDEX_ROOT attribute named $I30, even though NTFSRECFILE_F_DIRECTORY is set",
2460 pszWhat);
2461 }
2462 else
2463 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY, "%s: fFlags=%#x", pszWhat, pCore->pMftRec->pFileRec->fFlags);
2464 rtFsNtfsCore_Release(pCore);
2465 }
2466 return rc;
2467}
2468
2469
2470/**
2471 * Frees resource kept by an index node info structure.
2472 *
2473 * @param pNodeInfo The index node info structure to delelte.
2474 */
2475static void rtFsNtfsIdxNodeInfo_Delete(PRTFSNTFSIDXNODEINFO pNodeInfo)
2476{
2477 RTMemFree(pNodeInfo->papEntries);
2478 pNodeInfo->papEntries = NULL;
2479 pNodeInfo->pNode = NULL;
2480 pNodeInfo->pVol = NULL;
2481}
2482
2483
2484/**
2485 * Gets or loads the specified subnode.
2486 *
2487 * @returns IPRT status code.
2488 * @param pRootInfo The index root info.
2489 * @param iNode The address of the node being queried.
2490 * @param ppNode Where to return the referenced pointer to the node.
2491 */
2492static int rtFsNtfsIdxRootInfo_QueryNode(PRTFSNTFSIDXROOTINFO pRootInfo, int64_t iNode, PRTFSNTFSIDXNODE *ppNode)
2493{
2494 /*
2495 * A bit of paranoia. These has been checked already when loading, but it
2496 * usually doesn't hurt too much to be careful.
2497 */
2498 AssertReturn(!(iNode & pRootInfo->fNodeAddressMisalign), VERR_VFS_BOGUS_OFFSET);
2499 AssertReturn((uint64_t)iNode < pRootInfo->uEndNodeAddresses, VERR_VFS_BOGUS_OFFSET);
2500 AssertReturn(pRootInfo->pAlloc, VERR_VFS_BOGUS_OFFSET);
2501
2502 /*
2503 * First translate the node address to a disk byte offset and check the index node cache.
2504 */
2505 uint64_t offNode = iNode << pRootInfo->cNodeAddressByteShift;
2506 uint64_t offNodeOnDisk = rtFsNtfsAttr_OffsetToDisk(pRootInfo->pAlloc, offNode, NULL);
2507 PRTFSNTFSIDXNODE pNode = (PRTFSNTFSIDXNODE)RTAvlU64Get(&pRootInfo->NodeInfo.pVol->IdxNodeCacheRoot, offNodeOnDisk);
2508 if (pNode)
2509 {
2510 rtFsNtfsIdxNode_Retain(pNode);
2511 *ppNode = pNode;
2512 return VINF_SUCCESS;
2513 }
2514
2515 /*
2516 * Need to create a load a new node.
2517 */
2518 pNode = (PRTFSNTFSIDXNODE)RTMemAllocZ(sizeof(*pNode));
2519 AssertReturn(pNode, VERR_NO_MEMORY);
2520
2521 pNode->TreeNode.Key = offNodeOnDisk;
2522 uint32_t cbIndexNode = RT_LE2H_U32(pRootInfo->pRoot->cbIndexNode);
2523 pNode->cbCost = sizeof(*pNode) + cbIndexNode;
2524 pNode->cRefs = 1;
2525 pNode->pNode = (PNTFSATINDEXALLOC)RTMemAllocZ(cbIndexNode);
2526 int rc;
2527 if (pNode->pNode)
2528 {
2529 rc = rtFsNtfsAttr_Read(pRootInfo->pAlloc, offNode, pNode->pNode, cbIndexNode);
2530 if (RT_SUCCESS(rc))
2531 {
2532 rc = VERR_VFS_BOGUS_FORMAT;
2533 if (pNode->pNode->RecHdr.uMagic != NTFSREC_MAGIC_INDEX_ALLOC)
2534 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Invalid node magic %#x\n",
2535 iNode, RT_LE2H_U32(pNode->pNode->RecHdr.uMagic) ));
2536 else if ((int64_t)RT_LE2H_U64(pNode->pNode->iSelfAddress) != iNode)
2537 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Wrong iSelfAddress: %#x\n",
2538 iNode, RT_LE2H_U64(pNode->pNode->iSelfAddress) ));
2539 else
2540 {
2541 rc = rtFsNtfsRec_DoMultiSectorFixups(&pNode->pNode->RecHdr, cbIndexNode, false /*fRelaxedUsa*/, NULL /*pErrInfo*/);
2542 if (RT_SUCCESS(rc))
2543 {
2544 /*
2545 * Validate/parse it
2546 */
2547#ifdef LOG_ENABLED
2548 rtFsNtfsVol_LogIndexHdrAndEntries(&pNode->pNode->Hdr,
2549 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2550 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "index node",
2551 pRootInfo->pRoot->uType);
2552#endif
2553 rc = rtFsNtfsVol_LoadIndexNodeInfo(pRootInfo, &pNode->NodeInfo, &pNode->pNode->Hdr,
2554 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2555 NULL /*pErrInfo*/, "index node");
2556 if (RT_SUCCESS(rc))
2557 {
2558 pNode->cbCost += pNode->NodeInfo.cEntries * sizeof(pNode->NodeInfo.papEntries[0]);
2559
2560 /*
2561 * Insert it into the cache.
2562 */
2563 bool fInsertOkay = RTAvlU64Insert(&pRootInfo->NodeInfo.pVol->IdxNodeCacheRoot, &pNode->TreeNode);
2564 Assert(fInsertOkay);
2565 if (fInsertOkay)
2566 {
2567 *ppNode = pNode;
2568 return VINF_SUCCESS;
2569 }
2570 }
2571 }
2572 }
2573 }
2574
2575 RTMemFree(pNode->pNode);
2576 pNode->pNode = NULL;
2577 }
2578 else
2579 rc = VERR_NO_MEMORY;
2580 RTMemFree(pNode);
2581 return rc;
2582}
2583
2584
2585/**
2586 * Frees resource kept by an index root info structure.
2587 *
2588 * @param pRootInfo The index root info structure to delete.
2589 */
2590static void rtFsNtfsIdxRootInfo_Delete(PRTFSNTFSIDXROOTINFO pRootInfo)
2591{
2592 rtFsNtfsIdxNodeInfo_Delete(&pRootInfo->NodeInfo);
2593 pRootInfo->pRootAttr->uObj.pSharedDir = NULL;
2594 rtFsNtfsCore_Release(pRootInfo->pRootAttr->pCore);
2595 pRootInfo->pRootAttr = NULL;
2596 pRootInfo->pAlloc = NULL;
2597 pRootInfo->pRoot = NULL;
2598}
2599
2600
2601/**
2602 * Destroys a shared directory structure when the reference count reached zero.
2603 *
2604 * @returns zero
2605 * @param pThis The shared directory structure to destroy.
2606 */
2607static uint32_t rtFsNtfsDirShrd_Destroy(PRTFSNTFSDIRSHRD pThis)
2608{
2609 rtFsNtfsIdxRootInfo_Delete(&pThis->RootInfo);
2610 RTMemFree(pThis);
2611 return 0;
2612}
2613
2614
2615/**
2616 * Releases a references to a shared directory structure.
2617 *
2618 * @returns New reference count.
2619 * @param pThis The shared directory structure.
2620 */
2621static uint32_t rtFsNtfsDirShrd_Release(PRTFSNTFSDIRSHRD pThis)
2622{
2623 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2624 Assert(cRefs < 128);
2625 if (cRefs > 0)
2626 return cRefs;
2627 return rtFsNtfsDirShrd_Destroy(pThis);
2628}
2629
2630
2631/**
2632 * Retains a references to a shared directory structure.
2633 *
2634 * @returns New reference count.
2635 * @param pThis The shared directory structure.
2636 */
2637static uint32_t rtFsNtfsDirShrd_Retain(PRTFSNTFSDIRSHRD pThis)
2638{
2639 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2640 Assert(cRefs > 1);
2641 Assert(cRefs < 128);
2642 return cRefs;
2643}
2644
2645
2646/**
2647 * Compares the two filenames in an case insentivie manner.
2648 *
2649 * @retval -1 if the first filename comes first
2650 * @retval 0 if equal
2651 * @retval 1 if the second filename comes first.
2652 *
2653 * @param pwszUpper1 The first filename, this has been uppercase already.
2654 * @param cwcUpper1 The length of the first filename.
2655 * @param pawcFilename2 The second filename to compare it with. Not zero
2656 * terminated.
2657 * @param cwcFilename2 The length of the second filename.
2658 * @param pawcUpcase The uppercase table. 64K entries.
2659 */
2660static int rtFsNtfsIdxComp_Filename(PCRTUTF16 pwszUpper1, uint8_t cwcUpper1, PCRTUTF16 pawcFilename2, uint8_t cwcFilename2,
2661 PCRTUTF16 const pawcUpcase)
2662{
2663 while (cwcUpper1 > 0 && cwcFilename2 > 0)
2664 {
2665 RTUTF16 uc1 = *pwszUpper1++;
2666 RTUTF16 uc2 = *pawcFilename2++;
2667 if (uc1 != uc2)
2668 {
2669 uc2 = pawcUpcase[uc2];
2670 if (uc1 != uc2)
2671 return uc1 < uc2 ? -1 : 1;
2672 }
2673
2674 /* Decrement the lengths and loop. */
2675 cwcUpper1--;
2676 cwcFilename2--;
2677 }
2678
2679 if (!cwcUpper1)
2680 return cwcFilename2 ? -1 : 0;
2681 return cwcFilename2 ? 1 : 0;
2682}
2683
2684
2685/**
2686 * Look up a name in the directory.
2687 *
2688 * @returns IPRT status code.
2689 * @param pShared The shared directory structure.
2690 * @param pszEntry The name to lookup.
2691 * @param ppFilename Where to return the pointer to the filename structure.
2692 * @param ppEntryHdr Where to return the poitner to the entry header
2693 * structure.
2694 * @param ppNode Where to return the pointer to the node the filename
2695 * structure resides in. This must be released. It will
2696 * be set to NULL if the name was found in the root node.
2697 */
2698static int rtFsNtfsDirShrd_Lookup(PRTFSNTFSDIRSHRD pShared, const char *pszEntry,
2699 PCNTFSATFILENAME *ppFilename, PCNTFSIDXENTRYHDR *ppEntryHdr, PRTFSNTFSIDXNODE *ppNode)
2700{
2701 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
2702
2703 *ppFilename = NULL;
2704 *ppEntryHdr = NULL;
2705 *ppNode = NULL;
2706 /** @todo do streams (split on ':') */
2707
2708 /*
2709 * Convert the filename to UTF16 and uppercase.
2710 */
2711 PCRTUTF16 const pawcUpcase = pVol->pawcUpcase;
2712 RTUTF16 wszFilename[256+4];
2713 PRTUTF16 pwszDst = wszFilename;
2714 PRTUTF16 pwszEnd = &wszFilename[255];
2715 const char *pszSrc = pszEntry;
2716 for (;;)
2717 {
2718 RTUNICP uc;
2719 int rc = RTStrGetCpEx(&pszSrc, &uc);
2720 if (RT_SUCCESS(rc))
2721 {
2722 if (uc != 0)
2723 {
2724 if (uc < _64K)
2725 uc = pawcUpcase[uc];
2726 pwszDst = RTUtf16PutCp(pwszDst, uc);
2727 if ((uintptr_t)pwszDst <= (uintptr_t)pwszEnd)
2728 { /* likely */ }
2729 else
2730 {
2731 Log(("rtFsNtfsDirShrd_Lookup: Filename too long '%s'\n", pszEntry));
2732 return VERR_FILENAME_TOO_LONG;
2733 }
2734 }
2735 else
2736 {
2737 *pwszDst = '\0';
2738 break;
2739 }
2740 }
2741 else
2742 {
2743 Log(("rtFsNtfsDirShrd_Lookup: Invalid UTF-8 encoding (%Rrc): %.*Rhxs\n", rc, strlen(pszEntry), pszEntry));
2744 return rc;
2745 }
2746 }
2747 uint8_t const cwcFilename = (uint8_t)(pwszDst - wszFilename);
2748
2749 /*
2750 * Do the tree traversal.
2751 */
2752 PRTFSNTFSIDXROOTINFO pRootInfo = &pShared->RootInfo;
2753 PRTFSNTFSIDXNODEINFO pNodeInfo = &pRootInfo->NodeInfo;
2754 PRTFSNTFSIDXNODE pNode = NULL;
2755 for (;;)
2756 {
2757 /*
2758 * Search it.
2759 */
2760 PCNTFSIDXENTRYHDR *papEntries = pNodeInfo->papEntries;
2761 uint32_t iEnd = pNodeInfo->cEntries;
2762 AssertReturn(iEnd > 0, VERR_INTERNAL_ERROR_3);
2763
2764 /* Exclude the end node from the serach as it doesn't have any key. */
2765 if (papEntries[iEnd - 1]->fFlags & NTFSIDXENTRYHDR_F_END)
2766 iEnd--;
2767
2768 uint32_t iEntry;
2769 if (1 /*iEnd < 8*/ )
2770 {
2771 if (iEnd > 0)
2772 {
2773 for (iEntry = 0; iEntry < iEnd; iEntry++)
2774 {
2775 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(papEntries[iEntry] + 1);
2776 int iDiff = rtFsNtfsIdxComp_Filename(wszFilename, cwcFilename, pFilename->wszFilename,
2777 pFilename->cwcFilename, pawcUpcase);
2778 if (iDiff > 0)
2779 { /* likely */ }
2780 else if (iDiff == 0)
2781 {
2782 *ppNode = pNode;
2783 *ppEntryHdr = papEntries[iEntry];
2784 *ppFilename = pFilename;
2785 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Found it!\n", pszEntry));
2786 return VINF_SUCCESS;
2787 }
2788 else
2789 break;
2790 }
2791 }
2792 else
2793 iEntry = iEnd;
2794 }
2795 /* else: implement binary search */
2796
2797 /*
2798 * Decend thru node iEntry.
2799 *
2800 * We could be bold and ASSUME that there is always an END node, but we're
2801 * playing safe for now.
2802 */
2803 if (iEnd < pNodeInfo->cEntries)
2804 {
2805 PCNTFSIDXENTRYHDR pEntry = papEntries[iEntry];
2806 if (pEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2807 {
2808 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntry);
2809 rtFsNtfsIdxNode_Release(pNode);
2810 int rc = rtFsNtfsIdxRootInfo_QueryNode(pRootInfo, iSubnode, &pNode);
2811 if (RT_SUCCESS(rc))
2812 {
2813 pNodeInfo = &pNode->NodeInfo;
2814 continue;
2815 }
2816 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): rtFsNtfsIdxRootInfo_QueryNode(%#RX64) error %Rrc!\n",
2817 pszEntry, iSubnode, rc));
2818 return rc;
2819 }
2820 }
2821 rtFsNtfsIdxNode_Release(pNode);
2822 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Not found! (#2)\n", pszEntry));
2823 return VERR_FILE_NOT_FOUND;
2824 }
2825
2826 /* not reached */
2827}
2828
2829
2830/**
2831 * Destroys an index node.
2832 *
2833 * This will remove it from the cache tree, however the caller must make sure
2834 * its not in the reuse list any more.
2835 *
2836 * @param pNode The node to destroy.
2837 */
2838static void rtFsNtfsIdxNode_Destroy(PRTFSNTFSIDXNODE pNode)
2839{
2840 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
2841
2842 /* Remove it from the volume node cache. */
2843 PAVLU64NODECORE pAssertRemove = RTAvlU64Remove(&pVol->IdxNodeCacheRoot, pNode->TreeNode.Key);
2844 Assert(pAssertRemove == &pNode->TreeNode); NOREF(pAssertRemove);
2845 pVol->cIdxNodes--;
2846 pVol->cbIdxNodes -= pNode->cbCost;
2847
2848 /* Destroy it. */
2849 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
2850 RTMemFree(pNode->pNode);
2851 pNode->pNode = NULL;
2852 RTMemFree(pNode);
2853}
2854
2855
2856/**
2857 * Trims the index node cache.
2858 *
2859 * @param pThis The NTFS volume instance which index node cache
2860 * needs trimming.
2861 */
2862static void rtFsNtfsIdxVol_TrimIndexNodeCache(PRTFSNTFSVOL pThis)
2863{
2864 while ( pThis->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE
2865 && pThis->cUnusedIdxNodes)
2866 {
2867 PRTFSNTFSIDXNODE pNode = RTListRemoveFirst(&pThis->IdxNodeUnusedHead, RTFSNTFSIDXNODE, UnusedListEntry);
2868 pThis->cUnusedIdxNodes--;
2869 rtFsNtfsIdxNode_Destroy(pNode);
2870 }
2871}
2872
2873
2874/**
2875 * Index node reference reached zero, put it in the unused list and trim the
2876 * cache.
2877 *
2878 * @returns zero
2879 * @param pNode The index node.
2880 */
2881static uint32_t rtFsNtfsIdxNode_MaybeDestroy(PRTFSNTFSIDXNODE pNode)
2882{
2883 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
2884 if (pVol)
2885 {
2886 RTListAppend(&pVol->IdxNodeUnusedHead, &pNode->UnusedListEntry);
2887 pVol->cUnusedIdxNodes++;
2888 if (pVol->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE)
2889 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
2890 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
2891 return 0;
2892 }
2893 /* not sure if this is needed yet... */
2894 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
2895 RTMemFree(pNode);
2896 return 0;
2897}
2898
2899
2900/**
2901 * Releases a reference to an index node.
2902 *
2903 * @returns New reference count.
2904 * @param pNode The index node to release. NULL is ignored.
2905 */
2906static uint32_t rtFsNtfsIdxNode_Release(PRTFSNTFSIDXNODE pNode)
2907{
2908 if (pNode)
2909 {
2910 uint32_t cRefs = ASMAtomicDecU32(&pNode->cRefs);
2911 Assert(cRefs < 128);
2912 if (cRefs > 0)
2913 return cRefs;
2914 return rtFsNtfsIdxNode_MaybeDestroy(pNode);
2915 }
2916 return 0;
2917}
2918
2919
2920/**
2921 * Retains a reference to an index node.
2922 *
2923 * This will remove it from the unused list if necessary.
2924 *
2925 * @returns New reference count.
2926 * @param pNode The index to reference.
2927 */
2928static uint32_t rtFsNtfsIdxNode_Retain(PRTFSNTFSIDXNODE pNode)
2929{
2930 uint32_t cRefs = ASMAtomicIncU32(&pNode->cRefs);
2931 if (cRefs == 1)
2932 {
2933 RTListNodeRemove(&pNode->UnusedListEntry);
2934 pNode->NodeInfo.pVol->cUnusedIdxNodes--;
2935 }
2936 return cRefs;
2937}
2938
2939
2940
2941
2942/*
2943 *
2944 * Directory instance methods
2945 * Directory instance methods
2946 * Directory instance methods
2947 *
2948 */
2949
2950/**
2951 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2952 */
2953static DECLCALLBACK(int) rtFsNtfsDir_Close(void *pvThis)
2954{
2955 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
2956 LogFlow(("rtFsNtfsDir_Close(%p/%p)\n", pThis, pThis->pShared));
2957
2958 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
2959 pThis->pShared = NULL;
2960 if (pShared)
2961 rtFsNtfsDirShrd_Release(pShared);
2962
2963 while (pThis->cEnumStackEntries > 0)
2964 {
2965 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
2966 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
2967 pEntry->pNodeInfo = NULL;
2968 }
2969 RTMemFree(pThis->paEnumStack);
2970 pThis->paEnumStack = NULL;
2971 pThis->cEnumStackMaxDepth = 0;
2972
2973 return VINF_SUCCESS;
2974}
2975
2976
2977/**
2978 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2979 */
2980static DECLCALLBACK(int) rtFsNtfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2981{
2982 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
2983 Log(("rtFsNtfsDir_QueryInfo\n"));
2984 return rtFsNtfsCore_QueryInfo(pThis->pShared->RootInfo.pRootAttr->pCore,
2985 pThis->pShared->RootInfo.pAlloc ? pThis->pShared->RootInfo.pAlloc
2986 : pThis->pShared->RootInfo.pRootAttr,
2987 pObjInfo, enmAddAttr);
2988}
2989
2990
2991/**
2992 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2993 */
2994static DECLCALLBACK(int) rtFsNtfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2995{
2996 Log(("rtFsNtfsDir_SetMode\n"));
2997 RT_NOREF(pvThis, fMode, fMask);
2998 return VERR_WRITE_PROTECT;
2999}
3000
3001
3002/**
3003 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
3004 */
3005static DECLCALLBACK(int) rtFsNtfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
3006 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
3007{
3008 Log(("rtFsNtfsDir_SetTimes\n"));
3009 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
3010 return VERR_WRITE_PROTECT;
3011}
3012
3013
3014/**
3015 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
3016 */
3017static DECLCALLBACK(int) rtFsNtfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
3018{
3019 Log(("rtFsNtfsDir_SetOwner\n"));
3020 RT_NOREF(pvThis, uid, gid);
3021 return VERR_WRITE_PROTECT;
3022}
3023
3024
3025/**
3026 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
3027 */
3028static DECLCALLBACK(int) rtFsNtfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
3029 uint32_t fFlags, PRTVFSOBJ phVfsObj)
3030{
3031 LogFlow(("rtFsNtfsDir_Open: pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
3032 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3033 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3034 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
3035 int rc;
3036
3037 /*
3038 * We cannot create or replace anything, just open stuff.
3039 */
3040 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3041 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
3042 { /* likely */ }
3043 else
3044 return VERR_WRITE_PROTECT;
3045
3046 /*
3047 * Special cases '.' and '..'
3048 */
3049 if ( pszEntry[0] == '.'
3050 && ( pszEntry[1] == '\0'
3051 || ( pszEntry[1] == '.'
3052 && pszEntry[2] == '\0')))
3053 {
3054 if (!(fFlags & RTVFSOBJ_F_OPEN_DIRECTORY))
3055 return VERR_IS_A_DIRECTORY;
3056
3057 PRTFSNTFSDIRSHRD pSharedToOpen;
3058 if ( pszEntry[1] == '\0'
3059 || pVol->pRootDir == pShared)
3060 {
3061 pSharedToOpen = pShared;
3062 rtFsNtfsDirShrd_Retain(pSharedToOpen);
3063 rc = VINF_SUCCESS;
3064 }
3065 else
3066 {
3067 /* Find a parent mft reference */
3068 pSharedToOpen = NULL;
3069 rc = VERR_VFS_BOGUS_FORMAT;
3070 PRTFSNTFSCORE pCore = pShared->RootInfo.pRootAttr->pCore;
3071 PRTFSNTFSATTR pCurAttr;
3072 RTListForEach(&pCore->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
3073 {
3074 if ( pCurAttr->pAttrHdr->uAttrType == NTFS_AT_FILENAME
3075 && pCurAttr->cbResident >= sizeof(NTFSATFILENAME))
3076 {
3077 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pCurAttr->pAttrHdr);
3078 rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pVol, &pFilename->ParentDirMftRec,
3079 &pSharedToOpen, NULL /*pErrInfo*/, "..");
3080 break;
3081 }
3082 }
3083 }
3084 if (RT_SUCCESS(rc))
3085 {
3086 RTVFSDIR hVfsDir;
3087 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
3088 rtFsNtfsDirShrd_Release(pSharedToOpen);
3089 if (RT_SUCCESS(rc))
3090 {
3091 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3092 RTVfsDirRelease(hVfsDir);
3093 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3094 }
3095 }
3096 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3097 return rc;
3098 }
3099
3100 /*
3101 * Lookup the index entry.
3102 */
3103 PRTFSNTFSIDXNODE pNode;
3104 PCNTFSIDXENTRYHDR pEntryHdr;
3105 PCNTFSATFILENAME pFilename;
3106 rc = rtFsNtfsDirShrd_Lookup(pShared, pszEntry, &pFilename, &pEntryHdr, &pNode);
3107 if (RT_SUCCESS(rc))
3108 {
3109 uint32_t fFileAttribs = RT_LE2H_U32(pFilename->fFileAttribs);
3110 switch (fFileAttribs & (NTFS_FA_DIRECTORY | NTFS_FA_REPARSE_POINT | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT))
3111 {
3112 /*
3113 * File.
3114 */
3115 case 0:
3116 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3117 {
3118 //RTVFSFILE hVfsFile;
3119 //rc = rtFsIsoFile_New9660(pVol, pShared, pDirRec, cDirRecs,
3120 // offDirRec, fOpen, uVersion, &hVfsFile);
3121 //if (RT_SUCCESS(rc))
3122 //{
3123 // *phVfsObj = RTVfsObjFromFile(hVfsFile);
3124 // RTVfsFileRelease(hVfsFile);
3125 // AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3126 //}
3127 rc = VERR_NOT_IMPLEMENTED;
3128 }
3129 else
3130 rc = VERR_IS_A_FILE;
3131 break;
3132
3133 /*
3134 * Directory
3135 */
3136 case NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3137 case NTFS_FA_DIRECTORY | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3138 case NTFS_FA_DIRECTORY:
3139 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3140 {
3141 PRTFSNTFSDIRSHRD pSharedToOpen;
3142 rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pVol, &pEntryHdr->u.FileMftRec,
3143 &pSharedToOpen, NULL, pszEntry);
3144 if (RT_SUCCESS(rc))
3145 {
3146 RTVFSDIR hVfsDir;
3147 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
3148 rtFsNtfsDirShrd_Release(pSharedToOpen);
3149 if (RT_SUCCESS(rc))
3150 {
3151 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3152 RTVfsDirRelease(hVfsDir);
3153 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3154 }
3155 }
3156 }
3157 else
3158 rc = VERR_IS_A_DIRECTORY;
3159 break;
3160
3161 /*
3162 * Possible symbolic links.
3163 */
3164 case NTFS_FA_REPARSE_POINT:
3165 case NTFS_FA_REPARSE_POINT | NTFS_FA_DIRECTORY:
3166 case NTFS_FA_REPARSE_POINT | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3167 case NTFS_FA_REPARSE_POINT | NTFS_FA_DIRECTORY | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3168 rc = VERR_NOT_IMPLEMENTED;
3169 break;
3170
3171 default:
3172 AssertFailed();
3173 rc = VERR_FILE_NOT_FOUND;
3174 break;
3175 }
3176 rtFsNtfsIdxNode_Release(pNode);
3177 }
3178
3179 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3180 return rc;
3181}
3182
3183
3184/**
3185 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3186 */
3187static DECLCALLBACK(int) rtFsNtfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3188{
3189 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3190 Log(("rtFsNtfsDir_CreateDir\n"));
3191 return VERR_WRITE_PROTECT;
3192}
3193
3194
3195/**
3196 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3197 */
3198static DECLCALLBACK(int) rtFsNtfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3199{
3200 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3201 Log(("rtFsNtfsDir_OpenSymlink\n"));
3202 return VERR_NOT_SUPPORTED;
3203}
3204
3205
3206/**
3207 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3208 */
3209static DECLCALLBACK(int) rtFsNtfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3210 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3211{
3212 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3213 Log(("rtFsNtfsDir_CreateSymlink\n"));
3214 return VERR_WRITE_PROTECT;
3215}
3216
3217
3218/**
3219 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3220 */
3221static DECLCALLBACK(int) rtFsNtfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3222{
3223 RT_NOREF(pvThis, pszEntry, fType);
3224 Log(("rtFsNtfsDir_UnlinkEntry\n"));
3225 return VERR_WRITE_PROTECT;
3226}
3227
3228
3229/**
3230 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3231 */
3232static DECLCALLBACK(int) rtFsNtfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3233{
3234 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3235 Log(("rtFsNtfsDir_RenameEntry\n"));
3236 return VERR_WRITE_PROTECT;
3237}
3238
3239
3240/**
3241 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3242 */
3243static DECLCALLBACK(int) rtFsNtfsDir_RewindDir(void *pvThis)
3244{
3245 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3246 LogFlow(("rtFsNtfsDir_RewindDir\n"));
3247
3248 while (pThis->cEnumStackEntries > 0)
3249 {
3250 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
3251 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
3252 pEntry->pNodeInfo = NULL;
3253 }
3254 pThis->fNoMoreFiles = false;
3255
3256 return VINF_SUCCESS;
3257}
3258
3259
3260/**
3261 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3262 */
3263static DECLCALLBACK(int) rtFsNtfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3264 RTFSOBJATTRADD enmAddAttr)
3265{
3266 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3267 if (pThis->fNoMoreFiles)
3268 return VERR_NO_MORE_FILES;
3269 Log(("rtFsNtfsDir_ReadDir\n"));
3270 //PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3271 NOREF(pvThis); NOREF(pDirEntry); NOREF(pcbDirEntry); NOREF(enmAddAttr);
3272 return VERR_NOT_IMPLEMENTED;
3273}
3274
3275
3276/**
3277 * NTFS file operations.
3278 */
3279static const RTVFSDIROPS g_rtFsNtfsDirOps =
3280{
3281 { /* Obj */
3282 RTVFSOBJOPS_VERSION,
3283 RTVFSOBJTYPE_DIR,
3284 "NTFS Dir",
3285 rtFsNtfsDir_Close,
3286 rtFsNtfsDir_QueryInfo,
3287 RTVFSOBJOPS_VERSION
3288 },
3289 RTVFSDIROPS_VERSION,
3290 0,
3291 { /* ObjSet */
3292 RTVFSOBJSETOPS_VERSION,
3293 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3294 rtFsNtfsDir_SetMode,
3295 rtFsNtfsDir_SetTimes,
3296 rtFsNtfsDir_SetOwner,
3297 RTVFSOBJSETOPS_VERSION
3298 },
3299 rtFsNtfsDir_Open,
3300 NULL /* pfnFollowAbsoluteSymlink */,
3301 NULL /* pfnOpenFile */,
3302 NULL /* pfnOpenDir */,
3303 rtFsNtfsDir_CreateDir,
3304 rtFsNtfsDir_OpenSymlink,
3305 rtFsNtfsDir_CreateSymlink,
3306 NULL /* pfnQueryEntryInfo */,
3307 rtFsNtfsDir_UnlinkEntry,
3308 rtFsNtfsDir_RenameEntry,
3309 rtFsNtfsDir_RewindDir,
3310 rtFsNtfsDir_ReadDir,
3311 RTVFSDIROPS_VERSION,
3312};
3313
3314
3315/**
3316 * Creates a new directory instance given a shared directory structure.
3317 *
3318 * @returns IPRT status code.
3319 * @param pThis The NTFS volume instance.
3320 * @param pSharedDir The shared directory structure to create a new
3321 * handle to.
3322 * @param phVfsDir Where to return the directory handle.
3323 */
3324static int rtFsNtfsVol_NewDirFromShared(PRTFSNTFSVOL pThis, PRTFSNTFSDIRSHRD pSharedDir, PRTVFSDIR phVfsDir)
3325{
3326 PRTFSNTFSDIR pNewDir;
3327 int rc = RTVfsNewDir(&g_rtFsNtfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
3328 phVfsDir, (void **)&pNewDir);
3329 if (RT_SUCCESS(rc))
3330 {
3331 rtFsNtfsDirShrd_Retain(pSharedDir);
3332 pNewDir->pShared = pSharedDir;
3333 pNewDir->cEnumStackEntries = 0;
3334 pNewDir->cEnumStackMaxDepth = 0;
3335 pNewDir->paEnumStack = NULL;
3336 return VINF_SUCCESS;
3337 }
3338 return rc;
3339}
3340
3341
3342
3343/*
3344 *
3345 * Volume level code.
3346 * Volume level code.
3347 * Volume level code.
3348 *
3349 */
3350
3351
3352/**
3353 * Slow path for querying the allocation state of a cluster.
3354 *
3355 * @returns IPRT status code.
3356 * @param pThis The NTFS volume instance.
3357 * @param iCluster The cluster to query.
3358 * @param pfState Where to return the state.
3359 */
3360static int rtFsNtfsVol_QueryClusterStateSlow(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
3361{
3362 int rc;
3363 uint64_t const cbWholeBitmap = RT_LE2H_U64(pThis->pMftBitmap->pAttrHdr->u.NonRes.cbData);
3364 uint64_t const offInBitmap = iCluster >> 3;
3365 if (offInBitmap < cbWholeBitmap)
3366 {
3367 if (!pThis->pvBitmap)
3368 {
3369 /*
3370 * Try cache the whole bitmap if it's not too large.
3371 */
3372 if ( cbWholeBitmap <= RTFSNTFS_MAX_WHOLE_BITMAP_CACHE
3373 && cbWholeBitmap >= RT_ALIGN_64(pThis->cClusters >> 3, 8))
3374 {
3375 pThis->cbBitmapAlloc = RT_ALIGN_Z((uint32_t)cbWholeBitmap, 8);
3376 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
3377 if (pThis->pvBitmap)
3378 {
3379 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
3380 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, 0, pThis->pvBitmap, (uint32_t)cbWholeBitmap);
3381 if (RT_SUCCESS(rc))
3382 {
3383 pThis->iFirstBitmapCluster = 0;
3384 pThis->cBitmapClusters = pThis->cClusters;
3385 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iCluster);
3386 return VINF_SUCCESS;
3387 }
3388 RTMemFree(pThis->pvBitmap);
3389 pThis->pvBitmap = NULL;
3390 pThis->cbBitmapAlloc = 0;
3391 return rc;
3392 }
3393 }
3394
3395 /*
3396 * Do a cluster/4K cache.
3397 */
3398 pThis->cbBitmapAlloc = RT_MAX(pThis->cbCluster, _4K);
3399 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
3400 if (!pThis->pvBitmap)
3401 {
3402 pThis->cbBitmapAlloc = 0;
3403 return VERR_NO_MEMORY;
3404 }
3405 }
3406
3407 /*
3408 * Load a cache line.
3409 */
3410 Assert(RT_IS_POWER_OF_TWO(pThis->cbBitmapAlloc));
3411 uint64_t offLoad = offInBitmap & ~(pThis->cbBitmapAlloc - 1);
3412 uint32_t cbLoad = (uint32_t)RT_MIN(cbWholeBitmap - offLoad, pThis->cbBitmapAlloc);
3413
3414 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
3415 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, offLoad, pThis->pvBitmap, cbLoad);
3416 if (RT_SUCCESS(rc))
3417 {
3418 pThis->iFirstBitmapCluster = offLoad << 3;
3419 pThis->cBitmapClusters = cbLoad << 3;
3420 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)(iCluster - pThis->iFirstBitmapCluster));
3421 return VINF_SUCCESS;
3422 }
3423 pThis->cBitmapClusters = 0;
3424 }
3425 else
3426 {
3427 LogRel(("rtFsNtfsVol_QueryClusterStateSlow: iCluster=%#RX64 is outside the bitmap (%#RX64)\n", iCluster, cbWholeBitmap));
3428 rc = VERR_OUT_OF_RANGE;
3429 }
3430 return rc;
3431}
3432
3433
3434/**
3435 * Query the allocation state of the given cluster.
3436 *
3437 * @returns IPRT status code.
3438 * @param pThis The NTFS volume instance.
3439 * @param iCluster The cluster to query.
3440 * @param pfState Where to return the state.
3441 */
3442static int rtFsNtfsVol_QueryClusterState(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
3443{
3444 uint64_t iClusterInCache = iCluster - pThis->iFirstBitmapCluster;
3445 if (iClusterInCache < pThis->cBitmapClusters)
3446 {
3447 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iClusterInCache);
3448 return VINF_SUCCESS;
3449 }
3450 return rtFsNtfsVol_QueryClusterStateSlow(pThis, iCluster, pfState);
3451}
3452
3453
3454
3455/**
3456 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3457 */
3458static DECLCALLBACK(int) rtFsNtfsVol_Close(void *pvThis)
3459{
3460 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
3461
3462 RTVfsFileRelease(pThis->hVfsBacking);
3463 pThis->hVfsBacking = NIL_RTVFSFILE;
3464 pThis->hVfsSelf = NIL_RTVFS;
3465
3466 return VINF_SUCCESS;
3467}
3468
3469
3470/**
3471 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3472 */
3473static DECLCALLBACK(int) rtFsNtfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3474{
3475 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
3476 return VERR_WRONG_TYPE;
3477}
3478
3479
3480/**
3481 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
3482 */
3483static DECLCALLBACK(int) rtFsNtfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3484{
3485 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
3486 AssertReturn(pThis->pRootDir, VERR_INTERNAL_ERROR_4);
3487 int rc = rtFsNtfsVol_NewDirFromShared(pThis, pThis->pRootDir, phVfsDir);
3488 LogFlow(("rtFsNtfsVol_OpenRoot: returns %Rrc\n", rc));
3489 return rc;
3490}
3491
3492
3493/**
3494 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
3495 */
3496static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
3497{
3498 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
3499 *pfUsed = true;
3500
3501 /*
3502 * Round to a cluster range.
3503 */
3504 uint64_t iCluster = off >> pThis->cClusterShift;
3505
3506 Assert(RT_IS_POWER_OF_TWO(pThis->cbCluster));
3507 cb += off & (pThis->cbCluster - 1);
3508 cb = RT_ALIGN_Z(cb, pThis->cbCluster);
3509 size_t cClusters = cb >> pThis->cClusterShift;
3510
3511 /*
3512 * Check the clusters one-by-one.
3513 * Just to be cautious, we will always check the cluster at off, even when cb is zero.
3514 */
3515 do
3516 {
3517 bool fState = true;
3518 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
3519 if (RT_FAILURE(rc))
3520 return rc;
3521 if (fState)
3522 {
3523 *pfUsed = true;
3524 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - used\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
3525 return VINF_SUCCESS;
3526 }
3527
3528 iCluster++;
3529 } while (cClusters-- > 0);
3530
3531 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - unused\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
3532 *pfUsed = false;
3533 return VINF_SUCCESS;
3534}
3535
3536
3537DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsNtfsVolOps =
3538{
3539 /* .Obj = */
3540 {
3541 /* .uVersion = */ RTVFSOBJOPS_VERSION,
3542 /* .enmType = */ RTVFSOBJTYPE_VFS,
3543 /* .pszName = */ "NtfsVol",
3544 /* .pfnClose = */ rtFsNtfsVol_Close,
3545 /* .pfnQueryInfo = */ rtFsNtfsVol_QueryInfo,
3546 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
3547 },
3548 /* .uVersion = */ RTVFSOPS_VERSION,
3549 /* .fFeatures = */ 0,
3550 /* .pfnOpenRoot = */ rtFsNtfsVol_OpenRoot,
3551 /* .pfnQueryRangeState = */ rtFsNtfsVol_QueryRangeState,
3552 /* .uEndMarker = */ RTVFSOPS_VERSION
3553};
3554
3555
3556/**
3557 * Checks that the storage for the given attribute is all marked allocated in
3558 * the allocation bitmap of the volume.
3559 *
3560 * @returns IPRT status code.
3561 * @param pThis The NTFS volume instance.
3562 * @param pAttr The attribute to check.
3563 * @param pszDesc Description of the attribute.
3564 * @param pErrInfo Where to return error details.
3565 */
3566static int rtFsNtfsVolCheckBitmap(PRTFSNTFSVOL pThis, PRTFSNTFSATTR pAttr, const char *pszDesc, PRTERRINFO pErrInfo)
3567{
3568 PRTFSNTFSATTRSUBREC pSubRec = NULL;
3569 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
3570 uint64_t offFile = 0;
3571 for (;;)
3572 {
3573 uint32_t const cExtents = pTable->cExtents;
3574 PRTFSNTFSEXTENT paExtents = pTable->paExtents;
3575 for (uint32_t iExtent = 0; iExtent < cExtents; iExtent++)
3576 {
3577 uint64_t const off = paExtents[iExtent].off;
3578 if (off == UINT64_MAX)
3579 offFile += paExtents[iExtent].cbExtent;
3580 else
3581 {
3582 uint64_t iCluster = off >> pThis->cClusterShift;
3583 uint64_t cClusters = paExtents[iExtent].cbExtent >> pThis->cClusterShift;
3584 Assert((cClusters << pThis->cClusterShift) == paExtents[iExtent].cbExtent);
3585 Assert(cClusters != 0);
3586
3587 while (cClusters-- > 0)
3588 {
3589 bool fState = false;
3590 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
3591 if (RT_FAILURE(rc))
3592 return RTERRINFO_LOG_REL_SET_F(pErrInfo, rc,
3593 "Error querying allocation bitmap entry %#RX64 (for %s offset %#RX64)",
3594 iCluster, pszDesc, offFile);
3595 if (!fState)
3596 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3597 "Cluster %#RX64 at offset %#RX64 in %s is not marked allocated",
3598 iCluster, offFile, pszDesc);
3599 offFile += pThis->cbCluster;
3600 }
3601 }
3602 }
3603
3604 /* Next table. */
3605 pSubRec = pSubRec ? pSubRec->pNext : pAttr->pSubRecHead;
3606 if (!pSubRec)
3607 return VINF_SUCCESS;
3608 pTable = &pSubRec->Extents;
3609 }
3610}
3611
3612
3613/**
3614 * Loads, validates and setups the '.' (NTFS_MFT_IDX_ROOT) MFT entry.
3615 *
3616 * @returns IPRT status code
3617 * @param pThis The NTFS volume instance. Will set pawcUpcase.
3618 * @param pErrInfo Where to return additional error info.
3619 */
3620static int rtFsNtfsVolLoadRootDir(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3621{
3622 /*
3623 * Load it and do some checks.
3624 */
3625 PRTFSNTFSCORE pCore;
3626 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_ROOT, false /*fRelaxedUsa*/, &pCore, pErrInfo); // DON'T COMMIT
3627 if (RT_SUCCESS(rc))
3628 {
3629 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3630 if (!pFilenameAttr)
3631 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: has no FILENAME attribute!");
3632 else if (pFilenameAttr->pAttrHdr->fNonResident)
3633 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: FILENAME attribute is non-resident!");
3634 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[1]))
3635 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3636 "RootDir: FILENAME attribute value size is too small: %#x",
3637 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3638 else
3639 {
3640 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3641 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3642 if ( pFilename->cwcFilename != 1
3643 || ( RTUtf16NICmpAscii(pFilename->wszFilename, ".", 1) != 0
3644 && RTUtf16NICmpAscii(pFilename->wszFilename, "$", 1) != 0))
3645 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3646 "RootDir: FILENAME is not '.' nor '$: '%.*ls'",
3647 pFilename->cwcFilename, pFilename->wszFilename);
3648 else
3649 {
3650 PRTFSNTFSATTR pIndexRoot = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
3651 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3652 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
3653 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3654 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
3655 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3656 if (!pIndexRoot)
3657 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ROOT attribute named $I30");
3658 else if (!pIndexAlloc && pIndexBitmap)
3659 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ALLOCATION attribute named $I30");
3660 else if (!pIndexBitmap && pIndexAlloc)
3661 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no BITMAP attribute named $I30");
3662 if (RT_SUCCESS(rc) && pIndexAlloc)
3663 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexAlloc, "RootDir", pErrInfo);
3664 if (RT_SUCCESS(rc) && pIndexBitmap)
3665 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexBitmap, "RootDir/bitmap", pErrInfo);
3666 if (RT_SUCCESS(rc))
3667 {
3668 /*
3669 * Load it as a normal directory.
3670 */
3671 PRTFSNTFSDIRSHRD pSharedDir;
3672 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, &pSharedDir, pErrInfo, "RootDir");
3673 if (RT_SUCCESS(rc))
3674 {
3675 rtFsNtfsCore_Release(pCore);
3676 pThis->pRootDir = pSharedDir;
3677 return VINF_SUCCESS;
3678 }
3679 }
3680 }
3681 }
3682 rtFsNtfsCore_Release(pCore);
3683 }
3684 else
3685 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Root dir: Error reading MFT record");
3686 return rc;
3687}
3688
3689
3690/**
3691 * Loads, validates and setups the '$UpCase' (NTFS_MFT_IDX_UP_CASE) MFT entry.
3692 *
3693 * This is needed for filename lookups, I think.
3694 *
3695 * @returns IPRT status code
3696 * @param pThis The NTFS volume instance. Will set pawcUpcase.
3697 * @param pErrInfo Where to return additional error info.
3698 */
3699static int rtFsNtfsVolLoadUpCase(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3700{
3701 PRTFSNTFSCORE pCore;
3702 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_UP_CASE, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3703 if (RT_SUCCESS(rc))
3704 {
3705 PRTFSNTFSATTR pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
3706 if (pDataAttr)
3707 {
3708 /*
3709 * Validate the '$Upcase' MFT record.
3710 */
3711 uint32_t const cbMin = 512;
3712 uint32_t const cbMax = _128K;
3713 if (!pDataAttr->pAttrHdr->fNonResident)
3714 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: unnamed DATA attribute is resident!");
3715 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) < cbMin
3716 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) > cbMax)
3717 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3718 "$UpCase: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX32",
3719 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated), cbMin, cbMax);
3720 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) < cbMin
3721 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
3722 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
3723 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) & 1) )
3724 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3725 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
3726 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData), cbMin,
3727 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
3728 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) < cbMin
3729 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized)
3730 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated)
3731 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) & 1) )
3732 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3733 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
3734 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized), cbMin,
3735 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
3736 else if (pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit != 0)
3737 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3738 "$UpCase: unnamed DATA attribute is compressed: %#x",
3739 pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit);
3740 else
3741 {
3742 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3743 if (!pFilenameAttr)
3744 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase has no FILENAME attribute!");
3745 else if (pFilenameAttr->pAttrHdr->fNonResident)
3746 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase FILENAME attribute is non-resident!");
3747 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
3748 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3749 "$UpCase: FILENAME attribute value size is too small: %#x",
3750 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3751 else
3752 {
3753 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3754 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3755 if ( pFilename->cwcFilename != 7
3756 || RTUtf16NICmpAscii(pFilename->wszFilename, "$UpCase", 7) != 0)
3757 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3758 "$UpCase: FILENAME isn't '$UpCase': '%.*ls'",
3759 pFilename->cwcFilename, pFilename->wszFilename);
3760 else
3761 {
3762 /*
3763 * Allocate memory for the uppercase table and read it.
3764 */
3765 PRTUTF16 pawcUpcase;
3766 pThis->pawcUpcase = pawcUpcase = (PRTUTF16)RTMemAlloc(_64K * sizeof(pThis->pawcUpcase[0]));
3767 if (pawcUpcase)
3768 {
3769 for (size_t i = 0; i < _64K; i++)
3770 pawcUpcase[i] = (uint16_t)i;
3771
3772 rc = rtFsNtfsAttr_Read(pDataAttr, 0, pawcUpcase, pDataAttr->pAttrHdr->u.NonRes.cbData);
3773 if (RT_SUCCESS(rc))
3774 {
3775 /*
3776 * Check the data.
3777 */
3778 for (size_t i = 1; i < _64K; i++)
3779 if (pawcUpcase[i] == 0)
3780 {
3781 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3782 "$UpCase entry %#x is zero!", i);
3783 break;
3784 }
3785
3786 /*
3787 * While we still have the $UpCase file open, check it against the allocation bitmap.
3788 */
3789 if (RT_SUCCESS(rc))
3790 rc = rtFsNtfsVolCheckBitmap(pThis, pDataAttr, "$UpCase", pErrInfo);
3791
3792 /* We're done, no need for special success return here though. */
3793 }
3794 else
3795 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, rc, "Error reading $UpCase data into memory");
3796 }
3797 else
3798 rc = VERR_NO_MEMORY;
3799 }
3800 }
3801 }
3802 }
3803 else
3804 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: has no unnamed DATA attribute!");
3805 rtFsNtfsCore_Release(pCore);
3806 }
3807 else
3808 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$UpCase: Error reading the MFT record");
3809 return rc;
3810}
3811
3812
3813/**
3814 * Loads the allocation bitmap and does basic validation of.
3815 *
3816 * @returns IPRT status code.
3817 * @param pThis The NTFS volume instance. Will set up the
3818 * 'Allocation bitmap and cache' fields.
3819 * @param pErrInfo Where to return error details.
3820 */
3821static int rtFsNtfsVolLoadBitmap(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3822{
3823 PRTFSNTFSCORE pCore;
3824 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_BITMAP, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3825 if (RT_SUCCESS(rc))
3826 {
3827 PRTFSNTFSATTR pMftBitmap;
3828 pThis->pMftBitmap = pMftBitmap = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
3829 if (pMftBitmap)
3830 {
3831 /*
3832 * Validate the '$Bitmap' MFT record.
3833 * We expect the bitmap to be fully initialized and be sized according to the
3834 * formatted volume size. Allegedly, NTFS pads it to an even 8 byte in size.
3835 */
3836 uint64_t const cbMinBitmap = RT_ALIGN_64(pThis->cbVolume >> (pThis->cClusterShift + 3), 8);
3837 uint64_t const cbMaxBitmap = RT_ALIGN_64(cbMinBitmap, pThis->cbCluster);
3838 //uint64_t const cbMinInitialized = RT_ALIGN_64((RT_MAX(pThis->uLcnMft, pThis->uLcnMftMirror) + 16) >> 3, 8);
3839 if (!pMftBitmap->pAttrHdr->fNonResident)
3840 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is resident!");
3841 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) < cbMinBitmap
3842 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) > cbMaxBitmap)
3843 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3844 "$Bitmap: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
3845 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated), cbMinBitmap, cbMaxBitmap);
3846 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) < cbMinBitmap
3847 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData)
3848 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData))
3849 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3850 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
3851 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData), cbMinBitmap,
3852 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
3853 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) < cbMinBitmap
3854 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized)
3855 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated))
3856 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3857 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
3858 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized), cbMinBitmap,
3859 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
3860 else if (pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit != 0)
3861 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3862 "$Bitmap: unnamed DATA attribute is compressed: %#x",
3863 pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit);
3864 else if (pMftBitmap->Extents.cExtents != 1) /* paranoia for now */
3865 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3866 "$Bitmap: unnamed DATA attribute is expected to have a single extent: %u extents",
3867 pMftBitmap->Extents.cExtents);
3868 else if (pMftBitmap->Extents.paExtents[0].off == UINT64_MAX)
3869 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is sparse");
3870 else
3871 {
3872 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3873 if (!pFilenameAttr)
3874 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 has no FILENAME attribute!");
3875 else if (pFilenameAttr->pAttrHdr->fNonResident)
3876 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 FILENAME attribute is non-resident!");
3877 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
3878 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3879 "$Bitmap FILENAME attribute value size is too small: %#x",
3880 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3881 else
3882 {
3883 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3884 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3885 if ( pFilename->cwcFilename != 7
3886 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Bitmap", 7) != 0)
3887 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3888 "$Bitmap: FILENAME isn't '$Bitmap': '%.*ls'",
3889 pFilename->cwcFilename, pFilename->wszFilename);
3890 else
3891 {
3892 /*
3893 * Read some of it into the buffer and check that essential stuff is flagged as allocated.
3894 */
3895 /* The boot sector. */
3896 bool fState = false;
3897 rc = rtFsNtfsVol_QueryClusterState(pThis, 0, &fState);
3898 if (RT_SUCCESS(rc) && !fState)
3899 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3900 "MFT allocation bitmap error: Bootsector isn't marked allocated!");
3901 else if (RT_FAILURE(rc))
3902 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3903 "MFT allocation bitmap (offset 0) read error: %Rrc", rc);
3904
3905 /* The bitmap ifself, the MFT data, and the MFT bitmap. */
3906 if (RT_SUCCESS(rc))
3907 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftBitmap, "allocation bitmap", pErrInfo);
3908 if (RT_SUCCESS(rc))
3909 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftData, "MFT", pErrInfo);
3910 if (RT_SUCCESS(rc))
3911 rc = rtFsNtfsVolCheckBitmap(pThis,
3912 rtFsNtfsCore_FindUnnamedAttribute(pThis->pMftData->pCore, NTFS_AT_BITMAP),
3913 "MFT Bitmap", pErrInfo);
3914 if (RT_SUCCESS(rc))
3915 {
3916 /*
3917 * Looks like the bitmap is good.
3918 */
3919 return VINF_SUCCESS;
3920 }
3921 }
3922 }
3923 }
3924 pThis->pMftBitmap = NULL;
3925 }
3926 else
3927 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Bitmap: has no unnamed DATA attribute!");
3928 rtFsNtfsCore_Release(pCore);
3929 }
3930 else
3931 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$Bitmap: Error MFT record");
3932 return rc;
3933}
3934
3935
3936/**
3937 * Loads, validates and setups the '$Volume' (NTFS_MFT_IDX_VOLUME) MFT entry.
3938 *
3939 * @returns IPRT status code
3940 * @param pThis The NTFS volume instance. Will set uNtfsVersion
3941 * and fVolumeFlags.
3942 * @param pErrInfo Where to return additional error info.
3943 */
3944static int rtFsNtfsVolLoadVolumeInfo(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3945{
3946 PRTFSNTFSCORE pCore;
3947 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_VOLUME, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3948 if (RT_SUCCESS(rc))
3949 {
3950 PRTFSNTFSATTR pVolInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_VOLUME_INFORMATION);
3951 if (pVolInfoAttr)
3952 {
3953 /*
3954 * Validate the '$Volume' MFT record.
3955 */
3956 if (pVolInfoAttr->pAttrHdr->fNonResident)
3957 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume unnamed VOLUME_INFORMATION attribute is not resident!");
3958 else if ( pVolInfoAttr->cbResident != sizeof(NTFSATVOLUMEINFO)
3959 || pVolInfoAttr->cbValue != sizeof(NTFSATVOLUMEINFO))
3960 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3961 "$Volume VOLUME_INFORMATION attribute has the wrong size: cbValue=%#RX64, cbResident=%#RX32, expected %#x\n",
3962 pVolInfoAttr->cbValue, pVolInfoAttr->cbResident, sizeof(NTFSATVOLUMEINFO));
3963 else
3964 {
3965 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3966 if (!pFilenameAttr)
3967 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume has no FILENAME attribute!");
3968 else if (pFilenameAttr->pAttrHdr->fNonResident)
3969 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume FILENAME attribute is non-resident!");
3970 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
3971 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3972 "$Volume FILENAME attribute value size is too small: %#x",
3973 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3974 else
3975 {
3976 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3977 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3978 if ( pFilename->cwcFilename != 7
3979 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Volume", 7) != 0)
3980 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3981 "$Volume FILENAME isn't '$Volume': '%.*ls'",
3982 pFilename->cwcFilename, pFilename->wszFilename);
3983 else
3984 {
3985 /*
3986 * Look at the information.
3987 */
3988 PCNTFSATVOLUMEINFO pVolInfo;
3989 pVolInfo = (PCNTFSATVOLUMEINFO)((uint8_t *)pVolInfoAttr->pAttrHdr + pVolInfoAttr->pAttrHdr->u.Res.offValue);
3990 pThis->uNtfsVersion = RTFSNTFS_MAKE_VERSION(pVolInfo->uMajorVersion, pVolInfo->uMinorVersion);
3991 pThis->fVolumeFlags = RT_LE2H_U16(pVolInfo->fFlags);
3992 Log(("NTFS: Version %u.%u, flags=%#x\n", pVolInfo->uMajorVersion, pVolInfo->uMinorVersion, pThis->fVolumeFlags));
3993
3994 /* We're done, no need for special success return here though. */
3995 }
3996 }
3997 }
3998 }
3999 else
4000 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4001 "MFT record $Volume has no unnamed VOLUME_INFORMATION attribute!");
4002 rtFsNtfsCore_Release(pCore);
4003 }
4004 else
4005 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading $Volume MFT record");
4006 return rc;
4007}
4008
4009
4010/**
4011 * Loads, validates and setups the '$Mft' (NTFS_MFT_IDX_MFT) MFT entry.
4012 *
4013 * This is the first thing we do after we've checked out the boot sector and
4014 * extracted information from it, since everything else depends on us being able
4015 * to access the MFT data.
4016 *
4017 * @returns IPRT status code
4018 * @param pThis The NTFS volume instance. Will set pMftData.
4019 * @param pErrInfo Where to return additional error info.
4020 */
4021static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
4022{
4023 /*
4024 * Bootstrap the MFT data stream.
4025 */
4026 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, NTFS_MFT_IDX_MFT);
4027 AssertReturn(pRec, VERR_NO_MEMORY);
4028
4029#if 0 && defined(LOG_ENABLED)
4030 for (uint32_t i = 0; i < 128; i++)
4031 {
4032 uint64_t const offDisk = (pThis->uLcnMft << pThis->cClusterShift) + i * pThis->cbMftRecord;
4033 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
4034 if (RT_SUCCESS(rc))
4035 {
4036 pRec->TreeNode.Key = i;
4037 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
4038 pRec->TreeNode.Key = 0;
4039 }
4040 }
4041#endif
4042
4043 uint64_t const offDisk = pThis->uLcnMft << pThis->cClusterShift;
4044 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
4045 if (RT_SUCCESS(rc))
4046 {
4047 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, true, pErrInfo);
4048 if (RT_SUCCESS(rc))
4049 {
4050#ifdef LOG_ENABLED
4051 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
4052#endif
4053 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
4054 }
4055 if (RT_SUCCESS(rc))
4056 {
4057 pThis->pMftData = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_DATA);
4058 if (pThis->pMftData)
4059 {
4060 /*
4061 * Validate the '$Mft' MFT record.
4062 */
4063 if (!pThis->pMftData->pAttrHdr->fNonResident)
4064 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 unnamed DATA attribute is resident!");
4065 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) < pThis->cbMftRecord * 16U
4066 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) >= pThis->cbBacking)
4067 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4068 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
4069 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated));
4070 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) < pThis->cbMftRecord * 16U
4071 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) >= pThis->cbBacking)
4072 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4073 "MFT record #0 unnamed DATA attribute initialized size is out of range: %#RX64",
4074 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized));
4075 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) < pThis->cbMftRecord * 16U
4076 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) >= pThis->cbBacking)
4077 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4078 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
4079 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData));
4080 else if (pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit != 0)
4081 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4082 "MFT record #0 unnamed DATA attribute is compressed: %#x",
4083 pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit);
4084 else if (pThis->pMftData->Extents.cExtents == 0)
4085 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4086 "MFT record #0 unnamed DATA attribute has no data on the disk");
4087 else if (pThis->pMftData->Extents.paExtents[0].off != offDisk)
4088 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4089 "MFT record #0 unnamed DATA attribute has a bogus disk offset: %#RX64, expected %#RX64",
4090 pThis->pMftData->Extents.paExtents[0].off, offDisk);
4091 else if (!rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_BITMAP))
4092 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed BITMAP attribute!");
4093 else
4094 {
4095 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_FILENAME);
4096 if (!pFilenameAttr)
4097 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no FILENAME attribute!");
4098 else if (pFilenameAttr->pAttrHdr->fNonResident)
4099 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 FILENAME attribute is non-resident!");
4100 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[4]))
4101 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4102 "MFT record #0 FILENAME attribute value size is too small: %#x",
4103 pFilenameAttr->pAttrHdr->u.Res.cbValue);
4104 else
4105 {
4106 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
4107 + pFilenameAttr->pAttrHdr->u.Res.offValue);
4108 if ( pFilename->cwcFilename != 4
4109 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Mft", 4) != 0)
4110 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4111 "MFT record #0 FILENAME isn't '$Mft': '%.*ls'",
4112 pFilename->cwcFilename, pFilename->wszFilename);
4113 else
4114 {
4115 /*
4116 * Looks like we're good.
4117 */
4118 return VINF_SUCCESS;
4119 }
4120 }
4121 }
4122 pThis->pMftData = NULL;
4123 }
4124 else
4125 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!");
4126 }
4127 rtFsNtfsCore_Release(pRec->pCore);
4128 rtFsNtfsMftRec_Release(pRec, pThis);
4129 }
4130 else
4131 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #0");
4132 return rc;
4133}
4134
4135
4136/**
4137 * Loads the bootsector and parses it, copying values into the instance data.
4138 *
4139 * @returns IRPT status code.
4140 * @param pThis The instance data.
4141 * @param pvBuf The buffer.
4142 * @param cbBuf The buffer size.
4143 * @param pErrInfo Where to return additional error details.
4144 */
4145static int rtFsNtfsVolLoadAndParseBootsector(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
4146{
4147 AssertReturn(cbBuf >= sizeof(FATBOOTSECTOR), VERR_INTERNAL_ERROR_2);
4148
4149 /*
4150 * Read the boot sector and check that it makes sense for a NTFS volume.
4151 *
4152 * Note! There are two potential backup locations of the boot sector, however we
4153 * currently don't implement falling back on these on corruption/read errors.
4154 */
4155 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pvBuf;
4156 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, pBootSector, sizeof(*pBootSector), NULL);
4157 if (RT_FAILURE(rc))
4158 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading boot sector");
4159
4160 if (memcmp(pBootSector->achOemName, RT_STR_TUPLE(NTFS_OEM_ID_MAGIC)) != 0)
4161 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4162 "Not NTFS - OEM field mismatch: %.8Rhxs'", pBootSector->achOemName);
4163
4164 /* Check must-be-zero BPB fields. */
4165 if (pBootSector->Bpb.Ntfs.Bpb.cReservedSectors != 0)
4166 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cReservedSectors=%u",
4167 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cReservedSectors));
4168 if (pBootSector->Bpb.Ntfs.Bpb.cFats != 0)
4169 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cFats=%u",
4170 pBootSector->Bpb.Ntfs.Bpb.cFats);
4171 if (pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries != 0)
4172 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cMaxRootDirEntries=%u",
4173 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries));
4174 if (pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16 != 0)
4175 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cTotalSectors16=%u",
4176 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16));
4177 if (pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat != 0)
4178 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cSectorsPerFat=%u",
4179 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat));
4180
4181 /* Check other relevant BPB fields. */
4182 uint32_t cbSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cbSector);
4183 if ( cbSector != 512
4184 && cbSector != 1024
4185 && cbSector != 2048
4186 && cbSector != 4096)
4187 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - BPB.cbSector is ouf of range: %u", cbSector);
4188 pThis->cbSector = cbSector;
4189 Log2(("NTFS BPB: cbSector=%#x\n", cbSector));
4190
4191 uint32_t cClusterPerSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerCluster);
4192 if ( !RT_IS_POWER_OF_TWO(cClusterPerSector)
4193 || cClusterPerSector == 0)
4194 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4195 "Not NTFS - BPB.cCluster is ouf of range: %u", cClusterPerSector);
4196
4197 pThis->cbCluster = cClusterPerSector * cbSector;
4198 if (pThis->cbCluster > _64K)
4199 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4200 "cluster size exceeds 64KB: %#x", pThis->cbCluster);
4201 pThis->cClusterShift = ASMBitFirstSetU32(pThis->cbCluster) - 1;
4202 Log2(("NTFS BPB: cClusterPerSector=%#x => %#x bytes, %u shift\n", cClusterPerSector, pThis->cbCluster, pThis->cClusterShift));
4203 pThis->iMaxVirtualCluster = (uint64_t)INT64_MAX >> pThis->cClusterShift;
4204 Log2(("NTFS BPB: iMaxVirtualCluster=%#RX64\n", pThis->iMaxVirtualCluster));
4205
4206 /* NTFS BPB: cSectors. */
4207 uint64_t cSectors = RT_LE2H_U64(pBootSector->Bpb.Ntfs.cSectors);
4208 if (cSectors > pThis->cbBacking / pThis->cbSector)
4209 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4210 "NTFS sector count exceeds volume size: %#RX64 vs %#RX64",
4211 cSectors, pThis->cbBacking / pThis->cbSector);
4212 if (cSectors < 256)
4213 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "NTFS sector count too small: %#RX64", cSectors);
4214 pThis->cbVolume = cSectors * pThis->cbSector;
4215 pThis->cClusters = cSectors / cClusterPerSector;
4216 Log2(("NTFS BPB: cSectors=%#RX64 => %#xu bytes (%Rhcb) => cClusters=%#RX64\n",
4217 cSectors, pThis->cbVolume, pThis->cbVolume, pThis->cClusters));
4218
4219 /* NTFS BPB: MFT location. */
4220 uint64_t uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMft);
4221 if ( uLcn < 1
4222 || uLcn >= pThis->cClusters)
4223 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4224 "NTFS MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
4225 pThis->uLcnMft = uLcn;
4226 Log2(("NTFS BPB: uLcnMft=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
4227
4228 /* NTFS BPB: Mirror MFT location. */
4229 uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMftMirror);
4230 if ( uLcn < 1
4231 || uLcn >= pThis->cClusters)
4232 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4233 "NTFS mirror MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
4234 pThis->uLcnMftMirror = uLcn;
4235 Log2(("NTFS BPB: uLcnMftMirror=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
4236
4237 /* NTFS BPB: Size of MFT file record. */
4238 if (pBootSector->Bpb.Ntfs.cClustersPerMftRecord >= 0)
4239 {
4240 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord)
4241 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord == 0)
4242 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4243 "NTFS clusters-per-mft-record value is zero or not a power of two: %#x",
4244 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
4245 pThis->cbMftRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord << pThis->cClusterShift;
4246 Assert(pThis->cbMftRecord == pBootSector->Bpb.Ntfs.cClustersPerMftRecord * pThis->cbCluster);
4247 }
4248 else if ( pBootSector->Bpb.Ntfs.cClustersPerMftRecord < -20
4249 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord > -9)
4250 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4251 "NTFS clusters-per-mft-record is out of shift range: %d",
4252 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
4253 else
4254 pThis->cbMftRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
4255 Log2(("NTFS BPB: cbMftRecord=%#x\n", pThis->cbMftRecord));
4256 if ( pThis->cbMftRecord > _32K
4257 || pThis->cbMftRecord < 256)
4258 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4259 "Unsupported NTFS MFT record size: %#x", pThis->cbMftRecord);
4260
4261 /* NTFS BPB: Default index node size */
4262 if (pBootSector->Bpb.Ntfs.cClustersPerIndexNode >= 0)
4263 {
4264 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode)
4265 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode == 0)
4266 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4267 "NTFS default clusters-per-index-tree-node is zero or not a power of two: %#x",
4268 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
4269 pThis->cbDefaultIndexNode = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode << pThis->cClusterShift;
4270 Assert(pThis->cbDefaultIndexNode == pBootSector->Bpb.Ntfs.cClustersPerIndexNode * pThis->cbCluster);
4271 }
4272 else if ( pBootSector->Bpb.Ntfs.cClustersPerIndexNode < -32
4273 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode > -9)
4274 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4275 "NTFS default clusters-per-index-tree-node is out of shift range: %d",
4276 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
4277 else
4278 pThis->cbDefaultIndexNode = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
4279 Log2(("NTFS BPB: cbDefaultIndexNode=%#x\n", pThis->cbDefaultIndexNode));
4280
4281 pThis->uSerialNo = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uSerialNumber);
4282 Log2(("NTFS BPB: uSerialNo=%#x\n", pThis->uSerialNo));
4283
4284
4285 return VINF_SUCCESS;
4286}
4287
4288
4289RTDECL(int) RTFsNtfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fNtfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
4290{
4291 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
4292 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
4293 AssertReturn(!fNtfsFlags, VERR_INVALID_FLAGS);
4294
4295 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
4296 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
4297
4298 /*
4299 * Create a VFS instance and initialize the data so rtFsNtfsVol_Close works.
4300 */
4301 RTVFS hVfs;
4302 PRTFSNTFSVOL pThis;
4303 int rc = RTVfsNew(&g_rtFsNtfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
4304 if (RT_SUCCESS(rc))
4305 {
4306 pThis->hVfsBacking = hVfsFileIn;
4307 pThis->hVfsSelf = hVfs;
4308 pThis->fMntFlags = fMntFlags;
4309 pThis->fNtfsFlags = fNtfsFlags;
4310 RTListInit(&pThis->IdxNodeUnusedHead);
4311
4312 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
4313 if (RT_SUCCESS(rc))
4314 {
4315 void *pvBuf = RTMemTmpAlloc(_64K);
4316 if (pvBuf)
4317 {
4318 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo);
4319 if (RT_SUCCESS(rc))
4320 rc = rtFsNtfsVolLoadMft(pThis, pErrInfo);
4321 if (RT_SUCCESS(rc))
4322 rc = rtFsNtfsVolLoadVolumeInfo(pThis, pErrInfo);
4323 if (RT_SUCCESS(rc))
4324 rc = rtFsNtfsVolLoadBitmap(pThis, pErrInfo);
4325 if (RT_SUCCESS(rc))
4326 rc = rtFsNtfsVolLoadUpCase(pThis, pErrInfo);
4327 if (RT_SUCCESS(rc))
4328 rc = rtFsNtfsVolLoadRootDir(pThis, pErrInfo);
4329 RTMemTmpFree(pvBuf);
4330 if (RT_SUCCESS(rc))
4331 {
4332 *phVfs = hVfs;
4333 return VINF_SUCCESS;
4334 }
4335 }
4336 else
4337 rc = VERR_NO_TMP_MEMORY;
4338 }
4339
4340 RTVfsRelease(hVfs);
4341 *phVfs = NIL_RTVFS;
4342 }
4343 else
4344 RTVfsFileRelease(hVfsFileIn);
4345
4346 return rc;
4347}
4348
4349
4350/**
4351 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
4352 */
4353static DECLCALLBACK(int) rtVfsChainNtfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
4354 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
4355{
4356 RT_NOREF(pProviderReg);
4357
4358 /*
4359 * Basic checks.
4360 */
4361 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
4362 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
4363 if ( pElement->enmType != RTVFSOBJTYPE_VFS
4364 && pElement->enmType != RTVFSOBJTYPE_DIR)
4365 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
4366 if (pElement->cArgs > 1)
4367 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
4368
4369 /*
4370 * Parse the flag if present, save in pElement->uProvider.
4371 */
4372 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
4373 if (pElement->cArgs > 0)
4374 {
4375 const char *psz = pElement->paArgs[0].psz;
4376 if (*psz)
4377 {
4378 if (!strcmp(psz, "ro"))
4379 fReadOnly = true;
4380 else if (!strcmp(psz, "rw"))
4381 fReadOnly = false;
4382 else
4383 {
4384 *poffError = pElement->paArgs[0].offSpec;
4385 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
4386 }
4387 }
4388 }
4389
4390 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
4391 return VINF_SUCCESS;
4392}
4393
4394
4395/**
4396 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
4397 */
4398static DECLCALLBACK(int) rtVfsChainNtfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
4399 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
4400 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
4401{
4402 RT_NOREF(pProviderReg, pSpec, poffError);
4403
4404 int rc;
4405 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
4406 if (hVfsFileIn != NIL_RTVFSFILE)
4407 {
4408 RTVFS hVfs;
4409 rc = RTFsNtfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
4410 RTVfsFileRelease(hVfsFileIn);
4411 if (RT_SUCCESS(rc))
4412 {
4413 *phVfsObj = RTVfsObjFromVfs(hVfs);
4414 RTVfsRelease(hVfs);
4415 if (*phVfsObj != NIL_RTVFSOBJ)
4416 return VINF_SUCCESS;
4417 rc = VERR_VFS_CHAIN_CAST_FAILED;
4418 }
4419 }
4420 else
4421 rc = VERR_VFS_CHAIN_CAST_FAILED;
4422 return rc;
4423}
4424
4425
4426/**
4427 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
4428 */
4429static DECLCALLBACK(bool) rtVfsChainNtfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
4430 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
4431 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
4432{
4433 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
4434 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
4435 || !pReuseElement->paArgs[0].uProvider)
4436 return true;
4437 return false;
4438}
4439
4440
4441/** VFS chain element 'file'. */
4442static RTVFSCHAINELEMENTREG g_rtVfsChainNtfsVolReg =
4443{
4444 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
4445 /* fReserved = */ 0,
4446 /* pszName = */ "ntfs",
4447 /* ListEntry = */ { NULL, NULL },
4448 /* pszHelp = */ "Open a NTFS file system, requires a file object on the left side.\n"
4449 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
4450 /* pfnValidate = */ rtVfsChainNtfsVol_Validate,
4451 /* pfnInstantiate = */ rtVfsChainNtfsVol_Instantiate,
4452 /* pfnCanReuseElement = */ rtVfsChainNtfsVol_CanReuseElement,
4453 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
4454};
4455
4456RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainNtfsVolReg, rtVfsChainNtfsVolReg);
4457
Note: See TracBrowser for help on using the repository browser.

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