mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2908 lines
80 KiB
2908 lines
80 KiB
/*++
|
|
|
|
Copyright (c) 1990-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fatntfs.cxx
|
|
|
|
Abstract:
|
|
|
|
This module implements the conversion from FAT to NTFS
|
|
|
|
Author:
|
|
|
|
Ramon J. San Andres (ramonsa) Sep-19-1991
|
|
|
|
Environment:
|
|
|
|
ULIB, User Mode
|
|
|
|
--*/
|
|
|
|
|
|
#include <pch.cxx>
|
|
|
|
//
|
|
// ULIB include files
|
|
//
|
|
#define _NTAPI_ULIB_
|
|
#include "ulib.hxx"
|
|
#include "array.hxx"
|
|
#include "drive.hxx"
|
|
#include "hmem.hxx"
|
|
#include "wstring.hxx"
|
|
#include "rtmsg.h"
|
|
|
|
//#define CONVERT_PERF_COUNTERS 1
|
|
|
|
//
|
|
// IFSUTIL include files
|
|
//
|
|
#include "secrun.hxx"
|
|
|
|
#if defined ( _AUTOCONV_ )
|
|
#include "ifssys.hxx"
|
|
#else
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
#include <stdio.h>
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// CUFAT include files
|
|
//
|
|
|
|
#include "fatsa.hxx"
|
|
#include "fatntfs.hxx"
|
|
|
|
//
|
|
// UFAT include files
|
|
//
|
|
#include "fatdent.hxx"
|
|
#include "fatdir.hxx"
|
|
#include "filedir.hxx"
|
|
|
|
//
|
|
// UNTFS include files
|
|
//
|
|
#include "attrib.hxx"
|
|
#include "upfile.hxx"
|
|
#include "logfile.hxx"
|
|
#include "ntfssa.hxx"
|
|
|
|
//
|
|
// Number of clusters in boot sector
|
|
//
|
|
#define CLUSTERS_IN_BOOT ((BYTES_IN_BOOT_AREA + _ClusterFactor*_Drive->QuerySectorSize() - 1)/ \
|
|
(_ClusterFactor*_Drive->QuerySectorSize()))
|
|
|
|
//
|
|
// Maximum ntfs cluster size to use during conversion
|
|
//
|
|
#define MAX_NTFS_CLUSTER_SIZE (1024*4) // 4K
|
|
|
|
DEFINE_CONSTRUCTOR( FAT_NTFS, OBJECT );
|
|
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
typedef struct _PERF_DATA {
|
|
BOOLEAN first_call;
|
|
LARGE_INTEGER t1;
|
|
LARGE_INTEGER wwtime;
|
|
LARGE_INTEGER wrtime;
|
|
LARGE_INTEGER wctime;
|
|
LARGE_INTEGER wscnt;
|
|
LARGE_INTEGER wccnt;
|
|
LARGE_INTEGER rrtime;
|
|
LARGE_INTEGER rscnt;
|
|
LARGE_INTEGER rccnt;
|
|
} PERF_DATA;
|
|
|
|
BOOLEAN
|
|
CheckTime(
|
|
PMESSAGE Message,
|
|
PLOG_IO_DP_DRIVE pDrive,
|
|
PERF_DATA *pData,
|
|
PCHAR Title
|
|
)
|
|
{
|
|
LARGE_INTEGER freq;
|
|
LARGE_INTEGER wwtime;
|
|
LARGE_INTEGER wrtime;
|
|
LARGE_INTEGER wctime;
|
|
LARGE_INTEGER wscnt;
|
|
LARGE_INTEGER wccnt;
|
|
LARGE_INTEGER rrtime;
|
|
LARGE_INTEGER rscnt;
|
|
LARGE_INTEGER rccnt;
|
|
LARGE_INTEGER t2;
|
|
|
|
if (pData->first_call) {
|
|
pData->first_call = FALSE;
|
|
pDrive->QueryPerformanceCounters(&pData->wwtime,
|
|
&pData->wrtime,
|
|
&pData->wctime,
|
|
&pData->wscnt,
|
|
&pData->wccnt,
|
|
&pData->rrtime,
|
|
&pData->rscnt,
|
|
&pData->rccnt);
|
|
Message->DisplayMsg(MSG_CHK_NTFS_MESSAGE, "%s%s", "VVVV ", Title);
|
|
QueryPerformanceCounter(&pData->t1);
|
|
} else {
|
|
QueryPerformanceCounter(&t2);
|
|
pDrive->QueryPerformanceCounters(&wwtime,
|
|
&wrtime,
|
|
&wctime,
|
|
&wscnt,
|
|
&wccnt,
|
|
&rrtime,
|
|
&rscnt,
|
|
&rccnt);
|
|
QueryPerformanceFrequency(&freq);
|
|
Message->Set(MSG_CHK_NTFS_MESSAGE);
|
|
Message->Display("%s%I64d", "^^^^ Elapsed time in ms: ",
|
|
((t2.QuadPart-pData->t1.QuadPart)*1000)/freq.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ WWTime: ", ((wwtime.QuadPart-pData->wwtime.QuadPart)*1000)/freq.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ WRTime: ", ((wrtime.QuadPart-pData->wrtime.QuadPart)*1000)/freq.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ WCTime: ", ((wctime.QuadPart-pData->wctime.QuadPart)*1000)/freq.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ WSCnt: ", wscnt.QuadPart -pData->wscnt.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ WCCnt: ", wccnt.QuadPart -pData->wccnt.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ RCTime: ", ((rrtime.QuadPart-pData->rrtime.QuadPart)*1000)/freq.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ RSCnt: ", rscnt.QuadPart -pData->rscnt.QuadPart);
|
|
Message->Display("%s%I64d", "^^^^ RCCnt: ", rccnt.QuadPart -pData->rccnt.QuadPart);
|
|
|
|
pData->wwtime = wwtime;
|
|
pData->wrtime = wrtime;
|
|
pData->wctime = wctime;
|
|
pData->wscnt = wscnt;
|
|
pData->wccnt = wccnt;
|
|
pData->rrtime = rrtime;
|
|
pData->rscnt = rscnt;
|
|
pData->rccnt = rccnt;
|
|
|
|
Message->Display("%s%s", "VVVV ", Title);
|
|
QueryPerformanceCounter(&pData->t1);
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID
|
|
FAT_NTFS::Construct (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructs a FAT_NTFS object
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
_FatSa = NULL;
|
|
_Drive = NULL;
|
|
_Message = NULL;
|
|
_FileNameBuffer = NULL;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FAT_NTFS::Destroy (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys a FAT_NTFS object
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DELETE( _FatSa );
|
|
DELETE( _Drive );
|
|
DELETE( _Message );
|
|
DELETE( _FileNameBuffer );
|
|
}
|
|
|
|
|
|
|
|
FAT_NTFS::~FAT_NTFS (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for FAT_NTFS.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::Initialize(
|
|
IN OUT PLOG_IO_DP_DRIVE Drive,
|
|
IN OUT PREAL_FAT_SA FatSa,
|
|
IN PCWSTRING CvtZoneFileName,
|
|
IN OUT PMESSAGE Message,
|
|
IN ULONG Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a FAT_NTFS object
|
|
|
|
Arguments:
|
|
|
|
FatVol - Supplies the FAT volume
|
|
FatSa - Supplies pointer to FAT superarea
|
|
CvtZoneFileName - Supplies the name of the convert zone.
|
|
Message - Supplies message object
|
|
Flags - Supplies convert flags
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if successfully initialized, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG fat_cluster_size;
|
|
LCN cvt_zone_in_ntfs;
|
|
BIG_INT cvt_zone_size_in_ntfs;
|
|
|
|
DebugPtrAssert( Drive );
|
|
DebugPtrAssert( FatSa );
|
|
DebugPtrAssert( Message );
|
|
|
|
//
|
|
// Initialize the stuff passed as argument
|
|
//
|
|
_FatSa = FatSa;
|
|
_Drive = Drive;
|
|
_Message = Message;
|
|
_Flags = Flags;
|
|
|
|
// Floppies cannot be converted to NTFS.
|
|
//
|
|
if( _Drive->IsFloppy() ) {
|
|
|
|
Message->Set(MSG_NTFS_FORMAT_NO_FLOPPIES);
|
|
Message->Display();
|
|
return FALSE;
|
|
}
|
|
|
|
// To protect ourselves from disk drivers that cannot
|
|
// correctly determine the disk geometry, compare the
|
|
// boot-code-critical values from the existing BPB with
|
|
// the drive's values. If they don't match, we can't
|
|
// convert this drive because if it's the system partition,
|
|
// the system won't boot.
|
|
//
|
|
if( !CheckGeometryMatch( ) ) {
|
|
|
|
_Message->Set( MSG_CONV_GEOMETRY_MISMATCH );
|
|
_Message->Display( "%s", "NTFS" );
|
|
return FALSE;
|
|
}
|
|
|
|
fat_cluster_size = FatSa->QuerySectorsPerCluster() * _Drive->QuerySectorSize();
|
|
|
|
//
|
|
// For volumes that are not data aligned, make cluster size
|
|
// equals sector size.
|
|
// For volumes that are data aligned, preserve the cluster size
|
|
//
|
|
if (FatSa->IsVolumeDataAligned()) {
|
|
_ClusterFactor = min(FatSa->QuerySectorsPerCluster(),
|
|
MAX_NTFS_CLUSTER_SIZE/_Drive->QuerySectorSize());
|
|
_ClusterRatio = max(1, fat_cluster_size/MAX_NTFS_CLUSTER_SIZE);
|
|
} else {
|
|
_ClusterFactor = 1;
|
|
_ClusterRatio = FatSa->QuerySectorsPerCluster();
|
|
}
|
|
|
|
|
|
if (SMALL_FRS_SIZE >= Drive->QuerySectorSize())
|
|
_FrsSize = SMALL_FRS_SIZE;
|
|
else
|
|
_FrsSize = Drive->QuerySectorSize();
|
|
|
|
// Set the default number of clusters per Index Allocation Buffer
|
|
// to a useful value.
|
|
//
|
|
_ClustersPerIndexBuffer = NTFS_SA::QueryDefaultClustersPerIndexBuffer(
|
|
_Drive, _ClusterFactor);
|
|
|
|
//
|
|
// _NumberOfFiles and _NumberOfDirectories are used to extend
|
|
// the MFT when we know how many files there are in the volume.
|
|
// Unless we do a volume census these values must be zero.
|
|
//
|
|
_NumberOfFiles = 0;
|
|
_NumberOfDirectories = 0;
|
|
|
|
if ( CvtZoneFileName->QueryChCount() ) {
|
|
|
|
PFATDIR RootDir;
|
|
FAT_DIRENT CvtZoneFileEntry;
|
|
|
|
//
|
|
// Get the root directory
|
|
//
|
|
RootDir = (PFATDIR)_FatSa->GetRootDir();
|
|
if ( !RootDir ) {
|
|
RootDir = (PFATDIR)_FatSa->GetFileDir();
|
|
}
|
|
|
|
DebugPtrAssert( RootDir );
|
|
|
|
//
|
|
// Locate the convert zone file. If it exists then remember its starting cluster
|
|
// number and it's size.
|
|
//
|
|
// The starting cluster of the convert zone is remembered because it is used
|
|
// later on for identifying the convert zone file while traversing the root directory.
|
|
//
|
|
|
|
if ( CvtZoneFileEntry.Initialize( RootDir->SearchForDirEntry( CvtZoneFileName ),
|
|
FatSa->GetFileDir() ? FAT_TYPE_FAT32 : FAT_TYPE_EAS_OKAY )) {
|
|
|
|
if (CvtZoneFileEntry.IsDirectory()) {
|
|
Message->Set(MSG_CONV_CVTAREA_MUST_BE_FILE, ERROR_MESSAGE);
|
|
Message->Display("%W", CvtZoneFileName);
|
|
return FALSE;
|
|
}
|
|
_CvtZoneFileFirstCluster = CvtZoneFileEntry.QueryStartingCluster();
|
|
_CvtZoneSize = (((BIG_INT)CvtZoneFileEntry.QueryFileSize() + fat_cluster_size - 1)/fat_cluster_size).GetLowPart();
|
|
|
|
if (!FatSa->IsFileContiguous(_CvtZoneFileFirstCluster)) {
|
|
Message->Set(MSG_CONV_CVTAREA_FILE_NOT_CONTIGUOUS, ERROR_MESSAGE);
|
|
Message->Display("%W", CvtZoneFileName);
|
|
return FALSE;
|
|
}
|
|
|
|
cvt_zone_in_ntfs = FatClusterToLcn(_CvtZoneFileFirstCluster)/_ClusterFactor;
|
|
cvt_zone_size_in_ntfs = _CvtZoneSize*_ClusterRatio;
|
|
} else {
|
|
Message->Set(MSG_CONV_CVTAREA_FILE_MISSING, ERROR_MESSAGE);
|
|
Message->Display("%W", CvtZoneFileName);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
_CvtZoneFileFirstCluster = 0;
|
|
_CvtZoneSize = 0;
|
|
cvt_zone_in_ntfs = 0;
|
|
cvt_zone_size_in_ntfs = 0;
|
|
}
|
|
|
|
DebugAssert(cvt_zone_in_ntfs.GetHighPart() == 0);
|
|
DebugAssert(cvt_zone_size_in_ntfs.GetHighPart() == 0);
|
|
|
|
// Allocate space for the file name attribute buffer and initialize
|
|
// the NTFS superarea and the Bad LCN stack.
|
|
//
|
|
|
|
if ( (
|
|
|
|
_FileNameBuffer =
|
|
(PFILE_NAME)MALLOC( sizeof(FILE_NAME) +
|
|
sizeof(WCHAR) * NAMEBUFFERSIZE )) == NULL ||
|
|
!_RootIndexName.Initialize( FileNameIndexNameData ) ||
|
|
!_NtfsSa.Initialize( _Drive, _Message,
|
|
cvt_zone_in_ntfs,
|
|
cvt_zone_size_in_ntfs ) ||
|
|
!_BadLcn.Initialize() ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::Convert(
|
|
OUT PCONVERT_STATUS Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a FAT volume to NTFS
|
|
|
|
Arguments:
|
|
|
|
Status - Supplies pointer to conversion status
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if successfully converted, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Converted;
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
PERF_DATA pdata;
|
|
|
|
pdata.first_call = TRUE;
|
|
#endif
|
|
|
|
DebugPtrAssert( Status );
|
|
|
|
#if defined ( _AUTOCONV_ )
|
|
|
|
_Message->Set( MSG_CONV_WILL_REBOOT );
|
|
_Message->Display();
|
|
|
|
#endif // _AUTOCONV_
|
|
|
|
//
|
|
// The conversion from FAT to NTFS consists of a sequence of well-defined
|
|
// steps:
|
|
//
|
|
// 1.- Create holes (i.e. relocate FAT clusters) for fixed-location
|
|
// NTFS structures and save FAT.
|
|
//
|
|
// 2.- Create NTFS elementary data structures in FAT free space
|
|
//
|
|
// 3.- Convert the File system, creating the NTFS file system in
|
|
// the FAT free space.
|
|
//
|
|
// 4.- Mark as free in the NTFS bitmap those NTFS clusters being used
|
|
// by FAT-specific structures.
|
|
//
|
|
// 5.- Write NTFS boot sector
|
|
//
|
|
//
|
|
// Since a crash can occur at any time, we must minimize the chance of
|
|
// disk corruption. Note that (almost) all writes are to FAT free space,
|
|
// so a crash will preserve the FAT intact.
|
|
//
|
|
// The only times at which we write to non-free space, i.e. the times at
|
|
// which a crash might cause problems are:
|
|
//
|
|
// a.- At the end of step 1, when we overwrite the FAT. The algorithm
|
|
// for relocating clusters (in UFAT) guarantees that CHKDSK will be
|
|
// able to fix the disk without any loss of data.
|
|
//
|
|
// b.- In step 5, while writting the boot sector. If a crash occurs during
|
|
// this step, We're out of luck.
|
|
//
|
|
//
|
|
|
|
Converted = (BOOLEAN)( //
|
|
// Create holes for fixed-location structures
|
|
//
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "CheckSpaceAndCreateHoles") &&
|
|
#endif
|
|
CheckSpaceAndCreateHoles( ) &&
|
|
//
|
|
// Initialize the bitmaps
|
|
//
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "CreateBitmaps") &&
|
|
#endif
|
|
CreateBitmaps( ) &&
|
|
//
|
|
// Create the NTFS elementary data structures
|
|
//
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "CreateElementary") &&
|
|
#endif
|
|
CreateElementary( ) &&
|
|
//
|
|
// Convert the file system
|
|
//
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "ConvertFileSystem") &&
|
|
#endif
|
|
ConvertFileSystem( ) &&
|
|
//
|
|
// Mark the reserved sectors as free
|
|
//
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "FreeReservedSectors") &&
|
|
#endif
|
|
FreeReservedSectors( ) &&
|
|
//
|
|
// Volume converted, write the boot code
|
|
//
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "WriteBoot") &&
|
|
#endif
|
|
WriteBoot( ) &&
|
|
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "Done") &&
|
|
#endif
|
|
(TRUE));
|
|
|
|
*Status = _Status;
|
|
|
|
return Converted;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::CheckSpaceAndCreateHoles (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines free space requirements, makes sure that there is
|
|
enough space for conversion, and makes holes. All this
|
|
is done in one step so that we only have to traverse the
|
|
file system once.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if there is enough space for conversion and
|
|
the holes are in place.
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
INTSTACK HoleStack; // Stack of holes
|
|
CENSUS_REPORT CensusReport; // Census report
|
|
PCENSUS_REPORT Census; // Pointer to census report
|
|
BIG_INT SectorsTotal; // Number of sectors on the volume
|
|
BIG_INT SectorsFree; // Free sectors on the volume
|
|
BIG_INT SectorsNeeded; // Sectors needed by conversion
|
|
BIG_INT KbytesTotal;
|
|
BIG_INT KbytesFree;
|
|
BIG_INT KbytesNeeded;
|
|
BOOLEAN Relocated; // TRUE if relocated sectors
|
|
|
|
|
|
#if !defined ( _AUTOCHECK_ )
|
|
|
|
if(_FatSa->QueryVolumeFlags() & (FAT_BPB_RESERVED_DIRTY | FAT_BPB_RESERVED_TEST_SURFACE)) {
|
|
//
|
|
// The volume dirty bit is set??? Need to CHKDSK first.
|
|
//
|
|
_Message->Set( MSG_CONV_DISK_IS_DIRTY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_DRIVE_IS_DIRTY;
|
|
return FALSE;
|
|
}
|
|
|
|
#endif // !_AUTOCHECK_
|
|
|
|
//
|
|
// Identify all the "holes" that we need i.e. all those spots
|
|
// that are used by NTFS structures that need to be at a fixed
|
|
// location.
|
|
//
|
|
//
|
|
if ( !QueryNeededHoles( &HoleStack ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
SectorsTotal = _FatSa->QueryVirtualSectors();
|
|
SectorsFree = _FatSa->QueryFreeSectors();
|
|
// Census = ( SectorsFree > ( SectorsTotal / 2 ) ) ? NULL : &CensusReport;
|
|
Census = &CensusReport;
|
|
Relocated = FALSE;
|
|
|
|
//
|
|
// Create the holes and obtain the census if necessary
|
|
//
|
|
_Message->Set( MSG_CONV_CHECKING_SPACE );
|
|
_Message->Display();
|
|
|
|
if ( !_FatSa->QueryCensusAndRelocate( Census, &HoleStack, &Relocated )) {
|
|
|
|
_Message->Set( MSG_CONV_NO_DISK_SPACE, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_INSUFFICIENT_FREE_SPACE;
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined ( _AUTOCONV_ )
|
|
|
|
//
|
|
// If relocated sectors, then we will be overwritting data that might
|
|
// be needed by the system. In order to avoid this, we reboot so that
|
|
// the system will read its stuff from the new locations.
|
|
//
|
|
if ( Relocated ) {
|
|
|
|
_Drive->FlushCache();
|
|
|
|
_Message->Set( MSG_CONV_REBOOT_AFTER_RELOCATION );
|
|
_Message->Display();
|
|
|
|
IFS_SYSTEM::Reboot();
|
|
//
|
|
// If we reach this point, the reboot failed and we should not
|
|
// continue the conversion.
|
|
//
|
|
_Message->Set( MSG_CONV_CANNOT_RELOCATE, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
//
|
|
// Determine the number of sectors needed for the conversion
|
|
//
|
|
if ( Census ) {
|
|
|
|
//
|
|
// Estimate the number of sectors needed based on the
|
|
// volume census.
|
|
//
|
|
QuerySectorsNeededForConversion( Census, &SectorsNeeded );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We'll say that we need half of the disk
|
|
//
|
|
SectorsNeeded = SectorsTotal / 2;
|
|
}
|
|
|
|
//
|
|
// Take into account that the convert zone file is actually a free
|
|
// space for NTFS to use to put system files
|
|
//
|
|
SectorsFree += (_CvtZoneSize * _FatSa->QuerySectorsPerCluster());
|
|
|
|
KbytesTotal = SectorsTotal * _Drive->QuerySectorSize() / 1024;
|
|
KbytesFree = SectorsFree * _Drive->QuerySectorSize() / 1024;
|
|
KbytesNeeded = SectorsNeeded * _Drive->QuerySectorSize() / 1024;
|
|
|
|
_Message->Set( MSG_CONV_KBYTES_TOTAL );
|
|
_Message->Display( "%8d", KbytesTotal.GetLowPart() );
|
|
_Message->Set( MSG_CONV_KBYTES_FREE );
|
|
_Message->Display( "%8d", KbytesFree.GetLowPart() );
|
|
_Message->Set( MSG_CONV_KBYTES_NEEDED );
|
|
_Message->Display( "%8d", KbytesNeeded.GetLowPart() );
|
|
|
|
|
|
if ( SectorsFree < SectorsNeeded ) {
|
|
//
|
|
// Not enough disk space for conversion
|
|
//
|
|
_Message->Set( MSG_CONV_NO_DISK_SPACE, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_INSUFFICIENT_FREE_SPACE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// The disk has enough disk space.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertRoot (
|
|
IN PFATDIR Directory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the FAT root directory and recursively all its
|
|
subdirectories.
|
|
|
|
This is basically the same as the ConvertDirectory method, the
|
|
differences being:
|
|
|
|
1.- The index for the root has already been created by
|
|
NTFS_SA::CreateElementaryStructures()
|
|
|
|
2.- No FRS is created
|
|
|
|
3.- ConvertRoot does some extra checkings for
|
|
EA file (which is at the root level only).
|
|
|
|
|
|
Arguments:
|
|
|
|
Directory - Supplies root directory.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if root directory successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
NTFS_FILE_RECORD_SEGMENT FrsOfRootIndex; // FRS of NTFS root index
|
|
NTFS_INDEX_TREE RootIndex; // Root index
|
|
BOOLEAN Converted;
|
|
|
|
|
|
DebugPtrAssert( Directory );
|
|
|
|
_Level = 0;
|
|
|
|
//
|
|
// Obtain the NTFS root index
|
|
//
|
|
if ( !FrsOfRootIndex.Initialize( ROOT_FILE_NAME_INDEX_NUMBER, &_Mft ) ||
|
|
!FrsOfRootIndex.Read() ||
|
|
!RootIndex.Initialize( _Drive,
|
|
_ClusterFactor,
|
|
&_VolumeBitmap,
|
|
FrsOfRootIndex.GetUpcaseTable(),
|
|
FrsOfRootIndex.QueryMaximumAttributeRecordSize()/2,
|
|
&FrsOfRootIndex,
|
|
&_RootIndexName )
|
|
) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_MAKE_INDEX, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Convert the directory
|
|
//
|
|
if ( Converted = ConvertDir( Directory, &RootIndex, &FrsOfRootIndex )) {
|
|
//
|
|
// Save the index
|
|
//
|
|
if ( !RootIndex.Save( &FrsOfRootIndex ) ||
|
|
!FrsOfRootIndex.Flush( &_VolumeBitmap ) ) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_WRITE, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
Converted = FALSE;
|
|
}
|
|
}
|
|
|
|
return Converted;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertDirectory (
|
|
IN PFATDIR Directory,
|
|
IN PFAT_DIRENT DirEntry,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT FrsDir
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a FAT directory and recursively all its subdirectories
|
|
|
|
Arguments:
|
|
|
|
Directory - Supplies directory to convert
|
|
DirEntry - Supplies the directory entry of the directory
|
|
FrsDir - Supplies pointer to FRS of directory
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if directory successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTFS_INDEX_TREE Index; // NTFS index
|
|
BOOLEAN Converted; // FALSE if error
|
|
ULONG DirSize; // Dir size
|
|
ULONG SectorsPerFatCluster; // Sectors per cluster
|
|
ULONG Cluster; // Dir cluster number
|
|
PFAT Fat; // Pointer to FAT
|
|
LCN Lcn; // LCN
|
|
|
|
|
|
DebugPtrAssert( Directory );
|
|
DebugPtrAssert( DirEntry );
|
|
DebugPtrAssert( FrsDir );
|
|
|
|
|
|
//
|
|
// Create an index for this directory:
|
|
//
|
|
if ( !Index.Initialize( $FILE_NAME,
|
|
_Drive,
|
|
_ClusterFactor,
|
|
&_VolumeBitmap,
|
|
FrsDir->GetUpcaseTable(),
|
|
COLLATION_FILE_NAME,
|
|
SMALL_INDEX_BUFFER_SIZE,
|
|
FrsDir->QueryMaximumAttributeRecordSize() /2,
|
|
&_RootIndexName
|
|
)
|
|
) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
_Level++;
|
|
|
|
//
|
|
// Convert the directory.
|
|
//
|
|
if ( Converted = ConvertDir( Directory, &Index, FrsDir ) ) {
|
|
|
|
//
|
|
// If the directory has extended attributes, convert them.
|
|
//
|
|
// Then save the index.
|
|
//
|
|
if ( Converted = (BOOLEAN)( !DirEntry->QueryEaHandle() ||
|
|
ConvertExtendedAttributes( DirEntry, FrsDir ) ) ) {
|
|
|
|
|
|
//
|
|
// Mark the sectors used by this directory in the reserved
|
|
// bitmap.
|
|
//
|
|
DirSize = DirEntry->QueryFileSize();
|
|
SectorsPerFatCluster = _FatSa->QuerySectorsPerCluster();
|
|
Cluster = DirEntry->QueryStartingCluster();
|
|
Fat = _FatSa->GetFat();
|
|
|
|
while ( TRUE ) {
|
|
|
|
Lcn = FatClusterToLcn( Cluster )/_ClusterFactor;
|
|
|
|
_ReservedBitmap.SetAllocated( Lcn, _ClusterRatio );
|
|
|
|
if ( Fat->IsEndOfChain( Cluster )) {
|
|
break;
|
|
}
|
|
|
|
Cluster = Fat->QueryEntry( Cluster );
|
|
}
|
|
|
|
|
|
//
|
|
// Save the index for this directory
|
|
//
|
|
if ( !( Converted = (Index.Save( FrsDir ) ) ) ) {
|
|
_Message->Set( MSG_CONV_CANNOT_WRITE, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
_Level--;
|
|
|
|
return Converted;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertDir (
|
|
IN PFATDIR Directory,
|
|
IN OUT PNTFS_INDEX_TREE Index,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT FrsDir
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a FAT directory and recursively all its subdirectories
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if directory successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
FAT_DIRENT Entry; // Directory entry
|
|
HMEM HMem; // Memory
|
|
DSTRING DirName; // This dir's name
|
|
DSTRING LongName; // the associated long name
|
|
BOOLEAN UseLongName; // make one name attrib
|
|
STANDARD_INFORMATION StandardInformation;// Std. Info
|
|
ULONG EntryNumber; // Entry number counter
|
|
BOOLEAN Converted; // FALSE if error
|
|
NTFS_FILE_RECORD_SEGMENT Frs; // FRS of each entry
|
|
VCN FileNumber; // File Number of child.
|
|
USHORT FrsFlags;
|
|
PVOID DirEntry;
|
|
FILEDIR SubDir;
|
|
BOOLEAN HasLongName;
|
|
UCHAR FatType;
|
|
CANNED_SECURITY_TYPE Sd;
|
|
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
PERF_DATA pdata;
|
|
#endif
|
|
|
|
Converted = TRUE;
|
|
EntryNumber = 0;
|
|
|
|
if (_FatSa->GetFileDir())
|
|
FatType = FAT_TYPE_FAT32;
|
|
else
|
|
FatType = FAT_TYPE_EAS_OKAY;
|
|
|
|
DebugPtrAssert( Directory );
|
|
DebugPtrAssert( Index );
|
|
DebugPtrAssert( FrsDir );
|
|
|
|
|
|
//
|
|
// Traverse the directory, converting all its entries.
|
|
//
|
|
while ( Converted ) {
|
|
|
|
//
|
|
// Get next directory entry
|
|
//
|
|
if ( !(DirEntry = Directory->GetDirEntry( EntryNumber ))) {
|
|
break;
|
|
}
|
|
if ( !Entry.Initialize( DirEntry, FatType))
|
|
{
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
Converted = FALSE;
|
|
DebugAssert(FALSE);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If end of directory, get out
|
|
//
|
|
if ( Entry.IsEndOfDirectory() ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Ignore the deleted, the "parent" and the "self" entries, the
|
|
// volume label and the EA file.
|
|
//
|
|
if (( Entry.IsErased() ||
|
|
Entry.IsDot() ||
|
|
Entry.IsDotDot() ||
|
|
Entry.IsVolumeLabel() ||
|
|
Entry.IsLongEntry() ||
|
|
(_CvtZoneFileFirstCluster != 0 &&
|
|
Entry.QueryStartingCluster() == _CvtZoneFileFirstCluster) ||
|
|
(_EAFileFirstCluster != 0 &&
|
|
Entry.QueryStartingCluster() == _EAFileFirstCluster) ) ) {
|
|
|
|
EntryNumber++;
|
|
continue;
|
|
}
|
|
|
|
// Fill in the standard information for this file or
|
|
// directory. Note that NTFS stores Universal Time,
|
|
// whereas FAT stores Local Time, so the time has to
|
|
// be converted.
|
|
//
|
|
|
|
LARGE_INTEGER FatTime, NtfsTime;
|
|
|
|
Entry.QueryLastWriteTime( &FatTime );
|
|
RtlLocalTimeToSystemTime( &FatTime, &NtfsTime );
|
|
|
|
StandardInformation.LastModificationTime = NtfsTime;
|
|
StandardInformation.LastChangeTime = NtfsTime;
|
|
|
|
if (Entry.IsValidCreationTime()) {
|
|
|
|
Entry.QueryCreationTime( &FatTime );
|
|
RtlLocalTimeToSystemTime( &FatTime, &NtfsTime );
|
|
StandardInformation.CreationTime = NtfsTime;
|
|
} else {
|
|
|
|
StandardInformation.CreationTime = StandardInformation.LastChangeTime;
|
|
}
|
|
|
|
if (Entry.IsValidLastAccessTime()) {
|
|
|
|
Entry.QueryLastAccessTime( &FatTime );
|
|
RtlLocalTimeToSystemTime( &FatTime, &NtfsTime );
|
|
StandardInformation.LastAccessTime = NtfsTime;
|
|
} else {
|
|
|
|
StandardInformation.LastAccessTime = StandardInformation.LastChangeTime;
|
|
}
|
|
|
|
StandardInformation.FileAttributes = Entry.QueryAttributeByte();
|
|
|
|
|
|
//
|
|
// Get the WSTR name of the entry and fill in the FILE_NAME
|
|
// structure for the file name attribute. If this entry
|
|
// does not have an associated Long File Name, then its
|
|
// name is both a valid DOS and NTFS name; if there is an
|
|
// associated Long File Name, then the name is the DOS name
|
|
// and the long name is the NTFS name.
|
|
//
|
|
// If the long name is identical to the short name, ignore
|
|
// the long name.
|
|
//
|
|
Entry.QueryName( &DirName );
|
|
|
|
if( !Directory->QueryLongName( EntryNumber, &LongName ) ) {
|
|
|
|
DebugPrintTrace(( "CUFAT: QueryLongName failed.\n" ));
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
Converted = FALSE;
|
|
break;
|
|
}
|
|
|
|
HasLongName = (LongName.QueryChCount() != 0);
|
|
|
|
//
|
|
// If the long name is only a casewise permutation of the
|
|
// short name, use the long name as the single ntfs name
|
|
// attribute.
|
|
//
|
|
|
|
UseLongName = (HasLongName &&
|
|
0 == NtfsUpcaseCompare( DirName.GetWSTR(),
|
|
DirName.QueryChCount(),
|
|
LongName.GetWSTR(),
|
|
LongName.QueryChCount(),
|
|
FrsDir->GetUpcaseTable(),
|
|
FALSE ));
|
|
|
|
|
|
_FileNameBuffer->ParentDirectory = FrsDir->QuerySegmentReference();
|
|
_FileNameBuffer->FileNameLength = (unsigned char)DirName.QueryChCount();
|
|
if (UseLongName) {
|
|
|
|
_FileNameBuffer->Flags = FILE_NAME_NTFS | FILE_NAME_DOS;
|
|
|
|
if ( !LongName.QueryWSTR( 0, TO_END, NtfsFileNameGetName(_FileNameBuffer), NAMEBUFFERSIZE ) ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
Converted = FALSE;
|
|
DebugAssert(FALSE);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
_FileNameBuffer->Flags = HasLongName ?
|
|
FILE_NAME_DOS :
|
|
FILE_NAME_NTFS | FILE_NAME_DOS;
|
|
|
|
if ( !DirName.QueryWSTR( 0, TO_END, NtfsFileNameGetName(_FileNameBuffer), NAMEBUFFERSIZE ) ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
Converted = FALSE;
|
|
DebugAssert(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Allocate and create the FRS for this file or directory,
|
|
// add its file name, and add an appropriate entry to the
|
|
// index.
|
|
//
|
|
FrsFlags = (Entry.IsDirectory()) ? (USHORT)FILE_FILE_NAME_INDEX_PRESENT : (USHORT)0;
|
|
|
|
if (_Flags & CONVERT_NOSECURITY_FLAG) {
|
|
Sd = Entry.IsDirectory() ? EditWorldCannedDirSd : EditWorldCannedFileSd;
|
|
} else {
|
|
Sd = Entry.IsDirectory() ? NoAclCannedSd : NoAclCannedFileSd;
|
|
}
|
|
|
|
if ( !_Mft.AllocateFileRecordSegment( &FileNumber, FALSE ) ||
|
|
!Frs.Initialize( FileNumber, &_Mft ) ||
|
|
!Frs.Create( &StandardInformation, FrsFlags ) ||
|
|
!Frs.AddFileNameAttribute( _FileNameBuffer ) ||
|
|
!Frs.AddSecurityDescriptor( Sd, &_VolumeBitmap ) ||
|
|
!Index->InsertEntry( NtfsFileNameGetLength( _FileNameBuffer ),
|
|
_FileNameBuffer,
|
|
Frs.QuerySegmentReference() ) ) {
|
|
|
|
DebugPrint( "Can't create FRS in ConvertDirectory.\n" );
|
|
Converted = FALSE;
|
|
break;
|
|
}
|
|
|
|
// If the file has separate long name, add that entry to the FRS
|
|
// and the index.
|
|
//
|
|
if( HasLongName && !UseLongName ) {
|
|
|
|
|
|
_FileNameBuffer->ParentDirectory = FrsDir->QuerySegmentReference();
|
|
_FileNameBuffer->FileNameLength = (unsigned char)LongName.QueryChCount();
|
|
_FileNameBuffer->Flags = FILE_NAME_NTFS;
|
|
|
|
if ( !LongName.QueryWSTR( 0, TO_END, NtfsFileNameGetName(_FileNameBuffer), NAMEBUFFERSIZE ) ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
Converted = FALSE;
|
|
DebugAssert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if ( !Frs.AddFileNameAttribute( _FileNameBuffer ) ||
|
|
!Index->InsertEntry( NtfsFileNameGetLength( _FileNameBuffer ),
|
|
_FileNameBuffer,
|
|
Frs.QuerySegmentReference() ) ) {
|
|
|
|
DebugPrint( "Can't create FRS in ConvertDirectory.\n" );
|
|
Converted = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( _Flags & CONVERT_VERBOSE_FLAG ) {
|
|
STATIC CHAR NameDisplayBuffer[128];
|
|
ULONG NameStart = _Level * 4;
|
|
PWSTRING Name;
|
|
|
|
Name = (HasLongName ? &LongName : &DirName);
|
|
|
|
memset(NameDisplayBuffer, ' ', NameStart);
|
|
Name->QuerySTR( 0, TO_END, NameDisplayBuffer + NameStart,
|
|
128 - Name->QueryByteCount() );
|
|
NameDisplayBuffer[NameStart + Name->QueryByteCount()] = 0;
|
|
_Message->Set( MSG_ONE_STRING );
|
|
_Message->Display( "%s", NameDisplayBuffer );
|
|
}
|
|
|
|
//
|
|
// Determine if the entry is a directory or a file, and proccess it
|
|
// accordingly.
|
|
//
|
|
if ( Entry.IsDirectory() ) {
|
|
|
|
//
|
|
// Directory
|
|
//
|
|
//
|
|
// Convert the directory (and all its subdirectories)
|
|
//
|
|
|
|
|
|
if ( !HMem.Initialize() ||
|
|
!SubDir.Initialize( &HMem,
|
|
_Drive,
|
|
_FatSa,
|
|
_FatSa->GetFat(),
|
|
Entry.QueryStartingCluster() ) ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !SubDir.Read() ) {
|
|
_Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
pdata.first_call = TRUE;
|
|
CheckTime(_Message, _Drive, &pdata, "ConvertDirectory");
|
|
_Message->Set(MSG_CHK_NTFS_MESSAGE);
|
|
_Message->Display("%s%W", "VVVV Dir: ", &DirName);
|
|
#endif
|
|
if ( !ConvertDirectory( &SubDir, &Entry, &Frs ) ||
|
|
!Frs.Flush( &_VolumeBitmap, Index ) ) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CONVERT_DIRECTORY );
|
|
_Message->Display( "%W", &DirName );
|
|
Converted = FALSE;
|
|
break;
|
|
}
|
|
#if defined(CONVERT_PERF_COUNTERS)
|
|
CheckTime(_Message, _Drive, &pdata, "ConvertDirectory Done");
|
|
#endif
|
|
|
|
} else {
|
|
|
|
//
|
|
// File
|
|
//
|
|
DebugAssert( !Entry.IsVolumeLabel() );
|
|
DebugAssert( !Entry.IsLongEntry() );
|
|
DebugAssert( !_EAFileFirstCluster ||
|
|
(Entry.QueryStartingCluster() != _EAFileFirstCluster) );
|
|
|
|
//
|
|
// Convert the file.
|
|
//
|
|
if ( !ConvertFile( &Entry, &Frs ) ||
|
|
!Frs.Flush( &_VolumeBitmap, Index ) ) {
|
|
|
|
Converted = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
EntryNumber++;
|
|
}
|
|
|
|
|
|
return Converted;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertExtendedAttributes (
|
|
IN PFAT_DIRENT Dirent,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the extended attributes of a FAT directory entry.
|
|
|
|
Arguments:
|
|
|
|
Dirent - Supplies the directory entry
|
|
Frs - Supplies the file's FRS
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if extended attributes converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT EaHandle;
|
|
|
|
DebugPtrAssert( Dirent );
|
|
DebugPtrAssert( Frs );
|
|
|
|
EaHandle = Dirent->QueryEaHandle();
|
|
|
|
//
|
|
// If this entry has extended attributes, convert them
|
|
//
|
|
if ( EaHandle ) {
|
|
|
|
//
|
|
// Make sure that there is an EA file
|
|
//
|
|
if ( _EAFileFirstCluster == 0 ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_EA_FILE, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Convert the attributes
|
|
//
|
|
return ConvertExtendedAttributes( Frs,
|
|
EaHandle );
|
|
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertExtendedAttributes (
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT Frs,
|
|
IN USHORT EaHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the extended attributes of a FAT directory entry.
|
|
|
|
Arguments:
|
|
|
|
Frs - Supplies the file's FRS
|
|
Eahandle - Supplies the EA handle
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if extended attributes converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
EA_INFORMATION EaInformation;
|
|
NTFS_ATTRIBUTE EaInformationAttribute;
|
|
NTFS_ATTRIBUTE EaDataAttribute;
|
|
|
|
EA_SET EaSet; // Extended attribute set
|
|
HMEM Mem; // Memory
|
|
ULONG Index; // EA Index
|
|
PEA Ea; // Pointer to EA
|
|
ULONG Cluster; // EA set cluster number
|
|
PBYTE UnpackedEaList;
|
|
ULONG PackedEaLength, UnpackedEaLength, PackedListLength,
|
|
UnpackedListLength, NeedEaCount, TargetOffset;
|
|
|
|
|
|
//
|
|
// Read in the EA set
|
|
//
|
|
Cluster = (_FatSa->GetFat())->QueryNthCluster( _EAFileFirstCluster,
|
|
_EAHeader.QueryEaSetClusterNumber( EaHandle ));
|
|
|
|
if ( !Mem.Initialize() ||
|
|
!EaSet.Initialize( &Mem,
|
|
_Drive,
|
|
_FatSa,
|
|
_FatSa->GetFat(),
|
|
Cluster )
|
|
) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !EaSet.Read() ) {
|
|
_Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Walk the list to determine the packed and unpacked length. The
|
|
// packed list is simply all the EA's concatenated together, so its
|
|
// length is the sum of the individual lengths. The unpacked list
|
|
// consists of a set of entries which in turn consist of a ULONG
|
|
// and a DWORD-aligned EA, so it length is a bit more complex to
|
|
// compute.
|
|
//
|
|
Index = 0;
|
|
PackedListLength = 0;
|
|
UnpackedListLength = 0;
|
|
NeedEaCount = 0;
|
|
|
|
while( (Ea = EaSet.GetEa( Index++ )) != NULL ) {
|
|
|
|
PackedEaLength = sizeof( EA ) + Ea->NameSize +
|
|
*(USHORT UNALIGNED *)Ea->ValueSize;
|
|
|
|
UnpackedEaLength = sizeof(ULONG) + DwordAlign( PackedEaLength );
|
|
|
|
PackedListLength += PackedEaLength;
|
|
UnpackedListLength += UnpackedEaLength;
|
|
|
|
if( Ea->Flag & NeedFlag ) {
|
|
|
|
NeedEaCount += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to hold the unpacked list.
|
|
//
|
|
if( (UnpackedEaList = (PBYTE)MALLOC( (unsigned int)UnpackedListLength )) == NULL ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
memset( UnpackedEaList, 0, (unsigned int)UnpackedListLength );
|
|
|
|
//
|
|
// Walk the list again, copying EA's into the packed list buffer.
|
|
//
|
|
Index = 0;
|
|
TargetOffset = 0;
|
|
|
|
while( (Ea = EaSet.GetEa( Index++ )) != NULL ) {
|
|
|
|
PackedEaLength = sizeof( EA ) + Ea->NameSize +
|
|
*(USHORT UNALIGNED *)Ea->ValueSize;
|
|
|
|
UnpackedEaLength = sizeof(ULONG) + DwordAlign( PackedEaLength );
|
|
|
|
memcpy( UnpackedEaList + TargetOffset,
|
|
&UnpackedEaLength,
|
|
sizeof( ULONG ) );
|
|
|
|
memcpy( UnpackedEaList + TargetOffset + sizeof( ULONG ),
|
|
Ea,
|
|
(unsigned int)PackedEaLength );
|
|
|
|
TargetOffset += UnpackedEaLength;
|
|
}
|
|
|
|
// Create the EA Information Attribute--fill in the fields of
|
|
// the EA information structure, put it into a resident attribute
|
|
// of type $EA_INFORMATION, and insert the attribute into the file.
|
|
//
|
|
EaInformation.PackedEaSize = (unsigned short)PackedListLength;
|
|
EaInformation.NeedEaCount = (unsigned short)NeedEaCount;
|
|
EaInformation.UnpackedEaSize = UnpackedListLength;
|
|
|
|
if( !EaInformationAttribute.Initialize( _Drive,
|
|
_Mft.QueryClusterFactor(),
|
|
&EaInformation,
|
|
sizeof( EA_INFORMATION ),
|
|
$EA_INFORMATION,
|
|
NULL,
|
|
0 ) ||
|
|
!EaInformationAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
|
|
|
|
FREE( UnpackedEaList );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set up the Ea Data attribute. Start out with it resident; if
|
|
// it doesn't fit into the FRS, make it nonresident.
|
|
//
|
|
if( !EaDataAttribute.Initialize( _Drive,
|
|
_Mft.QueryClusterFactor(),
|
|
UnpackedEaList,
|
|
UnpackedListLength,
|
|
$EA_DATA,
|
|
NULL,
|
|
0 ) ) {
|
|
|
|
DebugPrint( "Cannot initialize resident attribute for EA List.\n" );
|
|
FREE( UnpackedEaList );
|
|
return FALSE;
|
|
}
|
|
|
|
if( !EaDataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
|
|
|
|
// Couldn't insert it in resident form; make it nonresident.
|
|
|
|
if( !EaDataAttribute.MakeNonresident( &_VolumeBitmap ) ||
|
|
!EaDataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
|
|
|
|
// Can't insert it.
|
|
|
|
FREE( UnpackedEaList );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// All the EAs have been converted
|
|
//
|
|
FREE( UnpackedEaList );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertFile (
|
|
IN PFAT_DIRENT Dirent,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT File
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a file from FAT to NTFS
|
|
|
|
Arguments:
|
|
|
|
Dirent - Supplies the directory entry of the file to convert
|
|
ParentFrs - Supplies the FRS of the directory which contains this file
|
|
File - Supplies pointer to FRS of file
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if file successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DSTRING FileName; // File name
|
|
|
|
DebugPtrAssert( Dirent );
|
|
DebugPtrAssert( File );
|
|
|
|
Dirent->QueryName( &FileName );
|
|
|
|
//
|
|
// Convert the file data and extended attributes.
|
|
//
|
|
if ( !ConvertFileData( Dirent, File ) ||
|
|
!( !Dirent->QueryEaHandle() ||
|
|
ConvertExtendedAttributes( Dirent, File ))
|
|
) {
|
|
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CONVERT_FILE, ERROR_MESSAGE );
|
|
_Message->Display( "%W", &FileName );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// File converted
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertFileData (
|
|
IN PFAT_DIRENT Dirent,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the file data from FAT to NTFS
|
|
|
|
Arguments:
|
|
|
|
Dirent - Supplies the directory entry of the file to convert
|
|
Frs - Supplies the FRS for the file
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if file data successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG FileSize; // File size
|
|
ULONG SectorsPerFatCluster; // Sectors per cluster
|
|
|
|
DebugPtrAssert( Dirent );
|
|
DebugPtrAssert( Frs );
|
|
|
|
//
|
|
// Get the file size
|
|
//
|
|
FileSize = Dirent->QueryFileSize();
|
|
SectorsPerFatCluster = _FatSa->QuerySectorsPerCluster();
|
|
|
|
//
|
|
// If the data is small enough to fit in the FRS, we make it resident and free
|
|
// the cluster it occupies, otherwise we reuse its allocation and make it
|
|
// non-resident.
|
|
//
|
|
// Note that we only make the data resident if it is less than one FAT cluster
|
|
// long.
|
|
//
|
|
if ( ( Frs->QueryFreeSpace() > (FileSize + SIZE_OF_RESIDENT_HEADER ) ) &&
|
|
( FileSize <= (SectorsPerFatCluster * _Drive->QuerySectorSize( )) )
|
|
) {
|
|
|
|
return ConvertFileDataResident( Dirent, Frs );
|
|
|
|
} else {
|
|
|
|
DebugAssert( FileSize > 0 );
|
|
|
|
return ConvertFileDataNonResident( Dirent, Frs );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertFileDataNonResident (
|
|
IN PFAT_DIRENT Dirent,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the file data from FAT to NTFS in nonresident form
|
|
|
|
Arguments:
|
|
|
|
Dirent - Supplies the directory entry of the file to convert
|
|
Frs - Supplies the FRS for the file
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if file data successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Cluster; // File cluster number
|
|
NTFS_ATTRIBUTE DataAttribute; // File's $DATA attribute
|
|
NTFS_EXTENT_LIST ExtentList; // NTFS extent list
|
|
ULONG Length; // Length of extent
|
|
ULONG FileSize; // File size
|
|
ULONG ClusterSize; // NTFS cluster size
|
|
ULONG ClustersLeft; // NTFS clusters left to convert
|
|
ULONG SectorsPerCluster; // Sectors per FAT cluster
|
|
VCN Vcn; // VCN
|
|
LCN Lcn; // LCN
|
|
PFAT Fat; // Pointer to FAT
|
|
|
|
DebugPtrAssert( Dirent );
|
|
DebugPtrAssert( Frs );
|
|
|
|
//
|
|
// Get the file size
|
|
//
|
|
FileSize = Dirent->QueryFileSize();
|
|
SectorsPerCluster = _FatSa->QuerySectorsPerCluster();
|
|
|
|
DebugAssert( FileSize > 0 );
|
|
|
|
//
|
|
// First we generate an extent list mapping the file's data
|
|
// allocation. We just add all the clusters in the file as
|
|
// extents of size SectorsPerCluster. Note that we don't
|
|
// have to do anything special about consecutive clusters
|
|
// (the Extent List coallesces them for us).
|
|
//
|
|
// If there are unused sectors in the last cluster, we mark
|
|
// them in the ReservedBitmap.
|
|
//
|
|
if ( !ExtentList.Initialize( 0, 0 ) ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
Cluster = Dirent->QueryStartingCluster();
|
|
ClusterSize = _ClusterFactor*_Drive->QuerySectorSize();
|
|
ClustersLeft = (FileSize + ClusterSize - 1)/ClusterSize;
|
|
Fat = _FatSa->GetFat();
|
|
Vcn = 0;
|
|
|
|
// Add all the FAT clusters to the NTFS extent list. Note that in the last
|
|
// cluster we only add those sectors that contain file data, and the rest
|
|
// will become free after the conversion.
|
|
//
|
|
while ( ClustersLeft ) {
|
|
|
|
Lcn = FatClusterToLcn( Cluster )/_ClusterFactor;
|
|
Length = min( ClustersLeft, _ClusterRatio );
|
|
|
|
//DebugPrintTrace(( " Extent: Cluster %d Vcn %d Lcn %d Length %d Left %d\n",
|
|
// Cluster, Vcn.GetLowPart(), Lcn.GetLowPart(), Length,
|
|
// SectorsLeft ));
|
|
|
|
if ( !ExtentList.AddExtent( Vcn, Lcn, Length ) ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
Vcn += Length;
|
|
ClustersLeft -= Length;
|
|
|
|
DebugAssert((ClustersLeft > 0) || Fat->IsEndOfChain(Cluster));
|
|
|
|
Cluster = Fat->QueryEntry( Cluster );
|
|
}
|
|
|
|
//
|
|
// Unused sectors in the last cluster are marked in the
|
|
// ReservedBitmap.
|
|
//
|
|
if ( Length < _ClusterRatio ) {
|
|
|
|
_ReservedBitmap.SetAllocated( Lcn+Length,
|
|
_ClusterRatio - Length );
|
|
}
|
|
|
|
//
|
|
// Now put the file data in the $DATA attribute of the file
|
|
//
|
|
if ( !DataAttribute.Initialize( _Drive,
|
|
_ClusterFactor,
|
|
&ExtentList,
|
|
FileSize,
|
|
FileSize,
|
|
$DATA ) ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !DataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CONVERT_DATA, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// File data converted
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertFileDataResident (
|
|
IN PFAT_DIRENT Dirent,
|
|
IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the file data from FAT to NTFS in resident form
|
|
|
|
Arguments:
|
|
|
|
Dirent - Supplies the directory entry of the file to convert
|
|
Frs - Supplies the FRS for the file
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if file data successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HMEM Hmem; // Memory
|
|
CLUSTER_CHAIN ClusterChain; // File cluster
|
|
NTFS_ATTRIBUTE DataAttribute; // File's $DATA attribute
|
|
ULONG FileSize; // File size
|
|
|
|
DebugPtrAssert( Dirent );
|
|
DebugPtrAssert( Frs );
|
|
|
|
//
|
|
// Get the file size
|
|
//
|
|
FileSize = Dirent->QueryFileSize();
|
|
|
|
if ( FileSize > 0 ) {
|
|
//
|
|
// Read the file data.
|
|
//
|
|
if ( !Hmem.Initialize() ||
|
|
!ClusterChain.Initialize( &Hmem,
|
|
_Drive,
|
|
_FatSa,
|
|
_FatSa->GetFat(),
|
|
Dirent->QueryStartingCluster(), 1 ) ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !ClusterChain.Read() ) {
|
|
|
|
//
|
|
// We cannot read the file data, possibly because there is a
|
|
// bad sector on the volume. We will try make the file data
|
|
// non-resident (that way we don't need to read the data, since
|
|
// all we do is generate the allocation info). Doing things
|
|
// this way does not change the state of the drive (i.e. it was
|
|
// bad before conversion, it is bad after the conversion).
|
|
//
|
|
return ConvertFileDataNonResident( Dirent, Frs );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now put the file data in the $DATA attribute of the file
|
|
//
|
|
if ( (FileSize > 0 && !ClusterChain.GetBuf()) ||
|
|
!DataAttribute.Initialize( _Drive,
|
|
_ClusterFactor,
|
|
(FileSize > 0) ? ClusterChain.GetBuf() : NULL,
|
|
FileSize,
|
|
$DATA ) ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !DataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CONVERT_DATA, ERROR_MESSAGE );
|
|
_Message->Display( );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We can now mark the cluster with the file data in the
|
|
// ReservedBitmap, so it will be freed after the conversion.
|
|
//
|
|
// Note that we cannot mark it free in the VolumeBitmap because
|
|
// the conversion process must NOT overwrite it (or data may
|
|
// be lost if the conversion fails!).
|
|
//
|
|
if ( FileSize > 0 ) {
|
|
ReserveCluster( Dirent->QueryStartingCluster() );
|
|
}
|
|
|
|
//
|
|
// File data converted
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ConvertFileSystem(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the existing FAT file system to NTFS. This is done by
|
|
traversing the file system tree and converting the FAT structures
|
|
(i.e. directories, files and EAs).
|
|
|
|
The space occupied by FAT-specific files (e.g. the EA file) is marked
|
|
in the ReservedBitmap so it will be freed up when the conversion is
|
|
done.
|
|
|
|
Note that Operating-System-specific files (e.g. IO.SYS, MSDOS.SYS) are
|
|
NOT removed by the conversion process. This is a File System conversion,
|
|
not an Operating System conversion (If this file system conversion is
|
|
being invoked by an operating system conversion program, then that
|
|
program is responsible for removing any system files).
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if file system successfully converted
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFATDIR RootDir; // FAT root directory
|
|
FAT_DIRENT EAFileDirEnt; // Entry for EA file
|
|
DSTRING EAFile; // Name of EA file
|
|
ULONG i; // Cluster index
|
|
PFAT Fat; // Pointer to FAT
|
|
UCHAR FatType;
|
|
|
|
_Message->Set( MSG_CONV_CONVERTING_FS );
|
|
_Message->Display();
|
|
|
|
//
|
|
// Get the root directory
|
|
//
|
|
RootDir = (PFATDIR)_FatSa->GetRootDir();
|
|
|
|
FatType = FAT_TYPE_EAS_OKAY;
|
|
|
|
if ( !RootDir ) {
|
|
RootDir = (PFATDIR)_FatSa->GetFileDir();
|
|
FatType = FAT_TYPE_FAT32;
|
|
}
|
|
|
|
DebugPtrAssert( RootDir );
|
|
|
|
//
|
|
// Locate the EA file. If it exists then remember its starting cluster
|
|
// number and initialize the EA header.
|
|
//
|
|
// The starting cluster of the EA file is remembered because it is used
|
|
// later on for identifying the EA file while traversing the root directory.
|
|
//
|
|
EAFile.Initialize( "EA DATA. SF" );
|
|
|
|
if ( (FAT_TYPE_FAT32 != FatType) && EAFileDirEnt.Initialize( RootDir->SearchForDirEntry( &EAFile )) )
|
|
{
|
|
_EAFileFirstCluster = EAFileDirEnt.QueryStartingCluster();
|
|
|
|
Fat = _FatSa->GetFat();
|
|
|
|
if ( !_EAMemory.Initialize() ||
|
|
!_EAHeader.Initialize( &_EAMemory,
|
|
_Drive,
|
|
_FatSa,
|
|
Fat,
|
|
_EAFileFirstCluster ) ||
|
|
|
|
!_EAHeader.Read()
|
|
) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Mark all the EA file sectors in the ReservedBitmap, so the
|
|
// space will be freed up when the conversion is done.
|
|
//
|
|
i = _EAFileFirstCluster;
|
|
|
|
#if DBG
|
|
if ( _Flags & CONVERT_VERBOSE_FLAG ) {
|
|
DebugPrintTrace(( "EA file at cluster %X\n", _EAFileFirstCluster ));
|
|
}
|
|
#endif
|
|
|
|
while (TRUE) {
|
|
|
|
ReserveCluster( i );
|
|
|
|
if ( Fat->IsEndOfChain( i ) ) {
|
|
break;
|
|
}
|
|
|
|
i = Fat->QueryEntry( i );
|
|
}
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
if ( _Flags & CONVERT_VERBOSE_FLAG ) {
|
|
DebugPrintTrace(( "The volume contains no EA file\n" ));
|
|
}
|
|
#endif
|
|
|
|
_EAFileFirstCluster = 0;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Convert the volume by recursively converting the root directory
|
|
//
|
|
return ConvertRoot( RootDir );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::CreateBitmaps(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates the NTFS bitmaps for the volume and the bad block stack.
|
|
|
|
Two bitmaps are created:
|
|
|
|
_VolumeBitmap - Is the bitmap for the volume. Represents the volume at
|
|
seemed by NTFS.
|
|
|
|
_ReservedBitmap - Contains those NTFS clusters that are marked as "in use"
|
|
in the _VolumeBitmap during the conversion, but that must
|
|
be marked as "free" after the conversion. This is
|
|
required so that the conversion don't try to allocate
|
|
those clusters. These "reserved" clusters include all
|
|
the FAT structures that will be thrown away after the
|
|
conversion.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if bitmaps and bad block stack created.
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFAT Fat; // The FAT
|
|
ULONG Cluster; // Used to traverse FAT
|
|
LCN Lcn; // Same as Cluster, but in sectors
|
|
ULONG ClusterCount; // Number of clusters in volume
|
|
LCN DataAreaStart; // Start of data area;
|
|
ULONG SectorsPerFatCluster; // Sector counter
|
|
BIG_INT NumberOfClusters;
|
|
|
|
//
|
|
// Initialize bitmaps
|
|
//
|
|
|
|
DebugAssert(_FatSa->IsVolumeDataAligned() || (_ClusterFactor == 1));
|
|
|
|
NumberOfClusters = (_Drive->QuerySectors() - 1)/_ClusterFactor;
|
|
DebugAssert(NumberOfClusters.GetHighPart() == 0);
|
|
|
|
if (!_VolumeBitmap.Initialize(NumberOfClusters, FALSE, _Drive, _ClusterFactor) ||
|
|
!_ReservedBitmap.Initialize(NumberOfClusters, FALSE, NULL, 0)) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The boot area must be free (it will become reserved when creating
|
|
// the elementary NTFS structures).
|
|
//
|
|
|
|
//
|
|
// The FAT ( From the end of the boot area up to the beginning of
|
|
// the data area) is reserved but will be freed at the end of the
|
|
// conversion.
|
|
//
|
|
DataAreaStart = _FatSa->QueryStartDataLbn();
|
|
|
|
DebugAssert(!_FatSa->IsVolumeDataAligned() || (DataAreaStart % _ClusterFactor == 0));
|
|
|
|
DataAreaStart = DataAreaStart / _ClusterFactor;
|
|
|
|
_VolumeBitmap.SetAllocated( CLUSTERS_IN_BOOT,
|
|
DataAreaStart - CLUSTERS_IN_BOOT );
|
|
|
|
_ReservedBitmap.SetAllocated( CLUSTERS_IN_BOOT,
|
|
DataAreaStart - CLUSTERS_IN_BOOT );
|
|
|
|
//
|
|
// Allocate the rest of the bitmap according to the FAT
|
|
//
|
|
Lcn = DataAreaStart;
|
|
Cluster = FirstDiskCluster;
|
|
ClusterCount = _FatSa->QueryClusterCount() - (ULONG)FirstDiskCluster;
|
|
Fat = _FatSa->GetFat();
|
|
SectorsPerFatCluster = _ClusterRatio * _ClusterFactor;
|
|
|
|
while ( ClusterCount-- ) {
|
|
|
|
//
|
|
// If the cluster is not free then allocate it if its OK, or
|
|
// push it onto the bad stack if it is bad.
|
|
//
|
|
if ( Fat->IsClusterFree( Cluster ) ) {
|
|
|
|
Lcn += _ClusterRatio;
|
|
|
|
} else if ( Fat->IsClusterBad( Cluster ) ) {
|
|
|
|
_BadLcn.Add( Lcn*_ClusterFactor, SectorsPerFatCluster );
|
|
Lcn += _ClusterRatio;
|
|
|
|
} else {
|
|
|
|
_VolumeBitmap.SetAllocated( Lcn, _ClusterRatio );
|
|
Lcn += _ClusterRatio;
|
|
}
|
|
|
|
Cluster++;
|
|
}
|
|
|
|
//
|
|
// Note that CLUSTERS_IN_BOOT are not really free (will be
|
|
// allocated later on).
|
|
//
|
|
_FreeSectorsBefore = ((BIG_INT)(_VolumeBitmap.QueryFreeClusters() - CLUSTERS_IN_BOOT))*
|
|
_Drive->QuerySectorSize();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::CreateElementary(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates the elementary NTFS data structures.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if elementary NTFS data structures created.
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
NTFS_UPCASE_FILE UpcaseFile;
|
|
NTFS_ATTRIBUTE UpcaseAttribute;
|
|
NTFS_LOG_FILE LogFile;
|
|
DSTRING VolumeLabel; // Volume label
|
|
|
|
BOOLEAN Error;
|
|
|
|
DebugAssert( _VolumeBitmap.IsFree( 0, CLUSTERS_IN_BOOT ) );
|
|
|
|
//
|
|
// Get the volume label and create the elementary NTFS structures.
|
|
// Pass in zero for the initial log file size to indicate that
|
|
// CreateElementaryStructures should decide how big to make it.
|
|
//
|
|
if ( !_FatSa->QueryLabel( &VolumeLabel ) ||
|
|
!_NtfsSa.CreateElementaryStructures( &_VolumeBitmap,
|
|
_ClusterFactor,
|
|
_FrsSize,
|
|
SMALL_INDEX_BUFFER_SIZE,
|
|
0,
|
|
&_BadLcn,
|
|
TRUE,
|
|
TRUE,
|
|
_Message,
|
|
_FatSa->GetBpb(),
|
|
&VolumeLabel ) ) {
|
|
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CREATE_ELEMENTARY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now that we have the elementary structures, obtain the MFT, which is
|
|
// used later on during the conversion. Since we don't have an upcase
|
|
// table yet, pass in NULL for that parameter.
|
|
//
|
|
if ( !_Mft.Initialize( _Drive,
|
|
_NtfsSa.QueryMftStartingLcn(),
|
|
_ClusterFactor,
|
|
_FrsSize,
|
|
_NtfsSa.QueryVolumeSectors(),
|
|
&_VolumeBitmap,
|
|
NULL ) ||
|
|
!_Mft.Read() ) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Tell the volume bitmap about the new Mft so it can add any
|
|
// bad clusters it finds to the bad cluster file.
|
|
|
|
_VolumeBitmap.SetMftPointer(_Mft.GetMasterFileTable());
|
|
|
|
|
|
// Get the upcase table.
|
|
//
|
|
if( !UpcaseFile.Initialize( _Mft.GetMasterFileTable() ) ||
|
|
!UpcaseFile.Read() ||
|
|
!UpcaseFile.QueryAttribute( &UpcaseAttribute, &Error, $DATA ) ||
|
|
!_UpcaseTable.Initialize( &UpcaseAttribute ) ) {
|
|
|
|
DebugPrint( "Can't get the upcase table.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
_Mft.SetUpcaseTable( &_UpcaseTable );
|
|
_Mft.GetMasterFileTable()->SetUpcaseTable( &_UpcaseTable );
|
|
|
|
//
|
|
// If we know how many files there are on the volume, extend the
|
|
// MFT so it is (sort of) contiguous.
|
|
//
|
|
if ( (_NumberOfFiles + _NumberOfDirectories) > 0 ) {
|
|
|
|
if ( !_Mft.Extend( _NumberOfFiles + _NumberOfDirectories + 20 ) ) {
|
|
|
|
DebugPrintTrace(( "Cannot extend MFT by %d segments\n", _NumberOfFiles + _NumberOfDirectories ));
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CREATE_ELEMENTARY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Flush the MFT now, so that it gets first claim to the FRS's
|
|
// at the beginning of the MFT.
|
|
//
|
|
if( !_Mft.Flush() ) {
|
|
|
|
DebugPrintTrace(( "CONVERT: Cannot flush the MFT\n" ));
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_CREATE_ELEMENTARY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
// Sanity check: make sure that the log file doesn't have
|
|
// an attribute list. The file system will die horribly
|
|
// if Convert creates a log file with external attributes.
|
|
//
|
|
if( !LogFile.Initialize( _Mft.GetMasterFileTable() ) ||
|
|
!LogFile.Read() ||
|
|
LogFile.IsAttributePresent( $ATTRIBUTE_LIST ) ) {
|
|
|
|
_Message->Set( MSG_CONV_VOLUME_TOO_FRAGMENTED );
|
|
_Message->Display( "" );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::FreeReservedSectors (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees up those sectors marked as "in use" in the _ReservedBitmap.
|
|
The _VolumeBitmap is updated and written to disk.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if sectors freed and _VolumeBitmap written.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTFS_BITMAP_FILE BitmapFile; // NTFS bitmap file
|
|
NTFS_ATTRIBUTE Attribute; // $DATA attribute of bitmap file
|
|
LCN Lcn; // LCN
|
|
BOOLEAN Error;
|
|
PFILEDIR DirF32; // Pointer to FAT 32 Root dir
|
|
BIG_INT NumberOfLcn;
|
|
|
|
//
|
|
// Free the "reserved" clusters
|
|
//
|
|
NumberOfLcn = _Drive->QuerySectors()/_ClusterFactor;
|
|
for ( Lcn = 0; Lcn < NumberOfLcn; Lcn += 1 ) {
|
|
if ( !_ReservedBitmap.IsFree( Lcn, 1 ) ) {
|
|
_VolumeBitmap.SetFree( Lcn, 1 );
|
|
_ReservedBitmap.SetFree( Lcn, 1 );
|
|
}
|
|
}
|
|
|
|
DirF32 = _FatSa->GetFileDir();
|
|
if (DirF32) { // it's a fat32 drive..
|
|
ULONG clus;
|
|
PFAT Fat; // The FAT
|
|
ULONG SectorsPerCluster; // Sectors per cluster
|
|
LCN DataAreaStart; // Start of data area;
|
|
LCN Lcn; // Logical cluster number
|
|
|
|
SectorsPerCluster = _FatSa->QuerySectorsPerCluster();
|
|
Fat = _FatSa->GetFat();
|
|
DataAreaStart = _FatSa->QueryStartDataLbn();
|
|
|
|
//
|
|
// Mark the first 32 reserved sectors for later cleanup
|
|
//
|
|
_ReservedBitmap.SetAllocated( CLUSTERS_IN_BOOT, (32-1)/_ClusterFactor+1-CLUSTERS_IN_BOOT );
|
|
|
|
//
|
|
// Mark the root chain as "UN-used" in the NTFS bitmap.
|
|
// Mark those root directory clusters in the reserved bitmap for later cleanup
|
|
//
|
|
for (clus = DirF32->QueryStartingCluster(); !Fat->IsEndOfChain(clus); clus = Fat->QueryEntry(clus)) {
|
|
{ // Free sectors under this Root Dir Cluster
|
|
Lcn = (DataAreaStart + ((clus-FirstDiskCluster)*SectorsPerCluster))/_ClusterFactor;
|
|
_VolumeBitmap.SetFree( Lcn, _ClusterRatio );
|
|
_ReservedBitmap.SetAllocated( Lcn, _ClusterRatio );
|
|
}
|
|
}
|
|
{ // Free sectors under this Root Dir Cluster
|
|
Lcn = (DataAreaStart + ((clus-FirstDiskCluster)*SectorsPerCluster))/_ClusterFactor;
|
|
_VolumeBitmap.SetFree( Lcn, _ClusterRatio );
|
|
_ReservedBitmap.SetAllocated( Lcn, _ClusterRatio );
|
|
}
|
|
} else {
|
|
|
|
ULONG root_offset = _FatSa->QueryReservedSectors()+_FatSa->QueryFats()*_FatSa->QuerySectorsPerFat();
|
|
ULONG root_size = (_FatSa->QueryRootEntries()*BytesPerDirent - 1)/_Drive->QuerySectorSize() + 1;
|
|
|
|
DebugAssert( root_offset % _ClusterFactor == 0 );
|
|
DebugAssert( root_size % _ClusterFactor == 0 );
|
|
|
|
_ReservedBitmap.SetAllocated( root_offset/_ClusterFactor, root_size/_ClusterFactor );
|
|
}
|
|
|
|
//
|
|
// Update the Bitmap file.
|
|
//
|
|
if ( !BitmapFile.Initialize( _Mft.GetMasterFileTable() ) ||
|
|
!BitmapFile.Read() ||
|
|
!BitmapFile.QueryAttribute( &Attribute, &Error, $DATA )
|
|
) {
|
|
|
|
_Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update the Bitmap file data attribute (i.e. the volume bitmap)
|
|
//
|
|
if ( !_VolumeBitmap.Write( &Attribute, &_VolumeBitmap ) ) {
|
|
_Message->Set( MSG_CONV_CANNOT_WRITE, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
_FreeSectorsAfter = ((BIG_INT)_VolumeBitmap.QueryFreeClusters())*_ClusterFactor;
|
|
|
|
#if DBG
|
|
if ( _Flags & CONVERT_VERBOSE_FLAG ) {
|
|
DebugPrintTrace(( "Free sectors before conversion: %d\n", _FreeSectorsBefore.GetLowPart() ));
|
|
DebugPrintTrace(( "Free sectors after conversion: %d\n", _FreeSectorsAfter.GetLowPart() ));
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::QueryNeededHoles (
|
|
OUT PINTSTACK Stack
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines what holes are required and pushes the hole
|
|
information in the supplied stack.
|
|
|
|
Arguments:
|
|
|
|
Stack - Supplies the stack where the hole information is
|
|
passed
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if all hole information is in stack
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BIG_INT HoleStart; // Starting sector of hole
|
|
BIG_INT HoleSize; // Size of the hole
|
|
BIG_INT BootSize; // Size of boot code
|
|
BIG_INT MftSize; // Size of MFT
|
|
BIG_INT MftReflectionSize; // Size of MFT reflection
|
|
ULONG sectorsize;
|
|
USHORT i;
|
|
|
|
//
|
|
// Initialize the hole stack
|
|
//
|
|
|
|
if ( !Stack->Initialize() ) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The only NTFS structure that needs a fixed location is
|
|
// the BOOT backup. Push the size and location of this sector
|
|
// onto the stack.
|
|
//
|
|
|
|
if ( !Stack->Push( 1 ) ||
|
|
!Stack->Push( _FatSa->QueryVirtualSectors() - 1 ) ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We also want to create a hole big enough for the MFT and
|
|
// the MFT reflection. These don't need to be in a particular
|
|
// location, but they have to be contiguous (i.e. occupy a
|
|
// single "hole").
|
|
//
|
|
sectorsize = _Drive->QuerySectorSize();
|
|
|
|
MftSize = (_FrsSize * FIRST_USER_FILE_NUMBER + (sectorsize - 1)) / sectorsize;
|
|
|
|
MftReflectionSize = (_FrsSize * REFLECTED_MFT_SEGMENTS + (sectorsize - 1))
|
|
/ sectorsize;
|
|
|
|
HoleSize = MftSize + MftReflectionSize;
|
|
HoleStart = _FatSa->QueryVirtualSectors() - 1 - HoleSize;
|
|
|
|
#if DBG
|
|
if ( _Flags & CONVERT_VERBOSE_FLAG ) {
|
|
DebugPrintTrace(( "Hole required: Sector %X, size %X\n",
|
|
HoleStart.GetLowPart(), HoleSize.GetLowPart() ));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Make sure that the hole lies entirely in the FAT data area. Otherwise
|
|
// we won't be able to relocate the clusters in the hole.
|
|
//
|
|
if ( HoleStart < _FatSa->QueryStartDataLbn() ) {
|
|
_Message->Set( MSG_CONV_CANNOT_CONVERT_VOLUME, ERROR_MESSAGE );
|
|
_Message->Display( "%s%s", "NTFS", "FAT" );
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Push the hole data in the stack. Size goes first!
|
|
//
|
|
if ( !Stack->Push( HoleSize ) ||
|
|
!Stack->Push( HoleStart ) ) {
|
|
|
|
_Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
|
_Message->Display();
|
|
_Status = CONVERT_STATUS_ERROR;
|
|
DebugAssert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
FAT_NTFS::QuerySectorsNeededForConversion (
|
|
IN PCENSUS_REPORT Census,
|
|
OUT PBIG_INT SectorsNeeded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines how many sectors are required for the conversion, given
|
|
the volume census.
|
|
|
|
Arguments:
|
|
|
|
Census - Supplies the volume census
|
|
SectorsNeeded - Supplies pointer to number of sectors needed
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
BIG_INT SectorsRequired;
|
|
BIG_INT BytesInIndices;
|
|
|
|
ULONG szTemp;
|
|
ULONG NtfsClusterSize; // Size of an NTFS cluster
|
|
ULONG sectorsize;
|
|
|
|
CONST AverageBytesPerIndexEntry = 128;
|
|
|
|
|
|
#if DBG
|
|
if ( _Flags & CONVERT_VERBOSE_FLAG ) {
|
|
DebugPrintTrace(( "\n" ));
|
|
DebugPrintTrace(( "---- Volume Census Data ----\n" ));
|
|
DebugPrintTrace(( "Number of dirs: %d\n", Census->DirEntriesCount ));
|
|
DebugPrintTrace(( "Number of files: %d\n", Census->FileEntriesCount ));
|
|
DebugPrintTrace(( "Clusters in dirs: %d\n", Census->DirClusters ));
|
|
DebugPrintTrace(( "Clusters in files: %d\n", Census->FileClusters ));
|
|
DebugPrintTrace(( "Clusters in EA file: %d\n", Census->EaClusters ));
|
|
DebugPrintTrace(( "\n\n" ));
|
|
}
|
|
#endif
|
|
|
|
|
|
NtfsClusterSize = _Drive->QuerySectorSize() * _ClusterFactor;
|
|
_NumberOfFiles = Census->FileEntriesCount;
|
|
_NumberOfDirectories = Census->DirEntriesCount;
|
|
|
|
SectorsRequired =
|
|
NTFS_SA::QuerySectorsInElementaryStructures( _Drive,
|
|
_ClusterFactor,
|
|
_FrsSize,
|
|
_ClustersPerIndexBuffer,
|
|
0 );
|
|
|
|
|
|
//
|
|
// We will need _ClustersPerFrs clusters for each file or
|
|
// directory, plus enough index blocks to hold the required
|
|
// index entries. (Multiply the size of indices by two to
|
|
// reflect the fact that the average index block will be
|
|
// half full.)
|
|
//
|
|
|
|
sectorsize = _Drive->QuerySectorSize();
|
|
|
|
SectorsRequired += ( _NumberOfFiles + _NumberOfDirectories ) *
|
|
((_FrsSize + (sectorsize - 1))/sectorsize);
|
|
|
|
BytesInIndices = ( _NumberOfFiles + _NumberOfDirectories ) *
|
|
AverageBytesPerIndexEntry * 2;
|
|
|
|
SectorsRequired += BytesInIndices / _Drive->QuerySectorSize();
|
|
|
|
//
|
|
// Extended attributes
|
|
//
|
|
if(Census->EaClusters) {
|
|
|
|
//
|
|
// With EAs each file will require one extra header for the EA itself
|
|
//
|
|
SectorsRequired += _NumberOfFiles *
|
|
((_FrsSize + (sectorsize - 1))/sectorsize);
|
|
|
|
//
|
|
// Compute the "per file average EA size", round it up to
|
|
// the sector size and multiply it times the file count to get
|
|
// the projected EA size on NTFS.
|
|
//
|
|
szTemp = Census->EaClusters * _FatSa->QuerySectorsPerCluster();
|
|
szTemp = (szTemp + (_NumberOfFiles - 1)) / _NumberOfFiles; // sectors per file in EAs
|
|
if(szTemp == 0) {
|
|
szTemp = 1;
|
|
}
|
|
SectorsRequired += szTemp * _NumberOfFiles;
|
|
}
|
|
|
|
//
|
|
// In case of unreported bad sectors, we reserve 0.1% of the disk
|
|
//
|
|
SectorsRequired += _Drive->QuerySectors() / 1000;
|
|
|
|
// And that's that.
|
|
|
|
*SectorsNeeded = SectorsRequired;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::ReserveCluster (
|
|
IN ULONG Cluster
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
"Reserves" all the sectors in the given clusters. This is done
|
|
by marking the sectors in the ReservedBitmap.
|
|
|
|
Arguments:
|
|
|
|
Cluster - Supplies cluster whose sectors are to be reserved
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if all sectors in the cluster have been reserved
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
LCN Lcn;
|
|
BIG_INT Clusters;
|
|
|
|
Clusters = ((ULONG)_FatSa->QuerySectorsPerCluster()) / _ClusterFactor;
|
|
|
|
if ( Cluster > 0 ) {
|
|
|
|
Lcn = FatClusterToLcn( Cluster )/_ClusterFactor;
|
|
|
|
_ReservedBitmap.SetAllocated( Lcn, Clusters );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
NONVIRTUAL
|
|
BOOLEAN
|
|
FAT_NTFS::CheckGeometryMatch(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method checks that the geometry recorded in the
|
|
Bios Parameter Block agrees with the geometry reported
|
|
by the driver.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the geometry in the BPB matches that reported
|
|
by the driver; false if not. Note that the only field
|
|
which is checked is BytesPerSector.
|
|
|
|
--*/
|
|
{
|
|
USHORT SectorSize, SectorsPerTrack, Heads;
|
|
ULONG HiddenSectors;
|
|
|
|
_FatSa->QueryGeometry( &SectorSize,
|
|
&SectorsPerTrack,
|
|
&Heads,
|
|
&HiddenSectors );
|
|
|
|
if( SectorSize != _Drive->QuerySectorSize() ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FAT_NTFS::WriteBoot (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the boot sector and writes any other information (e.g.
|
|
partition data) so that the volume will be recognized as an NTFS
|
|
volume.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if boot sector updated
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
FSTRING BootLogFileName;
|
|
PBYTE ZeroBuf;
|
|
ULONG Lcn;
|
|
|
|
BOOLEAN Done;
|
|
|
|
Done = (BOOLEAN)(_Mft.Flush() &&
|
|
_NtfsSa.Write( _Message ) &&
|
|
_NtfsSa.WriteRemainingBootCode());
|
|
|
|
if (Done) {
|
|
//
|
|
// Now that it passses the point of no return, we clean up
|
|
// any remaining fat32 reserved boot sectors and/or fat root directory.
|
|
//
|
|
|
|
ZeroBuf = (PBYTE)MALLOC( _Drive->QuerySectorSize()*_ClusterFactor );
|
|
|
|
if (ZeroBuf == NULL) {
|
|
_Message->Set( MSG_CONV_NO_MEMORY );
|
|
_Message->Display();
|
|
return FALSE;
|
|
}
|
|
|
|
memset( ZeroBuf, 0, _Drive->QuerySectorSize()*_ClusterFactor );
|
|
|
|
for ( Lcn = 0; Lcn < _ReservedBitmap.QuerySize(); Lcn +=1 ) {
|
|
|
|
if ( !_ReservedBitmap.IsFree( Lcn, 1 ) && _VolumeBitmap.IsFree( Lcn, 1 ) ) {
|
|
if (!_Drive->Write( Lcn*_ClusterFactor, 1, ZeroBuf )) {
|
|
DebugPrintTrace(("CUFAT: Failed to wipe clean cluster %x\n", Lcn));
|
|
}
|
|
}
|
|
}
|
|
|
|
FREE(ZeroBuf);
|
|
}
|
|
|
|
#if defined ( _AUTOCONV_ )
|
|
|
|
if ( Done ) {
|
|
|
|
//
|
|
// The volume is no longer FAT. We have to reboot so that the
|
|
// system recognizes it. Note that before we reboot, we
|
|
// must flush the drive's cache.
|
|
//
|
|
BootLogFileName.Initialize( L"bootex.log" );
|
|
|
|
if( _Message->IsLoggingEnabled() &&
|
|
!NTFS_SA::DumpMessagesToFile( &BootLogFileName,
|
|
&_Mft,
|
|
_Message ) ) {
|
|
|
|
DebugPrintTrace(( "CONVERT: Error writing messages to BOOTEX.LOG\n" ));
|
|
}
|
|
|
|
_Drive->FlushCache();
|
|
|
|
_Drive->InvalidateVolume();
|
|
|
|
//
|
|
// Unlock the volume and close our handle, to let the filesystem
|
|
// notice that things have changed.
|
|
//
|
|
|
|
DELETE(_Drive);
|
|
|
|
if ( _Flags & CONVERT_PAUSE_FLAG ) {
|
|
|
|
_Message->Set( MSG_CONV_PAUSE_BEFORE_REBOOT );
|
|
_Message->Display();
|
|
|
|
_Message->WaitForUserSignal();
|
|
} else {
|
|
_Message->Set( MSG_CONV_CONVERSION_COMPLETE );
|
|
_Message->Display();
|
|
}
|
|
|
|
//
|
|
// If we've paused for oem setup, we pass PowerOff = TRUE to
|
|
// Reboot.
|
|
//
|
|
|
|
IFS_SYSTEM::Reboot( (_Flags & CONVERT_PAUSE_FLAG) ? TRUE : FALSE );
|
|
}
|
|
#endif
|
|
|
|
return Done;
|
|
}
|