Modules/NTFS - Implimentation in progress
[tpg/acess2.git] / KernelLand / Modules / Filesystems / NTFS / main.c
1 /*
2  * Acess2 - NTFS Driver
3  * By John Hodge (thePowersGang)
4  *
5  * main.c
6  * - Driver core
7  *
8  * Reference: ntfsdoc.pdf
9  */
10 #define DEBUG   1
11 #define VERBOSE 0
12 #include <acess.h>
13 #include <vfs.h>
14 #include "common.h"
15 #include <modules.h>
16 #include <utf16.h>
17
18 // === PROTOTYPES ===
19  int    NTFS_Install(char **Arguments);
20  int    NTFS_Detect(int FD);
21 tVFS_Node       *NTFS_InitDevice(const char *Devices, const char **Options);
22 void    NTFS_Unmount(tVFS_Node *Node);
23 // - MFT Related Functions
24 tNTFS_FILE_Header       *NTFS_GetMFT(tNTFS_Disk *Disk, Uint32 MFTEntry);
25 void    NTFS_ReleaseMFT(tNTFS_Disk *Disk, Uint32 MFTEntry, tNTFS_FILE_Header *Entry);
26 tNTFS_Attrib    *NTFS_GetAttrib(tNTFS_Disk *Disk, Uint32 MFTEntry, int Type, const char *Name, int DesIdx);
27 size_t  NTFS_ReadAttribData(tNTFS_Attrib *Attrib, Uint64 Offset, size_t Length, void *Buffer);
28 void    NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry);
29
30 // === GLOBALS ===
31 MODULE_DEFINE(0, 0x0A /*v0.1*/, FS_NTFS, NTFS_Install, NULL);
32 tVFS_Driver     gNTFS_FSInfo = {
33         .Name = "ntfs",
34         .Detect = NTFS_Detect,
35         .InitDevice = NTFS_InitDevice,
36         .Unmount = NTFS_Unmount,
37         .GetNodeFromINode = NULL
38 };
39 tVFS_NodeType   gNTFS_DirType = {
40         .TypeName = "NTFS-Dir",
41         .ReadDir = NTFS_ReadDir,
42         .FindDir = NTFS_FindDir,
43         .Close = NULL
44         };
45
46 tNTFS_Disk      gNTFS_Disks;
47
48 // === CODE ===
49 /**
50  * \brief Installs the NTFS driver
51  */
52 int NTFS_Install(char **Arguments)
53 {
54         VFS_AddDriver( &gNTFS_FSInfo );
55         return 0;
56 }
57
58 /**
59  * \brief Detect if a volume is NTFS
60  */
61 int NTFS_Detect(int FD)
62 {
63         tNTFS_BootSector        bs;
64         VFS_ReadAt(FD, 0, 512, &bs);
65         
66         if( bs.BytesPerSector == 0 || (bs.BytesPerSector & 511) )
67                 return 0;
68
69         Uint64  ncluster = bs.TotalSectorCount / bs.SectorsPerCluster;
70         if( bs.MFTStart >= ncluster || bs.MFTMirrorStart >= ncluster )
71                 return 0;
72
73         if( memcmp(bs.SystemID, "NTFS    ", 8) != 0 )
74                 return 0;
75         
76         return 1;
77 }
78
79 /**
80  * \brief Mount a NTFS volume
81  */
82 tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options)
83 {
84         tNTFS_Disk      *disk;
85         tNTFS_BootSector        bs;
86         
87         disk = calloc( sizeof(tNTFS_Disk), 1 );
88         
89         disk->FD = VFS_Open(Device, VFS_OPENFLAG_READ);
90         if(!disk->FD) {
91                 free(disk);
92                 return NULL;
93         }
94         
95         Log_Debug("FS_NTFS", "&bs = %p", &bs);
96         VFS_ReadAt(disk->FD, 0, 512, &bs);
97         
98         Log_Debug("FS_NTFS", "Jump = %02x%02x%02x",
99                 bs.Jump[0],
100                 bs.Jump[1],
101                 bs.Jump[2]);
102         Log_Debug("FS_NTFS", "SystemID = %02x%02x%02x%02x%02x%02x%02x%02x (%8C)",
103                 bs.SystemID[0], bs.SystemID[1], bs.SystemID[2], bs.SystemID[3],
104                 bs.SystemID[4], bs.SystemID[5], bs.SystemID[6], bs.SystemID[7],
105                 bs.SystemID
106                 );
107         Log_Debug("FS_NTFS", "BytesPerSector = %i", bs.BytesPerSector);
108         Log_Debug("FS_NTFS", "SectorsPerCluster = %i", bs.SectorsPerCluster);
109         Log_Debug("FS_NTFS", "MediaDescriptor = 0x%x", bs.MediaDescriptor);
110         Log_Debug("FS_NTFS", "SectorsPerTrack = %i", bs.SectorsPerTrack);
111         Log_Debug("FS_NTFS", "Heads = %i", bs.Heads);
112         Log_Debug("FS_NTFS", "TotalSectorCount = 0x%llx", bs.TotalSectorCount);
113         Log_Debug("FS_NTFS", "MFTStart = 0x%llx", bs.MFTStart);
114         Log_Debug("FS_NTFS", "MFTMirrorStart = 0x%llx", bs.MFTMirrorStart);
115         Log_Debug("FS_NTFS", "ClustersPerMFTRecord = %i", bs.ClustersPerMFTRecord);
116         Log_Debug("FS_NTFS", "ClustersPerIndexRecord = %i", bs.ClustersPerIndexRecord);
117         Log_Debug("FS_NTFS", "SerialNumber = 0x%llx", bs.SerialNumber);
118         
119         disk->ClusterSize = bs.BytesPerSector * bs.SectorsPerCluster;
120         Log_Debug("NTFS", "Cluster Size = %i KiB", disk->ClusterSize/1024);
121         disk->MFTBase = bs.MFTStart;
122         Log_Debug("NTFS", "MFT Base = %i", disk->MFTBase);
123         Log_Debug("NTFS", "TotalSectorCount = 0x%x", bs.TotalSectorCount);
124         
125         if( bs.ClustersPerMFTRecord < 0 ) {
126                 disk->MFTRecSize = 1 << (-bs.ClustersPerMFTRecord);
127         }
128         else {
129                 disk->MFTRecSize = bs.ClustersPerMFTRecord * disk->ClusterSize;
130         }
131         NTFS_DumpEntry(disk, 0);        // $MFT
132         //NTFS_DumpEntry(disk, 3);      // $VOLUME
133         
134         disk->MFTDataAttr = NULL;
135         disk->MFTDataAttr = NTFS_GetAttrib(disk, 0, NTFS_FileAttrib_Data, "", 0);
136         NTFS_DumpEntry(disk, 5);        // .
137
138         disk->RootDir.I30Root = NTFS_GetAttrib(disk, 5, NTFS_FileAttrib_IndexRoot, "$I30", 0);
139         disk->RootDir.I30Allocation = NTFS_GetAttrib(disk, 5, NTFS_FileAttrib_IndexAllocation, "$I30", 0);
140         disk->RootDir.Node.Inode = 5;   // MFT Ent #5 is filesystem root
141         disk->RootDir.Node.ImplPtr = disk;
142         disk->RootDir.Node.Type = &gNTFS_DirType;
143         disk->RootDir.Node.Flags = VFS_FFLAG_DIRECTORY;
144         
145         disk->RootDir.Node.UID = 0;
146         disk->RootDir.Node.GID = 0;
147         
148         disk->RootDir.Node.NumACLs = 1;
149         disk->RootDir.Node.ACLs = &gVFS_ACL_EveryoneRX;
150
151         {
152                 // Read from allocation
153                 char buf[disk->ClusterSize];
154                 size_t len = NTFS_ReadAttribData(disk->RootDir.I30Allocation, 0, sizeof(buf), buf);
155                 Debug_HexDump("RootDir allocation", buf, len);
156         }
157
158         return &disk->RootDir.Node;
159 }
160
161 /**
162  * \brief Unmount an NTFS Disk
163  */
164 void NTFS_Unmount(tVFS_Node *Node)
165 {
166         tNTFS_Disk      *Disk = Node->ImplPtr;
167         VFS_Close(Disk->FD);
168         free(Disk);
169 }
170
171 tNTFS_FILE_Header *NTFS_GetMFT(tNTFS_Disk *Disk, Uint32 MFTEntry)
172 {
173         void    *ret = malloc( Disk->MFTRecSize );
174         if(!ret) {
175                 Log_Warning("FS_NTFS", "malloc() fail!");
176                 return NULL;
177         }
178         
179         // NOTE: The MFT is a file, and can get fragmented
180         if( !Disk->MFTDataAttr ) {
181                 VFS_ReadAt( Disk->FD,
182                         Disk->MFTBase * Disk->ClusterSize + MFTEntry * Disk->MFTRecSize,
183                         Disk->MFTRecSize,
184                         ret);
185         }
186         else {
187                 NTFS_ReadAttribData(Disk->MFTDataAttr, MFTEntry * Disk->MFTRecSize, Disk->MFTRecSize, ret);
188         }
189         
190         return ret;
191 }
192
193 void NTFS_ReleaseMFT(tNTFS_Disk *Disk, Uint32 MFTEntry, tNTFS_FILE_Header *Entry)
194 {
195         free(Entry);
196 }
197
198 static inline Uint64 _getVariableLengthInt(const void *Ptr, int Length, int bExtend)
199 {
200         const Uint8     *data = Ptr;
201         Uint64  bits = 0;
202         for( int i = 0; i < Length; i ++ )
203                 bits |= (Uint64)data[i] << (i*8);
204         if( bExtend && Length && data[Length-1] & 0x80 ) {
205                 for( int i = Length; i < 8; i ++ )
206                         bits |= 0xFF << (i*8);
207         }
208         return bits;    // 
209 }
210
211 const void *_GetDataRun(const void *ptr, const void *limit, Uint64 LastLCN, Uint64 *Count, Uint64 *LCN)
212 {
213         // Clean exit?
214         if( ptr == limit ) {
215                 LOG("Clean end of list");
216                 return NULL;
217         }
218         
219         const Uint8     *data = ptr;
220         
221         // Offset size
222         Uint8   ofsSize = data[0] >> 4;
223         Uint8   lenSize = data[0] & 0xF;
224         LOG("ofsSize = %i, lenSize = %i", ofsSize, lenSize);
225         if( ofsSize > 8 )
226                 return NULL;
227         if( lenSize > 8 || lenSize < 1 )
228                 return NULL;
229         if( data + 1 + ofsSize + lenSize > (const Uint8*)limit )
230                 return NULL;
231         
232         if( Count ) {
233                 *Count = _getVariableLengthInt(data + 1, lenSize, 0);
234         }
235         if( LCN ) {
236                 *LCN = LastLCN + (Sint64)_getVariableLengthInt(data + 1 + lenSize, ofsSize, 1);
237         }
238         
239         return data + 1 + ofsSize + lenSize;
240 }
241
242 tNTFS_Attrib *NTFS_GetAttrib(tNTFS_Disk *Disk, Uint32 MFTEntry, int Type, const char *Name, int DesIdx)
243 {
244         ENTER("pDisk xMFTEntry xType sName iDesIdx",
245                 Disk, MFTEntry, Type, Name, DesIdx);
246          int    curIdx = 0;
247         // TODO: Scan cache of attributes
248         
249         // Load MFT entry
250         tNTFS_FILE_Header *hdr = NTFS_GetMFT(Disk, MFTEntry);
251         LOG("hdr = %p", hdr);
252
253         tNTFS_FILE_Attrib       *attr;
254         for( size_t ofs = hdr->FirstAttribOfs; ofs < hdr->RecordSize; ofs += attr->Size )
255         {
256                 attr = (void*)( (tVAddr)hdr + ofs );
257                 // Sanity #1: Type
258                 if( ofs + 4 > hdr->RecordSize )
259                         break ;
260                 // End-of-list?
261                 if( attr->Type == 0xFFFFFFFF )
262                         break;
263                 // Sanity #2: Type,Size
264                 if( ofs + 8 > hdr->RecordSize )
265                         break;
266                 // Sanity #3: Reported size
267                 if( attr->Size < sizeof(attr->Resident) )
268                         break;
269                 // Sanity #4: Reported size fits
270                 if( ofs + attr->Size > hdr->RecordSize )
271                         break;
272                 
273                 // - Chceck if this attribute is the one requested
274                 LOG("Type check %x == %x", attr->Type, Type);
275                 if( attr->Type != Type )
276                         continue;
277                 if( Name ) {
278                         LOG("Name check = '%s'", Name);
279                         const void      *name16 = (char*)attr + attr->NameOffset;
280                         if( UTF16_CompareWithUTF8(attr->NameLength, name16, Name) != 0 )
281                                 continue ;
282                 }
283                 LOG("Idx check %i", curIdx);
284                 if( curIdx++ != DesIdx )
285                         continue ;
286
287                 // - Construct (and cache) attribute description
288                 ASSERT(attr->NameOffset % 1 == 0);
289                 Uint16  *name16 = (Uint16*)attr + attr->NameOffset/2;
290                 size_t  namelen = UTF16_ConvertToUTF8(0, NULL, attr->NameLength, name16);
291                 size_t  edatalen = (attr->NonresidentFlag ? 0 : attr->Resident.AttribLen*4);
292                 tNTFS_Attrib *ret = malloc( sizeof(tNTFS_Attrib) + namelen + 1 + edatalen );
293                 if(!ret) {
294                         LEAVE('n');
295                         return NULL;
296                 }
297                 if( attr->NonresidentFlag )
298                         ret->Name = (void*)(ret + 1);
299                 else {
300                         ret->ResidentData = ret + 1;
301                         ret->Name = (char*)ret->ResidentData + edatalen;
302                 }
303                 
304                 ret->Disk = Disk;
305                 ret->Type = attr->Type;
306                 UTF16_ConvertToUTF8(namelen+1, ret->Name, attr->NameLength, name16);
307                 ret->IsResident = !(attr->NonresidentFlag);
308
309                 LOG("Creating with %x '%s'", ret->Type, ret->Name);
310
311                 if( attr->NonresidentFlag )
312                 {
313                         ret->DataSize = attr->NonResident.RealSize;
314                         ret->NonResident.CompressionUnitL2Size = attr->NonResident.CompressionUnitSize;
315                         ret->NonResident.FirstPopulatedCluster = attr->NonResident.StartingVCN;
316                         // Count data runs
317                         const char *limit = (char*)attr + attr->Size;
318                          int    nruns = 0;
319                         const char *datarun = (char*)attr + attr->NonResident.DataRunOfs;
320                         while( (datarun = _GetDataRun(datarun, limit, 0, NULL, NULL)) )
321                                 nruns ++;
322                         LOG("nruns = %i", nruns);
323                         // Allocate data runs
324                         ret->NonResident.nRuns = nruns;
325                         ret->NonResident.Runs = malloc( sizeof(tNTFS_AttribDataRun) * nruns );
326                          int    i = 0;
327                         datarun = (char*)attr + attr->NonResident.DataRunOfs;
328                         Uint64  lastLCN = 0;
329                         while( datarun && i < nruns )
330                         {
331                                 tNTFS_AttribDataRun     *run = &ret->NonResident.Runs[i];
332                                 datarun = _GetDataRun(datarun,limit, lastLCN, &run->Count, &run->LCN);
333                                 LOG("Run %i: %llx+%llx", i, run->LCN, run->Count);
334                                 lastLCN = run->LCN;
335                                 i ++;
336                         }
337                 }
338                 else
339                 {
340                         memcpy(ret->ResidentData, (char*)attr + attr->Resident.AttribOfs, edatalen);
341                 }
342                 
343                 LEAVE('p', ret);
344                 return ret;
345         }
346
347         NTFS_ReleaseMFT(Disk, MFTEntry, hdr);
348         LEAVE('n');
349         return NULL;
350 }
351
352 size_t NTFS_ReadAttribData(tNTFS_Attrib *Attrib, Uint64 Offset, size_t Length, void *Buffer)
353 {
354         if( Offset >= Attrib->DataSize )
355                 return 0;
356         if( Length > Attrib->DataSize )
357                 Length = Attrib->DataSize;
358         if( Offset + Length > Attrib->DataSize )
359                 Length = Attrib->DataSize - Offset;
360                 
361         if( Attrib->IsResident )
362         {
363                 memcpy(Buffer, Attrib->ResidentData, Length);
364                 return Length;
365         }
366         else
367         {
368                 size_t  ret = 0;
369                 tNTFS_Disk      *Disk = Attrib->Disk;
370                 Uint64  first_cluster = Offset / Disk->ClusterSize;
371                 size_t  cluster_ofs = Offset % Disk->ClusterSize;
372                 if( first_cluster < Attrib->NonResident.FirstPopulatedCluster ) {
373                         Log_Warning("NTFS", "TODO: Ofs < FirstVCN");
374                 }
375                 first_cluster -= Attrib->NonResident.FirstPopulatedCluster;
376                 if( Attrib->NonResident.CompressionUnitL2Size )
377                 {
378                         // TODO: Compression
379                         Log_Warning("NTFS", "Compression unsupported");
380                         // NOTE: Compressed blocks show up in pairs of runs
381                         // - The first contains the compressed data
382                         // - The second is a placeholder 'sparse' (LCN=0) to align to the compression unit
383                 }
384                 else
385                 {
386                         // Iterate through data runs until the desired run is located
387                         for( int i = 0; i < Attrib->NonResident.nRuns && Length; i ++ )
388                         {
389                                 tNTFS_AttribDataRun     *run = &Attrib->NonResident.Runs[i];
390                                 if( first_cluster > run->Count ) {
391                                         first_cluster -= run->Count;
392                                         continue ;
393                                 }
394                                 size_t  avail_bytes = (run->Count-first_cluster)*Disk->ClusterSize - cluster_ofs;
395                                 if( avail_bytes > Length )
396                                         avail_bytes = Length;
397                                 // Read from this extent
398                                 if( run->LCN == 0 ) {
399                                         memset(Buffer, 0, avail_bytes);
400                                 }
401                                 else {
402                                         VFS_ReadAt(Disk->FD,
403                                                 (run->LCN + first_cluster)*Disk->ClusterSize + cluster_ofs,
404                                                 avail_bytes,
405                                                 Buffer
406                                                 );
407                                 }
408                                 Length -= avail_bytes;
409                                 Buffer += avail_bytes;
410                                 ret += avail_bytes;
411                                 first_cluster = 0;
412                                 cluster_ofs = 0;
413                                 continue ;
414                         }
415                 }
416                 return ret;
417         }
418 }
419
420 /**
421  * \brief Dumps a MFT Entry
422  */
423 void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry)
424 {
425         tNTFS_FILE_Attrib       *attr;
426          int    i;
427         
428         tNTFS_FILE_Header       *hdr = malloc( Disk->MFTRecSize );
429         if(!hdr) {
430                 Log_Warning("FS_NTFS", "malloc() fail!");
431                 return ;
432         }
433         
434         VFS_ReadAt( Disk->FD,
435                 Disk->MFTBase * Disk->ClusterSize + Entry * Disk->MFTRecSize,
436                 Disk->MFTRecSize,
437                 hdr);
438         
439         Log_Debug("FS_NTFS", "MFT Entry #%i", Entry);
440         Log_Debug("FS_NTFS", "- Magic = 0x%08x (%4C)", hdr->Magic, &hdr->Magic);
441         Log_Debug("FS_NTFS", "- UpdateSequenceOfs = 0x%x", hdr->UpdateSequenceOfs);
442         Log_Debug("FS_NTFS", "- UpdateSequenceSize = 0x%x", hdr->UpdateSequenceSize);
443         Log_Debug("FS_NTFS", "- LSN = 0x%x", hdr->LSN);
444         Log_Debug("FS_NTFS", "- SequenceNumber = %i", hdr->SequenceNumber);
445         Log_Debug("FS_NTFS", "- HardLinkCount = %i", hdr->HardLinkCount);
446         Log_Debug("FS_NTFS", "- FirstAttribOfs = 0x%x", hdr->FirstAttribOfs);
447         Log_Debug("FS_NTFS", "- Flags = 0x%x", hdr->Flags);
448         Log_Debug("FS_NTFS", "- RecordSize = 0x%x", hdr->RecordSize);
449         Log_Debug("FS_NTFS", "- RecordSpace = 0x%x", hdr->RecordSpace);
450         Log_Debug("FS_NTFS", "- Reference = 0x%llx", hdr->Reference);
451         Log_Debug("FS_NTFS", "- NextAttribID = 0x%04x", hdr->NextAttribID);
452         
453         attr = (void*)( (char*)hdr + hdr->FirstAttribOfs );
454         i = 0;
455         while( (tVAddr)attr < (tVAddr)hdr + hdr->RecordSize )
456         {
457                 if(attr->Type == 0xFFFFFFFF)    break;
458                 Log_Debug("FS_NTFS", "- Attribute %i", i ++);
459                 Log_Debug("FS_NTFS", " > Type = 0x%x", attr->Type);
460                 Log_Debug("FS_NTFS", " > Size = 0x%x", attr->Size);
461                 Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->NonresidentFlag);
462                 Log_Debug("FS_NTFS", " > NameLength = %i", attr->NameLength);
463                 Log_Debug("FS_NTFS", " > NameOffset = 0x%x", attr->NameOffset);
464                 Log_Debug("FS_NTFS", " > Flags = 0x%x", attr->Flags);
465                 Log_Debug("FS_NTFS", " > AttributeID = 0x%x", attr->AttributeID);
466                 {
467                         Uint16  *name16 = (void*)((char*)attr + attr->NameOffset);
468                         size_t  len = UTF16_ConvertToUTF8(0, NULL, attr->NameLength, name16);
469                         char    name[len+1];
470                         UTF16_ConvertToUTF8(len+1, name, attr->NameLength, name16);
471                         Log_Debug("FS_NTFS", " > Name = '%s'", name);
472                 }
473                 if( !attr->NonresidentFlag ) {
474                         Log_Debug("FS_NTFS", " > AttribLen = 0x%x", attr->Resident.AttribLen);
475                         Log_Debug("FS_NTFS", " > AttribOfs = 0x%x", attr->Resident.AttribOfs);
476                         Log_Debug("FS_NTFS", " > IndexedFlag = 0x%x", attr->Resident.IndexedFlag);
477                         Debug_HexDump("FS_NTFS",
478                                 (void*)( (tVAddr)attr + attr->Resident.AttribOfs ),
479                                 attr->Resident.AttribLen
480                                 );
481                 }
482                 else {
483                         Log_Debug("FS_NTFS", " > StartingVCN = 0x%llx", attr->NonResident.StartingVCN);
484                         Log_Debug("FS_NTFS", " > LastVCN = 0x%llx", attr->NonResident.LastVCN);
485                         Log_Debug("FS_NTFS", " > DataRunOfs = 0x%x", attr->NonResident.DataRunOfs);
486                         Log_Debug("FS_NTFS", " > CompressionUnitSize = 0x%x", attr->NonResident.CompressionUnitSize);
487                         Log_Debug("FS_NTFS", " > AllocatedSize = 0x%llx", attr->NonResident.AllocatedSize);
488                         Log_Debug("FS_NTFS", " > RealSize = 0x%llx", attr->NonResident.RealSize);
489                         Log_Debug("FS_NTFS", " > InitiatedSize = 0x%llx", attr->NonResident.InitiatedSize);
490                         Debug_HexDump("FS_NTFS",
491                                 (char*)attr + attr->NonResident.DataRunOfs,
492                                 attr->Size - attr->NonResident.DataRunOfs
493                                 );
494                 }
495                 
496                 attr = (void*)( (tVAddr)attr + attr->Size );
497         }
498         
499         free(hdr);
500 }

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