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

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