VirtualBox

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

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

iprt/ntfsvfs.cpp: Implemented file reading.

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