ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

ntfs.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.