VirtualBox

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

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

iprt/formats/ntfs: updates

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