/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
bldnames.cxx
Abstract:
This module contains the routines which build up the name-translation
table.
Author:
Bill McJohn (billmc) 02-Mar-1994
Environment:
ULIB, User Mode
--*/
#include <pch.cxx>
#define _NTAPI_ULIB_
#include "ulib.hxx"
#include "wstring.hxx"
#include "message.hxx"
#include "rtmsg.h"
#include "nametab.hxx"
#include "uhpfs.hxx"
#include "hpfsvol.hxx"
#include "hpfssa.hxx"
#include "superb.hxx"
#include "cpinfo.hxx"
#include "fnode.hxx"
#include "dirblk.hxx"
// Private prototypes:
//
BOOLEAN
NameIsCodepageInvariant(
IN USHORT NameLength,
IN PSTR Name
);
BOOLEAN
ConstructNameTableFromDirblk(
IN OUT PHPFS_VOL HpfsVol,
IN OUT PHPFS_SA HpfsSa,
IN LBN DirblkLbn,
IN OUT PNAME_TABLE NameTable,
IN OUT PMESSAGE Message,
IN PCWSTRING Path
);
BOOLEAN
ConstructNameTableFromFnode(
IN OUT PHPFS_VOL HpfsVol,
IN OUT PHPFS_SA HpfsSa,
IN LBN FnodeLbn,
IN OUT PNAME_TABLE NameTable,
IN OUT PMESSAGE Message,
IN PCWSTRING Path
);
BOOLEAN
NameIsCodepageInvariant(
IN USHORT NameLength,
IN PUCHAR Name
)
/*++
Routine Description:
This function determines whether a name is codepage-invariant,
i.e. has no characters greater than 127.
Arguments:
NameLength -- Supplies the number of bytes in the name.
Name -- Supplies the name.
Return Value:
TRUE if none of the bytes in the name are greater than 127.
--*/
{
USHORT i;
PUCHAR puch = (PUCHAR)Name;
for( i = 0; i < NameLength; i++ ) {
if( *puch > 127 ) {
return FALSE;
}
puch++;
}
return TRUE;
}
BOOLEAN
ConstructNameTableFromDirblk(
IN OUT PHPFS_VOL HpfsVol,
IN OUT PHPFS_SA HpfsSa,
IN LBN DirblkLbn,
IN OUT PNAME_TABLE NameTable,
IN OUT PMESSAGE Message,
IN PCWSTRING Path
)
/*++
Routine Description:
This adds to the Name Table the names in the directory sub-tree
rooted at DirblkLbn and all subdirectories.
Arguments:
HpfsVol -- Supplies the HPFS Volume being converted.
HpfsSa -- Supplies the Super Area for the volume.
DirblkLbn -- Supplies the LBN for the Dirblk in question.
NameTable -- Supplies the name table being constructed.
Message -- Supplies an outlet for messages.
Path -- Supplies the path for the directory containing
this dirblk.
Return Value:
TRUE upon successful completion.
--*/
{
CONST UnicodeBufferLength = 256;
WCHAR UnicodeNameBuffer[UnicodeBufferLength];
FSTRING Backslash;
DSTRING ChildName, ChildPath;
DIRBLK Dirblk;
PDIRENTD CurrentEntry;
ULONG CurrentOffset;
ULONG UnicodeNameLength;
USHORT CodepageId;
Backslash.Initialize( L"\\" );
if( !Dirblk.Initialize( HpfsVol, HpfsSa->GetHotfixList(), DirblkLbn ) ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
if( !Dirblk.Read() || !Dirblk.IsDirblk() ) {
Message->Set( MSG_CONV_HPFS_CORRUPT_VOLUME );
Message->Display( "" );
return FALSE;
}
// Spin through the directory entries in the DIRBLK, converting
// their names to Unicode and adding them to the Name Table.
// and recursing into child DIRBLK's.
//
CurrentEntry = Dirblk.GetFirstEntry();
CurrentOffset = Dirblk.QueryEntryOffset( CurrentEntry );
while( TRUE ) {
if( CurrentOffset + sizeof( USHORT ) > DIRBLK_SIZE ||
CurrentOffset + CurrentEntry->cchThisEntry > DIRBLK_SIZE ||
CurrentEntry->cchThisEntry == 0 ) {
Message->Set( MSG_CONV_HPFS_CORRUPT_VOLUME );
Message->Display( "" );
return FALSE;
}
// If this DIRBLK has a child, recurse into it.
//
if( (CurrentEntry->fFlags & DF_BTP) &&
!ConstructNameTableFromDirblk( HpfsVol,
HpfsSa,
BTP(CurrentEntry),
NameTable,
Message,
Path ) ) {
return FALSE;
}
if( CurrentEntry->fFlags & DF_END ) {
break;
}
if( CurrentEntry->fFlags & DF_SPEC ) {
// Move on to the next entry.
//
CurrentOffset += CurrentEntry->cchThisEntry;
CurrentEntry = NEXT_ENTRY( CurrentEntry );
continue;
}
if( CurrentEntry->cchName == 0 ) {
Message->Set( MSG_CONV_HPFS_CORRUPT_VOLUME );
Message->Display( "" );
return FALSE;
}
// Convert the name of this directory entry and
// add it to the name table. Note that it can
// be omitted from the name-table if it is
// codepage-invariant, but we need the unicode
// name to construct the full path.
//
CodepageId = HpfsSa->GetCasemap()->
QueryCodepageId( CurrentEntry->bCodePage );
UnicodeNameLength =
MultiByteToWideChar( CodepageId,
MB_PRECOMPOSED,
(PCHAR)&CurrentEntry->bName[0],
CurrentEntry->cchName,
UnicodeNameBuffer,
UnicodeBufferLength );
if( UnicodeNameLength == 0 ) {
if( Path->QueryChCount() == 0 ) {
Message->Set( MSG_CONV_INCONVERTIBLE_NAME_IN_ROOT );
Message->Display( "" );
} else {
Message->Set( MSG_CONV_INCONVERTIBLE_NAME );
Message->Display( "%W", Path );
}
return FALSE;
}
if( !NameIsCodepageInvariant( CurrentEntry->cchName,
&CurrentEntry->bName[0] ) ) {
if( !NameTable->Add( CodepageId,
CurrentEntry->cchName,
&CurrentEntry->bName[0],
(USHORT)UnicodeNameLength,
UnicodeNameBuffer ) ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
}
// If it's a subdirectory, recurse into it.
//
if( CurrentEntry->fAttr & ATTR_DIRECTORY ) {
if( !ChildName.Initialize( UnicodeNameBuffer, UnicodeNameLength ) ||
!ChildPath.Initialize( Path ) ||
!ChildPath.Strcat( &Backslash ) ||
!ChildPath.Strcat( &ChildName ) ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
if( !ConstructNameTableFromFnode( HpfsVol,
HpfsSa,
CurrentEntry->lbnFnode,
NameTable,
Message,
&ChildPath ) ) {
return FALSE;
}
}
// Move on to the next entry.
//
CurrentOffset += CurrentEntry->cchThisEntry;
CurrentEntry = NEXT_ENTRY( CurrentEntry );
}
return TRUE;
}
BOOLEAN
ConstructNameTableFromFnode(
IN OUT PHPFS_VOL HpfsVol,
IN OUT PHPFS_SA HpfsSa,
IN LBN FnodeLbn,
IN OUT PNAME_TABLE NameTable,
IN OUT PMESSAGE Message,
IN PCWSTRING Path
)
/*++
Routine Description:
This adds to the Name Table the names in the directory which has
its FNODE at FnodeLbn, and all subdirectories.
Arguments:
Return Value:
HpfsVol -- Supplies the HPFS Volume being converted.
HpfsSa -- Supplies the Super Area for the volume.
FnodeLbn -- Supplies the LBN for the FNode in question.
Note that this must be a directory FNode.
NameTable -- Supplies the name table being constructed.
Message -- Supplies an outlet for messages.
--*/
{
FNODE Fnode;
if( !Fnode.Initialize( HpfsVol, FnodeLbn ) ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
if( !Fnode.Read() || !Fnode.IsFnode() ) {
Message->Set( MSG_CONV_HPFS_CORRUPT_VOLUME );
Message->Display( "" );
return FALSE;
}
return( ConstructNameTableFromDirblk( HpfsVol,
HpfsSa,
Fnode.QueryRootDirblkLbn(),
NameTable,
Message,
Path ) );
}
extern "C"
BOOLEAN
FAR APIENTRY
ConstructNameTable(
IN PCWSTRING NtDriveName,
IN PCWSTRING FileName,
IN OUT PMESSAGE Message
)
/*++
Routine Description:
This function creates a MBCS-to-Unicode name translation
table for use by Autoconv. This allows Convert to squirrel
away Unicode translations for names that Autoconv can't
translate for itself.
Arguments:
NtDriveName -- Supplies the name of the volume for which the
name table is to be created.
FileName -- Supplies the name of the file in which the name
table will be stored. (This file will always
be in the root directory of the volume being
converted.)
Message -- Supplies an outlet for messages.
Return Value:
TRUE upon successful completion.
--*/
{
HPFS_VOL HpfsVol;
PHPFS_SA HpfsSa;
NAME_TABLE NameTable;
FSTRING BackSlash, RootName;
DSTRING QualifiedFileName;
if( !BackSlash.Initialize( L"\\" ) ||
!QualifiedFileName.Initialize( NtDriveName ) ||
!QualifiedFileName.Strcat( &BackSlash ) ||
!QualifiedFileName.Strcat( FileName ) ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
if( !NameTable.Initialize() ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
// Initialize the HPFS volume object and set up the helpers
// (bitmap, codepage and casemap, and hotfix list)
//
if( !HpfsVol.Initialize( NtDriveName, Message ) ) {
return FALSE;
}
HpfsSa = HpfsVol.GetHPFSSuperArea();
if( !HpfsSa->CheckSuperBlockSignatures() ) {
Message->Set( MSG_CONV_HPFS_NOT_HPFS );
Message->Display( "" );
return FALSE;
}
if( !HpfsSa->IsClean() ) {
Message->Set( MSG_CONV_HPFS_DIRTY_VOLUME );
Message->Display( "" );
return FALSE;
}
if( HpfsSa->GetSpare()->QueryHotFixCount() != 0 ||
HpfsSa->GetSpare()->IsHotFixesUsed() ) {
Message->Set( MSG_CONV_HPFS_HOTFIXES_PRESENT );
Message->Display( "" );
return FALSE;
}
if( !HpfsSa->SetupHelpers() ) {
Message->Set( MSG_CONV_HPFS_CORRUPT_VOLUME );
Message->Display( "" );
return FALSE;
}
Message->Set( MSG_CONV_CONSTRUCTING_NAME_TABLE );
Message->Display( "" );
RootName.Initialize( L"" );
if( !ConstructNameTableFromFnode( &HpfsVol,
HpfsSa,
HpfsSa->GetSuper()->QueryRootFnodeLbn(),
&NameTable,
Message,
&RootName ) ||
!NameTable.Write( &QualifiedFileName, Message ) ) {
return FALSE;
}
return TRUE;
}