VirtualBox

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

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

iprt/formats/ntfs: build fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.2 KB
Line 
1/* $Id: ntfsvfs.cpp 69862 2017-11-28 19:04:10Z 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/file.h>
38#include <iprt/log.h>
39#include <iprt/mem.h>
40#include <iprt/string.h>
41#include <iprt/vfs.h>
42#include <iprt/vfslowlevel.h>
43#include <iprt/formats/ntfs.h>
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49/** Pointer to the instance data for a NTFS volume. */
50typedef struct RTFSNTFSVOL *PRTFSNTFSVOL;
51/** Pointer to a NTFS MFT record. */
52typedef struct RTFSNTFSMFTREC *PRTFSNTFSMFTREC;
53
54/**
55 * NTFS MFT record.
56 */
57typedef struct RTFSNTFSMFTREC
58{
59 /** MFT record number as key. */
60 AVLU64NODECORE Core;
61 /** Pointer to the next MFT record if chained. */
62 PRTFSNTFSMFTREC pNext;
63 /** Pointer back to the volume. */
64 PRTFSNTFSVOL pVol;
65 /** The disk offset of this MFT entry. */
66 uint64_t offDisk;
67 union
68 {
69 /** Generic pointer. */
70 uint8_t *pbRec;
71 /** Pointer to the file record. */
72 PNTFSRECFILE pFileRec;
73 } RT_UNION_NM(u);
74
75 /** Reference counter. */
76 uint32_t volatile cRefs;
77
78 // ....
79} RTFSNTFSMFTREC;
80
81/**
82 * Instance data for an NTFS volume.
83 */
84typedef struct RTFSNTFSVOL
85{
86 /** Handle to itself. */
87 RTVFS hVfsSelf;
88 /** The file, partition, or whatever backing the NTFS volume. */
89 RTVFSFILE hVfsBacking;
90 /** The size of the backing thingy. */
91 uint64_t cbBacking;
92 /** The formatted size of the volume. */
93 uint64_t cbVolume;
94 /** cbVolume expressed as a cluster count. */
95 uint64_t cClusters;
96
97 /** RTVFSMNT_F_XXX. */
98 uint32_t fMntFlags;
99 /** RTFSNTVFS_F_XXX (currently none defined). */
100 uint32_t fNtfsFlags;
101
102 /** The (logical) cluster size. */
103 uint32_t cbCluster;
104 /** The (logical) sector size. */
105 uint32_t cbSector;
106
107 /** The shift count for converting between bytes and clusters. */
108 uint8_t cClusterShift;
109 /** Explicit padding. */
110 uint8_t abReserved[7];
111
112 /** The logical cluster number of the MFT. */
113 uint64_t uLcnMft;
114 /** The logical cluster number of the mirror MFT. */
115 uint64_t uLcnMftMirror;
116
117 /** The MFT record size. */
118 uint32_t cbMftRecord;
119 /** The index record size. */
120 uint32_t cbIndexRecord;
121
122 /** The volume serial number. */
123 uint64_t uSerialNo;
124
125 /** Pointer to the MFT record for the MFT. */
126 PRTFSNTFSMFTREC pMft;
127
128 /** Root of the MFT record tree (RTFSNTFSMFTREC). */
129 AVLU64TREE MftRoot;
130} RTFSNTFSVOL;
131
132
133static PRTFSNTFSMFTREC rtFsNtfsMftRec_New(PRTFSNTFSVOL pVol, uint64_t idMft)
134{
135 PRTFSNTFSMFTREC pRec = (PRTFSNTFSMFTREC)RTMemAllocZ(sizeof(*pRec));
136 if (pRec)
137 {
138 pRec->pbRec = (uint8_t *)RTMemAllocZ(pVol->cbMftRecord);
139 if (pRec->pbRec)
140 {
141 pRec->Core.Key = idMft;
142 pRec->pNext = NULL;
143 pRec->offDisk = UINT64_MAX / 2;
144 pRec->pVol = pVol;
145 pRec->cRefs = 1;
146 return pRec;
147 }
148 }
149 return NULL;
150}
151
152
153#if 0 /* currently unused */
154static uint32_t rtFsNtfsMftRec_Destroy(PRTFSNTFSMFTREC pThis)
155{
156 RTMemFree(pThis->pbRec);
157 pThis->pbRec = NULL;
158
159 PAVLU64NODECORE pRemoved = RTAvlU64Remove(&pThis->pVol->MftRoot, pThis->Core.Key);
160 Assert(pRemoved == &pThis->Core); NOREF(pRemoved);
161
162 pThis->pVol = NULL;
163 RTMemFree(pThis);
164
165 return 0;
166}
167
168
169static uint32_t rtFsNtfsMftRec_Retain(PRTFSNTFSMFTREC pThis)
170{
171 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
172 Assert(cRefs < 64);
173 return cRefs;
174}
175
176
177static uint32_t rtFsNtfsMftRec_Release(PRTFSNTFSMFTREC pThis)
178{
179 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
180 Assert(cRefs < 64);
181 if (cRefs != 0)
182 return cRefs;
183 return rtFsNtfsMftRec_Destroy(pThis);
184}
185#endif
186
187
188#ifdef LOG_ENABLED
189/**
190 * Logs the MFT record
191 *
192 * @param pRec The MFT record to log.
193 */
194static void rtfsNtfsMftRec_Log(PRTFSNTFSMFTREC pRec)
195{
196 if (LogIs2Enabled())
197 {
198 PCNTFSRECFILE pFileRec = pRec->pFileRec;
199 Log2(("NTFS: MFT #%#RX64 at %#RX64\n", pRec->Core.Key, pRec->offDisk));
200 if (pFileRec->Hdr.uMagic == NTFSREC_MAGIC_FILE)
201 {
202 size_t const cbRec = pRec->pVol->cbMftRecord;
203 uint8_t const * const pbRec = pRec->pbRec;
204
205 Log2(("NTFS: FILE record: \n"));
206 Log2(("NTFS: UpdateSeqArray %#x L %#x\n", RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray), RT_LE2H_U16(pFileRec->Hdr.cUpdateSeqEntries) ));
207 Log2(("NTFS: uLsn %#RX64\n", RT_LE2H_U64(pFileRec->uLsn)));
208 Log2(("NTFS: uRecReuseSeqNo %#RX16\n", RT_LE2H_U16(pFileRec->uRecReuseSeqNo)));
209 Log2(("NTFS: cLinks %#RX16\n", RT_LE2H_U16(pFileRec->cLinks)));
210 Log2(("NTFS: offFirstAttrib %#RX16\n", RT_LE2H_U16(pFileRec->offFirstAttrib)));
211 Log2(("NTFS: fFlags %#RX16%s%s\n", RT_LE2H_U16(pFileRec->fFlags),
212 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_IN_USE ? " in-use" : "",
213 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_DIRECTORY ? " directory" : ""));
214 Log2(("NTFS: cbRecUsed %#RX32\n", RT_LE2H_U32(pFileRec->cbRecUsed)));
215 Log2(("NTFS: BaseMftRec %#RX64, sqn %#x\n",
216 NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec), NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec)));
217 Log2(("NTFS: idNextAttrib %#RX16\n", RT_LE2H_U16(pFileRec->idNextAttrib)));
218 if ( RT_LE2H_U16(pFileRec->offFirstAttrib) >= sizeof(*pFileRec)
219 && ( RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray) >= sizeof(*pFileRec)
220 || pFileRec->Hdr.offUpdateSeqArray == 0))
221 {
222 Log2(("NTFS: uPaddingOrUsa %#RX16\n", pFileRec->uPaddingOrUsa));
223 Log2(("NTFS: idxMftSelf %#RX32\n", RT_LE2H_U32(pFileRec->idxMftSelf)));
224 }
225
226 uint32_t offRec = pFileRec->offFirstAttrib;
227 size_t cbRecUsed = RT_MIN(cbRec, pFileRec->cbRecUsed);
228 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
229 {
230 PCNTFSATTRIBHDR pHdr = (PCNTFSATTRIBHDR)&pbRec[offRec];
231 uint32_t const cbAttrib = RT_LE2H_U32(pHdr->cbAttrib);
232 Log2(("NTFS: @%#05x: Attrib record: %#x LB %#x, instance #%#x, fFlags=%#RX16, %s\n", offRec,
233 RT_LE2H_U32(pHdr->uAttrType), cbAttrib, RT_LE2H_U16(pHdr->idAttrib), RT_LE2H_U16(pHdr->fFlags),
234 pHdr->fNonResident == 0 ? "resident" : pHdr->fNonResident == 1 ? "non-resident" : "bad-resident-flag"));
235 if (pHdr->offName && pHdr->cwcName)
236 {
237 if (offRec + RT_LE2H_U16(pHdr->offName) + pHdr->cwcName * sizeof(RTUTF16) <= cbRec)
238 Log2(("NTFS: Name %.*ls\n", pHdr->cwcName,&pbRec[offRec + RT_LE2H_U16(pHdr->offName)]));
239 else
240 Log2(("NTFS: Name <!out of bounds!> %#x L %#x\n", RT_LE2H_U16(pHdr->offName), pHdr->cwcName));
241 }
242 switch (pHdr->uAttrType)
243 {
244 case NTFS_AT_UNUSED: Log2(("NTFS: Type: UNUSED\n")); break;
245 case NTFS_AT_STANDARD_INFORMATION: Log2(("NTFS: Type: STANDARD_INFORMATION\n")); break;
246 case NTFS_AT_ATTRIBUTE_LIST: Log2(("NTFS: Type: ATTRIBUTE_LIST\n")); break;
247 case NTFS_AT_FILENAME: Log2(("NTFS: Type: FILENAME\n")); break;
248 case NTFS_AT_OBJECT_ID: Log2(("NTFS: Type: OBJECT_ID\n")); break;
249 case NTFS_AT_SECURITY_DESCRIPTOR: Log2(("NTFS: Type: SECURITY_DESCRIPTOR\n")); break;
250 case NTFS_AT_VOLUME_NAME: Log2(("NTFS: Type: VOLUME_NAME\n")); break;
251 case NTFS_AT_VOLUME_INFORMATION: Log2(("NTFS: Type: VOLUME_INFORMATION\n")); break;
252 case NTFS_AT_DATA: Log2(("NTFS: Type: DATA\n")); break;
253 case NTFS_AT_INDEX_ROOT: Log2(("NTFS: Type: INDEX_ROOT\n")); break;
254 case NTFS_AT_INDEX_ALLOCATION: Log2(("NTFS: Type: INDEX_ALLOCATION\n")); break;
255 case NTFS_AT_BITMAP: Log2(("NTFS: Type: BITMAP\n")); break;
256 case NTFS_AT_REPARSE_POINT: Log2(("NTFS: Type: REPARSE_POINT\n")); break;
257 case NTFS_AT_EA_INFORMATION: Log2(("NTFS: Type: EA_INFORMATION\n")); break;
258 case NTFS_AT_EA: Log2(("NTFS: Type: EA\n")); break;
259 case NTFS_AT_PROPERTY_SET: Log2(("NTFS: Type: PROPERTY_SET\n")); break;
260 case NTFS_AT_LOGGED_UTILITY_STREAM: Log2(("NTFS: Type: LOGGED_UTILITY_STREAM\n")); break;
261 default:
262 if (RT_LE2H_U32(pHdr->uAttrType) >= RT_LE2H_U32_C(NTFS_AT_FIRST_USER_DEFINED))
263 Log2(("NTFS: Type: unknown user defined - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
264 else
265 Log2(("NTFS: Type: unknown - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
266 break;
267 }
268
269 size_t const cbMaxAttrib = cbRec - offRec;
270 if (!pHdr->fNonResident)
271 {
272 uint16_t const offValue = RT_LE2H_U16(pHdr->Res.offValue);
273 uint32_t const cbValue = RT_LE2H_U32(pHdr->Res.cbValue);
274 Log2(("NTFS: Value: %#x LB %#x, fFlags=%#x bReserved=%#x\n",
275 offValue, cbValue, pHdr->Res.fFlags, pHdr->Res.bReserved));
276 if ( offValue < cbMaxAttrib
277 && cbValue < cbMaxAttrib
278 && offValue + cbValue <= cbMaxAttrib)
279 {
280 uint8_t const *pbValue = &pbRec[offRec + offValue];
281 RTTIMESPEC Spec;
282 char sz[80];
283 switch (pHdr->uAttrType)
284 {
285 case NTFS_AT_STANDARD_INFORMATION:
286 {
287 PCNTFSATSTDINFO pInfo = (PCNTFSATSTDINFO)pbValue;
288 if (cbValue >= NTFSATSTDINFO_SIZE_NTFS_V12)
289 {
290 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
291 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
292 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
293 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
294 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
295 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
296 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
297 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
298 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
299 Log2(("NTFS: cMaxFileVersions %#RX32\n", RT_LE2H_U32(pInfo->cMaxFileVersions) ));
300 Log2(("NTFS: uFileVersion %#RX32\n", RT_LE2H_U32(pInfo->uFileVersion) ));
301 }
302 else
303 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATSTDINFO!\n",
304 cbValue, NTFSATSTDINFO_SIZE_NTFS_V12));
305 if (cbValue >= sizeof(*pInfo))
306 {
307 Log2(("NTFS: idClass %#RX32\n", RT_LE2H_U32(pInfo->idClass) ));
308 Log2(("NTFS: idOwner %#RX32\n", RT_LE2H_U32(pInfo->idOwner) ));
309 Log2(("NTFS: idSecurity %#RX32\n", RT_LE2H_U32(pInfo->idSecurity) ));
310 Log2(("NTFS: cbQuotaChared %#RX64\n", RT_LE2H_U64(pInfo->cbQuotaChared) ));
311 Log2(("NTFS: idxUpdateSequence %#RX64\n", RT_LE2H_U64(pInfo->idxUpdateSequence) ));
312 }
313 if (cbValue > sizeof(*pInfo))
314 Log2(("NTFS: Undefined data: %.*Rhxs\n", cbValue - sizeof(*pInfo), &pbValue[sizeof(*pInfo)]));
315 break;
316 }
317
318 //case NTFS_AT_ATTRIBUTE_LIST:
319
320 case NTFS_AT_FILENAME:
321 {
322 PCNTFSATFILENAME pInfo = (PCNTFSATFILENAME)pbValue;
323 if (cbValue >= RT_OFFSETOF(NTFSATFILENAME, wszFilename))
324 {
325 Log2(("NTFS: ParentDirMftRec %#RX64, sqn %#x\n",
326 NTFSMFTREF_GET_IDX(&pInfo->ParentDirMftRec), NTFSMFTREF_GET_SEQ(&pInfo->ParentDirMftRec) ));
327 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
328 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
329 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
330 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
331 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
332 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
333 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
334 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
335 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
336 RT_LE2H_U64(pInfo->cbAllocated), RT_LE2H_U64(pInfo->cbAllocated)));
337 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
338 RT_LE2H_U64(pInfo->cbData), RT_LE2H_U64(pInfo->cbData)));
339 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
340 if (RT_LE2H_U32(pInfo->fFileAttribs) & NTFS_FA_REPARSE_POINT)
341 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pInfo->uReparseTag) ));
342 else
343 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pInfo->cbPackedEas) ));
344 Log2(("NTFS: cwcFilename %#x\n", pInfo->cwcFilename));
345 Log2(("NTFS: fFilenameType %#x\n", pInfo->fFilenameType));
346 if (cbValue >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
347 Log2(("NTFS: wszFilename '%.*ls'\n", pInfo->cwcFilename, pInfo->wszFilename ));
348 else
349 Log2(("NTFS: Error! Truncated filename!!\n"));
350 }
351 else
352 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATFILENAME!\n",
353 cbValue, RT_OFFSETOF(NTFSATFILENAME, wszFilename) ));
354 break;
355 }
356
357 //case NTFS_AT_OBJECT_ID:
358 //case NTFS_AT_SECURITY_DESCRIPTOR:
359 //case NTFS_AT_VOLUME_NAME:
360 //case NTFS_AT_VOLUME_INFORMATION:
361 //case NTFS_AT_DATA:
362 //case NTFS_AT_INDEX_ROOT:
363 //case NTFS_AT_INDEX_ALLOCATION:
364 //case NTFS_AT_BITMAP:
365 //case NTFS_AT_REPARSE_POINT:
366 //case NTFS_AT_EA_INFORMATION:
367 //case NTFS_AT_EA:
368 //case NTFS_AT_PROPERTY_SET:
369 //case NTFS_AT_LOGGED_UTILITY_STREAM:
370
371 default:
372 if (cbValue <= 24)
373 Log2(("NTFS: %.*Rhxs\n", cbValue, pbValue));
374 else
375 Log2(("%.*Rhxd\n", cbValue, pbValue));
376 break;
377 }
378
379 }
380 else
381 Log2(("NTFS: !Value is out of bounds!\n"));
382 }
383 else if (RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED) <= cbMaxAttrib)
384 {
385 Log2(("NTFS: VNC range %#RX64 .. %#RX64 (%#RX64 clusters)\n",
386 RT_LE2H_U64(pHdr->NonRes.iVcnFirst), RT_LE2H_U64(pHdr->NonRes.iVcnLast),
387 RT_LE2H_U64(pHdr->NonRes.iVcnLast) - RT_LE2H_U64(pHdr->NonRes.iVcnFirst) + 1));
388 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
389 RT_LE2H_U64(pHdr->NonRes.cbAllocated), RT_LE2H_U64(pHdr->NonRes.cbAllocated)));
390 Log2(("NTFS: cbInitialized %#RX64 (%Rhcb)\n",
391 RT_LE2H_U64(pHdr->NonRes.cbInitialized), RT_LE2H_U64(pHdr->NonRes.cbInitialized)));
392 uint16_t const offMappingPairs = RT_LE2H_U16(pHdr->NonRes.offMappingPairs);
393 Log2(("NTFS: offMappingPairs %#RX16\n", offMappingPairs));
394 if ( pHdr->NonRes.abReserved[0] || pHdr->NonRes.abReserved[1]
395 || pHdr->NonRes.abReserved[2] || pHdr->NonRes.abReserved[3] || pHdr->NonRes.abReserved[4] )
396 Log2(("NTFS: abReserved %.7Rhxs\n", pHdr->NonRes.abReserved));
397 if (pHdr->NonRes.uCompressionUnit != 0)
398 Log2(("NTFS: Compression unit 2^%u clusters\n", pHdr->NonRes.uCompressionUnit));
399
400 if ( NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED <= cbMaxAttrib
401 && NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED <= cbAttrib
402 && ( offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
403 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED))
404 Log2(("NTFS: cbCompressed %#RX64 (%Rhcb)\n",
405 RT_LE2H_U64(pHdr->NonRes.cbCompressed), RT_LE2H_U64(pHdr->NonRes.cbCompressed)));
406 else if (pHdr->NonRes.uCompressionUnit != 0 && pHdr->NonRes.uCompressionUnit != 64)
407 Log2(("NTFS: !Error! Compressed attrib fields are out of bound!\n"));
408
409 if ( offMappingPairs < cbAttrib
410 && offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
411 {
412 uint8_t const *pbPairs = &pbRec[offRec + offMappingPairs];
413 uint32_t const cbMaxPairs = cbAttrib - offMappingPairs;
414 int64_t iVnc = pHdr->NonRes.iVcnFirst;
415 Log2(("NTFS: Mapping Pairs: %.*Rhxsd\n", cbMaxPairs, pbPairs));
416 if (!iVnc && !*pbPairs)
417 Log2(("NTFS: [0]: Empty\n", cbMaxPairs, pbPairs));
418 else
419 {
420 if (iVnc != 0)
421 Log2(("NTFS: [0]: VCN=%#012RX64 L %#012RX64 - not mapped\n", 0, iVnc));
422 int64_t iLnc = 0;
423 uint32_t iPair = 0;
424 uint32_t offPairs = 0;
425 while (offPairs < cbMaxPairs)
426 {
427 /* First byte: 4-bit length of each of the pair values */
428 uint8_t const bLengths = pbPairs[offPairs];
429 if (!bLengths)
430 break;
431 uint8_t const cbRun = (bLengths & 0x0f) + (bLengths >> 4);
432 if (offPairs + cbRun > cbMaxPairs)
433 {
434 Log2(("NTFS: [%d]: run overrun! cbRun=%#x bLengths=%#x offPairs=%#x cbMaxPairs=%#x\n",
435 iPair, cbRun, bLengths, offPairs, cbMaxPairs));
436 break;
437 }
438
439 /* Value 1: Number of (virtual) clusters in this run. */
440 int64_t cClustersInRun;
441 uint8_t cbNum = (bLengths & 0xf);
442 if (cbNum)
443 {
444 int8_t const *pbNum = (int8_t const *)&pbPairs[offPairs + cbNum]; /* last byte */
445 cClustersInRun = *pbNum--;
446 while (cbNum-- > 1)
447 cClustersInRun = (cClustersInRun << 8) + *pbNum--;
448 }
449 else
450 cClustersInRun = -1;
451
452 /* Value 2: The logical cluster delta to get to the first cluster in the run. */
453 cbNum = bLengths >> 4;
454 if (cbNum)
455 {
456 int8_t const *pbNum = (int8_t const *)&pbPairs[offPairs + cbNum + (bLengths & 0xf)]; /* last byte */
457 int64_t cLcnDelta = *pbNum--;
458 while (cbNum-- > 1)
459 cLcnDelta = (cLcnDelta << 8) + *pbNum--;
460 iLnc += cLcnDelta;
461 Log2(("NTFS: [%d]: VNC=%#012RX64 L %#012RX64 => LNC=%#012RX64\n",
462 iPair, iVnc, cClustersInRun, iLnc));
463 }
464 else
465 Log2(("NTFS: [%d]: VNC=%#012RX64 L %#012RX64 => HOLE\n",
466 iPair, iVnc, cClustersInRun));
467
468 /* Advance. */
469 iVnc += cClustersInRun;
470 offPairs += 1 + cbRun;
471 iPair++;
472 }
473 }
474 }
475 else if ( cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
476 && cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
477 {
478 Log2(("NTFS: Warning! Odd non-resident attribute size: %#x!\n", cbAttrib));
479 if (cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
480 Log2(("NTFS: @%05x: %.*Rhxs!\n", offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
481 cbAttrib - NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
482 &pbRec[offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED]));
483 }
484 }
485 else
486 Log2(("NTFS: !Attrib header is out of bound!\n"));
487
488 /* Advance. */
489 offRec += RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_RESIDENT);
490 }
491
492 /* Anything left? */
493 if (offRec < cbRecUsed)
494 Log2(("NTFS: @%#05x: Tail: %.*Rhxs\n", offRec, cbRecUsed - offRec, &pbRec[offRec]));
495 }
496 else
497 Log2(("NTFS: Unknown record type: %.4Rhxs\n", pFileRec));
498 }
499}
500#endif /* LOG_ENABLED */
501
502
503/**
504 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
505 */
506static DECLCALLBACK(int) rtFsNtfsVol_Close(void *pvThis)
507{
508 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
509
510 RTVfsFileRelease(pThis->hVfsBacking);
511 pThis->hVfsBacking = NIL_RTVFSFILE;
512 pThis->hVfsSelf = NIL_RTVFS;
513
514 return VINF_SUCCESS;
515}
516
517
518/**
519 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
520 */
521static DECLCALLBACK(int) rtFsNtfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
522{
523 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
524 return VERR_WRONG_TYPE;
525}
526
527
528/**
529 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
530 */
531static DECLCALLBACK(int) rtFsNtfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
532{
533 NOREF(pvThis); NOREF(phVfsDir);
534 return VERR_NOT_IMPLEMENTED;
535}
536
537
538/**
539 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
540 */
541static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
542{
543 NOREF(pvThis); NOREF(off); NOREF(cb); NOREF(pfUsed);
544 return VERR_NOT_IMPLEMENTED;
545}
546
547
548DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsNtfsVolOps =
549{
550 /* .Obj = */
551 {
552 /* .uVersion = */ RTVFSOBJOPS_VERSION,
553 /* .enmType = */ RTVFSOBJTYPE_VFS,
554 /* .pszName = */ "NtfsVol",
555 /* .pfnClose = */ rtFsNtfsVol_Close,
556 /* .pfnQueryInfo = */ rtFsNtfsVol_QueryInfo,
557 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
558 },
559 /* .uVersion = */ RTVFSOPS_VERSION,
560 /* .fFeatures = */ 0,
561 /* .pfnOpenRoot = */ rtFsNtfsVol_OpenRoot,
562 /* .pfnQueryRangeState = */ rtFsNtfsVol_QueryRangeState,
563 /* .uEndMarker = */ RTVFSOPS_VERSION
564};
565
566static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
567{
568 AssertReturn(cbBuf >= _64K, VERR_INTERNAL_ERROR_2);
569 NOREF(pThis); NOREF(pvBuf); NOREF(cbBuf); NOREF(pErrInfo);
570 /** @todo read MFT, find bitmap allocation, implement
571 * rtFsNtfsVol_QueryRangeState. */
572
573 PRTFSNTFSMFTREC pRec = rtFsNtfsMftRec_New(pThis, 0);
574 AssertReturn(pRec, VERR_NO_MEMORY);
575 pThis->pMft = pRec;
576
577 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->uLcnMft << pThis->cClusterShift, pRec->pbRec, pThis->cbMftRecord, NULL);
578 if (RT_FAILURE(rc))
579 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading MFT record #0");
580#ifdef LOG_ENABLED
581 rtfsNtfsMftRec_Log(pRec);
582#endif
583
584 //Log(("MFT#0:\n%.*Rhxd\n", pThis->cbMftRecord, pRec->pbRec));
585
586 return VINF_SUCCESS;
587}
588
589
590/**
591 * Loads the bootsector and parses it, copying values into the instance data.
592 *
593 * @returns IRPT status code.
594 * @param pThis The instance data.
595 * @param pvBuf The buffer.
596 * @param cbBuf The buffer size.
597 * @param pErrInfo Where to return additional error details.
598 */
599static int rtFsNtfsVolLoadAndParseBootsector(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
600{
601 AssertReturn(cbBuf >= sizeof(FATBOOTSECTOR), VERR_INTERNAL_ERROR_2);
602
603 /*
604 * Read the boot sector and check that it makes sense for a NTFS volume.
605 *
606 * Note! There are two potential backup locations of the boot sector, however we
607 * currently don't implement falling back on these on corruption/read errors.
608 */
609 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pvBuf;
610 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, pBootSector, sizeof(*pBootSector), NULL);
611 if (RT_FAILURE(rc))
612 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading boot sector");
613
614 if (memcmp(pBootSector->achOemName, RT_STR_TUPLE(NTFS_OEM_ID_MAGIC)) != 0)
615 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
616 "Not NTFS - OEM field mismatch: %.8Rhxs'", pBootSector->achOemName);
617
618 /* Check must-be-zero BPB fields. */
619 if (pBootSector->Bpb.Ntfs.Bpb.cReservedSectors != 0)
620 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cReservedSectors=%u",
621 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cReservedSectors));
622 if (pBootSector->Bpb.Ntfs.Bpb.cFats != 0)
623 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cFats=%u",
624 pBootSector->Bpb.Ntfs.Bpb.cFats);
625 if (pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries != 0)
626 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cMaxRootDirEntries=%u",
627 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries));
628 if (pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16 != 0)
629 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cTotalSectors16=%u",
630 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16));
631 if (pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat != 0)
632 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cSectorsPerFat=%u",
633 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat));
634
635 /* Check other relevant BPB fields. */
636 uint32_t cbSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cbSector);
637 if ( cbSector != 512
638 && cbSector != 1024
639 && cbSector != 2048
640 && cbSector != 4096)
641 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - BPB.cbSector is ouf of range: %u", cbSector);
642 pThis->cbSector = cbSector;
643 Log2(("NTFS BPB: cbSector=%#x\n", cbSector));
644
645 uint32_t cClusterPerSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerCluster);
646 if ( !RT_IS_POWER_OF_TWO(cClusterPerSector)
647 || cClusterPerSector == 0)
648 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
649 "Not NTFS - BPB.cCluster is ouf of range: %u", cClusterPerSector);
650
651 pThis->cbCluster = cClusterPerSector * cbSector;
652 if (pThis->cbCluster > _64K)
653 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
654 "cluster size exceeds 64KB: %#x", pThis->cbCluster);
655 pThis->cClusterShift = ASMBitFirstSetU32(pThis->cbCluster) - 1;
656 Log2(("NTFS BPB: cClusterPerSector=%#x => %#x bytes, %u shift\n", cClusterPerSector, pThis->cbCluster, pThis->cClusterShift));
657
658 /* NTFS BPB: cSectors. */
659 uint64_t cSectors = RT_LE2H_U64(pBootSector->Bpb.Ntfs.cSectors);
660 if (cSectors > pThis->cbBacking / pThis->cbSector)
661 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
662 "NTFS sector count exceeds volume size: %#RX64 vs %#RX64",
663 cSectors, pThis->cbBacking / pThis->cbSector);
664 if (cSectors < 256)
665 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "NTFS sector count too small: %#RX64", cSectors);
666 pThis->cbVolume = cSectors * pThis->cbSector;
667 pThis->cClusters = cSectors / cClusterPerSector;
668 Log2(("NTFS BPB: cSectors=%#RX64 => %#xu bytes (%Rhcb) => cClusters=%#RX64\n",
669 cSectors, pThis->cbVolume, pThis->cbVolume, pThis->cClusters));
670
671 /* NTFS BPB: MFT location. */
672 uint64_t uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMft);
673 if ( uLcn < 1
674 || uLcn >= pThis->cClusters)
675 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
676 "NTFS MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
677 pThis->uLcnMft = uLcn;
678 Log2(("NTFS BPB: uLcnMft=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
679
680 /* NTFS BPB: Mirror MFT location. */
681 uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMftMirror);
682 if ( uLcn < 1
683 || uLcn >= pThis->cClusters)
684 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
685 "NTFS mirror MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
686 pThis->uLcnMftMirror = uLcn;
687 Log2(("NTFS BPB: uLcnMftMirror=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
688
689 /* NTFS BPB: Size of MFT file record. */
690 if (pBootSector->Bpb.Ntfs.cClustersPerMftRecord >= 0)
691 {
692 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord)
693 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord == 0)
694 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
695 "NTFS clusters-per-mft-record value is zero or not a power of two: %#x",
696 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
697 pThis->cbMftRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord << pThis->cClusterShift;
698 Assert(pThis->cbMftRecord == pBootSector->Bpb.Ntfs.cClustersPerMftRecord * pThis->cbCluster);
699 }
700 else if ( pBootSector->Bpb.Ntfs.cClustersPerMftRecord < -20
701 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord > -9)
702 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
703 "NTFS clusters-per-mft-record is out of shift range: %d",
704 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
705 else
706 pThis->cbMftRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
707 Log2(("NTFS BPB: cbMftRecord=%#x\n", pThis->cbMftRecord));
708 if ( pThis->cbMftRecord > _32K
709 || pThis->cbMftRecord < 256)
710 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
711 "Unsupported NTFS MFT record size: %#x", pThis->cbMftRecord);
712
713 /* NTFS BPB: Index block size */
714 if (pBootSector->Bpb.Ntfs.cClusterPerIndexBlock >= 0)
715 {
716 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClusterPerIndexBlock)
717 || pBootSector->Bpb.Ntfs.cClusterPerIndexBlock == 0)
718 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
719 "NTFS clusters-per-index-block is zero or not a power of two: %#x",
720 pBootSector->Bpb.Ntfs.cClusterPerIndexBlock);
721 pThis->cbIndexRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClusterPerIndexBlock << pThis->cClusterShift;
722 Assert(pThis->cbIndexRecord == pBootSector->Bpb.Ntfs.cClusterPerIndexBlock * pThis->cbCluster);
723 }
724 else if ( pBootSector->Bpb.Ntfs.cClusterPerIndexBlock < -32
725 || pBootSector->Bpb.Ntfs.cClusterPerIndexBlock > -9)
726 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
727 "NTFS clusters-per-index-block is out of shift range: %d",
728 pBootSector->Bpb.Ntfs.cClusterPerIndexBlock);
729 else
730 pThis->cbIndexRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
731 Log2(("NTFS BPB: cbIndexRecord=%#x\n", pThis->cbIndexRecord));
732
733 pThis->uSerialNo = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uSerialNumber);
734 Log2(("NTFS BPB: uSerialNo=%#x\n", pThis->uSerialNo));
735
736
737 return VINF_SUCCESS;
738}
739
740
741RTDECL(int) RTFsNtfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fNtfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
742{
743 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
744 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
745 AssertReturn(!fNtfsFlags, VERR_INVALID_FLAGS);
746
747 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
748 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
749
750 /*
751 * Create a VFS instance and initialize the data so rtFsNtfsVol_Close works.
752 */
753 RTVFS hVfs;
754 PRTFSNTFSVOL pThis;
755 int rc = RTVfsNew(&g_rtFsNtfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
756 if (RT_SUCCESS(rc))
757 {
758 pThis->hVfsBacking = hVfsFileIn;
759 pThis->hVfsSelf = hVfs;
760 pThis->fMntFlags = fMntFlags;
761 pThis->fNtfsFlags = fNtfsFlags;
762
763 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
764 if (RT_SUCCESS(rc))
765 {
766 void *pvBuf = RTMemTmpAlloc(_64K);
767 if (pvBuf)
768 {
769 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo);
770 if (RT_SUCCESS(rc))
771 rc = rtFsNtfsVolLoadMft(pThis, pvBuf, _64K, pErrInfo);
772 RTMemTmpFree(pvBuf);
773 if (RT_SUCCESS(rc))
774 {
775 *phVfs = hVfs;
776 return VINF_SUCCESS;
777 }
778 }
779 else
780 rc = VERR_NO_TMP_MEMORY;
781 }
782
783 RTVfsRelease(hVfs);
784 *phVfs = NIL_RTVFS;
785 }
786 else
787 RTVfsFileRelease(hVfsFileIn);
788
789 return rc;
790}
791
792
793/**
794 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
795 */
796static DECLCALLBACK(int) rtVfsChainNtfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
797 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
798{
799 RT_NOREF(pProviderReg);
800
801 /*
802 * Basic checks.
803 */
804 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
805 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
806 if ( pElement->enmType != RTVFSOBJTYPE_VFS
807 && pElement->enmType != RTVFSOBJTYPE_DIR)
808 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
809 if (pElement->cArgs > 1)
810 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
811
812 /*
813 * Parse the flag if present, save in pElement->uProvider.
814 */
815 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
816 if (pElement->cArgs > 0)
817 {
818 const char *psz = pElement->paArgs[0].psz;
819 if (*psz)
820 {
821 if (!strcmp(psz, "ro"))
822 fReadOnly = true;
823 else if (!strcmp(psz, "rw"))
824 fReadOnly = false;
825 else
826 {
827 *poffError = pElement->paArgs[0].offSpec;
828 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
829 }
830 }
831 }
832
833 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
834 return VINF_SUCCESS;
835}
836
837
838/**
839 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
840 */
841static DECLCALLBACK(int) rtVfsChainNtfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
842 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
843 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
844{
845 RT_NOREF(pProviderReg, pSpec, poffError);
846
847 int rc;
848 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
849 if (hVfsFileIn != NIL_RTVFSFILE)
850 {
851 RTVFS hVfs;
852 rc = RTFsNtfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
853 RTVfsFileRelease(hVfsFileIn);
854 if (RT_SUCCESS(rc))
855 {
856 *phVfsObj = RTVfsObjFromVfs(hVfs);
857 RTVfsRelease(hVfs);
858 if (*phVfsObj != NIL_RTVFSOBJ)
859 return VINF_SUCCESS;
860 rc = VERR_VFS_CHAIN_CAST_FAILED;
861 }
862 }
863 else
864 rc = VERR_VFS_CHAIN_CAST_FAILED;
865 return rc;
866}
867
868
869/**
870 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
871 */
872static DECLCALLBACK(bool) rtVfsChainNtfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
873 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
874 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
875{
876 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
877 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
878 || !pReuseElement->paArgs[0].uProvider)
879 return true;
880 return false;
881}
882
883
884/** VFS chain element 'file'. */
885static RTVFSCHAINELEMENTREG g_rtVfsChainNtfsVolReg =
886{
887 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
888 /* fReserved = */ 0,
889 /* pszName = */ "ntfs",
890 /* ListEntry = */ { NULL, NULL },
891 /* pszHelp = */ "Open a NTFS file system, requires a file object on the left side.\n"
892 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
893 /* pfnValidate = */ rtVfsChainNtfsVol_Validate,
894 /* pfnInstantiate = */ rtVfsChainNtfsVol_Instantiate,
895 /* pfnCanReuseElement = */ rtVfsChainNtfsVol_CanReuseElement,
896 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
897};
898
899RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainNtfsVolReg, rtVfsChainNtfsVolReg);
900
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