3 * By John Hodge (thePowersGang)
4 * This file is published under the terms of the Acess licence. See the
5 * file COPYING for details.
7 * dir.c - Directory Handling
15 int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]);
16 tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags);
17 Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str);
20 const tNTFS_IndexEntry_Filename *NTFS_int_IterateIndex(const char *Buf, size_t Len, int *Count)
22 const tNTFS_IndexEntry_Filename *ent;
23 for( size_t ofs = 0; ofs < Len; ofs += ent->EntrySize )
25 ent = (const void*)(Buf + ofs);
27 if( ofs + sizeof(*ent) > Len ) {
30 if( ent->EntrySize < sizeof(*ent) ) {
33 // End of block - move on to the next one
34 if( ent->IndexFlags & 0x02 ) {
35 LOG("end of block at ofs %x", ofs);
39 // A little hacky - hide all internal files
40 if( (ent->MFTReference & ((1ULL << 48)-1)) < 12 )
43 if( ent->Filename.FilenameNamespace == NTFS_FilenameNamespace_DOS )
54 * \brief Get the name of an indexed directory entry
56 int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
58 tNTFS_Directory *dir = (void*)Node;
60 ENTER("XNode->Inode iPos", Node->Inode, Pos);
62 ASSERT(dir->I30Root->IsResident);
63 const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData;
65 // Check resident root
66 const tNTFS_IndexEntry_Filename *ent;
67 ent = NTFS_int_IterateIndex(
68 (char*)idxroot + (0x10 + idxroot->FirstEntryOfs),
69 dir->I30Root->DataSize - (0x10 + idxroot->FirstEntryOfs),
71 char buf[idxroot->AllocEntrySize];
74 while( !ent && (len = NTFS_ReadAttribData(dir->I30Allocation, vcn*sizeof(buf), sizeof(buf), buf)) )
77 struct sNTFS_IndexHeader *hdr = (void*)buf;
78 ASSERT(hdr->EntriesOffset + 0x18 < len);
79 // Apply update sequence
80 ASSERT(hdr->UpdateSequenceOfs + 2*hdr->UpdateSequenceSize <= len);
81 NTFS_int_ApplyUpdateSequence(buf,len, (void*)(buf+hdr->UpdateSequenceOfs), hdr->UpdateSequenceSize);
82 size_t ofs = hdr->EntriesOffset + 0x18;
83 ent = NTFS_int_IterateIndex(buf + ofs, len - ofs, &Pos);
90 // TODO: This is not future-proof
91 const Uint16 *name16 = ent->Filename.Filename;
92 UTF16_ConvertToUTF8(FILENAME_MAX, Dest, ent->Filename.FilenameLength, name16);
93 LOG("Filename '%s'", Dest);
98 typedef int (*tNTFS_BTreeSearch_CmpFcn)(const tNTFS_IndexEntry_Filename *Ent, size_t SLen, const void *Search);
100 int NTFS_BTreeSearch_CmpI30(const tNTFS_IndexEntry_Filename *Ent, size_t SLen, const void *Search)
103 size_t fname_len = Ent->Filename.FilenameLength*2;
104 size_t cmplen = MIN(fnamelen, SLen);
105 int ret = memcmp(Ent->Filename.Filename, Search, cmplen);
110 else if( cmplen == SLen )
115 //LOG("Cmp '%.*ls' == '%s'", Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
116 // return UTF16_CompareWithUTF8(Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
117 return UTF16_CompareWithUTF8CI(Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
121 Uint64 NTFS_BTreeSearch(size_t Length, const void *Data,
122 tNTFS_BTreeSearch_CmpFcn Cmp, size_t SLen, const void *Search)
124 void *buffer_end = (char*)Data + Length;
125 const tNTFS_IndexEntry_Filename *ent = Data;
126 while( !(ent->IndexFlags & 0x02) )
128 if( (void*)(&ent->_rsvd + 1) > buffer_end ) {
132 // TODO: Handle collations?
133 int cmp = Cmp(ent, SLen, Search);
135 LOG("Located at %p: 0x%016llx", ent, ent->MFTReference);
136 return ent->MFTReference & ((1ULL << 48)-1);
141 ent = (void*)((char*)ent + ent->EntrySize);
143 if( ent->IndexFlags & 0x01 ) {
144 LOG("Descend to VCN %llx", *(Uint64*)((char*)ent + ent->EntrySize - 8));
145 return (1ULL << 63) | *(Uint64*)((char*)ent + ent->EntrySize - 8);
151 #define _MFTREF_IDX(ref) ((ref) & ((1ULL<<48)-1))
152 #define _MFTREF_SEQ(ref) ((ref) >> 48)
154 void NTFS_int_DumpIndex(tNTFS_Attrib *Allocation, Uint AttribID)
156 ENTER("pAllocation xAttribID", Allocation, AttribID);
162 size_t block_size = MAX(2048, Allocation->Disk->ClusterSize);
163 char buf[block_size];
165 while( (len = NTFS_ReadAttribData(Allocation, vcn*block_size, block_size, buf)) )
167 struct sNTFS_IndexHeader *hdr = (void*)buf;
168 // Apply update sequence
169 ASSERT(hdr->UpdateSequenceOfs + 2*hdr->UpdateSequenceSize <= len);
170 NTFS_int_ApplyUpdateSequence(buf,len, (void*)(buf+hdr->UpdateSequenceOfs), hdr->UpdateSequenceSize);
172 LOG("VCN %x: Ofs=%x, Size=%x",
173 vcn, hdr->EntriesOffset, hdr->EntriesSize);
174 if( hdr->ThisVCN != vcn ) {
175 Log_Notice("NTFS", "Data error: Index header VCN mismatch (%x!=exp %x)", hdr->ThisVCN, vcn);
177 size_t ofs = hdr->EntriesOffset + 0x18;
178 while( ofs < hdr->EntriesSize + (hdr->EntriesOffset + 0x18) )
180 struct sNTFS_IndexEntry *ent = (void*)(buf + ofs);
181 if( ofs + sizeof(*ent) > len )
183 LOG("%03x: L=%02x,M=%02x,F=%02x, Ref=%x/%llx", ofs,
184 ent->EntrySize, ent->MessageLen, ent->IndexFlags,
185 _MFTREF_SEQ(ent->MFTReference), _MFTREF_IDX(ent->MFTReference));
187 if( ent->EntrySize < sizeof(*ent) ) {
188 Log_Notice("NTFS", "Data error: Index entry size too small");
191 if( ent->MessageLen + sizeof(*ent) > ent->EntrySize ) {
192 Log_Notice("NTFS", "Data error: Index entry message size > entry size");
195 if( ent->IndexFlags & NTFS_IndexFlag_HasSubNode )
197 if( ent->EntrySize < sizeof(*ent) + 8 ) {
198 Log_Notice("NTFS", "Data error: Index entry size too small (SubVCN)");
200 LOG("- SubVCN=%llx", *(Uint64*)((char*)ent + ent->EntrySize - 8));
202 if( ent->IndexFlags & NTFS_IndexFlag_IsLast )
208 struct sNTFS_Attrib_Filename *fname = (void*)(buf + ofs + sizeof(*ent));
209 LOG("- Filename: %i %.*ls",
210 fname->FilenameNamespace,
211 fname->FilenameLength, fname->Filename);
215 ofs += ent->EntrySize;
222 tVFS_Node *NTFS_int_CreateNode(tNTFS_Disk *Disk, Uint64 MFTEntry)
224 tNTFS_FILE_Header *ent = NTFS_GetMFT(Disk, MFTEntry);
225 if( !ent || !(ent->Flags & 0x01) )
231 tNTFS_Directory tpl_dir;
234 memset(&types, 0, sizeof(tVFS_Node));
235 if( ent->Flags & 0x02 )
238 size = sizeof(types.tpl_dir);
239 ret = &types.tpl_dir.Node;
240 ret->Type = &gNTFS_DirType;
241 ret->Flags = VFS_FFLAG_DIRECTORY;
243 types.tpl_dir.I30Root = NTFS_GetAttrib(Disk, MFTEntry, NTFS_FileAttrib_IndexRoot, "$I30", 0);
244 types.tpl_dir.I30Allocation = NTFS_GetAttrib(Disk, MFTEntry,
245 NTFS_FileAttrib_IndexAllocation, "$I30", 0);
247 ASSERT(types.tpl_dir.I30Root->IsResident);
249 Debug_HexDump("NTFS CreateNode Root",
250 types.tpl_dir.I30Root->ResidentData,
251 types.tpl_dir.I30Root->DataSize);
252 if( types.tpl_dir.I30Allocation ) {
253 NTFS_int_DumpIndex(types.tpl_dir.I30Allocation, 0x30);
258 if( MFTEntry == 0x1C )
260 char tmpbuf[Disk->ClusterSize];
261 NTFS_ReadAttribData(types.tpl_dir.I30Allocation, 0, sizeof(tmpbuf), tmpbuf);
262 Debug_HexDump("NTFS CreateNode VCN#0", tmpbuf, sizeof(tmpbuf));
263 NTFS_ReadAttribData(types.tpl_dir.I30Allocation, sizeof(tmpbuf), sizeof(tmpbuf), tmpbuf);
264 Debug_HexDump("NTFS CreateNode VCN#1", tmpbuf, sizeof(tmpbuf));
271 size = sizeof(types.tpl_file);
272 ret = &types.tpl_file.Node;
273 ret->Type = &gNTFS_FileType;
274 types.tpl_file.Data = NTFS_GetAttrib(Disk, MFTEntry, NTFS_FileAttrib_Data, "", 0);
275 ret->Size = types.tpl_file.Data->DataSize;
277 ret->Inode = MFTEntry;
282 NTFS_ReleaseMFT(Disk, MFTEntry, ent);
283 return Inode_CacheNodeEx(Disk->InodeCache, ret, size);
287 * \brief Get an entry from a directory by name
289 tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
291 tNTFS_Directory *dir = (void*)Node;
292 tNTFS_Disk *disk = Node->ImplPtr;
293 ASSERT(dir->I30Root->IsResident);
294 const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData;
296 ENTER("XNode->Inode sName xFlags",
297 Node->Inode, Name, Flags);
300 size_t name16len = UTF16_ConvertFromUTF8(0, NULL, Name);
301 Uint16 name16[name16len+1];
302 UTF16_ConvertFromUTF8(name16len+1, name16, Name);
307 // Check resident first
308 size_t ofs = 0x10 + idxroot->FirstEntryOfs;
309 mftent = NTFS_BTreeSearch(
310 dir->I30Root->DataSize - ofs, dir->I30Root->ResidentData + ofs,
311 NTFS_BTreeSearch_CmpI30, -1, Name
313 while( mftent & (1ULL << 63) )
315 size_t unit_len = idxroot->AllocEntrySize;
316 char buf[ unit_len ];
317 size_t ofs = (mftent & 0xFFFFFF) * unit_len;
318 size_t len = NTFS_ReadAttribData(dir->I30Allocation, ofs, sizeof(buf), buf);
321 tNTFS_IndexHeader *hdr = (void*)buf;
323 if( memcmp(&hdr->Magic, "INDX", 4) != 0 ) {
324 Log_Notice("NTFS", "FindDir %p:%X:%x index magic bad %08x",
325 disk, Node->Inode, ofs / unit_len, hdr->Magic);
328 // Apply update sequence
329 if(hdr->UpdateSequenceOfs + 2*hdr->UpdateSequenceSize > len) {
330 Log_Notice("NTFS", "FindDir %p:%X:%x index update sequence out of buffer (%x+%x>%x)",
331 disk, Node->Inode, ofs / unit_len,
332 hdr->UpdateSequenceOfs, 2*hdr->UpdateSequenceSize, len);
335 NTFS_int_ApplyUpdateSequence(buf,len, (void*)(buf+hdr->UpdateSequenceOfs), hdr->UpdateSequenceSize);
337 //mftent = NTFS_BTreeSearch(len, (void*)buf, NTFS_BTreeSearch_CmpI30, name16len*2, name16);
338 mftent = NTFS_BTreeSearch(
339 len-(hdr->EntriesOffset+0x18), buf+hdr->EntriesOffset+0x18,
340 NTFS_BTreeSearch_CmpI30, -1, Name
344 if( !mftent || (mftent & (1ULL << 63)) ) {
350 tVFS_Node *ret = Inode_GetCache(disk->InodeCache, mftent);
352 ret = NTFS_int_CreateNode(disk, mftent);