X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FModules%2FFilesystems%2FNTFS%2Fdir.c;h=f72c637c9cd60ca6043538003e6e5351df3e2e53;hb=9c4eedf4893f851bd1ba60ce541c8d098a9ef7f7;hp=11cd2417e43b25ccf58e4858e52c53d8c90f3314;hpb=4ebe00546574e97c5316881881f7f2562deea74b;p=tpg%2Facess2.git diff --git a/KernelLand/Modules/Filesystems/NTFS/dir.c b/KernelLand/Modules/Filesystems/NTFS/dir.c index 11cd2417..f72c637c 100644 --- a/KernelLand/Modules/Filesystems/NTFS/dir.c +++ b/KernelLand/Modules/Filesystems/NTFS/dir.c @@ -6,43 +6,351 @@ * * dir.c - Directory Handling */ +#define DEBUG 1 #include "common.h" -#include "index.h" +//#include "index.h" +#include // === PROTOTYPES === int NTFS_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]); -tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name); +tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags); 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]) { - return -ENOTIMPL; + tNTFS_Directory *dir = (void*)Node; + + ENTER("XNode->Inode iPos", Node->Inode, Pos); + + ASSERT(dir->I30Root->IsResident); + const tNTFS_Attrib_IndexRoot *idxroot = dir->I30Root->ResidentData; + + // 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)) ) + { + // Check allocation + struct sNTFS_IndexHeader *hdr = (void*)buf; + ASSERT(hdr->EntriesOffset + 0x18 < len); + // Apply update sequence + ASSERT(hdr->UpdateSequenceOfs + 2*hdr->UpdateSequenceSize <= len); + NTFS_int_ApplyUpdateSequence(buf,len, (void*)(buf+hdr->UpdateSequenceOfs), hdr->UpdateSequenceSize); + size_t ofs = hdr->EntriesOffset + 0x18; + ent = NTFS_int_IterateIndex(buf + ofs, len - ofs, &Pos); + vcn ++; + } + if( !ent ) { + LEAVE_RET('i', -1); + } + + // 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; } -/** - * \brief Get an entry from a directory by name - */ -tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name) +typedef int (*tNTFS_BTreeSearch_CmpFcn)(const tNTFS_IndexEntry_Filename *Ent, size_t SLen, const void *Search); + +int NTFS_BTreeSearch_CmpI30(const tNTFS_IndexEntry_Filename *Ent, size_t SLen, const void *Search) { - tNTFS_Disk *disk = Node->ImplPtr; - Uint64 inode = NTFS_int_IndexLookup(Node->Inode, "$I30", Name); - tVFS_Node node; - - if(!inode) return NULL; + #if 0 + size_t fname_len = Ent->Filename.FilenameLength*2; + size_t cmplen = MIN(fnamelen, SLen); + int ret = memcmp(Ent->Filename.Filename, Search, cmplen); + if( ret != 0 ) + return ret; + if( cmplen < SLen ) + return -1; + else if( cmplen == SLen ) + return 0; + else + return 1; + #else + //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, const void *Data, + tNTFS_BTreeSearch_CmpFcn Cmp, size_t SLen, const void *Search) +{ + void *buffer_end = (char*)Data + Length; + const tNTFS_IndexEntry_Filename *ent = Data; + while( !(ent->IndexFlags & 0x02) ) + { + if( (void*)(&ent->_rsvd + 1) > buffer_end ) { + // on-disk error + return 0; + } + // TODO: Handle collations? + int cmp = Cmp(ent, SLen, Search); + if( cmp == 0 ) { + LOG("Located at %p: 0x%016llx", ent, ent->MFTReference); + return ent->MFTReference & ((1ULL << 48)-1); + } + if( cmp > 0 ) + break; + + ent = (void*)((char*)ent + ent->EntrySize); + } + if( ent->IndexFlags & 0x01 ) { + LOG("Descend to VCN %llx", *(Uint64*)((char*)ent + ent->EntrySize - 8)); + return (1ULL << 63) | *(Uint64*)((char*)ent + ent->EntrySize - 8); + } + LOG("Not found"); + 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; + // Apply update sequence + ASSERT(hdr->UpdateSequenceOfs + 2*hdr->UpdateSequenceSize <= len); + NTFS_int_ApplyUpdateSequence(buf,len, (void*)(buf+hdr->UpdateSequenceOfs), hdr->UpdateSequenceSize); + + 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); + if( !ent || !(ent->Flags & 0x01) ) + return NULL; + + tVFS_Node *ret; + size_t size; + union { + tNTFS_Directory tpl_dir; + tNTFS_File tpl_file; + } types; + memset(&types, 0, sizeof(tVFS_Node)); + if( ent->Flags & 0x02 ) + { + // Directory + size = sizeof(types.tpl_dir); + ret = &types.tpl_dir.Node; + ret->Type = &gNTFS_DirType; + 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); + + 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 - node.Inode = inode; + #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 + { + // File + size = sizeof(types.tpl_file); + ret = &types.tpl_file.Node; + ret->Type = &gNTFS_FileType; + types.tpl_file.Data = NTFS_GetAttrib(Disk, MFTEntry, NTFS_FileAttrib_Data, "", 0); + ret->Size = types.tpl_file.Data->DataSize; + } + ret->Inode = MFTEntry; + ret->ImplPtr = Disk; + + // TODO: Permissions - return Inode_CacheNode(disk->CacheHandle, &node); + NTFS_ReleaseMFT(Disk, MFTEntry, ent); + return Inode_CacheNodeEx(Disk->InodeCache, ret, size); } /** - * \brief Scans an index for the requested value and returns the associated ID + * \brief Get an entry from a directory by name */ -Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str) +tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags) { - return 0; + tNTFS_Directory *dir = (void*)Node; + tNTFS_Disk *disk = Node->ImplPtr; + 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]; + UTF16_ConvertFromUTF8(name16len+1, name16, Name); + #endif + + Uint64 mftent = 0; + + // 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 = idxroot->AllocEntrySize; + char buf[ unit_len ]; + 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; + + if( memcmp(&hdr->Magic, "INDX", 4) != 0 ) { + Log_Notice("NTFS", "FindDir %p:%X:%x index magic bad %08x", + disk, Node->Inode, ofs / unit_len, hdr->Magic); + break; + } + // Apply update sequence + if(hdr->UpdateSequenceOfs + 2*hdr->UpdateSequenceSize > len) { + Log_Notice("NTFS", "FindDir %p:%X:%x index update sequence out of buffer (%x+%x>%x)", + disk, Node->Inode, ofs / unit_len, + hdr->UpdateSequenceOfs, 2*hdr->UpdateSequenceSize, len); + break; + } + NTFS_int_ApplyUpdateSequence(buf,len, (void*)(buf+hdr->UpdateSequenceOfs), hdr->UpdateSequenceSize); + // Search + //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 || (mftent & (1ULL << 63)) ) { + LEAVE('n'); + return NULL; + } + + // Allocate node + tVFS_Node *ret = Inode_GetCache(disk->InodeCache, mftent); + if(!ret) + ret = NTFS_int_CreateNode(disk, mftent); + LEAVE('p', ret); + return ret; } +