Changeset 69885 in vbox
- Timestamp:
- Nov 30, 2017 4:41:37 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/formats/ntfs.h
r69873 r69885 272 272 struct 273 273 { 274 /** 0x10: The first virtual cluster containing data. */ 274 /** 0x10: The first virtual cluster containing data. 275 * 276 * This is mainly for internal checking when the run list doesn't fit in one 277 * MFT record. It can also be used to avoid recording a sparse run at the 278 * beginning of the data covered by this attribute record. */ 275 279 int64_t iVcnFirst; 276 280 /** 0x18: The last virtual cluster containing data (inclusive). */ … … 284 288 /** 0x23: Reserved */ 285 289 uint8_t abReserved[5]; 286 /** 0x28: Allocated size. */ 290 /** 0x28: Allocated size (rouneded to cluster). 291 * @note Only set in the first attribute record (iVcnFirst == 0). */ 287 292 int64_t cbAllocated; 288 /** 0x30: Initialized size. */ 293 /** 0x30: The exact length of the data. 294 * @note Only set in the first attribute record (iVcnFirst == 0). */ 295 int64_t cbData; 296 /** 0x38: The length of the initialized data (rounded to cluster). 297 * @note Only set in the first attribute record (iVcnFirst == 0). */ 289 298 int64_t cbInitialized; 290 /** 0x 38: Compressed size if compressed, otherwise absent. */299 /** 0x40: Compressed size if compressed, otherwise absent. */ 291 300 int64_t cbCompressed; 292 301 } NonRes; 293 302 } u; 294 303 } NTFSATTRIBHDR; 295 AssertCompileSize(NTFSATTRIBHDR, 0x4 0);304 AssertCompileSize(NTFSATTRIBHDR, 0x48); 296 305 AssertCompileMemberOffset(NTFSATTRIBHDR, u.Res, 0x10); 297 306 AssertCompileMemberOffset(NTFSATTRIBHDR, u.Res.bReserved, 0x17); 298 307 AssertCompileMemberOffset(NTFSATTRIBHDR, u.NonRes, 0x10); 299 AssertCompileMemberOffset(NTFSATTRIBHDR, u.NonRes.cbCompressed, 0x 38);308 AssertCompileMemberOffset(NTFSATTRIBHDR, u.NonRes.cbCompressed, 0x40); 300 309 /** Pointer to a NTFS attribute header. */ 301 310 typedef NTFSATTRIBHDR *PNTFSATTRIBHDR; … … 308 317 #define NTFSATTRIBHDR_SIZE_RESIDENT (0x18) 309 318 /** Attribute header size for uncompressed non-resident values. */ 310 #define NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED (0x 38)319 #define NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED (0x40) 311 320 /** Attribute header size for compressed non-resident values. */ 312 #define NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED (0x4 0)321 #define NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED (0x48) 313 322 /** @} */ 314 323 -
trunk/src/VBox/Runtime/Makefile.kmk
r69854 r69885 558 558 common/string/RTUtf16ICmpAscii.cpp \ 559 559 common/string/RTUtf16End.cpp \ 560 common/string/RTUtf16NICmpAscii.cpp \ 560 561 common/string/RTUtf16NLen.cpp \ 561 562 common/string/RTUtf16NLenEx.cpp \ -
trunk/src/VBox/Runtime/common/fs/ntfsvfs.cpp
r69875 r69885 41 41 #include <iprt/vfs.h> 42 42 #include <iprt/vfslowlevel.h> 43 #include <iprt/utf16.h> 43 44 #include <iprt/formats/ntfs.h> 44 45 … … 47 48 * Defined Constants And Macros * 48 49 *********************************************************************************************************************************/ 49 /** Max clusters in an allocation run. 50 * This ASSUMES that the cluster size is at most 64KB. */ 51 #define RTFSNTFS_MAX_CLUSTER_IN_RUN UINT64_C(0x00007fffffffffff) 50 /** The maximum bitmap cache size. */ 51 #define RTFSNTFS_MAX_BITMAP_CACHE _64K 52 52 53 53 … … 122 122 /** Set if this is a base MFT record. */ 123 123 bool fIsBase; 124 /** The disk offset of this MFT entry. */125 uint64_t offDisk;126 124 } RTFSNTFSMFTREC; 127 125 … … 159 157 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */ 160 158 PNTFSATTRIBHDR pAttrHdr; 159 /** The offset of the attribute header in the MFT record. 160 * This is needed to validate header relative offsets. */ 161 uint32_t offAttrHdrInMftRec; 161 162 /** Disk space allocation if non-resident. */ 162 163 RTFSNTFSEXTENTS Extents; … … 229 230 uint64_t uSerialNo; 230 231 231 /** The MFTdata attribute. */232 /** The '$Mft' data attribute. */ 232 233 PRTFSNTFSATTR pMftData; 234 235 236 /** @name Allocation bitmap and cache. 237 * @{ */ 238 /** The '$Bitmap' data attribute. */ 239 PRTFSNTFSATTR pMftBitmap; 240 /** The first cluster currently loaded into the bitmap cache . */ 241 uint64_t iFirstBitmapCluster; 242 /** The number of clusters currently loaded into the bitmap cache */ 243 uint32_t cBitmapClusters; 244 /** The size of the pvBitmap allocation. */ 245 uint32_t cbBitmapAlloc; 246 /** Allocation bitmap cache buffer. */ 247 void *pvBitmap; 248 /** @} */ 233 249 234 250 /** Root of the MFT record tree (RTFSNTFSMFTREC). */ … … 237 253 238 254 239 static PRTFSNTFSMFTREC rtFsNtfsMftVol_New(PRTFSNTFSVOL pVol, uint64_t idMft) 255 256 /********************************************************************************************************************************* 257 * Internal Functions * 258 *********************************************************************************************************************************/ 259 static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis); 260 261 262 263 /** 264 * Checks if a bit is set in an NTFS bitmap (little endian). 265 * 266 * @returns true if set, false if not. 267 * @param pvBitmap The bitmap buffer. 268 * @param iBit The bit. 269 */ 270 DECLINLINE(bool) rtFsNtfsBitmap_IsSet(void *pvBitmap, uint32_t iBit) 271 { 272 #if 0 //def RT_LITTLE_ENDIAN 273 return ASMBitTest(pvBitmap, iBit); 274 #else 275 uint8_t b = ((uint8_t const *)pvBitmap)[iBit >> 3]; 276 return RT_BOOL(b & (1 << (iBit & 7))); 277 #endif 278 } 279 280 281 282 static PRTFSNTFSMFTREC rtFsNtfsVol_NewMftRec(PRTFSNTFSVOL pVol, uint64_t idMft) 240 283 { 241 284 PRTFSNTFSMFTREC pRec = (PRTFSNTFSMFTREC)RTMemAllocZ(sizeof(*pRec)); … … 247 290 pRec->TreeNode.Key = idMft; 248 291 pRec->pNext = NULL; 249 pRec->offDisk = UINT64_MAX;250 292 pRec->cRefs = 1; 251 293 if (RTAvlU64Insert(&pVol->MftRoot, &pRec->TreeNode)) … … 301 343 { 302 344 PCNTFSRECFILE pFileRec = pRec->pFileRec; 303 Log2(("NTFS: MFT #%#RX64 at %#RX64\n", pRec->TreeNode.Key, pRec->offDisk));345 Log2(("NTFS: MFT #%#RX64\n", pRec->TreeNode.Key)); 304 346 if (pFileRec->Hdr.uMagic == NTFSREC_MAGIC_FILE) 305 347 { … … 518 560 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n", 519 561 RT_LE2H_U64(pHdr->u.NonRes.cbAllocated), RT_LE2H_U64(pHdr->u.NonRes.cbAllocated))); 562 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n", 563 RT_LE2H_U64(pHdr->u.NonRes.cbData), RT_LE2H_U64(pHdr->u.NonRes.cbData))); 520 564 Log2(("NTFS: cbInitialized %#RX64 (%Rhcb)\n", 521 565 RT_LE2H_U64(pHdr->u.NonRes.cbInitialized), RT_LE2H_U64(pHdr->u.NonRes.cbInitialized))); … … 528 572 Log2(("NTFS: Compression unit 2^%u clusters\n", pHdr->u.NonRes.uCompressionUnit)); 529 573 530 if ( NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED <= cbMaxAttrib531 && NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED <= cbAttrib574 if ( cbMaxAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED 575 && cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED 532 576 && ( offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED 533 577 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)) 534 578 Log2(("NTFS: cbCompressed %#RX64 (%Rhcb)\n", 535 579 RT_LE2H_U64(pHdr->u.NonRes.cbCompressed), RT_LE2H_U64(pHdr->u.NonRes.cbCompressed))); 536 else if (pHdr->u.NonRes.uCompressionUnit != 0 && pHdr->u.NonRes.uCompressionUnit != 64) 580 else if ( pHdr->u.NonRes.uCompressionUnit != 0 581 && pHdr->u.NonRes.uCompressionUnit != 64 582 && pHdr->u.NonRes.iVcnFirst == 0) 537 583 Log2(("NTFS: !Error! Compressed attrib fields are out of bound!\n")); 538 584 … … 632 678 633 679 static int rtFsNtfsAttr_ParseExtents(PRTFSNTFSATTR pAttrib, PRTFSNTFSEXTENTS pExtents, uint8_t cClusterShift, int64_t iVcnFirst, 634 PRTERRINFO pErrInfo, uint64_t idxMft, uint32_t offAttrib)680 uint64_t cbVolume, PRTERRINFO pErrInfo, uint64_t idxMft, uint32_t offAttrib) 635 681 { 636 682 PCNTFSATTRIBHDR pAttrHdr = pAttrib->pAttrHdr; … … 777 823 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): iLcn %RX64 overflows when shifted by %u", 778 824 idxMft, iExtent, offAttrib, iLcn, cClusterShift)); 825 AssertBreakStmt( paExtents[iExtent].off < cbVolume 826 || paExtents[iExtent].cbExtent < cbVolume 827 || paExtents[iExtent].off + paExtents[iExtent].cbExtent <= cbVolume, 828 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 829 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x) outside volume: %#RX64 LB %#RX64, cbVolume=%#RX64", 830 idxMft, iExtent, offAttrib, paExtents[iExtent].off, 831 paExtents[iExtent].cbExtent, cbVolume)); 779 832 } 780 833 else … … 804 857 805 858 859 /** 860 * Parses the given MTF record and all related records, putting the result in 861 * pRec->pCore (with one reference for the caller). 862 * 863 * ASSUMES caller will release pRec->pCore on failure. 864 * 865 * @returns IPRT status code. 866 * @param pThis The volume. 867 * @param pRec The MFT record to parse. 868 * @param pErrInfo Where to return additional error information. Optional. 869 */ 806 870 static int rtFsNtfsVol_ParseMft(PRTFSNTFSVOL pThis, PRTFSNTFSMFTREC pRec, PRTERRINFO pErrInfo) 807 871 { … … 862 926 AssertReturn(pAttrib, VERR_NO_MEMORY); 863 927 pAttrib->pAttrHdr = pAttrHdr; 928 pAttrib->offAttrHdrInMftRec = offRec; 864 929 pAttrib->pCore = pCore; 865 930 //pAttrib->Extents.cExtents = 0; … … 869 934 { 870 935 int rc = rtFsNtfsAttr_ParseExtents(pAttrib, &pAttrib->Extents, pThis->cClusterShift, 0 /*iVncFirst*/, 871 p ErrInfo, pRec->TreeNode.Key, offRec);936 pThis->cbVolume, pErrInfo, pRec->TreeNode.Key, offRec); 872 937 if (RT_FAILURE(rc)) 873 938 { … … 895 960 return VINF_SUCCESS; 896 961 } 962 963 964 static int rtFsNtfsAttr_Read(PRTFSNTFSATTR pAttr, uint64_t off, void *pvBuf, size_t cbToRead) 965 { 966 PRTFSNTFSVOL pVol = pAttr->pCore->pVol; 967 int rc; 968 if (!pAttr->pAttrHdr->fNonResident) 969 { 970 /* 971 * The attribute is resident. 972 */ 973 uint32_t cbAttrib = RT_LE2H_U32(pAttr->pAttrHdr->cbAttrib); 974 uint32_t cbValue = RT_LE2H_U32(pAttr->pAttrHdr->u.Res.cbValue); 975 uint16_t offValue = RT_LE2H_U16(pAttr->pAttrHdr->u.Res.offValue); 976 if ( off < cbValue 977 && cbToRead <= cbValue 978 && off + cbToRead <= cbValue) 979 { 980 if (offValue <= cbAttrib) 981 { 982 cbAttrib -= offValue; 983 if (off < cbAttrib) 984 { 985 /** @todo check if its possible to have cbValue larger than the attribute and 986 * reading those extra bytes as zero. */ 987 if ( pAttr->offAttrHdrInMftRec + offValue + cbAttrib <= pVol->cbMftRecord 988 && cbAttrib <= pVol->cbMftRecord) 989 { 990 size_t cbToCopy = cbAttrib - off; 991 if (cbToCopy > cbToRead) 992 cbToCopy = cbToRead; 993 memcpy(pvBuf, (uint8_t *)pAttr->pAttrHdr + offValue, cbToCopy); 994 pvBuf = (uint8_t *)pvBuf + cbToCopy; 995 cbToRead -= cbToCopy; 996 rc = VINF_SUCCESS; 997 } 998 else 999 { 1000 rc = VERR_VFS_BOGUS_OFFSET; 1001 Log(("rtFsNtfsAttr_Read: bad resident attribute!\n")); 1002 } 1003 } 1004 else 1005 rc = VINF_SUCCESS; 1006 } 1007 else 1008 rc = VERR_VFS_BOGUS_FORMAT; 1009 } 1010 else 1011 rc = VERR_EOF; 1012 } 1013 else if (pAttr->pAttrHdr->u.NonRes.uCompressionUnit == 0) 1014 { 1015 /* 1016 * Uncompressed non-resident attribute. 1017 */ 1018 uint64_t const cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated); 1019 if ( off >= cbAllocated 1020 || cbToRead > cbAllocated 1021 || off + cbToRead > cbAllocated) 1022 rc = VERR_EOF; 1023 else 1024 { 1025 rc = VINF_SUCCESS; 1026 1027 uint64_t const cbInitialized = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbInitialized); 1028 if ( off < cbInitialized 1029 && cbToRead > 0) 1030 { 1031 /* 1032 * Locate the first extent. This is a tad complicated. 1033 * 1034 * We move off along as we traverse the extent tables, so that it is relative 1035 * to the start of the current extent. 1036 */ 1037 PRTFSNTFSEXTENTS pTable = &pAttr->Extents; 1038 uint32_t iExtent = 0; 1039 PRTFSNTFSATTRSUBREC pCurSub = NULL; 1040 for (;;) 1041 { 1042 if (off < pTable->cbData) 1043 { 1044 while ( iExtent < pTable->cExtents 1045 && off >= pTable->paExtents[iExtent].cbExtent) 1046 { 1047 off -= pTable->paExtents[iExtent].cbExtent; 1048 iExtent++; 1049 } 1050 AssertReturn(iExtent < pTable->cExtents, VERR_INTERNAL_ERROR_2); 1051 break; 1052 } 1053 1054 /* Next table. */ 1055 off -= pTable->cbData; 1056 if (!pCurSub) 1057 pCurSub = pAttr->pSubRecHead; 1058 else 1059 pCurSub = pCurSub->pNext; 1060 if (!pCurSub) 1061 { 1062 iExtent = UINT32_MAX; 1063 break; 1064 } 1065 pTable = &pCurSub->Extents; 1066 iExtent = 0; 1067 } 1068 1069 /* 1070 * The read loop. 1071 */ 1072 while (iExtent != UINT32_MAX) 1073 { 1074 uint64_t cbMaxRead = pTable->paExtents[iExtent].cbExtent; 1075 Assert(off < cbMaxRead); 1076 cbMaxRead -= off; 1077 size_t const cbThisRead = cbMaxRead >= cbToRead ? cbToRead : (size_t)cbMaxRead; 1078 if (pTable->paExtents[iExtent].off == UINT64_MAX) 1079 RT_BZERO(pvBuf, cbThisRead); 1080 else 1081 { 1082 rc = RTVfsFileReadAt(pVol->hVfsBacking, pTable->paExtents[iExtent].off + off, pvBuf, cbThisRead, NULL); 1083 if (RT_FAILURE(rc)) 1084 break; 1085 } 1086 pvBuf = (uint8_t *)pvBuf + cbThisRead; 1087 cbToRead -= cbThisRead; 1088 if (!cbToRead) 1089 break; 1090 1091 /* 1092 * Advance to the next extent. 1093 */ 1094 iExtent++; 1095 if (iExtent >= pTable->cExtents) 1096 { 1097 pCurSub = pCurSub ? pCurSub->pNext : pAttr->pSubRecHead; 1098 if (!pCurSub) 1099 break; 1100 pTable = &pCurSub->Extents; 1101 iExtent = 0; 1102 } 1103 } 1104 } 1105 } 1106 } 1107 else 1108 { 1109 LogRel(("rtFsNtfsAttr_Read: Compressed files are not supported\n")); 1110 rc = VERR_NOT_SUPPORTED; 1111 } 1112 1113 /* 1114 * Anything else beyond the end of what's stored/initialized? 1115 */ 1116 if ( cbToRead > 0 1117 && RT_SUCCESS(rc)) 1118 { 1119 RT_BZERO(pvBuf, cbToRead); 1120 } 1121 1122 return rc; 1123 } 1124 1125 1126 static int rtFsNtfsVol_NewCoreForMftIdx(PRTFSNTFSVOL pThis, uint64_t idxMft, PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo) 1127 { 1128 Assert(pThis->pMftData); 1129 Assert(RTAvlU64Get(&pThis->MftRoot, idxMft) == NULL); 1130 1131 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, idxMft); 1132 AssertReturn(pRec, VERR_NO_MEMORY); 1133 1134 uint64_t offRec = idxMft * pThis->cbMftRecord; 1135 int rc = rtFsNtfsAttr_Read(pThis->pMftData, offRec, pRec->pbRec, pThis->cbMftRecord); 1136 if (RT_SUCCESS(rc)) 1137 { 1138 #ifdef LOG_ENABLED 1139 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord); 1140 #endif 1141 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo); 1142 if (RT_SUCCESS(rc)) 1143 { 1144 rtFsNtfsMftRec_Release(pRec, pThis); 1145 *ppCore = pRec->pCore; 1146 return VINF_SUCCESS; 1147 } 1148 rtFsNtfsCore_Release(pRec->pCore); 1149 rtFsNtfsMftRec_Release(pRec, pThis); 1150 } 1151 return rc; 1152 } 1153 897 1154 898 1155 … … 960 1217 static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis) 961 1218 { 962 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); 963 Assert(cRefs < 128); 964 if (cRefs != 0) 965 return cRefs; 966 return rtFsNtfsCore_Destroy(pThis); 1219 if (pThis) 1220 { 1221 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); 1222 Assert(cRefs < 128); 1223 if (cRefs != 0) 1224 return cRefs; 1225 return rtFsNtfsCore_Destroy(pThis); 1226 } 1227 return 0; 967 1228 } 968 1229 … … 1005 1266 1006 1267 1268 /** 1269 * Slow path for querying the allocation state of a cluster. 1270 * 1271 * @returns IPRT status code. 1272 * @param pThis The NTFS volume instance. 1273 * @param iCluster The cluster to query. 1274 * @param pfState Where to return the state. 1275 */ 1276 static int rtFsNtfsVol_QueryClusterStateSlow(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState) 1277 { 1278 int rc; 1279 uint64_t const cbWholeBitmap = RT_LE2H_U64(pThis->pMftBitmap->pAttrHdr->u.NonRes.cbData); 1280 uint64_t const offInBitmap = iCluster >> 3; 1281 if (offInBitmap < cbWholeBitmap) 1282 { 1283 if (!pThis->pvBitmap) 1284 { 1285 /* 1286 * Try cache the whole bitmap if it's not too large. 1287 */ 1288 if ( cbWholeBitmap <= RTFSNTFS_MAX_BITMAP_CACHE 1289 && cbWholeBitmap >= RT_ALIGN_64(pThis->cClusters >> 3, 8)) 1290 { 1291 pThis->cbBitmapAlloc = RT_ALIGN_Z((uint32_t)cbWholeBitmap, 8); 1292 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc); 1293 if (pThis->pvBitmap) 1294 { 1295 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc); 1296 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, 0, pThis->pvBitmap, (uint32_t)cbWholeBitmap); 1297 if (RT_SUCCESS(rc)) 1298 { 1299 pThis->iFirstBitmapCluster = 0; 1300 pThis->cBitmapClusters = pThis->cClusters; 1301 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iCluster); 1302 return VINF_SUCCESS; 1303 } 1304 RTMemFree(pThis->pvBitmap); 1305 pThis->pvBitmap = NULL; 1306 pThis->cbBitmapAlloc = 0; 1307 return rc; 1308 } 1309 } 1310 1311 /* 1312 * Do a cluster/4K cache. 1313 */ 1314 pThis->cbBitmapAlloc = RT_MAX(pThis->cbCluster, _4K); 1315 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc); 1316 if (!pThis->pvBitmap) 1317 { 1318 pThis->cbBitmapAlloc = 0; 1319 return VERR_NO_MEMORY; 1320 } 1321 } 1322 1323 /* 1324 * Load a cache line. 1325 */ 1326 Assert(RT_IS_POWER_OF_TWO(pThis->cbBitmapAlloc)); 1327 uint64_t offLoad = offInBitmap & ~(pThis->cbBitmapAlloc - 1); 1328 uint32_t cbLoad = (uint32_t)RT_MIN(cbWholeBitmap - offLoad, pThis->cbBitmapAlloc); 1329 1330 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc); 1331 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, offLoad, pThis->pvBitmap, cbLoad); 1332 if (RT_SUCCESS(rc)) 1333 { 1334 pThis->iFirstBitmapCluster = offLoad << 3; 1335 pThis->cBitmapClusters = cbLoad << 3; 1336 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)(iCluster - pThis->iFirstBitmapCluster)); 1337 return VINF_SUCCESS; 1338 } 1339 pThis->cBitmapClusters = 0; 1340 } 1341 else 1342 { 1343 LogRel(("rtFsNtfsVol_QueryClusterStateSlow: iCluster=%#RX64 is outside the bitmap (%#RX64)\n", iCluster, cbWholeBitmap)); 1344 rc = VERR_OUT_OF_RANGE; 1345 } 1346 return rc; 1347 } 1348 1349 1350 /** 1351 * Query the allocation state of the given cluster. 1352 * 1353 * @returns IPRT status code. 1354 * @param pThis The NTFS volume instance. 1355 * @param iCluster The cluster to query. 1356 * @param pfState Where to return the state. 1357 */ 1358 static int rtFsNtfsVol_QueryClusterState(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState) 1359 { 1360 uint64_t iClusterInCache = iCluster - pThis->iFirstBitmapCluster; 1361 if (iClusterInCache < pThis->cBitmapClusters) 1362 { 1363 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iClusterInCache); 1364 return VINF_SUCCESS; 1365 } 1366 return rtFsNtfsVol_QueryClusterStateSlow(pThis, iCluster, pfState); 1367 } 1368 1369 1007 1370 1008 1371 /** … … 1046 1409 static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed) 1047 1410 { 1048 NOREF(pvThis); NOREF(off); NOREF(cb); NOREF(pfUsed); 1049 return VERR_NOT_IMPLEMENTED; 1411 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis; 1412 *pfUsed = true; 1413 1414 /* 1415 * Round to a cluster range. 1416 */ 1417 uint64_t iCluster = off >> pThis->cClusterShift; 1418 1419 Assert(RT_IS_POWER_OF_TWO(pThis->cbCluster)); 1420 cb += off & (pThis->cbCluster - 1); 1421 cb = RT_ALIGN_Z(cb, pThis->cbCluster); 1422 size_t cClusters = cb >> pThis->cClusterShift; 1423 1424 /* 1425 * Check the clusters one-by-one. 1426 * Just to be cautious, we will always check the cluster at off, even when cb is zero. 1427 */ 1428 do 1429 { 1430 bool fState = true; 1431 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState); 1432 if (RT_FAILURE(rc)) 1433 return rc; 1434 if (fState) 1435 { 1436 *pfUsed = true; 1437 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - used\n", off, cb)); 1438 return VINF_SUCCESS; 1439 } 1440 1441 iCluster++; 1442 } while (cClusters-- > 0); 1443 1444 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - unused\n", off, cb)); 1445 *pfUsed = false; 1446 return VINF_SUCCESS; 1050 1447 } 1051 1448 … … 1070 1467 1071 1468 1072 1073 static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo) 1074 { 1075 AssertReturn(cbBuf >= _64K, VERR_INTERNAL_ERROR_2); 1076 NOREF(pThis); NOREF(pvBuf); NOREF(cbBuf); NOREF(pErrInfo); 1077 /** @todo read MFT, find bitmap allocation, implement 1078 * rtFsNtfsVol_QueryRangeState. */ 1079 1469 /** 1470 * Checks that the storage for the given attribute is all marked allocated in 1471 * the allocation bitmap of the volume. 1472 * 1473 * @returns IPRT status code. 1474 * @param pThis The NTFS volume instance. 1475 * @param pAttr The attribute to check. 1476 * @param pszDesc Description of the attribute. 1477 * @param pErrInfo Where to return error details. 1478 */ 1479 static int rtFsNtfsVolCheckBitmap(PRTFSNTFSVOL pThis, PRTFSNTFSATTR pAttr, const char *pszDesc, PRTERRINFO pErrInfo) 1480 { 1481 PRTFSNTFSATTRSUBREC pSubRec = NULL; 1482 PRTFSNTFSEXTENTS pTable = &pAttr->Extents; 1483 uint64_t offFile = 0; 1484 for (;;) 1485 { 1486 uint32_t const cExtents = pTable->cExtents; 1487 PRTFSNTFSEXTENT paExtents = pTable->paExtents; 1488 for (uint32_t iExtent = 0; iExtent < cExtents; iExtent++) 1489 { 1490 uint64_t const off = paExtents[iExtent].off; 1491 if (off == UINT64_MAX) 1492 offFile += paExtents[iExtent].cbExtent; 1493 else 1494 { 1495 uint64_t iCluster = off >> pThis->cClusterShift; 1496 uint64_t cClusters = paExtents[iExtent].cbExtent >> pThis->cClusterShift; 1497 Assert((cClusters << pThis->cClusterShift) == paExtents[iExtent].cbExtent); 1498 Assert(cClusters != 0); 1499 1500 while (cClusters-- > 0) 1501 { 1502 bool fState = false; 1503 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState); 1504 if (RT_FAILURE(rc)) 1505 return RTERRINFO_LOG_REL_SET_F(pErrInfo, rc, 1506 "Error querying allocation bitmap entry %#RX64 (for %s offset %#RX64)", 1507 iCluster, pszDesc, offFile); 1508 if (!fState) 1509 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1510 "Cluster %#RX64 at offset %#RX64 in %s is not marked allocated", 1511 iCluster, offFile, pszDesc); 1512 offFile += pThis->cbCluster; 1513 } 1514 } 1515 } 1516 1517 /* Next table. */ 1518 pSubRec = pSubRec ? pSubRec->pNext : pAttr->pSubRecHead; 1519 if (!pSubRec) 1520 return VINF_SUCCESS; 1521 pTable = &pSubRec->Extents; 1522 } 1523 } 1524 1525 1526 /** 1527 * Loads the allocation bitmap and does basic validation of. 1528 * 1529 * @returns IPRT status code. 1530 * @param pThis The NTFS volume instance. Will set up the 1531 * 'Allocation bitmap and cache' fields. 1532 * @param pErrInfo Where to return error details. 1533 */ 1534 static int rtFsNtfsVolLoadBitmap(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo) 1535 { 1536 PRTFSNTFSCORE pCore; 1537 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_BITMAP, &pCore, pErrInfo); 1538 if (RT_SUCCESS(rc)) 1539 { 1540 PRTFSNTFSATTR pMftBitmap; 1541 pThis->pMftBitmap = pMftBitmap = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA); 1542 if (pMftBitmap) 1543 { 1544 /* 1545 * Validate the '$Bitmap' MFT record. 1546 * We expect the bitmap to be fully initialized and be sized according to the 1547 * formatted volume size. Allegedly, NTFS pads it to an even 8 byte in size. 1548 */ 1549 uint64_t const cbMinBitmap = RT_ALIGN_64(pThis->cbVolume >> (pThis->cClusterShift + 3), 8); 1550 uint64_t const cbMaxBitmap = RT_ALIGN_64(cbMinBitmap, pThis->cbCluster); 1551 //uint64_t const cbMinInitialized = RT_ALIGN_64((RT_MAX(pThis->uLcnMft, pThis->uLcnMftMirror) + 16) >> 3, 8); 1552 if (!pMftBitmap->pAttrHdr->fNonResident) 1553 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is resident!"); 1554 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) < cbMinBitmap 1555 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) > cbMaxBitmap) 1556 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1557 "MFT record #6 unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64", 1558 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated), cbMinBitmap, cbMaxBitmap); 1559 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) < cbMinBitmap 1560 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) 1561 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData)) 1562 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1563 "MFT record #6 unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64", 1564 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData), cbMinBitmap, 1565 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) ); 1566 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) < cbMinBitmap 1567 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) 1568 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated)) 1569 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1570 "MFT record #6 unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64", 1571 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized), cbMinBitmap, 1572 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) ); 1573 else if (pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit != 0) 1574 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1575 "MFT record #6 unnamed DATA attribute is compressed: %#x", 1576 pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit); 1577 else if (pMftBitmap->Extents.cExtents != 1) /* paranoia for now */ 1578 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1579 "MFT record #6 unnamed DATA attribute is expected to have a single extent: %u extents", 1580 pMftBitmap->Extents.cExtents); 1581 else if (pMftBitmap->Extents.paExtents[0].off == UINT64_MAX) 1582 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is sparse"); 1583 else 1584 { 1585 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME); 1586 if (!pFilenameAttr) 1587 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 has no FILENAME attribute!"); 1588 else if (pFilenameAttr->pAttrHdr->fNonResident) 1589 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 FILENAME attribute is non-resident!"); 1590 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7])) 1591 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1592 "MFT record #6 FILENAME attribute value size is too small: %#x", 1593 pFilenameAttr->pAttrHdr->u.Res.cbValue); 1594 else 1595 { 1596 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr 1597 + pFilenameAttr->pAttrHdr->u.Res.offValue); 1598 if ( pFilename->cwcFilename != 7 1599 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Bitmap", 7) != 0) 1600 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1601 "MFT record #6 FILENAME isn't '$Bitmap': '%.*ls'", 1602 pFilename->cwcFilename, pFilename->wszFilename); 1603 else 1604 { 1605 /* 1606 * Read some of it into the buffer and check that essential stuff is flagged as allocated. 1607 */ 1608 /* The boot sector. */ 1609 bool fState = false; 1610 rc = rtFsNtfsVol_QueryClusterState(pThis, 0, &fState); 1611 if (RT_SUCCESS(rc) && !fState) 1612 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1613 "MFT allocation bitmap error: Bootsector isn't marked allocated!"); 1614 else if (RT_FAILURE(rc)) 1615 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1616 "MFT allocation bitmap (offset 0) read error: %Rrc", rc); 1617 1618 /* The bitmap ifself, the MFT data, and the MFT bitmap. */ 1619 if (RT_SUCCESS(rc)) 1620 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftBitmap, "allocation bitmap", pErrInfo); 1621 if (RT_SUCCESS(rc)) 1622 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftData, "MFT", pErrInfo); 1623 if (RT_SUCCESS(rc)) 1624 rc = rtFsNtfsVolCheckBitmap(pThis, 1625 rtFsNtfsCore_FindUnnamedAttribute(pThis->pMftData->pCore, NTFS_AT_BITMAP), 1626 "MFT Bitmap", pErrInfo); 1627 if (RT_SUCCESS(rc)) 1628 { 1629 /* 1630 * Looks like the bitmap is good. 1631 */ 1632 return VINF_SUCCESS; 1633 } 1634 } 1635 } 1636 } 1637 pThis->pMftBitmap = NULL; 1638 } 1639 else 1640 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!"); 1641 rtFsNtfsCore_Release(pCore); 1642 } 1643 else 1644 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #6"); 1645 return rc; 1646 } 1647 1648 1649 1650 /** 1651 * Loads, validates and setups the '$Mft' (NTFS_MFT_IDX_MFT) MFT entry. 1652 * 1653 * This is the first thing we do after we've checked out the boot sector and 1654 * extracted information from it, since everything else depends on us being able 1655 * to access the MFT data. 1656 * 1657 * @returns IPRT status code 1658 * @param pThis The NTFS volume instance. Will set pMftData. 1659 * @param pErrInfo Where to return additional error info. 1660 */ 1661 static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo) 1662 { 1080 1663 /* 1081 1664 * Bootstrap the MFT data stream. 1082 1665 */ 1083 PRTFSNTFSMFTREC pRec = rtFsNtfs MftVol_New(pThis, 0);1666 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, NTFS_MFT_IDX_MFT); 1084 1667 AssertReturn(pRec, VERR_NO_MEMORY); 1085 1668 1086 #if 0 //def LOG_ENABLED 1087 for (uint32_t i = 0; i < 64; i++) 1088 { 1089 RTVfsFileReadAt(pThis->hVfsBacking, (pThis->uLcnMft << pThis->cClusterShift) + i * pThis->cbMftRecord, 1090 pRec->pbRec, pThis->cbMftRecord, NULL); 1091 pRec->TreeNode.Key = i; 1092 Log(("\n")); 1093 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord); 1094 } 1095 pRec->TreeNode.Key = 0; 1096 #endif 1097 1098 pRec->offDisk = pThis->uLcnMft << pThis->cClusterShift; 1099 int rc = RTVfsFileReadAt(pThis->hVfsBacking, pRec->offDisk, pRec->pbRec, pThis->cbMftRecord, NULL); 1669 uint64_t const offDisk = pThis->uLcnMft << pThis->cClusterShift; 1670 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL); 1100 1671 if (RT_SUCCESS(rc)) 1101 1672 { … … 1109 1680 if (pThis->pMftData) 1110 1681 { 1111 /** @todo sanity check the attribute. */ 1112 rtFsNtfsMftRec_Release(pRec, pThis); 1113 return rc; 1682 /* 1683 * Validate the '$Mft' MFT record. 1684 */ 1685 if (!pThis->pMftData->pAttrHdr->fNonResident) 1686 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 unnamed DATA attribute is resident!"); 1687 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) < pThis->cbMftRecord * 16U 1688 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) >= pThis->cbBacking) 1689 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1690 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64", 1691 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated)); 1692 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) < pThis->cbMftRecord * 16U 1693 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) >= pThis->cbBacking) 1694 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1695 "MFT record #0 unnamed DATA attribute initialized size is out of range: %#RX64", 1696 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized)); 1697 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) < pThis->cbMftRecord * 16U 1698 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) >= pThis->cbBacking) 1699 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1700 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64", 1701 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData)); 1702 else if (pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit != 0) 1703 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1704 "MFT record #0 unnamed DATA attribute is compressed: %#x", 1705 pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit); 1706 else if (pThis->pMftData->Extents.cExtents == 0) 1707 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1708 "MFT record #0 unnamed DATA attribute has no data on the disk"); 1709 else if (pThis->pMftData->Extents.paExtents[0].off != offDisk) 1710 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1711 "MFT record #0 unnamed DATA attribute has a bogus disk offset: %#RX64, expected %#RX64", 1712 pThis->pMftData->Extents.paExtents[0].off, offDisk); 1713 else if (!rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_BITMAP)) 1714 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed BITMAP attribute!"); 1715 else 1716 { 1717 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_FILENAME); 1718 if (!pFilenameAttr) 1719 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no FILENAME attribute!"); 1720 else if (pFilenameAttr->pAttrHdr->fNonResident) 1721 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 FILENAME attribute is non-resident!"); 1722 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[4])) 1723 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1724 "MFT record #0 FILENAME attribute value size is too small: %#x", 1725 pFilenameAttr->pAttrHdr->u.Res.cbValue); 1726 else 1727 { 1728 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr 1729 + pFilenameAttr->pAttrHdr->u.Res.offValue); 1730 if ( pFilename->cwcFilename != 4 1731 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Mft", 4) != 0) 1732 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, 1733 "MFT record #0 FILENAME isn't '$Mft': '%.*ls'", 1734 pFilename->cwcFilename, pFilename->wszFilename); 1735 else 1736 { 1737 /* 1738 * Looks like we're good. 1739 */ 1740 return VINF_SUCCESS; 1741 } 1742 } 1743 } 1744 pThis->pMftData = NULL; 1114 1745 } 1115 rc = RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!"); 1746 else 1747 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!"); 1116 1748 } 1117 if (pRec->pCore) 1118 rtFsNtfsCore_Release(pRec->pCore); 1749 rtFsNtfsCore_Release(pRec->pCore); 1119 1750 rtFsNtfsMftRec_Release(pRec, pThis); 1120 1751 } 1121 1752 else 1122 rc = RTERRINFO_LOG_ SET(pErrInfo, rc, "Error reading MFT record #0");1753 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #0"); 1123 1754 return rc; 1124 1755 } … … 1306 1937 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo); 1307 1938 if (RT_SUCCESS(rc)) 1308 rc = rtFsNtfsVolLoadMft(pThis, pvBuf, _64K, pErrInfo); 1939 rc = rtFsNtfsVolLoadMft(pThis, pErrInfo); 1940 if (RT_SUCCESS(rc)) 1941 rc = rtFsNtfsVolLoadBitmap(pThis, pErrInfo); 1309 1942 RTMemTmpFree(pvBuf); 1310 1943 if (RT_SUCCESS(rc))
Note:
See TracChangeset
for help on using the changeset viewer.