VirtualBox

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

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

Runtime/ntfsvfs.cpp: Additional checks, rtFsNtfsVol_ParseMft() might fail early without having a core structure allocated

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 239.5 KB
Line 
1/* $Id: ntfsvfs.cpp 72585 2018-06-17 15:30:26Z 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 * @note Only modifying non-resident data is currently supported. No
1565 * shrinking or growing. Metadata is not modified.
1566 */
1567static int rtFsNtfsAttr_Write(PRTFSNTFSATTR pAttr, uint64_t off, void const *pvBuf, size_t cbToWrite)
1568{
1569 PRTFSNTFSVOL pVol = pAttr->pCore->pVol;
1570 int rc;
1571 if (!pAttr->pAttrHdr->fNonResident)
1572 {
1573 /*
1574 * The attribute is resident. Currently not supported.
1575 */
1576#if 0
1577 uint32_t cbAttrib = RT_LE2H_U32(pAttr->pAttrHdr->cbAttrib);
1578 uint32_t cbValue = RT_LE2H_U32(pAttr->pAttrHdr->u.Res.cbValue);
1579 uint16_t offValue = RT_LE2H_U16(pAttr->pAttrHdr->u.Res.offValue);
1580 if ( off < cbValue
1581 && cbToWrite <= cbValue
1582 && off + cbToWrite <= cbValue)
1583 {
1584 if (offValue <= cbAttrib)
1585 {
1586 cbAttrib -= offValue;
1587 if (off < cbAttrib)
1588 {
1589 /** @todo check if its possible to have cbValue larger than the attribute and
1590 * reading those extra bytes as zero. */
1591 if ( pAttr->offAttrHdrInMftRec + offValue + cbAttrib <= pVol->cbMftRecord
1592 && cbAttrib <= pVol->cbMftRecord)
1593 {
1594 size_t cbToCopy = cbAttrib - off;
1595 if (cbToCopy > cbToWrite)
1596 cbToCopy = cbToWrite;
1597 memcpy(pvBuf, (uint8_t *)pAttr->pAttrHdr + offValue, cbToCopy);
1598 pvBuf = (uint8_t *)pvBuf + cbToCopy;
1599 cbToWrite -= cbToCopy;
1600 rc = VINF_SUCCESS;
1601 }
1602 else
1603 {
1604 rc = VERR_VFS_BOGUS_OFFSET;
1605 Log(("rtFsNtfsAttr_Write: bad resident attribute!\n"));
1606 }
1607 }
1608 else
1609 rc = VINF_SUCCESS;
1610 }
1611 else
1612 rc = VERR_VFS_BOGUS_FORMAT;
1613 }
1614 else
1615 rc = VERR_EOF;
1616#else
1617 LogRel(("rtFsNtfsAttr_Write: file too small to write to.\n"));
1618 rc = VERR_INTERNAL_ERROR_3;
1619#endif
1620 }
1621 else if (pAttr->pAttrHdr->u.NonRes.uCompressionUnit == 0)
1622 {
1623 /*
1624 * Uncompressed non-resident attribute.
1625 * Note! We currently
1626 */
1627 uint64_t const cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated);
1628 if ( off >= cbAllocated
1629 || cbToWrite > cbAllocated
1630 || off + cbToWrite > cbAllocated)
1631 rc = VERR_EOF;
1632 else
1633 {
1634 rc = VINF_SUCCESS;
1635
1636 uint64_t const cbInitialized = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbInitialized);
1637 if ( off < cbInitialized
1638 && cbToWrite > 0)
1639 {
1640 /*
1641 * Locate the first extent. This is a tad complicated.
1642 *
1643 * We move off along as we traverse the extent tables, so that it is relative
1644 * to the start of the current extent.
1645 */
1646 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1647 uint32_t iExtent = 0;
1648 PRTFSNTFSATTRSUBREC pCurSub = NULL;
1649 for (;;)
1650 {
1651 if (off < pTable->cbData)
1652 {
1653 while ( iExtent < pTable->cExtents
1654 && off >= pTable->paExtents[iExtent].cbExtent)
1655 {
1656 off -= pTable->paExtents[iExtent].cbExtent;
1657 iExtent++;
1658 }
1659 AssertReturn(iExtent < pTable->cExtents, VERR_INTERNAL_ERROR_2);
1660 break;
1661 }
1662
1663 /* Next table. */
1664 off -= pTable->cbData;
1665 if (!pCurSub)
1666 pCurSub = pAttr->pSubRecHead;
1667 else
1668 pCurSub = pCurSub->pNext;
1669 if (!pCurSub)
1670 {
1671 iExtent = UINT32_MAX;
1672 break;
1673 }
1674 pTable = &pCurSub->Extents;
1675 iExtent = 0;
1676 }
1677
1678 /*
1679 * The write loop.
1680 */
1681 while (iExtent != UINT32_MAX)
1682 {
1683 uint64_t cbMaxWrite = pTable->paExtents[iExtent].cbExtent;
1684 Assert(off < cbMaxWrite);
1685 cbMaxWrite -= off;
1686 size_t const cbThisWrite = cbMaxWrite >= cbToWrite ? cbToWrite : (size_t)cbMaxWrite;
1687 if (pTable->paExtents[iExtent].off == UINT64_MAX)
1688 {
1689 if (!ASMMemIsZero(pvBuf, cbThisWrite))
1690 {
1691 LogRel(("rtFsNtfsAttr_Write: Unable to modify sparse section of file!\n"));
1692 rc = VERR_INTERNAL_ERROR_2;
1693 break;
1694 }
1695 }
1696 else
1697 {
1698 rc = RTVfsFileWriteAt(pVol->hVfsBacking, pTable->paExtents[iExtent].off + off, pvBuf, cbThisWrite, NULL);
1699 Log4(("NTFS: Volume write: @%#RX64 LB %#zx -> %Rrc\n", pTable->paExtents[iExtent].off + off, cbThisWrite, rc));
1700 if (RT_FAILURE(rc))
1701 break;
1702 }
1703 pvBuf = (uint8_t const *)pvBuf + cbThisWrite;
1704 cbToWrite -= cbThisWrite;
1705 if (!cbToWrite)
1706 break;
1707 off = 0;
1708
1709 /*
1710 * Advance to the next extent.
1711 */
1712 iExtent++;
1713 if (iExtent >= pTable->cExtents)
1714 {
1715 pCurSub = pCurSub ? pCurSub->pNext : pAttr->pSubRecHead;
1716 if (!pCurSub)
1717 break;
1718 pTable = &pCurSub->Extents;
1719 iExtent = 0;
1720 }
1721 }
1722 }
1723 }
1724 }
1725 else
1726 {
1727 LogRel(("rtFsNtfsAttr_Write: Compressed files are not supported\n"));
1728 rc = VERR_NOT_SUPPORTED;
1729 }
1730
1731 /*
1732 * Anything else beyond the end of what's stored/initialized?
1733 */
1734 if ( cbToWrite > 0
1735 && RT_SUCCESS(rc))
1736 {
1737 LogRel(("rtFsNtfsAttr_Write: Unable to modify sparse section (tail) of file!\n"));
1738 rc = VERR_INTERNAL_ERROR_2;
1739 }
1740
1741 return rc;
1742}
1743
1744
1745/**
1746 *
1747 * @returns
1748 * @param pRecHdr .
1749 * @param cbRec .
1750 * @param fRelaxedUsa .
1751 * @param pErrInfo .
1752 *
1753 * @see https://msdn.microsoft.com/en-us/library/bb470212%28v=vs.85%29.aspx
1754 */
1755static int rtFsNtfsRec_DoMultiSectorFixups(PNTFSRECHDR pRecHdr, uint32_t cbRec, bool fRelaxedUsa, PRTERRINFO pErrInfo)
1756{
1757 /*
1758 * Do sanity checking.
1759 */
1760 uint16_t offUpdateSeqArray = RT_LE2H_U16(pRecHdr->offUpdateSeqArray);
1761 uint16_t cUpdateSeqEntries = RT_LE2H_U16(pRecHdr->cUpdateSeqEntries);
1762 if ( !(cbRec & (NTFS_MULTI_SECTOR_STRIDE - 1))
1763 && !(offUpdateSeqArray & 1) /* two byte aligned */
1764 && cUpdateSeqEntries == 1 + cbRec / NTFS_MULTI_SECTOR_STRIDE
1765 && offUpdateSeqArray + (uint32_t)cUpdateSeqEntries * 2U < NTFS_MULTI_SECTOR_STRIDE - 2U)
1766 {
1767 uint16_t const *pauUsa = (uint16_t const *)((uint8_t *)pRecHdr + offUpdateSeqArray);
1768
1769 /*
1770 * The first update seqence array entry is the value stored at
1771 * the fixup locations at the end of the blocks. We read this
1772 * and check each of the blocks.
1773 */
1774 uint16_t const uCheck = *pauUsa++;
1775 cUpdateSeqEntries--;
1776 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1777 {
1778 uint16_t const *puBlockCheck = (uint16_t const *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1779 if (*puBlockCheck == uCheck)
1780 { /* likely */ }
1781 else if (!fRelaxedUsa)
1782 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1783 "Multisector transfer error: block #%u ends with %#x instead of %#x (fixup: %#x)",
1784 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) );
1785 else
1786 {
1787 Log(("NTFS: Multisector transfer warning: block #%u ends with %#x instead of %#x (fixup: %#x)\n",
1788 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) ));
1789 return VINF_SUCCESS;
1790 }
1791 }
1792
1793 /*
1794 * Apply the fixups.
1795 * Note! We advanced pauUsa above, so it's now at the fixup values.
1796 */
1797 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1798 {
1799 uint16_t *puFixup = (uint16_t *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1800 *puFixup = pauUsa[iBlock];
1801 }
1802 return VINF_SUCCESS;
1803 }
1804 if (fRelaxedUsa)
1805 {
1806 Log(("NTFS: Ignoring bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x\n",
1807 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries ));
1808 return VINF_SUCCESS;
1809 }
1810 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1811 "Bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x",
1812 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries);
1813}
1814
1815
1816/**
1817 * Allocate and parse an MFT record, returning a core object structure.
1818 *
1819 * @returns IPRT status code.
1820 * @param pThis The NTFS volume instance.
1821 * @param idxMft The index of the MTF record.
1822 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1823 * checks doesn't work or not present.
1824 * @param ppCore Where to return the core object structure.
1825 * @param pErrInfo Where to return error details. Optional.
1826 */
1827static int rtFsNtfsVol_NewCoreForMftIdx(PRTFSNTFSVOL pThis, uint64_t idxMft, bool fRelaxedUsa,
1828 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1829{
1830 *ppCore = NULL;
1831 Assert(pThis->pMftData);
1832 Assert(RTAvlU64Get(&pThis->MftRoot, idxMft) == NULL);
1833
1834 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, idxMft);
1835 AssertReturn(pRec, VERR_NO_MEMORY);
1836
1837 uint64_t offRec = idxMft * pThis->cbMftRecord;
1838 int rc = rtFsNtfsAttr_Read(pThis->pMftData, offRec, pRec->pbRec, pThis->cbMftRecord);
1839 if (RT_SUCCESS(rc))
1840 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, fRelaxedUsa, pErrInfo);
1841 if (RT_SUCCESS(rc))
1842 {
1843#ifdef LOG_ENABLED
1844 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
1845#endif
1846 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
1847 if (RT_SUCCESS(rc))
1848 {
1849 PRTFSNTFSCORE pCore = pRec->pCore;
1850 rtFsNtfsMftRec_Release(pRec, pThis);
1851
1852 /* Insert core into the cache list and update the cost, maybe trimming the cache. */
1853 RTListAppend(&pThis->CoreInUseHead, &pCore->ListEntry);
1854 pThis->cbCoreObjects += pCore->cbCost;
1855 if (pThis->cbCoreObjects > RTFSNTFS_MAX_CORE_CACHE_SIZE)
1856 rtFsNtfsIdxVol_TrimCoreObjectCache(pThis);
1857
1858 *ppCore = pCore;
1859 return VINF_SUCCESS;
1860 }
1861
1862 if (pRec->pCore)
1863 rtFsNtfsCore_Destroy(pRec->pCore);
1864 rtFsNtfsMftRec_Release(pRec, pThis);
1865 }
1866 return rc;
1867}
1868
1869
1870/**
1871 * Queries the core object struct for the given MFT record reference.
1872 *
1873 * Does caching.
1874 *
1875 * @returns IPRT status code.
1876 * @param pThis The NTFS volume instance.
1877 * @param pMftRef The MFT reference to get the corresponding core
1878 * for.
1879 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1880 * checks doesn't work or not present.
1881 * @param ppCore Where to return the referenced core object
1882 * structure.
1883 * @param pErrInfo Where to return error details. Optional.
1884 */
1885static int rtFsNtfsVol_QueryCoreForMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pMftRef , bool fRelaxedUsa,
1886 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1887{
1888 *ppCore = NULL;
1889 Assert(pThis->pMftData);
1890
1891 int rc;
1892 PRTFSNTFSMFTREC pMftRec = (PRTFSNTFSMFTREC)RTAvlU64Get(&pThis->MftRoot, NTFSMFTREF_GET_IDX(pMftRef));
1893 if (pMftRec)
1894 {
1895 /*
1896 * Cache hit. Check that the resure sequence number matches.
1897 * To be slightly paranoid, also check that it's a base MFT record and that it has been parsed already.
1898 */
1899 if (RT_LE2H_U16(pMftRec->pFileRec->uRecReuseSeqNo) == NTFSMFTREF_GET_SEQ(pMftRef))
1900 {
1901 if ( NTFSMFTREF_IS_ZERO(&pMftRec->pFileRec->BaseMftRec)
1902 && pMftRec->pCore)
1903 {
1904 rtFsNtfsCore_Retain(pMftRec->pCore);
1905 *ppCore = pMftRec->pCore;
1906 rc = VINF_SUCCESS;
1907 }
1908 else
1909 AssertLogRelMsgFailedStmt(("pCore=%p; BaseMftRec=%#RX64 sqn %#x\n", pMftRec->pCore,
1910 NTFSMFTREF_GET_IDX(&pMftRec->pFileRec->BaseMftRec),
1911 NTFSMFTREF_GET_SEQ(&pMftRec->pFileRec->BaseMftRec)),
1912 rc = VERR_INTERNAL_ERROR_3 );
1913 }
1914 else
1915 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1916 "Stale parent directory MFT reference: %#RX64 sqn %#x - current sqn %#x",
1917 NTFSMFTREF_GET_IDX(pMftRef), NTFSMFTREF_GET_SEQ(pMftRef),
1918 RT_LE2H_U16(pMftRec->pFileRec->uRecReuseSeqNo) );
1919 }
1920 else
1921 {
1922 /*
1923 * Load new and check that the reuse sequence number match.
1924 */
1925 rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFSMFTREF_GET_IDX(pMftRef), fRelaxedUsa, ppCore, pErrInfo);
1926 if (RT_SUCCESS(rc))
1927 {
1928 PRTFSNTFSCORE pCore = *ppCore;
1929 if (RT_LE2H_U16(pCore->pMftRec->pFileRec->uRecReuseSeqNo) == NTFSMFTREF_GET_SEQ(pMftRef))
1930 rc = VINF_SUCCESS;
1931 else
1932 {
1933 rtFsNtfsCore_Release(pCore);
1934 *ppCore = NULL;
1935 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1936 "Stale parent directory MFT reference: %#RX64 sqn %#x - current sqn %#x",
1937 NTFSMFTREF_GET_IDX(pMftRef), NTFSMFTREF_GET_SEQ(pMftRef),
1938 RT_LE2H_U16(pCore->pMftRec->pFileRec->uRecReuseSeqNo) );
1939 }
1940 }
1941 }
1942 return rc;
1943}
1944
1945
1946/**
1947 * Destroys a core structure.
1948 *
1949 * ASSUMES the caller has remove @a pThis from the list it's on and updated the
1950 * cbCoreObjects as necessary.
1951 *
1952 * @returns 0
1953 * @param pThis The core structure.
1954 */
1955static uint32_t rtFsNtfsCore_Destroy(PRTFSNTFSCORE pThis)
1956{
1957 /*
1958 * Free attributes.
1959 */
1960 PRTFSNTFSATTR pCurAttr;
1961 PRTFSNTFSATTR pNextAttr;
1962 RTListForEachSafe(&pThis->AttribHead, pCurAttr, pNextAttr, RTFSNTFSATTR, ListEntry)
1963 {
1964 PRTFSNTFSATTRSUBREC pSub = pCurAttr->pSubRecHead;
1965 while (pSub)
1966 {
1967 pCurAttr->pSubRecHead = pSub->pNext;
1968 RTMemFree(pSub->Extents.paExtents);
1969 pSub->Extents.paExtents = NULL;
1970 pSub->pAttrHdr = NULL;
1971 pSub->pNext = NULL;
1972 RTMemFree(pSub);
1973
1974 pSub = pCurAttr->pSubRecHead;
1975 }
1976
1977 pCurAttr->pCore = NULL;
1978 pCurAttr->pAttrHdr = NULL;
1979 RTMemFree(pCurAttr->Extents.paExtents);
1980 pCurAttr->Extents.paExtents = NULL;
1981 }
1982
1983 /*
1984 * Release the MFT chain.
1985 */
1986 PRTFSNTFSMFTREC pMftRec = pThis->pMftRec;
1987 while (pMftRec)
1988 {
1989 pThis->pMftRec = pMftRec->pNext;
1990 Assert(pMftRec->pCore == pThis);
1991 pMftRec->pNext = NULL;
1992 pMftRec->pCore = NULL;
1993 rtFsNtfsMftRec_Release(pMftRec, pThis->pVol);
1994
1995 pMftRec = pThis->pMftRec;
1996 }
1997
1998 RTMemFree(pThis);
1999
2000 return 0;
2001}
2002
2003
2004/**
2005 * Trims the core object cache down to RTFSNTFS_MAX_CORE_CACHE_SIZE.
2006 *
2007 * @param pThis The NTFS volume instance.
2008 */
2009static void rtFsNtfsIdxVol_TrimCoreObjectCache(PRTFSNTFSVOL pThis)
2010{
2011 while (pThis->cbCoreObjects > RTFSNTFS_MAX_CORE_CACHE_SIZE)
2012 {
2013 PRTFSNTFSCORE pCore = RTListRemoveFirst(&pThis->CoreUnusedHead, RTFSNTFSCORE, ListEntry);
2014 if (!pCore)
2015 break;
2016 pThis->cbCoreObjects -= pCore->cbCost;
2017 rtFsNtfsCore_Destroy(pCore);
2018 }
2019}
2020
2021
2022/**
2023 * Releases a refernece to a core structure, maybe destroying it.
2024 *
2025 * @returns New reference count.
2026 * @param pThis The core structure.
2027 */
2028static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis)
2029{
2030 if (pThis)
2031 {
2032 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2033 Assert(cRefs < 128);
2034 if (cRefs != 0)
2035 return cRefs;
2036
2037 /* Move from in-use list to unused list. Trim the cache if too big. */
2038 RTListNodeRemove(&pThis->ListEntry);
2039
2040 PRTFSNTFSVOL pVol = pThis->pVol;
2041 RTListAppend(&pVol->CoreUnusedHead, &pThis->ListEntry);
2042 if (pVol->cbCoreObjects > RTFSNTFS_MAX_CORE_CACHE_SIZE)
2043 rtFsNtfsIdxVol_TrimCoreObjectCache(pVol);
2044 }
2045 return 0;
2046}
2047
2048
2049/**
2050 * Retains a refernece to a core structure.
2051 *
2052 * @returns New reference count.
2053 * @param pThis The core structure.
2054 */
2055static uint32_t rtFsNtfsCore_Retain(PRTFSNTFSCORE pThis)
2056{
2057 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2058 if (cRefs == 1)
2059 {
2060 /* Move from unused list to in-use list. */
2061 RTListNodeRemove(&pThis->ListEntry);
2062 RTListAppend(&pThis->pVol->CoreInUseHead, &pThis->ListEntry);
2063 }
2064 Assert(cRefs < 128);
2065 return cRefs;
2066}
2067
2068
2069/**
2070 * Finds an unnamed attribute.
2071 *
2072 * @returns Pointer to the attribute structure if found, NULL if not.
2073 * @param pThis The core object structure to search.
2074 * @param uAttrType The attribute type to find.
2075 */
2076static PRTFSNTFSATTR rtFsNtfsCore_FindUnnamedAttribute(PRTFSNTFSCORE pThis, uint32_t uAttrType)
2077{
2078 PRTFSNTFSATTR pCurAttr;
2079 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
2080 {
2081 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
2082 if ( pAttrHdr->uAttrType == uAttrType
2083 && pAttrHdr->cwcName == 0)
2084 return pCurAttr;
2085 }
2086 return NULL;
2087}
2088
2089
2090/**
2091 * Finds a named attribute, case insensitive ASCII variant.
2092 *
2093 * @returns Pointer to the attribute structure if found, NULL if not.
2094 * @param pThis The core object structure to search.
2095 * @param uAttrType The attribute type to find.
2096 * @param pszAttrib The attribute name, predefined 7-bit ASCII name.
2097 * @param cchAttrib The length of the attribute.
2098 */
2099static PRTFSNTFSATTR rtFsNtfsCore_FindNamedAttributeAscii(PRTFSNTFSCORE pThis, uint32_t uAttrType,
2100 const char *pszAttrib, size_t cchAttrib)
2101{
2102 Assert(cchAttrib > 0);
2103 PRTFSNTFSATTR pCurAttr;
2104 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
2105 {
2106 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
2107 if ( pAttrHdr->uAttrType == uAttrType
2108 && pAttrHdr->cwcName == cchAttrib
2109 && RTUtf16NICmpAscii(NTFSATTRIBHDR_GET_NAME(pAttrHdr), pszAttrib, cchAttrib) == 0)
2110 return pCurAttr;
2111 }
2112 return NULL;
2113}
2114
2115
2116/**
2117 * This attribute conversion code is a slightly modified version of rtFsModeFromDos.
2118 *
2119 * @returns IPRT fmode mask.
2120 * @param fFileAttribs The NT file attributes.
2121 * @param pFilename The filename attribute structure, optional.
2122 * @param cbFilename The size of the filename attribute structure.
2123 */
2124static RTFMODE rtFsNtfsConvertFileattribsToMode(uint32_t fFileAttribs, PCNTFSATFILENAME pFilename, uint32_t cbFilename)
2125{
2126 RTFMODE fMode = (fFileAttribs << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT;
2127 if (fFileAttribs & NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT)
2128 fMode |= RTFS_DOS_DIRECTORY;
2129
2130 /* everything is readable. */
2131 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
2132 if (fMode & RTFS_DOS_DIRECTORY)
2133 /* directories are executable. */
2134 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
2135 else
2136 {
2137 fMode |= RTFS_TYPE_FILE;
2138 if ( pFilename
2139 && pFilename->cwcFilename >= 4
2140 && RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) <= cbFilename)
2141 {
2142 PCRTUTF16 pwcExt = &pFilename->wszFilename[pFilename->cwcFilename - 4];
2143 if ( *pwcExt++ == '.')
2144 {
2145 /* check for executable extension. */
2146 if ( (unsigned)pwcExt[0] < 0x7fU
2147 && (unsigned)pwcExt[1] < 0x7fU
2148 && (unsigned)pwcExt[2] < 0x7fU)
2149 {
2150 char szExt[4];
2151 szExt[0] = RT_C_TO_LOWER(pwcExt[0]);
2152 szExt[1] = RT_C_TO_LOWER(pwcExt[1]);
2153 szExt[2] = RT_C_TO_LOWER(pwcExt[2]);
2154 szExt[3] = '\0';
2155 if ( !memcmp(szExt, "exe", 4)
2156 || !memcmp(szExt, "bat", 4)
2157 || !memcmp(szExt, "com", 4)
2158 || !memcmp(szExt, "cmd", 4)
2159 || !memcmp(szExt, "btm", 4)
2160 )
2161 fMode |= RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
2162 }
2163 }
2164 }
2165 }
2166
2167 /* Is it really a symbolic link? */
2168 if ( (fMode & RTFS_DOS_NT_REPARSE_POINT)
2169 && pFilename
2170 && pFilename->u.uReparseTag == RTFSMODE_SYMLINK_REPARSE_TAG)
2171 fMode = (fMode & ~RTFS_TYPE_MASK) | RTFS_TYPE_SYMLINK;
2172
2173 /* writable? */
2174 if (!(fMode & RTFS_DOS_READONLY))
2175 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
2176
2177 return fMode;
2178}
2179
2180
2181/**
2182 * Worker for various QueryInfo methods.
2183 *
2184 * @returns IPRT status code.
2185 * @param pThis The core object structure to return info for.
2186 * @param pAttr The attribute that's being presented. Take the
2187 * allocation and timestamp info from it, if
2188 * non-resident.
2189 * @param pObjInfo Where to return object info.
2190 * @param enmAddAttr What additional info to return.
2191 */
2192static int rtFsNtfsCore_QueryInfo(PRTFSNTFSCORE pThis, PRTFSNTFSATTR pAttr, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2193{
2194 /*
2195 * Wipe the structure and fill in common dummy value.
2196 */
2197 RT_ZERO(*pObjInfo);
2198 switch (enmAddAttr)
2199 {
2200 case RTFSOBJATTRADD_UNIX:
2201 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
2202 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
2203 pObjInfo->Attr.u.Unix.cHardlinks = 1;
2204 //pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
2205 pObjInfo->Attr.u.Unix.INodeId = pThis->pMftRec->TreeNode.Key;
2206 //pObjInfo->Attr.u.Unix.fFlags = 0;
2207 //pObjInfo->Attr.u.Unix.GenerationId = 0;
2208 //pObjInfo->Attr.u.Unix.Device = 0;
2209 break;
2210
2211 case RTFSOBJATTRADD_UNIX_OWNER:
2212 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
2213 break;
2214
2215 case RTFSOBJATTRADD_UNIX_GROUP:
2216 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
2217 break;
2218
2219 default:
2220 break;
2221 }
2222
2223 /*
2224 * Look for the standard information attribute and use that as basis.
2225 */
2226 uint32_t fFileAttribs;
2227 PRTFSNTFSATTR pStdInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pThis, NTFS_AT_STANDARD_INFORMATION);
2228 if ( pStdInfoAttr
2229 && pStdInfoAttr->cbResident >= sizeof(NTFSATSTDINFO) )
2230 {
2231 Assert(!pStdInfoAttr->pAttrHdr->fNonResident);
2232 PCNTFSATSTDINFO pStdInfo = (PCNTFSATSTDINFO)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pStdInfoAttr->pAttrHdr);
2233 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, RT_LE2H_U64(pStdInfo->iCreationTime));
2234 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, RT_LE2H_U64(pStdInfo->iLastDataModTime));
2235 RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, RT_LE2H_U64(pStdInfo->iLastMftModTime));
2236 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, RT_LE2H_U64(pStdInfo->iLastAccessTime));
2237 if (enmAddAttr == RTFSOBJATTRADD_UNIX)
2238 {
2239 pObjInfo->Attr.u.Unix.uid = pStdInfo->idOwner;
2240 pObjInfo->Attr.u.Unix.GenerationId = pStdInfo->uFileVersion;
2241 }
2242 else if (enmAddAttr == RTFSOBJATTRADD_UNIX_OWNER)
2243 pObjInfo->Attr.u.UnixOwner.uid = pStdInfo->idOwner;
2244 fFileAttribs = pStdInfo->fFileAttribs;
2245 }
2246 else
2247 {
2248 /** @todo check out the filename record? */
2249 switch (pAttr->pAttrHdr->uAttrType)
2250 {
2251 default:
2252 AssertFailed();
2253 RT_FALL_THRU();
2254 case NTFS_AT_DATA:
2255 fFileAttribs = NTFS_FA_NORMAL;
2256 break;
2257
2258 case NTFS_AT_INDEX_ROOT:
2259 case NTFS_AT_INDEX_ALLOCATION:
2260 fFileAttribs = NTFS_FA_DIRECTORY;
2261 break;
2262 }
2263 }
2264
2265 /*
2266 * Take the allocation info from the destilled attribute data.
2267 */
2268 pObjInfo->cbObject = pAttr->cbValue;
2269 pObjInfo->cbAllocated = pAttr->Extents.cbData;
2270 if ( pAttr->pAttrHdr->fNonResident
2271 && (int64_t)pObjInfo->cbAllocated < (int64_t)RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated))
2272 pObjInfo->cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated);
2273
2274 /*
2275 * See if we can find a filename record before we try convert the file attributes to mode.
2276 */
2277 PCNTFSATFILENAME pFilename = NULL;
2278 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pThis, NTFS_AT_FILENAME);
2279 if ( pFilenameAttr
2280 && pFilenameAttr->cbResident >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename) )
2281 {
2282 Assert(!pFilenameAttr->pAttrHdr->fNonResident);
2283 pFilename = (PCNTFSATFILENAME)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pFilenameAttr->pAttrHdr);
2284 if (pStdInfoAttr)
2285 fFileAttribs |= pFilename->fFileAttribs;
2286 else
2287 fFileAttribs = pFilename->fFileAttribs;
2288 }
2289
2290 /*
2291 * Convert attribs to file mode flags.
2292 */
2293 pObjInfo->Attr.fMode = rtFsNtfsConvertFileattribsToMode(fFileAttribs, pFilename,
2294 pFilenameAttr ? pFilenameAttr->cbResident : 0);
2295
2296 return VINF_SUCCESS;
2297}
2298
2299
2300
2301
2302/*
2303 *
2304 * File operations.
2305 * File operations.
2306 * File operations.
2307 *
2308 */
2309
2310/**
2311 * Releases a reference to a shared NTFS file structure.
2312 *
2313 * @returns New reference count.
2314 * @param pShared The shared NTFS file structure.
2315 */
2316static uint32_t rtFsNtfsFileShrd_Release(PRTFSNTFSFILESHRD pShared)
2317{
2318 uint32_t cRefs = ASMAtomicDecU32(&pShared->cRefs);
2319 Assert(cRefs < 64);
2320 if (cRefs == 0)
2321 {
2322 LogFlow(("rtFsNtfsFileShrd_Release(%p): Destroying it\n", pShared));
2323 Assert(pShared->pData->uObj.pSharedFile == pShared);
2324 pShared->pData->uObj.pSharedFile = NULL;
2325 rtFsNtfsCore_Release(pShared->pData->pCore);
2326 pShared->pData = NULL;
2327 RTMemFree(pShared);
2328 }
2329 return cRefs;
2330}
2331
2332
2333/**
2334 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2335 */
2336static DECLCALLBACK(int) rtFsNtfsFile_Close(void *pvThis)
2337{
2338 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2339 LogFlow(("rtFsNtfsFile_Close(%p/%p)\n", pThis, pThis->pShared));
2340
2341 PRTFSNTFSFILESHRD pShared = pThis->pShared;
2342 pThis->pShared = NULL;
2343 if (pShared)
2344 rtFsNtfsFileShrd_Release(pShared);
2345 return VINF_SUCCESS;
2346}
2347
2348
2349/**
2350 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2351 */
2352static DECLCALLBACK(int) rtFsNtfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2353{
2354 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2355 PRTFSNTFSATTR pDataAttr = pThis->pShared->pData;
2356 return rtFsNtfsCore_QueryInfo(pDataAttr->pCore, pDataAttr, pObjInfo, enmAddAttr);
2357}
2358
2359
2360/**
2361 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
2362 */
2363static DECLCALLBACK(int) rtFsNtfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
2364{
2365 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2366 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
2367 RT_NOREF(fBlocking);
2368
2369 if (off == -1)
2370 off = pThis->offFile;
2371 else
2372 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
2373
2374 int rc;
2375 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
2376 if (!pcbRead)
2377 {
2378 rc = rtFsNtfsAttr_Read(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbRead);
2379 if (RT_SUCCESS(rc))
2380 pThis->offFile = off + cbRead;
2381 Log6(("rtFsNtfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
2382 }
2383 else
2384 {
2385 PRTFSNTFSATTR pDataAttr = pThis->pShared->pData;
2386 if ((uint64_t)off >= pDataAttr->cbValue)
2387 {
2388 *pcbRead = 0;
2389 rc = VINF_EOF;
2390 }
2391 else
2392 {
2393 if ((uint64_t)off + cbRead <= pDataAttr->cbValue)
2394 rc = rtFsNtfsAttr_Read(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbRead);
2395 else
2396 {
2397 /* Return VINF_EOF if beyond end-of-file. */
2398 cbRead = (size_t)(pDataAttr->cbValue - (uint64_t)off);
2399 rc = rtFsNtfsAttr_Read(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbRead);
2400 if (RT_SUCCESS(rc))
2401 rc = VINF_EOF;
2402 }
2403 if (RT_SUCCESS(rc))
2404 {
2405 pThis->offFile = off + cbRead;
2406 *pcbRead = cbRead;
2407 }
2408 else
2409 *pcbRead = 0;
2410 }
2411 Log6(("rtFsNtfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
2412 }
2413
2414 return rc;
2415}
2416
2417
2418/**
2419 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
2420 */
2421static DECLCALLBACK(int) rtFsNtfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
2422{
2423 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2424 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
2425 RT_NOREF(fBlocking);
2426
2427 if (off == -1)
2428 off = pThis->offFile;
2429 else
2430 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
2431
2432 int rc;
2433 PRTFSNTFSATTR pDataAttr = pThis->pShared->pData;
2434 size_t cbToWrite = pSgBuf->paSegs[0].cbSeg;
2435 if ((uint64_t)off + cbToWrite <= pDataAttr->cbValue)
2436 {
2437 rc = rtFsNtfsAttr_Write(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbToWrite);
2438 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> %Rrc\n", off, cbToWrite, rc));
2439 if (RT_SUCCESS(rc))
2440 pThis->offFile = off + cbToWrite;
2441 if (pcbWritten)
2442 *pcbWritten = RT_SUCCESS(rc) ? cbToWrite : 0;
2443 }
2444 else if ((uint64_t)off < pDataAttr->cbValue)
2445 {
2446 size_t cbWritten = pDataAttr->cbValue - off;
2447 rc = rtFsNtfsAttr_Write(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbWritten);
2448 if (RT_SUCCESS(rc))
2449 {
2450 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> VERR_EOF [EOF: %#RX64, Written: %#zx]\n",
2451 off, cbToWrite, pDataAttr->cbValue, cbWritten));
2452 pThis->offFile = off + cbWritten;
2453 if (pcbWritten)
2454 *pcbWritten = cbWritten;
2455 rc = VERR_EOF;
2456 }
2457 else
2458 {
2459 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> %Rrc [EOF: %#RX64]\n", off, cbToWrite, rc, pDataAttr->cbValue));
2460 if (pcbWritten)
2461 *pcbWritten = 0;
2462 }
2463 }
2464 else
2465 {
2466 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> VERR_EOF [EOF: %#RX64]\n", off, cbToWrite, pDataAttr->cbValue));
2467 rc = VERR_EOF;
2468 if (pcbWritten)
2469 *pcbWritten = 0;
2470 }
2471
2472 return rc;
2473}
2474
2475
2476/**
2477 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
2478 */
2479static DECLCALLBACK(int) rtFsNtfsFile_Flush(void *pvThis)
2480{
2481 RT_NOREF(pvThis);
2482 return VINF_SUCCESS;
2483}
2484
2485
2486/**
2487 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2488 */
2489static DECLCALLBACK(int) rtFsNtfsFile_Tell(void *pvThis, PRTFOFF poffActual)
2490{
2491 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2492 *poffActual = pThis->offFile;
2493 return VINF_SUCCESS;
2494}
2495
2496
2497/**
2498 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2499 */
2500static DECLCALLBACK(int) rtFsNtfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2501{
2502 RT_NOREF(pvThis, fMode, fMask);
2503 return VERR_WRITE_PROTECT;
2504}
2505
2506
2507/**
2508 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2509 */
2510static DECLCALLBACK(int) rtFsNtfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2511 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2512{
2513 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2514 return VERR_WRITE_PROTECT;
2515}
2516
2517
2518/**
2519 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2520 */
2521static DECLCALLBACK(int) rtFsNtfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2522{
2523 RT_NOREF(pvThis, uid, gid);
2524 return VERR_WRITE_PROTECT;
2525}
2526
2527
2528/**
2529 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2530 */
2531static DECLCALLBACK(int) rtFsNtfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2532{
2533 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2534 RTFOFF offNew;
2535 switch (uMethod)
2536 {
2537 case RTFILE_SEEK_BEGIN:
2538 offNew = offSeek;
2539 break;
2540 case RTFILE_SEEK_END:
2541 offNew = (RTFOFF)pThis->pShared->pData->cbValue + offSeek;
2542 break;
2543 case RTFILE_SEEK_CURRENT:
2544 offNew = (RTFOFF)pThis->offFile + offSeek;
2545 break;
2546 default:
2547 return VERR_INVALID_PARAMETER;
2548 }
2549 if (offNew >= 0)
2550 {
2551 pThis->offFile = offNew;
2552 *poffActual = offNew;
2553 return VINF_SUCCESS;
2554 }
2555 return VERR_NEGATIVE_SEEK;
2556}
2557
2558
2559/**
2560 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2561 */
2562static DECLCALLBACK(int) rtFsNtfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2563{
2564 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2565 *pcbFile = pThis->pShared->pData->cbValue;
2566 return VINF_SUCCESS;
2567}
2568
2569
2570/**
2571 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
2572 */
2573static DECLCALLBACK(int) rtFsNtfsFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
2574{
2575 NOREF(pvThis); NOREF(cbFile); NOREF(fFlags);
2576 return VERR_NOT_IMPLEMENTED;
2577}
2578
2579
2580/**
2581 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
2582 */
2583static DECLCALLBACK(int) rtFsNtfsFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
2584{
2585 RT_NOREF(pvThis);
2586 *pcbMax = INT64_MAX;
2587 return VINF_SUCCESS;
2588}
2589
2590
2591/**
2592 * NTFS file operations.
2593 */
2594static const RTVFSFILEOPS g_rtFsNtfsFileOps =
2595{
2596 { /* Stream */
2597 { /* Obj */
2598 RTVFSOBJOPS_VERSION,
2599 RTVFSOBJTYPE_FILE,
2600 "NTFS File",
2601 rtFsNtfsFile_Close,
2602 rtFsNtfsFile_QueryInfo,
2603 RTVFSOBJOPS_VERSION
2604 },
2605 RTVFSIOSTREAMOPS_VERSION,
2606 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2607 rtFsNtfsFile_Read,
2608 rtFsNtfsFile_Write,
2609 rtFsNtfsFile_Flush,
2610 NULL /*PollOne*/,
2611 rtFsNtfsFile_Tell,
2612 NULL /*pfnSkip*/,
2613 NULL /*pfnZeroFill*/,
2614 RTVFSIOSTREAMOPS_VERSION,
2615 },
2616 RTVFSFILEOPS_VERSION,
2617 0,
2618 { /* ObjSet */
2619 RTVFSOBJSETOPS_VERSION,
2620 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
2621 rtFsNtfsFile_SetMode,
2622 rtFsNtfsFile_SetTimes,
2623 rtFsNtfsFile_SetOwner,
2624 RTVFSOBJSETOPS_VERSION
2625 },
2626 rtFsNtfsFile_Seek,
2627 rtFsNtfsFile_QuerySize,
2628 rtFsNtfsFile_SetSize,
2629 rtFsNtfsFile_QueryMaxSize,
2630 RTVFSFILEOPS_VERSION
2631};
2632
2633
2634static int rtFsNtfsVol_NewFile(PRTFSNTFSVOL pThis, uint64_t fOpen, PCNTFSIDXENTRYHDR pEntryHdr, const char *pszStreamName,
2635 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
2636{
2637 /*
2638 * Get the core structure for the MFT record and check that it's a directory we've got.
2639 */
2640 PRTFSNTFSCORE pCore;
2641 int rc = rtFsNtfsVol_QueryCoreForMftRef(pThis, &pEntryHdr->u.FileMftRec, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2642 if (RT_SUCCESS(rc))
2643 {
2644 if (!(pCore->pMftRec->pFileRec->fFlags & NTFSRECFILE_F_DIRECTORY))
2645 {
2646 /*
2647 * Locate the data attribute.
2648 */
2649 PRTFSNTFSATTR pDataAttr;
2650 if (pszStreamName == NULL)
2651 {
2652 pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
2653 if (pDataAttr)
2654 rc = VINF_SUCCESS;
2655 else
2656 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: no unamed data stream", pszWhat);
2657 }
2658 else
2659 {
2660 NOREF(pszStreamName);
2661 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_IMPLEMENTED, "%s: named data streams not implemented yet", pszWhat);
2662 pDataAttr = NULL;
2663 }
2664 if (RT_SUCCESS(rc))
2665 {
2666 /*
2667 * Get a referenced shared file structure, creating it if necessary.
2668 */
2669 PRTFSNTFSFILESHRD pShared = pDataAttr->uObj.pSharedFile;
2670 if (pShared)
2671 {
2672 uint32_t cRefs = ASMAtomicIncU32(&pShared->cRefs);
2673 Assert(cRefs > 1); NOREF(cRefs);
2674 }
2675 else
2676 {
2677 pShared = (PRTFSNTFSFILESHRD)RTMemAllocZ(sizeof(*pShared));
2678 if (pShared)
2679 {
2680 pShared->cRefs = 1;
2681 pShared->pData = pDataAttr;
2682 rtFsNtfsCore_Retain(pCore);
2683 pDataAttr->uObj.pSharedFile = pShared;
2684 }
2685 }
2686 if (pShared)
2687 {
2688 /*
2689 * Create the open file instance.
2690 */
2691 PRTFSNTFSFILE pNewFile;
2692 rc = RTVfsNewFile(&g_rtFsNtfsFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
2693 phVfsFile, (void **)&pNewFile);
2694 if (RT_SUCCESS(rc))
2695 {
2696 pNewFile->offFile = 0;
2697 pNewFile->pShared = pShared;
2698 rtFsNtfsCore_Release(pCore);
2699 return VINF_SUCCESS;
2700 }
2701
2702 rtFsNtfsFileShrd_Release(pShared);
2703 }
2704 else
2705 rc = VERR_NO_MEMORY;
2706 }
2707 }
2708 else
2709 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fFlags=%#x", pszWhat, pCore->pMftRec->pFileRec->fFlags);
2710 rtFsNtfsCore_Release(pCore);
2711 }
2712 return rc;
2713}
2714
2715
2716
2717/*
2718 *
2719 * NTFS directory code.
2720 * NTFS directory code.
2721 * NTFS directory code.
2722 *
2723 */
2724
2725#ifdef LOG_ENABLED
2726
2727/**
2728 * Logs an index header and all the entries.
2729 *
2730 * @param pIdxHdr The index header.
2731 * @param cbIndex The number of valid bytes starting with the header.
2732 * @param offIndex The offset of the index header into the parent
2733 * structure.
2734 * @param pszPrefix The log prefix.
2735 * @param uIdxType The index type.
2736 */
2737static void rtFsNtfsVol_LogIndexHdrAndEntries(PCNTFSINDEXHDR pIdxHdr, uint32_t cbIndex, uint32_t offIndex,
2738 const char *pszPrefix, uint32_t uIdxType)
2739{
2740 if (!LogIs2Enabled())
2741 return;
2742
2743 /*
2744 * Do the header.
2745 */
2746 if (cbIndex <= sizeof(*pIdxHdr))
2747 {
2748 Log2(("NTFS: %s: Error! Not enough space for the index header! cbIndex=%#x, index head needs %#x\n",
2749 pszPrefix, cbIndex, sizeof(*pIdxHdr)));
2750 return;
2751 }
2752
2753 Log2(("NTFS: %s: offFirstEntry %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->offFirstEntry),
2754 RT_LE2H_U32(pIdxHdr->offFirstEntry) >= cbIndex ? " !out-of-bounds!" : ""));
2755 Log2(("NTFS: %s: cbUsed %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbUsed),
2756 RT_LE2H_U32(pIdxHdr->cbUsed) > cbIndex ? " !out-of-bounds!" : ""));
2757 Log2(("NTFS: %s: cbAllocated %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbAllocated),
2758 RT_LE2H_U32(pIdxHdr->cbAllocated) > cbIndex ? " !out-of-bounds!" : ""));
2759 Log2(("NTFS: %s: fFlags %#x (%s%s)\n", pszPrefix, pIdxHdr->fFlags,
2760 pIdxHdr->fFlags & NTFSINDEXHDR_F_INTERNAL ? "internal" : "leaf",
2761 pIdxHdr->fFlags & ~NTFSINDEXHDR_F_INTERNAL ? " !!unknown-flags!!" : ""));
2762 if (pIdxHdr->abReserved[0]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[0]));
2763 if (pIdxHdr->abReserved[1]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[1]));
2764 if (pIdxHdr->abReserved[2]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[2]));
2765
2766 /*
2767 * The entries.
2768 */
2769 bool fSeenEnd = false;
2770 uint32_t iEntry = 0;
2771 uint32_t offCurEntry = RT_LE2H_U32(pIdxHdr->offFirstEntry);
2772 while (offCurEntry < cbIndex)
2773 {
2774 if (offCurEntry + sizeof(NTFSIDXENTRYHDR) > cbIndex)
2775 {
2776 Log2(("NTFS: Entry[%#04x]: Out of bounds: %#x LB %#x, max %#x\n",
2777 iEntry, offCurEntry, sizeof(NTFSIDXENTRYHDR), cbIndex));
2778 break;
2779 }
2780 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIdxHdr + offCurEntry);
2781 Log2(("NTFS: [%#04x]: @%#05x/@%#05x cbEntry=%#x cbKey=%#x fFlags=%#x (%s%s%s)\n",
2782 iEntry, offCurEntry, offCurEntry + offIndex, RT_LE2H_U16(pEntryHdr->cbEntry), RT_LE2H_U16(pEntryHdr->cbKey),
2783 RT_LE2H_U16(pEntryHdr->fFlags),
2784 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? "internal" : "leaf",
2785 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END ? " end" : "",
2786 pEntryHdr->fFlags & ~(NTFSIDXENTRYHDR_F_INTERNAL | NTFSIDXENTRYHDR_F_END) ? " !unknown!" : ""));
2787 if (uIdxType == NTFSATINDEXROOT_TYPE_DIR)
2788 Log2(("NTFS: FileMftRec %#RX64 sqn %#x\n",
2789 NTFSMFTREF_GET_IDX(&pEntryHdr->u.FileMftRec), NTFSMFTREF_GET_SEQ(&pEntryHdr->u.FileMftRec) ));
2790 else
2791 Log2(("NTFS: offData=%#x cbData=%#x uReserved=%#x\n",
2792 RT_LE2H_U16(pEntryHdr->u.View.offData), RT_LE2H_U16(pEntryHdr->u.View.cbData),
2793 RT_LE2H_U32(pEntryHdr->u.View.uReserved) ));
2794 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2795 Log2(("NTFS: Subnode=%#RX64\n", RT_LE2H_U64(NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr)) ));
2796
2797 if ( RT_LE2H_U16(pEntryHdr->cbKey) >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename)
2798 && uIdxType == NTFSATINDEXROOT_TYPE_DIR)
2799 {
2800 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2801 RTTIMESPEC Spec;
2802 char sz[80];
2803 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iCreationTime),
2804 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iCreationTime)), sz, sizeof(sz)) ));
2805 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastDataModTime),
2806 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastDataModTime)), sz, sizeof(sz)) ));
2807 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastMftModTime),
2808 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastMftModTime)), sz, sizeof(sz)) ));
2809 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastAccessTime),
2810 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastAccessTime)), sz, sizeof(sz)) ));
2811 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
2812 RT_LE2H_U64(pFilename->cbAllocated), RT_LE2H_U64(pFilename->cbAllocated)));
2813 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
2814 RT_LE2H_U64(pFilename->cbData), RT_LE2H_U64(pFilename->cbData)));
2815 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pFilename->fFileAttribs) ));
2816 if (RT_LE2H_U32(pFilename->fFileAttribs) & NTFS_FA_REPARSE_POINT)
2817 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pFilename->u.uReparseTag) ));
2818 else
2819 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pFilename->u.cbPackedEas) ));
2820 Log2(("NTFS: cwcFilename %#x\n", pFilename->cwcFilename));
2821 Log2(("NTFS: fFilenameType %#x\n", pFilename->fFilenameType));
2822 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) <= RT_LE2H_U16(pEntryHdr->cbKey))
2823 Log2(("NTFS: wszFilename '%.*ls'\n", pFilename->cwcFilename, pFilename->wszFilename ));
2824 else
2825 Log2(("NTFS: Error! Truncated filename!!\n"));
2826 }
2827
2828
2829 /* next */
2830 iEntry++;
2831 offCurEntry += RT_LE2H_U16(pEntryHdr->cbEntry);
2832 fSeenEnd = RT_BOOL(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END);
2833 if (fSeenEnd || RT_LE2H_U16(pEntryHdr->cbEntry) < sizeof(*pEntryHdr))
2834 break;
2835 }
2836 if (!fSeenEnd)
2837 Log2(("NTFS: %s: Warning! Missing NTFSIDXENTRYHDR_F_END node!\n", pszPrefix));
2838}
2839
2840# if 0 /* unused */
2841static void rtFsNtfsVol_LogIndexNode(PCNTFSATINDEXALLOC pIdxNode, uint32_t cbIdxNode, uint32_t uType)
2842{
2843 if (!LogIs2Enabled())
2844 return;
2845 if (cbIdxNode < sizeof(*pIdxNode))
2846 Log2(("NTFS: Index Node: Error! Too small! cbIdxNode=%#x, index node needs %#x\n", cbIdxNode, sizeof(*pIdxNode)));
2847 else
2848 {
2849 Log2(("NTFS: Index Node: uMagic %#x\n", RT_LE2H_U32(pIdxNode->RecHdr.uMagic)));
2850 Log2(("NTFS: Index Node: UpdateSeqArray %#x L %#x\n",
2851 RT_LE2H_U16(pIdxNode->RecHdr.offUpdateSeqArray), RT_LE2H_U16(pIdxNode->RecHdr.cUpdateSeqEntries) ));
2852 Log2(("NTFS: Index Node: uLsn %#RX64\n", RT_LE2H_U64(pIdxNode->uLsn) ));
2853 Log2(("NTFS: Index Node: iSelfAddress %#RX64\n", RT_LE2H_U64(pIdxNode->iSelfAddress) ));
2854 if (pIdxNode->RecHdr.uMagic == NTFSREC_MAGIC_INDEX_ALLOC)
2855 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxNode->Hdr, cbIdxNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2856 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "Index Node Hdr", uType);
2857 else
2858 Log2(("NTFS: Index Node: !Error! Invalid magic!\n"));
2859 }
2860}
2861# endif
2862
2863/**
2864 * Logs a index root structure and what follows (index header + entries).
2865 *
2866 * @param pIdxRoot The index root.
2867 * @param cbIdxRoot Number of valid bytes starting with @a pIdxRoot.
2868 */
2869static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot)
2870{
2871 if (!LogIs2Enabled())
2872 return;
2873 if (cbIdxRoot < sizeof(*pIdxRoot))
2874 Log2(("NTFS: Index Root: Error! Too small! cbIndex=%#x, index head needs %#x\n", cbIdxRoot, sizeof(*pIdxRoot)));
2875 else
2876 {
2877 Log2(("NTFS: Index Root: cbIdxRoot %#x\n", cbIdxRoot));
2878 Log2(("NTFS: Index Root: uType %#x %s\n", RT_LE2H_U32(pIdxRoot->uType),
2879 pIdxRoot->uType == NTFSATINDEXROOT_TYPE_VIEW ? "view"
2880 : pIdxRoot->uType == NTFSATINDEXROOT_TYPE_DIR ? "directory" : "!unknown!"));
2881 Log2(("NTFS: Index Root: uCollationRules %#x %s\n", RT_LE2H_U32(pIdxRoot->uCollationRules),
2882 pIdxRoot->uCollationRules == NTFS_COLLATION_BINARY ? "binary"
2883 : pIdxRoot->uCollationRules == NTFS_COLLATION_FILENAME ? "filename"
2884 : pIdxRoot->uCollationRules == NTFS_COLLATION_UNICODE_STRING ? "unicode-string"
2885 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32 ? "uint32"
2886 : pIdxRoot->uCollationRules == NTFS_COLLATION_SID ? "sid"
2887 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_PAIR ? "uint32-pair"
2888 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_SEQ ? "uint32-sequence" : "!unknown!"));
2889 Log2(("NTFS: Index Root: cbIndexNode %#x\n", RT_LE2H_U32(pIdxRoot->cbIndexNode) ));
2890 Log2(("NTFS: Index Root: cAddressesPerIndexNode %#x => cbNodeAddressingUnit=%#x\n",
2891 pIdxRoot->cAddressesPerIndexNode, RT_LE2H_U32(pIdxRoot->cbIndexNode) / RT_MAX(1, pIdxRoot->cAddressesPerIndexNode) ));
2892 if (pIdxRoot->abReserved[0]) Log2(("NTFS: Index Root: abReserved[0] %#x\n", pIdxRoot->abReserved[0]));
2893 if (pIdxRoot->abReserved[1]) Log2(("NTFS: Index Root: abReserved[1] %#x\n", pIdxRoot->abReserved[1]));
2894 if (pIdxRoot->abReserved[2]) Log2(("NTFS: Index Root: abReserved[2] %#x\n", pIdxRoot->abReserved[2]));
2895
2896 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxRoot->Hdr, cbIdxRoot - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr),
2897 RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), "Index Root Hdr", pIdxRoot->uType);
2898 }
2899}
2900
2901#endif /* LOG_ENABLED */
2902
2903
2904/**
2905 * Validates an index header.
2906 *
2907 * @returns IPRT status code.
2908 * @param pRootInfo Pointer to the index root info.
2909 * @param pNodeInfo Pointer to the node info structure to load.
2910 * @param pIndexHdr Pointer to the index header.
2911 * @param cbIndex Size of the index.
2912 * @param pErrInfo Where to return extra error info.
2913 * @param pszWhat Error prefix.
2914 */
2915static int rtFsNtfsVol_LoadIndexNodeInfo(PCRTFSNTFSIDXROOTINFO pRootInfo, PRTFSNTFSIDXNODEINFO pNodeInfo, PCNTFSINDEXHDR pIndexHdr,
2916 uint32_t cbIndex, PRTERRINFO pErrInfo, const char *pszWhat)
2917{
2918 uint32_t const cbMinIndex = sizeof(*pIndexHdr) + sizeof(NTFSIDXENTRYHDR);
2919 if (cbIndex < cbMinIndex)
2920 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2921 "%s: Not enough room for the index header and one entry header! cbIndex=%#x (cbMinIndex=%#x)",
2922 pszWhat, cbIndex, cbMinIndex);
2923 uint32_t const cbAllocated = RT_LE2H_U32(pIndexHdr->cbAllocated);
2924 if ( cbAllocated > cbIndex
2925 || cbAllocated < cbMinIndex
2926 || (cbAllocated & 7) )
2927 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2928 "%s: Bogus index allocation size: %#x (min %#x, max %#x, 8 byte aligned)",
2929 pszWhat, cbAllocated, cbMinIndex, cbIndex);
2930 uint32_t const cbUsed = RT_LE2H_U32(pIndexHdr->cbUsed);
2931 if ( cbUsed > cbAllocated
2932 || cbUsed < cbMinIndex
2933 || (cbUsed & 7) )
2934 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2935 "%s: Bogus index used size: %#x (min %#x, max %#x, 8 byte aligned)",
2936 pszWhat, cbUsed, cbMinIndex, cbAllocated);
2937 uint32_t const offFirstEntry = RT_LE2H_U32(pIndexHdr->offFirstEntry);
2938 if ( offFirstEntry < sizeof(*pIndexHdr)
2939 || ( offFirstEntry > cbUsed - sizeof(NTFSIDXENTRYHDR)
2940 && offFirstEntry != cbUsed /* empty dir */)
2941 || (offFirstEntry & 7) )
2942 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2943 "%s: Bogus first entry offset: %#x (min %#x, max %#x, 8 byte aligned)",
2944 pszWhat, offFirstEntry, sizeof(*pIndexHdr), cbUsed - sizeof(NTFSIDXENTRYHDR));
2945
2946 /*
2947 * The index entries.
2948 */
2949 uint32_t const uType = pRootInfo->pRoot->uType;
2950 uint32_t offEntry = offFirstEntry;
2951 uint32_t iEntry = 0;
2952 for (;;)
2953 {
2954 if (offEntry + sizeof(NTFSIDXENTRYHDR) > cbUsed)
2955 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2956 "%s: Entry #%u is out of bound: offset %#x (cbUsed=%#x)",
2957 pszWhat, iEntry, offEntry, cbUsed);
2958 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIndexHdr + offEntry);
2959 uint16_t const cbEntry = RT_LE2H_U16(pEntryHdr->cbEntry);
2960 uint32_t const cbSubnodeAddr = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? sizeof(int64_t) : 0);
2961 uint32_t const cbMinEntry = sizeof(*pEntryHdr) + cbSubnodeAddr;
2962 if ( cbEntry < cbMinEntry
2963 || offEntry + cbEntry > cbUsed
2964 || (cbEntry & 7) )
2965 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2966 "%s: Entry #%u has a bogus size: %#x (min %#x, max %#x, 8 byte aligned)",
2967 pszWhat, iEntry, cbEntry, cbMinEntry, cbUsed - offEntry);
2968
2969 uint32_t const cbMaxKey = cbEntry - sizeof(*pEntryHdr) - cbSubnodeAddr;
2970 uint32_t const cbMinKey = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END) ? 0
2971 : uType == NTFSATINDEXROOT_TYPE_DIR ? RT_UOFFSETOF(NTFSATFILENAME, wszFilename) : 0;
2972 uint16_t const cbKey = RT_LE2H_U16(pEntryHdr->cbKey);
2973 if ( cbKey < cbMinKey
2974 || cbKey > cbMaxKey)
2975 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2976 "%s: Entry #%u has a bogus key size: %#x (min %#x, max %#x)",
2977 pszWhat, iEntry, cbKey, cbMinKey, cbMaxKey);
2978 if ( !(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
2979 && uType == NTFSATINDEXROOT_TYPE_DIR)
2980 {
2981 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2982 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) > cbKey)
2983 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2984 "%s: Entry #%u filename is out of bounds: cwcFilename=%#x -> %#x key, max %#x",
2985 pszWhat, iEntry, pFilename->cwcFilename,
2986 RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]), cbKey);
2987 }
2988
2989 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2990 {
2991 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr);
2992 if ( (uint64_t)iSubnode >= pRootInfo->uEndNodeAddresses
2993 || (iSubnode & pRootInfo->fNodeAddressMisalign) )
2994 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2995 "%s: Entry #%u has bogus subnode address: %#RX64 (max %#RX64, misalign %#x)",
2996 pszWhat, iEntry, iSubnode, pRootInfo->uEndNodeAddresses,
2997 pRootInfo->fNodeAddressMisalign);
2998 }
2999
3000 /* Advance. */
3001 offEntry += cbEntry;
3002 iEntry++;
3003 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
3004 break;
3005 }
3006
3007 /*
3008 * Popuplate the node info structure.
3009 */
3010 pNodeInfo->pIndexHdr = pIndexHdr;
3011 pNodeInfo->fInternal = RT_BOOL(pIndexHdr->fFlags & NTFSINDEXHDR_F_INTERNAL);
3012 if (pNodeInfo != &pRootInfo->NodeInfo)
3013 pNodeInfo->pVol = pRootInfo->NodeInfo.pVol;
3014 pNodeInfo->cEntries = iEntry;
3015 pNodeInfo->papEntries = (PCNTFSIDXENTRYHDR *)RTMemAlloc(iEntry * sizeof(pNodeInfo->papEntries[0]));
3016 if (pNodeInfo->papEntries)
3017 {
3018 PCNTFSIDXENTRYHDR pEntryHdr = NTFSINDEXHDR_GET_FIRST_ENTRY(pIndexHdr);
3019 for (iEntry = 0; iEntry < pNodeInfo->cEntries; iEntry++)
3020 {
3021 pNodeInfo->papEntries[iEntry] = pEntryHdr;
3022 pEntryHdr = NTFSIDXENTRYHDR_GET_NEXT(pEntryHdr);
3023 }
3024 return VINF_SUCCESS;
3025 }
3026 return VERR_NO_MEMORY;
3027}
3028
3029
3030/**
3031 * Creates a shared directory structure given a MFT core.
3032 *
3033 * @returns IPRT status code.
3034 * @param pThis The NTFS volume instance.
3035 * @param pCore The MFT core structure that's allegedly a directory.
3036 * (No reference consumed of course.)
3037 * @param ppSharedDir Where to return the pointer to the new shared directory
3038 * structure on success. (Referenced.)
3039 * @param pErrInfo Where to return additions error info. Optional.
3040 * @param pszWhat Context prefix for error reporting and logging.
3041 */
3042static int rtFsNtfsVol_NewSharedDirFromCore(PRTFSNTFSVOL pThis, PRTFSNTFSCORE pCore, PRTFSNTFSDIRSHRD *ppSharedDir,
3043 PRTERRINFO pErrInfo, const char *pszWhat)
3044{
3045 *ppSharedDir = NULL;
3046
3047 /*
3048 * Look for the index root and validate it.
3049 */
3050 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
3051 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3052 if (!pRootAttr)
3053 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: Found no INDEX_ROOT attribute named $I30", pszWhat);
3054 if (pRootAttr->pAttrHdr->fNonResident)
3055 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is is not resident", pszWhat);
3056 if (pRootAttr->cbResident < sizeof(NTFSATINDEXROOT))
3057 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is too small: %#x, min %#x ",
3058 pszWhat, pRootAttr->cbResident, sizeof(pRootAttr->cbResident));
3059
3060 PCNTFSATINDEXROOT pIdxRoot = (PCNTFSATINDEXROOT)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pRootAttr->pAttrHdr);
3061#ifdef LOG_ENABLED
3062 rtFsNtfsVol_LogIndexRoot(pIdxRoot, pRootAttr->cbResident);
3063#endif
3064 if (pIdxRoot->uType != NTFSATINDEXROOT_TYPE_DIR)
3065 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3066 "%s: Wrong INDEX_ROOT type for a directory: %#x, expected %#x",
3067 pszWhat, RT_LE2H_U32(pIdxRoot->uType), RT_LE2H_U32_C(NTFSATINDEXROOT_TYPE_DIR));
3068 if (pIdxRoot->uCollationRules != NTFS_COLLATION_FILENAME)
3069 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3070 "%s: Wrong collation rules for a directory: %#x, expected %#x",
3071 pszWhat, RT_LE2H_U32(pIdxRoot->uCollationRules), RT_LE2H_U32_C(NTFS_COLLATION_FILENAME));
3072 uint32_t cbIndexNode = RT_LE2H_U32(pIdxRoot->cbIndexNode);
3073 if (cbIndexNode < 512 || cbIndexNode > _64K || !RT_IS_POWER_OF_TWO(cbIndexNode))
3074 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3075 "%s: Bogus index node size: %#x (expected power of two between 512 and 64KB)",
3076 pszWhat, cbIndexNode);
3077 unsigned const cNodeAddressShift = cbIndexNode >= pThis->cbCluster ? pThis->cClusterShift : 9;
3078 if (((uint32_t)pIdxRoot->cAddressesPerIndexNode << cNodeAddressShift) != cbIndexNode)
3079 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3080 "%s: Bogus addresses per index node value: %#x (cbIndexNode=%#x cNodeAddressShift=%#x)",
3081 pszWhat, pIdxRoot->cAddressesPerIndexNode, cbIndexNode, cNodeAddressShift);
3082 AssertReturn(pRootAttr->uObj.pSharedDir == NULL, VERR_INTERNAL_ERROR_3);
3083
3084 /*
3085 * Check for the node data stream and related allocation bitmap.
3086 */
3087 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
3088 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3089 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
3090 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3091 if (pIndexAlloc && !pIndexBitmap)
3092 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3093 "%s: INDEX_ALLOCATION attribute without BITMAP", pszWhat);
3094 if (!pIndexAlloc && pIndexBitmap)
3095 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3096 "%s: BITMAP attribute without INDEX_ALLOCATION", pszWhat);
3097 uint64_t uNodeAddressEnd = 0;
3098 if (pIndexAlloc)
3099 {
3100 if (!pIndexAlloc->pAttrHdr->fNonResident)
3101 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ALLOCATION is resident", pszWhat);
3102 if (pIndexAlloc->cbValue & (cbIndexNode - 1))
3103 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3104 "%s: INDEX_ALLOCATION size isn't aligned on node boundrary: %#RX64, cbIndexNode=%#x",
3105 pszWhat, pIndexAlloc->cbValue, cbIndexNode);
3106 uint64_t const cNodes = pIndexAlloc->cbValue / cbIndexNode;
3107 if (pIndexBitmap->cbValue != (RT_ALIGN_64(cNodes, 64) >> 3))
3108 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3109 "%s: BITMAP size does not match INDEX_ALLOCATION: %#RX64, expected %#RX64 (cbIndexNode=%#x, cNodes=%#RX64)",
3110 pszWhat, pIndexBitmap->cbValue, RT_ALIGN_64(cNodes, 64) >> 3, cbIndexNode, cNodes);
3111 uNodeAddressEnd = cNodes * pIdxRoot->cAddressesPerIndexNode;
3112 }
3113
3114 /*
3115 * Create a directory instance.
3116 */
3117 PRTFSNTFSDIRSHRD pNewDir = (PRTFSNTFSDIRSHRD)RTMemAllocZ(sizeof(*pNewDir));
3118 if (!pNewDir)
3119 return VERR_NO_MEMORY;
3120
3121 pNewDir->cRefs = 1;
3122 rtFsNtfsCore_Retain(pCore);
3123 pNewDir->RootInfo.pRootAttr = pRootAttr;
3124 pNewDir->RootInfo.pRoot = pIdxRoot;
3125 pNewDir->RootInfo.pAlloc = pIndexAlloc;
3126 pNewDir->RootInfo.uEndNodeAddresses = uNodeAddressEnd;
3127 pNewDir->RootInfo.cNodeAddressByteShift = cNodeAddressShift;
3128 pNewDir->RootInfo.fNodeAddressMisalign = pIdxRoot->cAddressesPerIndexNode - 1;
3129 pNewDir->RootInfo.NodeInfo.pVol = pThis;
3130
3131 /*
3132 * Finally validate the index header and entries.
3133 */
3134 int rc = rtFsNtfsVol_LoadIndexNodeInfo(&pNewDir->RootInfo, &pNewDir->RootInfo.NodeInfo, &pIdxRoot->Hdr,
3135 pRootAttr->cbResident - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), pErrInfo, pszWhat);
3136 if (RT_SUCCESS(rc))
3137 {
3138 *ppSharedDir = pNewDir;
3139 pRootAttr->uObj.pSharedDir = pNewDir;
3140 return VINF_SUCCESS;
3141 }
3142 RTMemFree(pNewDir);
3143 rtFsNtfsCore_Release(pCore);
3144 return rc;
3145}
3146
3147
3148/**
3149 * Gets a shared directory structure given an MFT record reference, creating a
3150 * new one if necessary.
3151 *
3152 * @returns IPRT status code.
3153 * @param pThis The NTFS volume instance.
3154 * @param pDirMftRef The MFT record reference to follow.
3155 * @param ppSharedDir Where to return the shared directory structure
3156 * (referenced).
3157 * @param pErrInfo Where to return error details. Optional.
3158 * @param pszWhat Error/log prefix.
3159 */
3160static int rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pDirMftRef,
3161 PRTFSNTFSDIRSHRD *ppSharedDir, PRTERRINFO pErrInfo, const char *pszWhat)
3162{
3163 /*
3164 * Get the core structure for the MFT record and check that it's a directory we've got.
3165 */
3166 PRTFSNTFSCORE pCore;
3167 int rc = rtFsNtfsVol_QueryCoreForMftRef(pThis, pDirMftRef, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3168 if (RT_SUCCESS(rc))
3169 {
3170 if (pCore->pMftRec->pFileRec->fFlags & NTFSRECFILE_F_DIRECTORY)
3171 {
3172 /*
3173 * Locate the $I30 root index attribute as we associate the
3174 * pointer to the shared directory pointer with it.
3175 */
3176 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
3177 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3178 if (pRootAttr)
3179 {
3180 if (!pRootAttr->uObj.pSharedDir)
3181 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, ppSharedDir, pErrInfo, pszWhat);
3182 else
3183 {
3184 Assert(pRootAttr->uObj.pSharedDir->RootInfo.pRootAttr->pCore == pCore);
3185 rtFsNtfsDirShrd_Retain(pRootAttr->uObj.pSharedDir);
3186 *ppSharedDir = pRootAttr->uObj.pSharedDir;
3187 }
3188 }
3189 else
3190 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY,
3191 "%s: Found INDEX_ROOT attribute named $I30, even though NTFSRECFILE_F_DIRECTORY is set",
3192 pszWhat);
3193 }
3194 else
3195 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY, "%s: fFlags=%#x", pszWhat, pCore->pMftRec->pFileRec->fFlags);
3196 rtFsNtfsCore_Release(pCore);
3197 }
3198 return rc;
3199}
3200
3201
3202/**
3203 * Frees resource kept by an index node info structure.
3204 *
3205 * @param pNodeInfo The index node info structure to delelte.
3206 */
3207static void rtFsNtfsIdxNodeInfo_Delete(PRTFSNTFSIDXNODEINFO pNodeInfo)
3208{
3209 RTMemFree(pNodeInfo->papEntries);
3210 pNodeInfo->papEntries = NULL;
3211 pNodeInfo->pNode = NULL;
3212 pNodeInfo->pVol = NULL;
3213}
3214
3215
3216/**
3217 * Gets or loads the specified subnode.
3218 *
3219 * @returns IPRT status code.
3220 * @param pRootInfo The index root info.
3221 * @param iNode The address of the node being queried.
3222 * @param ppNode Where to return the referenced pointer to the node.
3223 */
3224static int rtFsNtfsIdxRootInfo_QueryNode(PRTFSNTFSIDXROOTINFO pRootInfo, int64_t iNode, PRTFSNTFSIDXNODE *ppNode)
3225{
3226 PRTFSNTFSVOL pVol = pRootInfo->NodeInfo.pVol;
3227
3228 /*
3229 * A bit of paranoia. These has been checked already when loading, but it
3230 * usually doesn't hurt too much to be careful.
3231 */
3232 AssertReturn(!(iNode & pRootInfo->fNodeAddressMisalign), VERR_VFS_BOGUS_OFFSET);
3233 AssertReturn((uint64_t)iNode < pRootInfo->uEndNodeAddresses, VERR_VFS_BOGUS_OFFSET);
3234 AssertReturn(pRootInfo->pAlloc, VERR_VFS_BOGUS_OFFSET);
3235
3236 /*
3237 * First translate the node address to a disk byte offset and check the index node cache.
3238 */
3239 uint64_t offNode = iNode << pRootInfo->cNodeAddressByteShift;
3240 uint64_t offNodeOnDisk = rtFsNtfsAttr_OffsetToDisk(pRootInfo->pAlloc, offNode, NULL);
3241 PRTFSNTFSIDXNODE pNode = (PRTFSNTFSIDXNODE)RTAvlU64Get(&pVol->IdxNodeCacheRoot, offNodeOnDisk);
3242 if (pNode)
3243 {
3244 rtFsNtfsIdxNode_Retain(pNode);
3245 *ppNode = pNode;
3246 return VINF_SUCCESS;
3247 }
3248
3249 /*
3250 * Need to create a load a new node.
3251 */
3252 pNode = (PRTFSNTFSIDXNODE)RTMemAllocZ(sizeof(*pNode));
3253 AssertReturn(pNode, VERR_NO_MEMORY);
3254
3255 pNode->TreeNode.Key = offNodeOnDisk;
3256 uint32_t cbIndexNode = RT_LE2H_U32(pRootInfo->pRoot->cbIndexNode);
3257 pNode->cbCost = sizeof(*pNode) + cbIndexNode;
3258 pNode->cRefs = 1;
3259 pNode->pNode = (PNTFSATINDEXALLOC)RTMemAllocZ(cbIndexNode);
3260 int rc;
3261 if (pNode->pNode)
3262 {
3263 rc = rtFsNtfsAttr_Read(pRootInfo->pAlloc, offNode, pNode->pNode, cbIndexNode);
3264 if (RT_SUCCESS(rc))
3265 {
3266 rc = VERR_VFS_BOGUS_FORMAT;
3267 if (pNode->pNode->RecHdr.uMagic != NTFSREC_MAGIC_INDEX_ALLOC)
3268 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Invalid node magic %#x -> VERR_VFS_BOGUS_FORMAT\n",
3269 iNode, RT_LE2H_U32(pNode->pNode->RecHdr.uMagic) ));
3270 else if ((int64_t)RT_LE2H_U64(pNode->pNode->iSelfAddress) != iNode)
3271 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Wrong iSelfAddress: %#x -> VERR_VFS_BOGUS_FORMAT\n",
3272 iNode, RT_LE2H_U64(pNode->pNode->iSelfAddress) ));
3273 else
3274 {
3275 rc = rtFsNtfsRec_DoMultiSectorFixups(&pNode->pNode->RecHdr, cbIndexNode, false /*fRelaxedUsa*/, NULL /*pErrInfo*/);
3276 if (RT_SUCCESS(rc))
3277 {
3278 /*
3279 * Validate/parse it
3280 */
3281#ifdef LOG_ENABLED
3282 rtFsNtfsVol_LogIndexHdrAndEntries(&pNode->pNode->Hdr,
3283 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
3284 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "index node",
3285 pRootInfo->pRoot->uType);
3286#endif
3287 rc = rtFsNtfsVol_LoadIndexNodeInfo(pRootInfo, &pNode->NodeInfo, &pNode->pNode->Hdr,
3288 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
3289 NULL /*pErrInfo*/, "index node");
3290 if (RT_SUCCESS(rc))
3291 {
3292 pNode->cbCost += pNode->NodeInfo.cEntries * sizeof(pNode->NodeInfo.papEntries[0]);
3293
3294 /*
3295 * Insert it into the cache, trimming the cache if necessary.
3296 */
3297 bool fInsertOkay = RTAvlU64Insert(&pVol->IdxNodeCacheRoot, &pNode->TreeNode);
3298 Assert(fInsertOkay);
3299 if (fInsertOkay)
3300 {
3301 pVol->cIdxNodes += 1;
3302 pVol->cbIdxNodes += pNode->cbCost;
3303 if (pVol->cbIdxNodes > RTFSNTFS_MAX_CORE_CACHE_SIZE)
3304 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
3305
3306 *ppNode = pNode;
3307 return VINF_SUCCESS;
3308 }
3309 }
3310 }
3311 }
3312 }
3313
3314 RTMemFree(pNode->pNode);
3315 pNode->pNode = NULL;
3316 }
3317 else
3318 rc = VERR_NO_MEMORY;
3319 RTMemFree(pNode);
3320 return rc;
3321}
3322
3323
3324/**
3325 * Frees resource kept by an index root info structure.
3326 *
3327 * @param pRootInfo The index root info structure to delete.
3328 */
3329static void rtFsNtfsIdxRootInfo_Delete(PRTFSNTFSIDXROOTINFO pRootInfo)
3330{
3331 rtFsNtfsIdxNodeInfo_Delete(&pRootInfo->NodeInfo);
3332 pRootInfo->pRootAttr->uObj.pSharedDir = NULL;
3333 rtFsNtfsCore_Release(pRootInfo->pRootAttr->pCore);
3334 pRootInfo->pRootAttr = NULL;
3335 pRootInfo->pAlloc = NULL;
3336 pRootInfo->pRoot = NULL;
3337}
3338
3339
3340/**
3341 * Destroys a shared directory structure when the reference count reached zero.
3342 *
3343 * @returns zero
3344 * @param pThis The shared directory structure to destroy.
3345 */
3346static uint32_t rtFsNtfsDirShrd_Destroy(PRTFSNTFSDIRSHRD pThis)
3347{
3348 rtFsNtfsIdxRootInfo_Delete(&pThis->RootInfo);
3349 RTMemFree(pThis);
3350 return 0;
3351}
3352
3353
3354/**
3355 * Releases a references to a shared directory structure.
3356 *
3357 * @returns New reference count.
3358 * @param pThis The shared directory structure.
3359 */
3360static uint32_t rtFsNtfsDirShrd_Release(PRTFSNTFSDIRSHRD pThis)
3361{
3362 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
3363 Assert(cRefs < 4096);
3364 if (cRefs > 0)
3365 return cRefs;
3366 return rtFsNtfsDirShrd_Destroy(pThis);
3367}
3368
3369
3370/**
3371 * Retains a references to a shared directory structure.
3372 *
3373 * @returns New reference count.
3374 * @param pThis The shared directory structure.
3375 */
3376static uint32_t rtFsNtfsDirShrd_Retain(PRTFSNTFSDIRSHRD pThis)
3377{
3378 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
3379 Assert(cRefs > 1);
3380 Assert(cRefs < 4096);
3381 return cRefs;
3382}
3383
3384
3385/**
3386 * Compares the two filenames in an case insentivie manner.
3387 *
3388 * @retval -1 if the first filename comes first
3389 * @retval 0 if equal
3390 * @retval 1 if the second filename comes first.
3391 *
3392 * @param pwszUpper1 The first filename, this has been uppercase already.
3393 * @param cwcUpper1 The length of the first filename.
3394 * @param pawcFilename2 The second filename to compare it with. Not zero
3395 * terminated.
3396 * @param cwcFilename2 The length of the second filename.
3397 * @param pawcUpcase The uppercase table. 64K entries.
3398 */
3399static int rtFsNtfsIdxComp_Filename(PCRTUTF16 pwszUpper1, uint8_t cwcUpper1, PCRTUTF16 pawcFilename2, uint8_t cwcFilename2,
3400 PCRTUTF16 const pawcUpcase)
3401{
3402 while (cwcUpper1 > 0 && cwcFilename2 > 0)
3403 {
3404 RTUTF16 uc1 = *pwszUpper1++;
3405 RTUTF16 uc2 = *pawcFilename2++;
3406 if (uc1 != uc2)
3407 {
3408 uc2 = pawcUpcase[uc2];
3409 if (uc1 != uc2)
3410 return uc1 < uc2 ? -1 : 1;
3411 }
3412
3413 /* Decrement the lengths and loop. */
3414 cwcUpper1--;
3415 cwcFilename2--;
3416 }
3417
3418 if (!cwcUpper1)
3419 {
3420 if (!cwcFilename2)
3421 return 0;
3422 return -1;
3423 }
3424 return 1;
3425}
3426
3427
3428/**
3429 * Look up a name in the directory.
3430 *
3431 * @returns IPRT status code.
3432 * @param pShared The shared directory structure.
3433 * @param pszEntry The name to lookup.
3434 * @param ppFilename Where to return the pointer to the filename structure.
3435 * @param ppEntryHdr Where to return the poitner to the entry header
3436 * structure.
3437 * @param ppNode Where to return the pointer to the node the filename
3438 * structure resides in. This must be released. It will
3439 * be set to NULL if the name was found in the root node.
3440 */
3441static int rtFsNtfsDirShrd_Lookup(PRTFSNTFSDIRSHRD pShared, const char *pszEntry,
3442 PCNTFSATFILENAME *ppFilename, PCNTFSIDXENTRYHDR *ppEntryHdr, PRTFSNTFSIDXNODE *ppNode)
3443{
3444 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
3445
3446 *ppFilename = NULL;
3447 *ppEntryHdr = NULL;
3448 *ppNode = NULL;
3449 /** @todo do streams (split on ':') */
3450
3451 /*
3452 * Convert the filename to UTF16 and uppercase.
3453 */
3454 PCRTUTF16 const pawcUpcase = pVol->pawcUpcase;
3455 RTUTF16 wszFilename[256+4];
3456 PRTUTF16 pwszDst = wszFilename;
3457 PRTUTF16 pwszEnd = &wszFilename[255];
3458 const char *pszSrc = pszEntry;
3459 for (;;)
3460 {
3461 RTUNICP uc;
3462 int rc = RTStrGetCpEx(&pszSrc, &uc);
3463 if (RT_SUCCESS(rc))
3464 {
3465 if (uc != 0)
3466 {
3467 if (uc < _64K)
3468 uc = pawcUpcase[uc];
3469 pwszDst = RTUtf16PutCp(pwszDst, uc);
3470 if ((uintptr_t)pwszDst <= (uintptr_t)pwszEnd)
3471 { /* likely */ }
3472 else
3473 {
3474 Log(("rtFsNtfsDirShrd_Lookup: Filename too long '%s'\n", pszEntry));
3475 return VERR_FILENAME_TOO_LONG;
3476 }
3477 }
3478 else
3479 {
3480 *pwszDst = '\0';
3481 break;
3482 }
3483 }
3484 else
3485 {
3486 Log(("rtFsNtfsDirShrd_Lookup: Invalid UTF-8 encoding (%Rrc): %.*Rhxs\n", rc, strlen(pszEntry), pszEntry));
3487 return rc;
3488 }
3489 }
3490 uint8_t const cwcFilename = (uint8_t)(pwszDst - wszFilename);
3491
3492 /*
3493 * Do the tree traversal.
3494 */
3495 PRTFSNTFSIDXROOTINFO pRootInfo = &pShared->RootInfo;
3496 PRTFSNTFSIDXNODEINFO pNodeInfo = &pRootInfo->NodeInfo;
3497 PRTFSNTFSIDXNODE pNode = NULL;
3498 for (;;)
3499 {
3500 /*
3501 * Search it.
3502 */
3503 PCNTFSIDXENTRYHDR *papEntries = pNodeInfo->papEntries;
3504 uint32_t iEnd = pNodeInfo->cEntries;
3505 AssertReturn(iEnd > 0, VERR_INTERNAL_ERROR_3);
3506
3507 /* Exclude the end node from the serach as it doesn't have any key. */
3508 if (papEntries[iEnd - 1]->fFlags & NTFSIDXENTRYHDR_F_END)
3509 iEnd--;
3510
3511 uint32_t iEntry;
3512 if (1 /*iEnd < 8*/ )
3513 {
3514 if (iEnd > 0)
3515 {
3516 for (iEntry = 0; iEntry < iEnd; iEntry++)
3517 {
3518 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(papEntries[iEntry] + 1);
3519 int iDiff = rtFsNtfsIdxComp_Filename(wszFilename, cwcFilename, pFilename->wszFilename,
3520 pFilename->cwcFilename, pawcUpcase);
3521 if (iDiff > 0)
3522 { /* likely */ }
3523 else if (iDiff == 0)
3524 {
3525 *ppNode = pNode;
3526 *ppEntryHdr = papEntries[iEntry];
3527 *ppFilename = pFilename;
3528 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Found it! (iEntry=%u, FileMftRec=%#RX64 sqn %#x)\n",
3529 pszEntry, iEntry, NTFSMFTREF_GET_IDX(&papEntries[iEntry]->u.FileMftRec),
3530 NTFSMFTREF_GET_SEQ(&papEntries[iEntry]->u.FileMftRec) ));
3531 return VINF_SUCCESS;
3532 }
3533 else
3534 break;
3535 }
3536 }
3537 else
3538 iEntry = iEnd;
3539 }
3540 /* else: implement binary search */
3541
3542 /*
3543 * Decend thru node iEntry.
3544 *
3545 * We could be bold and ASSUME that there is always an END node, but we're
3546 * playing safe for now.
3547 */
3548 if (iEnd < pNodeInfo->cEntries)
3549 {
3550 PCNTFSIDXENTRYHDR pEntry = papEntries[iEntry];
3551 if (pEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
3552 {
3553 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntry);
3554 rtFsNtfsIdxNode_Release(pNode);
3555 int rc = rtFsNtfsIdxRootInfo_QueryNode(pRootInfo, iSubnode, &pNode);
3556 if (RT_SUCCESS(rc))
3557 {
3558 pNodeInfo = &pNode->NodeInfo;
3559 continue;
3560 }
3561 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): rtFsNtfsIdxRootInfo_QueryNode(%#RX64) error %Rrc!\n",
3562 pszEntry, iSubnode, rc));
3563 return rc;
3564 }
3565 }
3566 rtFsNtfsIdxNode_Release(pNode);
3567 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Not found! (#2)\n", pszEntry));
3568 return VERR_FILE_NOT_FOUND;
3569 }
3570
3571 /* not reached */
3572}
3573
3574
3575/**
3576 * Gets the shared directory structure for the parent.
3577 *
3578 * @returns IPRT status code.
3579 * @param pThis The directory which parent we want.
3580 * @param ppDotDot Where to return the referenced shared parent dir
3581 * structure.
3582 *
3583 */
3584static int rtFsNtfsDirShrd_QueryParent(PRTFSNTFSDIRSHRD pThis, PRTFSNTFSDIRSHRD *ppDotDot)
3585{
3586 /*
3587 * The root directory has no parent from our perspective.
3588 */
3589 if (pThis == pThis->RootInfo.NodeInfo.pVol->pRootDir)
3590 {
3591 rtFsNtfsDirShrd_Retain(pThis);
3592 *ppDotDot = pThis;
3593 return VINF_SUCCESS;
3594 }
3595
3596 /*
3597 * Look for a filename record so we know where we go from here.
3598 */
3599 PRTFSNTFSCORE pCore = pThis->RootInfo.pRootAttr->pCore;
3600 PRTFSNTFSATTR pCurAttr;
3601 RTListForEach(&pCore->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
3602 {
3603 if ( pCurAttr->pAttrHdr->uAttrType == NTFS_AT_FILENAME
3604 && pCurAttr->cbResident >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
3605 {
3606 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pCurAttr->pAttrHdr);
3607 int rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pThis->RootInfo.NodeInfo.pVol, &pFilename->ParentDirMftRec,
3608 ppDotDot, NULL /*pErrInfo*/, "..");
3609 if (RT_SUCCESS(rc))
3610 return VINF_SUCCESS;
3611 LogRel(("rtFsNtfsDirShrd_QueryParent: rtFsNtfsVol_QueryOrCreateSharedDirByMftRef failed: %Rrc\n", rc));
3612 return rc;
3613 }
3614 }
3615
3616 LogRel(("rtFsNtfsDirShrd_QueryParent: Couldn't find '..' filename for MFT record %RX64!\n",
3617 pThis->RootInfo.pRootAttr->pCore->pMftRec->TreeNode.Key));
3618 return VERR_VFS_BOGUS_FORMAT;
3619}
3620
3621
3622
3623/**
3624 * Destroys an index node.
3625 *
3626 * This will remove it from the cache tree, however the caller must make sure
3627 * its not in the reuse list any more.
3628 *
3629 * @param pNode The node to destroy.
3630 */
3631static void rtFsNtfsIdxNode_Destroy(PRTFSNTFSIDXNODE pNode)
3632{
3633 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
3634
3635 /* Remove it from the volume node cache. */
3636 PAVLU64NODECORE pAssertRemove = RTAvlU64Remove(&pVol->IdxNodeCacheRoot, pNode->TreeNode.Key);
3637 Assert(pAssertRemove == &pNode->TreeNode); NOREF(pAssertRemove);
3638 pVol->cIdxNodes--;
3639 pVol->cbIdxNodes -= pNode->cbCost;
3640
3641 /* Destroy it. */
3642 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
3643 RTMemFree(pNode->pNode);
3644 pNode->pNode = NULL;
3645 RTMemFree(pNode);
3646}
3647
3648
3649/**
3650 * Trims the index node cache.
3651 *
3652 * @param pThis The NTFS volume instance which index node cache
3653 * needs trimming.
3654 */
3655static void rtFsNtfsIdxVol_TrimIndexNodeCache(PRTFSNTFSVOL pThis)
3656{
3657 while ( pThis->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE
3658 && pThis->cUnusedIdxNodes)
3659 {
3660 PRTFSNTFSIDXNODE pNode = RTListRemoveFirst(&pThis->IdxNodeUnusedHead, RTFSNTFSIDXNODE, UnusedListEntry);
3661 pThis->cUnusedIdxNodes--;
3662 rtFsNtfsIdxNode_Destroy(pNode);
3663 }
3664}
3665
3666
3667/**
3668 * Index node reference reached zero, put it in the unused list and trim the
3669 * cache.
3670 *
3671 * @returns zero
3672 * @param pNode The index node.
3673 */
3674static uint32_t rtFsNtfsIdxNode_MaybeDestroy(PRTFSNTFSIDXNODE pNode)
3675{
3676 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
3677 if (pVol)
3678 {
3679 RTListAppend(&pVol->IdxNodeUnusedHead, &pNode->UnusedListEntry);
3680 pVol->cUnusedIdxNodes++;
3681 if (pVol->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE)
3682 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
3683 return 0;
3684 }
3685 /* not sure if this is needed yet... */
3686 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
3687 RTMemFree(pNode);
3688 return 0;
3689}
3690
3691
3692/**
3693 * Releases a reference to an index node.
3694 *
3695 * @returns New reference count.
3696 * @param pNode The index node to release. NULL is ignored.
3697 */
3698static uint32_t rtFsNtfsIdxNode_Release(PRTFSNTFSIDXNODE pNode)
3699{
3700 if (pNode)
3701 {
3702 uint32_t cRefs = ASMAtomicDecU32(&pNode->cRefs);
3703 Assert(cRefs < 128);
3704 if (cRefs > 0)
3705 return cRefs;
3706 return rtFsNtfsIdxNode_MaybeDestroy(pNode);
3707 }
3708 return 0;
3709}
3710
3711
3712/**
3713 * Retains a reference to an index node.
3714 *
3715 * This will remove it from the unused list if necessary.
3716 *
3717 * @returns New reference count.
3718 * @param pNode The index to reference.
3719 */
3720static uint32_t rtFsNtfsIdxNode_Retain(PRTFSNTFSIDXNODE pNode)
3721{
3722 uint32_t cRefs = ASMAtomicIncU32(&pNode->cRefs);
3723 if (cRefs == 1)
3724 {
3725 RTListNodeRemove(&pNode->UnusedListEntry);
3726 pNode->NodeInfo.pVol->cUnusedIdxNodes--;
3727 }
3728 return cRefs;
3729}
3730
3731
3732
3733
3734/*
3735 *
3736 * Directory instance methods
3737 * Directory instance methods
3738 * Directory instance methods
3739 *
3740 */
3741
3742/**
3743 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
3744 */
3745static DECLCALLBACK(int) rtFsNtfsDir_Close(void *pvThis)
3746{
3747 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3748 LogFlow(("rtFsNtfsDir_Close(%p/%p)\n", pThis, pThis->pShared));
3749
3750 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3751 pThis->pShared = NULL;
3752 if (pShared)
3753 rtFsNtfsDirShrd_Release(pShared);
3754
3755 while (pThis->cEnumStackEntries > 0)
3756 {
3757 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
3758 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
3759 pEntry->pNodeInfo = NULL;
3760 }
3761 RTMemFree(pThis->paEnumStack);
3762 pThis->paEnumStack = NULL;
3763 pThis->cEnumStackMaxDepth = 0;
3764
3765 return VINF_SUCCESS;
3766}
3767
3768
3769/**
3770 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
3771 */
3772static DECLCALLBACK(int) rtFsNtfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3773{
3774 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3775 Log(("rtFsNtfsDir_QueryInfo\n"));
3776 return rtFsNtfsCore_QueryInfo(pThis->pShared->RootInfo.pRootAttr->pCore,
3777 pThis->pShared->RootInfo.pAlloc ? pThis->pShared->RootInfo.pAlloc
3778 : pThis->pShared->RootInfo.pRootAttr,
3779 pObjInfo, enmAddAttr);
3780}
3781
3782
3783/**
3784 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
3785 */
3786static DECLCALLBACK(int) rtFsNtfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
3787{
3788 Log(("rtFsNtfsDir_SetMode\n"));
3789 RT_NOREF(pvThis, fMode, fMask);
3790 return VERR_WRITE_PROTECT;
3791}
3792
3793
3794/**
3795 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
3796 */
3797static DECLCALLBACK(int) rtFsNtfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
3798 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
3799{
3800 Log(("rtFsNtfsDir_SetTimes\n"));
3801 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
3802 return VERR_WRITE_PROTECT;
3803}
3804
3805
3806/**
3807 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
3808 */
3809static DECLCALLBACK(int) rtFsNtfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
3810{
3811 Log(("rtFsNtfsDir_SetOwner\n"));
3812 RT_NOREF(pvThis, uid, gid);
3813 return VERR_WRITE_PROTECT;
3814}
3815
3816
3817/**
3818 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
3819 */
3820static DECLCALLBACK(int) rtFsNtfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
3821 uint32_t fFlags, PRTVFSOBJ phVfsObj)
3822{
3823 LogFlow(("rtFsNtfsDir_Open: pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
3824 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3825 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3826 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
3827 int rc;
3828
3829 /*
3830 * We cannot create or replace anything, just open stuff.
3831 */
3832 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3833 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
3834 { /* likely */ }
3835 else
3836 return VERR_WRITE_PROTECT;
3837
3838 /*
3839 * Special cases '.' and '..'
3840 */
3841 if ( pszEntry[0] == '.'
3842 && ( pszEntry[1] == '\0'
3843 || ( pszEntry[1] == '.'
3844 && pszEntry[2] == '\0')))
3845 {
3846 if (!(fFlags & RTVFSOBJ_F_OPEN_DIRECTORY))
3847 return VERR_IS_A_DIRECTORY;
3848
3849 PRTFSNTFSDIRSHRD pSharedToOpen;
3850 if (pszEntry[1] == '\0')
3851 {
3852 pSharedToOpen = pShared;
3853 rtFsNtfsDirShrd_Retain(pSharedToOpen);
3854 rc = VINF_SUCCESS;
3855 }
3856 else
3857 {
3858 pSharedToOpen = NULL;
3859 rc = rtFsNtfsDirShrd_QueryParent(pShared, &pSharedToOpen);
3860 }
3861 if (RT_SUCCESS(rc))
3862 {
3863 RTVFSDIR hVfsDir;
3864 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
3865 rtFsNtfsDirShrd_Release(pSharedToOpen);
3866 if (RT_SUCCESS(rc))
3867 {
3868 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3869 RTVfsDirRelease(hVfsDir);
3870 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3871 }
3872 }
3873 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3874 return rc;
3875 }
3876
3877 /*
3878 * Lookup the index entry.
3879 */
3880 PRTFSNTFSIDXNODE pNode;
3881 PCNTFSIDXENTRYHDR pEntryHdr;
3882 PCNTFSATFILENAME pFilename;
3883 rc = rtFsNtfsDirShrd_Lookup(pShared, pszEntry, &pFilename, &pEntryHdr, &pNode);
3884 if (RT_SUCCESS(rc))
3885 {
3886 uint32_t fFileAttribs = RT_LE2H_U32(pFilename->fFileAttribs);
3887 switch (fFileAttribs & (NTFS_FA_DIRECTORY | NTFS_FA_REPARSE_POINT | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT))
3888 {
3889 /*
3890 * File.
3891 */
3892 case 0:
3893 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3894 {
3895 RTVFSFILE hVfsFile;
3896 rc = rtFsNtfsVol_NewFile(pVol, fOpen, pEntryHdr, NULL /*pszStreamName*/, &hVfsFile, NULL, pszEntry);
3897 if (RT_SUCCESS(rc))
3898 {
3899 *phVfsObj = RTVfsObjFromFile(hVfsFile);
3900 RTVfsFileRelease(hVfsFile);
3901 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3902 }
3903 }
3904 else
3905 rc = VERR_IS_A_FILE;
3906 break;
3907
3908 /*
3909 * Directory
3910 */
3911 case NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3912 case NTFS_FA_DIRECTORY | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3913 case NTFS_FA_DIRECTORY:
3914 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3915 {
3916 PRTFSNTFSDIRSHRD pSharedToOpen;
3917 rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pVol, &pEntryHdr->u.FileMftRec,
3918 &pSharedToOpen, NULL, pszEntry);
3919 if (RT_SUCCESS(rc))
3920 {
3921 RTVFSDIR hVfsDir;
3922 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
3923 rtFsNtfsDirShrd_Release(pSharedToOpen);
3924 if (RT_SUCCESS(rc))
3925 {
3926 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3927 RTVfsDirRelease(hVfsDir);
3928 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3929 }
3930 }
3931 }
3932 else
3933 rc = VERR_IS_A_DIRECTORY;
3934 break;
3935
3936 /*
3937 * Possible symbolic links.
3938 */
3939 case NTFS_FA_REPARSE_POINT:
3940 case NTFS_FA_REPARSE_POINT | NTFS_FA_DIRECTORY:
3941 case NTFS_FA_REPARSE_POINT | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3942 case NTFS_FA_REPARSE_POINT | NTFS_FA_DIRECTORY | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3943 rc = VERR_NOT_IMPLEMENTED;
3944 break;
3945
3946 default:
3947 AssertFailed();
3948 rc = VERR_FILE_NOT_FOUND;
3949 break;
3950 }
3951 rtFsNtfsIdxNode_Release(pNode);
3952 }
3953
3954 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3955 return rc;
3956}
3957
3958
3959/**
3960 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3961 */
3962static DECLCALLBACK(int) rtFsNtfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3963{
3964 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3965 Log(("rtFsNtfsDir_CreateDir\n"));
3966 return VERR_WRITE_PROTECT;
3967}
3968
3969
3970/**
3971 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3972 */
3973static DECLCALLBACK(int) rtFsNtfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3974{
3975 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3976 Log(("rtFsNtfsDir_OpenSymlink\n"));
3977 return VERR_NOT_SUPPORTED;
3978}
3979
3980
3981/**
3982 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3983 */
3984static DECLCALLBACK(int) rtFsNtfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3985 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3986{
3987 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3988 Log(("rtFsNtfsDir_CreateSymlink\n"));
3989 return VERR_WRITE_PROTECT;
3990}
3991
3992
3993/**
3994 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3995 */
3996static DECLCALLBACK(int) rtFsNtfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3997{
3998 RT_NOREF(pvThis, pszEntry, fType);
3999 Log(("rtFsNtfsDir_UnlinkEntry\n"));
4000 return VERR_WRITE_PROTECT;
4001}
4002
4003
4004/**
4005 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
4006 */
4007static DECLCALLBACK(int) rtFsNtfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
4008{
4009 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
4010 Log(("rtFsNtfsDir_RenameEntry\n"));
4011 return VERR_WRITE_PROTECT;
4012}
4013
4014
4015/**
4016 * Cleans up the directory enumeration stack, releasing all node references.
4017 *
4018 * @param pThis The open directory instance data.
4019 */
4020static void rtFsNtfsDir_StackCleanup(PRTFSNTFSDIR pThis)
4021{
4022 while (pThis->cEnumStackEntries > 0)
4023 {
4024 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
4025 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
4026 pEntry->pNodeInfo = NULL;
4027 }
4028 if (pThis->paEnumStack)
4029 pThis->paEnumStack[0].iNext = 0;
4030}
4031
4032
4033/**
4034 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
4035 */
4036static DECLCALLBACK(int) rtFsNtfsDir_RewindDir(void *pvThis)
4037{
4038 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
4039 LogFlow(("rtFsNtfsDir_RewindDir\n"));
4040
4041 rtFsNtfsDir_StackCleanup(pThis);
4042 pThis->fNoMoreFiles = false;
4043
4044 return VINF_SUCCESS;
4045}
4046
4047/**
4048 * Descends down @a iSubnode to the first entry in left most leaf node.
4049 *
4050 * @returns IPRT status code.
4051 * @param pThis The open directory instance data.
4052 * @param pRootInfo The root info structure.
4053 * @param iSubnode The subnode address to descend thru.
4054 */
4055static int rtFsNtfsDir_StackDescend(PRTFSNTFSDIR pThis, PRTFSNTFSIDXROOTINFO pRootInfo, int64_t iSubnode)
4056{
4057 for (;;)
4058 {
4059 /* Load the node. */
4060 PRTFSNTFSIDXNODE pNode;
4061 int rc = rtFsNtfsIdxRootInfo_QueryNode(pRootInfo, iSubnode, &pNode);
4062 if (RT_SUCCESS(rc))
4063 { /* likely */ }
4064 else
4065 {
4066 LogFlow(("rtFsNtfsDir_StackDescend: rtFsNtfsIdxRootInfo_QueryNode(%#RX64) error %Rrc!\n", iSubnode, rc));
4067 return rc;
4068 }
4069
4070 /* Push it onto the stack. */
4071 uint32_t iStack = pThis->cEnumStackEntries;
4072 if (iStack + 1 < pThis->cEnumStackMaxDepth)
4073 { /* likely */ }
4074 else if (pThis->cEnumStackMaxDepth < 1024)
4075 {
4076 Assert(pThis->cEnumStackMaxDepth> 0);
4077 uint32_t cDepth = pThis->cEnumStackMaxDepth * 2;
4078 Log5(("rtFsNtfsDir_ReadDir: Growing stack size to %u entries (from %u)\n", cDepth, pThis->cEnumStackMaxDepth));
4079 void *pvNew = RTMemRealloc(pThis->paEnumStack, cDepth * sizeof(pThis->paEnumStack[0]));
4080 if (pvNew)
4081 pThis->paEnumStack = (PRTFSNTFSIDXSTACKENTRY)pvNew;
4082 else
4083 return VERR_NO_MEMORY;
4084 pThis->cEnumStackMaxDepth = cDepth;
4085 }
4086 else
4087 {
4088 LogRel(("rtFsNtfsDir_StackDescend: Badly unbalanced index! (MFT record #%#RX64) -> VERR_VFS_BOGUS_FORMAT\n",
4089 pThis->pShared->RootInfo.pRootAttr->pCore->pMftRec->TreeNode.Key));
4090 return VERR_VFS_BOGUS_FORMAT;
4091 }
4092
4093 Log5(("rtFsNtfsDir_ReadDir: pushing %#RX64 (cEntries=%u, iStack=%u)\n", iSubnode, pNode->NodeInfo.cEntries, iStack));
4094 pThis->paEnumStack[iStack].iNext = 0;
4095 pThis->paEnumStack[iStack].fDescend = false;
4096 pThis->paEnumStack[iStack].pNodeInfo = &pNode->NodeInfo;
4097 pThis->cEnumStackEntries = iStack + 1;
4098
4099 /* Stop if this is a leaf node. */
4100 if ( !pNode->NodeInfo.fInternal
4101 || !pNode->NodeInfo.cEntries /* paranoia */)
4102 return VINF_SUCCESS;
4103
4104 /* Get the first entry and check that it's an internal node before trying to following it. */
4105 PCNTFSIDXENTRYHDR pFirstEntry = pNode->NodeInfo.papEntries[0];
4106 if (pFirstEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
4107 { /* likely */ }
4108 else
4109 return VINF_SUCCESS;
4110 iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pFirstEntry);
4111 }
4112}
4113
4114
4115/**
4116 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
4117 */
4118static DECLCALLBACK(int) rtFsNtfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4119 RTFSOBJATTRADD enmAddAttr)
4120{
4121 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
4122 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
4123 int rc;
4124 Log(("rtFsNtfsDir_ReadDir\n"));
4125
4126 /*
4127 * Return immediately if no files at hand.
4128 */
4129 if (pThis->fNoMoreFiles)
4130 return VERR_NO_MORE_FILES;
4131
4132 /*
4133 * Make sure we've got a stack before we jump into the fray.
4134 */
4135 if (!pThis->cEnumStackMaxDepth)
4136 {
4137 uint32_t cDepth;
4138 if (!pShared->RootInfo.pAlloc)
4139 cDepth = 2;
4140 else
4141 {
4142 cDepth = ASMBitFirstSetU64(pShared->RootInfo.pAlloc->cbValue / RT_LE2H_U32(pShared->RootInfo.pRoot->cbIndexNode));
4143 cDepth += 3;
4144 }
4145
4146 pThis->paEnumStack = (PRTFSNTFSIDXSTACKENTRY)RTMemAllocZ(cDepth * sizeof(pThis->paEnumStack[0]));
4147 if (!pThis->paEnumStack)
4148 return VERR_NO_MEMORY;
4149 pThis->cEnumStackMaxDepth = cDepth;
4150 pThis->cEnumStackEntries = 0;
4151 Log5(("rtFsNtfsDir_ReadDir: Initial stack size: %u entries\n", cDepth));
4152 //pThis->paEnumStack[0].iNext = 0;
4153 }
4154
4155 /*
4156 * Deal with '.' and '..' by using stack entry zero without setting cEnumStack to zero.
4157 * This is fine because we've got the fNoMoreFiles flag that got checked already.
4158 */
4159 size_t const cbDirEntry = *pcbDirEntry;
4160 if (pThis->cEnumStackEntries == 0)
4161 {
4162 if (pThis->paEnumStack[0].iNext <= 1)
4163 {
4164
4165 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName[pThis->paEnumStack[0].iNext + 2]);
4166 if (*pcbDirEntry > cbDirEntry)
4167 return VERR_BUFFER_OVERFLOW;
4168
4169 /* Names. */
4170 pDirEntry->cbName = pThis->paEnumStack[0].iNext + 1;
4171 pDirEntry->szName[0] = '.';
4172 pDirEntry->szName[pDirEntry->cbName - 1] = '.';
4173 pDirEntry->szName[pDirEntry->cbName] = '\0';
4174 pDirEntry->wszShortName[0] = '\0';
4175 pDirEntry->cwcShortName = 0;
4176
4177 /* Get referenced shared directory structure that we return info about. */
4178 PRTFSNTFSDIRSHRD pDotShared;
4179 if (pThis->paEnumStack[0].iNext == 0)
4180 {
4181 rtFsNtfsDirShrd_Retain(pShared);
4182 pDotShared = pShared;
4183 }
4184 else
4185 {
4186 pDotShared = NULL;
4187 rc = rtFsNtfsDirShrd_QueryParent(pShared, &pDotShared);
4188 if (RT_FAILURE(rc))
4189 {
4190 LogRel(("rtFsNtfsDir_ReadDir: couldn't find '..' filename! %Rrc\n", rc));
4191 return rc;
4192 }
4193 }
4194
4195 /* Get the info. */
4196 rc = rtFsNtfsCore_QueryInfo(pDotShared->RootInfo.pRootAttr->pCore, pDotShared->RootInfo.pRootAttr,
4197 &pDirEntry->Info, enmAddAttr);
4198 rtFsNtfsDirShrd_Release(pDotShared);
4199 if (RT_SUCCESS(rc))
4200 pThis->paEnumStack[0].iNext++;
4201 Log5(("rtFsNtfsDir_ReadDir: => '%s' (%Rrc)\n", pDirEntry->szName, rc));
4202 return rc;
4203 }
4204
4205 /*
4206 * Push the root onto the stack and decend down the left side of the tree.
4207 */
4208 PRTFSNTFSIDXNODEINFO pNodeInfo = &pShared->RootInfo.NodeInfo;
4209 pThis->paEnumStack[0].pNodeInfo = pNodeInfo;
4210 pThis->paEnumStack[0].iNext = 0;
4211 pThis->cEnumStackEntries = 1;
4212 Log5(("rtFsNtfsDir_ReadDir: pushing root\n"));
4213 if ( pNodeInfo->fInternal
4214 && pNodeInfo->cEntries > 0
4215 && (pNodeInfo->papEntries[0]->fFlags & NTFSIDXENTRYHDR_F_INTERNAL) /* parnaoia */ )
4216 {
4217 rc = rtFsNtfsDir_StackDescend(pThis, &pShared->RootInfo, NTFSIDXENTRYHDR_GET_SUBNODE(pNodeInfo->papEntries[0]));
4218 if (RT_FAILURE(rc))
4219 {
4220 pThis->fNoMoreFiles = true;
4221 rtFsNtfsDir_StackCleanup(pThis);
4222 return rc;
4223 }
4224 }
4225 }
4226
4227 /*
4228 * Work the stack.
4229 */
4230 int32_t iStack = pThis->cEnumStackEntries - 1;
4231 while (iStack >= 0)
4232 {
4233 PRTFSNTFSIDXNODEINFO pNodeInfo = pThis->paEnumStack[iStack].pNodeInfo;
4234 uint32_t iNext = pThis->paEnumStack[iStack].iNext;
4235 if (iNext < pNodeInfo->cEntries)
4236 {
4237 PCNTFSIDXENTRYHDR pEntry = pNodeInfo->papEntries[iNext];
4238 if ( !(pEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
4239 || !pThis->paEnumStack[iStack].fDescend)
4240 {
4241 if (!(pEntry->fFlags & NTFSIDXENTRYHDR_F_END))
4242 {
4243 /*
4244 * Try return the current entry.
4245 */
4246 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntry + 1);
4247
4248 /* Deal with the filename. */
4249 size_t cchFilename;
4250 rc = RTUtf16CalcUtf8LenEx(pFilename->wszFilename, pFilename->cwcFilename, &cchFilename);
4251 if (RT_FAILURE(rc))
4252 {
4253 cchFilename = 48;
4254 LogRel(("rtFsNtfsDir_ReadDir: Bad filename (%Rrc) %.*Rhxs\n",
4255 rc, pFilename->cwcFilename * sizeof(RTUTF16), pFilename->wszFilename));
4256 }
4257 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName[cchFilename + 1]);
4258 if (*pcbDirEntry > cbDirEntry)
4259 {
4260 Log5(("rtFsNtfsDir_ReadDir: returns VERR_BUFFER_OVERFLOW (for '%.*ls')\n",
4261 pFilename->cwcFilename, pFilename->wszFilename));
4262 return VERR_BUFFER_OVERFLOW;
4263 }
4264
4265 char *pszDst = pDirEntry->szName;
4266 if (RT_SUCCESS(rc))
4267 rc = RTUtf16ToUtf8Ex(pFilename->wszFilename, pFilename->cwcFilename, &pszDst,
4268 cbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName), &cchFilename);
4269 if (RT_FAILURE(rc))
4270 cchFilename = RTStrPrintf(pDirEntry->szName, cbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName),
4271 "{invalid-name-%#RX64}", NTFSMFTREF_GET_IDX(&pEntry->u.FileMftRec));
4272 pDirEntry->cbName = (uint16_t)cchFilename;
4273
4274 /* Figure out how to detect short names. */
4275 pDirEntry->cwcShortName = 0;
4276 pDirEntry->wszShortName[0] = '\0';
4277
4278 /* Standard attributes: file mode, sizes and timestamps. */
4279 pDirEntry->Info.cbObject = RT_LE2H_U64(pFilename->cbData);
4280 pDirEntry->Info.cbAllocated = RT_LE2H_U64(pFilename->cbAllocated);
4281 RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, RT_LE2H_U64(pFilename->iCreationTime));
4282 RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, RT_LE2H_U64(pFilename->iLastDataModTime));
4283 RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, RT_LE2H_U64(pFilename->iLastMftModTime));
4284 RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, RT_LE2H_U64(pFilename->iLastAccessTime));
4285 pDirEntry->Info.Attr.fMode = rtFsNtfsConvertFileattribsToMode(RT_LE2H_U32(pFilename->fFileAttribs), pFilename,
4286 RT_LE2H_U16(pEntry->cbKey));
4287
4288 /* additional stuff. */
4289 switch (enmAddAttr)
4290 {
4291 case RTFSOBJATTRADD_NOTHING:
4292 enmAddAttr = RTFSOBJATTRADD_UNIX;
4293 RT_FALL_THRU();
4294 case RTFSOBJATTRADD_UNIX:
4295 pDirEntry->Info.Attr.u.Unix.uid = NIL_RTUID;
4296 pDirEntry->Info.Attr.u.Unix.gid = NIL_RTGID;
4297 pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
4298 pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0;
4299 pDirEntry->Info.Attr.u.Unix.INodeId = NTFSMFTREF_GET_IDX(&pEntry->u.FileMftRec);
4300 pDirEntry->Info.Attr.u.Unix.fFlags = 0;
4301 pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
4302 pDirEntry->Info.Attr.u.Unix.Device = 0;
4303 break;
4304
4305 case RTFSOBJATTRADD_UNIX_OWNER:
4306 pDirEntry->Info.Attr.u.UnixOwner.uid = NIL_RTUID;
4307 pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0';
4308 break;
4309
4310 case RTFSOBJATTRADD_UNIX_GROUP:
4311 pDirEntry->Info.Attr.u.UnixGroup.gid = NIL_RTGID;
4312 pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
4313 break;
4314
4315 case RTFSOBJATTRADD_EASIZE:
4316 if (!(pFilename->fFileAttribs & RT_H2LE_U32_C(NTFS_FA_REPARSE_POINT)))
4317 pDirEntry->Info.Attr.u.EASize.cb = pFilename->u.cbPackedEas;
4318 else
4319 pDirEntry->Info.Attr.u.EASize.cb = 0;
4320 break;
4321
4322 default:
4323 AssertFailed();
4324 RT_ZERO(pDirEntry->Info.Attr.u);
4325 break;
4326 }
4327 pDirEntry->Info.Attr.enmAdditional = enmAddAttr;
4328
4329 /*
4330 * Advance the stack entry to the next entry and return.
4331 */
4332 Log5(("rtFsNtfsDir_ReadDir: => iStack=%u iNext=%u - '%.*ls'\n",
4333 iStack, iNext, pFilename->cwcFilename, pFilename->wszFilename));
4334 pThis->paEnumStack[iStack].iNext = iNext + 1;
4335 pThis->paEnumStack[iStack].fDescend = true;
4336 return VINF_SUCCESS;
4337 }
4338
4339 /*
4340 * End node, so pop it. We join the beoynd-end-of-entries path
4341 * further down, forcing the descend code to use continue.
4342 */
4343 }
4344 else
4345 {
4346 /*
4347 * Descend.
4348 */
4349 rc = rtFsNtfsDir_StackDescend(pThis, &pShared->RootInfo,
4350 NTFSIDXENTRYHDR_GET_SUBNODE(pNodeInfo->papEntries[iNext]));
4351 if (RT_SUCCESS(rc))
4352 {
4353 pThis->paEnumStack[iStack].fDescend = false;
4354 iStack = pThis->cEnumStackEntries - 1;
4355 continue;
4356 }
4357 pThis->fNoMoreFiles = true;
4358 rtFsNtfsDir_StackCleanup(pThis);
4359 return rc;
4360 }
4361 }
4362
4363 /*
4364 * Pop at stack entry.
4365 */
4366 Log5(("rtFsNtfsDir_ReadDir: popping %#RX64 (iNext=%u, cEntries=%u, iStack=%u) -> %#RX64 (iNext=%d, cEntries=%u)\n",
4367 pNodeInfo->pNode ? pNodeInfo->pNode->pNode->iSelfAddress : 0, iNext, pNodeInfo->cEntries, iStack,
4368 iStack > 0 && pThis->paEnumStack[iStack - 1].pNodeInfo->pNode
4369 ? pThis->paEnumStack[iStack - 1].pNodeInfo->pNode->pNode->iSelfAddress : UINT64_MAX,
4370 iStack > 0 ? pThis->paEnumStack[iStack - 1].iNext : -1,
4371 iStack > 0 ? pThis->paEnumStack[iStack - 1].pNodeInfo->cEntries : 0 ));
4372 rtFsNtfsIdxNode_Release(pNodeInfo->pNode);
4373 pThis->paEnumStack[iStack].pNodeInfo = NULL;
4374 pThis->cEnumStackEntries = iStack;
4375 iStack--;
4376 Assert(iStack < 0 || !pThis->paEnumStack[iStack].fDescend);
4377 }
4378
4379 /*
4380 * The End.
4381 */
4382 Log5(("rtFsNtfsDir_ReadDir: no more files\n"));
4383 pThis->fNoMoreFiles = true;
4384 return VERR_NO_MORE_FILES;
4385}
4386
4387
4388/**
4389 * NTFS directory operations.
4390 */
4391static const RTVFSDIROPS g_rtFsNtfsDirOps =
4392{
4393 { /* Obj */
4394 RTVFSOBJOPS_VERSION,
4395 RTVFSOBJTYPE_DIR,
4396 "NTFS Dir",
4397 rtFsNtfsDir_Close,
4398 rtFsNtfsDir_QueryInfo,
4399 RTVFSOBJOPS_VERSION
4400 },
4401 RTVFSDIROPS_VERSION,
4402 0,
4403 { /* ObjSet */
4404 RTVFSOBJSETOPS_VERSION,
4405 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
4406 rtFsNtfsDir_SetMode,
4407 rtFsNtfsDir_SetTimes,
4408 rtFsNtfsDir_SetOwner,
4409 RTVFSOBJSETOPS_VERSION
4410 },
4411 rtFsNtfsDir_Open,
4412 NULL /* pfnFollowAbsoluteSymlink */,
4413 NULL /* pfnOpenFile */,
4414 NULL /* pfnOpenDir */,
4415 rtFsNtfsDir_CreateDir,
4416 rtFsNtfsDir_OpenSymlink,
4417 rtFsNtfsDir_CreateSymlink,
4418 NULL /* pfnQueryEntryInfo */,
4419 rtFsNtfsDir_UnlinkEntry,
4420 rtFsNtfsDir_RenameEntry,
4421 rtFsNtfsDir_RewindDir,
4422 rtFsNtfsDir_ReadDir,
4423 RTVFSDIROPS_VERSION,
4424};
4425
4426
4427/**
4428 * Creates a new directory instance given a shared directory structure.
4429 *
4430 * @returns IPRT status code.
4431 * @param pThis The NTFS volume instance.
4432 * @param pSharedDir The shared directory structure to create a new
4433 * handle to.
4434 * @param phVfsDir Where to return the directory handle.
4435 */
4436static int rtFsNtfsVol_NewDirFromShared(PRTFSNTFSVOL pThis, PRTFSNTFSDIRSHRD pSharedDir, PRTVFSDIR phVfsDir)
4437{
4438 PRTFSNTFSDIR pNewDir;
4439 int rc = RTVfsNewDir(&g_rtFsNtfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
4440 phVfsDir, (void **)&pNewDir);
4441 if (RT_SUCCESS(rc))
4442 {
4443 rtFsNtfsDirShrd_Retain(pSharedDir);
4444 pNewDir->pShared = pSharedDir;
4445 pNewDir->cEnumStackEntries = 0;
4446 pNewDir->cEnumStackMaxDepth = 0;
4447 pNewDir->paEnumStack = NULL;
4448 return VINF_SUCCESS;
4449 }
4450 return rc;
4451}
4452
4453
4454
4455/*
4456 *
4457 * Volume level code.
4458 * Volume level code.
4459 * Volume level code.
4460 *
4461 */
4462
4463
4464/**
4465 * Slow path for querying the allocation state of a cluster.
4466 *
4467 * @returns IPRT status code.
4468 * @param pThis The NTFS volume instance.
4469 * @param iCluster The cluster to query.
4470 * @param pfState Where to return the state.
4471 */
4472static int rtFsNtfsVol_QueryClusterStateSlow(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
4473{
4474 int rc;
4475 uint64_t const cbWholeBitmap = RT_LE2H_U64(pThis->pMftBitmap->pAttrHdr->u.NonRes.cbData);
4476 uint64_t const offInBitmap = iCluster >> 3;
4477 if (offInBitmap < cbWholeBitmap)
4478 {
4479 if (!pThis->pvBitmap)
4480 {
4481 /*
4482 * Try cache the whole bitmap if it's not too large.
4483 */
4484 if ( cbWholeBitmap <= RTFSNTFS_MAX_WHOLE_BITMAP_CACHE
4485 && cbWholeBitmap >= RT_ALIGN_64(pThis->cClusters >> 3, 8))
4486 {
4487 pThis->cbBitmapAlloc = RT_ALIGN_Z((uint32_t)cbWholeBitmap, 8);
4488 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
4489 if (pThis->pvBitmap)
4490 {
4491 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
4492 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, 0, pThis->pvBitmap, (uint32_t)cbWholeBitmap);
4493 if (RT_SUCCESS(rc))
4494 {
4495 pThis->iFirstBitmapCluster = 0;
4496 pThis->cBitmapClusters = pThis->cClusters;
4497 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iCluster);
4498 return VINF_SUCCESS;
4499 }
4500 RTMemFree(pThis->pvBitmap);
4501 pThis->pvBitmap = NULL;
4502 pThis->cbBitmapAlloc = 0;
4503 return rc;
4504 }
4505 }
4506
4507 /*
4508 * Do a cluster/4K cache.
4509 */
4510 pThis->cbBitmapAlloc = RT_MAX(pThis->cbCluster, _4K);
4511 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
4512 if (!pThis->pvBitmap)
4513 {
4514 pThis->cbBitmapAlloc = 0;
4515 return VERR_NO_MEMORY;
4516 }
4517 }
4518
4519 /*
4520 * Load a cache line.
4521 */
4522 Assert(RT_IS_POWER_OF_TWO(pThis->cbBitmapAlloc));
4523 uint64_t offLoad = offInBitmap & ~(pThis->cbBitmapAlloc - 1);
4524 uint32_t cbLoad = (uint32_t)RT_MIN(cbWholeBitmap - offLoad, pThis->cbBitmapAlloc);
4525
4526 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
4527 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, offLoad, pThis->pvBitmap, cbLoad);
4528 if (RT_SUCCESS(rc))
4529 {
4530 pThis->iFirstBitmapCluster = offLoad << 3;
4531 pThis->cBitmapClusters = cbLoad << 3;
4532 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)(iCluster - pThis->iFirstBitmapCluster));
4533 return VINF_SUCCESS;
4534 }
4535 pThis->cBitmapClusters = 0;
4536 }
4537 else
4538 {
4539 LogRel(("rtFsNtfsVol_QueryClusterStateSlow: iCluster=%#RX64 is outside the bitmap (%#RX64)\n", iCluster, cbWholeBitmap));
4540 rc = VERR_OUT_OF_RANGE;
4541 }
4542 return rc;
4543}
4544
4545
4546/**
4547 * Query the allocation state of the given cluster.
4548 *
4549 * @returns IPRT status code.
4550 * @param pThis The NTFS volume instance.
4551 * @param iCluster The cluster to query.
4552 * @param pfState Where to return the state.
4553 */
4554static int rtFsNtfsVol_QueryClusterState(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
4555{
4556 uint64_t iClusterInCache = iCluster - pThis->iFirstBitmapCluster;
4557 if (iClusterInCache < pThis->cBitmapClusters)
4558 {
4559 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iClusterInCache);
4560 return VINF_SUCCESS;
4561 }
4562 return rtFsNtfsVol_QueryClusterStateSlow(pThis, iCluster, pfState);
4563}
4564
4565
4566/**
4567 * Callback for RTAvlU64Destroy used by rtFsNtfsVol_Close to destroy the MFT
4568 * record cache.
4569 *
4570 * @returns VINF_SUCCESS
4571 * @param pNode The MFT record to destroy.
4572 * @param pvUser Ignored.
4573 */
4574static DECLCALLBACK(int) rtFsNtFsVol_DestroyCachedMftRecord(PAVLU64NODECORE pNode, void *pvUser)
4575{
4576 PRTFSNTFSMFTREC pMftRec = (PRTFSNTFSMFTREC)pNode;
4577 RT_NOREF(pvUser);
4578
4579 RTMemFree(pMftRec->pbRec);
4580 pMftRec->pbRec = NULL;
4581 RTMemFree(pMftRec);
4582
4583 return VINF_SUCCESS;
4584}
4585
4586
4587
4588/**
4589 * Callback for RTAvlU64Destroy used by rtFsNtfsVol_Close to destroy the index
4590 * node cache.
4591 *
4592 * @returns VINF_SUCCESS
4593 * @param pNode The index node to destroy.
4594 * @param pvUser Ignored.
4595 */
4596static DECLCALLBACK(int) rtFsNtfsVol_DestroyIndexNode(PAVLU64NODECORE pNode, void *pvUser)
4597{
4598 PRTFSNTFSIDXNODE pIdxNode = (PRTFSNTFSIDXNODE)pNode;
4599 RT_NOREF(pvUser);
4600
4601 RTMemFree(pIdxNode->pNode);
4602 RTMemFree(pIdxNode->NodeInfo.papEntries);
4603 pIdxNode->pNode = NULL;
4604 pIdxNode->NodeInfo.papEntries = NULL;
4605 pIdxNode->NodeInfo.pIndexHdr = NULL;
4606 pIdxNode->NodeInfo.pVol = NULL;
4607 return VINF_SUCCESS;
4608}
4609
4610
4611/**
4612 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4613 */
4614static DECLCALLBACK(int) rtFsNtfsVol_Close(void *pvThis)
4615{
4616 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
4617 Log(("rtFsNtfsVol_Close(%p):\n", pThis));
4618
4619 /*
4620 * Index / directory related members.
4621 */
4622 if (pThis->pRootDir)
4623 {
4624 rtFsNtfsDirShrd_Release(pThis->pRootDir);
4625 pThis->pRootDir = NULL;
4626 }
4627
4628 RTAvlU64Destroy(&pThis->IdxNodeCacheRoot, rtFsNtfsVol_DestroyIndexNode, NULL);
4629
4630 RTMemFree(pThis->pawcUpcase);
4631 pThis->pawcUpcase = NULL;
4632
4633 pThis->IdxNodeUnusedHead.pPrev = pThis->IdxNodeUnusedHead.pNext = NULL;
4634
4635 /*
4636 * Allocation bitmap cache.
4637 */
4638 if (pThis->pMftBitmap)
4639 {
4640 rtFsNtfsCore_Release(pThis->pMftBitmap->pCore);
4641 pThis->pMftBitmap = NULL;
4642 }
4643 RTMemFree(pThis->pvBitmap);
4644 pThis->pvBitmap = NULL;
4645
4646 /*
4647 * The MFT and MFT cache.
4648 */
4649 if (pThis->pMftData)
4650 {
4651 rtFsNtfsCore_Release(pThis->pMftData->pCore);
4652 pThis->pMftData = NULL;
4653 }
4654
4655 Assert(RTListIsEmpty(&pThis->CoreInUseHead));
4656 PRTFSNTFSCORE pCurCore, pNextCore;
4657 RTListForEachSafe(&pThis->CoreInUseHead, pCurCore, pNextCore, RTFSNTFSCORE, ListEntry)
4658 rtFsNtfsCore_Destroy(pCurCore);
4659 RTListForEachSafe(&pThis->CoreUnusedHead, pCurCore, pNextCore, RTFSNTFSCORE, ListEntry)
4660 rtFsNtfsCore_Destroy(pCurCore);
4661
4662 pThis->CoreInUseHead.pPrev = pThis->CoreInUseHead.pNext = NULL;
4663 pThis->CoreUnusedHead.pPrev = pThis->CoreUnusedHead.pNext = NULL;
4664
4665 Assert(pThis->MftRoot == NULL);
4666 RTAvlU64Destroy(&pThis->MftRoot, rtFsNtFsVol_DestroyCachedMftRecord, NULL);
4667
4668 /*
4669 * Backing file and handles.
4670 */
4671 RTVfsFileRelease(pThis->hVfsBacking);
4672 pThis->hVfsBacking = NIL_RTVFSFILE;
4673 pThis->hVfsSelf = NIL_RTVFS;
4674
4675 return VINF_SUCCESS;
4676}
4677
4678
4679/**
4680 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4681 */
4682static DECLCALLBACK(int) rtFsNtfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4683{
4684 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
4685 return VERR_WRONG_TYPE;
4686}
4687
4688
4689/**
4690 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
4691 */
4692static DECLCALLBACK(int) rtFsNtfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
4693{
4694 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
4695 AssertReturn(pThis->pRootDir, VERR_INTERNAL_ERROR_4);
4696 int rc = rtFsNtfsVol_NewDirFromShared(pThis, pThis->pRootDir, phVfsDir);
4697 LogFlow(("rtFsNtfsVol_OpenRoot: returns %Rrc\n", rc));
4698 return rc;
4699}
4700
4701
4702/**
4703 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
4704 */
4705static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
4706{
4707 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
4708 *pfUsed = true;
4709
4710 /*
4711 * Round to a cluster range.
4712 */
4713 uint64_t iCluster = off >> pThis->cClusterShift;
4714
4715 Assert(RT_IS_POWER_OF_TWO(pThis->cbCluster));
4716 cb += off & (pThis->cbCluster - 1);
4717 cb = RT_ALIGN_Z(cb, pThis->cbCluster);
4718 size_t cClusters = cb >> pThis->cClusterShift;
4719
4720 /*
4721 * Check the clusters one-by-one.
4722 * Just to be cautious, we will always check the cluster at off, even when cb is zero.
4723 */
4724 do
4725 {
4726 bool fState = true;
4727 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
4728 if (RT_FAILURE(rc))
4729 return rc;
4730 if (fState)
4731 {
4732 *pfUsed = true;
4733 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - used\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
4734 return VINF_SUCCESS;
4735 }
4736
4737 iCluster++;
4738 } while (cClusters-- > 0);
4739
4740 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - unused\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
4741 *pfUsed = false;
4742 return VINF_SUCCESS;
4743}
4744
4745
4746/**
4747 * NTFS volume operations.
4748 */
4749static const RTVFSOPS g_rtFsNtfsVolOps =
4750{
4751 /* .Obj = */
4752 {
4753 /* .uVersion = */ RTVFSOBJOPS_VERSION,
4754 /* .enmType = */ RTVFSOBJTYPE_VFS,
4755 /* .pszName = */ "NtfsVol",
4756 /* .pfnClose = */ rtFsNtfsVol_Close,
4757 /* .pfnQueryInfo = */ rtFsNtfsVol_QueryInfo,
4758 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
4759 },
4760 /* .uVersion = */ RTVFSOPS_VERSION,
4761 /* .fFeatures = */ 0,
4762 /* .pfnOpenRoot = */ rtFsNtfsVol_OpenRoot,
4763 /* .pfnQueryRangeState = */ rtFsNtfsVol_QueryRangeState,
4764 /* .uEndMarker = */ RTVFSOPS_VERSION
4765};
4766
4767
4768/**
4769 * Checks that the storage for the given attribute is all marked allocated in
4770 * the allocation bitmap of the volume.
4771 *
4772 * @returns IPRT status code.
4773 * @param pThis The NTFS volume instance.
4774 * @param pAttr The attribute to check.
4775 * @param pszDesc Description of the attribute.
4776 * @param pErrInfo Where to return error details.
4777 */
4778static int rtFsNtfsVolCheckBitmap(PRTFSNTFSVOL pThis, PRTFSNTFSATTR pAttr, const char *pszDesc, PRTERRINFO pErrInfo)
4779{
4780 PRTFSNTFSATTRSUBREC pSubRec = NULL;
4781 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
4782 uint64_t offFile = 0;
4783 for (;;)
4784 {
4785 uint32_t const cExtents = pTable->cExtents;
4786 PRTFSNTFSEXTENT paExtents = pTable->paExtents;
4787 for (uint32_t iExtent = 0; iExtent < cExtents; iExtent++)
4788 {
4789 uint64_t const off = paExtents[iExtent].off;
4790 if (off == UINT64_MAX)
4791 offFile += paExtents[iExtent].cbExtent;
4792 else
4793 {
4794 uint64_t iCluster = off >> pThis->cClusterShift;
4795 uint64_t cClusters = paExtents[iExtent].cbExtent >> pThis->cClusterShift;
4796 Assert((cClusters << pThis->cClusterShift) == paExtents[iExtent].cbExtent);
4797 Assert(cClusters != 0);
4798
4799 while (cClusters-- > 0)
4800 {
4801 bool fState = false;
4802 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
4803 if (RT_FAILURE(rc))
4804 return RTERRINFO_LOG_REL_SET_F(pErrInfo, rc,
4805 "Error querying allocation bitmap entry %#RX64 (for %s offset %#RX64)",
4806 iCluster, pszDesc, offFile);
4807 if (!fState)
4808 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4809 "Cluster %#RX64 at offset %#RX64 in %s is not marked allocated",
4810 iCluster, offFile, pszDesc);
4811 offFile += pThis->cbCluster;
4812 }
4813 }
4814 }
4815
4816 /* Next table. */
4817 pSubRec = pSubRec ? pSubRec->pNext : pAttr->pSubRecHead;
4818 if (!pSubRec)
4819 return VINF_SUCCESS;
4820 pTable = &pSubRec->Extents;
4821 }
4822}
4823
4824
4825/**
4826 * Loads, validates and setups the '.' (NTFS_MFT_IDX_ROOT) MFT entry.
4827 *
4828 * @returns IPRT status code
4829 * @param pThis The NTFS volume instance. Will set pawcUpcase.
4830 * @param pErrInfo Where to return additional error info.
4831 */
4832static int rtFsNtfsVolLoadRootDir(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
4833{
4834 /*
4835 * Load it and do some checks.
4836 */
4837 PRTFSNTFSCORE pCore;
4838 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_ROOT, false /*fRelaxedUsa*/, &pCore, pErrInfo); // DON'T COMMIT
4839 if (RT_SUCCESS(rc))
4840 {
4841 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
4842 if (!pFilenameAttr)
4843 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: has no FILENAME attribute!");
4844 else if (pFilenameAttr->pAttrHdr->fNonResident)
4845 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: FILENAME attribute is non-resident!");
4846 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[1]))
4847 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4848 "RootDir: FILENAME attribute value size is too small: %#x",
4849 pFilenameAttr->pAttrHdr->u.Res.cbValue);
4850 else
4851 {
4852 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
4853 + pFilenameAttr->pAttrHdr->u.Res.offValue);
4854 if ( pFilename->cwcFilename != 1
4855 || ( RTUtf16NICmpAscii(pFilename->wszFilename, ".", 1) != 0
4856 && RTUtf16NICmpAscii(pFilename->wszFilename, "$", 1) != 0))
4857 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4858 "RootDir: FILENAME is not '.' nor '$: '%.*ls'",
4859 pFilename->cwcFilename, pFilename->wszFilename);
4860 else
4861 {
4862 PRTFSNTFSATTR pIndexRoot = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
4863 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
4864 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
4865 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
4866 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
4867 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
4868 if (!pIndexRoot)
4869 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ROOT attribute named $I30");
4870 else if (!pIndexAlloc && pIndexBitmap)
4871 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ALLOCATION attribute named $I30");
4872 else if (!pIndexBitmap && pIndexAlloc)
4873 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no BITMAP attribute named $I30");
4874 if (RT_SUCCESS(rc) && pIndexAlloc)
4875 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexAlloc, "RootDir", pErrInfo);
4876 if (RT_SUCCESS(rc) && pIndexBitmap)
4877 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexBitmap, "RootDir/bitmap", pErrInfo);
4878 if (RT_SUCCESS(rc))
4879 {
4880 /*
4881 * Load it as a normal directory.
4882 */
4883 PRTFSNTFSDIRSHRD pSharedDir;
4884 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, &pSharedDir, pErrInfo, "RootDir");
4885 if (RT_SUCCESS(rc))
4886 {
4887 rtFsNtfsCore_Release(pCore);
4888 pThis->pRootDir = pSharedDir;
4889 return VINF_SUCCESS;
4890 }
4891 }
4892 }
4893 }
4894 rtFsNtfsCore_Release(pCore);
4895 }
4896 else
4897 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Root dir: Error reading MFT record");
4898 return rc;
4899}
4900
4901
4902/**
4903 * Loads, validates and setups the '$UpCase' (NTFS_MFT_IDX_UP_CASE) MFT entry.
4904 *
4905 * This is needed for filename lookups, I think.
4906 *
4907 * @returns IPRT status code
4908 * @param pThis The NTFS volume instance. Will set pawcUpcase.
4909 * @param pErrInfo Where to return additional error info.
4910 */
4911static int rtFsNtfsVolLoadUpCase(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
4912{
4913 PRTFSNTFSCORE pCore;
4914 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_UP_CASE, false /*fRelaxedUsa*/, &pCore, pErrInfo);
4915 if (RT_SUCCESS(rc))
4916 {
4917 PRTFSNTFSATTR pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
4918 if (pDataAttr)
4919 {
4920 /*
4921 * Validate the '$Upcase' MFT record.
4922 */
4923 uint32_t const cbMin = 512;
4924 uint32_t const cbMax = _128K;
4925 if (!pDataAttr->pAttrHdr->fNonResident)
4926 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: unnamed DATA attribute is resident!");
4927 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) < cbMin
4928 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) > cbMax)
4929 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4930 "$UpCase: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX32",
4931 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated), cbMin, cbMax);
4932 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) < cbMin
4933 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
4934 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
4935 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) & 1) )
4936 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4937 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
4938 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData), cbMin,
4939 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
4940 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) < cbMin
4941 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized)
4942 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated)
4943 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) & 1) )
4944 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4945 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
4946 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized), cbMin,
4947 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
4948 else if (pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit != 0)
4949 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4950 "$UpCase: unnamed DATA attribute is compressed: %#x",
4951 pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit);
4952 else
4953 {
4954 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
4955 if (!pFilenameAttr)
4956 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase has no FILENAME attribute!");
4957 else if (pFilenameAttr->pAttrHdr->fNonResident)
4958 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase FILENAME attribute is non-resident!");
4959 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
4960 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4961 "$UpCase: FILENAME attribute value size is too small: %#x",
4962 pFilenameAttr->pAttrHdr->u.Res.cbValue);
4963 else
4964 {
4965 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
4966 + pFilenameAttr->pAttrHdr->u.Res.offValue);
4967 if ( pFilename->cwcFilename != 7
4968 || RTUtf16NICmpAscii(pFilename->wszFilename, "$UpCase", 7) != 0)
4969 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4970 "$UpCase: FILENAME isn't '$UpCase': '%.*ls'",
4971 pFilename->cwcFilename, pFilename->wszFilename);
4972 else
4973 {
4974 /*
4975 * Allocate memory for the uppercase table and read it.
4976 */
4977 PRTUTF16 pawcUpcase;
4978 pThis->pawcUpcase = pawcUpcase = (PRTUTF16)RTMemAlloc(_64K * sizeof(pThis->pawcUpcase[0]));
4979 if (pawcUpcase)
4980 {
4981 for (size_t i = 0; i < _64K; i++)
4982 pawcUpcase[i] = (uint16_t)i;
4983
4984 rc = rtFsNtfsAttr_Read(pDataAttr, 0, pawcUpcase, pDataAttr->pAttrHdr->u.NonRes.cbData);
4985 if (RT_SUCCESS(rc))
4986 {
4987 /*
4988 * Check the data.
4989 */
4990 for (size_t i = 1; i < _64K; i++)
4991 if (pawcUpcase[i] == 0)
4992 {
4993 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4994 "$UpCase entry %#x is zero!", i);
4995 break;
4996 }
4997
4998 /*
4999 * While we still have the $UpCase file open, check it against the allocation bitmap.
5000 */
5001 if (RT_SUCCESS(rc))
5002 rc = rtFsNtfsVolCheckBitmap(pThis, pDataAttr, "$UpCase", pErrInfo);
5003
5004 /* We're done, no need for special success return here though. */
5005 }
5006 else
5007 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, rc, "Error reading $UpCase data into memory");
5008 }
5009 else
5010 rc = VERR_NO_MEMORY;
5011 }
5012 }
5013 }
5014 }
5015 else
5016 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: has no unnamed DATA attribute!");
5017 rtFsNtfsCore_Release(pCore);
5018 }
5019 else
5020 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$UpCase: Error reading the MFT record");
5021 return rc;
5022}
5023
5024
5025/**
5026 * Loads the allocation bitmap and does basic validation of.
5027 *
5028 * @returns IPRT status code.
5029 * @param pThis The NTFS volume instance. Will set up the
5030 * 'Allocation bitmap and cache' fields.
5031 * @param pErrInfo Where to return error details.
5032 */
5033static int rtFsNtfsVolLoadBitmap(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
5034{
5035 PRTFSNTFSCORE pCore;
5036 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_BITMAP, false /*fRelaxedUsa*/, &pCore, pErrInfo);
5037 if (RT_SUCCESS(rc))
5038 {
5039 PRTFSNTFSATTR pMftBitmap;
5040 pThis->pMftBitmap = pMftBitmap = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
5041 if (pMftBitmap)
5042 {
5043 /*
5044 * Validate the '$Bitmap' MFT record.
5045 * We expect the bitmap to be fully initialized and be sized according to the
5046 * formatted volume size. Allegedly, NTFS pads it to an even 8 byte in size.
5047 */
5048 uint64_t const cbMinBitmap = RT_ALIGN_64(pThis->cbVolume >> (pThis->cClusterShift + 3), 8);
5049 uint64_t const cbMaxBitmap = RT_ALIGN_64(cbMinBitmap, pThis->cbCluster);
5050 //uint64_t const cbMinInitialized = RT_ALIGN_64((RT_MAX(pThis->uLcnMft, pThis->uLcnMftMirror) + 16) >> 3, 8);
5051 if (!pMftBitmap->pAttrHdr->fNonResident)
5052 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is resident!");
5053 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) < cbMinBitmap
5054 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) > cbMaxBitmap)
5055 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5056 "$Bitmap: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
5057 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated), cbMinBitmap, cbMaxBitmap);
5058 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) < cbMinBitmap
5059 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData)
5060 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData))
5061 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5062 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
5063 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData), cbMinBitmap,
5064 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
5065 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) < cbMinBitmap
5066 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized)
5067 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated))
5068 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5069 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
5070 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized), cbMinBitmap,
5071 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
5072 else if (pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit != 0)
5073 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5074 "$Bitmap: unnamed DATA attribute is compressed: %#x",
5075 pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit);
5076 else if (pMftBitmap->Extents.cExtents != 1) /* paranoia for now */
5077 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5078 "$Bitmap: unnamed DATA attribute is expected to have a single extent: %u extents",
5079 pMftBitmap->Extents.cExtents);
5080 else if (pMftBitmap->Extents.paExtents[0].off == UINT64_MAX)
5081 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is sparse");
5082 else
5083 {
5084 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
5085 if (!pFilenameAttr)
5086 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 has no FILENAME attribute!");
5087 else if (pFilenameAttr->pAttrHdr->fNonResident)
5088 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 FILENAME attribute is non-resident!");
5089 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
5090 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5091 "$Bitmap FILENAME attribute value size is too small: %#x",
5092 pFilenameAttr->pAttrHdr->u.Res.cbValue);
5093 else
5094 {
5095 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
5096 + pFilenameAttr->pAttrHdr->u.Res.offValue);
5097 if ( pFilename->cwcFilename != 7
5098 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Bitmap", 7) != 0)
5099 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5100 "$Bitmap: FILENAME isn't '$Bitmap': '%.*ls'",
5101 pFilename->cwcFilename, pFilename->wszFilename);
5102 else
5103 {
5104 /*
5105 * Read some of it into the buffer and check that essential stuff is flagged as allocated.
5106 */
5107 /* The boot sector. */
5108 bool fState = false;
5109 rc = rtFsNtfsVol_QueryClusterState(pThis, 0, &fState);
5110 if (RT_SUCCESS(rc) && !fState)
5111 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5112 "MFT allocation bitmap error: Bootsector isn't marked allocated!");
5113 else if (RT_FAILURE(rc))
5114 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5115 "MFT allocation bitmap (offset 0) read error: %Rrc", rc);
5116
5117 /* The bitmap ifself, the MFT data, and the MFT bitmap. */
5118 if (RT_SUCCESS(rc))
5119 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftBitmap, "allocation bitmap", pErrInfo);
5120 if (RT_SUCCESS(rc))
5121 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftData, "MFT", pErrInfo);
5122 if (RT_SUCCESS(rc))
5123 rc = rtFsNtfsVolCheckBitmap(pThis,
5124 rtFsNtfsCore_FindUnnamedAttribute(pThis->pMftData->pCore, NTFS_AT_BITMAP),
5125 "MFT Bitmap", pErrInfo);
5126 if (RT_SUCCESS(rc))
5127 {
5128 /*
5129 * Looks like the bitmap is good.
5130 */
5131 return VINF_SUCCESS;
5132 }
5133 }
5134 }
5135 }
5136 pThis->pMftBitmap = NULL;
5137 }
5138 else
5139 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Bitmap: has no unnamed DATA attribute!");
5140 rtFsNtfsCore_Release(pCore);
5141 }
5142 else
5143 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$Bitmap: Error MFT record");
5144 return rc;
5145}
5146
5147
5148/**
5149 * Loads, validates and setups the '$Volume' (NTFS_MFT_IDX_VOLUME) MFT entry.
5150 *
5151 * @returns IPRT status code
5152 * @param pThis The NTFS volume instance. Will set uNtfsVersion
5153 * and fVolumeFlags.
5154 * @param pErrInfo Where to return additional error info.
5155 */
5156static int rtFsNtfsVolLoadVolumeInfo(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
5157{
5158 PRTFSNTFSCORE pCore;
5159 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_VOLUME, false /*fRelaxedUsa*/, &pCore, pErrInfo);
5160 if (RT_SUCCESS(rc))
5161 {
5162 PRTFSNTFSATTR pVolInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_VOLUME_INFORMATION);
5163 if (pVolInfoAttr)
5164 {
5165 /*
5166 * Validate the '$Volume' MFT record.
5167 */
5168 if (pVolInfoAttr->pAttrHdr->fNonResident)
5169 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume unnamed VOLUME_INFORMATION attribute is not resident!");
5170 else if ( pVolInfoAttr->cbResident != sizeof(NTFSATVOLUMEINFO)
5171 || pVolInfoAttr->cbValue != sizeof(NTFSATVOLUMEINFO))
5172 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5173 "$Volume VOLUME_INFORMATION attribute has the wrong size: cbValue=%#RX64, cbResident=%#RX32, expected %#x\n",
5174 pVolInfoAttr->cbValue, pVolInfoAttr->cbResident, sizeof(NTFSATVOLUMEINFO));
5175 else
5176 {
5177 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
5178 if (!pFilenameAttr)
5179 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume has no FILENAME attribute!");
5180 else if (pFilenameAttr->pAttrHdr->fNonResident)
5181 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume FILENAME attribute is non-resident!");
5182 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
5183 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5184 "$Volume FILENAME attribute value size is too small: %#x",
5185 pFilenameAttr->pAttrHdr->u.Res.cbValue);
5186 else
5187 {
5188 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
5189 + pFilenameAttr->pAttrHdr->u.Res.offValue);
5190 if ( pFilename->cwcFilename != 7
5191 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Volume", 7) != 0)
5192 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5193 "$Volume FILENAME isn't '$Volume': '%.*ls'",
5194 pFilename->cwcFilename, pFilename->wszFilename);
5195 else
5196 {
5197 /*
5198 * Look at the information.
5199 */
5200 PCNTFSATVOLUMEINFO pVolInfo;
5201 pVolInfo = (PCNTFSATVOLUMEINFO)((uint8_t *)pVolInfoAttr->pAttrHdr + pVolInfoAttr->pAttrHdr->u.Res.offValue);
5202 pThis->uNtfsVersion = RTFSNTFS_MAKE_VERSION(pVolInfo->uMajorVersion, pVolInfo->uMinorVersion);
5203 pThis->fVolumeFlags = RT_LE2H_U16(pVolInfo->fFlags);
5204 Log(("NTFS: Version %u.%u, flags=%#x\n", pVolInfo->uMajorVersion, pVolInfo->uMinorVersion, pThis->fVolumeFlags));
5205
5206 /* We're done, no need for special success return here though. */
5207 }
5208 }
5209 }
5210 }
5211 else
5212 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5213 "MFT record $Volume has no unnamed VOLUME_INFORMATION attribute!");
5214 rtFsNtfsCore_Release(pCore);
5215 }
5216 else
5217 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading $Volume MFT record");
5218 return rc;
5219}
5220
5221
5222/**
5223 * Loads, validates and setups the '$Mft' (NTFS_MFT_IDX_MFT) MFT entry.
5224 *
5225 * This is the first thing we do after we've checked out the boot sector and
5226 * extracted information from it, since everything else depends on us being able
5227 * to access the MFT data.
5228 *
5229 * @returns IPRT status code
5230 * @param pThis The NTFS volume instance. Will set pMftData.
5231 * @param pErrInfo Where to return additional error info.
5232 */
5233static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
5234{
5235 /*
5236 * Bootstrap the MFT data stream.
5237 */
5238 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, NTFS_MFT_IDX_MFT);
5239 AssertReturn(pRec, VERR_NO_MEMORY);
5240
5241#if 0 && defined(LOG_ENABLED)
5242 for (uint32_t i = 0; i < 128; i++)
5243 {
5244 uint64_t const offDisk = (pThis->uLcnMft << pThis->cClusterShift) + i * pThis->cbMftRecord;
5245 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
5246 if (RT_SUCCESS(rc))
5247 {
5248 pRec->TreeNode.Key = i;
5249 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
5250 pRec->TreeNode.Key = 0;
5251 }
5252 }
5253#endif
5254
5255 uint64_t const offDisk = pThis->uLcnMft << pThis->cClusterShift;
5256 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
5257 if (RT_SUCCESS(rc))
5258 {
5259 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, true, pErrInfo);
5260 if (RT_SUCCESS(rc))
5261 {
5262#ifdef LOG_ENABLED
5263 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
5264#endif
5265 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
5266 }
5267 if (RT_SUCCESS(rc))
5268 {
5269 PRTFSNTFSCORE pCore = pRec->pCore;
5270 PRTFSNTFSATTR pMftData;
5271 pThis->pMftData = pMftData = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
5272 if (pMftData)
5273 {
5274 /*
5275 * Validate the '$Mft' MFT record.
5276 */
5277 PNTFSATTRIBHDR pAttrHdr = pMftData->pAttrHdr;
5278 if (!pAttrHdr->fNonResident)
5279 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 unnamed DATA attribute is resident!");
5280 else if ( (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated) < pThis->cbMftRecord * 16U
5281 || (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated) >= pThis->cbBacking)
5282 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5283 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
5284 RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated));
5285 else if ( (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized) < pThis->cbMftRecord * 16U
5286 || (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized) >= pThis->cbBacking)
5287 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5288 "MFT record #0 unnamed DATA attribute initialized size is out of range: %#RX64",
5289 RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized));
5290 else if ( (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbData) < pThis->cbMftRecord * 16U
5291 || (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbData) >= pThis->cbBacking)
5292 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5293 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
5294 RT_LE2H_U64(pAttrHdr->u.NonRes.cbData));
5295 else if (pAttrHdr->u.NonRes.uCompressionUnit != 0)
5296 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5297 "MFT record #0 unnamed DATA attribute is compressed: %#x",
5298 pAttrHdr->u.NonRes.uCompressionUnit);
5299 else if (pMftData->Extents.cExtents == 0)
5300 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5301 "MFT record #0 unnamed DATA attribute has no data on the disk");
5302 else if (pMftData->Extents.paExtents[0].off != offDisk)
5303 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5304 "MFT record #0 unnamed DATA attribute has a bogus disk offset: %#RX64, expected %#RX64",
5305 pMftData->Extents.paExtents[0].off, offDisk);
5306 else if (!rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_BITMAP))
5307 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed BITMAP attribute!");
5308 else
5309 {
5310 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
5311 if (!pFilenameAttr)
5312 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no FILENAME attribute!");
5313 else if (pFilenameAttr->pAttrHdr->fNonResident)
5314 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 FILENAME attribute is non-resident!");
5315 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[4]))
5316 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5317 "MFT record #0 FILENAME attribute value size is too small: %#x",
5318 pFilenameAttr->pAttrHdr->u.Res.cbValue);
5319 else
5320 {
5321 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
5322 + pFilenameAttr->pAttrHdr->u.Res.offValue);
5323 if ( pFilename->cwcFilename != 4
5324 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Mft", 4) != 0)
5325 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5326 "MFT record #0 FILENAME isn't '$Mft': '%.*ls'",
5327 pFilename->cwcFilename, pFilename->wszFilename);
5328 else
5329 {
5330 /*
5331 * Looks like we're good. Insert core record into the cache.
5332 */
5333 RTListAppend(&pThis->CoreInUseHead, &pCore->ListEntry);
5334 pThis->cbCoreObjects += pCore->cbCost;
5335
5336 Assert(pCore->cRefs == 1);
5337 Assert(pRec->cRefs == 2);
5338 rtFsNtfsMftRec_Release(pRec, pThis);
5339
5340 return VINF_SUCCESS;
5341 }
5342 }
5343 }
5344 pThis->pMftData = NULL;
5345 }
5346 else
5347 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!");
5348 }
5349 if (pRec->pCore)
5350 rtFsNtfsCore_Destroy(pRec->pCore);
5351 rtFsNtfsMftRec_Release(pRec, pThis);
5352 }
5353 else
5354 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #0");
5355 return rc;
5356}
5357
5358
5359/**
5360 * Loads the bootsector and parses it, copying values into the instance data.
5361 *
5362 * @returns IRPT status code.
5363 * @param pThis The instance data.
5364 * @param pvBuf The buffer.
5365 * @param cbBuf The buffer size.
5366 * @param pErrInfo Where to return additional error details.
5367 */
5368static int rtFsNtfsVolLoadAndParseBootsector(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5369{
5370 AssertReturn(cbBuf >= sizeof(FATBOOTSECTOR), VERR_INTERNAL_ERROR_2);
5371
5372 /*
5373 * Read the boot sector and check that it makes sense for a NTFS volume.
5374 *
5375 * Note! There are two potential backup locations of the boot sector, however we
5376 * currently don't implement falling back on these on corruption/read errors.
5377 */
5378 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pvBuf;
5379 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, pBootSector, sizeof(*pBootSector), NULL);
5380 if (RT_FAILURE(rc))
5381 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading boot sector");
5382
5383 if (memcmp(pBootSector->achOemName, RT_STR_TUPLE(NTFS_OEM_ID_MAGIC)) != 0)
5384 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5385 "Not NTFS - OEM field mismatch: %.8Rhxs'", pBootSector->achOemName);
5386
5387 /* Check must-be-zero BPB fields. */
5388 if (pBootSector->Bpb.Ntfs.Bpb.cReservedSectors != 0)
5389 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cReservedSectors=%u",
5390 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cReservedSectors));
5391 if (pBootSector->Bpb.Ntfs.Bpb.cFats != 0)
5392 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cFats=%u",
5393 pBootSector->Bpb.Ntfs.Bpb.cFats);
5394 if (pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries != 0)
5395 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cMaxRootDirEntries=%u",
5396 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries));
5397 if (pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16 != 0)
5398 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cTotalSectors16=%u",
5399 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16));
5400 if (pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat != 0)
5401 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cSectorsPerFat=%u",
5402 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat));
5403
5404 /* Check other relevant BPB fields. */
5405 uint32_t cbSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cbSector);
5406 if ( cbSector != 512
5407 && cbSector != 1024
5408 && cbSector != 2048
5409 && cbSector != 4096)
5410 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - BPB.cbSector is ouf of range: %u", cbSector);
5411 pThis->cbSector = cbSector;
5412 Log2(("NTFS BPB: cbSector=%#x\n", cbSector));
5413
5414 uint32_t cClusterPerSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerCluster);
5415 if ( !RT_IS_POWER_OF_TWO(cClusterPerSector)
5416 || cClusterPerSector == 0)
5417 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5418 "Not NTFS - BPB.cCluster is ouf of range: %u", cClusterPerSector);
5419
5420 pThis->cbCluster = cClusterPerSector * cbSector;
5421 if (pThis->cbCluster > _64K)
5422 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5423 "cluster size exceeds 64KB: %#x", pThis->cbCluster);
5424 pThis->cClusterShift = ASMBitFirstSetU32(pThis->cbCluster) - 1;
5425 Log2(("NTFS BPB: cClusterPerSector=%#x => %#x bytes, %u shift\n", cClusterPerSector, pThis->cbCluster, pThis->cClusterShift));
5426 pThis->iMaxVirtualCluster = (uint64_t)INT64_MAX >> pThis->cClusterShift;
5427 Log2(("NTFS BPB: iMaxVirtualCluster=%#RX64\n", pThis->iMaxVirtualCluster));
5428
5429 /* NTFS BPB: cSectors. */
5430 uint64_t cSectors = RT_LE2H_U64(pBootSector->Bpb.Ntfs.cSectors);
5431 if (cSectors > pThis->cbBacking / pThis->cbSector)
5432 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5433 "NTFS sector count exceeds volume size: %#RX64 vs %#RX64",
5434 cSectors, pThis->cbBacking / pThis->cbSector);
5435 if (cSectors < 256)
5436 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "NTFS sector count too small: %#RX64", cSectors);
5437 pThis->cbVolume = cSectors * pThis->cbSector;
5438 pThis->cClusters = cSectors / cClusterPerSector;
5439 Log2(("NTFS BPB: cSectors=%#RX64 => %#xu bytes (%Rhcb) => cClusters=%#RX64\n",
5440 cSectors, pThis->cbVolume, pThis->cbVolume, pThis->cClusters));
5441
5442 /* NTFS BPB: MFT location. */
5443 uint64_t uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMft);
5444 if ( uLcn < 1
5445 || uLcn >= pThis->cClusters)
5446 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5447 "NTFS MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
5448 pThis->uLcnMft = uLcn;
5449 Log2(("NTFS BPB: uLcnMft=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
5450
5451 /* NTFS BPB: Mirror MFT location. */
5452 uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMftMirror);
5453 if ( uLcn < 1
5454 || uLcn >= pThis->cClusters)
5455 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5456 "NTFS mirror MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
5457 pThis->uLcnMftMirror = uLcn;
5458 Log2(("NTFS BPB: uLcnMftMirror=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
5459
5460 /* NTFS BPB: Size of MFT file record. */
5461 if (pBootSector->Bpb.Ntfs.cClustersPerMftRecord >= 0)
5462 {
5463 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord)
5464 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord == 0)
5465 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5466 "NTFS clusters-per-mft-record value is zero or not a power of two: %#x",
5467 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
5468 pThis->cbMftRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord << pThis->cClusterShift;
5469 Assert(pThis->cbMftRecord == pBootSector->Bpb.Ntfs.cClustersPerMftRecord * pThis->cbCluster);
5470 }
5471 else if ( pBootSector->Bpb.Ntfs.cClustersPerMftRecord < -20
5472 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord > -9)
5473 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5474 "NTFS clusters-per-mft-record is out of shift range: %d",
5475 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
5476 else
5477 pThis->cbMftRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
5478 Log2(("NTFS BPB: cbMftRecord=%#x\n", pThis->cbMftRecord));
5479 if ( pThis->cbMftRecord > _32K
5480 || pThis->cbMftRecord < 256)
5481 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5482 "Unsupported NTFS MFT record size: %#x", pThis->cbMftRecord);
5483
5484 /* NTFS BPB: Default index node size */
5485 if (pBootSector->Bpb.Ntfs.cClustersPerIndexNode >= 0)
5486 {
5487 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode)
5488 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode == 0)
5489 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5490 "NTFS default clusters-per-index-tree-node is zero or not a power of two: %#x",
5491 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
5492 pThis->cbDefaultIndexNode = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode << pThis->cClusterShift;
5493 Assert(pThis->cbDefaultIndexNode == pBootSector->Bpb.Ntfs.cClustersPerIndexNode * pThis->cbCluster);
5494 }
5495 else if ( pBootSector->Bpb.Ntfs.cClustersPerIndexNode < -32
5496 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode > -9)
5497 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5498 "NTFS default clusters-per-index-tree-node is out of shift range: %d",
5499 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
5500 else
5501 pThis->cbDefaultIndexNode = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
5502 Log2(("NTFS BPB: cbDefaultIndexNode=%#x\n", pThis->cbDefaultIndexNode));
5503
5504 pThis->uSerialNo = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uSerialNumber);
5505 Log2(("NTFS BPB: uSerialNo=%#x\n", pThis->uSerialNo));
5506
5507
5508 return VINF_SUCCESS;
5509}
5510
5511
5512RTDECL(int) RTFsNtfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fNtfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
5513{
5514 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
5515 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
5516 AssertReturn(!fNtfsFlags, VERR_INVALID_FLAGS);
5517
5518 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
5519 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5520
5521 /*
5522 * Create a VFS instance and initialize the data so rtFsNtfsVol_Close works.
5523 */
5524 RTVFS hVfs;
5525 PRTFSNTFSVOL pThis;
5526 int rc = RTVfsNew(&g_rtFsNtfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
5527 if (RT_SUCCESS(rc))
5528 {
5529 pThis->hVfsBacking = hVfsFileIn;
5530 pThis->hVfsSelf = hVfs;
5531 pThis->fMntFlags = fMntFlags;
5532 pThis->fNtfsFlags = fNtfsFlags;
5533 RTListInit(&pThis->CoreInUseHead);
5534 RTListInit(&pThis->CoreUnusedHead);
5535 RTListInit(&pThis->IdxNodeUnusedHead);
5536
5537 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
5538 if (RT_SUCCESS(rc))
5539 {
5540 void *pvBuf = RTMemTmpAlloc(_64K);
5541 if (pvBuf)
5542 {
5543 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo);
5544 if (RT_SUCCESS(rc))
5545 rc = rtFsNtfsVolLoadMft(pThis, pErrInfo);
5546 if (RT_SUCCESS(rc))
5547 rc = rtFsNtfsVolLoadVolumeInfo(pThis, pErrInfo);
5548 if (RT_SUCCESS(rc))
5549 rc = rtFsNtfsVolLoadBitmap(pThis, pErrInfo);
5550 if (RT_SUCCESS(rc))
5551 rc = rtFsNtfsVolLoadUpCase(pThis, pErrInfo);
5552 if (RT_SUCCESS(rc))
5553 rc = rtFsNtfsVolLoadRootDir(pThis, pErrInfo);
5554 RTMemTmpFree(pvBuf);
5555 if (RT_SUCCESS(rc))
5556 {
5557 *phVfs = hVfs;
5558 return VINF_SUCCESS;
5559 }
5560 }
5561 else
5562 rc = VERR_NO_TMP_MEMORY;
5563 }
5564
5565 RTVfsRelease(hVfs);
5566 *phVfs = NIL_RTVFS;
5567 }
5568 else
5569 RTVfsFileRelease(hVfsFileIn);
5570
5571 return rc;
5572}
5573
5574
5575/**
5576 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5577 */
5578static DECLCALLBACK(int) rtVfsChainNtfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5579 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5580{
5581 RT_NOREF(pProviderReg);
5582
5583 /*
5584 * Basic checks.
5585 */
5586 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5587 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5588 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5589 && pElement->enmType != RTVFSOBJTYPE_DIR)
5590 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5591 if (pElement->cArgs > 1)
5592 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5593
5594 /*
5595 * Parse the flag if present, save in pElement->uProvider.
5596 */
5597 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
5598 if (pElement->cArgs > 0)
5599 {
5600 const char *psz = pElement->paArgs[0].psz;
5601 if (*psz)
5602 {
5603 if (!strcmp(psz, "ro"))
5604 fReadOnly = true;
5605 else if (!strcmp(psz, "rw"))
5606 fReadOnly = false;
5607 else
5608 {
5609 *poffError = pElement->paArgs[0].offSpec;
5610 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
5611 }
5612 }
5613 }
5614
5615 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
5616 return VINF_SUCCESS;
5617}
5618
5619
5620/**
5621 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5622 */
5623static DECLCALLBACK(int) rtVfsChainNtfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
5624 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
5625 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
5626{
5627 RT_NOREF(pProviderReg, pSpec, poffError);
5628
5629 int rc;
5630 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
5631 if (hVfsFileIn != NIL_RTVFSFILE)
5632 {
5633 RTVFS hVfs;
5634 rc = RTFsNtfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
5635 RTVfsFileRelease(hVfsFileIn);
5636 if (RT_SUCCESS(rc))
5637 {
5638 *phVfsObj = RTVfsObjFromVfs(hVfs);
5639 RTVfsRelease(hVfs);
5640 if (*phVfsObj != NIL_RTVFSOBJ)
5641 return VINF_SUCCESS;
5642 rc = VERR_VFS_CHAIN_CAST_FAILED;
5643 }
5644 }
5645 else
5646 rc = VERR_VFS_CHAIN_CAST_FAILED;
5647 return rc;
5648}
5649
5650
5651/**
5652 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
5653 */
5654static DECLCALLBACK(bool) rtVfsChainNtfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
5655 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
5656 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
5657{
5658 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
5659 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
5660 || !pReuseElement->paArgs[0].uProvider)
5661 return true;
5662 return false;
5663}
5664
5665
5666/** VFS chain element 'file'. */
5667static RTVFSCHAINELEMENTREG g_rtVfsChainNtfsVolReg =
5668{
5669 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
5670 /* fReserved = */ 0,
5671 /* pszName = */ "ntfs",
5672 /* ListEntry = */ { NULL, NULL },
5673 /* pszHelp = */ "Open a NTFS file system, requires a file object on the left side.\n"
5674 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
5675 /* pfnValidate = */ rtVfsChainNtfsVol_Validate,
5676 /* pfnInstantiate = */ rtVfsChainNtfsVol_Instantiate,
5677 /* pfnCanReuseElement = */ rtVfsChainNtfsVol_CanReuseElement,
5678 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
5679};
5680
5681RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainNtfsVolReg, rtVfsChainNtfsVolReg);
5682
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