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

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