Modules/NTFS - Implimentation in progress
authorJohn Hodge <[email protected]>
Sat, 22 Jun 2013 16:06:53 +0000 (00:06 +0800)
committerJohn Hodge <[email protected]>
Sat, 22 Jun 2013 16:06:53 +0000 (00:06 +0800)
KernelLand/Modules/Filesystems/NTFS/attributes.h
KernelLand/Modules/Filesystems/NTFS/common.h
KernelLand/Modules/Filesystems/NTFS/dir.c
KernelLand/Modules/Filesystems/NTFS/main.c
KernelLand/Modules/Filesystems/NTFS/ntfs.h [new file with mode: 0644]

index 671a36e..b72588e 100644 (file)
@@ -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
index d6b11e8..ac93b52 100644 (file)
  *
  * common.h - Common Types and Definitions
  */
-#ifndef _COMMON_H_
-#define _COMMON_H_
+#ifndef _NTFS__COMMON_H_
+#define _NTFS__COMMON_H_
 
 #include <acess.h>
 #include <vfs.h>
+#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);
index 538445b..5066783 100644 (file)
@@ -6,8 +6,10 @@
  *
  * dir.c - Directory Handling
  */
+#define DEBUG  1
 #include "common.h"
 #include "index.h"
+#include <utf16.h>
 
 // === 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;
 }
 
 /**
index b9c4b8d..dd1bf84 100644 (file)
@@ -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
 #include <vfs.h>
 #include "common.h"
 #include <modules.h>
+#include <utf16.h>
 
 // === 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 (file)
index 0000000..797d2db
--- /dev/null
@@ -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
+

UCC git Repository :: git.ucc.asn.au