Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenntfs.c
Go to the documentation of this file.
00001 /* 00002 * FreeLoader NTFS support 00003 * Copyright (C) 2004 Filip Navara <xnavara@volny.cz> 00004 * Copyright (C) 2009-2010 Hervé Poussineau 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00019 */ 00020 00021 /* 00022 * Limitations: 00023 * - No support for compressed files. 00024 * - May crash on corrupted filesystem. 00025 */ 00026 00027 #ifndef _M_ARM 00028 #include <freeldr.h> 00029 #include <debug.h> 00030 00031 DBG_DEFAULT_CHANNEL(FILESYSTEM); 00032 00033 typedef struct _NTFS_VOLUME_INFO 00034 { 00035 NTFS_BOOTSECTOR BootSector; 00036 ULONG ClusterSize; 00037 ULONG MftRecordSize; 00038 ULONG IndexRecordSize; 00039 PNTFS_MFT_RECORD MasterFileTable; 00040 /* FIXME: MFTContext is never freed. */ 00041 PNTFS_ATTR_CONTEXT MFTContext; 00042 ULONG DeviceId; 00043 PUCHAR TemporarySector; 00044 } NTFS_VOLUME_INFO; 00045 00046 PNTFS_VOLUME_INFO NtfsVolumes[MAX_FDS]; 00047 00048 static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord) 00049 { 00050 if (AttrRecord->IsNonResident) 00051 return AttrRecord->NonResident.DataSize; 00052 else 00053 return AttrRecord->Resident.ValueLength; 00054 } 00055 00056 static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength) 00057 { 00058 UCHAR DataRunOffsetSize; 00059 UCHAR DataRunLengthSize; 00060 CHAR i; 00061 00062 DataRunOffsetSize = (*DataRun >> 4) & 0xF; 00063 DataRunLengthSize = *DataRun & 0xF; 00064 *DataRunOffset = 0; 00065 *DataRunLength = 0; 00066 DataRun++; 00067 for (i = 0; i < DataRunLengthSize; i++) 00068 { 00069 *DataRunLength += *DataRun << (i << 3); 00070 DataRun++; 00071 } 00072 00073 /* NTFS 3+ sparse files */ 00074 if (DataRunOffsetSize == 0) 00075 { 00076 *DataRunOffset = -1; 00077 } 00078 else 00079 { 00080 for (i = 0; i < DataRunOffsetSize - 1; i++) 00081 { 00082 *DataRunOffset += *DataRun << (i << 3); 00083 DataRun++; 00084 } 00085 /* The last byte contains sign so we must process it different way. */ 00086 *DataRunOffset = ((CHAR)(*(DataRun++)) << (i << 3)) + *DataRunOffset; 00087 } 00088 00089 TRACE("DataRunOffsetSize: %x\n", DataRunOffsetSize); 00090 TRACE("DataRunLengthSize: %x\n", DataRunLengthSize); 00091 TRACE("DataRunOffset: %x\n", *DataRunOffset); 00092 TRACE("DataRunLength: %x\n", *DataRunLength); 00093 00094 return DataRun; 00095 } 00096 00097 static PNTFS_ATTR_CONTEXT NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord) 00098 { 00099 PNTFS_ATTR_CONTEXT Context; 00100 00101 Context = MmHeapAlloc(FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length); 00102 RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length); 00103 if (AttrRecord->IsNonResident) 00104 { 00105 LONGLONG DataRunOffset; 00106 ULONGLONG DataRunLength; 00107 00108 Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset; 00109 Context->CacheRunOffset = 0; 00110 Context->CacheRun = NtfsDecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength); 00111 Context->CacheRunLength = DataRunLength; 00112 if (DataRunOffset != -1) 00113 { 00114 /* Normal run. */ 00115 Context->CacheRunStartLCN = 00116 Context->CacheRunLastLCN = DataRunOffset; 00117 } 00118 else 00119 { 00120 /* Sparse run. */ 00121 Context->CacheRunStartLCN = -1; 00122 Context->CacheRunLastLCN = 0; 00123 } 00124 Context->CacheRunCurrentOffset = 0; 00125 } 00126 00127 return Context; 00128 } 00129 00130 static VOID NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context) 00131 { 00132 MmHeapFree(Context); 00133 } 00134 00135 static BOOLEAN NtfsDiskRead(PNTFS_VOLUME_INFO Volume, ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer) 00136 { 00137 LARGE_INTEGER Position; 00138 ULONG Count; 00139 ULONG ReadLength; 00140 LONG ret; 00141 00142 TRACE("NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset, Length); 00143 00144 // 00145 // I. Read partial first sector if needed 00146 // 00147 if (Offset % Volume->BootSector.BytesPerSector) 00148 { 00149 Position.QuadPart = Offset & ~(Volume->BootSector.BytesPerSector - 1); 00150 ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute); 00151 if (ret != ESUCCESS) 00152 return FALSE; 00153 ret = ArcRead(Volume->DeviceId, Volume->TemporarySector, Volume->BootSector.BytesPerSector, &Count); 00154 if (ret != ESUCCESS || Count != Volume->BootSector.BytesPerSector) 00155 return FALSE; 00156 ReadLength = (USHORT)min(Length, Volume->BootSector.BytesPerSector - (Offset % Volume->BootSector.BytesPerSector)); 00157 00158 // 00159 // Copy interesting data 00160 // 00161 RtlCopyMemory(Buffer, 00162 &Volume->TemporarySector[Offset % Volume->BootSector.BytesPerSector], 00163 ReadLength); 00164 00165 // 00166 // Move to unfilled buffer part 00167 // 00168 Buffer += ReadLength; 00169 Length -= ReadLength; 00170 Offset += ReadLength; 00171 } 00172 00173 // 00174 // II. Read all complete blocks 00175 // 00176 if (Length >= Volume->BootSector.BytesPerSector) 00177 { 00178 Position.QuadPart = Offset; 00179 ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute); 00180 if (ret != ESUCCESS) 00181 return FALSE; 00182 ReadLength = Length & ~(Volume->BootSector.BytesPerSector - 1); 00183 ret = ArcRead(Volume->DeviceId, Buffer, ReadLength, &Count); 00184 if (ret != ESUCCESS || Count != ReadLength) 00185 return FALSE; 00186 00187 // 00188 // Move to unfilled buffer part 00189 // 00190 Buffer += ReadLength; 00191 Length -= ReadLength; 00192 Offset += ReadLength; 00193 } 00194 00195 // 00196 // III. Read the rest of data 00197 // 00198 if (Length) 00199 { 00200 Position.QuadPart = Offset; 00201 ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute); 00202 if (ret != ESUCCESS) 00203 return FALSE; 00204 ret = ArcRead(Volume->DeviceId, Buffer, (ULONG)Length, &Count); 00205 if (ret != ESUCCESS || Count != Length) 00206 return FALSE; 00207 } 00208 00209 return TRUE; 00210 } 00211 00212 static ULONG NtfsReadAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length) 00213 { 00214 ULONGLONG LastLCN; 00215 PUCHAR DataRun; 00216 LONGLONG DataRunOffset; 00217 ULONGLONG DataRunLength; 00218 LONGLONG DataRunStartLCN; 00219 ULONGLONG CurrentOffset; 00220 ULONG ReadLength; 00221 ULONG AlreadyRead; 00222 00223 if (!Context->Record.IsNonResident) 00224 { 00225 if (Offset > Context->Record.Resident.ValueLength) 00226 return 0; 00227 if (Offset + Length > Context->Record.Resident.ValueLength) 00228 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset); 00229 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length); 00230 return Length; 00231 } 00232 00233 /* 00234 * Non-resident attribute 00235 */ 00236 00237 /* 00238 * I. Find the corresponding start data run. 00239 */ 00240 00241 AlreadyRead = 0; 00242 00243 // FIXME: Cache seems to be non-working. Disable it for now 00244 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize) 00245 if (0) 00246 { 00247 DataRun = Context->CacheRun; 00248 LastLCN = Context->CacheRunLastLCN; 00249 DataRunStartLCN = Context->CacheRunStartLCN; 00250 DataRunLength = Context->CacheRunLength; 00251 CurrentOffset = Context->CacheRunCurrentOffset; 00252 } 00253 else 00254 { 00255 LastLCN = 0; 00256 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset; 00257 CurrentOffset = 0; 00258 00259 while (1) 00260 { 00261 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength); 00262 if (DataRunOffset != -1) 00263 { 00264 /* Normal data run. */ 00265 DataRunStartLCN = LastLCN + DataRunOffset; 00266 LastLCN = DataRunStartLCN; 00267 } 00268 else 00269 { 00270 /* Sparse data run. */ 00271 DataRunStartLCN = -1; 00272 } 00273 00274 if (Offset >= CurrentOffset && 00275 Offset < CurrentOffset + (DataRunLength * Volume->ClusterSize)) 00276 { 00277 break; 00278 } 00279 00280 if (*DataRun == 0) 00281 { 00282 return AlreadyRead; 00283 } 00284 00285 CurrentOffset += DataRunLength * Volume->ClusterSize; 00286 } 00287 } 00288 00289 /* 00290 * II. Go through the run list and read the data 00291 */ 00292 00293 ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset), Length); 00294 if (DataRunStartLCN == -1) 00295 RtlZeroMemory(Buffer, ReadLength); 00296 if (NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize + Offset - CurrentOffset, ReadLength, Buffer)) 00297 { 00298 Length -= ReadLength; 00299 Buffer += ReadLength; 00300 AlreadyRead += ReadLength; 00301 00302 if (ReadLength == DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset)) 00303 { 00304 CurrentOffset += DataRunLength * Volume->ClusterSize; 00305 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength); 00306 if (DataRunLength != (ULONGLONG)-1) 00307 { 00308 DataRunStartLCN = LastLCN + DataRunOffset; 00309 LastLCN = DataRunStartLCN; 00310 } 00311 else 00312 DataRunStartLCN = -1; 00313 00314 if (*DataRun == 0) 00315 return AlreadyRead; 00316 } 00317 00318 while (Length > 0) 00319 { 00320 ReadLength = (ULONG)min(DataRunLength * Volume->ClusterSize, Length); 00321 if (DataRunStartLCN == -1) 00322 RtlZeroMemory(Buffer, ReadLength); 00323 else if (!NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize, ReadLength, Buffer)) 00324 break; 00325 00326 Length -= ReadLength; 00327 Buffer += ReadLength; 00328 AlreadyRead += ReadLength; 00329 00330 /* We finished this request, but there still data in this data run. */ 00331 if (Length == 0 && ReadLength != DataRunLength * Volume->ClusterSize) 00332 break; 00333 00334 /* 00335 * Go to next run in the list. 00336 */ 00337 00338 if (*DataRun == 0) 00339 break; 00340 CurrentOffset += DataRunLength * Volume->ClusterSize; 00341 DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength); 00342 if (DataRunOffset != -1) 00343 { 00344 /* Normal data run. */ 00345 DataRunStartLCN = LastLCN + DataRunOffset; 00346 LastLCN = DataRunStartLCN; 00347 } 00348 else 00349 { 00350 /* Sparse data run. */ 00351 DataRunStartLCN = -1; 00352 } 00353 } /* while */ 00354 00355 } /* if Disk */ 00356 00357 Context->CacheRun = DataRun; 00358 Context->CacheRunOffset = Offset + AlreadyRead; 00359 Context->CacheRunStartLCN = DataRunStartLCN; 00360 Context->CacheRunLength = DataRunLength; 00361 Context->CacheRunLastLCN = LastLCN; 00362 Context->CacheRunCurrentOffset = CurrentOffset; 00363 00364 return AlreadyRead; 00365 } 00366 00367 static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength) 00368 { 00369 while (AttrRecord < AttrRecordEnd) 00370 { 00371 if (AttrRecord->Type == NTFS_ATTR_TYPE_END) 00372 break; 00373 00374 if (AttrRecord->Type == NTFS_ATTR_TYPE_ATTRIBUTE_LIST) 00375 { 00376 PNTFS_ATTR_CONTEXT Context; 00377 PNTFS_ATTR_CONTEXT ListContext; 00378 PVOID ListBuffer; 00379 ULONGLONG ListSize; 00380 PNTFS_ATTR_RECORD ListAttrRecord; 00381 PNTFS_ATTR_RECORD ListAttrRecordEnd; 00382 00383 ListContext = NtfsPrepareAttributeContext(AttrRecord); 00384 00385 ListSize = NtfsGetAttributeSize(&ListContext->Record); 00386 if(ListSize <= 0xFFFFFFFF) 00387 ListBuffer = MmHeapAlloc((ULONG)ListSize); 00388 else 00389 ListBuffer = NULL; 00390 00391 if(!ListBuffer) 00392 { 00393 TRACE("Failed to allocate memory: %x\n", (ULONG)ListSize); 00394 continue; 00395 } 00396 00397 ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer; 00398 ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize); 00399 00400 if (NtfsReadAttribute(Volume, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize) 00401 { 00402 Context = NtfsFindAttributeHelper(Volume, ListAttrRecord, ListAttrRecordEnd, 00403 Type, Name, NameLength); 00404 00405 NtfsReleaseAttributeContext(ListContext); 00406 MmHeapFree(ListBuffer); 00407 00408 if (Context != NULL) 00409 return Context; 00410 } 00411 } 00412 00413 if (AttrRecord->Type == Type) 00414 { 00415 if (AttrRecord->NameLength == NameLength) 00416 { 00417 PWCHAR AttrName; 00418 00419 AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset); 00420 if (RtlEqualMemory(AttrName, Name, NameLength << 1)) 00421 { 00422 /* Found it, fill up the context and return. */ 00423 return NtfsPrepareAttributeContext(AttrRecord); 00424 } 00425 } 00426 } 00427 00428 if (AttrRecord->Length == 0) 00429 return NULL; 00430 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length); 00431 } 00432 00433 return NULL; 00434 } 00435 00436 static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name) 00437 { 00438 PNTFS_ATTR_RECORD AttrRecord; 00439 PNTFS_ATTR_RECORD AttrRecordEnd; 00440 ULONG NameLength; 00441 00442 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset); 00443 AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Volume->MftRecordSize); 00444 for (NameLength = 0; Name[NameLength] != 0; NameLength++) 00445 ; 00446 00447 return NtfsFindAttributeHelper(Volume, AttrRecord, AttrRecordEnd, Type, Name, NameLength); 00448 } 00449 00450 static BOOLEAN NtfsFixupRecord(PNTFS_VOLUME_INFO Volume, PNTFS_RECORD Record) 00451 { 00452 USHORT *USA; 00453 USHORT USANumber; 00454 USHORT USACount; 00455 USHORT *Block; 00456 00457 USA = (USHORT*)((PCHAR)Record + Record->USAOffset); 00458 USANumber = *(USA++); 00459 USACount = Record->USACount - 1; /* Exclude the USA Number. */ 00460 Block = (USHORT*)((PCHAR)Record + Volume->BootSector.BytesPerSector - 2); 00461 00462 while (USACount) 00463 { 00464 if (*Block != USANumber) 00465 return FALSE; 00466 *Block = *(USA++); 00467 Block = (USHORT*)((PCHAR)Block + Volume->BootSector.BytesPerSector); 00468 USACount--; 00469 } 00470 00471 return TRUE; 00472 } 00473 00474 static BOOLEAN NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PNTFS_MFT_RECORD Buffer) 00475 { 00476 ULONGLONG BytesRead; 00477 00478 BytesRead = NtfsReadAttribute(Volume, Volume->MFTContext, MFTIndex * Volume->MftRecordSize, (PCHAR)Buffer, Volume->MftRecordSize); 00479 if (BytesRead != Volume->MftRecordSize) 00480 return FALSE; 00481 00482 /* Apply update sequence array fixups. */ 00483 return NtfsFixupRecord(Volume, (PNTFS_RECORD)Buffer); 00484 } 00485 00486 #if DBG 00487 VOID NtfsPrintFile(PNTFS_INDEX_ENTRY IndexEntry) 00488 { 00489 PWCHAR FileName; 00490 UCHAR FileNameLength; 00491 CHAR AnsiFileName[256]; 00492 UCHAR i; 00493 00494 FileName = IndexEntry->FileName.FileName; 00495 FileNameLength = min(IndexEntry->FileName.FileNameLength, 255); 00496 00497 for (i = 0; i < FileNameLength; i++) 00498 AnsiFileName[i] = (CHAR)FileName[i]; 00499 AnsiFileName[i] = 0; 00500 00501 TRACE("- %s (%x)\n", AnsiFileName, IndexEntry->Data.Directory.IndexedFile); 00502 } 00503 #endif 00504 00505 static BOOLEAN NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry) 00506 { 00507 PWCHAR EntryFileName; 00508 UCHAR EntryFileNameLength; 00509 UCHAR i; 00510 00511 EntryFileName = IndexEntry->FileName.FileName; 00512 EntryFileNameLength = IndexEntry->FileName.FileNameLength; 00513 00514 #if DBG 00515 NtfsPrintFile(IndexEntry); 00516 #endif 00517 00518 if (strlen(FileName) != EntryFileNameLength) 00519 return FALSE; 00520 00521 /* Do case-sensitive compares for Posix file names. */ 00522 if (IndexEntry->FileName.FileNameType == NTFS_FILE_NAME_POSIX) 00523 { 00524 for (i = 0; i < EntryFileNameLength; i++) 00525 if (EntryFileName[i] != FileName[i]) 00526 return FALSE; 00527 } 00528 else 00529 { 00530 for (i = 0; i < EntryFileNameLength; i++) 00531 if (tolower(EntryFileName[i]) != tolower(FileName[i])) 00532 return FALSE; 00533 } 00534 00535 return TRUE; 00536 } 00537 00538 static BOOLEAN NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume, ULONGLONG MFTIndex, PCHAR FileName, ULONGLONG *OutMFTIndex) 00539 { 00540 PNTFS_MFT_RECORD MftRecord; 00541 //ULONG Magic; 00542 PNTFS_ATTR_CONTEXT IndexRootCtx; 00543 PNTFS_ATTR_CONTEXT IndexBitmapCtx; 00544 PNTFS_ATTR_CONTEXT IndexAllocationCtx; 00545 PNTFS_INDEX_ROOT IndexRoot; 00546 ULONGLONG BitmapDataSize; 00547 ULONGLONG IndexAllocationSize; 00548 PCHAR BitmapData; 00549 PCHAR IndexRecord; 00550 PNTFS_INDEX_ENTRY IndexEntry, IndexEntryEnd; 00551 ULONG RecordOffset; 00552 ULONG IndexBlockSize; 00553 00554 MftRecord = MmHeapAlloc(Volume->MftRecordSize); 00555 if (MftRecord == NULL) 00556 { 00557 return FALSE; 00558 } 00559 00560 if (NtfsReadMftRecord(Volume, MFTIndex, MftRecord)) 00561 { 00562 //Magic = MftRecord->Magic; 00563 00564 IndexRootCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30"); 00565 if (IndexRootCtx == NULL) 00566 { 00567 MmHeapFree(MftRecord); 00568 return FALSE; 00569 } 00570 00571 IndexRecord = MmHeapAlloc(Volume->IndexRecordSize); 00572 if (IndexRecord == NULL) 00573 { 00574 MmHeapFree(MftRecord); 00575 return FALSE; 00576 } 00577 00578 NtfsReadAttribute(Volume, IndexRootCtx, 0, IndexRecord, Volume->IndexRecordSize); 00579 IndexRoot = (PNTFS_INDEX_ROOT)IndexRecord; 00580 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)&IndexRoot->IndexHeader + IndexRoot->IndexHeader.EntriesOffset); 00581 /* Index root is always resident. */ 00582 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength); 00583 NtfsReleaseAttributeContext(IndexRootCtx); 00584 00585 TRACE("IndexRecordSize: %x IndexBlockSize: %x\n", Volume->IndexRecordSize, IndexRoot->IndexBlockSize); 00586 00587 while (IndexEntry < IndexEntryEnd && 00588 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) 00589 { 00590 if (NtfsCompareFileName(FileName, IndexEntry)) 00591 { 00592 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile; 00593 MmHeapFree(IndexRecord); 00594 MmHeapFree(MftRecord); 00595 return TRUE; 00596 } 00597 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length); 00598 } 00599 00600 if (IndexRoot->IndexHeader.Flags & NTFS_LARGE_INDEX) 00601 { 00602 TRACE("Large Index!\n"); 00603 00604 IndexBlockSize = IndexRoot->IndexBlockSize; 00605 00606 IndexBitmapCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30"); 00607 if (IndexBitmapCtx == NULL) 00608 { 00609 TRACE("Corrupted filesystem!\n"); 00610 MmHeapFree(MftRecord); 00611 return FALSE; 00612 } 00613 BitmapDataSize = NtfsGetAttributeSize(&IndexBitmapCtx->Record); 00614 TRACE("BitmapDataSize: %x\n", (ULONG)BitmapDataSize); 00615 if(BitmapDataSize <= 0xFFFFFFFF) 00616 BitmapData = MmHeapAlloc((ULONG)BitmapDataSize); 00617 else 00618 BitmapData = NULL; 00619 00620 if (BitmapData == NULL) 00621 { 00622 MmHeapFree(IndexRecord); 00623 MmHeapFree(MftRecord); 00624 return FALSE; 00625 } 00626 NtfsReadAttribute(Volume, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize); 00627 NtfsReleaseAttributeContext(IndexBitmapCtx); 00628 00629 IndexAllocationCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30"); 00630 if (IndexAllocationCtx == NULL) 00631 { 00632 TRACE("Corrupted filesystem!\n"); 00633 MmHeapFree(BitmapData); 00634 MmHeapFree(IndexRecord); 00635 MmHeapFree(MftRecord); 00636 return FALSE; 00637 } 00638 IndexAllocationSize = NtfsGetAttributeSize(&IndexAllocationCtx->Record); 00639 00640 RecordOffset = 0; 00641 00642 for (;;) 00643 { 00644 TRACE("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize); 00645 for (; RecordOffset < IndexAllocationSize;) 00646 { 00647 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7); 00648 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3; 00649 if ((BitmapData[Byte] & Bit)) 00650 break; 00651 RecordOffset += IndexBlockSize; 00652 } 00653 00654 if (RecordOffset >= IndexAllocationSize) 00655 { 00656 break; 00657 } 00658 00659 NtfsReadAttribute(Volume, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize); 00660 00661 if (!NtfsFixupRecord(Volume, (PNTFS_RECORD)IndexRecord)) 00662 { 00663 break; 00664 } 00665 00666 /* FIXME */ 00667 IndexEntry = (PNTFS_INDEX_ENTRY)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18)); 00668 IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexBlockSize); 00669 00670 while (IndexEntry < IndexEntryEnd && 00671 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) 00672 { 00673 if (NtfsCompareFileName(FileName, IndexEntry)) 00674 { 00675 TRACE("File found\n"); 00676 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile; 00677 MmHeapFree(BitmapData); 00678 MmHeapFree(IndexRecord); 00679 MmHeapFree(MftRecord); 00680 NtfsReleaseAttributeContext(IndexAllocationCtx); 00681 return TRUE; 00682 } 00683 IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length); 00684 } 00685 00686 RecordOffset += IndexBlockSize; 00687 } 00688 00689 NtfsReleaseAttributeContext(IndexAllocationCtx); 00690 MmHeapFree(BitmapData); 00691 } 00692 00693 MmHeapFree(IndexRecord); 00694 } 00695 else 00696 { 00697 TRACE("Can't read MFT record\n"); 00698 } 00699 MmHeapFree(MftRecord); 00700 00701 return FALSE; 00702 } 00703 00704 static BOOLEAN NtfsLookupFile(PNTFS_VOLUME_INFO Volume, PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT *DataContext) 00705 { 00706 ULONG NumberOfPathParts; 00707 CHAR PathPart[261]; 00708 ULONGLONG CurrentMFTIndex; 00709 UCHAR i; 00710 00711 TRACE("NtfsLookupFile() FileName = %s\n", FileName); 00712 00713 CurrentMFTIndex = NTFS_FILE_ROOT; 00714 NumberOfPathParts = FsGetNumPathParts(FileName); 00715 for (i = 0; i < NumberOfPathParts; i++) 00716 { 00717 FsGetFirstNameFromPath(PathPart, FileName); 00718 00719 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++) 00720 ; 00721 FileName++; 00722 00723 TRACE("- Lookup: %s\n", PathPart); 00724 if (!NtfsFindMftRecord(Volume, CurrentMFTIndex, PathPart, &CurrentMFTIndex)) 00725 { 00726 TRACE("- Failed\n"); 00727 return FALSE; 00728 } 00729 TRACE("- Lookup: %x\n", CurrentMFTIndex); 00730 } 00731 00732 if (!NtfsReadMftRecord(Volume, CurrentMFTIndex, MftRecord)) 00733 { 00734 TRACE("NtfsLookupFile: Can't read MFT record\n"); 00735 return FALSE; 00736 } 00737 00738 *DataContext = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_DATA, L""); 00739 if (*DataContext == NULL) 00740 { 00741 TRACE("NtfsLookupFile: Can't find data attribute\n"); 00742 return FALSE; 00743 } 00744 00745 return TRUE; 00746 } 00747 00748 LONG NtfsClose(ULONG FileId) 00749 { 00750 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId); 00751 00752 NtfsReleaseAttributeContext(FileHandle->DataContext); 00753 MmHeapFree(FileHandle); 00754 00755 return ESUCCESS; 00756 } 00757 00758 LONG NtfsGetFileInformation(ULONG FileId, FILEINFORMATION* Information) 00759 { 00760 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId); 00761 00762 RtlZeroMemory(Information, sizeof(FILEINFORMATION)); 00763 Information->EndingAddress.QuadPart = NtfsGetAttributeSize(&FileHandle->DataContext->Record); 00764 Information->CurrentAddress.QuadPart = FileHandle->Offset; 00765 00766 TRACE("NtfsGetFileInformation() FileSize = %d\n", 00767 Information->EndingAddress.LowPart); 00768 TRACE("NtfsGetFileInformation() FilePointer = %d\n", 00769 Information->CurrentAddress.LowPart); 00770 00771 return ESUCCESS; 00772 } 00773 00774 LONG NtfsOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId) 00775 { 00776 PNTFS_VOLUME_INFO Volume; 00777 PNTFS_FILE_HANDLE FileHandle; 00778 PNTFS_MFT_RECORD MftRecord; 00779 ULONG DeviceId; 00780 00781 // 00782 // Check parameters 00783 // 00784 if (OpenMode != OpenReadOnly) 00785 return EACCES; 00786 00787 // 00788 // Get underlying device 00789 // 00790 DeviceId = FsGetDeviceId(*FileId); 00791 Volume = NtfsVolumes[DeviceId]; 00792 00793 TRACE("NtfsOpen() FileName = %s\n", Path); 00794 00795 // 00796 // Allocate file structure 00797 // 00798 FileHandle = MmHeapAlloc(sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize); 00799 if (!FileHandle) 00800 { 00801 return ENOMEM; 00802 } 00803 RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize); 00804 FileHandle->Volume = Volume; 00805 00806 // 00807 // Search file entry 00808 // 00809 MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1); 00810 if (!NtfsLookupFile(Volume, Path, MftRecord, &FileHandle->DataContext)) 00811 { 00812 MmHeapFree(FileHandle); 00813 return ENOENT; 00814 } 00815 00816 FsSetDeviceSpecific(*FileId, FileHandle); 00817 return ESUCCESS; 00818 } 00819 00820 LONG NtfsRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count) 00821 { 00822 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId); 00823 ULONGLONG BytesRead64; 00824 00825 // 00826 // Read file 00827 // 00828 BytesRead64 = NtfsReadAttribute(FileHandle->Volume, FileHandle->DataContext, FileHandle->Offset, Buffer, N); 00829 *Count = (ULONG)BytesRead64; 00830 00831 // 00832 // Check for success 00833 // 00834 if (BytesRead64 > 0) 00835 return ESUCCESS; 00836 else 00837 return EIO; 00838 } 00839 00840 LONG NtfsSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode) 00841 { 00842 PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId); 00843 00844 TRACE("NtfsSeek() NewFilePointer = %lu\n", Position->LowPart); 00845 00846 if (SeekMode != SeekAbsolute) 00847 return EINVAL; 00848 if (Position->HighPart != 0) 00849 return EINVAL; 00850 if (Position->LowPart >= (ULONG)NtfsGetAttributeSize(&FileHandle->DataContext->Record)) 00851 return EINVAL; 00852 00853 FileHandle->Offset = Position->LowPart; 00854 return ESUCCESS; 00855 } 00856 00857 const DEVVTBL NtfsFuncTable = 00858 { 00859 NtfsClose, 00860 NtfsGetFileInformation, 00861 NtfsOpen, 00862 NtfsRead, 00863 NtfsSeek, 00864 L"ntfs", 00865 }; 00866 00867 const DEVVTBL* NtfsMount(ULONG DeviceId) 00868 { 00869 PNTFS_VOLUME_INFO Volume; 00870 LARGE_INTEGER Position; 00871 ULONG Count; 00872 LONG ret; 00873 00874 // 00875 // Allocate data for volume information 00876 // 00877 Volume = MmHeapAlloc(sizeof(NTFS_VOLUME_INFO)); 00878 if (!Volume) 00879 return NULL; 00880 RtlZeroMemory(Volume, sizeof(NTFS_VOLUME_INFO)); 00881 00882 // 00883 // Read the BootSector 00884 // 00885 Position.HighPart = 0; 00886 Position.LowPart = 0; 00887 ret = ArcSeek(DeviceId, &Position, SeekAbsolute); 00888 if (ret != ESUCCESS) 00889 { 00890 MmHeapFree(Volume); 00891 return NULL; 00892 } 00893 ret = ArcRead(DeviceId, &Volume->BootSector, sizeof(Volume->BootSector), &Count); 00894 if (ret != ESUCCESS || Count != sizeof(Volume->BootSector)) 00895 { 00896 MmHeapFree(Volume); 00897 return NULL; 00898 } 00899 00900 // 00901 // Check if BootSector is valid. If no, return early 00902 // 00903 if (!RtlEqualMemory(Volume->BootSector.SystemId, "NTFS", 4)) 00904 { 00905 MmHeapFree(Volume); 00906 return NULL; 00907 } 00908 00909 // 00910 // Calculate cluster size and MFT record size 00911 // 00912 Volume->ClusterSize = Volume->BootSector.SectorsPerCluster * Volume->BootSector.BytesPerSector; 00913 if (Volume->BootSector.ClustersPerMftRecord > 0) 00914 Volume->MftRecordSize = Volume->BootSector.ClustersPerMftRecord * Volume->ClusterSize; 00915 else 00916 Volume->MftRecordSize = 1 << (-Volume->BootSector.ClustersPerMftRecord); 00917 if (Volume->BootSector.ClustersPerIndexRecord > 0) 00918 Volume->IndexRecordSize = Volume->BootSector.ClustersPerIndexRecord * Volume->ClusterSize; 00919 else 00920 Volume->IndexRecordSize = 1 << (-Volume->BootSector.ClustersPerIndexRecord); 00921 00922 TRACE("ClusterSize: 0x%x\n", Volume->ClusterSize); 00923 TRACE("ClustersPerMftRecord: %d\n", Volume->BootSector.ClustersPerMftRecord); 00924 TRACE("ClustersPerIndexRecord: %d\n", Volume->BootSector.ClustersPerIndexRecord); 00925 TRACE("MftRecordSize: 0x%x\n", Volume->MftRecordSize); 00926 TRACE("IndexRecordSize: 0x%x\n", Volume->IndexRecordSize); 00927 00928 // 00929 // Read MFT index 00930 // 00931 TRACE("Reading MFT index...\n"); 00932 Volume->MasterFileTable = MmHeapAlloc(Volume->MftRecordSize); 00933 if (!Volume->MasterFileTable) 00934 { 00935 MmHeapFree(Volume); 00936 return NULL; 00937 } 00938 Position.QuadPart = Volume->BootSector.MftLocation * Volume->ClusterSize; 00939 ret = ArcSeek(DeviceId, &Position, SeekAbsolute); 00940 if (ret != ESUCCESS) 00941 { 00942 FileSystemError("Failed to seek to Master File Table record."); 00943 MmHeapFree(Volume->MasterFileTable); 00944 MmHeapFree(Volume); 00945 return NULL; 00946 } 00947 ret = ArcRead(DeviceId, Volume->MasterFileTable, Volume->MftRecordSize, &Count); 00948 if (ret != ESUCCESS || Count != Volume->MftRecordSize) 00949 { 00950 FileSystemError("Failed to read the Master File Table record."); 00951 MmHeapFree(Volume->MasterFileTable); 00952 MmHeapFree(Volume); 00953 return NULL; 00954 } 00955 00956 // 00957 // Keep room to read partial sectors 00958 // 00959 Volume->TemporarySector = MmHeapAlloc(Volume->BootSector.BytesPerSector); 00960 if (!Volume->TemporarySector) 00961 { 00962 FileSystemError("Failed to allocate memory."); 00963 MmHeapFree(Volume->MasterFileTable); 00964 MmHeapFree(Volume); 00965 return NULL; 00966 } 00967 00968 // 00969 // Keep device id 00970 // 00971 Volume->DeviceId = DeviceId; 00972 00973 // 00974 // Search DATA attribute 00975 // 00976 TRACE("Searching for DATA attribute...\n"); 00977 Volume->MFTContext = NtfsFindAttribute(Volume, Volume->MasterFileTable, NTFS_ATTR_TYPE_DATA, L""); 00978 if (!Volume->MFTContext) 00979 { 00980 FileSystemError("Can't find data attribute for Master File Table."); 00981 MmHeapFree(Volume->MasterFileTable); 00982 MmHeapFree(Volume); 00983 return NULL; 00984 } 00985 00986 // 00987 // Remember NTFS volume information 00988 // 00989 NtfsVolumes[DeviceId] = Volume; 00990 00991 // 00992 // Return success 00993 // 00994 return &NtfsFuncTable; 00995 } 00996 00997 #endif Generated on Sat May 26 2012 04:17:58 for ReactOS by
1.7.6.1
|