VirtualBox

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

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

iprt/rtFsNtfsAttr_Read: multi extent read fix.

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

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