Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str);
// === CODE ===
+const tNTFS_IndexEntry_Filename *NTFS_int_IterateIndex(const char *Buf, size_t Len, int *Count)
+{
+ const tNTFS_IndexEntry_Filename *ent;
+ for( size_t ofs = 0; ofs < Len; ofs += ent->EntrySize )
+ {
+ ent = (const void*)(Buf + ofs);
+
+ if( ofs + sizeof(*ent) > Len ) {
+ break;
+ }
+ if( ent->EntrySize < sizeof(*ent) ) {
+ break;
+ }
+ // End of block - move on to the next one
+ if( ent->IndexFlags & 0x02 ) {
+ LOG("end of block at ofs %x", ofs);
+ break;
+ }
+
+ // A little hacky - hide all internal files
+ if( (ent->MFTReference & ((1ULL << 48)-1)) < 12 )
+ continue;
+ // Skip DOS filenames
+ if( ent->Filename.FilenameNamespace == NTFS_FilenameNamespace_DOS )
+ continue ;
+
+ // Located
+ if( (*Count)-- <= 0 )
+ return ent;
+ }
+ return NULL;
+}
+
/**
* \brief Get the name of an indexed directory entry
*/
int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
{
tNTFS_Directory *dir = (void*)Node;
- tNTFS_Disk *disk = Node->ImplPtr;
+
+ ENTER("XNode->Inode iPos", Node->Inode, Pos);
ASSERT(dir->I30Root->IsResident);
const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData;
- //const tNTFS_Attrib_IndexEntry *rootents = (void*)(idxroot + 1);
- if( idxroot->Flags & 0x01 )
+ // Check resident root
+ const tNTFS_IndexEntry_Filename *ent;
+ ent = NTFS_int_IterateIndex(
+ (char*)idxroot + (0x10 + idxroot->FirstEntryOfs),
+ dir->I30Root->DataSize - (0x10 + idxroot->FirstEntryOfs),
+ &Pos);
+ char buf[idxroot->AllocEntrySize];
+ size_t vcn = 0;
+ size_t len;
+ while( !ent && (len = NTFS_ReadAttribData(dir->I30Allocation, vcn*sizeof(buf), sizeof(buf), buf)) )
{
- // Read from allocation
- char buf[disk->ClusterSize];
+ // Check allocation
struct sNTFS_IndexHeader *hdr = (void*)buf;
- size_t ofs = 0;
- size_t len = sizeof(buf);
- struct sNTFS_IndexEntry_Filename *ent = (void*)(buf + len);
-
- for( ; ; ent = (void*)((char*)ent + ent->EntrySize) )
- {
- 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));
- }
- // TODO: When end of node is hit, should the next cluster be loaded?
- if( ent->IndexFlags & 0x02 )
- break;
- // A little hacky - hide all internal files
- if( (ent->MFTReference & ((1ULL << 48)-1)) < 12 )
- continue;
- if( Pos -- <= 0 )
- break;
-
- //LOG("ent = {.MFTEnt=%llx,.FilenameOfs=%x}", ent->MFTReference, ent->FilenameOfs);
- #if 0
- //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 = %i '%s'", ent->Filename.FilenameNamespace, tmpname);
- #elif 0
- LOG("name = '%.*ls'", ent->Filename.FilenameLength, ent->Filename.Filename);
- #endif
- }
-
- if( Pos >= 0 )
- return -1;
- // Last entry does not refer to a file
- if( ent->IndexFlags & 0x02 )
- return -1;
-
- if( ent->Filename.FilenameNamespace == NTFS_FilenameNamespace_DOS )
- return 1;
-
- //Uint16 *name16 = (Uint16*)ent + ent->FilenameOfs/2;
- Uint16 *name16 = ent->Filename.Filename;
- UTF16_ConvertToUTF8(FILENAME_MAX, Dest, ent->Filename.FilenameLength, name16);
- LOG("Filename '%s'", Dest);
- return 0;
+ ASSERT(hdr->EntriesOffset + 0x18 < len);
+ size_t ofs = hdr->EntriesOffset + 0x18;
+ ent = NTFS_int_IterateIndex(buf + ofs, len - ofs, &Pos);
+ vcn ++;
}
- else
- {
- // Local only
+ if( !ent ) {
+ LEAVE('i', 1);
+ return -1;
}
- return -ENOTIMPL;
+ // TODO: This is not future-proof
+ const Uint16 *name16 = ent->Filename.Filename;
+ UTF16_ConvertToUTF8(FILENAME_MAX, Dest, ent->Filename.FilenameLength, name16);
+ LOG("Filename '%s'", Dest);
+ LEAVE('i', 0);
+ return 0;
}
typedef int (*tNTFS_BTreeSearch_CmpFcn)(const tNTFS_IndexEntry_Filename *Ent, size_t SLen, const void *Search);
else
return 1;
#else
- LOG("Cmp '%.*ls' == '%s'", Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
+ //LOG("Cmp '%.*ls' == '%s'", Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
// return UTF16_CompareWithUTF8(Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
return UTF16_CompareWithUTF8CI(Ent->Filename.FilenameLength, Ent->Filename.Filename, Search);
#endif
}
-Uint64 NTFS_BTreeSearch(size_t Length, tNTFS_IndexHeader *IndexHdr,
+Uint64 NTFS_BTreeSearch(size_t Length, const void *Data,
tNTFS_BTreeSearch_CmpFcn Cmp, size_t SLen, const void *Search)
{
- void *buffer_end = (char*)IndexHdr + Length;
- tNTFS_IndexEntry_Filename *ent = (void*)((char*)IndexHdr + IndexHdr->EntriesOffset + 0x18);
+ void *buffer_end = (char*)Data + Length;
+ const tNTFS_IndexEntry_Filename *ent = Data;
while( !(ent->IndexFlags & 0x02) )
{
if( (void*)(&ent->_rsvd + 1) > buffer_end ) {
// TODO: Handle collations?
int cmp = Cmp(ent, SLen, Search);
if( cmp == 0 ) {
- LOG("Located at %p: 0x%016llx", ent->MFTReference);
+ LOG("Located at %p: 0x%016llx", ent, ent->MFTReference);
return ent->MFTReference & ((1ULL << 48)-1);
}
if( cmp > 0 )
return 0;
}
+#define _MFTREF_IDX(ref) ((ref) & ((1ULL<<48)-1))
+#define _MFTREF_SEQ(ref) ((ref) >> 48)
+
+void NTFS_int_DumpIndex(tNTFS_Attrib *Allocation, Uint AttribID)
+{
+ ENTER("pAllocation xAttribID", Allocation, AttribID);
+ if(!Allocation) {
+ LEAVE('-');
+ return ;
+ }
+ Uint32 vcn = 0;
+ size_t block_size = MAX(2048, Allocation->Disk->ClusterSize);
+ char buf[block_size];
+ size_t len;
+ while( (len = NTFS_ReadAttribData(Allocation, vcn*block_size, block_size, buf)) )
+ {
+ struct sNTFS_IndexHeader *hdr = (void*)buf;
+ LOG("VCN %x: Ofs=%x, Size=%x",
+ vcn, hdr->EntriesOffset, hdr->EntriesSize);
+ if( hdr->ThisVCN != vcn ) {
+ Log_Notice("NTFS", "Data error: Index header VCN mismatch (%x!=exp %x)", hdr->ThisVCN, vcn);
+ }
+ size_t ofs = hdr->EntriesOffset + 0x18;
+ while( ofs < hdr->EntriesSize + (hdr->EntriesOffset + 0x18) )
+ {
+ struct sNTFS_IndexEntry *ent = (void*)(buf + ofs);
+ if( ofs + sizeof(*ent) > len )
+ break;
+ LOG("%03x: L=%02x,M=%02x,F=%02x, Ref=%x/%llx", ofs,
+ ent->EntrySize, ent->MessageLen, ent->IndexFlags,
+ _MFTREF_SEQ(ent->MFTReference), _MFTREF_IDX(ent->MFTReference));
+
+ if( ent->EntrySize < sizeof(*ent) ) {
+ Log_Notice("NTFS", "Data error: Index entry size too small");
+ break ;
+ }
+ if( ent->MessageLen + sizeof(*ent) > ent->EntrySize ) {
+ Log_Notice("NTFS", "Data error: Index entry message size > entry size");
+ }
+
+ if( ent->IndexFlags & NTFS_IndexFlag_HasSubNode )
+ {
+ if( ent->EntrySize < sizeof(*ent) + 8 ) {
+ Log_Notice("NTFS", "Data error: Index entry size too small (SubVCN)");
+ }
+ LOG("- SubVCN=%llx", *(Uint64*)((char*)ent + ent->EntrySize - 8));
+ }
+ if( ent->IndexFlags & NTFS_IndexFlag_IsLast )
+ break;
+
+ switch(AttribID)
+ {
+ case 0x30: {
+ struct sNTFS_Attrib_Filename *fname = (void*)(buf + ofs + sizeof(*ent));
+ LOG("- Filename: %i %.*ls",
+ fname->FilenameNamespace,
+ fname->FilenameLength, fname->Filename);
+ break; }
+ }
+
+ ofs += ent->EntrySize;
+ }
+ vcn ++;
+ }
+ LEAVE('-');
+}
+
tVFS_Node *NTFS_int_CreateNode(tNTFS_Disk *Disk, Uint64 MFTEntry)
{
tNTFS_FILE_Header *ent = NTFS_GetMFT(Disk, MFTEntry);
ret->Flags = VFS_FFLAG_DIRECTORY;
types.tpl_dir.I30Root = NTFS_GetAttrib(Disk, MFTEntry, NTFS_FileAttrib_IndexRoot, "$I30", 0);
- types.tpl_dir.I30Allocation = NTFS_GetAttrib(Disk, MFTEntry, NTFS_FileAttrib_IndexAllocation, "$I30", 0);
+ types.tpl_dir.I30Allocation = NTFS_GetAttrib(Disk, MFTEntry,
+ NTFS_FileAttrib_IndexAllocation, "$I30", 0);
+
+ ASSERT(types.tpl_dir.I30Root->IsResident);
+ #if 0
+ Debug_HexDump("NTFS CreateNode Root",
+ types.tpl_dir.I30Root->ResidentData,
+ types.tpl_dir.I30Root->DataSize);
+ if( types.tpl_dir.I30Allocation ) {
+ NTFS_int_DumpIndex(types.tpl_dir.I30Allocation, 0x30);
+ }
+ #endif
+
+ #if 0
+ if( MFTEntry == 0x1C )
+ {
+ char tmpbuf[Disk->ClusterSize];
+ NTFS_ReadAttribData(types.tpl_dir.I30Allocation, 0, sizeof(tmpbuf), tmpbuf);
+ Debug_HexDump("NTFS CreateNode VCN#0", tmpbuf, sizeof(tmpbuf));
+ NTFS_ReadAttribData(types.tpl_dir.I30Allocation, sizeof(tmpbuf), sizeof(tmpbuf), tmpbuf);
+ Debug_HexDump("NTFS CreateNode VCN#1", tmpbuf, sizeof(tmpbuf));
+ }
+ #endif
}
else
{
ASSERT(dir->I30Root->IsResident);
const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData;
+ ENTER("XNode->Inode sName xFlags",
+ Node->Inode, Name, Flags);
+
#if 0
size_t name16len = UTF16_ConvertFromUTF8(0, NULL, Name);
Uint16 name16[name16len+1];
#endif
Uint64 mftent = 0;
- if( idxroot->Flags & 0x01 )
+
+ // Check resident first
+ size_t ofs = 0x10 + idxroot->FirstEntryOfs;
+ mftent = NTFS_BTreeSearch(
+ dir->I30Root->DataSize - ofs, dir->I30Root->ResidentData + ofs,
+ NTFS_BTreeSearch_CmpI30, -1, Name
+ );
+ while( mftent & (1ULL << 63) )
{
- size_t unit_len = MAX(disk->ClusterSize, 2048);
+ size_t unit_len = idxroot->AllocEntrySize;
char buf[ unit_len ];
- do {
- size_t ofs = (mftent & 0xFFFFFF) * unit_len;
- size_t len = NTFS_ReadAttribData(dir->I30Allocation, ofs, sizeof(buf), buf);
- //mftent = NTFS_BTreeSearch(len, (void*)buf, NTFS_BTreeSearch_CmpI30, name16len*2, name16);
- mftent = NTFS_BTreeSearch(len, (void*)buf, NTFS_BTreeSearch_CmpI30, -1, Name);
- } while(mftent & (1ULL << 63));
- }
- else
- {
+ size_t ofs = (mftent & 0xFFFFFF) * unit_len;
+ size_t len = NTFS_ReadAttribData(dir->I30Allocation, ofs, sizeof(buf), buf);
+ if( len == 0 )
+ break;
+ tNTFS_IndexHeader *hdr = (void*)buf;
+ //mftent = NTFS_BTreeSearch(len, (void*)buf, NTFS_BTreeSearch_CmpI30, name16len*2, name16);
+ mftent = NTFS_BTreeSearch(
+ len-(hdr->EntriesOffset+0x18), buf+hdr->EntriesOffset+0x18,
+ NTFS_BTreeSearch_CmpI30, -1, Name
+ );
}
- if( !mftent )
+ if( !mftent ) {
+ LEAVE('n');
return NULL;
+ }
// Allocate node
tVFS_Node *ret = Inode_GetCache(disk->InodeCache, mftent);
- if(ret)
- return ret;
- return NTFS_int_CreateNode(disk, mftent);
+ if(!ret)
+ ret = NTFS_int_CreateNode(disk, mftent);
+ LEAVE('p', ret);
+ return ret;
}