NT4/private/utils/cuhpfs/cvtdir.cxx
2020-09-30 17:12:29 +02:00

1094 lines
36 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
cvtdir.cxx
Abstract:
This module contains the routines which convert an HPFS directory
to NTFS.
Author:
Bill McJohn (billmc) 18-Nov-1991
Environment:
ULIB, User Mode
--*/
#include <pch.cxx>
#define _NTAPI_ULIB_
#include "ulib.hxx"
#include "hmem.hxx"
#include "rtmsg.h"
#include "ifssys.hxx"
#include "drive.hxx"
#include "secrun.hxx"
#include "cannedsd.hxx"
#include "uhpfs.hxx"
#include "bitmap.hxx"
#include "dirblk.hxx"
#include "error.hxx"
#include "fnode.hxx"
#include "message.hxx"
#include "store.hxx"
#include "cpinfo.hxx"
#include "untfs.hxx"
#include "attrib.hxx"
#include "extents.hxx"
#include "frs.hxx"
#include "mft.hxx"
#include "ntfsbit.hxx"
#include "indxtree.hxx"
#include "mftfile.hxx"
#include "cuhpfs.hxx"
#include "nametab.hxx"
extern "C" ULONG
GetAcp(
);
BOOLEAN
ConvertNameToUnicode(
IN ULONG CodepageId,
IN PSTR Name,
IN ULONG NameLength,
IN PWSTR Buffer,
IN ULONG BufferLength,
OUT PULONG CharsInUnicodeName
)
/*++
Routine Description:
This function converts the name contained in DirectoryEntry
into a unicode name.
Arguments:
CodepageId -- Supplies the codepage with which the name
is associated.
Name -- Supplies the name to convert.
NameLength -- Supplies the length of the name to convert.
Buffer -- Supplies the target buffer for the conversion.
BufferLength -- Supplies the length (in bytes) of the
target buffer.
CharsInUnicodeName -- Receives the number of unicode characters
in the converted name.
Return Value:
TRUE if successful.
--*/
{
#ifdef _AUTOCHECK_ // AutoConvert--Windows API not available.
STATIC ULONG Acp = 0;
NTSTATUS Status;
PUCHAR puch;
ULONG BytesInUnicodeString, i;
// If we haven't already, fetch the system codepage ID.
//
if( Acp == 0 ) {
Acp = GetAcp();
if( Acp == 0 ) {
return FALSE;
}
}
if( Acp == CodepageId ) {
// This name is associated with the system codepage,
// so we can use the convenient RTL routines.
//
Status = RtlMultiByteToUnicodeN( Buffer,
BufferLength,
&BytesInUnicodeString,
Name,
NameLength );
if( !NT_SUCCESS( Status ) ) {
return FALSE;
}
*CharsInUnicodeName = BytesInUnicodeString / sizeof( WCHAR );
return TRUE;
}
// The name is not associated with the system codepage,
// so we have to be sneaky. If the name is codepage-invariant,
// we can convert it by just promoting the bytes to words.
//
puch = (PUCHAR)Name;
for( i = 0; i < NameLength; i++ ) {
if( *puch > 127 ) {
// Name is not codepage-invariant.
//
return FALSE;
}
Buffer[i] = (WCHAR)(*puch);
puch++;
}
*CharsInUnicodeName = NameLength;
return TRUE;
#else // _AUTOCHECK_ not defined--use Windows API
ULONG CharsConverted;
CharsConverted = MultiByteToWideChar( CodepageId,
MB_PRECOMPOSED,
Name,
NameLength,
Buffer,
BufferLength );
if( CharsConverted == 0 ) {
return FALSE;
}
*CharsInUnicodeName = CharsConverted;
return TRUE;
#endif
}
BOOLEAN
ConvertDirectoryToNtfs(
IN OUT PLOG_IO_DP_DRIVE Drive,
IN PCNAME_LOOKUP_TABLE NameTable,
IN OUT PMESSAGE Message,
IN OUT PNTFS_BITMAP VolumeBitmap,
IN OUT PHPFS_MAIN_BITMAP HpfsOnlyBitmap,
IN PCASEMAP Casemap,
IN OUT PNTFS_MFT_FILE Mft,
IN OUT ULONG ClustersPerIndexBuffer,
IN LBN RootDirblkLbn,
IN OUT PNTFS_FILE_RECORD_SEGMENT TargetFrs,
IN OUT PBOOLEAN IsCorrupt,
IN BOOLEAN Verbose,
IN OUT PVOID NameBuffer,
IN ULONG NameBufferLength,
IN OUT PVOID EaBuffer,
IN ULONG EaBufferLength,
IN ULONG Level,
IN PCWSTRING DirectoryPath
)
/*++
Routine Description:
This method converts an HPFS Directory Fnode to NTFS. It converts
every directory entry in the directory, and constructs an NTFS index
over $FILE_NAME for the converted entries.
Arguments:
Drive -- Supplies the drive being converted.
Message -- Supplies an outlet for messages.
VolumeBitmap -- Supplies the NTFS bitmap for the volume.
HpfsOnlyBitmap -- Supplies the bitmap of HPFS-only structures.
Casemap -- Supplies the case-mapping table for the
HPFS volume.
Mft -- Supplies the Master File Table.
RootDirblkLbn -- Supplies the LBN of the root dirblk of
this directory.
TargetFrs -- Supplies the FRS for the directory being
converted.
IsCorrupt -- Receives TRUE if the volume is found to be
corrupt. (Note that it may already be TRUE.)
Verbose -- Supplies a flag which indicates (if TRUE) that
every file converted should be displayed.
NameBuffer -- Supplies a scratch buffer for converting
directory entry names.
NameBufferLength -- Supplies the length (in bytes) of the file
name conversion buffer.
EaBuffer -- Supplies a scratch buffer for Extended
Attribute conversion.
EaBufferLength -- Supplies the length of EaBuffer
Level -- Supplies the depth of this directory
(root directory is level 0).
DirectoryPath -- Supplies the directory path, for display
Return Value:
TRUE upon successful completion.
Notes:
This method does not write TargetFrs.
--*/
{
NTFS_INDEX_TREE IndexTree;
DIRBLK ChildDirblk;
DSTRING FileNameIndexName;
// Create an index out of whole cloth. It's an index over
// $FILE_NAME, and its name is $I3.
if( !FileNameIndexName.Initialize( FileNameIndexNameData ) ||
!IndexTree.Initialize( $FILE_NAME,
Drive,
Mft->QueryClusterFactor(),
VolumeBitmap,
TargetFrs->GetUpcaseTable(),
COLLATION_FILE_NAME,
(UCHAR)ClustersPerIndexBuffer,
TargetFrs->QueryMaximumAttributeRecordSize()/2,
&FileNameIndexName ) ) {
return FALSE;
}
// Initialize the root dirblk. Note that Convert assumes that there
// are no hotfixes on the volume, so I can pass in NULL for the
// hotfix list.
if( !ChildDirblk.Initialize( Drive, NULL, RootDirblkLbn ) ) {
return FALSE;
}
// Read the dirblk and check that it is really a dirblk. If
// it's unreadable or not a dirblk, the volume is corrupt.
if( !ChildDirblk.Read() || !ChildDirblk.IsDirblk() ) {
*IsCorrupt = TRUE;
return FALSE;
}
// Convert the root dirblk. This will recursively convert the
// entire directory. Note that it takes two passes; on the
// first pass, only short names (ie. names which are both
// DOS and NTFS names) are converted; on the second, long
// names (names which require a generated short name) are
// converted. This prevents collisions between existing
// short names and generated short names.
//
if( !ConvertDirblkToNtfs( Drive,
NameTable,
Message,
VolumeBitmap,
HpfsOnlyBitmap,
Casemap,
Mft,
ClustersPerIndexBuffer,
&ChildDirblk,
&IndexTree,
TargetFrs->QuerySegmentReference(),
IsCorrupt,
Verbose,
NameBuffer,
NameBufferLength,
EaBuffer,
EaBufferLength,
Level,
DirectoryPath,
FALSE ) ||
!ConvertDirblkToNtfs( Drive,
NameTable,
Message,
VolumeBitmap,
HpfsOnlyBitmap,
Casemap,
Mft,
ClustersPerIndexBuffer,
&ChildDirblk,
&IndexTree,
TargetFrs->QuerySegmentReference(),
IsCorrupt,
Verbose,
NameBuffer,
NameBufferLength,
EaBuffer,
EaBufferLength,
Level,
DirectoryPath,
TRUE ) ) {
return FALSE;
}
// Save the index.
if( !IndexTree.Save( TargetFrs ) ) {
DebugPrint( "Couldn't save an index.\n" );
return FALSE;
}
return TRUE;
}
VOID
DisplayName(
IN OUT PMESSAGE Message,
IN PBYTE Name,
IN ULONG NameLength,
IN ULONG Level
)
/*++
Routine Description:
This method displays a file name. It indents entries under the
parent directory, so that the tree structure is evident.
Arguments:
Message -- Supplies the outlet for messages.
Name -- Supplies the name to display.
NameLength -- Supplies the length of the name.
Level -- Supplies the level of the directory that
contains this name (zero is root).
Return Value:
None.
--*/
{
STATIC CHAR NameDisplayBuffer[512];
ULONG NameStart;
// Insert a number of blanks in the name display buffer to
// correctly indent this name under its parent directory.
NameStart = Level * 4;
memset( NameDisplayBuffer, ' ', NameStart );
memcpy( NameDisplayBuffer + NameStart, Name, NameLength );
NameDisplayBuffer[NameStart + NameLength] = 0;
Message->Set( MSG_ONE_STRING_NEWLINE );
Message->Display( "%s", NameDisplayBuffer );
}
BOOLEAN
GenerateDosName(
IN PFILE_NAME NtfsName,
OUT PFILE_NAME DosName,
IN ULONG DosNameMaxLength,
IN PNTFS_INDEX_TREE ParentIndex
)
/*++
Routine Description:
This function fills in the DosName with an 8.3 name constructed
from the given NtfsName, ensuring that the generated name does not
exist in the specified index.
Arguments:
NtfsName -- Supplies the source name.
DosName -- Receives the generated DOS name.
DosNameMaxLength -- Supplies the maximum length (in chars) of
the file-name portion of the DOS name.
ParentIndex -- Supplies the index within which the generated
name must be unique.
Return Value:
TRUE if successful, otherwise FALSE.
--*/
{
CONST ULONG MaximumToTry = 1024;
GENERATE_NAME_CONTEXT NameContext;
MFT_SEGMENT_REFERENCE SegmentReference;
UNICODE_STRING NtfsNameString, DosNameString;
ULONG i;
BOOLEAN Error = FALSE;
// Fill in the non-name portions of the file name. Note that the
// duplicated information will be set up when the FRS is flushed.
//
memset( DosName, 0, DosNameMaxLength );
DosName->ParentDirectory = NtfsName->ParentDirectory;
DosName->Flags = FILE_NAME_DOS;
memset( &NameContext, 0, sizeof( GENERATE_NAME_CONTEXT ) );
NtfsNameString.Length = NtfsName->FileNameLength * sizeof( WCHAR );
NtfsNameString.MaximumLength = NtfsNameString.Length;
NtfsNameString.Buffer = NtfsFileNameGetName( NtfsName );
DosNameString.Length = 0;
DosNameString.MaximumLength = (USHORT)DosNameMaxLength;
DosNameString.Buffer = NtfsFileNameGetName( DosName );
for( i = 0; i < MaximumToTry; i++ ) {
RtlGenerate8dot3Name( &NtfsNameString, FALSE, &NameContext, &DosNameString );
DosName->FileNameLength = DosNameString.Length / sizeof( WCHAR );
if( !ParentIndex->QueryFileReference( NtfsFileNameGetLength(DosName),
DosName,
0,
&SegmentReference,
&Error ) ) {
// If no error occurred, then there is no matching name
// in the index, which means this name is acceptable;
// if an error did occur, I want to bail out. Either
// way, it's over.
//
return (!Error);
}
}
// Tried a whole lot of different names. None of them worked.
//
return FALSE;
}
CONST ULONG ShortNameBufferLength = 256;
CONST ULONG MaximumDosNameLength = 14;
BYTE ShortNameBuffer[ShortNameBufferLength];
BOOLEAN
ConvertDirentToNtfs(
IN OUT PLOG_IO_DP_DRIVE Drive,
IN PCNAME_LOOKUP_TABLE NameTable,
IN OUT PMESSAGE Message,
IN OUT PNTFS_BITMAP VolumeBitmap,
IN OUT PHPFS_MAIN_BITMAP HpfsOnlyBitmap,
IN PCASEMAP Casemap,
IN OUT PNTFS_MFT_FILE Mft,
IN ULONG ClustersPerIndexBuffer,
IN PDIRENTD DirectoryEntry,
IN OUT PNTFS_INDEX_TREE ParentIndex,
IN MFT_SEGMENT_REFERENCE ParentSegmentReference,
IN OUT PBOOLEAN IsCorrupt,
IN BOOLEAN Verbose,
IN OUT PVOID NameBuffer,
IN ULONG NameBufferLength,
IN OUT PVOID EaBuffer,
IN ULONG EaBufferLength,
IN ULONG Level,
IN PCWSTRING ParentDirectoryPath,
IN BOOLEAN ConvertLongNames
)
/*++
Routine Description:
This function creates an NTFS File Record Segment which corresponds
to a given HPFS Directory Entry.
Arguments:
Drive -- Supplies the drive being converted.
Message -- Supplies an outlet for messages.
VolumeBitmap -- Supplies the NTFS bitmap for the volume.
HpfsOnlyBitmap -- Supplies the bitmap of HPFS-only structures.
Casemap -- Supplies the case-mapping table for the
HPFS volume.
Mft -- Supplies the Master File Table.
DirectoryEntry -- Supplies the directory entry.
ParentIndex -- Supplies the index into which the converted
file or directory's name should be inserted.
ParentSegmentReference -- Supplies the segment reference of the File
Record Segment that contains the parent
index.
IsCorrupt -- Receives TRUE if the volume is found to
be corrupt.
Verbose -- Supplies a flag which indicates (if TRUE)
that every file converted should be displayed.
NameBuffer -- Supplies a scratch buffer for converting
file names.
NameBufferLength -- Supplies the length (in bytes) of the file
name conversion buffer.
EaBuffer -- Supplies a scratch buffer for Extended
Attribute conversion.
EaBufferLength -- Supplies the length of EaBuffer
Level -- Supplies the depth of the directory which
contains this dirent. (Root directory is
level 0).
ParentDirectoryPath -- Supplies the path of the parent directory,
for message display.
ConvertLongNames -- Supplies a flag which indicates whether
this pass is converting long or short names.
If the flag is FALSE, this entry is converted
only if it is a valid DOS name; if the flag
is TRUE, the entry is only converted if it
is not a valid DOS name.
Return Value:
TRUE upon successful completion.
--*/
{
STANDARD_INFORMATION StandardInfo;
FNODE Fnode;
NTFS_FILE_RECORD_SEGMENT NewFrs;
FSTRING Backslash;
DSTRING Name, FullPath;
VCN NewFileNumber;
MFT_SEGMENT_REFERENCE SegmentReference;
PFILE_NAME FileName = (PFILE_NAME)NameBuffer;
PFILE_NAME ShortName = (PFILE_NAME)ShortNameBuffer;
ULONG CharsInUnicodeName;
USHORT UnicodeChars;
USHORT FrsFlags;
USHORT CodepageId;
BOOLEAN result, Error;
ULONG LegalAttributeBits = FAT_DIRENT_ATTR_READ_ONLY |
FAT_DIRENT_ATTR_HIDDEN |
FAT_DIRENT_ATTR_SYSTEM |
FAT_DIRENT_ATTR_ARCHIVE;
DebugPtrAssert( Drive );
DebugPtrAssert( Message );
DebugPtrAssert( VolumeBitmap );
DebugPtrAssert( HpfsOnlyBitmap );
DebugPtrAssert( Casemap );
DebugPtrAssert( Mft );
DebugPtrAssert( DirectoryEntry );
DebugPtrAssert( ParentIndex );
DebugPtrAssert( IsCorrupt );
DebugPtrAssert( NameBuffer );
DebugPtrAssert( EaBuffer );
// Set up a FILE_NAME structure for the name. This includes
// converting the name to Unicode. Note that the duplicated
// information will be updated when the FRS is flushed.
//
memset( NameBuffer, 0, NameBufferLength );
FileName = (PFILE_NAME)NameBuffer;
CodepageId = Casemap->QueryCodepageId( DirectoryEntry->bCodePage );
if( !ConvertNameToUnicode(
CodepageId,
(PSTR)(&DirectoryEntry->bName[0]),
DirectoryEntry->cchName,
(PWSTR)NtfsFileNameGetName( FileName ),
NameBufferLength,
&CharsInUnicodeName ) ) {
// Can't convert this name--look in the name
// translation table, if it was supplied.
//
UnicodeChars = (USHORT)NameBufferLength;
if( NameTable == NULL ) {
Message->Set( MSG_CONV_INCONVERTIBLE_NAME );
Message->Display( "%W", ParentDirectoryPath );
Message->Set( MSG_CONV_USE_NAMETABLE );
Message->Display( "" );
return FALSE;
} else if( !NameTable->Lookup( CodepageId,
DirectoryEntry->cchName,
(PUCHAR)(&DirectoryEntry->bName[0]),
&UnicodeChars,
(PWCHAR)NtfsFileNameGetName( FileName ) ) ) {
if( ParentDirectoryPath->QueryChCount() == 0 ) {
Message->Set( MSG_CONV_INCONVERTIBLE_NAME_IN_ROOT );
Message->Display( "" );
} else {
Message->Set( MSG_CONV_INCONVERTIBLE_NAME );
Message->Display( "%W", ParentDirectoryPath );
}
return FALSE;
}
CharsInUnicodeName = UnicodeChars;
}
FileName->ParentDirectory = ParentSegmentReference;
FileName->FileNameLength = (UCHAR)CharsInUnicodeName;
FileName->Flags = NTFS_SA::IsDosName(FileName) ?
(FILE_NAME_DOS | FILE_NAME_NTFS) :
(FILE_NAME_NTFS);
// Fill in the standard information: Creation Time and Last
// Access Time go directly over; Last Modification Time becomes
// both Last Change Time and Last Modification Time.
IFS_SYSTEM::ConvertHpfsTimeToNtfsTime( DirectoryEntry->timCreate,
&StandardInfo.CreationTime );
IFS_SYSTEM::ConvertHpfsTimeToNtfsTime( DirectoryEntry->timLastAccess,
&StandardInfo.LastAccessTime );
IFS_SYSTEM::ConvertHpfsTimeToNtfsTime( DirectoryEntry->timLastMod,
&StandardInfo.LastModificationTime );
IFS_SYSTEM::ConvertHpfsTimeToNtfsTime( DirectoryEntry->timLastMod,
&StandardInfo.LastChangeTime );
StandardInfo.FileAttributes = DirectoryEntry->fAttr & LegalAttributeBits;
StandardInfo.MaximumVersions = 0;
StandardInfo.VersionNumber = 0;
// Check that this entry should be converted on this pass:
//
if( (ConvertLongNames && (FileName->Flags & FILE_NAME_DOS)) ||
(!ConvertLongNames && !(FileName->Flags & FILE_NAME_DOS)) ) {
// This name does not get converted on this pass.
//
return TRUE;
}
// If Convert is running in verbose mode, display the name.
//
if( Verbose ) {
DisplayName( Message,
DirectoryEntry->bName,
DirectoryEntry->cchName,
Level );
}
// Set up the full path for this entry.
//
if( !Backslash.Initialize( L"\\" ) ||
!Name.Initialize( (PWSTR)NtfsFileNameGetName( FileName ),
FileName->FileNameLength ) ||
!FullPath.Initialize( ParentDirectoryPath ) ||
!FullPath.Strcat( &Backslash ) ||
!FullPath.Strcat( &Name ) ) {
Message->Set( MSG_CONV_NO_MEMORY );
Message->Display( "" );
return FALSE;
}
// Initialize and read the entry's FNode. If this sector
// is unreadable or not an FNode, then the volume is corrupt.
if( !Fnode.Initialize( Drive, DirectoryEntry->lbnFnode ) ) {
DebugPrintf( "CONVERT: can't initialize FNode\n" );
return FALSE;
}
if( !Fnode.Read() || !Fnode.IsFnode() ) {
*IsCorrupt = TRUE;
DebugPrintf( "CONVERT: Bad FNode at lbn 0x%x\n", DirectoryEntry->lbnFnode );
return FALSE;
}
// Set up a new File Record Segment for the file or directory.
FrsFlags = 0;
if( DirectoryEntry->fAttr & ATTR_DIRECTORY ) {
FrsFlags |= FILE_FILE_NAME_INDEX_PRESENT;
}
if( !Mft->AllocateFileRecordSegment( &NewFileNumber, FALSE ) ||
!NewFrs.Initialize( NewFileNumber, Mft ) ||
!NewFrs.Create( &StandardInfo, FrsFlags ) ||
!NewFrs.AddFileNameAttribute( FileName ) ||
!NewFrs.AddSecurityDescriptor( NoAclCannedSd,
VolumeBitmap ) ) {
// Insufficient disk space or insufficient memory.
DebugPrintf( "CONVERT: ConvertDirectoryEntryToNtfs--can't create FRS.\n" );
return FALSE;
}
// Add an entry for the new File Record Segment to the parent
// index. Note that the value of the index entry is the entire
// FILE_NAME structure (which is the value of the $FILE_NAME
// attribute).
//
if( ParentIndex->QueryFileReference( NtfsFileNameGetLength( FileName ),
FileName,
0,
&SegmentReference,
&Error ) ) {
// This entry already exists in the index--since duplicates
// are not legal under NTFS, the conversion fails. Note that
// this condition can arise on a legal HPFS volume, since two
// distinct MBCS names can map to the same Unicode name.
//
Message->Set( MSG_CONV_DUPLICATE_NAME );
Message->Display( "%W", ParentDirectoryPath );
return FALSE;
}
if( !ParentIndex->InsertEntry( NtfsFileNameGetLength( FileName ),
FileName,
NewFrs.QuerySegmentReference() ) ) {
DebugPrintf( "CONVERT: Can't add entry to index\n" );
return FALSE;
}
// If the file name is not dos-compatible, generate an
// 8.3 name and add it to the index and the FRS.
//
if( !(FileName->Flags & FILE_NAME_DOS) ) {
if( !GenerateDosName( FileName,
ShortName,
MaximumDosNameLength,
ParentIndex ) ) {
DebugPrintf( "HPFS Convert: can't generate a DOS name.\n" );
return FALSE;
}
ShortName->Flags = FILE_NAME_DOS;
if( !ParentIndex->InsertEntry( NtfsFileNameGetLength(ShortName),
ShortName,
NewFrs.QuerySegmentReference() ) ||
!NewFrs.AddFileNameAttribute( ShortName ) ) {
DebugPrintf( "HPFS Convert: can't add DOS name to FRS.\n" );
return FALSE;
}
}
if( DirectoryEntry->fAttr & ATTR_DIRECTORY ) {
// This entry represents a directory--convert it.
result = ConvertDirectoryToNtfs( Drive,
NameTable,
Message,
VolumeBitmap,
HpfsOnlyBitmap,
Casemap,
Mft,
ClustersPerIndexBuffer,
Fnode.QueryRootDirblkLbn(),
&NewFrs,
IsCorrupt,
Verbose,
NameBuffer,
NameBufferLength,
EaBuffer,
EaBufferLength,
Level + 1,
&FullPath ) ;
} else {
// This entry represents a file--convert it.
result = ConvertFileFnodeToNtfs( Drive,
VolumeBitmap,
HpfsOnlyBitmap,
Mft,
&Fnode,
&NewFrs,
DirectoryEntry->cchFSize,
IsCorrupt,
&FullPath ) ;
}
if( result ) {
// Convert the Extended Attributes and write the FRS.
if( !ConvertEasToNtfs( Drive,
VolumeBitmap,
Mft,
&Fnode,
&NewFrs,
DirectoryEntry->ulEALen,
Casemap->
QueryCodepageId(DirectoryEntry->bCodePage),
IsCorrupt,
NameBuffer,
NameBufferLength,
EaBuffer,
EaBufferLength ) ) {
DebugPrintf( "Cannot convert EAs in fnode at lbn 0x%x.\n, DirectoryEntry->lbnFnode" );
return FALSE;
}
result = NewFrs.Flush( VolumeBitmap, ParentIndex );
}
return result;
}
BOOLEAN
ConvertDirblkToNtfs(
IN OUT PLOG_IO_DP_DRIVE Drive,
IN PCNAME_LOOKUP_TABLE NameTable,
IN OUT PMESSAGE Message,
IN OUT PNTFS_BITMAP VolumeBitmap,
IN OUT PHPFS_MAIN_BITMAP HpfsOnlyBitmap,
IN PCASEMAP Casemap,
IN OUT PNTFS_MFT_FILE Mft,
IN ULONG ClustersPerIndexBuffer,
IN PDIRBLK Dirblk,
IN OUT PNTFS_INDEX_TREE NtfsIndex,
IN MFT_SEGMENT_REFERENCE IndexSegmentReference,
IN OUT PBOOLEAN IsCorrupt,
IN BOOLEAN Verbose,
IN OUT PVOID NameBuffer,
IN ULONG NameBufferLength,
IN OUT PVOID EaBuffer,
IN ULONG EaBufferLength,
IN ULONG Level,
IN PCWSTRING DirectoryPath,
IN BOOLEAN ConvertLongNames
)
/*++
Routine Description:
This method converts the entries in an HPFS Dirblk to NTFS.
It also recurses into any child dirblks and converts them, too.
Arguments:
Drive -- Supplies the drive being converted.
Message -- Supplies an outlet for messages.
VolumeBitmap -- Supplies the NTFS bitmap for the volume.
HpfsOnlyBitmap -- Supplies the bitmap of HPFS-only structures.
Casemap -- Supplies the case-mapping table for the
HPFS volume.
Mft -- Supplies the Master File Table.
NtfsIndex -- Supplies the NTFS index which corresponds
to the directory being converted.
IndexSegmentReference -- Supplies the segment reference for the
File Record Segment which contains the index.
IsCorrupt -- Receives TRUE if the volume is found to
be corrupt.
Verbose -- Supplies a flag which indicates (if TRUE)
that every file converted should be displayed.
NameBuffer -- Supplies a scratch buffer for converting
file names.
NameBufferLength -- Supplies the length (in bytes) of the file
name conversion buffer.
EaBuffer -- Supplies a scratch buffer for Extended
Attribute conversion.
EaBufferLength -- Supplies the length of EaBuffer
Level -- Supplies the depth of the directory which
contains this dirblk (root directory
is level zero).
DirectoryPath -- Supplies the path of this directory.
ConvertLongNames -- Supplies a flag which indicates whether
to convert entries with long names. If this
flag is FALSE, only entries with names
that are valid DOS names are converted; if
this flag is TRUE, only entries that are
not valid DOS names are converted.
Return Value:
TRUE upon successful completion.
--*/
{
DIRBLK ChildDirblk;
PDIRENTD CurrentEntry;
ULONG CurrentOffset;
DebugPtrAssert( Drive );
DebugPtrAssert( Message );
DebugPtrAssert( VolumeBitmap );
DebugPtrAssert( HpfsOnlyBitmap );
DebugPtrAssert( Casemap );
DebugPtrAssert( Mft );
DebugPtrAssert( Dirblk );
DebugPtrAssert( NtfsIndex );
DebugPtrAssert( IsCorrupt );
DebugPtrAssert( NameBuffer );
DebugPtrAssert( EaBuffer );
// Initialize the child dirblk once for all. Note that Convert
// requires that the volume have no hotfixes, so I can pass in
// NULL for the hotfix list.
if( !ChildDirblk.Initialize( Drive, NULL, 0 ) ) {
DebugPrintf( "CONVERT: Can't initialize Dirblk.\n" );
return FALSE;
}
// Walk through all the entries in this dirblk, converting as
// I go. Note that this loops terminates in the middle, either
// by returning FALSE or by breaking.
CurrentEntry = Dirblk->GetFirstEntry();
CurrentOffset = Dirblk->QueryEntryOffset( CurrentEntry );
while( TRUE ) {
// Check that the current entry fits in the dirblk. First, check
// that this entry's cchThisEntry field fits in the dirblk, and
// then check that the entire length of the entry (based on that
// field) also fits.
//
if( CurrentOffset + sizeof(USHORT) > DIRBLK_SIZE ||
CurrentOffset + CurrentEntry->cchThisEntry > DIRBLK_SIZE ||
CurrentEntry->cchThisEntry == 0 ) {
// This entry overflows the DIRBLK or has zero length.
// In either case, it's corrupt.
//
DebugPrintf( "Dirblk at lbn 0x%x has no END entry.\n", Dirblk->QueryStartSector() );
*IsCorrupt = TRUE;
return FALSE;
}
// If the current entry has a B-Tree downpointer, recurse
// into the subtree.
if( CurrentEntry->fFlags & DF_BTP ) {
// Read the child dirblk--if it's unreadable or not a
// dirblk, this volume is corrupt.
ChildDirblk.Relocate( BTP(CurrentEntry ) );
if( !ChildDirblk.Read() || !ChildDirblk.IsDirblk() ) {
// The volume is corrupt.
DebugPrintf( "CONVERT: Child dirblk is not a dirblk.\n" );
*IsCorrupt = TRUE;
return FALSE;
}
if( !ConvertDirblkToNtfs(Drive,
NameTable,
Message,
VolumeBitmap,
HpfsOnlyBitmap,
Casemap,
Mft,
ClustersPerIndexBuffer,
&ChildDirblk,
NtfsIndex,
IndexSegmentReference,
IsCorrupt,
Verbose,
NameBuffer,
NameBufferLength,
EaBuffer,
EaBufferLength,
Level,
DirectoryPath,
ConvertLongNames ) ) {
// Couldn't convert the subtree. Give up. Note that
// if the child detected corruption, it will have set
// *IsCorrupt.
//
return FALSE;
}
}
if( CurrentEntry->fFlags & DF_END ) {
// End of the road.
break;
}
// If the current entry isn't the special '..' entry,
// convert it.
if( !(CurrentEntry->fFlags & DF_SPEC) &&
!ConvertDirentToNtfs( Drive,
NameTable,
Message,
VolumeBitmap,
HpfsOnlyBitmap,
Casemap,
Mft,
ClustersPerIndexBuffer,
CurrentEntry,
NtfsIndex,
IndexSegmentReference,
IsCorrupt,
Verbose,
NameBuffer,
NameBufferLength,
EaBuffer,
EaBufferLength,
Level,
DirectoryPath,
ConvertLongNames ) ) {
return FALSE;
}
// Move on to the next entry.
CurrentOffset += CurrentEntry->cchThisEntry;
CurrentEntry = NEXT_ENTRY( CurrentEntry );
}
return TRUE;
}