From: John Hodge Date: Sat, 22 Jun 2013 16:06:53 +0000 (+0800) Subject: Modules/NTFS - Implimentation in progress X-Git-Tag: rel0.15~422 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=3baaddee040c6e58d3fdb10c3d04b69354e221e7;hp=cdf177534936e5380ff41a5c073367553e8a55bf;p=tpg%2Facess2.git Modules/NTFS - Implimentation in progress --- diff --git a/KernelLand/Modules/Filesystems/NTFS/attributes.h b/KernelLand/Modules/Filesystems/NTFS/attributes.h index 671a36e7..b72588e9 100644 --- a/KernelLand/Modules/Filesystems/NTFS/attributes.h +++ b/KernelLand/Modules/Filesystems/NTFS/attributes.h @@ -9,12 +9,49 @@ #ifndef _ATTRIBUTES_H_ #define _ATTRIBUTES_H_ +enum eNTFS_FILE_Attribs +{ + NTFS_FileAttrib_StandardInformation = 0x10, + NTFS_FileAttrib_FileName = 0x30, + NTFS_FileAttrib_SecurityDescriptor = 0x50, + NTFS_FileAttrib_Data = 0x80, + NTFS_FileAttrib_IndexRoot = 0x90, + NTFS_FileAttrib_IndexAllocation = 0xA0, + NTFS_FileAttrib_Bitmap = 0xB0, + // NTFS_FileAttrib_ +}; + typedef struct +{ + Uint64 CreationTime; + Uint64 AlterTime; + Uint64 MFTChangeTime; + Uint64 ReadTime; + Uint32 DOSFilePermissions; + Uint32 MaxNumVersions; + Uint32 VersionNumber; + Uint32 ClassID; + // 2K+ + Uint32 OwnerID; + Uint32 SecurityID; + Uint64 QuotaCharged; + Uint64 UpdateSequenceNumber; +} PACKED tNTFS_Attrib_StandardInformation; + +enum eNTFS_Filename_Namespaces +{ + NTFS_FilenameNamespace_POSIX, // [^NUL,/] + NTFS_FilenameNamespace_Win32, // + NTFS_FilenameNamespace_DOS, // + NTFS_FilenameNamespace_Win32DOS, // Same name in both Win32/DOS, so merged +}; + +typedef struct sNTFS_Attrib_Filename { Uint64 ParentDirectory; //!< Parent directory MFT entry Sint64 CreationTime; //!< Time the file was created Sint64 LastDataModTime; //!< Last change time for the data - Sint64 LastMtfModTime; //!< Last change time for the MFT entry + Sint64 LastMftModTime; //!< Last change time for the MFT entry Sint64 LastAccessTime; //!< Last Access Time (unreliable on most systems) Uint64 AllocatedSize; //!< Allocated data size for $DATA unnamed stream @@ -30,9 +67,38 @@ typedef struct Uint32 Tag; //!< Type of reparse point } PACKED ReparsePoint; } PACKED Type; - - Uint8 FilenameType; //!< Filename namespace (DOS, Windows, Unix) - WCHAR Filename[0]; + + Uint8 FilenameLength; + Uint8 FilenameNamespac; //!< Filename namespace (DOS, Windows, Unix) + Uint16 Filename[0]; } PACKED tNTFS_Attrib_Filename; +typedef struct sNTFS_Attrib_IndexEntry +{ + Uint64 FileReference; + Uint16 EntryLength; + Uint16 StreamLength; + Uint8 Flags; // [0]: Sub-node, [1]: Last entry in node + Uint8 _pad[3]; + // Stream data and sub-node VCN (64-bit) follows +} PACKED tNTFS_Attrib_IndexEntry; + +typedef struct sNTFS_Attrib_IndexRoot +{ + // Index Root + Uint32 AttributeType; // Type of indexed attribute + Uint32 CollationRule; // Sorting method + Uint32 AllocEntrySize; // + Uint8 ClustersPerIndexRec; + Uint8 _pad1[3]; + // Index Header + Uint32 FirstEntryOfs; + Uint32 TotalSize; + Uint32 AllocSize; + Uint8 Flags; + Uint8 _pad2[3]; + // List of IndexEntry structures follow +} PACKED tNTFS_Attrib_IndexRoot; + + #endif diff --git a/KernelLand/Modules/Filesystems/NTFS/common.h b/KernelLand/Modules/Filesystems/NTFS/common.h index d6b11e8a..ac93b520 100644 --- a/KernelLand/Modules/Filesystems/NTFS/common.h +++ b/KernelLand/Modules/Filesystems/NTFS/common.h @@ -6,150 +6,83 @@ * * common.h - Common Types and Definitions */ -#ifndef _COMMON_H_ -#define _COMMON_H_ +#ifndef _NTFS__COMMON_H_ +#define _NTFS__COMMON_H_ #include #include +#include "ntfs.h" -typedef Uint16 WCHAR; +typedef struct sNTFS_File tNTFS_File; +typedef struct sNTFS_Directory tNTFS_Directory; +typedef struct sNTFS_Disk tNTFS_Disk; +typedef struct sNTFS_Attrib tNTFS_Attrib; +typedef struct sNTFS_AttribDataRun tNTFS_AttribDataRun; // === STRUCTURES === + +struct sNTFS_File +{ + tVFS_Node Node; + tNTFS_Attrib *Data; +}; + +struct sNTFS_Directory +{ + tVFS_Node Node; + tNTFS_Attrib *I30Root; + tNTFS_Attrib *I30Allocation; +}; + /** * In-memory representation of an NTFS Disk */ -typedef struct sNTFS_Disk +struct sNTFS_Disk { int FD; int CacheHandle; int ClusterSize; + Uint64 MFTBase; Uint32 MFTRecSize; - tVFS_Node RootNode; -} tNTFS_Disk; - -typedef struct sNTFS_BootSector -{ - // 0 - Uint8 Jump[3]; - Uint8 SystemID[8]; // = "NTFS " - Uint16 BytesPerSector; - Uint8 SectorsPerCluster; - - // 0xE - Uint8 Unused[7]; - Uint8 MediaDescriptor; - Uint16 Unused2; - Uint16 SectorsPerTrack; - Uint16 Heads; - - // 0x1C - Uint64 Unused3; - Uint32 Unkown; // Usually 0x00800080 (according to Linux docs) - - // 0x28 - Uint64 TotalSectorCount; // Size of volume in sectors - Uint64 MFTStart; // Logical Cluster Number of Cluster 0 of MFT - Uint64 MFTMirrorStart; // Logical Cluster Number of Cluster 0 of MFT Backup - - // 0x40 - // If either of these are -ve, the size can be obtained via - // SizeInBytes = 2^(-1 * Value) - Sint8 ClustersPerMFTRecord; - Uint8 Unused4[3]; - Sint8 ClustersPerIndexRecord; - Uint8 Unused5[3]; + tNTFS_Attrib *MFTDataAttr; + tNTFS_Attrib *MFTBitmapAttr; - Uint64 SerialNumber; - - Uint8 Padding[512-0x50]; - -} PACKED tNTFS_BootSector; + tNTFS_Directory RootDir; +}; -/** - * FILE header, an entry in the MFT - */ -typedef struct sNTFS_FILE_Header +struct sNTFS_AttribDataRun { - Uint32 Magic; // 'FILE' - Uint16 UpdateSequenceOfs; - Uint16 UpdateSequenceSize; // Size in words of the UpdateSequenceArray - - Uint64 LSN; // $LogFile Sequence Number - - Uint16 SequenceNumber; - Uint16 HardLinkCount; - Uint16 FirstAttribOfs; // Size of header? - Uint16 Flags; // 0: In Use, 1: Directory - - Uint32 RecordSize; // Real Size of FILE Record - Uint32 RecordSpace; // Allocated Size for FILE Record - - /** - * Base address of the MFT containing this record - */ - Uint64 Reference; // "File reference to the base FILE record" ??? - - Uint16 NextAttribID; - union - { - // Only in XP - struct { - Uint16 AlignTo4Byte; - Uint16 RecordNumber; // Number of this MFT Record - Uint16 UpdateSequenceNumber; - Uint16 UpdateSequenceArray[]; - } XP; - struct { - Uint16 UpdateSequenceNumber; - Uint16 UpdateSequenceArray[]; - } All; - } OSDep; - -} PACKED tNTFS_FILE_Header; + Uint64 Count; + Uint64 LCN; +}; -/** - * File Attribute, follows the FILE header - */ -typedef struct sNTFS_FILE_Attrib +struct sNTFS_Attrib { - Uint32 Type; // See eNTFS_FILE_Attribs - Uint32 Size; // Includes header - - Uint8 ResidentFlag; // (What does this mean?) - Uint8 NameLength; - Uint16 NameOffset; - Uint16 Flags; // 0: Compressed, 14: Encrypted, 15: Sparse - Uint16 AttributeID; - - union - { - struct { - Uint32 AttribLen; // In words - Uint16 AttribOfs; - Uint8 IndexedFlag; - Uint8 Padding; - - Uint16 Name[]; // UTF-16 - // Attribute Data - } Resident; + tNTFS_Disk *Disk; + Uint32 Type; + + char *Name; + + Uint64 DataSize; + + int IsResident; + union { + void *ResidentData; struct { - Uint64 StartingVCN; - Uint64 LastVCN; - Uint16 DataRunOfs; - Uint16 CompressionUnitSize; - Uint32 Padding; - Uint64 AllocatedSize; - Uint64 RealSize; - Uint64 InitiatedSize; // One assumes, ammount of actual data stored - Uint16 Name[]; // UTF-16 - // Data Runs - } NonResident; + int nRuns; + int CompressionUnitL2Size; // 0 = uncompressed + Uint64 FirstPopulatedCluster; + tNTFS_AttribDataRun *Runs; + } NonResident; }; -} PACKED tNTFS_FILE_Attrib; +}; +// -- MFT Access / Manipulation +extern tNTFS_Attrib *NTFS_GetAttrib(tNTFS_Disk *Disk, Uint32 MFTEntry, int Type, const char *Name, int DesIdx); +extern size_t NTFS_ReadAttribData(tNTFS_Attrib *Attrib, Uint64 Offset, size_t Length, void *Buffer); // -- dir.c extern int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]); extern tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags); diff --git a/KernelLand/Modules/Filesystems/NTFS/dir.c b/KernelLand/Modules/Filesystems/NTFS/dir.c index 538445b3..50667837 100644 --- a/KernelLand/Modules/Filesystems/NTFS/dir.c +++ b/KernelLand/Modules/Filesystems/NTFS/dir.c @@ -6,8 +6,10 @@ * * dir.c - Directory Handling */ +#define DEBUG 1 #include "common.h" #include "index.h" +#include // === PROTOTYPES === int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]); @@ -20,6 +22,57 @@ Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str */ int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]) { + tNTFS_Directory *dir = (void*)Node; + tNTFS_Disk *disk = Node->ImplPtr; + + ASSERT(dir->I30Root->IsResident); + const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData; + //const tNTFS_Attrib_IndexEntry *rootents = (void*)(idxroot + 1); + + if( idxroot->Flags & 0x01 ) + { + // Read from allocation + char buf[disk->ClusterSize]; + struct sNTFS_IndexHeader *hdr = (void*)buf; + size_t ofs = 0; + size_t len = sizeof(buf); + struct sNTFS_IndexEntry_Filename *ent = (void*)(buf + len); + + for(;;) + { + if( (char*)ent == buf + len ) { + if( len < sizeof(buf)) + break ; + len = NTFS_ReadAttribData(dir->I30Allocation, ofs, sizeof(buf), buf); + ofs += sizeof(buf); + //Debug_HexDump("NTFS_ReadDir", buf, sizeof(*hdr)); + ent = (void*)(buf + (hdr->EntriesOffset + 0x18)); + } + if( Pos -- <= 0 ) + break; + LOG("ent = {.MFTEnt=%llx,.FilenameOfs=%x}", ent->MFTReference, ent->FilenameOfs); + //Uint16 *name16 = (Uint16*)ent + ent->FilenameOfs/2; + Uint16 *name16 = ent->Filename.Filename; + size_t nlen = UTF16_ConvertToUTF8(0, NULL, ent->Filename.FilenameLength, name16); + char tmpname[ nlen+1 ]; + UTF16_ConvertToUTF8(nlen+1, tmpname, ent->Filename.FilenameLength, name16); + LOG("name = '%s'", tmpname); + ent = (void*)((char*)ent + ent->EntrySize); + } + + if( Pos < 0 ) + { + //Uint16 *name16 = (Uint16*)ent + ent->FilenameOfs/2; + Uint16 *name16 = ent->Filename.Filename; + UTF16_ConvertToUTF8(FILENAME_MAX, Dest, ent->Filename.FilenameLength, name16); + return 0; + } + } + else + { + // Local only + } + return -ENOTIMPL; } @@ -28,15 +81,21 @@ int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]) */ tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags) { + #if 0 + tNTFS_Directory *dir = (void*)Node; tNTFS_Disk *disk = Node->ImplPtr; - Uint64 inode = NTFS_int_IndexLookup(Node->Inode, "$I30", Name); - tVFS_Node node; - - if(!inode) return NULL; - - node.Inode = inode; + ASSERT(dir->I30Root->IsResident); + const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData; + + if( idxroot->Flags & 0x01 ) + { + char buf[disk->ClusterSize]; + size_t len = NTFS_ReadAttribData(dir->I30Allocation, 0, sizeof(buf), buf); + struct sNTFS_IndexHeader *hdr = (void*)buf; + } + #endif - return Inode_CacheNode(disk->CacheHandle, &node); + return NULL; } /** diff --git a/KernelLand/Modules/Filesystems/NTFS/main.c b/KernelLand/Modules/Filesystems/NTFS/main.c index b9c4b8dd..dd1bf846 100644 --- a/KernelLand/Modules/Filesystems/NTFS/main.c +++ b/KernelLand/Modules/Filesystems/NTFS/main.c @@ -2,7 +2,10 @@ * Acess2 - NTFS Driver * By John Hodge (thePowersGang) * - * main.c - Driver core + * main.c + * - Driver core + * + * Reference: ntfsdoc.pdf */ #define DEBUG 1 #define VERBOSE 0 @@ -10,23 +13,31 @@ #include #include "common.h" #include +#include // === PROTOTYPES === int NTFS_Install(char **Arguments); + int NTFS_Detect(int FD); tVFS_Node *NTFS_InitDevice(const char *Devices, const char **Options); void NTFS_Unmount(tVFS_Node *Node); +// - MFT Related Functions +tNTFS_FILE_Header *NTFS_GetMFT(tNTFS_Disk *Disk, Uint32 MFTEntry); +void NTFS_ReleaseMFT(tNTFS_Disk *Disk, Uint32 MFTEntry, tNTFS_FILE_Header *Entry); +tNTFS_Attrib *NTFS_GetAttrib(tNTFS_Disk *Disk, Uint32 MFTEntry, int Type, const char *Name, int DesIdx); +size_t NTFS_ReadAttribData(tNTFS_Attrib *Attrib, Uint64 Offset, size_t Length, void *Buffer); void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry); // === GLOBALS === MODULE_DEFINE(0, 0x0A /*v0.1*/, FS_NTFS, NTFS_Install, NULL); tVFS_Driver gNTFS_FSInfo = { .Name = "ntfs", + .Detect = NTFS_Detect, .InitDevice = NTFS_InitDevice, .Unmount = NTFS_Unmount, .GetNodeFromINode = NULL }; tVFS_NodeType gNTFS_DirType = { - .TypeName = "NTFS-File", + .TypeName = "NTFS-Dir", .ReadDir = NTFS_ReadDir, .FindDir = NTFS_FindDir, .Close = NULL @@ -44,6 +55,27 @@ int NTFS_Install(char **Arguments) return 0; } +/** + * \brief Detect if a volume is NTFS + */ +int NTFS_Detect(int FD) +{ + tNTFS_BootSector bs; + VFS_ReadAt(FD, 0, 512, &bs); + + if( bs.BytesPerSector == 0 || (bs.BytesPerSector & 511) ) + return 0; + + Uint64 ncluster = bs.TotalSectorCount / bs.SectorsPerCluster; + if( bs.MFTStart >= ncluster || bs.MFTMirrorStart >= ncluster ) + return 0; + + if( memcmp(bs.SystemID, "NTFS ", 8) != 0 ) + return 0; + + return 1; +} + /** * \brief Mount a NTFS volume */ @@ -52,7 +84,7 @@ tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options) tNTFS_Disk *disk; tNTFS_BootSector bs; - disk = malloc( sizeof(tNTFS_Disk) ); + disk = calloc( sizeof(tNTFS_Disk), 1 ); disk->FD = VFS_Open(Device, VFS_OPENFLAG_READ); if(!disk->FD) { @@ -96,22 +128,34 @@ tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options) else { disk->MFTRecSize = bs.ClustersPerMFTRecord * disk->ClusterSize; } + NTFS_DumpEntry(disk, 0); // $MFT + //NTFS_DumpEntry(disk, 3); // $VOLUME - disk->RootNode.Inode = 5; // MFT Ent #5 is filesystem root - disk->RootNode.ImplPtr = disk; - - disk->RootNode.UID = 0; - disk->RootNode.GID = 0; - - disk->RootNode.NumACLs = 1; - disk->RootNode.ACLs = &gVFS_ACL_EveryoneRX; - - disk->RootNode.Type = &gNTFS_DirType; + disk->MFTDataAttr = NULL; + disk->MFTDataAttr = NTFS_GetAttrib(disk, 0, NTFS_FileAttrib_Data, "", 0); + NTFS_DumpEntry(disk, 5); // . + disk->RootDir.I30Root = NTFS_GetAttrib(disk, 5, NTFS_FileAttrib_IndexRoot, "$I30", 0); + disk->RootDir.I30Allocation = NTFS_GetAttrib(disk, 5, NTFS_FileAttrib_IndexAllocation, "$I30", 0); + disk->RootDir.Node.Inode = 5; // MFT Ent #5 is filesystem root + disk->RootDir.Node.ImplPtr = disk; + disk->RootDir.Node.Type = &gNTFS_DirType; + disk->RootDir.Node.Flags = VFS_FFLAG_DIRECTORY; - NTFS_DumpEntry(disk, 5); + disk->RootDir.Node.UID = 0; + disk->RootDir.Node.GID = 0; - return &disk->RootNode; + disk->RootDir.Node.NumACLs = 1; + disk->RootDir.Node.ACLs = &gVFS_ACL_EveryoneRX; + + { + // Read from allocation + char buf[disk->ClusterSize]; + size_t len = NTFS_ReadAttribData(disk->RootDir.I30Allocation, 0, sizeof(buf), buf); + Debug_HexDump("RootDir allocation", buf, len); + } + + return &disk->RootDir.Node; } /** @@ -119,7 +163,258 @@ tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options) */ void NTFS_Unmount(tVFS_Node *Node) { + tNTFS_Disk *Disk = Node->ImplPtr; + VFS_Close(Disk->FD); + free(Disk); +} + +tNTFS_FILE_Header *NTFS_GetMFT(tNTFS_Disk *Disk, Uint32 MFTEntry) +{ + void *ret = malloc( Disk->MFTRecSize ); + if(!ret) { + Log_Warning("FS_NTFS", "malloc() fail!"); + return NULL; + } + + // NOTE: The MFT is a file, and can get fragmented + if( !Disk->MFTDataAttr ) { + VFS_ReadAt( Disk->FD, + Disk->MFTBase * Disk->ClusterSize + MFTEntry * Disk->MFTRecSize, + Disk->MFTRecSize, + ret); + } + else { + NTFS_ReadAttribData(Disk->MFTDataAttr, MFTEntry * Disk->MFTRecSize, Disk->MFTRecSize, ret); + } + + return ret; +} + +void NTFS_ReleaseMFT(tNTFS_Disk *Disk, Uint32 MFTEntry, tNTFS_FILE_Header *Entry) +{ + free(Entry); +} + +static inline Uint64 _getVariableLengthInt(const void *Ptr, int Length, int bExtend) +{ + const Uint8 *data = Ptr; + Uint64 bits = 0; + for( int i = 0; i < Length; i ++ ) + bits |= (Uint64)data[i] << (i*8); + if( bExtend && Length && data[Length-1] & 0x80 ) { + for( int i = Length; i < 8; i ++ ) + bits |= 0xFF << (i*8); + } + return bits; // +} + +const void *_GetDataRun(const void *ptr, const void *limit, Uint64 LastLCN, Uint64 *Count, Uint64 *LCN) +{ + // Clean exit? + if( ptr == limit ) { + LOG("Clean end of list"); + return NULL; + } + + const Uint8 *data = ptr; + + // Offset size + Uint8 ofsSize = data[0] >> 4; + Uint8 lenSize = data[0] & 0xF; + LOG("ofsSize = %i, lenSize = %i", ofsSize, lenSize); + if( ofsSize > 8 ) + return NULL; + if( lenSize > 8 || lenSize < 1 ) + return NULL; + if( data + 1 + ofsSize + lenSize > (const Uint8*)limit ) + return NULL; + + if( Count ) { + *Count = _getVariableLengthInt(data + 1, lenSize, 0); + } + if( LCN ) { + *LCN = LastLCN + (Sint64)_getVariableLengthInt(data + 1 + lenSize, ofsSize, 1); + } + + return data + 1 + ofsSize + lenSize; +} + +tNTFS_Attrib *NTFS_GetAttrib(tNTFS_Disk *Disk, Uint32 MFTEntry, int Type, const char *Name, int DesIdx) +{ + ENTER("pDisk xMFTEntry xType sName iDesIdx", + Disk, MFTEntry, Type, Name, DesIdx); + int curIdx = 0; + // TODO: Scan cache of attributes + // Load MFT entry + tNTFS_FILE_Header *hdr = NTFS_GetMFT(Disk, MFTEntry); + LOG("hdr = %p", hdr); + + tNTFS_FILE_Attrib *attr; + for( size_t ofs = hdr->FirstAttribOfs; ofs < hdr->RecordSize; ofs += attr->Size ) + { + attr = (void*)( (tVAddr)hdr + ofs ); + // Sanity #1: Type + if( ofs + 4 > hdr->RecordSize ) + break ; + // End-of-list? + if( attr->Type == 0xFFFFFFFF ) + break; + // Sanity #2: Type,Size + if( ofs + 8 > hdr->RecordSize ) + break; + // Sanity #3: Reported size + if( attr->Size < sizeof(attr->Resident) ) + break; + // Sanity #4: Reported size fits + if( ofs + attr->Size > hdr->RecordSize ) + break; + + // - Chceck if this attribute is the one requested + LOG("Type check %x == %x", attr->Type, Type); + if( attr->Type != Type ) + continue; + if( Name ) { + LOG("Name check = '%s'", Name); + const void *name16 = (char*)attr + attr->NameOffset; + if( UTF16_CompareWithUTF8(attr->NameLength, name16, Name) != 0 ) + continue ; + } + LOG("Idx check %i", curIdx); + if( curIdx++ != DesIdx ) + continue ; + + // - Construct (and cache) attribute description + ASSERT(attr->NameOffset % 1 == 0); + Uint16 *name16 = (Uint16*)attr + attr->NameOffset/2; + size_t namelen = UTF16_ConvertToUTF8(0, NULL, attr->NameLength, name16); + size_t edatalen = (attr->NonresidentFlag ? 0 : attr->Resident.AttribLen*4); + tNTFS_Attrib *ret = malloc( sizeof(tNTFS_Attrib) + namelen + 1 + edatalen ); + if(!ret) { + LEAVE('n'); + return NULL; + } + if( attr->NonresidentFlag ) + ret->Name = (void*)(ret + 1); + else { + ret->ResidentData = ret + 1; + ret->Name = (char*)ret->ResidentData + edatalen; + } + + ret->Disk = Disk; + ret->Type = attr->Type; + UTF16_ConvertToUTF8(namelen+1, ret->Name, attr->NameLength, name16); + ret->IsResident = !(attr->NonresidentFlag); + + LOG("Creating with %x '%s'", ret->Type, ret->Name); + + if( attr->NonresidentFlag ) + { + ret->DataSize = attr->NonResident.RealSize; + ret->NonResident.CompressionUnitL2Size = attr->NonResident.CompressionUnitSize; + ret->NonResident.FirstPopulatedCluster = attr->NonResident.StartingVCN; + // Count data runs + const char *limit = (char*)attr + attr->Size; + int nruns = 0; + const char *datarun = (char*)attr + attr->NonResident.DataRunOfs; + while( (datarun = _GetDataRun(datarun, limit, 0, NULL, NULL)) ) + nruns ++; + LOG("nruns = %i", nruns); + // Allocate data runs + ret->NonResident.nRuns = nruns; + ret->NonResident.Runs = malloc( sizeof(tNTFS_AttribDataRun) * nruns ); + int i = 0; + datarun = (char*)attr + attr->NonResident.DataRunOfs; + Uint64 lastLCN = 0; + while( datarun && i < nruns ) + { + tNTFS_AttribDataRun *run = &ret->NonResident.Runs[i]; + datarun = _GetDataRun(datarun,limit, lastLCN, &run->Count, &run->LCN); + LOG("Run %i: %llx+%llx", i, run->LCN, run->Count); + lastLCN = run->LCN; + i ++; + } + } + else + { + memcpy(ret->ResidentData, (char*)attr + attr->Resident.AttribOfs, edatalen); + } + + LEAVE('p', ret); + return ret; + } + + NTFS_ReleaseMFT(Disk, MFTEntry, hdr); + LEAVE('n'); + return NULL; +} + +size_t NTFS_ReadAttribData(tNTFS_Attrib *Attrib, Uint64 Offset, size_t Length, void *Buffer) +{ + if( Offset >= Attrib->DataSize ) + return 0; + if( Length > Attrib->DataSize ) + Length = Attrib->DataSize; + if( Offset + Length > Attrib->DataSize ) + Length = Attrib->DataSize - Offset; + + if( Attrib->IsResident ) + { + memcpy(Buffer, Attrib->ResidentData, Length); + return Length; + } + else + { + size_t ret = 0; + tNTFS_Disk *Disk = Attrib->Disk; + Uint64 first_cluster = Offset / Disk->ClusterSize; + size_t cluster_ofs = Offset % Disk->ClusterSize; + if( first_cluster < Attrib->NonResident.FirstPopulatedCluster ) { + Log_Warning("NTFS", "TODO: Ofs < FirstVCN"); + } + first_cluster -= Attrib->NonResident.FirstPopulatedCluster; + if( Attrib->NonResident.CompressionUnitL2Size ) + { + // TODO: Compression + Log_Warning("NTFS", "Compression unsupported"); + // NOTE: Compressed blocks show up in pairs of runs + // - The first contains the compressed data + // - The second is a placeholder 'sparse' (LCN=0) to align to the compression unit + } + else + { + // Iterate through data runs until the desired run is located + for( int i = 0; i < Attrib->NonResident.nRuns && Length; i ++ ) + { + tNTFS_AttribDataRun *run = &Attrib->NonResident.Runs[i]; + if( first_cluster > run->Count ) { + first_cluster -= run->Count; + continue ; + } + size_t avail_bytes = (run->Count-first_cluster)*Disk->ClusterSize - cluster_ofs; + if( avail_bytes > Length ) + avail_bytes = Length; + // Read from this extent + if( run->LCN == 0 ) { + memset(Buffer, 0, avail_bytes); + } + else { + VFS_ReadAt(Disk->FD, + (run->LCN + first_cluster)*Disk->ClusterSize + cluster_ofs, + avail_bytes, + Buffer + ); + } + Length -= avail_bytes; + Buffer += avail_bytes; + ret += avail_bytes; + first_cluster = 0; + cluster_ofs = 0; + continue ; + } + } + return ret; + } } /** @@ -127,12 +422,11 @@ void NTFS_Unmount(tVFS_Node *Node) */ void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry) { - void *buf = malloc( Disk->MFTRecSize ); - tNTFS_FILE_Header *hdr = buf; tNTFS_FILE_Attrib *attr; int i; - if(!buf) { + tNTFS_FILE_Header *hdr = malloc( Disk->MFTRecSize ); + if(!hdr) { Log_Warning("FS_NTFS", "malloc() fail!"); return ; } @@ -140,7 +434,7 @@ void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry) VFS_ReadAt( Disk->FD, Disk->MFTBase * Disk->ClusterSize + Entry * Disk->MFTRecSize, Disk->MFTRecSize, - buf); + hdr); Log_Debug("FS_NTFS", "MFT Entry #%i", Entry); Log_Debug("FS_NTFS", "- Magic = 0x%08x (%4C)", hdr->Magic, &hdr->Magic); @@ -164,16 +458,22 @@ void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry) Log_Debug("FS_NTFS", "- Attribute %i", i ++); Log_Debug("FS_NTFS", " > Type = 0x%x", attr->Type); Log_Debug("FS_NTFS", " > Size = 0x%x", attr->Size); - Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->ResidentFlag); + Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->NonresidentFlag); Log_Debug("FS_NTFS", " > NameLength = %i", attr->NameLength); Log_Debug("FS_NTFS", " > NameOffset = 0x%x", attr->NameOffset); Log_Debug("FS_NTFS", " > Flags = 0x%x", attr->Flags); Log_Debug("FS_NTFS", " > AttributeID = 0x%x", attr->AttributeID); - if( !attr->ResidentFlag ) { + { + Uint16 *name16 = (void*)((char*)attr + attr->NameOffset); + size_t len = UTF16_ConvertToUTF8(0, NULL, attr->NameLength, name16); + char name[len+1]; + UTF16_ConvertToUTF8(len+1, name, attr->NameLength, name16); + Log_Debug("FS_NTFS", " > Name = '%s'", name); + } + if( !attr->NonresidentFlag ) { Log_Debug("FS_NTFS", " > AttribLen = 0x%x", attr->Resident.AttribLen); Log_Debug("FS_NTFS", " > AttribOfs = 0x%x", attr->Resident.AttribOfs); Log_Debug("FS_NTFS", " > IndexedFlag = 0x%x", attr->Resident.IndexedFlag); - Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->Resident.Name); Debug_HexDump("FS_NTFS", (void*)( (tVAddr)attr + attr->Resident.AttribOfs ), attr->Resident.AttribLen @@ -187,11 +487,14 @@ void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry) Log_Debug("FS_NTFS", " > AllocatedSize = 0x%llx", attr->NonResident.AllocatedSize); Log_Debug("FS_NTFS", " > RealSize = 0x%llx", attr->NonResident.RealSize); Log_Debug("FS_NTFS", " > InitiatedSize = 0x%llx", attr->NonResident.InitiatedSize); - Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->NonResident.Name); + Debug_HexDump("FS_NTFS", + (char*)attr + attr->NonResident.DataRunOfs, + attr->Size - attr->NonResident.DataRunOfs + ); } attr = (void*)( (tVAddr)attr + attr->Size ); } - free(buf); + free(hdr); } diff --git a/KernelLand/Modules/Filesystems/NTFS/ntfs.h b/KernelLand/Modules/Filesystems/NTFS/ntfs.h new file mode 100644 index 00000000..797d2db8 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/ntfs.h @@ -0,0 +1,178 @@ +/* + * Acess2 NTFS Driver + * - By John Hodge (thePowersGang) + * + * ntfs.h + * - NTFS on-disk structures + */ +#ifndef _NTFS__NTFS_H_ +#define _NTFS__NTFS_H_ + +typedef struct sNTFS_BootSector tNTFS_BootSector; + +struct sNTFS_BootSector +{ + // 0 + Uint8 Jump[3]; + Uint8 SystemID[8]; // = "NTFS " + Uint16 BytesPerSector; + Uint8 SectorsPerCluster; + + // 0xE + Uint8 _unused[7]; + Uint8 MediaDescriptor; + Uint16 _unused2; + Uint16 SectorsPerTrack; + Uint16 Heads; + + // 0x1C + Uint64 _unused3; + Uint32 _unknown; // Usually 0x00800080 (according to Linux docs) + + // 0x28 + Uint64 TotalSectorCount; // Size of volume in sectors + Uint64 MFTStart; // Logical Cluster Number of Cluster 0 of MFT + Uint64 MFTMirrorStart; // Logical Cluster Number of Cluster 0 of MFT Backup + + // 0x40 + // If either of these are -ve, the size can be obtained via + // SizeInBytes = 2^(-1 * Value) + Sint8 ClustersPerMFTRecord; + Uint8 _unused4[3]; + Sint8 ClustersPerIndexRecord; + Uint8 _unused5[3]; + + Uint64 SerialNumber; + + Uint8 Padding[512-0x50]; + +} PACKED; + +/** + * FILE header, an entry in the MFT + */ +typedef struct sNTFS_FILE_Header +{ + Uint32 Magic; // 'FILE' + Uint16 UpdateSequenceOfs; + Uint16 UpdateSequenceSize; // Size in words of the UpdateSequenceArray + + Uint64 LSN; // $LogFile Sequence Number + + Uint16 SequenceNumber; + Uint16 HardLinkCount; + Uint16 FirstAttribOfs; // Size of header? + Uint16 Flags; // 0: In Use, 1: Directory + + Uint32 RecordSize; // Real Size of FILE Record + Uint32 RecordSpace; // Allocated Size for FILE Record + + /** + * Base address of the MFT containing this record + */ + Uint64 Reference; // "File reference to the base FILE record" ??? + + Uint16 NextAttribID; + union + { + // Only in XP + struct { + Uint16 AlignTo4Byte; + Uint16 RecordNumber; // Number of this MFT Record + Uint16 UpdateSequenceNumber; + Uint16 UpdateSequenceArray[]; + } XP; + struct { + Uint16 UpdateSequenceNumber; + Uint16 UpdateSequenceArray[]; + } All; + } OSDep; + +} PACKED tNTFS_FILE_Header; + +/** + * File Attribute, follows the FILE header + */ +typedef struct sNTFS_FILE_Attrib +{ + Uint32 Type; // See eNTFS_FILE_Attribs + Uint32 Size; // Includes header + + Uint8 NonresidentFlag; + Uint8 NameLength; + Uint16 NameOffset; + Uint16 Flags; // 0: Compressed, 14: Encrypted, 15: Sparse + Uint16 AttributeID; + + union + { + struct { + Uint32 AttribLen; // In words + Uint16 AttribOfs; + Uint8 IndexedFlag; + Uint8 Padding; + + Uint16 Name[]; // UTF-16 + // Attribute Data + } Resident; + struct { + Uint64 StartingVCN; // VCN of first data run + Uint64 LastVCN; // Last VCN in data runs + Uint16 DataRunOfs; + Uint16 CompressionUnitSize; + Uint32 Padding; + Uint64 AllocatedSize; // Allocated clusters in bytes + Uint64 RealSize; + Uint64 InitiatedSize; // One assumes, ammount of actual data stored + Uint16 Name[]; // UTF-16 + // Data Runs + } NonResident; + }; +} PACKED tNTFS_FILE_Attrib; + +#include "attributes.h" + +struct sNTFS_IndexHeader +{ + Uint32 Magic; // = 'INDX' LE + Uint16 UpdateSequenceOfs; + Uint16 UpdateSequenceSize; // incl number + Uint64 LogFileSequenceNum; + Uint64 ThisVCN; + Uint32 EntriesOffset; // add 0x18 + Uint32 EntriesSize; // (maybe) add 0x18 + Uint32 EntriesAllocSize; + Uint8 Flags; // [0]: Not leaf node + Uint8 _pad[3]; + Uint16 UpdateSequence; + Uint16 UpdateSequenceArray[]; +} PACKED; + +struct sNTFS_IndexEntry_Filename +{ + Uint64 MFTReference; + Uint16 EntrySize; + Uint16 FilenameOfs; + Uint16 IndexFlags; + Uint16 _flags; + + #if 1 + struct sNTFS_Attrib_Filename Filename; + #else + Uint64 ParentMFTReference; + Uint64 CreationTime; + Uint64 ModifcationTime; + Uint64 MFTModTime; + Uint64 AccessTime; + Uint64 AllocSize; + Uint64 RealSize; + Uint64 FileFlags; + + Uint8 FilenameLen; + Uint8 FilenameNamespace; + #endif + // Filename +}; + +#endif +